From b42f09fc80ce0848233753bbf38643b88e208786 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Tue, 15 Jul 2014 13:24:07 -0400
Subject: [PATCH] Fix REASSIGN OWNED for text search objects
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Trying to reassign objects owned by a user that had text search
dictionaries or configurations used to fail with:
ERROR:  unexpected classid 3600
or
ERROR:  unexpected classid 3602

Fix by adding cases for those object types in a switch in pg_shdepend.c.

Both REASSIGN OWNED and text search objects go back all the way to 8.1,
so backpatch to all supported branches.  In 9.3 the alter-owner code was
made generic, so the required change in recent branches is pretty
simple; however, for 9.2 and older ones we need some additional
reshuffling to enable specifying objects by OID rather than name.

Text search templates and parsers are not owned objects, so there's no
change required for them.

Per bug #9749 reported by Michal Novotný
---
 src/backend/catalog/pg_shdepend.c  | 10 ++++
 src/backend/commands/tsearchcmds.c | 92 +++++++++++++++++++++++-------
 src/include/commands/defrem.h      |  2 +
 3 files changed, 82 insertions(+), 22 deletions(-)

diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
index 1843a82db79..8ca2c3ee6d6 100644
--- a/src/backend/catalog/pg_shdepend.c
+++ b/src/backend/catalog/pg_shdepend.c
@@ -37,6 +37,8 @@
 #include "catalog/pg_proc.h"
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_tablespace.h"
+#include "catalog/pg_ts_config.h"
+#include "catalog/pg_ts_dict.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
 #include "commands/collationcmds.h"
@@ -1405,6 +1407,14 @@ shdepReassignOwned(List *roleids, Oid newrole)
 					AlterExtensionOwner_oid(sdepForm->objid, newrole);
 					break;
 
+				case TSConfigRelationId:
+					AlterTSConfigurationOwner_oid(sdepForm->objid, newrole);
+					break;
+
+				case TSDictionaryRelationId:
+					AlterTSDictionaryOwner_oid(sdepForm->objid, newrole);
+					break;
+
 				default:
 					elog(ERROR, "unexpected classid %u", sdepForm->classid);
 					break;
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index 86cb8704da8..5b6b2b4d720 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -810,22 +810,16 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt)
 }
 
 /*
- * ALTER TEXT SEARCH DICTIONARY OWNER
+ * Internal routine for changing the owner of a text search dictionary
  */
-void
-AlterTSDictionaryOwner(List *name, Oid newOwnerId)
+static void
+AlterTSDictionaryOwner_internal(Relation rel, Oid dictId, Oid newOwnerId)
 {
 	HeapTuple	tup;
-	Relation	rel;
-	Oid			dictId;
 	Oid			namespaceOid;
 	AclResult	aclresult;
 	Form_pg_ts_dict form;
 
-	rel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
-
-	dictId = get_ts_dict_oid(name, false);
-
 	tup = SearchSysCacheCopy1(TSDICTOID, ObjectIdGetDatum(dictId));
 
 	if (!HeapTupleIsValid(tup)) /* should not happen */
@@ -843,7 +837,7 @@ AlterTSDictionaryOwner(List *name, Oid newOwnerId)
 			/* must be owner */
 			if (!pg_ts_dict_ownercheck(dictId, GetUserId()))
 				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
-							   NameListToString(name));
+							   NameStr(form->dictname));
 
 			/* Must be able to become new owner */
 			check_is_member_of_role(GetUserId(), newOwnerId);
@@ -865,10 +859,41 @@ AlterTSDictionaryOwner(List *name, Oid newOwnerId)
 								newOwnerId);
 	}
 
-	heap_close(rel, NoLock);
 	heap_freetuple(tup);
 }
 
+/*
+ * ALTER TEXT SEARCH DICTIONARY OWNER
+ */
+void
+AlterTSDictionaryOwner(List *name, Oid newOwnerId)
+{
+	Relation	rel;
+	Oid			dictId;
+
+	rel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
+	dictId = get_ts_dict_oid(name, false);
+
+	AlterTSDictionaryOwner_internal(rel, dictId, newOwnerId);
+
+	heap_close(rel, NoLock);
+}
+
+/*
+ * Change text search dictionary owner, by OID
+ */
+void
+AlterTSDictionaryOwner_oid(Oid dictId, Oid newOwnerId)
+{
+	Relation	rel;
+
+	rel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
+
+	AlterTSDictionaryOwner_internal(rel, dictId, newOwnerId);
+
+	heap_close(rel, NoLock);
+}
+
 /* ---------------------- TS Template commands -----------------------*/
 
 /*
@@ -1578,22 +1603,16 @@ RemoveTSConfigurationById(Oid cfgId)
 }
 
 /*
- * ALTER TEXT SEARCH CONFIGURATION OWNER
+ * Internal routine for changing the owner of a text search configuration
  */
-void
-AlterTSConfigurationOwner(List *name, Oid newOwnerId)
+static void
+AlterTSConfigurationOwner_internal(Relation rel, Oid cfgId, Oid newOwnerId)
 {
 	HeapTuple	tup;
-	Relation	rel;
-	Oid			cfgId;
 	AclResult	aclresult;
 	Oid			namespaceOid;
 	Form_pg_ts_config form;
 
-	rel = heap_open(TSConfigRelationId, RowExclusiveLock);
-
-	cfgId = get_ts_config_oid(name, false);
-
 	tup = SearchSysCacheCopy1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
 
 	if (!HeapTupleIsValid(tup)) /* should not happen */
@@ -1611,7 +1630,7 @@ AlterTSConfigurationOwner(List *name, Oid newOwnerId)
 			/* must be owner */
 			if (!pg_ts_config_ownercheck(cfgId, GetUserId()))
 				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION,
-							   NameListToString(name));
+							   NameStr(form->cfgname));
 
 			/* Must be able to become new owner */
 			check_is_member_of_role(GetUserId(), newOwnerId);
@@ -1633,10 +1652,39 @@ AlterTSConfigurationOwner(List *name, Oid newOwnerId)
 								newOwnerId);
 	}
 
-	heap_close(rel, NoLock);
 	heap_freetuple(tup);
 }
 
+/*
+ * ALTER TEXT SEARCH CONFIGURATION OWNER
+ */
+void
+AlterTSConfigurationOwner(List *name, Oid newOwnerId)
+{
+	Relation	rel;
+	Oid			cfgId;
+
+	rel = heap_open(TSConfigRelationId, RowExclusiveLock);
+	cfgId = get_ts_config_oid(name, false);
+
+	AlterTSConfigurationOwner_internal(rel, cfgId, newOwnerId);
+
+	heap_close(rel, NoLock);
+}
+
+void
+AlterTSConfigurationOwner_oid(Oid cfgId, Oid newOwnerId)
+{
+	Relation	rel;
+
+	rel = heap_open(TSConfigRelationId, RowExclusiveLock);
+
+	AlterTSConfigurationOwner_internal(rel, cfgId, newOwnerId);
+
+	heap_close(rel, NoLock);
+}
+
+
 /*
  * ALTER TEXT SEARCH CONFIGURATION - main entry point
  */
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index a00fd376206..cb901a1b21c 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -107,6 +107,7 @@ extern void RenameTSDictionary(List *oldname, const char *newname);
 extern void RemoveTSDictionaryById(Oid dictId);
 extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt);
 extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId);
+extern void AlterTSDictionaryOwner_oid(Oid dictId, Oid newOwnerId);
 extern void AlterTSDictionaryNamespace(List *name, const char *newschema);
 extern Oid	AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid);
 
@@ -121,6 +122,7 @@ extern void RenameTSConfiguration(List *oldname, const char *newname);
 extern void RemoveTSConfigurationById(Oid cfgId);
 extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
 extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId);
+extern void AlterTSConfigurationOwner_oid(Oid cfgId, Oid newOwnerId);
 extern void AlterTSConfigurationNamespace(List *name, const char *newschema);
 extern Oid	AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid);
 
-- 
GitLab