Skip to content

Commit d9253df

Browse files
committed
Fix handling of CREATE TABLE LIKE with inheritance.
If a CREATE TABLE command uses both LIKE and traditional inheritance, Vars in CHECK constraints and expression indexes that are absorbed from a LIKE parent table tended to get mis-numbered, resulting in wrong answers and/or bizarre error messages (though probably not any actual crashes, thanks to validation occurring in the executor). In v12 and up, the same could happen to Vars in GENERATED expressions, even in cases with no LIKE clause but multiple traditional-inheritance parents. The cause of the problem for LIKE is that parse_utilcmd.c supposed it could renumber such Vars correctly during transformCreateStmt(), which it cannot since we have not yet accounted for columns added via inheritance. Fix that by postponing processing of LIKE INCLUDING CONSTRAINTS, DEFAULTS, GENERATED, INDEXES till after we've performed DefineRelation(). The error with GENERATED and multiple inheritance is a simple oversight in MergeAttributes(); it knows it has to renumber Vars in inherited CHECK constraints, but forgot to apply the same processing to inherited GENERATED expressions (a/k/a defaults). Per bug #16272 from Tom Gottfried. The non-GENERATED variants of the issue are ancient, presumably dating right back to the addition of CREATE TABLE LIKE; hence back-patch to all supported branches. Discussion: https://postgr.es/m/16272-6e32da020e9a9381@postgresql.org
1 parent 3248633 commit d9253df

File tree

9 files changed

+440
-178
lines changed

9 files changed

+440
-178
lines changed

src/backend/commands/tablecmds.c

Lines changed: 105 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,8 @@ static bool ConstraintImpliedByRelConstraint(Relation scanrel,
387387
List *testConstraint, List *provenConstraint);
388388
static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
389389
Node *newDefault, LOCKMODE lockmode);
390+
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
391+
Node *newDefault);
390392
static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
391393
Node *def, LOCKMODE lockmode);
392394
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
@@ -2010,8 +2012,8 @@ storage_name(char c)
20102012
* 'schema' is the column/attribute definition for the table. (It's a list
20112013
* of ColumnDef's.) It is destructively changed.
20122014
* 'supers' is a list of OIDs of parent relations, already locked by caller.
2013-
* 'relpersistence' is a persistence type of the table.
2014-
* 'is_partition' tells if the table is a partition
2015+
* 'relpersistence' is the persistence type of the table.
2016+
* 'is_partition' tells if the table is a partition.
20152017
*
20162018
* Output arguments:
20172019
* 'supconstr' receives a list of constraints belonging to the parents,
@@ -2176,7 +2178,11 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
21762178
TupleDesc tupleDesc;
21772179
TupleConstr *constr;
21782180
AttrNumber *newattno;
2181+
List *inherited_defaults;
2182+
List *cols_with_defaults;
21792183
AttrNumber parent_attno;
2184+
ListCell *lc1;
2185+
ListCell *lc2;
21802186

21812187
/* caller already got lock */
21822188
relation = table_open(parent, NoLock);
@@ -2263,6 +2269,9 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
22632269
newattno = (AttrNumber *)
22642270
palloc0(tupleDesc->natts * sizeof(AttrNumber));
22652271

2272+
/* We can't process inherited defaults until newattno[] is complete. */
2273+
inherited_defaults = cols_with_defaults = NIL;
2274+
22662275
for (parent_attno = 1; parent_attno <= tupleDesc->natts;
22672276
parent_attno++)
22682277
{
@@ -2318,7 +2327,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
23182327
get_collation_name(defCollId),
23192328
get_collation_name(attribute->attcollation))));
23202329

2321-
/* Copy storage parameter */
2330+
/* Copy/check storage parameter */
23222331
if (def->storage == 0)
23232332
def->storage = attribute->attstorage;
23242333
else if (def->storage != attribute->attstorage)
@@ -2369,7 +2378,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
23692378
}
23702379

23712380
/*
2372-
* Copy default if any
2381+
* Locate default if any
23732382
*/
23742383
if (attribute->atthasdef)
23752384
{
@@ -2391,23 +2400,59 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
23912400
Assert(this_default != NULL);
23922401

23932402
/*
2394-
* If default expr could contain any vars, we'd need to fix
2395-
* 'em, but it can't; so default is ready to apply to child.
2396-
*
2397-
* If we already had a default from some prior parent, check
2398-
* to see if they are the same. If so, no problem; if not,
2399-
* mark the column as having a bogus default. Below, we will
2400-
* complain if the bogus default isn't overridden by the child
2401-
* schema.
2403+
* If it's a GENERATED default, it might contain Vars that
2404+
* need to be mapped to the inherited column(s)' new numbers.
2405+
* We can't do that till newattno[] is ready, so just remember
2406+
* all the inherited default expressions for the moment.
24022407
*/
2403-
Assert(def->raw_default == NULL);
2404-
if (def->cooked_default == NULL)
2405-
def->cooked_default = this_default;
2406-
else if (!equal(def->cooked_default, this_default))
2407-
{
2408-
def->cooked_default = &bogus_marker;
2409-
have_bogus_defaults = true;
2410-
}
2408+
inherited_defaults = lappend(inherited_defaults, this_default);
2409+
cols_with_defaults = lappend(cols_with_defaults, def);
2410+
}
2411+
}
2412+
2413+
/*
2414+
* Now process any inherited default expressions, adjusting attnos
2415+
* using the completed newattno[] map.
2416+
*/
2417+
forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2418+
{
2419+
Node *this_default = (Node *) lfirst(lc1);
2420+
ColumnDef *def = (ColumnDef *) lfirst(lc2);
2421+
bool found_whole_row;
2422+
2423+
/* Adjust Vars to match new table's column numbering */
2424+
this_default = map_variable_attnos(this_default,
2425+
1, 0,
2426+
newattno, tupleDesc->natts,
2427+
InvalidOid, &found_whole_row);
2428+
2429+
/*
2430+
* For the moment we have to reject whole-row variables. We could
2431+
* convert them, if we knew the new table's rowtype OID, but that
2432+
* hasn't been assigned yet. (A variable could only appear in a
2433+
* generation expression, so the error message is correct.)
2434+
*/
2435+
if (found_whole_row)
2436+
ereport(ERROR,
2437+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2438+
errmsg("cannot convert whole-row table reference"),
2439+
errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2440+
def->colname,
2441+
RelationGetRelationName(relation))));
2442+
2443+
/*
2444+
* If we already had a default from some prior parent, check to
2445+
* see if they are the same. If so, no problem; if not, mark the
2446+
* column as having a bogus default. Below, we will complain if
2447+
* the bogus default isn't overridden by the child schema.
2448+
*/
2449+
Assert(def->raw_default == NULL);
2450+
if (def->cooked_default == NULL)
2451+
def->cooked_default = this_default;
2452+
else if (!equal(def->cooked_default, this_default))
2453+
{
2454+
def->cooked_default = &bogus_marker;
2455+
have_bogus_defaults = true;
24112456
}
24122457
}
24132458

@@ -2625,7 +2670,6 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
26252670
def->raw_default = newdef->raw_default;
26262671
def->cooked_default = newdef->cooked_default;
26272672
}
2628-
26292673
}
26302674
else
26312675
{
@@ -3729,6 +3773,7 @@ AlterTableGetLockLevel(List *cmds)
37293773
* Theoretically, these could be ShareRowExclusiveLock.
37303774
*/
37313775
case AT_ColumnDefault:
3776+
case AT_CookedColumnDefault:
37323777
case AT_AlterConstraint:
37333778
case AT_AddIndex: /* from ADD CONSTRAINT */
37343779
case AT_AddIndexConstraint:
@@ -3982,6 +4027,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
39824027
/* No command-specific prep needed */
39834028
pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
39844029
break;
4030+
case AT_CookedColumnDefault: /* add a pre-cooked default */
4031+
/* This is currently used only in CREATE TABLE */
4032+
/* (so the permission check really isn't necessary) */
4033+
ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4034+
/* This command never recurses */
4035+
pass = AT_PASS_ADD_CONSTR;
4036+
break;
39854037
case AT_AddIdentity:
39864038
ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
39874039
/* This command never recurses */
@@ -4316,6 +4368,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
43164368
case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
43174369
address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
43184370
break;
4371+
case AT_CookedColumnDefault: /* add a pre-cooked default */
4372+
address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
4373+
break;
43194374
case AT_AddIdentity:
43204375
address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode);
43214376
break;
@@ -6564,6 +6619,35 @@ ATExecColumnDefault(Relation rel, const char *colName,
65646619
return address;
65656620
}
65666621

6622+
/*
6623+
* Add a pre-cooked default expression.
6624+
*
6625+
* Return the address of the affected column.
6626+
*/
6627+
static ObjectAddress
6628+
ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
6629+
Node *newDefault)
6630+
{
6631+
ObjectAddress address;
6632+
6633+
/* We assume no checking is required */
6634+
6635+
/*
6636+
* Remove any old default for the column. We use RESTRICT here for
6637+
* safety, but at present we do not expect anything to depend on the
6638+
* default. (In ordinary cases, there could not be a default in place
6639+
* anyway, but it's possible when combining LIKE with inheritance.)
6640+
*/
6641+
RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
6642+
true);
6643+
6644+
(void) StoreAttrDefault(rel, attnum, newDefault, true, false);
6645+
6646+
ObjectAddressSubSet(address, RelationRelationId,
6647+
RelationGetRelid(rel), attnum);
6648+
return address;
6649+
}
6650+
65676651
/*
65686652
* ALTER TABLE ALTER COLUMN ADD IDENTITY
65696653
*

0 commit comments

Comments
 (0)