From d85668b2ea0605cbfd20342a852a04057f76326b Mon Sep 17 00:00:00 2001 From: Thomas Munro Date: Mon, 24 Sep 2018 00:20:28 +1200 Subject: [PATCH 4/6] Track collation versions for CHECK constraints. Record the current version of dependent collations in pg_depend when creating CHECK constraints, and then warn if the collation versions change. Author: Thomas Munro Reviewed-by: Discussion: https://postgr.es/m/CAEepm%3D0uEQCpfq_%2BLYFBdArCe4Ot98t1aR4eYiYTe%3DyavQygiQ%40mail.gmail.com --- src/backend/catalog/dependency.c | 28 +++++++++++++++++-- src/backend/catalog/pg_constraint.c | 43 +++++++++++++++++++++++++++++ src/backend/utils/cache/relcache.c | 4 +++ src/include/catalog/pg_constraint.h | 3 ++ 4 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index c674120eaf5..f924dbaf242 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -78,6 +78,7 @@ #include "utils/fmgroids.h" #include "utils/guc.h" #include "utils/lsyscache.h" +#include "utils/pg_locale.h" #include "utils/syscache.h" #include "utils/tqual.h" @@ -1399,6 +1400,21 @@ ReleaseDeletionLock(const ObjectAddress *object) AccessExclusiveLock); } +static NameData * +capture_collation_versions(ObjectAddresses *addrs) +{ + NameData *results = palloc0(sizeof(NameData) * addrs->numrefs); + + for (int i = 0; i < addrs->numrefs; ++i) + { + if (addrs->refs[i].classId != CollationRelationId) + continue; + get_collation_version_for_oid(addrs->refs[i].objectId, &results[i]); + } + + return results; +} + /* * recordDependencyOnExpr - find expression dependencies * @@ -1418,6 +1434,7 @@ recordDependencyOnExpr(const ObjectAddress *depender, Node *expr, List *rtable, DependencyType behavior) { + NameData *versions; find_expr_references_context context; context.addrs = new_object_addresses(); @@ -1431,9 +1448,12 @@ recordDependencyOnExpr(const ObjectAddress *depender, /* Remove any duplicates */ eliminate_duplicate_dependencies(context.addrs); + versions = capture_collation_versions(context.addrs); + /* And record 'em */ recordMultipleDependencies(depender, - context.addrs->refs, NULL, + context.addrs->refs, + versions, context.addrs->numrefs, behavior); @@ -1464,6 +1484,7 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender, { find_expr_references_context context; RangeTblEntry rte; + NameData *versions; context.addrs = new_object_addresses(); @@ -1524,9 +1545,12 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender, free_object_addresses(self_addrs); } + versions = capture_collation_versions(context.addrs); + /* Record the external dependencies */ recordMultipleDependencies(depender, - context.addrs->refs, NULL, + context.addrs->refs, + versions, context.addrs->numrefs, behavior); diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index 6781b00c6e6..2972706cf4c 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -23,6 +23,7 @@ #include "catalog/indexing.h" #include "catalog/objectaccess.h" #include "catalog/partition.h" +#include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_operator.h" #include "catalog/pg_type.h" @@ -32,6 +33,7 @@ #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" +#include "utils/pg_locale.h" #include "utils/rel.h" #include "utils/syscache.h" #include "utils/tqual.h" @@ -1420,3 +1422,44 @@ check_functional_grouping(Oid relid, return false; } + +static NameData * +constraint_check_collation_version(const ObjectAddress *otherObject, + const NameData *version, + void *userdata) +{ + const char *constraint_name = (const char *) userdata; + NameData current_version; + + /* We only care about dependencies on collations. */ + if (otherObject->classId != CollationRelationId) + return NULL; + + /* Compare with the current version. */ + get_collation_version_for_oid(otherObject->objectId, ¤t_version); + if (strncmp(NameStr(*version), + NameStr(current_version), + sizeof(NameData)) != 0) + ereport(WARNING, + (errmsg("constraint \"%s\" depends on collation %u version \"%s\", but the current version is \"%s\"", + constraint_name, + otherObject->objectId, + NameStr(*version), + NameStr(current_version)), + errdetail("The constraint may be corrupted due to changes in sort order."), + errhint("Drop and recreate the constraint to avoid the risk of corruption."))); + + return NULL; +} + +void +constraint_check_collation_versions(Oid oid, const char *constraint_name) +{ + ObjectAddress object; + + object.classId = ConstraintRelationId; + object.objectId = oid; + object.objectSubId = 0; + visitDependentObjects(&object, &constraint_check_collation_version, + (void *) constraint_name); +} diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index b58d4f2af6d..c554704b771 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -4056,6 +4056,10 @@ CheckConstraintFetch(Relation relation) check[found].ccbin = MemoryContextStrdup(CacheMemoryContext, s); pfree(s); + /* Generate warnings if dependent collation versions have moved. */ + constraint_check_collation_versions(HeapTupleGetOid(htup), + NameStr(conform->conname)); + found++; } diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h index 66b3f13f74a..2126f503d35 100644 --- a/src/include/catalog/pg_constraint.h +++ b/src/include/catalog/pg_constraint.h @@ -261,4 +261,7 @@ extern bool check_functional_grouping(Oid relid, List *grouping_columns, List **constraintDeps); +void +constraint_check_collation_versions(Oid oid, const char *constraint_name); + #endif /* PG_CONSTRAINT_H */ -- 2.17.0