Skip to content

Commit e415916

Browse files
committed
Block ALTER TABLE .. DROP NOT NULL on columns in replica identity index
Replica identities that depend directly on an index rely on a set of properties, one of them being that all the columns defined in this index have to be marked as NOT NULL. There was a hole in the logic with ALTER TABLE DROP NOT NULL, where it was possible to remove the NOT NULL property of a column part of an index used as replica identity, so block it to avoid problems with logical decoding down the road. The same check was already done columns part of a primary key, so the fix is straight-forward. Author: Haiying Tang, Hou Zhijie Reviewed-by: Dilip Kumar, Michael Paquier Discussion: https://postgr.es/m/OS0PR01MB6113338C102BEE8B2FFC5BD9FB619@OS0PR01MB6113.jpnprd01.prod.outlook.com Backpatch-through: 10
1 parent d2198b4 commit e415916

File tree

3 files changed

+30
-9
lines changed

3 files changed

+30
-9
lines changed

src/backend/commands/tablecmds.c

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7020,7 +7020,8 @@ ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
70207020
colName, RelationGetRelationName(rel))));
70217021

70227022
/*
7023-
* Check that the attribute is not in a primary key
7023+
* Check that the attribute is not in a primary key or in an index used as
7024+
* a replica identity.
70247025
*
70257026
* Note: we'll throw error even if the pkey index is not valid.
70267027
*/
@@ -7040,20 +7041,32 @@ ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
70407041
elog(ERROR, "cache lookup failed for index %u", indexoid);
70417042
indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
70427043

7043-
/* If the index is not a primary key, skip the check */
7044-
if (indexStruct->indisprimary)
7044+
/*
7045+
* If the index is not a primary key or an index used as replica
7046+
* identity, skip the check.
7047+
*/
7048+
if (indexStruct->indisprimary || indexStruct->indisreplident)
70457049
{
70467050
/*
7047-
* Loop over each attribute in the primary key and see if it
7048-
* matches the to-be-altered attribute
7051+
* Loop over each attribute in the primary key or the index used
7052+
* as replica identity and see if it matches the to-be-altered
7053+
* attribute.
70497054
*/
70507055
for (i = 0; i < indexStruct->indnkeyatts; i++)
70517056
{
70527057
if (indexStruct->indkey.values[i] == attnum)
7053-
ereport(ERROR,
7054-
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7055-
errmsg("column \"%s\" is in a primary key",
7056-
colName)));
7058+
{
7059+
if (indexStruct->indisprimary)
7060+
ereport(ERROR,
7061+
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7062+
errmsg("column \"%s\" is in a primary key",
7063+
colName)));
7064+
else
7065+
ereport(ERROR,
7066+
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7067+
errmsg("column \"%s\" is in index used as replica identity",
7068+
colName)));
7069+
}
70577070
}
70587071
}
70597072

src/test/regress/expected/replica_identity.out

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint;
223223
Indexes:
224224
"test_replica_identity3_id_key" UNIQUE, btree (id) REPLICA IDENTITY
225225

226+
-- ALTER TABLE DROP NOT NULL is not allowed for columns part of an index
227+
-- used as replica identity.
228+
ALTER TABLE test_replica_identity3 ALTER COLUMN id DROP NOT NULL;
229+
ERROR: column "id" is in index used as replica identity
226230
DROP TABLE test_replica_identity;
227231
DROP TABLE test_replica_identity2;
228232
DROP TABLE test_replica_identity3;

src/test/regress/sql/replica_identity.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ ALTER TABLE test_replica_identity3 REPLICA IDENTITY USING INDEX test_replica_ide
9494
ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint;
9595
\d test_replica_identity3
9696

97+
-- ALTER TABLE DROP NOT NULL is not allowed for columns part of an index
98+
-- used as replica identity.
99+
ALTER TABLE test_replica_identity3 ALTER COLUMN id DROP NOT NULL;
100+
97101
DROP TABLE test_replica_identity;
98102
DROP TABLE test_replica_identity2;
99103
DROP TABLE test_replica_identity3;

0 commit comments

Comments
 (0)