From 349bdf1827e88781501dac424addd82d7fb081ae Mon Sep 17 00:00:00 2001
From: Julien Rouhaud <julien.rouhaud@free.fr>
Date: Wed, 11 Dec 2019 13:54:32 +0100
Subject: [PATCH 6/6] Add a new ALTER INDEX name DEPENDS ON COLLATION name
 CURRENT VERSION

This command allows privileged users to specifify that the currently installed
collation library version, for a specific collation, is binary compatible with
the one that was installed when the specified index was built for the last
time.

Author: Julien Rouhaud
Reviewed-by:
Discussion: https://postgr.es/m/CAEepm%3D0uEQCpfq_%2BLYFBdArCe4Ot98t1aR4eYiYTe%3DyavQygiQ%40mail.gmail.com
---
 doc/src/sgml/ref/alter_index.sgml | 13 +++++++++++++
 src/backend/commands/tablecmds.c  | 14 ++++++++++++--
 src/backend/parser/gram.y         |  9 +++++++++
 src/bin/psql/tab-complete.c       | 26 +++++++++++++++++++++++++-
 src/include/nodes/parsenodes.h    |  5 ++++-
 5 files changed, 63 insertions(+), 4 deletions(-)

diff --git a/doc/src/sgml/ref/alter_index.sgml b/doc/src/sgml/ref/alter_index.sgml
index 6d34dbb74e..f4a4333ab1 100644
--- a/doc/src/sgml/ref/alter_index.sgml
+++ b/doc/src/sgml/ref/alter_index.sgml
@@ -25,6 +25,7 @@ ALTER INDEX [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENA
 ALTER INDEX [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET TABLESPACE <replaceable class="parameter">tablespace_name</replaceable>
 ALTER INDEX <replaceable class="parameter">name</replaceable> ATTACH PARTITION <replaceable class="parameter">index_name</replaceable>
 ALTER INDEX <replaceable class="parameter">name</replaceable> DEPENDS ON EXTENSION <replaceable class="parameter">extension_name</replaceable>
+ALTER INDEX <replaceable class="parameter">name</replaceable> DEPENDS ON COLLATION <replaceable class="parameter">collation_name</replaceable> CURRENT VERSION
 ALTER INDEX [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] )
 ALTER INDEX [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RESET ( <replaceable class="parameter">storage_parameter</replaceable> [, ... ] )
 ALTER INDEX [ IF EXISTS ] <replaceable class="parameter">name</replaceable> ALTER [ COLUMN ] <replaceable class="parameter">column_number</replaceable>
@@ -109,6 +110,18 @@ ALTER INDEX ALL IN TABLESPACE <replaceable class="parameter">name</replaceable>
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>DEPENDS ON COLLATION</literal></term>
+    <listitem>
+     <para>
+      This form update the index existing dependency on a specific collation,
+      to specificy the the currently installed collation version is compatible
+      with the version used the last time the index was built.  Be aware that
+      an incorrect use of this form can hide a corruption on the index.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><literal>SET ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] )</literal></term>
     <listitem>
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 67d1fd5732..5a7da64f95 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -92,6 +92,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"
@@ -3973,7 +3974,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			break;
 		case AT_DependsOnCollationVersion:	/* DEPENDS ON COLLATION ...
 											 * [UNKNOWN VERSION | VERSION ...] */
-			if (!IsBinaryUpgrade)
+			if (!IsBinaryUpgrade && cmd->version)
 				ereport(ERROR,
 						(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
 						 (errmsg("command can only be called when server is in binary upgrade mode"))));
@@ -16767,7 +16768,16 @@ ATExecDependsOnCollationVersion(Relation rel, List *coll, char *version)
 		forced_dependency.oid = InvalidOid;
 	else
 		forced_dependency.oid = get_collation_oid(coll, false);
-	strncpy(NameStr(forced_dependency.version), version, sizeof(NameData));
+
+	if (version)
+		strncpy(NameStr(forced_dependency.version), version, sizeof(NameData));
+	else
+	{
+		/* Retrieve the current version for the CURRENT VERSION case. */
+		Assert(OidIsValid(forced_dependency.oid));
+		get_collation_version_for_oid(forced_dependency.oid,
+				&forced_dependency.version);
+	}
 
 	object.classId = RelationRelationId;
 	object.objectId = rel->rd_id;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4f53737e66..4d09f7bdac 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -2567,6 +2567,15 @@ alter_table_cmd:
 					n->version = "";
 					$$ = (Node *)n;
 				}
+			/* ALTER INDEX <name> DEPENDS ON ANY COLLATION UNKNOWN VERSION */
+			| DEPENDS ON COLLATION any_name CURRENT_P VERSION_P
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_DependsOnCollationVersion;
+					n->object = $4;
+					n->version = NULL;
+					$$ = (Node *)n;
+				}
 			| alter_generic_options
 				{
 					AlterTableCmd *n = makeNode(AlterTableCmd);
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index df26826993..e869f3fc47 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -44,6 +44,7 @@
 
 #include "catalog/pg_am_d.h"
 #include "catalog/pg_class_d.h"
+#include "catalog/pg_collation_d.h"
 #include "common.h"
 #include "libpq-fe.h"
 #include "pqexpbuffer.h"
@@ -792,6 +793,20 @@ static const SchemaQuery Query_for_list_of_statistics = {
 "       (SELECT tgrelid FROM pg_catalog.pg_trigger "\
 "         WHERE pg_catalog.quote_ident(tgname)='%s')"
 
+/* the silly-looking length condition is just to eat up the current word */
+#define Query_for_list_of_colls_for_one_index \
+" SELECT DISTINCT pg_catalog.quote_ident(coll.collname) " \
+"   FROM pg_catalog.pg_depend d, pg_catalog.pg_collation coll, " \
+"     pg_catalog.pg_class c" \
+" WHERE (%d = pg_catalog.length('%s'))" \
+"   AND d.refclassid = " CppAsString2(CollationRelationId) \
+"   AND d.refobjid = coll.oid " \
+"   AND d.classid = " CppAsString2(RelationRelationId) \
+"   AND d.objid = c.oid " \
+"   AND c.relkind = " CppAsString2(RELKIND_INDEX) \
+"   AND pg_catalog.pg_table_is_visible(c.oid) " \
+"   AND c.relname = '%s'"
+
 #define Query_for_list_of_ts_configurations \
 "SELECT pg_catalog.quote_ident(cfgname) FROM pg_catalog.pg_ts_config "\
 " WHERE substring(pg_catalog.quote_ident(cfgname),1,%d)='%s'"
@@ -1643,7 +1658,7 @@ psql_completion(const char *text, int start, int end)
 	/* ALTER INDEX <name> */
 	else if (Matches("ALTER", "INDEX", MatchAny))
 		COMPLETE_WITH("ALTER COLUMN", "OWNER TO", "RENAME TO", "SET",
-					  "RESET", "ATTACH PARTITION");
+					  "RESET", "ATTACH PARTITION", "DEPENDS ON COLLATION");
 	else if (Matches("ALTER", "INDEX", MatchAny, "ATTACH"))
 		COMPLETE_WITH("PARTITION");
 	else if (Matches("ALTER", "INDEX", MatchAny, "ATTACH", "PARTITION"))
@@ -1689,6 +1704,15 @@ psql_completion(const char *text, int start, int end)
 					  "buffering =",	/* GiST */
 					  "pages_per_range =", "autosummarize ="	/* BRIN */
 			);
+	/* ALTER INDEX <name> DEPENDS ON COLLATION */
+	else if (Matches("ALTER", "INDEX", MatchAny, "DEPENDS", "ON", "COLLATION"))
+	{
+		completion_info_charp = prev4_wd;
+		COMPLETE_WITH_QUERY(Query_for_list_of_colls_for_one_index);
+	}
+	/* ALTER INDEX <name> DEPENDS ON COLLATION <name> */
+	else if (Matches("ALTER", "INDEX", MatchAny, "DEPENDS", "ON", "COLLATION", MatchAny))
+		COMPLETE_WITH("CURRENT VERSION");
 
 	/* ALTER LANGUAGE <name> */
 	else if (Matches("ALTER", "LANGUAGE", MatchAny))
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 4e559fea09..c16d633ed8 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1844,7 +1844,10 @@ typedef struct AlterTableCmd	/* one subcommand of an ALTER TABLE */
 	List	   *object;			/* collation to act on if it's a collation */
 	int16		num;			/* attribute number for columns referenced by
 								 * number */
-	char	   *version;		/* version reference for collation dependency */
+	char	   *version;		/* version reference for collation dependency.
+								 * An empty string is used to represent an
+								 * unknown version, and a NULL pointer is used
+								 * to represent the current version */
 	RoleSpec   *newowner;
 	Node	   *def;			/* definition of new column, index,
 								 * constraint, or parent table */
-- 
2.20.1

