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:
nutvii 2023-03-21 13:14:36 +08:00
parent 528070a6f5
commit 59f8a1047d
11 changed files with 910 additions and 0 deletions

View file

@ -19,3 +19,4 @@ People who contributed to hypopg:
* github user nikhil-postgres
* Xiaozhe Yao
* Krzysztof Szularz
* NutVII

View file

@ -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

View file

@ -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.

View file

@ -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)

View 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)

View file

@ -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;

View file

@ -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;

View file

@ -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 */

View file

@ -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

View file

@ -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

View 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();