0003-Track-collation-versions-for-indexes-v4.patch
application/octet-stream
Filename: 0003-Track-collation-versions-for-indexes-v4.patch
Type: application/octet-stream
Part: 4
Message:
Re: Collation versioning
Patch
Format: format-patch
Series: patch v4-0003
Subject: Track collation versions for indexes.
| File | + | − |
|---|---|---|
| src/backend/catalog/dependency.c | 123 | 20 |
| src/backend/catalog/heap.c | 4 | 3 |
| src/backend/catalog/index.c | 106 | 10 |
| src/backend/catalog/pg_constraint.c | 1 | 1 |
| src/backend/catalog/pg_depend.c | 52 | 17 |
| src/backend/catalog/pg_type.c | 47 | 0 |
| src/backend/utils/adt/pg_locale.c | 39 | 0 |
| src/backend/utils/cache/relcache.c | 5 | 0 |
| src/include/catalog/dependency.h | 14 | 7 |
| src/include/catalog/index.h | 2 | 0 |
| src/include/catalog/pg_type.h | 2 | 0 |
| src/include/utils/pg_locale.h | 1 | 0 |
| src/test/regress/expected/create_index.out | 6 | 2 |
From 6faee17b28f9342dbf20535a8708f02c869f4991 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Tue, 28 May 2019 14:16:29 -0400
Subject: [PATCH 3/5] Track collation versions for indexes.
Record the current version of dependent collations in pg_depend when
creating or rebuilding an index. Whenever we load an index into the
relcache, check if the collation versions still match those reported
by the collation provider. Warn that the index may be corrupted if
not.
Author: Thomas Munro, Julien Rouhaud
Reviewed-by:
Discussion: https://postgr.es/m/CAEepm%3D0uEQCpfq_%2BLYFBdArCe4Ot98t1aR4eYiYTe%3DyavQygiQ%40mail.gmail.com
---
src/backend/catalog/dependency.c | 143 ++++++++++++++++++---
src/backend/catalog/heap.c | 7 +-
src/backend/catalog/index.c | 116 +++++++++++++++--
src/backend/catalog/pg_constraint.c | 2 +-
src/backend/catalog/pg_depend.c | 69 +++++++---
src/backend/catalog/pg_type.c | 47 +++++++
src/backend/utils/adt/pg_locale.c | 39 ++++++
src/backend/utils/cache/relcache.c | 5 +
src/include/catalog/dependency.h | 21 ++-
src/include/catalog/index.h | 2 +
src/include/catalog/pg_type.h | 2 +
src/include/utils/pg_locale.h | 1 +
src/test/regress/expected/create_index.out | 8 +-
13 files changed, 402 insertions(+), 60 deletions(-)
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 0bb1504b20..e8e3129886 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -137,6 +137,8 @@ typedef struct
{
ObjectAddresses *addrs; /* addresses being accumulated */
List *rtables; /* list of rangetables to resolve Vars */
+ bool track_version; /* whether caller asked to track dependency
+ * versions */
} find_expr_references_context;
/*
@@ -437,6 +439,63 @@ performMultipleDeletions(const ObjectAddresses *objects,
table_close(depRel, RowExclusiveLock);
}
+/*
+ * Call a function for all objects that depend on 'object'. If the function
+ * returns a non-NULL pointer to a new version string, update the version.
+ */
+void
+visitDependentObjects(const ObjectAddress *object,
+ VisitDependentObjectsFun 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);
+ NameData *new_version;
+
+ otherObject.classId = foundDep->refclassid;
+ otherObject.objectId = foundDep->refobjid;
+ otherObject.objectSubId = foundDep->refobjsubid;
+
+ new_version = callback(&otherObject, &foundDep->refobjversion,
+ userdata);
+ if (new_version)
+ {
+ /* Make a modifyable copy. */
+ tup = heap_copytuple(tup);
+ foundDep = (Form_pg_depend) GETSTRUCT(tup);
+ foundDep->refobjversion = *new_version;
+ CatalogTupleUpdate(depRel, &tup->t_self, tup);
+ heap_freetuple(tup);
+ }
+ }
+ systable_endscan(scan);
+ table_close(depRel, RowExclusiveLock);
+}
+
/*
* findDependentObjects - find all objects that depend on 'object'
*
@@ -1602,9 +1661,10 @@ recordDependencyOnExpr(const ObjectAddress *depender,
/* And record 'em */
recordMultipleDependencies(depender,
- context.addrs->refs, NULL,
+ context.addrs->refs,
context.addrs->numrefs,
- behavior);
+ behavior,
+ false);
free_object_addresses(context.addrs);
}
@@ -1631,12 +1691,14 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
Node *expr, Oid relId,
DependencyType behavior,
DependencyType self_behavior,
- bool reverse_self)
+ bool reverse_self,
+ bool track_version)
{
find_expr_references_context context;
RangeTblEntry rte;
context.addrs = new_object_addresses();
+ context.track_version = track_version;
/* We gin up a rather bogus rangetable list to handle Vars */
MemSet(&rte, 0, sizeof(rte));
@@ -1690,9 +1752,10 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
/* Record the self-dependencies with the appropriate direction */
if (!reverse_self)
recordMultipleDependencies(depender,
- self_addrs->refs, NULL,
+ self_addrs->refs,
self_addrs->numrefs,
- self_behavior);
+ self_behavior,
+ track_version);
else
{
/* Can't use recordMultipleDependencies, so do it the hard way */
@@ -1711,9 +1774,10 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
/* Record the external dependencies */
recordMultipleDependencies(depender,
- context.addrs->refs, NULL,
+ context.addrs->refs,
context.addrs->numrefs,
- behavior);
+ behavior,
+ track_version);
free_object_addresses(context.addrs);
}
@@ -1775,6 +1839,32 @@ 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);
+ /* and its type's collation if valid */
+ if (OidIsValid(var->varcollid))
+ {
+ add_object_address(OCLASS_COLLATION, var->varcollid, 0,
+ context->addrs);
+ }
+ /*
+ * otherwise, it may be a composite type having underlying
+ * collations */
+ else if (var->vartype >= FirstNormalObjectId)
+ {
+ List *collations = NIL;
+ ListCell *lc;
+
+ collations = GetTypeCollations(var->vartype);
+
+ foreach(lc, collations)
+ {
+ Oid coll = lfirst_oid(lc);
+
+ if (OidIsValid(coll) && (coll != DEFAULT_COLLATION_OID ||
+ context->track_version))
+ add_object_address(OCLASS_COLLATION, lfirst_oid(lc), 0,
+ context->addrs);
+ }
+ }
}
else if (rte->rtekind == RTE_JOIN)
{
@@ -1808,11 +1898,13 @@ 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. However we can save work in the most common case
+ * where the collation is "default", since we know that's pinned, if
+ * the caller didn't ask to track dependency versions.
*/
if (OidIsValid(con->constcollid) &&
- con->constcollid != DEFAULT_COLLATION_OID)
+ (con->constcollid != DEFAULT_COLLATION_OID ||
+ context->track_version))
add_object_address(OCLASS_COLLATION, con->constcollid, 0,
context->addrs);
@@ -1902,7 +1994,8 @@ find_expr_references_walker(Node *node,
context->addrs);
/* and its collation, just as for Consts */
if (OidIsValid(param->paramcollid) &&
- param->paramcollid != DEFAULT_COLLATION_OID)
+ (param->paramcollid != DEFAULT_COLLATION_OID ||
+ context->track_version))
add_object_address(OCLASS_COLLATION, param->paramcollid, 0,
context->addrs);
}
@@ -1990,7 +2083,8 @@ find_expr_references_walker(Node *node,
context->addrs);
/* the collation might not be referenced anywhere else, either */
if (OidIsValid(fselect->resultcollid) &&
- fselect->resultcollid != DEFAULT_COLLATION_OID)
+ (fselect->resultcollid != DEFAULT_COLLATION_OID ||
+ context->track_version))
add_object_address(OCLASS_COLLATION, fselect->resultcollid, 0,
context->addrs);
}
@@ -2021,7 +2115,8 @@ find_expr_references_walker(Node *node,
context->addrs);
/* the collation might not be referenced anywhere else, either */
if (OidIsValid(relab->resultcollid) &&
- relab->resultcollid != DEFAULT_COLLATION_OID)
+ (relab->resultcollid != DEFAULT_COLLATION_OID ||
+ context->track_version))
add_object_address(OCLASS_COLLATION, relab->resultcollid, 0,
context->addrs);
}
@@ -2034,7 +2129,8 @@ find_expr_references_walker(Node *node,
context->addrs);
/* the collation might not be referenced anywhere else, either */
if (OidIsValid(iocoerce->resultcollid) &&
- iocoerce->resultcollid != DEFAULT_COLLATION_OID)
+ (iocoerce->resultcollid != DEFAULT_COLLATION_OID ||
+ context->track_version))
add_object_address(OCLASS_COLLATION, iocoerce->resultcollid, 0,
context->addrs);
}
@@ -2047,7 +2143,8 @@ find_expr_references_walker(Node *node,
context->addrs);
/* the collation might not be referenced anywhere else, either */
if (OidIsValid(acoerce->resultcollid) &&
- acoerce->resultcollid != DEFAULT_COLLATION_OID)
+ (acoerce->resultcollid != DEFAULT_COLLATION_OID ||
+ context->track_version))
add_object_address(OCLASS_COLLATION, acoerce->resultcollid, 0,
context->addrs);
/* fall through to examine arguments */
@@ -2136,7 +2233,8 @@ find_expr_references_walker(Node *node,
add_object_address(OCLASS_PROC, wc->endInRangeFunc, 0,
context->addrs);
if (OidIsValid(wc->inRangeColl) &&
- wc->inRangeColl != DEFAULT_COLLATION_OID)
+ (wc->inRangeColl != DEFAULT_COLLATION_OID ||
+ context->track_version))
add_object_address(OCLASS_COLLATION, wc->inRangeColl, 0,
context->addrs);
/* fall through to examine substructure */
@@ -2254,7 +2352,9 @@ find_expr_references_walker(Node *node,
{
Oid collid = lfirst_oid(ct);
- if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
+ if (OidIsValid(collid) &&
+ (collid != DEFAULT_COLLATION_OID ||
+ context->track_version))
add_object_address(OCLASS_COLLATION, collid, 0,
context->addrs);
}
@@ -2276,7 +2376,9 @@ find_expr_references_walker(Node *node,
{
Oid collid = lfirst_oid(ct);
- if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
+ if (OidIsValid(collid) &&
+ (collid != DEFAULT_COLLATION_OID ||
+ context->track_version))
add_object_address(OCLASS_COLLATION, collid, 0,
context->addrs);
}
@@ -2672,8 +2774,9 @@ record_object_address_dependencies(const ObjectAddress *depender,
{
eliminate_duplicate_dependencies(referenced);
recordMultipleDependencies(depender,
- referenced->refs, NULL, referenced->numrefs,
- behavior);
+ referenced->refs, referenced->numrefs,
+ behavior,
+ false);
}
/*
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 8404904710..f9d9ef96c8 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2273,7 +2273,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
*/
recordDependencyOnSingleRelExpr(&colobject, expr, RelationGetRelid(rel),
DEPENDENCY_AUTO,
- DEPENDENCY_AUTO, false);
+ DEPENDENCY_AUTO, false, false);
}
else
{
@@ -2283,7 +2283,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
*/
recordDependencyOnSingleRelExpr(&defobject, expr, RelationGetRelid(rel),
DEPENDENCY_NORMAL,
- DEPENDENCY_NORMAL, false);
+ DEPENDENCY_NORMAL, false, false);
}
/*
@@ -3527,7 +3527,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 e9955707fa..a0977df7db 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -74,6 +74,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/snapmgr.h"
@@ -1116,19 +1117,26 @@ index_create(Relation heapRelation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
}
- /* Store dependency on collations */
- /* The default collation is pinned, so don't bother recording it */
+ /*
+ * Store dependency on collations The C and posix collations are
+ * pinned, so don't bother recording them
+ */
for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
{
- if (OidIsValid(collationObjectId[i]) &&
- collationObjectId[i] != DEFAULT_COLLATION_OID)
+ List *collations = NIL;
+
+ if (OidIsValid(collationObjectId[i]))
+ collations = list_make1_oid(collationObjectId[i]);
+ else
{
- referenced.classId = CollationRelationId;
- referenced.objectId = collationObjectId[i];
- referenced.objectSubId = 0;
+ Form_pg_attribute att = TupleDescAttr(indexTupDesc, i);
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ Assert(i < indexTupDesc->natts);
+
+ collations = GetTypeCollations(att->atttypid);
}
+
+ recordDependencyOnCollations(&myself, collations);
}
/* Store dependency on operator classes */
@@ -1148,7 +1156,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 */
@@ -1158,7 +1166,7 @@ index_create(Relation heapRelation,
(Node *) indexInfo->ii_Predicate,
heapRelationId,
DEPENDENCY_NORMAL,
- DEPENDENCY_AUTO, false);
+ DEPENDENCY_AUTO, false, true);
}
}
else
@@ -1230,6 +1238,91 @@ index_create(Relation heapRelation,
return indexRelationId;
}
+static NameData *
+index_check_collation_version(const ObjectAddress *otherObject,
+ const NameData *version,
+ void *userdata)
+{
+ Oid relid = *(Oid *) 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)
+ {
+ if (strncmp(NameStr(*version),
+ "",
+ sizeof(NameData)) == 0)
+ {
+ ereport(WARNING,
+ (errmsg("index \"%s\" depends on collation \"%s\" with an unkown version, but the current version is \"%s\"",
+ get_rel_name(relid),
+ get_collation_name(otherObject->objectId),
+ NameStr(current_version)),
+ errdetail("The index may be corrupted due to changes in sort order."),
+ errhint("REINDEX to avoid the risk of corruption.")));
+ }
+ else
+ {
+ ereport(WARNING,
+ (errmsg("index \"%s\" depends on collation \"%s\" version \"%s\", but the current version is \"%s\"",
+ get_rel_name(relid),
+ get_collation_name(otherObject->objectId),
+ NameStr(*version),
+ NameStr(current_version)),
+ errdetail("The index may be corrupted due to changes in sort order."),
+ errhint("REINDEX to avoid the risk of corruption.")));
+ }
+ }
+
+ return NULL;
+}
+
+void
+index_check_collation_versions(Oid relid)
+{
+ ObjectAddress object;
+
+ object.classId = RelationRelationId;
+ object.objectId = relid;
+ object.objectSubId = 0;
+ visitDependentObjects(&object, &index_check_collation_version, &relid);
+}
+
+static NameData *
+index_update_collation_version(const ObjectAddress *otherObject,
+ const NameData *version,
+ void *userdata)
+{
+ NameData *current_version = (NameData *) userdata;
+
+ /* We only care about dependencies on collations. */
+ if (otherObject->classId != CollationRelationId)
+ return NULL;
+
+ get_collation_version_for_oid(otherObject->objectId, current_version);
+ return current_version;
+}
+
+static void
+index_update_collation_versions(Oid relid)
+{
+ ObjectAddress object;
+ NameData current_version;
+
+ object.classId = RelationRelationId;
+ object.objectId = relid;
+ object.objectSubId = 0;
+ visitDependentObjects(&object, &index_update_collation_version,
+ ¤t_version);
+}
+
/*
* index_concurrently_create_copy
*
@@ -3597,6 +3690,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);
}
/*
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 56568b0105..a271eb2e2d 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -361,7 +361,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 f1dc1143a7..d78df96304 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"
@@ -26,6 +27,7 @@
#include "miscadmin.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
+#include "utils/pg_locale.h"
#include "utils/rel.h"
@@ -44,22 +46,29 @@ recordDependencyOn(const ObjectAddress *depender,
const ObjectAddress *referenced,
DependencyType behavior)
{
- recordMultipleDependencies(depender, referenced, NULL, 1, behavior);
+ recordMultipleDependencies(depender, referenced, 1, behavior, false);
}
/*
- * As recordDependencyOn(), but also capture a version string so that changes
- * in the referenced object can be detected. The meaning of the version
- * string depends on the referenced object. Currently it is used for
- * detecting changes in collation versions.
+ * Given a list of collations, record a dependency on its underlying collation
+ * version.
*/
-void
-recordDependencyOnVersion(const ObjectAddress *depender,
- const ObjectAddress *referenced,
- const NameData *version,
- DependencyType behavior)
+void recordDependencyOnCollations(ObjectAddress *myself,
+ List *collations)
{
- recordMultipleDependencies(depender, referenced, version, 1, behavior);
+ ListCell *lc;
+
+ foreach(lc, collations)
+ {
+ ObjectAddress referenced;
+
+ referenced.classId = CollationRelationId;
+ referenced.objectId = lfirst_oid(lc);
+ referenced.objectSubId = 0;
+
+ recordMultipleDependencies(myself, &referenced, 1,
+ DEPENDENCY_NORMAL, true);
+ }
}
/*
@@ -69,9 +78,9 @@ recordDependencyOnVersion(const ObjectAddress *depender,
void
recordMultipleDependencies(const ObjectAddress *depender,
const ObjectAddress *referenced,
- const NameData *version,
int nreferenced,
- DependencyType behavior)
+ DependencyType behavior,
+ bool track_version)
{
Relation dependDesc;
CatalogIndexState indstate;
@@ -79,6 +88,7 @@ recordMultipleDependencies(const ObjectAddress *depender,
int i;
bool nulls[Natts_pg_depend];
Datum values[Natts_pg_depend];
+ NameData version;
if (nreferenced <= 0)
return; /* nothing to do */
@@ -99,12 +109,37 @@ recordMultipleDependencies(const ObjectAddress *depender,
for (i = 0; i < nreferenced; i++, referenced++)
{
+ bool ignore_systempin = false;
+
+ version.data[0] = '\0';
+
+ if (track_version)
+ {
+ /* Only dependency on a collation needs to be tracked */
+ if (referenced->classId == CollationRelationId)
+ {
+ /* C and POSIX collations don't require tracking the version */
+ if (referenced->objectId == C_COLLATION_OID ||
+ referenced->objectId == POSIX_COLLATION_OID)
+ continue;
+
+ /*
+ * 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;;
+ get_collation_version_for_oid(referenced->objectId, &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))
{
/*
* Record the Dependency. Note we don't bother to check for
@@ -117,7 +152,7 @@ recordMultipleDependencies(const ObjectAddress *depender,
values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
- values[Anum_pg_depend_refobjversion - 1] = version ? NameGetDatum(version) : CStringGetDatum("");
+ values[Anum_pg_depend_refobjversion - 1] = NameGetDatum(&version);
values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
tup = heap_form_tuple(dependDesc->rd_att, values, nulls);
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index a8c1de511f..818c53d7e4 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"
@@ -511,6 +512,52 @@ TypeCreate(Oid newTypeOid,
return address;
}
+/* Get a list of all 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 = lappend_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 = lappend_oid(result, att->attcollation);
+ else
+ result = list_concat(result, GetTypeCollations(att->atttypid));
+ }
+
+ relation_close(rel, NoLock);
+ }
+ else if (typeTup->typtype == TYPTYPE_DOMAIN)
+ {
+ Assert(OidIsValid(typeTup->typbasetype));
+
+ result = list_concat(result, GetTypeCollations(typeTup->typbasetype));
+ }
+ else if (OidIsValid(typeTup->typelem))
+ result = list_concat(result, GetTypeCollations(typeTup->typelem));
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
/*
* GenerateTypeDependencies: build the dependencies needed for a type
*
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index b7a4a1421e..19a1a6573a 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"
@@ -1501,6 +1503,43 @@ get_collation_actual_version(char collprovider, const char *collcollate)
return collversion;
}
+/*
+ * Get provider-specific collation version string given a collatoin OID.
+ */
+void
+get_collation_version_for_oid(Oid oid, NameData *output)
+{
+ HeapTuple tp;
+ const char *version;
+
+ 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));
+ }
+
+ memset(output, 0, sizeof(NameData));
+ if (version)
+ strncpy(NameStr(*output), version, sizeof(NameData));
+ ReleaseSysCache(tp);
+}
#ifdef USE_ICU
/*
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 50f8912c13..f1b3d50d1f 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -41,6 +41,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"
@@ -1469,6 +1470,10 @@ RelationInitIndexAccessInfo(Relation relation)
indcoll = (oidvector *) DatumGetPointer(indcollDatum);
memcpy(relation->rd_indcollation, indcoll->values, indnkeyatts * sizeof(Oid));
+ /* Warn if any dependent collations' versions have moved. */
+ if (!IsCatalogRelation(relation))
+ index_check_collation_versions(RelationGetRelid(relation));
+
/*
* indclass cannot be referenced directly through the C struct, because it
* comes after the variable-width indkey field. Must extract the datum
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index b5d3276e77..eb34616635 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -156,7 +156,8 @@ extern void recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
Node *expr, Oid relId,
DependencyType behavior,
DependencyType self_behavior,
- bool reverse_self);
+ bool reverse_self,
+ bool track_version);
extern ObjectClass getObjectClass(const ObjectAddress *object);
@@ -176,22 +177,28 @@ extern void sort_object_addresses(ObjectAddresses *addrs);
extern void free_object_addresses(ObjectAddresses *addrs);
+typedef NameData *(*VisitDependentObjectsFun) (const ObjectAddress *otherObject,
+ const NameData *version,
+ void *userdata);
+
+extern void visitDependentObjects(const ObjectAddress *object,
+ VisitDependentObjectsFun callback,
+ void *userdata);
+
/* in pg_depend.c */
extern void recordDependencyOn(const ObjectAddress *depender,
const ObjectAddress *referenced,
DependencyType behavior);
-extern void recordDependencyOnVersion(const ObjectAddress *depender,
- const ObjectAddress *referenced,
- const NameData *version,
- DependencyType behavior);
+extern void recordDependencyOnCollations(ObjectAddress *myself,
+ List *collations);
extern void recordMultipleDependencies(const ObjectAddress *depender,
const ObjectAddress *referenced,
- const NameData *version,
int nreferenced,
- DependencyType behavior);
+ DependencyType behavior,
+ bool track_version);
extern void recordDependencyOnCurrentExtension(const ObjectAddress *object,
bool isReplace);
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index 27d9e537d3..f8b7a5ed54 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -121,6 +121,8 @@ extern void FormIndexDatum(IndexInfo *indexInfo,
Datum *values,
bool *isnull);
+extern void index_check_collation_versions(Oid relid);
+
extern void index_build(Relation heapRelation,
Relation indexRelation,
IndexInfo *indexInfo,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 0c273a0449..a38fd566ff 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -335,6 +335,8 @@ extern void GenerateTypeDependencies(Oid typeObjectId,
bool isDependentType,
bool rebuild);
+extern List *GetTypeCollations(Oid typeObjectid);
+
extern void RenameTypeInternal(Oid typeOid, const char *newTypeName,
Oid typeNamespace);
diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h
index b4b3aa5843..023ef693e1 100644
--- a/src/include/utils/pg_locale.h
+++ b/src/include/utils/pg_locale.h
@@ -104,6 +104,7 @@ typedef struct pg_locale_struct *pg_locale_t;
extern pg_locale_t pg_newlocale_from_collation(Oid collid);
extern char *get_collation_actual_version(char collprovider, const char *collcollate);
+extern void get_collation_version_for_oid(Oid collid, NameData *output);
#ifdef USE_ICU
extern int32_t icu_to_uchar(UChar **buff_uchar, const char *buff, size_t nbytes);
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index 1cdb7a9663..2f30d66935 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -1990,15 +1990,17 @@ WHERE classid = 'pg_class'::regclass AND
obj | objref | deptype
------------------------------------------+------------------------------------------------------------+---------
index concur_reindex_ind1 | constraint concur_reindex_ind1 on table concur_reindex_tab | i
+ index concur_reindex_ind2 | collation "default" | n
index concur_reindex_ind2 | column c2 of table concur_reindex_tab | a
index concur_reindex_ind3 | column c1 of table concur_reindex_tab | a
index concur_reindex_ind3 | table concur_reindex_tab | a
+ index concur_reindex_ind4 | collation "default" | n
index concur_reindex_ind4 | column c1 of table concur_reindex_tab | a
index concur_reindex_ind4 | column c1 of table concur_reindex_tab | a
index concur_reindex_ind4 | column c2 of table concur_reindex_tab | a
materialized view concur_reindex_matview | schema public | n
table concur_reindex_tab | schema public | n
-(9 rows)
+(11 rows)
REINDEX INDEX CONCURRENTLY concur_reindex_ind1;
REINDEX TABLE CONCURRENTLY concur_reindex_tab;
@@ -2018,15 +2020,17 @@ WHERE classid = 'pg_class'::regclass AND
obj | objref | deptype
------------------------------------------+------------------------------------------------------------+---------
index concur_reindex_ind1 | constraint concur_reindex_ind1 on table concur_reindex_tab | i
+ index concur_reindex_ind2 | collation "default" | n
index concur_reindex_ind2 | column c2 of table concur_reindex_tab | a
index concur_reindex_ind3 | column c1 of table concur_reindex_tab | a
index concur_reindex_ind3 | table concur_reindex_tab | a
+ index concur_reindex_ind4 | collation "default" | n
index concur_reindex_ind4 | column c1 of table concur_reindex_tab | a
index concur_reindex_ind4 | column c1 of table concur_reindex_tab | a
index concur_reindex_ind4 | column c2 of table concur_reindex_tab | a
materialized view concur_reindex_matview | schema public | n
table concur_reindex_tab | schema public | n
-(9 rows)
+(11 rows)
-- Check that comments are preserved
CREATE TABLE testcomment (i int);
--
2.20.1