Skip to content

Commit af20e2d

Browse files
committed
Fix ALTER TABLE code to update domain constraints when needed.
It's possible for dropping a column, or altering its type, to require changes in domain CHECK constraint expressions; but the code was previously only expecting to find dependent table CHECK constraints. Make the necessary adjustments. This is a fairly old oversight, but it's a lot easier to encounter the problem in the context of domains over composite types than it was before. Given the lack of field complaints, I'm not going to bother with a back-patch, though I'd be willing to reconsider that decision if someone does complain. Patch by me, reviewed by Michael Paquier Discussion: https://postgr.es/m/30656.1509128130@sss.pgh.pa.us
1 parent 387ec70 commit af20e2d

File tree

5 files changed

+186
-25
lines changed

5 files changed

+186
-25
lines changed

src/backend/commands/tablecmds.c

Lines changed: 82 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,8 @@ static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
425425
char *cmd, List **wqueue, LOCKMODE lockmode,
426426
bool rewrite);
427427
static void RebuildConstraintComment(AlteredTableInfo *tab, int pass,
428-
Oid objid, Relation rel, char *conname);
428+
Oid objid, Relation rel, List *domname,
429+
char *conname);
429430
static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
430431
static void TryReuseForeignKey(Oid oldId, Constraint *con);
431432
static void change_owner_fix_column_acls(Oid relationOid,
@@ -3319,6 +3320,7 @@ AlterTableGetLockLevel(List *cmds)
33193320
case AT_ProcessedConstraint: /* becomes AT_AddConstraint */
33203321
case AT_AddConstraintRecurse: /* becomes AT_AddConstraint */
33213322
case AT_ReAddConstraint: /* becomes AT_AddConstraint */
3323+
case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
33223324
if (IsA(cmd->def, Constraint))
33233325
{
33243326
Constraint *con = (Constraint *) cmd->def;
@@ -3819,7 +3821,9 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
38193821
rel = relation_open(tab->relid, NoLock);
38203822

38213823
foreach(lcmd, subcmds)
3822-
ATExecCmd(wqueue, tab, rel, (AlterTableCmd *) lfirst(lcmd), lockmode);
3824+
ATExecCmd(wqueue, tab, rel,
3825+
castNode(AlterTableCmd, lfirst(lcmd)),
3826+
lockmode);
38233827

38243828
/*
38253829
* After the ALTER TYPE pass, do cleanup work (this is not done in
@@ -3936,6 +3940,13 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
39363940
ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
39373941
true, true, lockmode);
39383942
break;
3943+
case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
3944+
* constraint */
3945+
address =
3946+
AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
3947+
((AlterDomainStmt *) cmd->def)->def,
3948+
NULL);
3949+
break;
39393950
case AT_ReAddComment: /* Re-add existing comment */
39403951
address = CommentObject((CommentStmt *) cmd->def);
39413952
break;
@@ -9616,7 +9627,15 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
96169627
if (!HeapTupleIsValid(tup)) /* should not happen */
96179628
elog(ERROR, "cache lookup failed for constraint %u", oldId);
96189629
con = (Form_pg_constraint) GETSTRUCT(tup);
9619-
relid = con->conrelid;
9630+
if (OidIsValid(con->conrelid))
9631+
relid = con->conrelid;
9632+
else
9633+
{
9634+
/* must be a domain constraint */
9635+
relid = get_typ_typrelid(getBaseType(con->contypid));
9636+
if (!OidIsValid(relid))
9637+
elog(ERROR, "could not identify relation associated with constraint %u", oldId);
9638+
}
96209639
confrelid = con->confrelid;
96219640
conislocal = con->conislocal;
96229641
ReleaseSysCache(tup);
@@ -9753,7 +9772,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
97539772

97549773
foreach(lcmd, stmt->cmds)
97559774
{
9756-
AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
9775+
AlterTableCmd *cmd = castNode(AlterTableCmd, lfirst(lcmd));
97579776

97589777
if (cmd->subtype == AT_AddIndex)
97599778
{
@@ -9777,13 +9796,14 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
97779796
RebuildConstraintComment(tab,
97789797
AT_PASS_OLD_INDEX,
97799798
oldId,
9780-
rel, indstmt->idxname);
9799+
rel,
9800+
NIL,
9801+
indstmt->idxname);
97819802
}
97829803
else if (cmd->subtype == AT_AddConstraint)
97839804
{
9784-
Constraint *con;
9805+
Constraint *con = castNode(Constraint, cmd->def);
97859806

9786-
con = castNode(Constraint, cmd->def);
97879807
con->old_pktable_oid = refRelId;
97889808
/* rewriting neither side of a FK */
97899809
if (con->contype == CONSTR_FOREIGN &&
@@ -9797,13 +9817,41 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
97979817
RebuildConstraintComment(tab,
97989818
AT_PASS_OLD_CONSTR,
97999819
oldId,
9800-
rel, con->conname);
9820+
rel,
9821+
NIL,
9822+
con->conname);
98019823
}
98029824
else
98039825
elog(ERROR, "unexpected statement subtype: %d",
98049826
(int) cmd->subtype);
98059827
}
98069828
}
9829+
else if (IsA(stm, AlterDomainStmt))
9830+
{
9831+
AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
9832+
9833+
if (stmt->subtype == 'C') /* ADD CONSTRAINT */
9834+
{
9835+
Constraint *con = castNode(Constraint, stmt->def);
9836+
AlterTableCmd *cmd = makeNode(AlterTableCmd);
9837+
9838+
cmd->subtype = AT_ReAddDomainConstraint;
9839+
cmd->def = (Node *) stmt;
9840+
tab->subcmds[AT_PASS_OLD_CONSTR] =
9841+
lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
9842+
9843+
/* recreate any comment on the constraint */
9844+
RebuildConstraintComment(tab,
9845+
AT_PASS_OLD_CONSTR,
9846+
oldId,
9847+
NULL,
9848+
stmt->typeName,
9849+
con->conname);
9850+
}
9851+
else
9852+
elog(ERROR, "unexpected statement subtype: %d",
9853+
(int) stmt->subtype);
9854+
}
98079855
else
98089856
elog(ERROR, "unexpected statement type: %d",
98099857
(int) nodeTag(stm));
@@ -9813,12 +9861,19 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
98139861
}
98149862

98159863
/*
9816-
* Subroutine for ATPostAlterTypeParse() to recreate a comment entry for
9817-
* a constraint that is being re-added.
9864+
* Subroutine for ATPostAlterTypeParse() to recreate any existing comment
9865+
* for a table or domain constraint that is being rebuilt.
9866+
*
9867+
* objid is the OID of the constraint.
9868+
* Pass "rel" for a table constraint, or "domname" (domain's qualified name
9869+
* as a string list) for a domain constraint.
9870+
* (We could dig that info, as well as the conname, out of the pg_constraint
9871+
* entry; but callers already have them so might as well pass them.)
98189872
*/
98199873
static void
98209874
RebuildConstraintComment(AlteredTableInfo *tab, int pass, Oid objid,
9821-
Relation rel, char *conname)
9875+
Relation rel, List *domname,
9876+
char *conname)
98229877
{
98239878
CommentStmt *cmd;
98249879
char *comment_str;
@@ -9829,12 +9884,23 @@ RebuildConstraintComment(AlteredTableInfo *tab, int pass, Oid objid,
98299884
if (comment_str == NULL)
98309885
return;
98319886

9832-
/* Build node CommentStmt */
9887+
/* Build CommentStmt node, copying all input data for safety */
98339888
cmd = makeNode(CommentStmt);
9834-
cmd->objtype = OBJECT_TABCONSTRAINT;
9835-
cmd->object = (Node *) list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
9836-
makeString(pstrdup(RelationGetRelationName(rel))),
9837-
makeString(pstrdup(conname)));
9889+
if (rel)
9890+
{
9891+
cmd->objtype = OBJECT_TABCONSTRAINT;
9892+
cmd->object = (Node *)
9893+
list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
9894+
makeString(pstrdup(RelationGetRelationName(rel))),
9895+
makeString(pstrdup(conname)));
9896+
}
9897+
else
9898+
{
9899+
cmd->objtype = OBJECT_DOMCONSTRAINT;
9900+
cmd->object = (Node *)
9901+
list_make2(makeTypeNameFromNameList(copyObject(domname)),
9902+
makeString(pstrdup(conname)));
9903+
}
98389904
cmd->comment = comment_str;
98399905

98409906
/* Append it to list of commands */

src/backend/utils/adt/ruleutils.c

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@ static char *generate_function_name(Oid funcid, int nargs,
460460
bool has_variadic, bool *use_variadic_p,
461461
ParseExprKind special_exprkind);
462462
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
463+
static char *generate_qualified_type_name(Oid typid);
463464
static text *string_to_text(char *str);
464465
static char *flatten_reloptions(Oid relid);
465466

@@ -1867,15 +1868,27 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
18671868

18681869
if (fullCommand)
18691870
{
1870-
/*
1871-
* Currently, callers want ALTER TABLE (without ONLY) for CHECK
1872-
* constraints, and other types of constraints don't inherit anyway so
1873-
* it doesn't matter whether we say ONLY or not. Someday we might
1874-
* need to let callers specify whether to put ONLY in the command.
1875-
*/
1876-
appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
1877-
generate_qualified_relation_name(conForm->conrelid),
1878-
quote_identifier(NameStr(conForm->conname)));
1871+
if (OidIsValid(conForm->conrelid))
1872+
{
1873+
/*
1874+
* Currently, callers want ALTER TABLE (without ONLY) for CHECK
1875+
* constraints, and other types of constraints don't inherit
1876+
* anyway so it doesn't matter whether we say ONLY or not. Someday
1877+
* we might need to let callers specify whether to put ONLY in the
1878+
* command.
1879+
*/
1880+
appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
1881+
generate_qualified_relation_name(conForm->conrelid),
1882+
quote_identifier(NameStr(conForm->conname)));
1883+
}
1884+
else
1885+
{
1886+
/* Must be a domain constraint */
1887+
Assert(OidIsValid(conForm->contypid));
1888+
appendStringInfo(&buf, "ALTER DOMAIN %s ADD CONSTRAINT %s ",
1889+
generate_qualified_type_name(conForm->contypid),
1890+
quote_identifier(NameStr(conForm->conname)));
1891+
}
18791892
}
18801893

18811894
switch (conForm->contype)
@@ -10778,6 +10791,42 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2)
1077810791
return buf.data;
1077910792
}
1078010793

10794+
/*
10795+
* generate_qualified_type_name
10796+
* Compute the name to display for a type specified by OID
10797+
*
10798+
* This is different from format_type_be() in that we unconditionally
10799+
* schema-qualify the name. That also means no special syntax for
10800+
* SQL-standard type names ... although in current usage, this should
10801+
* only get used for domains, so such cases wouldn't occur anyway.
10802+
*/
10803+
static char *
10804+
generate_qualified_type_name(Oid typid)
10805+
{
10806+
HeapTuple tp;
10807+
Form_pg_type typtup;
10808+
char *typname;
10809+
char *nspname;
10810+
char *result;
10811+
10812+
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
10813+
if (!HeapTupleIsValid(tp))
10814+
elog(ERROR, "cache lookup failed for type %u", typid);
10815+
typtup = (Form_pg_type) GETSTRUCT(tp);
10816+
typname = NameStr(typtup->typname);
10817+
10818+
nspname = get_namespace_name(typtup->typnamespace);
10819+
if (!nspname)
10820+
elog(ERROR, "cache lookup failed for namespace %u",
10821+
typtup->typnamespace);
10822+
10823+
result = quote_qualified_identifier(nspname, typname);
10824+
10825+
ReleaseSysCache(tp);
10826+
10827+
return result;
10828+
}
10829+
1078110830
/*
1078210831
* generate_collation_name
1078310832
* Compute the name to display for a collation specified by OID

src/include/nodes/parsenodes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1713,6 +1713,7 @@ typedef enum AlterTableType
17131713
AT_AddConstraint, /* add constraint */
17141714
AT_AddConstraintRecurse, /* internal to commands/tablecmds.c */
17151715
AT_ReAddConstraint, /* internal to commands/tablecmds.c */
1716+
AT_ReAddDomainConstraint, /* internal to commands/tablecmds.c */
17161717
AT_AlterConstraint, /* alter constraint */
17171718
AT_ValidateConstraint, /* validate constraint */
17181719
AT_ValidateConstraintRecurse, /* internal to commands/tablecmds.c */

src/test/regress/expected/domain.out

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,31 @@ Rules:
284284
WHERE (dcomptable.d1).i > 0::double precision
285285

286286
drop table dcomptable;
287+
drop type comptype cascade;
288+
NOTICE: drop cascades to type dcomptype
289+
-- check altering and dropping columns used by domain constraints
290+
create type comptype as (r float8, i float8);
291+
create domain dcomptype as comptype;
292+
alter domain dcomptype add constraint c1 check ((value).r > 0);
293+
comment on constraint c1 on domain dcomptype is 'random commentary';
294+
select row(0,1)::dcomptype; -- fail
295+
ERROR: value for domain dcomptype violates check constraint "c1"
296+
alter type comptype alter attribute r type varchar; -- fail
297+
ERROR: operator does not exist: character varying > double precision
298+
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
299+
alter type comptype alter attribute r type bigint;
300+
alter type comptype drop attribute r; -- fail
301+
ERROR: cannot drop composite type comptype column r because other objects depend on it
302+
DETAIL: constraint c1 depends on composite type comptype column r
303+
HINT: Use DROP ... CASCADE to drop the dependent objects too.
304+
alter type comptype drop attribute i;
305+
select conname, obj_description(oid, 'pg_constraint') from pg_constraint
306+
where contypid = 'dcomptype'::regtype; -- check comment is still there
307+
conname | obj_description
308+
---------+-------------------
309+
c1 | random commentary
310+
(1 row)
311+
287312
drop type comptype cascade;
288313
NOTICE: drop cascades to type dcomptype
289314
-- Test domains over arrays of composite

src/test/regress/sql/domain.sql

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,26 @@ drop table dcomptable;
159159
drop type comptype cascade;
160160

161161

162+
-- check altering and dropping columns used by domain constraints
163+
create type comptype as (r float8, i float8);
164+
create domain dcomptype as comptype;
165+
alter domain dcomptype add constraint c1 check ((value).r > 0);
166+
comment on constraint c1 on domain dcomptype is 'random commentary';
167+
168+
select row(0,1)::dcomptype; -- fail
169+
170+
alter type comptype alter attribute r type varchar; -- fail
171+
alter type comptype alter attribute r type bigint;
172+
173+
alter type comptype drop attribute r; -- fail
174+
alter type comptype drop attribute i;
175+
176+
select conname, obj_description(oid, 'pg_constraint') from pg_constraint
177+
where contypid = 'dcomptype'::regtype; -- check comment is still there
178+
179+
drop type comptype cascade;
180+
181+
162182
-- Test domains over arrays of composite
163183

164184
create type comptype as (r float8, i float8);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy