diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index b7bcdd9d0f..8404904710 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -3148,8 +3148,15 @@ RelationTruncateIndexes(Relation heapRelation) /* Open the index relation; use exclusive lock, just to be sure */ currentIndex = index_open(indexId, AccessExclusiveLock); - /* Fetch info needed for index_build */ - indexInfo = BuildIndexInfo(currentIndex); + /* + * Fetch info needed for index_build. Since we know there are no + * tuples that actually need indexing, we can use a dummy IndexInfo. + * This is slightly cheaper to build, but the real point is to avoid + * possibly running user-defined code in index expressions or + * predicates. We might be getting invoked during ON COMMIT + * processing, and we don't want to run any such code then. + */ + indexInfo = BuildDummyIndexInfo(currentIndex); /* * Now truncate the actual file (and discard buffers). diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 67f637de11..e9955707fa 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -2324,6 +2324,56 @@ BuildIndexInfo(Relation index) return ii; } +/* ---------------- + * BuildDummyIndexInfo + * Construct a dummy IndexInfo record for an open index + * + * This differs from the real BuildIndexInfo in that it will never run any + * user-defined code that might exist in index expressions or predicates. + * Instead of the real index expressions, we return null constants that have + * the right types/typmods/collations. Predicates and exclusion clauses are + * just ignored. This is sufficient for the purpose of truncating an index, + * since we will not need to actually evaluate the expressions or predicates; + * the only thing that's likely to be done with the data is construction of + * a tupdesc describing the index's rowtype. + * ---------------- + */ +IndexInfo * +BuildDummyIndexInfo(Relation index) +{ + IndexInfo *ii; + Form_pg_index indexStruct = index->rd_index; + int i; + int numAtts; + + /* check the number of keys, and copy attr numbers into the IndexInfo */ + numAtts = indexStruct->indnatts; + if (numAtts < 1 || numAtts > INDEX_MAX_KEYS) + elog(ERROR, "invalid indnatts %d for index %u", + numAtts, RelationGetRelid(index)); + + /* + * Create the node, using dummy index expressions, and pretending there is + * no predicate. + */ + ii = makeIndexInfo(indexStruct->indnatts, + indexStruct->indnkeyatts, + index->rd_rel->relam, + RelationGetDummyIndexExpressions(index), + NIL, + indexStruct->indisunique, + indexStruct->indisready, + false); + + /* fill in attribute numbers */ + for (i = 0; i < numAtts; i++) + ii->ii_IndexAttrNumbers[i] = indexStruct->indkey.values[i]; + + /* We ignore the exclusion constraint if any */ + + return ii; +} + /* * CompareIndexInfo * Return whether the properties of two indexes (in different tables) diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index ad1ff01b32..50f8912c13 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -4629,6 +4629,57 @@ RelationGetIndexExpressions(Relation relation) return result; } +/* + * RelationGetDummyIndexExpressions -- get dummy expressions for an index + * + * Return a list of dummy expressions (just Const nodes) with the same + * types/typmods/collations as the index's real expressions. This is + * useful in situations where we don't want to run any user-defined code. + */ +List * +RelationGetDummyIndexExpressions(Relation relation) +{ + List *result; + Datum exprsDatum; + bool isnull; + char *exprsString; + List *rawExprs; + ListCell *lc; + + /* Quick exit if there is nothing to do. */ + if (relation->rd_indextuple == NULL || + heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs, NULL)) + return NIL; + + /* Extract raw node tree(s) from index tuple. */ + exprsDatum = heap_getattr(relation->rd_indextuple, + Anum_pg_index_indexprs, + GetPgIndexDescriptor(), + &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + rawExprs = (List *) stringToNode(exprsString); + pfree(exprsString); + + /* Construct null Consts; the typlen and typbyval are arbitrary. */ + result = NIL; + foreach(lc, rawExprs) + { + Node *rawExpr = (Node *) lfirst(lc); + + result = lappend(result, + makeConst(exprType(rawExpr), + exprTypmod(rawExpr), + exprCollation(rawExpr), + 1, + (Datum) 0, + true, + true)); + } + + return result; +} + /* * RelationGetIndexPredicate -- get the index predicate for an index * diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 1113d25b2d..27d9e537d3 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -106,6 +106,8 @@ extern void index_drop(Oid indexId, bool concurrent, bool concurrent_lock_mode); extern IndexInfo *BuildIndexInfo(Relation index); +extern IndexInfo *BuildDummyIndexInfo(Relation index); + extern bool CompareIndexInfo(IndexInfo *info1, IndexInfo *info2, Oid *collations1, Oid *collations2, Oid *opfamilies1, Oid *opfamilies2, diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index 2f2ace35b0..90487b2b2e 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -48,6 +48,7 @@ extern List *RelationGetStatExtList(Relation relation); extern Oid RelationGetPrimaryKeyIndex(Relation relation); extern Oid RelationGetReplicaIndex(Relation relation); extern List *RelationGetIndexExpressions(Relation relation); +extern List *RelationGetDummyIndexExpressions(Relation relation); extern List *RelationGetIndexPredicate(Relation relation); typedef enum IndexAttrBitmapKind diff --git a/src/test/regress/expected/temp.out b/src/test/regress/expected/temp.out index b1d2ffdef3..a5b3ed34a3 100644 --- a/src/test/regress/expected/temp.out +++ b/src/test/regress/expected/temp.out @@ -49,6 +49,8 @@ LINE 1: SELECT * FROM temptest; ^ -- Test ON COMMIT DELETE ROWS CREATE TEMP TABLE temptest(col int) ON COMMIT DELETE ROWS; +-- while we're here, verify successful truncation of index with SQL function +CREATE INDEX ON temptest(bit_length('')); BEGIN; INSERT INTO temptest VALUES (1); INSERT INTO temptest VALUES (2); diff --git a/src/test/regress/sql/temp.sql b/src/test/regress/sql/temp.sql index b636b33dca..424d12b283 100644 --- a/src/test/regress/sql/temp.sql +++ b/src/test/regress/sql/temp.sql @@ -55,6 +55,9 @@ SELECT * FROM temptest; CREATE TEMP TABLE temptest(col int) ON COMMIT DELETE ROWS; +-- while we're here, verify successful truncation of index with SQL function +CREATE INDEX ON temptest(bit_length('')); + BEGIN; INSERT INTO temptest VALUES (1); INSERT INTO temptest VALUES (2);