mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Preserve replica identity index across ALTER TABLE rewrite
If an index was explicitly set as replica identity index, this setting was lost when a table was rewritten by ALTER TABLE. Because this setting is part of pg_index but actually controlled by ALTER TABLE (not part of CREATE INDEX, say), we have to do some extra work to restore it. Based-on-patch-by: Quan Zongliang <quanzongliang@gmail.com> Reviewed-by: Euler Taveira <euler.taveira@2ndquadrant.com> Discussion: https://www.postgresql.org/message-id/flat/c70fcab2-4866-0d9f-1d01-e75e189db342@gmail.com
This commit is contained in:
		@@ -166,6 +166,7 @@ typedef struct AlteredTableInfo
 | 
			
		||||
	List	   *changedConstraintDefs;	/* string definitions of same */
 | 
			
		||||
	List	   *changedIndexOids;		/* OIDs of indexes to rebuild */
 | 
			
		||||
	List	   *changedIndexDefs;		/* string definitions of same */
 | 
			
		||||
	char	   *replicaIdentityIndex;	/* index to reset as REPLICA IDENTITY */
 | 
			
		||||
} AlteredTableInfo;
 | 
			
		||||
 | 
			
		||||
/* Struct describing one new constraint to check in Phase 3 scan */
 | 
			
		||||
@@ -8566,6 +8567,22 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 | 
			
		||||
	return address;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Subroutine for ATExecAlterColumnType: remember that a replica identity
 | 
			
		||||
 * needs to be reset.
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
 | 
			
		||||
{
 | 
			
		||||
	if (!get_index_isreplident(indoid))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (tab->replicaIdentityIndex)
 | 
			
		||||
		elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
 | 
			
		||||
 | 
			
		||||
	tab->replicaIdentityIndex = get_rel_name(indoid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
 | 
			
		||||
 * to be rebuilt (which we might already know).
 | 
			
		||||
@@ -8585,6 +8602,7 @@ RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab,
 | 
			
		||||
	{
 | 
			
		||||
		/* OK, capture the constraint's existing definition string */
 | 
			
		||||
		char	   *defstring = pg_get_constraintdef_command(conoid);
 | 
			
		||||
		Oid			indoid;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Put NORMAL dependencies at the front of the list and AUTO
 | 
			
		||||
@@ -8608,6 +8626,10 @@ RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab,
 | 
			
		||||
			tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
 | 
			
		||||
												 defstring);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		indoid = get_constraint_index(conoid);
 | 
			
		||||
		if (OidIsValid(indoid))
 | 
			
		||||
			RememberReplicaIdentityForRebuilding(indoid, tab);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -8651,6 +8673,8 @@ RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
 | 
			
		||||
												indoid);
 | 
			
		||||
			tab->changedIndexDefs = lappend(tab->changedIndexDefs,
 | 
			
		||||
											defstring);
 | 
			
		||||
 | 
			
		||||
			RememberReplicaIdentityForRebuilding(indoid, tab);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -8844,6 +8868,24 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
 | 
			
		||||
							 wqueue, lockmode, tab->rewrite);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Queue up command to restore replica identity index marking
 | 
			
		||||
	 */
 | 
			
		||||
	if (tab->replicaIdentityIndex)
 | 
			
		||||
	{
 | 
			
		||||
		AlterTableCmd *cmd = makeNode(AlterTableCmd);
 | 
			
		||||
		ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
 | 
			
		||||
 | 
			
		||||
		subcmd->identity_type = REPLICA_IDENTITY_INDEX;
 | 
			
		||||
		subcmd->name = tab->replicaIdentityIndex;
 | 
			
		||||
		cmd->subtype = AT_ReplicaIdentity;
 | 
			
		||||
		cmd->def = (Node *) subcmd;
 | 
			
		||||
 | 
			
		||||
		/* do it after indexes and constraints */
 | 
			
		||||
		tab->subcmds[AT_PASS_OLD_CONSTR] =
 | 
			
		||||
			lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Now we can drop the existing constraints and indexes --- constraints
 | 
			
		||||
	 * first, since some of them might depend on the indexes.  In fact, we
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								src/backend/utils/cache/lsyscache.c
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								src/backend/utils/cache/lsyscache.c
									
									
									
									
										vendored
									
									
								
							@@ -3045,3 +3045,28 @@ get_range_collation(Oid rangeOid)
 | 
			
		||||
	else
 | 
			
		||||
		return InvalidOid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*				---------- PG_INDEX CACHE ----------				 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * get_index_isreplident
 | 
			
		||||
 *
 | 
			
		||||
 *		Given the index OID, return pg_index.indisreplident.
 | 
			
		||||
 */
 | 
			
		||||
bool
 | 
			
		||||
get_index_isreplident(Oid index_oid)
 | 
			
		||||
{
 | 
			
		||||
	HeapTuple		tuple;
 | 
			
		||||
	Form_pg_index	rd_index;
 | 
			
		||||
	bool			result;
 | 
			
		||||
 | 
			
		||||
	tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
 | 
			
		||||
	if (!HeapTupleIsValid(tuple))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	rd_index = (Form_pg_index) GETSTRUCT(tuple);
 | 
			
		||||
	result = rd_index->indisreplident;
 | 
			
		||||
	ReleaseSysCache(tuple);
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -158,6 +158,7 @@ extern char *get_namespace_name(Oid nspid);
 | 
			
		||||
extern char *get_namespace_name_or_temp(Oid nspid);
 | 
			
		||||
extern Oid	get_range_subtype(Oid rangeOid);
 | 
			
		||||
extern Oid	get_range_collation(Oid rangeOid);
 | 
			
		||||
extern bool	get_index_isreplident(Oid index_oid);
 | 
			
		||||
 | 
			
		||||
#define type_is_array(typid)  (get_element_type(typid) != InvalidOid)
 | 
			
		||||
/* type_is_array_domain accepts both plain arrays and domains over arrays */
 | 
			
		||||
 
 | 
			
		||||
@@ -187,5 +187,51 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass;
 | 
			
		||||
 n
 | 
			
		||||
(1 row)
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
-- Test that ALTER TABLE rewrite preserves nondefault replica identity
 | 
			
		||||
---
 | 
			
		||||
-- constraint variant
 | 
			
		||||
CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL);
 | 
			
		||||
ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key;
 | 
			
		||||
\d test_replica_identity2
 | 
			
		||||
Table "public.test_replica_identity2"
 | 
			
		||||
 Column |  Type   | Modifiers 
 | 
			
		||||
--------+---------+-----------
 | 
			
		||||
 id     | integer | not null
 | 
			
		||||
Indexes:
 | 
			
		||||
    "test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY
 | 
			
		||||
 | 
			
		||||
ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint;
 | 
			
		||||
\d test_replica_identity2
 | 
			
		||||
Table "public.test_replica_identity2"
 | 
			
		||||
 Column |  Type  | Modifiers 
 | 
			
		||||
--------+--------+-----------
 | 
			
		||||
 id     | bigint | not null
 | 
			
		||||
Indexes:
 | 
			
		||||
    "test_replica_identity2_id_key" UNIQUE CONSTRAINT, btree (id) REPLICA IDENTITY
 | 
			
		||||
 | 
			
		||||
-- straight index variant
 | 
			
		||||
CREATE TABLE test_replica_identity3 (id int NOT NULL);
 | 
			
		||||
CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id);
 | 
			
		||||
ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key;
 | 
			
		||||
\d test_replica_identity3
 | 
			
		||||
Table "public.test_replica_identity3"
 | 
			
		||||
 Column |  Type   | Modifiers 
 | 
			
		||||
--------+---------+-----------
 | 
			
		||||
 id     | integer | not null
 | 
			
		||||
Indexes:
 | 
			
		||||
    "test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY
 | 
			
		||||
 | 
			
		||||
ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint;
 | 
			
		||||
\d test_replica_identity3
 | 
			
		||||
Table "public.test_replica_identity3"
 | 
			
		||||
 Column |  Type  | Modifiers 
 | 
			
		||||
--------+--------+-----------
 | 
			
		||||
 id     | bigint | not null
 | 
			
		||||
Indexes:
 | 
			
		||||
    "test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY
 | 
			
		||||
 | 
			
		||||
DROP TABLE test_replica_identity;
 | 
			
		||||
DROP TABLE test_replica_identity2;
 | 
			
		||||
DROP TABLE test_replica_identity3;
 | 
			
		||||
DROP TABLE test_replica_identity_othertable;
 | 
			
		||||
 
 | 
			
		||||
@@ -79,5 +79,26 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass;
 | 
			
		||||
ALTER TABLE test_replica_identity REPLICA IDENTITY NOTHING;
 | 
			
		||||
SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass;
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
-- Test that ALTER TABLE rewrite preserves nondefault replica identity
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
-- constraint variant
 | 
			
		||||
CREATE TABLE test_replica_identity2 (id int UNIQUE NOT NULL);
 | 
			
		||||
ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_id_key;
 | 
			
		||||
\d test_replica_identity2
 | 
			
		||||
ALTER TABLE test_replica_identity2 ALTER COLUMN id TYPE bigint;
 | 
			
		||||
\d test_replica_identity2
 | 
			
		||||
 | 
			
		||||
-- straight index variant
 | 
			
		||||
CREATE TABLE test_replica_identity3 (id int NOT NULL);
 | 
			
		||||
CREATE UNIQUE INDEX test_replica_identity3_id_key ON test_replica_identity3 (id);
 | 
			
		||||
ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_identity3_id_key;
 | 
			
		||||
\d test_replica_identity3
 | 
			
		||||
ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint;
 | 
			
		||||
\d test_replica_identity3
 | 
			
		||||
 | 
			
		||||
DROP TABLE test_replica_identity;
 | 
			
		||||
DROP TABLE test_replica_identity2;
 | 
			
		||||
DROP TABLE test_replica_identity3;
 | 
			
		||||
DROP TABLE test_replica_identity_othertable;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user