Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/dependency.c182
-rw-r--r--src/backend/catalog/heap.c7
-rw-r--r--src/backend/catalog/index.c197
-rw-r--r--src/backend/catalog/pg_constraint.c2
-rw-r--r--src/backend/catalog/pg_depend.c46
-rw-r--r--src/backend/catalog/pg_type.c60
-rw-r--r--src/backend/commands/collationcmds.c16
-rw-r--r--src/backend/commands/tablecmds.c31
-rw-r--r--src/backend/nodes/copyfuncs.c1
-rw-r--r--src/backend/optimizer/util/plancat.c9
-rw-r--r--src/backend/parser/gram.y8
-rw-r--r--src/backend/utils/adt/pg_locale.c46
-rw-r--r--src/backend/utils/adt/pg_upgrade_support.c1
-rw-r--r--src/backend/utils/cache/relcache.c2
14 files changed, 541 insertions, 67 deletions
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 1a927377e73..b0d037600e9 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -76,6 +76,7 @@
#include "rewrite/rewriteRemove.h"
#include "storage/lmgr.h"
#include "utils/acl.h"
+#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
@@ -435,6 +436,84 @@ performMultipleDeletions(const ObjectAddresses *objects,
}
/*
+ * Call a function for all objects that 'object' depend on. If the function
+ * returns true, refobjversion will be updated in the catalog.
+ */
+void
+visitDependenciesOf(const ObjectAddress *object,
+ VisitDependenciesOfCB callback,
+ void *userdata)
+{
+ Relation depRel;
+ ScanKeyData key[3];
+ SysScanDesc scan;
+ HeapTuple tup;
+ ObjectAddress otherObject;
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->classId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+ ScanKeyInit(&key[2],
+ Anum_pg_depend_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(object->objectSubId));
+
+ depRel = table_open(DependRelationId, RowExclusiveLock);
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ NULL, 3, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
+ char *new_version;
+ Datum depversion;
+ bool isnull;
+
+ otherObject.classId = foundDep->refclassid;
+ otherObject.objectId = foundDep->refobjid;
+ otherObject.objectSubId = foundDep->refobjsubid;
+
+ depversion = heap_getattr(tup, Anum_pg_depend_refobjversion,
+ RelationGetDescr(depRel), &isnull);
+
+ /* Does the callback want to update the version? */
+ if (callback(&otherObject,
+ isnull ? NULL : TextDatumGetCString(depversion),
+ &new_version,
+ userdata))
+ {
+ Datum values[Natts_pg_depend];
+ bool nulls[Natts_pg_depend];
+ bool replaces[Natts_pg_depend];
+
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+ memset(replaces, false, sizeof(replaces));
+
+ if (new_version)
+ values[Anum_pg_depend_refobjversion - 1] =
+ CStringGetTextDatum(new_version);
+ else
+ nulls[Anum_pg_depend_refobjversion - 1] = true;
+ replaces[Anum_pg_depend_refobjversion - 1] = true;
+
+ tup = heap_modify_tuple(tup, RelationGetDescr(depRel), values,
+ nulls, replaces);
+ CatalogTupleUpdate(depRel, &tup->t_self, tup);
+
+ heap_freetuple(tup);
+ }
+ }
+ systable_endscan(scan);
+ table_close(depRel, RowExclusiveLock);
+}
+
+/*
* findDependentObjects - find all objects that depend on 'object'
*
* For every object that depends on the starting object, acquire a deletion
@@ -1567,6 +1646,38 @@ ReleaseDeletionLock(const ObjectAddress *object)
}
/*
+ * Record dependencies on a list of collations, optionally with their current
+ * version.
+ */
+void
+recordDependencyOnCollations(ObjectAddress *myself,
+ List *collations,
+ bool record_version)
+{
+ ObjectAddresses *addrs;
+ ListCell *lc;
+
+ if (list_length(collations) == 0)
+ return;
+
+ addrs = new_object_addresses();
+ foreach(lc, collations)
+ {
+ ObjectAddress referenced;
+
+ ObjectAddressSet(referenced, CollationRelationId, lfirst_oid(lc));
+
+ add_exact_object_address(&referenced, addrs);
+ }
+
+ eliminate_duplicate_dependencies(addrs);
+ recordMultipleDependencies(myself, addrs->refs, addrs->numrefs,
+ DEPENDENCY_NORMAL, record_version);
+
+ free_object_addresses(addrs);
+}
+
+/*
* recordDependencyOnExpr - find expression dependencies
*
* This is used to find the dependencies of rules, constraint expressions,
@@ -1602,8 +1713,8 @@ recordDependencyOnExpr(const ObjectAddress *depender,
recordMultipleDependencies(depender,
context.addrs->refs,
context.addrs->numrefs,
- NULL,
- behavior);
+ behavior,
+ false);
free_object_addresses(context.addrs);
}
@@ -1630,7 +1741,8 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
Node *expr, Oid relId,
DependencyType behavior,
DependencyType self_behavior,
- bool reverse_self)
+ bool reverse_self,
+ bool record_version)
{
find_expr_references_context context;
RangeTblEntry rte;
@@ -1691,8 +1803,8 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
recordMultipleDependencies(depender,
self_addrs->refs,
self_addrs->numrefs,
- NULL,
- self_behavior);
+ self_behavior,
+ record_version);
else
{
/* Can't use recordMultipleDependencies, so do it the hard way */
@@ -1713,8 +1825,8 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
recordMultipleDependencies(depender,
context.addrs->refs,
context.addrs->numrefs,
- NULL,
- behavior);
+ behavior,
+ record_version);
free_object_addresses(context.addrs);
}
@@ -1770,6 +1882,29 @@ find_expr_references_walker(Node *node,
/* If it's a plain relation, reference this column */
add_object_address(OCLASS_CLASS, rte->relid, var->varattno,
context->addrs);
+
+ /* Top-level collation if valid */
+ if (OidIsValid(var->varcollid))
+ add_object_address(OCLASS_COLLATION, var->varcollid, 0,
+ context->addrs);
+ /* Otherwise, it may be a type with internal collations */
+ else if (var->vartype >= FirstNormalObjectId)
+ {
+ List *collations;
+ ListCell *lc;
+
+ collations = GetTypeCollations(var->vartype);
+
+ foreach(lc, collations)
+ {
+ Oid coll = lfirst_oid(lc);
+
+ if (OidIsValid(coll))
+ add_object_address(OCLASS_COLLATION,
+ lfirst_oid(lc), 0,
+ context->addrs);
+ }
+ }
}
/*
@@ -1794,11 +1929,9 @@ find_expr_references_walker(Node *node,
/*
* We must also depend on the constant's collation: it could be
* different from the datatype's, if a CollateExpr was const-folded to
- * a simple constant. However we can save work in the most common
- * case where the collation is "default", since we know that's pinned.
+ * a simple constant.
*/
- if (OidIsValid(con->constcollid) &&
- con->constcollid != DEFAULT_COLLATION_OID)
+ if (OidIsValid(con->constcollid))
add_object_address(OCLASS_COLLATION, con->constcollid, 0,
context->addrs);
@@ -1887,8 +2020,7 @@ find_expr_references_walker(Node *node,
add_object_address(OCLASS_TYPE, param->paramtype, 0,
context->addrs);
/* and its collation, just as for Consts */
- if (OidIsValid(param->paramcollid) &&
- param->paramcollid != DEFAULT_COLLATION_OID)
+ if (OidIsValid(param->paramcollid))
add_object_address(OCLASS_COLLATION, param->paramcollid, 0,
context->addrs);
}
@@ -1975,8 +2107,7 @@ find_expr_references_walker(Node *node,
add_object_address(OCLASS_TYPE, fselect->resulttype, 0,
context->addrs);
/* the collation might not be referenced anywhere else, either */
- if (OidIsValid(fselect->resultcollid) &&
- fselect->resultcollid != DEFAULT_COLLATION_OID)
+ if (OidIsValid(fselect->resultcollid))
add_object_address(OCLASS_COLLATION, fselect->resultcollid, 0,
context->addrs);
}
@@ -2006,8 +2137,7 @@ find_expr_references_walker(Node *node,
add_object_address(OCLASS_TYPE, relab->resulttype, 0,
context->addrs);
/* the collation might not be referenced anywhere else, either */
- if (OidIsValid(relab->resultcollid) &&
- relab->resultcollid != DEFAULT_COLLATION_OID)
+ if (OidIsValid(relab->resultcollid))
add_object_address(OCLASS_COLLATION, relab->resultcollid, 0,
context->addrs);
}
@@ -2019,8 +2149,7 @@ find_expr_references_walker(Node *node,
add_object_address(OCLASS_TYPE, iocoerce->resulttype, 0,
context->addrs);
/* the collation might not be referenced anywhere else, either */
- if (OidIsValid(iocoerce->resultcollid) &&
- iocoerce->resultcollid != DEFAULT_COLLATION_OID)
+ if (OidIsValid(iocoerce->resultcollid))
add_object_address(OCLASS_COLLATION, iocoerce->resultcollid, 0,
context->addrs);
}
@@ -2032,8 +2161,7 @@ find_expr_references_walker(Node *node,
add_object_address(OCLASS_TYPE, acoerce->resulttype, 0,
context->addrs);
/* the collation might not be referenced anywhere else, either */
- if (OidIsValid(acoerce->resultcollid) &&
- acoerce->resultcollid != DEFAULT_COLLATION_OID)
+ if (OidIsValid(acoerce->resultcollid))
add_object_address(OCLASS_COLLATION, acoerce->resultcollid, 0,
context->addrs);
/* fall through to examine arguments */
@@ -2121,8 +2249,7 @@ find_expr_references_walker(Node *node,
if (OidIsValid(wc->endInRangeFunc))
add_object_address(OCLASS_PROC, wc->endInRangeFunc, 0,
context->addrs);
- if (OidIsValid(wc->inRangeColl) &&
- wc->inRangeColl != DEFAULT_COLLATION_OID)
+ if (OidIsValid(wc->inRangeColl))
add_object_address(OCLASS_COLLATION, wc->inRangeColl, 0,
context->addrs);
/* fall through to examine substructure */
@@ -2267,7 +2394,7 @@ find_expr_references_walker(Node *node,
{
Oid collid = lfirst_oid(ct);
- if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
+ if (OidIsValid(collid))
add_object_address(OCLASS_COLLATION, collid, 0,
context->addrs);
}
@@ -2289,7 +2416,7 @@ find_expr_references_walker(Node *node,
{
Oid collid = lfirst_oid(ct);
- if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
+ if (OidIsValid(collid))
add_object_address(OCLASS_COLLATION, collid, 0,
context->addrs);
}
@@ -2685,8 +2812,9 @@ record_object_address_dependencies(const ObjectAddress *depender,
{
eliminate_duplicate_dependencies(referenced);
recordMultipleDependencies(depender,
- referenced->refs, referenced->numrefs, NULL,
- behavior);
+ referenced->refs, referenced->numrefs,
+ behavior,
+ false);
}
/*
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 9ccf378d45b..4cd7d769381 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2336,7 +2336,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
*/
recordDependencyOnSingleRelExpr(&colobject, expr, RelationGetRelid(rel),
DEPENDENCY_AUTO,
- DEPENDENCY_AUTO, false);
+ DEPENDENCY_AUTO, false, false);
}
else
{
@@ -2346,7 +2346,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
*/
recordDependencyOnSingleRelExpr(&defobject, expr, RelationGetRelid(rel),
DEPENDENCY_NORMAL,
- DEPENDENCY_NORMAL, false);
+ DEPENDENCY_NORMAL, false, false);
}
/*
@@ -3706,7 +3706,8 @@ StorePartitionKey(Relation rel,
RelationGetRelid(rel),
DEPENDENCY_NORMAL,
DEPENDENCY_INTERNAL,
- true /* reverse the self-deps */ );
+ true /* reverse the self-deps */ ,
+ false /* don't track versions */ );
/*
* We must invalidate the relcache so that the next
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index a18cc7cad30..b88b4a1f12b 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -53,6 +53,7 @@
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "catalog/storage.h"
+#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/progress.h"
#include "commands/tablecmds.h"
@@ -75,6 +76,7 @@
#include "utils/guc.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
+#include "utils/pg_locale.h"
#include "utils/memutils.h"
#include "utils/pg_rusage.h"
#include "utils/rel.h"
@@ -1020,6 +1022,8 @@ index_create(Relation heapRelation,
ObjectAddress myself,
referenced;
ObjectAddresses *addrs;
+ List *colls = NIL,
+ *colls_no_version = NIL;
ObjectAddressSet(myself, RelationRelationId, indexRelationId);
@@ -1103,30 +1107,65 @@ index_create(Relation heapRelation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
}
- /* placeholder for normal dependencies */
- addrs = new_object_addresses();
-
- /* Store dependency on collations */
-
- /* The default collation is pinned, so don't bother recording it */
+ /* Get dependencies on collations for all index keys. */
for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
{
- if (OidIsValid(collationObjectId[i]) &&
- collationObjectId[i] != DEFAULT_COLLATION_OID)
+ Oid colloid = collationObjectId[i];
+
+ if (OidIsValid(colloid))
{
- ObjectAddressSet(referenced, CollationRelationId,
- collationObjectId[i]);
- add_exact_object_address(&referenced, addrs);
+ Oid opclass = classObjectId[i];
+
+ /*
+ * The *_pattern_ops opclasses are special: they explicitly do
+ * not depend on collation order so we can save some effort.
+ *
+ * XXX With more analysis, we could also skip version tracking
+ * for some cases like hash indexes with deterministic
+ * collations, because they will never need to order strings.
+ */
+ if (opclass == TEXT_BTREE_PATTERN_OPS_OID ||
+ opclass == VARCHAR_BTREE_PATTERN_OPS_OID ||
+ opclass == BPCHAR_BTREE_PATTERN_OPS_OID)
+ colls_no_version = lappend_oid(colls_no_version, colloid);
+ else
+ colls = lappend_oid(colls, colloid);
+ }
+ else
+ {
+ Form_pg_attribute att = TupleDescAttr(indexTupDesc, i);
+
+ Assert(i < indexTupDesc->natts);
+
+ /*
+ * Even though there is no top-level collation, there may be
+ * collations affecting ordering inside types, so look there
+ * too.
+ */
+ colls = list_concat(colls, GetTypeCollations(att->atttypid));
}
}
+ /*
+ * If we have anything in both lists, keep just the versioned one to
+ * avoid some duplication.
+ */
+ if (colls_no_version != NIL && colls != NIL)
+ colls_no_version = list_difference_oid(colls_no_version, colls);
+
+ /* Store the versioned and unversioned collation dependencies. */
+ if (colls_no_version != NIL)
+ recordDependencyOnCollations(&myself, colls_no_version, false);
+ if (colls != NIL)
+ recordDependencyOnCollations(&myself, colls, true);
+
/* Store dependency on operator classes */
+ addrs = new_object_addresses();
for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
{
ObjectAddressSet(referenced, OperatorClassRelationId, classObjectId[i]);
add_exact_object_address(&referenced, addrs);
}
-
record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
free_object_addresses(addrs);
@@ -1137,7 +1176,7 @@ index_create(Relation heapRelation,
(Node *) indexInfo->ii_Expressions,
heapRelationId,
DEPENDENCY_NORMAL,
- DEPENDENCY_AUTO, false);
+ DEPENDENCY_AUTO, false, true);
}
/* Store dependencies on anything mentioned in predicate */
@@ -1147,7 +1186,7 @@ index_create(Relation heapRelation,
(Node *) indexInfo->ii_Predicate,
heapRelationId,
DEPENDENCY_NORMAL,
- DEPENDENCY_AUTO, false);
+ DEPENDENCY_AUTO, false, true);
}
}
else
@@ -1226,6 +1265,129 @@ index_create(Relation heapRelation,
return indexRelationId;
}
+typedef struct do_collation_version_check_context
+{
+ Oid relid;
+ List *warned_colls;
+} do_collation_version_check_context;
+
+/*
+ * Raise a warning if the recorded and current collation version don't match.
+ * This is a callback for visitDependenciesOf().
+ */
+static bool
+do_collation_version_check(const ObjectAddress *otherObject,
+ const char *version,
+ char **new_version,
+ void *data)
+{
+ do_collation_version_check_context *context = data;
+ char *current_version;
+
+ /* We only care about dependencies on collations with a version. */
+ if (!version || otherObject->classId != CollationRelationId)
+ 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;
+
+ /*
+ * We don't expect too many duplicates, but it's possible, and we don't
+ * want to generate duplicate warnings.
+ */
+ if (list_member_oid(context->warned_colls, otherObject->objectId))
+ return false;
+
+ /* Do they match? */
+ if (strcmp(current_version, version) != 0)
+ {
+ /*
+ * The version has changed, probably due to an OS/library upgrade or
+ * streaming replication between different OS/library versions.
+ */
+ ereport(WARNING,
+ (errmsg("index \"%s\" depends on collation \"%s\" version \"%s\", but the current version is \"%s\"",
+ get_rel_name(context->relid),
+ get_collation_name(otherObject->objectId),
+ version,
+ current_version),
+ errdetail("The index may be corrupted due to changes in sort order."),
+ errhint("REINDEX to avoid the risk of corruption.")));
+
+ /* Remember not to complain about this collation again. */
+ context->warned_colls = lappend_oid(context->warned_colls,
+ otherObject->objectId);
+ }
+
+ return false;
+}
+
+/* index_check_collation_versions
+ * Check the collation version for all dependencies on the given index.
+ */
+void
+index_check_collation_versions(Oid relid)
+{
+ do_collation_version_check_context context;
+ ObjectAddress object;
+
+ /*
+ * The callback needs the relid for error messages, and some scratch space
+ * to avoid duplicate warnings.
+ */
+ context.relid = relid;
+ context.warned_colls = NIL;
+
+ object.classId = RelationRelationId;
+ object.objectId = relid;
+ object.objectSubId = 0;
+
+ visitDependenciesOf(&object, &do_collation_version_check, &context);
+
+ list_free(context.warned_colls);
+}
+
+/*
+ * Update the version for collations. A callback for visitDependenciesOf().
+ */
+static bool
+do_collation_version_update(const ObjectAddress *otherObject,
+ const char *version,
+ char **new_version,
+ void *data)
+{
+ Oid *coll = data;
+
+ /* We only care about dependencies on collations with versions. */
+ if (!version || otherObject->classId != CollationRelationId)
+ return false;
+
+ /* If we're only trying to update one collation, skip others. */
+ if (OidIsValid(*coll) && otherObject->objectId != *coll)
+ return false;
+
+ *new_version = get_collation_version_for_oid(otherObject->objectId);
+
+ return true;
+}
+
+/*
+ * Record the current versions of one or all collations that an index depends
+ * on. InvalidOid means all.
+ */
+void
+index_update_collation_versions(Oid relid, Oid coll)
+{
+ ObjectAddress object;
+
+ object.classId = RelationRelationId;
+ object.objectId = relid;
+ object.objectSubId = 0;
+ visitDependenciesOf(&object, &do_collation_version_update, &coll);
+}
+
/*
* index_concurrently_create_copy
*
@@ -1686,6 +1848,10 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
changeDependenciesOf(RelationRelationId, oldIndexId, newIndexId);
changeDependenciesOn(RelationRelationId, oldIndexId, newIndexId);
+ /* Now we have the old index's collation versions, so fix that. */
+ CommandCounterIncrement();
+ index_update_collation_versions(newIndexId, InvalidOid);
+
/*
* Copy over statistics from old to new index
*/
@@ -3638,6 +3804,9 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
/* Close rels, but keep locks */
index_close(iRel, NoLock);
table_close(heapRelation, NoLock);
+
+ /* Record the current versions of all depended-on collations. */
+ index_update_collation_versions(indexId, InvalidOid);
}
/*
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 0d70cb0c3c9..93774c9d21a 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -362,7 +362,7 @@ CreateConstraintEntry(const char *constraintName,
*/
recordDependencyOnSingleRelExpr(&conobject, conExpr, relId,
DEPENDENCY_NORMAL,
- DEPENDENCY_NORMAL, false);
+ DEPENDENCY_NORMAL, false, true);
}
/* Post creation hook for new constraint */
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 09c30b13e88..25290c821fd 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -19,6 +19,7 @@
#include "access/table.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_extension.h"
@@ -27,6 +28,7 @@
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
+#include "utils/pg_locale.h"
#include "utils/rel.h"
@@ -45,19 +47,24 @@ recordDependencyOn(const ObjectAddress *depender,
const ObjectAddress *referenced,
DependencyType behavior)
{
- recordMultipleDependencies(depender, referenced, 1, NULL, behavior);
+ recordMultipleDependencies(depender, referenced, 1, behavior, false);
}
/*
* Record multiple dependencies (of the same kind) for a single dependent
* object. This has a little less overhead than recording each separately.
+ *
+ * If record_version is true, then a record is added even if the referenced
+ * object is pinned, and the dependency version will be retrieved according to
+ * the referenced object kind. For now, only collation version is
+ * supported.
*/
void
recordMultipleDependencies(const ObjectAddress *depender,
const ObjectAddress *referenced,
int nreferenced,
- const char *version,
- DependencyType behavior)
+ DependencyType behavior,
+ bool record_version)
{
Relation dependDesc;
CatalogIndexState indstate;
@@ -66,6 +73,7 @@ recordMultipleDependencies(const ObjectAddress *depender,
max_slots,
slot_init_count,
slot_stored_count;
+ char *version = NULL;
if (nreferenced <= 0)
return; /* nothing to do */
@@ -96,12 +104,38 @@ recordMultipleDependencies(const ObjectAddress *depender,
slot_init_count = 0;
for (i = 0; i < nreferenced; i++, referenced++)
{
+ bool ignore_systempin = false;
+
+ if (record_version)
+ {
+ /* For now we only know how to deal with collations. */
+ if (referenced->classId == CollationRelationId)
+ {
+ /* C and POSIX don't need version tracking. */
+ if (referenced->objectId == C_COLLATION_OID ||
+ referenced->objectId == POSIX_COLLATION_OID)
+ continue;
+
+ version = get_collation_version_for_oid(referenced->objectId);
+
+ /*
+ * Default collation is pinned, so we need to force recording
+ * the dependency to store the version.
+ */
+ if (referenced->objectId == DEFAULT_COLLATION_OID)
+ ignore_systempin = true;
+ }
+ }
+ else
+ Assert(!version);
+
/*
* If the referenced object is pinned by the system, there's no real
- * need to record dependencies on it. This saves lots of space in
- * pg_depend, so it's worth the time taken to check.
+ * need to record dependencies on it, unless we need to record a
+ * version. This saves lots of space in pg_depend, so it's worth the
+ * time taken to check.
*/
- if (isObjectPinned(referenced, dependDesc))
+ if (!ignore_systempin && isObjectPinned(referenced, dependDesc))
continue;
if (slot_init_count < max_slots)
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 0b04dff7731..44eed1a0b34 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -15,6 +15,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/relation.h"
#include "access/table.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
@@ -513,6 +514,65 @@ TypeCreate(Oid newTypeOid,
}
/*
+ * Get a list of all distinct collations that the given type depends on.
+ */
+List *
+GetTypeCollations(Oid typeoid)
+{
+ List *result = NIL;
+ HeapTuple tuple;
+ Form_pg_type typeTup;
+
+ tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeoid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for type %u", typeoid);
+ typeTup = (Form_pg_type) GETSTRUCT(tuple);
+
+ if (OidIsValid(typeTup->typcollation))
+ result = list_append_unique_oid(result, typeTup->typcollation);
+ else if (typeTup->typtype == TYPTYPE_COMPOSITE)
+ {
+ Relation rel = relation_open(typeTup->typrelid, AccessShareLock);
+ TupleDesc desc = RelationGetDescr(rel);
+
+ for (int i = 0; i < RelationGetNumberOfAttributes(rel); i++)
+ {
+ Form_pg_attribute att = TupleDescAttr(desc, i);
+
+ if (OidIsValid(att->attcollation))
+ result = list_append_unique_oid(result, att->attcollation);
+ else
+ result = list_concat_unique_oid(result,
+ GetTypeCollations(att->atttypid));
+ }
+
+ relation_close(rel, NoLock);
+ }
+ else if (typeTup->typtype == TYPTYPE_DOMAIN)
+ {
+ Assert(OidIsValid(typeTup->typbasetype));
+
+ result = list_concat_unique_oid(result,
+ GetTypeCollations(typeTup->typbasetype));
+ }
+ else if (typeTup->typtype == TYPTYPE_RANGE)
+ {
+ Oid rangeid = get_range_subtype(typeTup->oid);
+
+ Assert(OidIsValid(rangeid));
+
+ result = list_concat_unique_oid(result, GetTypeCollations(rangeid));
+ }
+ else if (OidIsValid(typeTup->typelem))
+ result = list_concat_unique_oid(result,
+ GetTypeCollations(typeTup->typelem));
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
+/*
* GenerateTypeDependencies: build the dependencies needed for a type
*
* Most of what this function needs to know about the type is passed as the
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c
index 5ad8886e60a..7b31272734d 100644
--- a/src/backend/commands/collationcmds.c
+++ b/src/backend/commands/collationcmds.c
@@ -270,23 +270,9 @@ Datum
pg_collation_actual_version(PG_FUNCTION_ARGS)
{
Oid collid = PG_GETARG_OID(0);
- HeapTuple tp;
- char *collcollate;
- char collprovider;
char *version;
- tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
- if (!HeapTupleIsValid(tp))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("collation with OID %u does not exist", collid)));
-
- collcollate = pstrdup(NameStr(((Form_pg_collation) GETSTRUCT(tp))->collcollate));
- collprovider = ((Form_pg_collation) GETSTRUCT(tp))->collprovider;
-
- ReleaseSysCache(tp);
-
- version = get_collation_actual_version(collprovider, collcollate);
+ version = get_collation_version_for_oid(collid);
if (version)
PG_RETURN_TEXT_P(cstring_to_text(version));
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index df13b72974f..1b105ba1c42 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -93,6 +93,7 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/partcache.h"
+#include "utils/pg_locale.h"
#include "utils/relcache.h"
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
@@ -559,6 +560,7 @@ static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
Relation partitionTbl);
static List *GetParentedForeignKeyRefs(Relation partition);
static void ATDetachCheckNoForeignKeyRefs(Relation partition);
+static void ATExecAlterCollationRefreshVersion(Relation rel, List *coll);
/* ----------------------------------------------------------------
@@ -3986,6 +3988,10 @@ AlterTableGetLockLevel(List *cmds)
cmd_lockmode = AccessShareLock;
break;
+ case AT_AlterCollationRefreshVersion:
+ cmd_lockmode = AccessExclusiveLock;
+ break;
+
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -4160,6 +4166,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* This command never recurses */
pass = AT_PASS_MISC;
break;
+ case AT_AlterCollationRefreshVersion: /* ALTER COLLATION ... REFRESH
+ * VERSION */
+ ATSimplePermissions(rel, ATT_INDEX);
+ /* This command never recurses */
+ pass = AT_PASS_MISC;
+ break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
@@ -4738,6 +4750,11 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
ATExecDetachPartition(rel, ((PartitionCmd *) cmd->def)->name);
break;
+ case AT_AlterCollationRefreshVersion:
+ /* ATPrepCmd ensured it must be an index */
+ Assert(rel->rd_rel->relkind == RELKIND_INDEX);
+ ATExecAlterCollationRefreshVersion(rel, cmd->object);
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -17582,3 +17599,17 @@ ATDetachCheckNoForeignKeyRefs(Relation partition)
table_close(rel, NoLock);
}
}
+
+/*
+ * ALTER INDEX ... ALTER COLLATION ... REFRESH VERSION
+ *
+ * Update refobjversion to the current collation version by force. This clears
+ * warnings about version mismatches without the need to run REINDEX,
+ * potentially hiding corruption due to ordering changes.
+ */
+static void
+ATExecAlterCollationRefreshVersion(Relation rel, List *coll)
+{
+ index_update_collation_versions(rel->rd_id, get_collation_oid(coll, false));
+ CacheInvalidateRelcache(rel);
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index ac8b57109c5..530aac68a76 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3215,6 +3215,7 @@ _copyAlterTableCmd(const AlterTableCmd *from)
COPY_SCALAR_FIELD(subtype);
COPY_STRING_FIELD(name);
+ COPY_NODE_FIELD(object);
COPY_SCALAR_FIELD(num);
COPY_NODE_FIELD(newowner);
COPY_NODE_FIELD(def);
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 4f0da51c269..52c01eb86b1 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -28,6 +28,7 @@
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
+#include "catalog/index.h"
#include "catalog/pg_am.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_statistic_ext.h"
@@ -198,6 +199,14 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
indexRelation = index_open(indexoid, lmode);
index = indexRelation->rd_index;
+ /* Warn if any dependent collations' versions have moved. */
+ if (!IsSystemRelation(relation) &&
+ !indexRelation->rd_version_checked)
+ {
+ index_check_collation_versions(indexoid);
+ indexRelation->rd_version_checked = true;
+ }
+
/*
* Ignore invalid indexes, since they can't safely be used for
* queries. Note that this is OK because the data structure we
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 60cf7242a30..357ab93fb65 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -2591,6 +2591,14 @@ alter_table_cmd:
n->subtype = AT_NoForceRowSecurity;
$$ = (Node *)n;
}
+ /* ALTER INDEX <name> ALTER COLLATION ... REFRESH VERSION */
+ | ALTER COLLATION any_name REFRESH VERSION_P
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_AlterCollationRefreshVersion;
+ n->object = $3;
+ $$ = (Node *)n;
+ }
| alter_generic_options
{
AlterTableCmd *n = makeNode(AlterTableCmd);
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 514e0fa0af8..3b0324ce18a 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -57,7 +57,9 @@
#include "access/htup_details.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_control.h"
+#include "catalog/pg_database.h"
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/formatting.h"
#include "utils/hsearch.h"
@@ -139,6 +141,9 @@ static char *IsoLocaleName(const char *); /* MSVC specific */
static void icu_set_collation_attributes(UCollator *collator, const char *loc);
#endif
+static char *get_collation_actual_version(char collprovider,
+ const char *collcollate);
+
/*
* pg_perm_setlocale
*
@@ -1630,7 +1635,7 @@ pg_newlocale_from_collation(Oid collid)
* Get provider-specific collation version string for the given collation from
* the operating system/library.
*/
-char *
+static char *
get_collation_actual_version(char collprovider, const char *collcollate)
{
char *collversion = NULL;
@@ -1712,6 +1717,45 @@ get_collation_actual_version(char collprovider, const char *collcollate)
return collversion;
}
+/*
+ * Get provider-specific collation version string for a given collation OID.
+ * Return NULL if the provider doesn't support versions.
+ */
+char *
+get_collation_version_for_oid(Oid oid)
+{
+ HeapTuple tp;
+ char *version = NULL;
+
+ Assert(oid != C_COLLATION_OID && oid != POSIX_COLLATION_OID);
+
+ if (oid == DEFAULT_COLLATION_OID)
+ {
+ Form_pg_database dbform;
+
+ tp = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
+ dbform = (Form_pg_database) GETSTRUCT(tp);
+ version = get_collation_actual_version(COLLPROVIDER_LIBC,
+ NameStr(dbform->datcollate));
+ }
+ else
+ {
+ Form_pg_collation collform;
+
+ tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(oid));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for collation %u", oid);
+ collform = (Form_pg_collation) GETSTRUCT(tp);
+ version = get_collation_actual_version(collform->collprovider,
+ NameStr(collform->collcollate));
+ }
+
+ ReleaseSysCache(tp);
+
+ return version;
+}
#ifdef USE_ICU
/*
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 14d9eb2b5b3..8a5953881ee 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -13,6 +13,7 @@
#include "catalog/binary_upgrade.h"
#include "catalog/heap.h"
+#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "commands/extension.h"
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 9224e2ffeda..66393becfb6 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -42,6 +42,7 @@
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
+#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/partition.h"
@@ -5934,6 +5935,7 @@ load_relcache_init_file(bool shared)
rel->rd_idattr = NULL;
rel->rd_pubactions = NULL;
rel->rd_statvalid = false;
+ rel->rd_version_checked = false;
rel->rd_statlist = NIL;
rel->rd_fkeyvalid = false;
rel->rd_fkeylist = NIL;