mirror of
https://github.com/HypoPG/hypopg
synced 2026-05-24 09:38:21 +00:00
Add support for hypothetically hide existing index on explain.
Add new list hideIndexes and develop new function by hypo_get_relation_info_hook. User can hide or unhide existing index and hypothetical indexes. When user set hide existing index and execute EXPLAIN, it will scan the same oid in hideIndexes and remove it to make query-plan can't use it. Add regression tests for hiding existing indexes. Update to version hypopg--1.4.0. Provide simple examples of using the hide series functions in README.md. Update doc about hypothetically hide existing indexes.
This commit is contained in:
parent
528070a6f5
commit
59f8a1047d
11 changed files with 910 additions and 0 deletions
|
|
@ -19,3 +19,4 @@ People who contributed to hypopg:
|
|||
* github user nikhil-postgres
|
||||
* Xiaozhe Yao
|
||||
* Krzysztof Szularz
|
||||
* NutVII
|
||||
2
Makefile
2
Makefile
|
|
@ -51,6 +51,8 @@ ifneq ($(MAJORVERSION),$(filter $(MAJORVERSION), 9.2 9.3 9.4 9.5 9.6))
|
|||
REGRESS += hypo_hash
|
||||
endif
|
||||
|
||||
REGRESS += hypo_hide_index
|
||||
|
||||
DEBUILD_ROOT = /tmp/$(EXTENSION)
|
||||
|
||||
deb: release-zip
|
||||
|
|
|
|||
85
README.md
85
README.md
|
|
@ -99,3 +99,88 @@ To remove your backend's hypothetical indexes, you can use the function
|
|||
`hypopg_drop_index(indexrelid)` with the OID that the `hypopg_list_indexes`
|
||||
view returns and call `hypopg_reset()` to remove all at once, or just close
|
||||
your current connection.
|
||||
|
||||
Continuing with the above case, you can `hide existing indexes`,
|
||||
but should be use `hypopg_reset()` to clear the previous effects of other indexes at first.
|
||||
|
||||
Create two real indexes and run `EXPLAIN`:
|
||||
|
||||
rjuju=# SELECT hypopg_reset();
|
||||
rjuju=# CREATE INDEX ON hypo(id);
|
||||
rjuju=# CREATE INDEX ON hypo(id, val);
|
||||
rjuju=# EXPLAIN SELECT * FROM hypo WHERE id = 1;
|
||||
QUERY PLAN
|
||||
----------------------------------------------------------------------------------
|
||||
Index Only Scan using hypo_id_val_idx on hypo (cost=0.29..8.30 rows=1 width=13)
|
||||
Index Cond: (id = 1)
|
||||
(2 rows)
|
||||
|
||||
The query plan is using the `hypo_id_val_idx` index. Use `hypopg_hide_index(oid)` to hide one of the indexes:
|
||||
|
||||
rjuju=# SELECT hypopg_hide_index('hypo_id_val_idx'::REGCLASS);
|
||||
rjuju=# EXPLAIN SELECT * FROM hypo WHERE id = 1;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------------------
|
||||
Index Scan using hypo_id_idx on hypo (cost=0.29..8.30 rows=1 width=13)
|
||||
Index Cond: (id = 1)
|
||||
(2 rows)
|
||||
|
||||
The query plan is using the other index `hypo_id_idx` now. Use `hypopg_hide_index(oid)` to hide it:
|
||||
|
||||
rjuju=# SELECT hypopg_hide_index('hypo_id_idx'::REGCLASS);
|
||||
rjuju=# EXPLAIN SELECT * FROM hypo WHERE id = 1;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------
|
||||
Seq Scan on hypo (cost=0.00..180.00 rows=1 width=13)
|
||||
Filter: (id = 1)
|
||||
(2 rows)
|
||||
|
||||
And now the query plan changes back to `Seq Scan`. Use `hypopg_unhide_index(oid)` to restore index:
|
||||
|
||||
rjuju=# SELECT hypopg_unhide_index('hypo_id_idx'::regclass);
|
||||
rjuju=# EXPLAIN SELECT * FROM hypo WHERE id = 1;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------------------
|
||||
Index Scan using hypo_id_idx on hypo (cost=0.29..8.30 rows=1 width=13)
|
||||
Index Cond: (id = 1)
|
||||
(2 rows)
|
||||
|
||||
Of course, you can also hide hypothetical indexes:
|
||||
|
||||
rjuju=# SELECT hypopg_create_index('CREATE INDEX ON hypo(id)');
|
||||
rjuju=# EXPLAIN SELECT * FROM hypo WHERE id = 1;
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------------------------------
|
||||
Index Scan using "<12659>btree_hypo_id" on hypo (cost=0.04..8.05 rows=1 width=13)
|
||||
Index Cond: (id = 1)
|
||||
(2 rows)
|
||||
|
||||
rjuju=# SELECT hypopg_hide_index(12659);
|
||||
rjuju=# EXPLAIN SELECT * FROM hypo WHERE id = 1;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------
|
||||
Seq Scan on hypo (cost=0.00..180.00 rows=1 width=13)
|
||||
Filter: (id = 1)
|
||||
(2 rows)
|
||||
|
||||
You can check which indexes are hidden using `hypopg_hidden_indexes()` or the `hypopg_hidden_indexes` view:
|
||||
|
||||
rjuju=# SELECT * FROM hypopg_hidden_indexes();
|
||||
indexid
|
||||
---------
|
||||
526604
|
||||
526603
|
||||
12659
|
||||
(3 rows)
|
||||
|
||||
rjuju=# SELECT * FROM hypopg_hidden_indexes;
|
||||
indexrelid | index_name | schema_name | table_name | am_name | is_hypo
|
||||
------------+----------------------+-------------+------------+---------+---------
|
||||
12659 | <12659>btree_hypo_id | public | hypo | btree | t
|
||||
526603 | hypo_id_idx | public | hypo | btree | f
|
||||
526604 | hypo_id_val_idx | public | hypo | btree | f
|
||||
(3 rows)
|
||||
|
||||
To restore all existing indexes, you can use the function `hypopg_unhide_all_indexes()`.
|
||||
Note that the functionality to hide existing indexes only applies to the EXPLAIN command in the current session
|
||||
and will not affect other sessions.
|
||||
166
docs/usage.rst
166
docs/usage.rst
|
|
@ -224,3 +224,169 @@ Some other convenience functions and views are available:
|
|||
- **hypopg_drop_index(oid)**: function that removes the given hypothetical
|
||||
index
|
||||
- **hypopg_reset()**: function that removes all hypothetical indexes
|
||||
|
||||
Hypothetically hide existing indexes
|
||||
------------------------------------
|
||||
|
||||
You can hide both existing and hypothetical indexes hypothetically.
|
||||
If you want to test it as described in the documentation,
|
||||
you should first use **hypopg_reset()** to clear the effects of any other hypothetical indexes.
|
||||
|
||||
As a simple case, let's consider two indexes:
|
||||
|
||||
.. code-block:: psql
|
||||
|
||||
SELECT hypopg_reset();
|
||||
CREATE INDEX ON hypo(id);
|
||||
CREATE INDEX ON hypo(id, val);
|
||||
|
||||
.. code-block:: psql
|
||||
:emphasize-lines: 4
|
||||
|
||||
EXPLAIN SELECT * FROM hypo WHERE id = 1;
|
||||
QUERY PLAN
|
||||
----------------------------------------------------------------------------------
|
||||
Index Only Scan using hypo_id_val_idx on hypo (cost=0.29..8.30 rows=1 width=13)
|
||||
Index Cond: (id = 1)
|
||||
(2 rows)
|
||||
|
||||
The query plan is using the **hypo_id_val_idx** index now.
|
||||
|
||||
- **hypopg_hide_index(oid)**: function that allows you to hide an index in the EXPLAIN output by using its OID.
|
||||
It returns `true` if the index was successfully hidden, and `false` otherwise.
|
||||
|
||||
.. code-block:: psql
|
||||
:emphasize-lines: 10
|
||||
|
||||
SELECT hypopg_hide_index('hypo_id_val_idx'::REGCLASS);
|
||||
hypopg_hide_index
|
||||
-------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
EXPLAIN SELECT * FROM hypo WHERE id = 1;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------------------
|
||||
Index Scan using hypo_id_idx on hypo (cost=0.29..8.30 rows=1 width=13)
|
||||
Index Cond: (id = 1)
|
||||
(2 rows)
|
||||
|
||||
As an example, let's assume that the query plan is currently using the **hypo_id_val_idx** index.
|
||||
To continue testing, use the **hypopg_hide_index(oid)** function to hide another index.
|
||||
|
||||
.. code-block:: psql
|
||||
:emphasize-lines: 10
|
||||
|
||||
SELECT hypopg_hide_index('hypo_id_idx'::REGCLASS);
|
||||
hypopg_hide_index
|
||||
-------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
EXPLAIN SELECT * FROM hypo WHERE id = 1;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------
|
||||
Seq Scan on hypo (cost=0.00..180.00 rows=1 width=13)
|
||||
Filter: (id = 1)
|
||||
(2 rows)
|
||||
|
||||
- **hypopg_unhide_index(oid)**: function that restore a previously hidden index in the EXPLAIN output by using its OID.
|
||||
It returns `true` if the index was successfully restored, and `false` otherwise.
|
||||
|
||||
.. code-block:: psql
|
||||
:emphasize-lines: 10
|
||||
|
||||
SELECT hypopg_unhide_index('hypo_id_idx'::regclass);
|
||||
hypopg_unhide_index
|
||||
-------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
EXPLAIN SELECT * FROM hypo WHERE id = 1;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------------------
|
||||
Index Scan using hypo_id_idx on hypo (cost=0.29..8.30 rows=1 width=13)
|
||||
Index Cond: (id = 1)
|
||||
(2 rows)
|
||||
|
||||
- **hypopg_unhide_all_index()**: function that restore all hidden indexes and returns void.
|
||||
|
||||
- **hypopg_hidden_indexes()**: function that returns a list of OIDs for all hidden indexes.
|
||||
|
||||
.. code-block:: psql
|
||||
|
||||
SELECT * FROM hypopg_hidden_indexes();
|
||||
indexid
|
||||
---------
|
||||
526604
|
||||
(1 rows)
|
||||
|
||||
- **hypopg_hidden_indexes**: view that returns a formatted list of all hidden indexes.
|
||||
|
||||
.. code-block:: psql
|
||||
|
||||
SELECT * FROM hypopg_hidden_indexes;
|
||||
indexrelid | index_name | schema_name | table_name | am_name | is_hypo
|
||||
-------------+----------------------+-------------+------------+---------+---------
|
||||
526604 | hypo_id_val_idx | public | hypo | btree | f
|
||||
(1 rows)
|
||||
|
||||
.. note::
|
||||
|
||||
Hypothetical indexes can be hidden as well.
|
||||
|
||||
.. code-block:: psql
|
||||
:emphasize-lines: 10
|
||||
|
||||
SELECT hypopg_create_index('CREATE INDEX ON hypo(id)');
|
||||
hypopg_create_index
|
||||
------------------------------
|
||||
(12659,<12659>btree_hypo_id)
|
||||
(1 row)
|
||||
|
||||
EXPLAIN SELECT * FROM hypo WHERE id = 1;
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------------------------------
|
||||
Index Scan using "<12659>btree_hypo_id" on hypo (cost=0.04..8.05 rows=1 width=13)
|
||||
Index Cond: (id = 1)
|
||||
(2 rows)
|
||||
|
||||
Now that the hypothetical index is being used, we can try hiding it to see the change:
|
||||
|
||||
.. code-block:: psql
|
||||
:emphasize-lines: 10
|
||||
|
||||
SELECT hypopg_hide_index(12659);
|
||||
hypopg_hide_index
|
||||
-------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
EXPLAIN SELECT * FROM hypo WHERE id = 1;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------------------
|
||||
Index Scan using hypo_id_idx on hypo (cost=0.29..8.30 rows=1 width=13)
|
||||
Index Cond: (id = 1)
|
||||
(2 rows)
|
||||
|
||||
SELECT * FROM hypopg_hidden_indexes;
|
||||
indexrelid | index_name | schema_name | table_name | am_name | is_hypo
|
||||
-------------+----------------------+-------------+------------+---------+---------
|
||||
12659 | <12659>btree_hypo_id | public | hypo | btree | t
|
||||
526604 | hypo_id_val_idx | public | hypo | btree | f
|
||||
(2 rows)
|
||||
|
||||
.. note::
|
||||
|
||||
If a hypothetical index has been hidden, it will be automatically unhidden
|
||||
when it is deleted using **hypopg_drop_index(oid)** or **hypopg_reset()**.
|
||||
|
||||
.. code-block:: psql
|
||||
|
||||
SELECT hypopg_drop_index(12659);
|
||||
|
||||
SELECT * FROM hypopg_hidden_indexes;
|
||||
indexrelid | index_name | schema_name | table_name | am_name | is_hypo
|
||||
-------------+----------------------+-------------+------------+---------+---------
|
||||
526604 | hypo_id_val_idx | public | hypo | btree | f
|
||||
(2 rows)
|
||||
268
expected/hypo_hide_index.out
Normal file
268
expected/hypo_hide_index.out
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
-- Hypothetically hiding existing indexes tests
|
||||
-- Remove all the hypothetical indexes if any
|
||||
SELECT hypopg_reset();
|
||||
hypopg_reset
|
||||
--------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- The EXPLAIN initial state
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'hypo_id_idx';
|
||||
count
|
||||
-------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
-- Create real index in hypo and use this index
|
||||
CREATE INDEX hypo_id_idx ON hypo(id);
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'hypo_id_idx';
|
||||
count
|
||||
-------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
-- Should be zero
|
||||
SELECT COUNT(*) FROM hypopg_hidden_indexes();
|
||||
count
|
||||
-------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
-- The hypo_id_idx index should not be used
|
||||
SELECT hypopg_hide_index('hypo_id_idx'::regclass);
|
||||
hypopg_hide_index
|
||||
-------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'hypo_id_idx';
|
||||
count
|
||||
-------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
-- Should be only one record
|
||||
SELECT COUNT(*) FROM hypopg_hidden_indexes();
|
||||
count
|
||||
-------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT table_name,index_name FROM hypopg_hidden_indexes;
|
||||
table_name | index_name
|
||||
------------+-------------
|
||||
hypo | hypo_id_idx
|
||||
(1 row)
|
||||
|
||||
-- Create the real index again and
|
||||
-- EXPLAIN should use this index instead of the previous one
|
||||
CREATE index hypo_id_val_idx ON hypo(id, val);
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'hypo_id_val_idx';
|
||||
count
|
||||
-------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
-- Shouldn't use any index
|
||||
SELECT hypopg_hide_index('hypo_id_val_idx'::regclass);
|
||||
hypopg_hide_index
|
||||
-------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'hypo_id_val_idx';
|
||||
count
|
||||
-------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
-- Should be two records
|
||||
SELECT table_name,index_name FROM hypopg_hidden_indexes;
|
||||
table_name | index_name
|
||||
------------+-----------------
|
||||
hypo | hypo_id_idx
|
||||
hypo | hypo_id_val_idx
|
||||
(2 rows)
|
||||
|
||||
-- Try to add one repeatedly or add another wrong index oid
|
||||
SELECT hypopg_hide_index('hypo_id_idx'::regclass);
|
||||
hypopg_hide_index
|
||||
-------------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT hypopg_hide_index('hypo'::regclass);
|
||||
hypopg_hide_index
|
||||
-------------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT hypopg_hide_index(0);
|
||||
hypopg_hide_index
|
||||
-------------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
-- Also of course can be used to hide hypothetical indexes
|
||||
SELECT COUNT(*) FROM hypopg_create_index('create index on hypo(id,val);');
|
||||
count
|
||||
-------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'Index.*<\d+>btree_hypo.*';
|
||||
count
|
||||
-------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT hypopg_hide_index((SELECT indexrelid FROM hypopg_list_indexes LIMIT 1));
|
||||
hypopg_hide_index
|
||||
-------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'Index.*<\d+>btree_hypo.*';
|
||||
count
|
||||
-------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
-- Should be only three records
|
||||
SELECT COUNT(*) FROM hypopg_hidden_indexes;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
-- Hypothetical indexes should be unhidden when deleting
|
||||
SELECT hypopg_drop_index((SELECT indexrelid FROM hypopg_list_indexes LIMIT 1));
|
||||
hypopg_drop_index
|
||||
-------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
-- Should become two records
|
||||
SELECT COUNT(*) FROM hypopg_hidden_indexes;
|
||||
count
|
||||
-------
|
||||
2
|
||||
(1 row)
|
||||
|
||||
-- Hypopg_reset can also unhidden the hidden indexes
|
||||
-- due to the deletion of hypothetical indexes.
|
||||
SELECT COUNT(*) FROM hypopg_create_index('create index on hypo(id,val);');
|
||||
count
|
||||
-------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'Index.*<\d+>btree_hypo.*';
|
||||
count
|
||||
-------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT hypopg_hide_index((SELECT indexrelid FROM hypopg_list_indexes LIMIT 1));
|
||||
hypopg_hide_index
|
||||
-------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
-- Changed from three records to two records.
|
||||
SELECT COUNT(*) FROM hypopg_hidden_indexes;
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT hypopg_reset();
|
||||
hypopg_reset
|
||||
--------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM hypopg_hidden_indexes;
|
||||
count
|
||||
-------
|
||||
2
|
||||
(1 row)
|
||||
|
||||
-- Unhide an index
|
||||
SELECT hypopg_unhide_index('hypo_id_idx'::regclass);
|
||||
hypopg_unhide_index
|
||||
---------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'hypo_id_idx';
|
||||
count
|
||||
-------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
-- Should become one record
|
||||
SELECT table_name,index_name FROM hypopg_hidden_indexes;
|
||||
table_name | index_name
|
||||
------------+-----------------
|
||||
hypo | hypo_id_val_idx
|
||||
(1 row)
|
||||
|
||||
-- Try to delete one repeatedly or delete another wrong index oid
|
||||
SELECT hypopg_unhide_index('hypo_id_idx'::regclass);
|
||||
hypopg_unhide_index
|
||||
---------------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT hypopg_unhide_index('hypo'::regclass);
|
||||
hypopg_unhide_index
|
||||
---------------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT hypopg_unhide_index(0);
|
||||
hypopg_unhide_index
|
||||
---------------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
-- Should still have one record
|
||||
SELECT table_name,index_name FROM hypopg_hidden_indexes;
|
||||
table_name | index_name
|
||||
------------+-----------------
|
||||
hypo | hypo_id_val_idx
|
||||
(1 row)
|
||||
|
||||
-- Unhide all indexes
|
||||
SELECT hypopg_unhide_all_indexes();
|
||||
hypopg_unhide_all_indexes
|
||||
---------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- Should change back to the original zero
|
||||
SELECT COUNT(*) FROM hypopg_hidden_indexes();
|
||||
count
|
||||
-------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
-- Clean real indexes and hypothetical indexes
|
||||
DROP INDEX hypo_id_idx;
|
||||
DROP INDEX hypo_id_val_idx;
|
||||
SELECT hypopg_reset();
|
||||
hypopg_reset
|
||||
--------------
|
||||
|
||||
(1 row)
|
||||
|
||||
|
|
@ -5,3 +5,46 @@
|
|||
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "ALTER EXTENSION hypopg" to load this file. \quit
|
||||
|
||||
CREATE FUNCTION
|
||||
hypopg_hide_index(IN indexid oid)
|
||||
RETURNS bool
|
||||
LANGUAGE C STRICT VOLATILE COST 100
|
||||
AS '$libdir/hypopg', 'hypopg_hide_index';
|
||||
|
||||
CREATE FUNCTION
|
||||
hypopg_unhide_index(IN indexid oid)
|
||||
RETURNS bool
|
||||
LANGUAGE C STRICT VOLATILE COST 100
|
||||
AS '$libdir/hypopg', 'hypopg_unhide_index';
|
||||
|
||||
CREATE FUNCTION
|
||||
hypopg_unhide_all_indexes()
|
||||
RETURNS void
|
||||
LANGUAGE C VOLATILE COST 100
|
||||
AS '$libdir/hypopg', 'hypopg_unhide_all_indexes';
|
||||
|
||||
CREATE FUNCTION hypopg_hidden_indexes()
|
||||
RETURNS TABLE (indexid oid)
|
||||
LANGUAGE C STRICT VOLATILE
|
||||
AS '$libdir/hypopg', 'hypopg_hidden_indexes';
|
||||
|
||||
CREATE VIEW hypopg_hidden_indexes
|
||||
AS
|
||||
SELECT h.indexid AS indexrelid,
|
||||
i.relname AS index_name,
|
||||
n.nspname AS schema_name,
|
||||
t.relname AS table_name,
|
||||
m.amname AS am_name,
|
||||
false AS is_hypo
|
||||
FROM hypopg_hidden_indexes() h
|
||||
JOIN pg_index x ON x.indexrelid = h.indexid
|
||||
JOIN pg_class i ON i.oid = h.indexid
|
||||
JOIN pg_namespace n ON n.oid = i.relnamespace
|
||||
JOIN pg_class t ON t.oid = x.indrelid
|
||||
JOIN pg_am m ON m.oid = i.relam
|
||||
UNION ALL
|
||||
SELECT hl.*, true AS is_hypo
|
||||
FROM hypopg_hidden_indexes() hi
|
||||
JOIN hypopg_list_indexes hl on hl.indexrelid = hi.indexid
|
||||
ORDER BY index_name;
|
||||
|
|
@ -60,3 +60,46 @@ hypopg_get_indexdef(IN indexid oid)
|
|||
RETURNS text
|
||||
LANGUAGE C STRICT VOLATILE COST 100
|
||||
AS '$libdir/hypopg', 'hypopg_get_indexdef';
|
||||
|
||||
CREATE FUNCTION
|
||||
hypopg_hide_index(IN indexid oid)
|
||||
RETURNS bool
|
||||
LANGUAGE C STRICT VOLATILE COST 100
|
||||
AS '$libdir/hypopg', 'hypopg_hide_index';
|
||||
|
||||
CREATE FUNCTION
|
||||
hypopg_unhide_index(IN indexid oid)
|
||||
RETURNS bool
|
||||
LANGUAGE C STRICT VOLATILE COST 100
|
||||
AS '$libdir/hypopg', 'hypopg_unhide_index';
|
||||
|
||||
CREATE FUNCTION
|
||||
hypopg_unhide_all_indexes()
|
||||
RETURNS void
|
||||
LANGUAGE C VOLATILE COST 100
|
||||
AS '$libdir/hypopg', 'hypopg_unhide_all_indexes';
|
||||
|
||||
CREATE FUNCTION hypopg_hidden_indexes()
|
||||
RETURNS TABLE (indexid oid)
|
||||
LANGUAGE C STRICT VOLATILE
|
||||
AS '$libdir/hypopg', 'hypopg_hidden_indexes';
|
||||
|
||||
CREATE VIEW hypopg_hidden_indexes
|
||||
AS
|
||||
SELECT h.indexid AS indexrelid,
|
||||
i.relname AS index_name,
|
||||
n.nspname AS schema_name,
|
||||
t.relname AS table_name,
|
||||
m.amname AS am_name,
|
||||
false AS is_hypo
|
||||
FROM hypopg_hidden_indexes() h
|
||||
JOIN pg_index x ON x.indexrelid = h.indexid
|
||||
JOIN pg_class i ON i.oid = h.indexid
|
||||
JOIN pg_namespace n ON n.oid = i.relnamespace
|
||||
JOIN pg_class t ON t.oid = x.indrelid
|
||||
JOIN pg_am m ON m.oid = i.relam
|
||||
UNION ALL
|
||||
SELECT hl.*, true AS is_hypo
|
||||
FROM hypopg_hidden_indexes() hi
|
||||
JOIN hypopg_list_indexes hl on hl.indexrelid = hi.indexid
|
||||
ORDER BY index_name;
|
||||
3
hypopg.c
3
hypopg.c
|
|
@ -122,6 +122,7 @@ _PG_init(void)
|
|||
|
||||
isExplain = false;
|
||||
hypoIndexes = NIL;
|
||||
hypoHiddenIndexes = NIL;
|
||||
|
||||
HypoMemoryContext = AllocSetContextCreate(TopMemoryContext,
|
||||
"HypoPG context",
|
||||
|
|
@ -528,6 +529,8 @@ hypo_get_relation_info_hook(PlannerInfo *root,
|
|||
inhparent, rel, relation, entry);
|
||||
}
|
||||
}
|
||||
|
||||
hypo_hideIndexes(rel);
|
||||
}
|
||||
|
||||
/* Close the relation release the lock now */
|
||||
|
|
|
|||
189
hypopg_index.c
189
hypopg_index.c
|
|
@ -81,6 +81,7 @@ static Oid BLOOM_AM_OID = InvalidOid;
|
|||
|
||||
explain_get_index_name_hook_type prev_explain_get_index_name_hook;
|
||||
List *hypoIndexes;
|
||||
List *hypoHiddenIndexes;
|
||||
|
||||
/*--- Functions --- */
|
||||
|
||||
|
|
@ -90,6 +91,10 @@ PG_FUNCTION_INFO_V1(hypopg_drop_index);
|
|||
PG_FUNCTION_INFO_V1(hypopg_relation_size);
|
||||
PG_FUNCTION_INFO_V1(hypopg_get_indexdef);
|
||||
PG_FUNCTION_INFO_V1(hypopg_reset_index);
|
||||
PG_FUNCTION_INFO_V1(hypopg_hide_index);
|
||||
PG_FUNCTION_INFO_V1(hypopg_unhide_index);
|
||||
PG_FUNCTION_INFO_V1(hypopg_unhide_all_indexes);
|
||||
PG_FUNCTION_INFO_V1(hypopg_hidden_indexes);
|
||||
|
||||
|
||||
static void hypo_addIndex(hypoIndex * entry);
|
||||
|
|
@ -101,6 +106,7 @@ static void hypo_estimate_index(hypoIndex * entry, RelOptInfo *rel);
|
|||
static int hypo_estimate_index_colsize(hypoIndex * entry, int col);
|
||||
static void hypo_index_pfree(hypoIndex * entry);
|
||||
static bool hypo_index_remove(Oid indexid);
|
||||
static bool hypo_index_unhide(Oid indexid);
|
||||
static const hypoIndex *hypo_index_store_parsetree(IndexStmt *node,
|
||||
const char *queryString);
|
||||
static hypoIndex * hypo_newIndex(Oid relid, char *accessMethod, int nkeycolumns,
|
||||
|
|
@ -922,6 +928,9 @@ hypo_index_remove(Oid indexid)
|
|||
{
|
||||
ListCell *lc;
|
||||
|
||||
/* remove this index from the list of hidden indexes if present */
|
||||
hypo_index_unhide(indexid);
|
||||
|
||||
foreach(lc, hypoIndexes)
|
||||
{
|
||||
hypoIndex *entry = (hypoIndex *) lfirst(lc);
|
||||
|
|
@ -933,6 +942,7 @@ hypo_index_remove(Oid indexid)
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1592,6 +1602,185 @@ hypopg_reset_index(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the given oid for the list of hidden indexes
|
||||
* if it's a valid index (hypothetical or real), and if not hidden already.
|
||||
* Return true if the oid is added to the list, false otherwise.
|
||||
*/
|
||||
Datum
|
||||
hypopg_hide_index(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid indexid = PG_GETARG_OID(0);
|
||||
MemoryContext old_context;
|
||||
bool is_hypo = false;
|
||||
ListCell *lc;
|
||||
|
||||
/* first check if it is in hypoIndexes */
|
||||
foreach(lc, hypoIndexes)
|
||||
{
|
||||
hypoIndex *entry = (hypoIndex *) lfirst(lc);
|
||||
|
||||
if (entry->oid == indexid)
|
||||
{
|
||||
is_hypo = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_hypo)
|
||||
{
|
||||
HeapTuple index_tup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexid));
|
||||
|
||||
if (!HeapTupleIsValid(index_tup))
|
||||
return false;
|
||||
|
||||
ReleaseSysCache(index_tup);
|
||||
}
|
||||
|
||||
if (list_member_oid(hypoHiddenIndexes, indexid))
|
||||
return false;
|
||||
|
||||
old_context = MemoryContextSwitchTo(HypoMemoryContext);
|
||||
hypoHiddenIndexes = lappend_oid(hypoHiddenIndexes, indexid);
|
||||
MemoryContextSwitchTo(old_context);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unhide the given index oid (hypothetical or not) to make it visible to
|
||||
* the planner again.
|
||||
*/
|
||||
Datum
|
||||
hypopg_unhide_index(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid indexid = PG_GETARG_OID(0);
|
||||
|
||||
PG_RETURN_BOOL(hypo_index_unhide(indexid));
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore all hidden index.
|
||||
*/
|
||||
Datum
|
||||
hypopg_unhide_all_indexes(PG_FUNCTION_ARGS)
|
||||
{
|
||||
list_free(hypoHiddenIndexes);
|
||||
hypoHiddenIndexes = NIL;
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/*
|
||||
* Get all hidden index oid.
|
||||
*/
|
||||
Datum
|
||||
hypopg_hidden_indexes(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||
MemoryContext oldcontext;
|
||||
TupleDesc tupdesc;
|
||||
Tuplestorestate *tupstore;
|
||||
ListCell *lc;
|
||||
|
||||
/* check to see if caller supports us returning a tuplestore */
|
||||
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("set-valued function called in context that cannot accept a set")));
|
||||
if (!(rsinfo->allowedModes & SFRM_Materialize))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("materialize mode required, but it is not " \
|
||||
"allowed in this context")));
|
||||
|
||||
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
|
||||
|
||||
tupdesc = CreateTemplateTupleDesc(1);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "indexid", OIDOID, -1, 0);
|
||||
|
||||
tupstore = tuplestore_begin_heap(true, false, work_mem);
|
||||
rsinfo->returnMode = SFRM_Materialize;
|
||||
rsinfo->setResult = tupstore;
|
||||
rsinfo->setDesc = tupdesc;
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
foreach(lc, hypoHiddenIndexes)
|
||||
{
|
||||
Oid indexid = lfirst_oid(lc);
|
||||
Datum values[HYPO_HIDDEN_INDEX_COLS];
|
||||
bool nulls[HYPO_HIDDEN_INDEX_COLS];
|
||||
|
||||
memset(values, 0, sizeof(values));
|
||||
memset(nulls, 0, sizeof(nulls));
|
||||
|
||||
values[0] = ObjectIdGetDatum(indexid);
|
||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
||||
}
|
||||
|
||||
/* clean up and return the tuplestore */
|
||||
tuplestore_donestoring(tupstore);
|
||||
|
||||
return (Datum) 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the oid to restore this index on EXPLAIN.
|
||||
*/
|
||||
bool
|
||||
hypo_index_unhide(Oid indexid)
|
||||
{
|
||||
int prev_length = list_length(hypoHiddenIndexes);
|
||||
|
||||
hypoHiddenIndexes = list_delete_oid(hypoHiddenIndexes, indexid);
|
||||
return prev_length > list_length(hypoHiddenIndexes);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check rel and delete the same oid index as hypoHiddenIndexes
|
||||
* in rel->indexlist.
|
||||
*/
|
||||
void
|
||||
hypo_hideIndexes(RelOptInfo *rel)
|
||||
{
|
||||
ListCell *cell = NULL;
|
||||
|
||||
if (rel == NULL)
|
||||
return;
|
||||
|
||||
if (list_length(rel->indexlist) == 0 || list_length(hypoHiddenIndexes) == 0)
|
||||
return;
|
||||
|
||||
foreach(cell, hypoHiddenIndexes)
|
||||
{
|
||||
Oid oid = lfirst_oid(cell);
|
||||
ListCell *lc = NULL;
|
||||
|
||||
#if PG_VERSION_NUM >= 130000
|
||||
foreach(lc, rel->indexlist)
|
||||
{
|
||||
IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
|
||||
|
||||
if (index->indexoid == oid)
|
||||
rel->indexlist = foreach_delete_current(rel->indexlist, lc);
|
||||
}
|
||||
#else
|
||||
ListCell *next;
|
||||
ListCell *prev = NULL;
|
||||
|
||||
for (lc = list_head(rel->indexlist); lc != NULL; lc = next)
|
||||
{
|
||||
IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
|
||||
|
||||
next = lnext(lc);
|
||||
if (index->indexoid == oid)
|
||||
rel->indexlist = list_delete_cell(rel->indexlist, lc, prev);
|
||||
else
|
||||
prev = lc;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Simple function to set the indexname, dealing with max name length, and the
|
||||
* ending \0
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
#define HYPO_INDEX_NB_COLS 12 /* # of column hypopg() returns */
|
||||
#define HYPO_INDEX_CREATE_COLS 2 /* # of column hypopg_create_index()
|
||||
* returns */
|
||||
#define HYPO_HIDDEN_INDEX_COLS 1 /* # of column hypopg_hidden_indexes()
|
||||
* returns */
|
||||
|
||||
#if PG_VERSION_NUM >= 90600
|
||||
/* hardcode some bloom values, bloom.h is not exported */
|
||||
|
|
@ -110,6 +112,9 @@ typedef struct hypoIndex
|
|||
/* List of hypothetic indexes for current backend */
|
||||
extern List *hypoIndexes;
|
||||
|
||||
/* List of hypothetical hidden existing indexes for current backend */
|
||||
extern List *hypoHiddenIndexes;
|
||||
|
||||
/*--- Functions --- */
|
||||
|
||||
void hypo_index_reset(void);
|
||||
|
|
@ -120,6 +125,10 @@ PGDLLEXPORT Datum hypopg_drop_index(PG_FUNCTION_ARGS);
|
|||
PGDLLEXPORT Datum hypopg_relation_size(PG_FUNCTION_ARGS);
|
||||
PGDLLEXPORT Datum hypopg_get_indexdef(PG_FUNCTION_ARGS);
|
||||
PGDLLEXPORT Datum hypopg_reset_index(PG_FUNCTION_ARGS);
|
||||
PGDLLEXPORT Datum hypopg_hide_index(PG_FUNCTION_ARGS);
|
||||
PGDLLEXPORT Datum hypopg_unhide_index(PG_FUNCTION_ARGS);
|
||||
PGDLLEXPORT Datum hypopg_unhide_all_indexes(PG_FUNCTION_ARGS);
|
||||
PGDLLEXPORT Datum hypopg_hidden_indexes(PG_FUNCTION_ARGS);
|
||||
|
||||
extern explain_get_index_name_hook_type prev_explain_get_index_name_hook;
|
||||
hypoIndex *hypo_get_index(Oid indexId);
|
||||
|
|
@ -131,5 +140,6 @@ void hypo_injectHypotheticalIndex(PlannerInfo *root,
|
|||
RelOptInfo *rel,
|
||||
Relation relation,
|
||||
hypoIndex * entry);
|
||||
void hypo_hideIndexes(RelOptInfo *rel);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
100
test/sql/hypo_hide_index.sql
Normal file
100
test/sql/hypo_hide_index.sql
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
-- Hypothetically hiding existing indexes tests
|
||||
|
||||
-- Remove all the hypothetical indexes if any
|
||||
SELECT hypopg_reset();
|
||||
|
||||
-- The EXPLAIN initial state
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'hypo_id_idx';
|
||||
|
||||
-- Create real index in hypo and use this index
|
||||
CREATE INDEX hypo_id_idx ON hypo(id);
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'hypo_id_idx';
|
||||
|
||||
-- Should be zero
|
||||
SELECT COUNT(*) FROM hypopg_hidden_indexes();
|
||||
|
||||
-- The hypo_id_idx index should not be used
|
||||
SELECT hypopg_hide_index('hypo_id_idx'::regclass);
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'hypo_id_idx';
|
||||
|
||||
-- Should be only one record
|
||||
SELECT COUNT(*) FROM hypopg_hidden_indexes();
|
||||
SELECT table_name,index_name FROM hypopg_hidden_indexes;
|
||||
|
||||
-- Create the real index again and
|
||||
-- EXPLAIN should use this index instead of the previous one
|
||||
CREATE index hypo_id_val_idx ON hypo(id, val);
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'hypo_id_val_idx';
|
||||
|
||||
-- Shouldn't use any index
|
||||
SELECT hypopg_hide_index('hypo_id_val_idx'::regclass);
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'hypo_id_val_idx';
|
||||
|
||||
-- Should be two records
|
||||
SELECT table_name,index_name FROM hypopg_hidden_indexes;
|
||||
|
||||
-- Try to add one repeatedly or add another wrong index oid
|
||||
SELECT hypopg_hide_index('hypo_id_idx'::regclass);
|
||||
SELECT hypopg_hide_index('hypo'::regclass);
|
||||
SELECT hypopg_hide_index(0);
|
||||
|
||||
-- Also of course can be used to hide hypothetical indexes
|
||||
SELECT COUNT(*) FROM hypopg_create_index('create index on hypo(id,val);');
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'Index.*<\d+>btree_hypo.*';
|
||||
SELECT hypopg_hide_index((SELECT indexrelid FROM hypopg_list_indexes LIMIT 1));
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'Index.*<\d+>btree_hypo.*';
|
||||
|
||||
-- Should be only three records
|
||||
SELECT COUNT(*) FROM hypopg_hidden_indexes;
|
||||
|
||||
-- Hypothetical indexes should be unhidden when deleting
|
||||
SELECT hypopg_drop_index((SELECT indexrelid FROM hypopg_list_indexes LIMIT 1));
|
||||
|
||||
-- Should become two records
|
||||
SELECT COUNT(*) FROM hypopg_hidden_indexes;
|
||||
|
||||
-- Hypopg_reset can also unhidden the hidden indexes
|
||||
-- due to the deletion of hypothetical indexes.
|
||||
SELECT COUNT(*) FROM hypopg_create_index('create index on hypo(id,val);');
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'Index.*<\d+>btree_hypo.*';
|
||||
SELECT hypopg_hide_index((SELECT indexrelid FROM hypopg_list_indexes LIMIT 1));
|
||||
|
||||
-- Changed from three records to two records.
|
||||
SELECT COUNT(*) FROM hypopg_hidden_indexes;
|
||||
SELECT hypopg_reset();
|
||||
SELECT COUNT(*) FROM hypopg_hidden_indexes;
|
||||
|
||||
-- Unhide an index
|
||||
SELECT hypopg_unhide_index('hypo_id_idx'::regclass);
|
||||
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE id = 1') e
|
||||
WHERE e ~ 'hypo_id_idx';
|
||||
|
||||
-- Should become one record
|
||||
SELECT table_name,index_name FROM hypopg_hidden_indexes;
|
||||
|
||||
-- Try to delete one repeatedly or delete another wrong index oid
|
||||
SELECT hypopg_unhide_index('hypo_id_idx'::regclass);
|
||||
SELECT hypopg_unhide_index('hypo'::regclass);
|
||||
SELECT hypopg_unhide_index(0);
|
||||
|
||||
-- Should still have one record
|
||||
SELECT table_name,index_name FROM hypopg_hidden_indexes;
|
||||
|
||||
-- Unhide all indexes
|
||||
SELECT hypopg_unhide_all_indexes();
|
||||
|
||||
-- Should change back to the original zero
|
||||
SELECT COUNT(*) FROM hypopg_hidden_indexes();
|
||||
|
||||
-- Clean real indexes and hypothetical indexes
|
||||
DROP INDEX hypo_id_idx;
|
||||
DROP INDEX hypo_id_val_idx;
|
||||
SELECT hypopg_reset();
|
||||
Loading…
Reference in a new issue