From 5acf42e15c0dc8b185547ff9cb9371a86a057ec9 Mon Sep 17 00:00:00 2001 From: Julien Rouhaud Date: Thu, 3 Dec 2020 15:54:42 +0800 Subject: [PATCH v1] Add a new COLLATION option to REINDEX. --- doc/src/sgml/ref/reindex.sgml | 13 +++++ src/backend/catalog/index.c | 59 +++++++++++++++++++++- src/backend/commands/indexcmds.c | 12 +++-- src/backend/utils/cache/relcache.c | 43 ++++++++++++++++ src/include/catalog/index.h | 6 ++- src/include/utils/relcache.h | 1 + src/test/regress/expected/create_index.out | 10 ++++ src/test/regress/sql/create_index.sql | 10 ++++ 8 files changed, 149 insertions(+), 5 deletions(-) diff --git a/doc/src/sgml/ref/reindex.sgml b/doc/src/sgml/ref/reindex.sgml index 6e1cf06713..eb8da9c070 100644 --- a/doc/src/sgml/ref/reindex.sgml +++ b/doc/src/sgml/ref/reindex.sgml @@ -25,6 +25,7 @@ REINDEX [ ( option [, ...] ) ] { IN where option can be one of: + COLLATION [ text ] CONCURRENTLY [ boolean ] VERBOSE [ boolean ] @@ -168,6 +169,18 @@ REINDEX [ ( option [, ...] ) ] { IN + + COLLATION + + + This option can be used to filter the list of indexes to rebuild. The + only allowed value is 'not_current', which will only + process indexes that depend on a collation version different than the + current one. + + + + CONCURRENTLY diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 731610c701..7d941f40af 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -99,6 +99,12 @@ typedef struct Oid pendingReindexedIndexes[FLEXIBLE_ARRAY_MEMBER]; } SerializedReindexState; +typedef struct +{ + Oid relid; /* targetr index oid */ + bool deprecated; /* depends on at least on deprected collation? */ +} IndexHasDeprecatedColl; + /* non-export function prototypes */ static bool relationHasPrimaryKey(Relation rel); static TupleDesc ConstructTupleDescriptor(Relation heapRelation, @@ -1349,6 +1355,57 @@ index_check_collation_versions(Oid relid) list_free(context.warned_colls); } +/* + * Detect if an index depends on at least one deprecated collation. + * This is a callback for visitDependenciesOf(). + */ +static bool +do_check_index_has_deprecated_collation(const ObjectAddress *otherObject, + const char *version, + char **new_version, + void *data) +{ + IndexHasDeprecatedColl *context = data; + char *current_version; + + /* We only care about dependencies on collations. */ + if (otherObject->classId != CollationRelationId) + return false; + + /* Fast exit if we already found a deprecated collation version. */ + if (context->deprecated) + return false; + + /* Ask the provider for the current version. Give up if unsupported. */ + current_version = get_collation_version_for_oid(otherObject->objectId); + if (!current_version) + return false; + + if (!version || strcmp(version, current_version) != 0) + context->deprecated = true; + + return false; +} + +bool +index_has_deprecated_collation(Oid relid) +{ + ObjectAddress object; + IndexHasDeprecatedColl context; + + object.classId = RelationRelationId; + object.objectId = relid; + object.objectSubId = 0; + + context.relid = relid; + context.deprecated = false; + + visitDependenciesOf(&object, &do_check_index_has_deprecated_collation, + &context); + + return context.deprecated; +} + /* * Update the version for collations. A callback for visitDependenciesOf(). */ @@ -3886,7 +3943,7 @@ reindex_relation(Oid relid, int flags, int options) * relcache to get this with a sequential scan if ignoring system * indexes.) */ - indexIds = RelationGetIndexList(rel); + indexIds = RelationGetIndexListFiltered(rel, options); if (flags & REINDEX_REL_SUPPRESS_INDEX_USE) { diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 14d24b3cc4..2477ad6c74 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -2462,6 +2462,7 @@ ReindexParseOptions(ParseState *pstate, ReindexStmt *stmt) int options = 0; bool concurrently = false; bool verbose = false; + bool coll_filter = false; /* Parse option list */ foreach(lc, stmt->params) @@ -2472,6 +2473,9 @@ ReindexParseOptions(ParseState *pstate, ReindexStmt *stmt) verbose = defGetBoolean(opt); else if (strcmp(opt->defname, "concurrently") == 0) concurrently = defGetBoolean(opt); + else if ((strcmp(opt->defname, "collation") == 0) + && (strcmp(defGetString(opt), "not_current") == 0)) + coll_filter = true; else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), @@ -2482,7 +2486,8 @@ ReindexParseOptions(ParseState *pstate, ReindexStmt *stmt) options = (verbose ? REINDEXOPT_VERBOSE : 0) | - (concurrently ? REINDEXOPT_CONCURRENTLY : 0); + (concurrently ? REINDEXOPT_CONCURRENTLY : 0) | + (coll_filter ? REINDEXOPT_COLL_NOT_CURRENT : 0); return options; } @@ -3150,7 +3155,7 @@ ReindexRelationConcurrently(Oid relationOid, int options) ShareUpdateExclusiveLock); /* Add all the valid indexes of relation to list */ - foreach(lc, RelationGetIndexList(heapRelation)) + foreach(lc, RelationGetIndexListFiltered(heapRelation, options)) { Oid cellOid = lfirst_oid(lc); Relation indexRelation = index_open(cellOid, @@ -3196,7 +3201,8 @@ ReindexRelationConcurrently(Oid relationOid, int options) MemoryContextSwitchTo(oldcontext); - foreach(lc2, RelationGetIndexList(toastRelation)) + foreach(lc2, RelationGetIndexListFiltered(toastRelation, + options)) { Oid cellOid = lfirst_oid(lc2); Relation indexRelation = index_open(cellOid, diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 66393becfb..9045dce2d7 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -4609,6 +4609,49 @@ RelationGetIndexList(Relation relation) return result; } +/* + * RelationGetIndexListFiltered -- get a filtered list of indexes on this + * relation. + * + * Calls RelationGetIndexList and filters out the result as required by the + * caller. The filters are cumulative, although for now the only supported + * filter is for indexes that don't depend on deprecated collation version. + */ +List * +RelationGetIndexListFiltered(Relation relation, int options) +{ + List *result, + *full_list; + ListCell *lc; + + full_list = RelationGetIndexList(relation); + + /* Fast exit if no filtering was asked, or if the list if empty. */ + if (!reindexHasFilter(options) || full_list == NIL) + return full_list; + + result = NIL; + foreach(lc, full_list) + { + Oid indexOid = lfirst_oid(lc); + + /* + * The caller wants to discard indexes that don't depend on a + * deprecated collation version. + */ + if ((options & REINDEXOPT_COLL_NOT_CURRENT) != 0) + { + if (!index_has_deprecated_collation(indexOid)) + continue; + } + + /* Index passed all filters, keep it */ + result = lappend_oid(result, indexOid); + } + + return result; +} + /* * RelationGetStatExtList * get a list of OIDs of statistics objects on this relation diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index c041628049..9f2bb60343 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -35,9 +35,12 @@ typedef enum ReindexOption REINDEXOPT_VERBOSE = 1 << 0, /* print progress info */ REINDEXOPT_REPORT_PROGRESS = 1 << 1, /* report pgstat progress */ REINDEXOPT_MISSING_OK = 1 << 2, /* skip missing relations */ - REINDEXOPT_CONCURRENTLY = 1 << 3 /* concurrent mode */ + REINDEXOPT_CONCURRENTLY = 1 << 3, /* concurrent mode */ + REINDEXOPT_COLL_NOT_CURRENT = 1 << 4 } ReindexOption; +#define reindexHasFilter(x) ((x & REINDEXOPT_COLL_NOT_CURRENT) != 0) + /* state info for validate_index bulkdelete callback */ typedef struct ValidateIndexState { @@ -131,6 +134,7 @@ extern void FormIndexDatum(IndexInfo *indexInfo, bool *isnull); extern void index_check_collation_versions(Oid relid); +extern bool index_has_deprecated_collation(Oid relid); extern void index_update_collation_versions(Oid relid, Oid coll); extern void index_build(Relation heapRelation, diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index 9a85b7dd57..4ef02c9851 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -45,6 +45,7 @@ extern void RelationClose(Relation relation); */ extern List *RelationGetFKeyList(Relation relation); extern List *RelationGetIndexList(Relation relation); +extern List *RelationGetIndexListFiltered(Relation relation, int options); extern List *RelationGetStatExtList(Relation relation); extern Oid RelationGetPrimaryKeyIndex(Relation relation); extern Oid RelationGetReplicaIndex(Relation relation); diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index fc6afab58a..39a5b91d1a 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -2018,6 +2018,16 @@ INFO: index "reindex_verbose_pkey" was reindexed \set VERBOSITY default DROP TABLE reindex_verbose; -- +-- REINDEX (COLLATION 'not_current') +-- +CREATE TABLE reindex_coll(id integer primary key); +\set VERBOSITY terse \\ -- suppress machine-dependent details +-- no suitable index should be found +REINDEX (COLLATION 'not_current') TABLE reindex_coll; +NOTICE: table "reindex_coll" has no indexes to reindex +\set VERBOSITY default +DROP TABLE reindex_coll; +-- -- REINDEX CONCURRENTLY -- CREATE TABLE concur_reindex_tab (c1 int); diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql index 824cb9f9e8..99f45c0d02 100644 --- a/src/test/regress/sql/create_index.sql +++ b/src/test/regress/sql/create_index.sql @@ -790,6 +790,16 @@ REINDEX (VERBOSE) TABLE reindex_verbose; \set VERBOSITY default DROP TABLE reindex_verbose; +-- +-- REINDEX (COLLATION 'not_current') +-- +CREATE TABLE reindex_coll(id integer primary key); +\set VERBOSITY terse \\ -- suppress machine-dependent details +-- no suitable index should be found +REINDEX (COLLATION 'not_current') TABLE reindex_coll; +\set VERBOSITY default +DROP TABLE reindex_coll; + -- -- REINDEX CONCURRENTLY -- -- 2.20.1