Skip to content

Commit cb8962c

Browse files
committed
Further fix ALTER COLUMN TYPE's handling of indexes and index constraints.
This patch reverts all the code changes of commit e76de88, which turns out to have been seriously misguided. We can't wait till later to compute the definition string for an index; we must capture that before applying the data type change for any column it depends on, else ruleutils.c will deliverr wrong/misleading results. (This fine point was documented nowhere, of course.) I'd also managed to forget that ATExecAlterColumnType executes once per ALTER COLUMN TYPE clause, not once per statement; which resulted in the code being basically completely broken for any case in which multiple ALTER COLUMN TYPE clauses are applied to a table having non-constraint indexes that must be rebuilt. Through very bad luck, none of the existing test cases nor the ones added by e76de88 caught that, but of course it was soon found in the field. The previous patch also had an implicit assumption that if a constraint's index had a dependency on a table column, so would the constraint --- but that isn't actually true, so it didn't fix such cases. Instead of trying to delete unneeded index dependencies later, do the is-there-a-constraint lookup immediately on seeing an index dependency, and switch to remembering the constraint if so. In the unusual case of multiple column dependencies for a constraint index, this will result in duplicate constraint lookups, but that's not that horrible compared to all the other work that happens here. Besides, such cases did not work at all before, so it's hard to argue that they're performance-critical for anyone. Per bug #15865 from Keith Fiske. As before, back-patch to all supported branches. Discussion: https://postgr.es/m/15865-17940eacc8f8b081@postgresql.org
1 parent 05399b1 commit cb8962c

File tree

3 files changed

+125
-93
lines changed

3 files changed

+125
-93
lines changed

src/backend/commands/tablecmds.c

Lines changed: 95 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,9 @@ static void ATPrepAlterColumnType(List **wqueue,
415415
static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
416416
static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
417417
AlterTableCmd *cmd, LOCKMODE lockmode);
418+
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab,
419+
DependencyType deptype);
420+
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
418421
static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
419422
List *options, LOCKMODE lockmode);
420423
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
@@ -9021,9 +9024,6 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
90219024
SysScanDesc scan;
90229025
HeapTuple depTup;
90239026
ObjectAddress address;
9024-
ListCell *lc;
9025-
ListCell *prev;
9026-
ListCell *next;
90279027

90289028
attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
90299029

@@ -9092,11 +9092,6 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
90929092
* performed all the individual ALTER TYPE operations. We have to save
90939093
* the info before executing ALTER TYPE, though, else the deparser will
90949094
* get confused.
9095-
*
9096-
* There could be multiple entries for the same object, so we must check
9097-
* to ensure we process each one only once. Note: we assume that an index
9098-
* that implements a constraint will not show a direct dependency on the
9099-
* column.
91009095
*/
91019096
depRel = heap_open(DependRelationId, RowExclusiveLock);
91029097

@@ -9137,20 +9132,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
91379132

91389133
if (relKind == RELKIND_INDEX)
91399134
{
9140-
/*
9141-
* Indexes that are directly dependent on the table
9142-
* might be regular indexes or constraint indexes.
9143-
* Constraint indexes typically have only indirect
9144-
* dependencies; but there are exceptions, notably
9145-
* partial exclusion constraints. Hence we must check
9146-
* whether the index depends on any constraint that's
9147-
* due to be rebuilt, which we'll do below after we've
9148-
* found all such constraints.
9149-
*/
91509135
Assert(foundObject.objectSubId == 0);
9151-
tab->changedIndexOids =
9152-
list_append_unique_oid(tab->changedIndexOids,
9153-
foundObject.objectId);
9136+
RememberIndexForRebuilding(foundObject.objectId, tab);
91549137
}
91559138
else if (relKind == RELKIND_SEQUENCE)
91569139
{
@@ -9171,39 +9154,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
91719154

91729155
case OCLASS_CONSTRAINT:
91739156
Assert(foundObject.objectSubId == 0);
9174-
if (!list_member_oid(tab->changedConstraintOids,
9175-
foundObject.objectId))
9176-
{
9177-
char *defstring = pg_get_constraintdef_command(foundObject.objectId);
9178-
9179-
/*
9180-
* Put NORMAL dependencies at the front of the list and
9181-
* AUTO dependencies at the back. This makes sure that
9182-
* foreign-key constraints depending on this column will
9183-
* be dropped before unique or primary-key constraints of
9184-
* the column; which we must have because the FK
9185-
* constraints depend on the indexes belonging to the
9186-
* unique constraints.
9187-
*/
9188-
if (foundDep->deptype == DEPENDENCY_NORMAL)
9189-
{
9190-
tab->changedConstraintOids =
9191-
lcons_oid(foundObject.objectId,
9192-
tab->changedConstraintOids);
9193-
tab->changedConstraintDefs =
9194-
lcons(defstring,
9195-
tab->changedConstraintDefs);
9196-
}
9197-
else
9198-
{
9199-
tab->changedConstraintOids =
9200-
lappend_oid(tab->changedConstraintOids,
9201-
foundObject.objectId);
9202-
tab->changedConstraintDefs =
9203-
lappend(tab->changedConstraintDefs,
9204-
defstring);
9205-
}
9206-
}
9157+
RememberConstraintForRebuilding(foundObject.objectId, tab,
9158+
foundDep->deptype);
92079159
break;
92089160

92099161
case OCLASS_REWRITE:
@@ -9322,41 +9274,6 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
93229274

93239275
systable_endscan(scan);
93249276

9325-
/*
9326-
* Check the collected index OIDs to see which ones belong to the
9327-
* constraint(s) of the table, and drop those from the list of indexes
9328-
* that we need to process; rebuilding the constraints will handle them.
9329-
*/
9330-
prev = NULL;
9331-
for (lc = list_head(tab->changedIndexOids); lc; lc = next)
9332-
{
9333-
Oid indexoid = lfirst_oid(lc);
9334-
Oid conoid;
9335-
9336-
next = lnext(lc);
9337-
9338-
conoid = get_index_constraint(indexoid);
9339-
if (OidIsValid(conoid) &&
9340-
list_member_oid(tab->changedConstraintOids, conoid))
9341-
tab->changedIndexOids = list_delete_cell(tab->changedIndexOids,
9342-
lc, prev);
9343-
else
9344-
prev = lc;
9345-
}
9346-
9347-
/*
9348-
* Now collect the definitions of the indexes that must be rebuilt. (We
9349-
* could merge this into the previous loop, but it'd be more complicated
9350-
* for little gain.)
9351-
*/
9352-
foreach(lc, tab->changedIndexOids)
9353-
{
9354-
Oid indexoid = lfirst_oid(lc);
9355-
9356-
tab->changedIndexDefs = lappend(tab->changedIndexDefs,
9357-
pg_get_indexdef_string(indexoid));
9358-
}
9359-
93609277
/*
93619278
* Now scan for dependencies of this column on other things. The only
93629279
* thing we should find is the dependency on the column datatype, which we
@@ -9460,6 +9377,95 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
94609377
return address;
94619378
}
94629379

9380+
/*
9381+
* Subroutine for ATExecAlterColumnType: remember that a constraint needs
9382+
* to be rebuilt (which we might already know).
9383+
*/
9384+
static void
9385+
RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab,
9386+
DependencyType deptype)
9387+
{
9388+
/*
9389+
* This de-duplication check is critical for two independent reasons: we
9390+
* mustn't try to recreate the same constraint twice, and if a constraint
9391+
* depends on more than one column whose type is to be altered, we must
9392+
* capture its definition string before applying any of the column type
9393+
* changes. ruleutils.c will get confused if we ask again later.
9394+
*/
9395+
if (!list_member_oid(tab->changedConstraintOids, conoid))
9396+
{
9397+
/* OK, capture the constraint's existing definition string */
9398+
char *defstring = pg_get_constraintdef_command(conoid);
9399+
9400+
/*
9401+
* Put NORMAL dependencies at the front of the list and AUTO
9402+
* dependencies at the back. This makes sure that foreign-key
9403+
* constraints depending on this column will be dropped before unique
9404+
* or primary-key constraints of the column; which we must have
9405+
* because the FK constraints depend on the indexes belonging to the
9406+
* unique constraints.
9407+
*/
9408+
if (deptype == DEPENDENCY_NORMAL)
9409+
{
9410+
tab->changedConstraintOids = lcons_oid(conoid,
9411+
tab->changedConstraintOids);
9412+
tab->changedConstraintDefs = lcons(defstring,
9413+
tab->changedConstraintDefs);
9414+
}
9415+
else
9416+
{
9417+
tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
9418+
conoid);
9419+
tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
9420+
defstring);
9421+
}
9422+
}
9423+
}
9424+
9425+
/*
9426+
* Subroutine for ATExecAlterColumnType: remember that an index needs
9427+
* to be rebuilt (which we might already know).
9428+
*/
9429+
static void
9430+
RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
9431+
{
9432+
/*
9433+
* This de-duplication check is critical for two independent reasons: we
9434+
* mustn't try to recreate the same index twice, and if an index depends
9435+
* on more than one column whose type is to be altered, we must capture
9436+
* its definition string before applying any of the column type changes.
9437+
* ruleutils.c will get confused if we ask again later.
9438+
*/
9439+
if (!list_member_oid(tab->changedIndexOids, indoid))
9440+
{
9441+
/*
9442+
* Before adding it as an index-to-rebuild, we'd better see if it
9443+
* belongs to a constraint, and if so rebuild the constraint instead.
9444+
* Typically this check fails, because constraint indexes normally
9445+
* have only dependencies on their constraint. But it's possible for
9446+
* such an index to also have direct dependencies on table columns,
9447+
* for example with a partial exclusion constraint.
9448+
*/
9449+
Oid conoid = get_index_constraint(indoid);
9450+
9451+
if (OidIsValid(conoid))
9452+
{
9453+
/* index dependencies on columns should generally be AUTO */
9454+
RememberConstraintForRebuilding(conoid, tab, DEPENDENCY_AUTO);
9455+
}
9456+
else
9457+
{
9458+
/* OK, capture the index's existing definition string */
9459+
char *defstring = pg_get_indexdef_string(indoid);
9460+
9461+
tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
9462+
indoid);
9463+
tab->changedIndexDefs = lappend(tab->changedIndexDefs,
9464+
defstring);
9465+
}
9466+
}
9467+
}
9468+
94639469
/*
94649470
* Returns the address of the modified column
94659471
*/

src/test/regress/expected/alter_table.out

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1934,12 +1934,19 @@ select * from anothertab;
19341934
(3 rows)
19351935

19361936
drop table anothertab;
1937-
-- Test alter table column type with constraint indexes (cf. bug #15835)
1938-
create table anothertab(f1 int primary key, f2 int unique, f3 int, f4 int);
1937+
-- Test index handling in alter table column type (cf. bugs #15835, #15865)
1938+
create table anothertab(f1 int primary key, f2 int unique,
1939+
f3 int, f4 int, f5 int);
19391940
alter table anothertab
19401941
add exclude using btree (f3 with =);
19411942
alter table anothertab
19421943
add exclude using btree (f4 with =) where (f4 is not null);
1944+
alter table anothertab
1945+
add exclude using btree (f4 with =) where (f5 > 0);
1946+
alter table anothertab
1947+
add unique(f1,f4);
1948+
create index on anothertab(f2,f3);
1949+
create unique index on anothertab(f4);
19431950
\d anothertab
19441951
Table "public.anothertab"
19451952
Column | Type | Collation | Nullable | Default
@@ -1948,17 +1955,23 @@ alter table anothertab
19481955
f2 | integer | | |
19491956
f3 | integer | | |
19501957
f4 | integer | | |
1958+
f5 | integer | | |
19511959
Indexes:
19521960
"anothertab_pkey" PRIMARY KEY, btree (f1)
1961+
"anothertab_f1_f4_key" UNIQUE CONSTRAINT, btree (f1, f4)
19531962
"anothertab_f2_key" UNIQUE CONSTRAINT, btree (f2)
1963+
"anothertab_f4_idx" UNIQUE, btree (f4)
1964+
"anothertab_f2_f3_idx" btree (f2, f3)
19541965
"anothertab_f3_excl" EXCLUDE USING btree (f3 WITH =)
19551966
"anothertab_f4_excl" EXCLUDE USING btree (f4 WITH =) WHERE (f4 IS NOT NULL)
1967+
"anothertab_f4_excl1" EXCLUDE USING btree (f4 WITH =) WHERE (f5 > 0)
19561968

19571969
alter table anothertab alter column f1 type bigint;
19581970
alter table anothertab
19591971
alter column f2 type bigint,
19601972
alter column f3 type bigint,
19611973
alter column f4 type bigint;
1974+
alter table anothertab alter column f5 type bigint;
19621975
\d anothertab
19631976
Table "public.anothertab"
19641977
Column | Type | Collation | Nullable | Default
@@ -1967,11 +1980,16 @@ alter table anothertab
19671980
f2 | bigint | | |
19681981
f3 | bigint | | |
19691982
f4 | bigint | | |
1983+
f5 | bigint | | |
19701984
Indexes:
19711985
"anothertab_pkey" PRIMARY KEY, btree (f1)
1986+
"anothertab_f1_f4_key" UNIQUE CONSTRAINT, btree (f1, f4)
19721987
"anothertab_f2_key" UNIQUE CONSTRAINT, btree (f2)
1988+
"anothertab_f4_idx" UNIQUE, btree (f4)
1989+
"anothertab_f2_f3_idx" btree (f2, f3)
19731990
"anothertab_f3_excl" EXCLUDE USING btree (f3 WITH =)
19741991
"anothertab_f4_excl" EXCLUDE USING btree (f4 WITH =) WHERE (f4 IS NOT NULL)
1992+
"anothertab_f4_excl1" EXCLUDE USING btree (f4 WITH =) WHERE (f5 > 0)
19751993

19761994
drop table anothertab;
19771995
create table another (f1 int, f2 text);

src/test/regress/sql/alter_table.sql

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,19 +1307,27 @@ select * from anothertab;
13071307

13081308
drop table anothertab;
13091309

1310-
-- Test alter table column type with constraint indexes (cf. bug #15835)
1311-
create table anothertab(f1 int primary key, f2 int unique, f3 int, f4 int);
1310+
-- Test index handling in alter table column type (cf. bugs #15835, #15865)
1311+
create table anothertab(f1 int primary key, f2 int unique,
1312+
f3 int, f4 int, f5 int);
13121313
alter table anothertab
13131314
add exclude using btree (f3 with =);
13141315
alter table anothertab
13151316
add exclude using btree (f4 with =) where (f4 is not null);
1317+
alter table anothertab
1318+
add exclude using btree (f4 with =) where (f5 > 0);
1319+
alter table anothertab
1320+
add unique(f1,f4);
1321+
create index on anothertab(f2,f3);
1322+
create unique index on anothertab(f4);
13161323

13171324
\d anothertab
13181325
alter table anothertab alter column f1 type bigint;
13191326
alter table anothertab
13201327
alter column f2 type bigint,
13211328
alter column f3 type bigint,
13221329
alter column f4 type bigint;
1330+
alter table anothertab alter column f5 type bigint;
13231331
\d anothertab
13241332

13251333
drop table anothertab;

0 commit comments

Comments
 (0)