heap_close(conRel, RowExclusiveLock);
}
-/*
- * get_constraint_relation_oids
- * Find the IDs of the relations to which a constraint refers.
- */
-void
-get_constraint_relation_oids(Oid constraint_oid, Oid *conrelid, Oid *confrelid)
-{
- HeapTuple tup;
- Form_pg_constraint con;
-
- tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraint_oid));
- if (!HeapTupleIsValid(tup)) /* should not happen */
- elog(ERROR, "cache lookup failed for constraint %u", constraint_oid);
- con = (Form_pg_constraint) GETSTRUCT(tup);
- *conrelid = con->conrelid;
- *confrelid = con->confrelid;
- ReleaseSysCache(tup);
-}
-
/*
* get_relation_constraint_oid
* Find a constraint on the specified relation with the specified name.
bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode);
static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
Relation rel, ColumnDef *colDef, bool isOid,
- bool recurse, bool recursing, bool if_not_exists, LOCKMODE lockmode);
+ bool recurse, bool recursing,
+ bool if_not_exists, LOCKMODE lockmode);
static bool check_for_column_name_collision(Relation rel, const char *colname,
- bool if_not_exists);
+ bool if_not_exists);
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
case AT_AddColumnToView: /* add column via CREATE OR REPLACE
* VIEW */
address = ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
- false, false, false, false, lockmode);
+ false, false, false,
+ false, lockmode);
break;
case AT_AddColumnRecurse:
address = ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
- false, true, false, cmd->missing_ok, lockmode);
+ false, true, false,
+ cmd->missing_ok, lockmode);
break;
case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
* constraint */
address =
ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
- false, true, lockmode);
+ true, true, lockmode);
break;
case AT_ReAddComment: /* Re-add existing comment */
address = CommentObject((CommentStmt *) cmd->def);
if (cmd->def != NULL)
address =
ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
- true, false, false, cmd->missing_ok, lockmode);
+ true, false, false,
+ cmd->missing_ok, lockmode);
break;
case AT_AddOidsRecurse: /* SET WITH OIDS */
/* Use the ADD COLUMN code, unless prep decided to do nothing */
if (cmd->def != NULL)
address =
ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
- true, true, false, cmd->missing_ok, lockmode);
+ true, true, false,
+ cmd->missing_ok, lockmode);
break;
case AT_DropOids: /* SET WITHOUT OIDS */
static ObjectAddress
ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
ColumnDef *colDef, bool isOid,
- bool recurse, bool recursing, bool if_not_exists, LOCKMODE lockmode)
+ bool recurse, bool recursing,
+ bool if_not_exists, LOCKMODE lockmode)
{
Oid myrelid = RelationGetRelid(rel);
Relation pgclass,
* AddRelationNewConstraints would normally assign different names to the
* child constraints. To fix that, we must capture the name assigned at
* the parent table and pass that down.
- *
- * When re-adding a previously existing constraint (during ALTER COLUMN TYPE),
- * we don't need to recurse here, because recursion will be carried out at a
- * higher level; the constraint name issue doesn't apply because the names
- * have already been assigned and are just being re-used. We need a separate
- * "is_readd" flag for that; just setting recurse=false would result in an
- * error if there are child tables.
*/
static ObjectAddress
ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
*/
newcons = AddRelationNewConstraints(rel, NIL,
list_make1(copyObject(constr)),
- recursing, /* allow_merge */
+ recursing | is_readd, /* allow_merge */
!recursing, /* is_local */
is_readd); /* is_internal */
/*
* If adding a NO INHERIT constraint, no need to find our children.
- * Likewise, in a re-add operation, we don't need to recurse (that will be
- * handled at higher levels).
*/
- if (constr->is_no_inherit || is_readd)
+ if (constr->is_no_inherit)
return address;
/*
if (!list_member_oid(tab->changedConstraintOids,
foundObject.objectId))
{
- char *defstring = pg_get_constraintdef_string(foundObject.objectId);
+ char *defstring = pg_get_constraintdef_command(foundObject.objectId);
/*
* Put NORMAL dependencies at the front of the list and
def_item, tab->changedConstraintDefs)
{
Oid oldId = lfirst_oid(oid_item);
+ HeapTuple tup;
+ Form_pg_constraint con;
Oid relid;
Oid confrelid;
+ bool conislocal;
+
+ tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
+ if (!HeapTupleIsValid(tup)) /* should not happen */
+ elog(ERROR, "cache lookup failed for constraint %u", oldId);
+ con = (Form_pg_constraint) GETSTRUCT(tup);
+ relid = con->conrelid;
+ confrelid = con->confrelid;
+ conislocal = con->conislocal;
+ ReleaseSysCache(tup);
+
+ /*
+ * If the constraint is inherited (only), we don't want to inject a
+ * new definition here; it'll get recreated when ATAddCheckConstraint
+ * recurses from adding the parent table's constraint. But we had to
+ * carry the info this far so that we can drop the constraint below.
+ */
+ if (!conislocal)
+ continue;
- get_constraint_relation_oids(oldId, &relid, &confrelid);
ATPostAlterTypeParse(oldId, relid, confrelid,
(char *) lfirst(def_item),
wqueue, lockmode, tab->rewrite);
rel, con->conname);
}
else
- elog(ERROR, "unexpected statement type: %d",
+ elog(ERROR, "unexpected statement subtype: %d",
(int) cmd->subtype);
}
}
if (foreignrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
- errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
- RelationGetRelationName(rel),
- RelationGetRelationName(foreignrel)),
+ errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
+ RelationGetRelationName(rel),
+ RelationGetRelationName(foreignrel)),
errtableconstraint(rel, NameStr(con->conname))));
}
else
if (foreignrel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
- errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
- RelationGetRelationName(rel),
- RelationGetRelationName(foreignrel)),
+ errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
+ RelationGetRelationName(rel),
+ RelationGetRelationName(foreignrel)),
errtableconstraint(rel, NameStr(con->conname))));
}
static void printSubscripts(ArrayRef *aref, deparse_context *context);
static char *get_relation_name(Oid relid);
static char *generate_relation_name(Oid relid, List *namespaces);
+static char *generate_qualified_relation_name(Oid relid);
static char *generate_function_name(Oid funcid, int nargs,
List *argnames, Oid *argtypes,
bool has_variadic, bool *use_variadic_p,
prettyFlags)));
}
-/* Internal version that returns a palloc'd C string; no pretty-printing */
+/*
+ * Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command
+ */
char *
-pg_get_constraintdef_string(Oid constraintId)
+pg_get_constraintdef_command(Oid constraintId)
{
return pg_get_constraintdef_worker(constraintId, true, 0);
}
initStringInfo(&buf);
- if (fullCommand && OidIsValid(conForm->conrelid))
+ if (fullCommand)
{
- appendStringInfo(&buf, "ALTER TABLE ONLY %s ADD CONSTRAINT %s ",
- generate_relation_name(conForm->conrelid, NIL),
+ /*
+ * Currently, callers want ALTER TABLE (without ONLY) for CHECK
+ * constraints, and other types of constraints don't inherit anyway so
+ * it doesn't matter whether we say ONLY or not. Someday we might
+ * need to let callers specify whether to put ONLY in the command.
+ */
+ appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
+ generate_qualified_relation_name(conForm->conrelid),
quote_identifier(NameStr(conForm->conname)));
}
if (OidIsValid(sequenceId))
{
- HeapTuple classtup;
- Form_pg_class classtuple;
- char *nspname;
char *result;
- /* Get the sequence's pg_class entry */
- classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(sequenceId));
- if (!HeapTupleIsValid(classtup))
- elog(ERROR, "cache lookup failed for relation %u", sequenceId);
- classtuple = (Form_pg_class) GETSTRUCT(classtup);
-
- /* Get the namespace */
- nspname = get_namespace_name(classtuple->relnamespace);
- if (!nspname)
- elog(ERROR, "cache lookup failed for namespace %u",
- classtuple->relnamespace);
-
- /* And construct the result string */
- result = quote_qualified_identifier(nspname,
- NameStr(classtuple->relname));
-
- ReleaseSysCache(classtup);
+ result = generate_qualified_relation_name(sequenceId);
PG_RETURN_TEXT_P(string_to_text(result));
}
return result;
}
+/*
+ * generate_qualified_relation_name
+ * Compute the name to display for a relation specified by OID
+ *
+ * As above, but unconditionally schema-qualify the name.
+ */
+static char *
+generate_qualified_relation_name(Oid relid)
+{
+ HeapTuple tp;
+ Form_pg_class reltup;
+ char *relname;
+ char *nspname;
+ char *result;
+
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for relation %u", relid);
+ reltup = (Form_pg_class) GETSTRUCT(tp);
+ relname = NameStr(reltup->relname);
+
+ nspname = get_namespace_name(reltup->relnamespace);
+ if (!nspname)
+ elog(ERROR, "cache lookup failed for namespace %u",
+ reltup->relnamespace);
+
+ result = quote_qualified_identifier(nspname, relname);
+
+ ReleaseSysCache(tp);
+
+ return result;
+}
+
/*
* generate_function_name
* Compute the name to display for a function specified by OID,
extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
Oid newNspId, bool isType, ObjectAddresses *objsMoved);
-extern void get_constraint_relation_oids(Oid constraint_oid, Oid *conrelid, Oid *confrelid);
extern Oid get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok);
extern Oid get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok);
extern char *pg_get_indexdef_string(Oid indexrelid);
extern char *pg_get_indexdef_columns(Oid indexrelid, bool pretty);
-extern char *pg_get_constraintdef_string(Oid constraintId);
+extern char *pg_get_constraintdef_command(Oid constraintId);
extern char *deparse_expression(Node *expr, List *dpcontext,
bool forceprefix, bool showimplicit);
extern List *deparse_context_for(const char *aliasname, Oid relid);
t
(1 row)
--- ALTER TYPE with a check constraint and a child table (bug before Nov 2012)
-CREATE TABLE test_inh_check (a float check (a > 10.2));
+-- ALTER COLUMN TYPE with a check constraint and a child table (bug #13779)
+CREATE TABLE test_inh_check (a float check (a > 10.2), b float);
CREATE TABLE test_inh_check_child() INHERITS(test_inh_check);
+\d test_inh_check
+ Table "public.test_inh_check"
+ Column | Type | Modifiers
+--------+------------------+-----------
+ a | double precision |
+ b | double precision |
+Check constraints:
+ "test_inh_check_a_check" CHECK (a > 10.2::double precision)
+Number of child tables: 1 (Use \d+ to list them.)
+
+\d test_inh_check_child
+ Table "public.test_inh_check_child"
+ Column | Type | Modifiers
+--------+------------------+-----------
+ a | double precision |
+ b | double precision |
+Check constraints:
+ "test_inh_check_a_check" CHECK (a > 10.2::double precision)
+Inherits: test_inh_check
+
+select relname, conname, coninhcount, conislocal, connoinherit
+ from pg_constraint c, pg_class r
+ where relname like 'test_inh_check%' and c.conrelid = r.oid
+ order by 1, 2;
+ relname | conname | coninhcount | conislocal | connoinherit
+----------------------+------------------------+-------------+------------+--------------
+ test_inh_check | test_inh_check_a_check | 0 | t | f
+ test_inh_check_child | test_inh_check_a_check | 1 | f | f
+(2 rows)
+
ALTER TABLE test_inh_check ALTER COLUMN a TYPE numeric;
+\d test_inh_check
+ Table "public.test_inh_check"
+ Column | Type | Modifiers
+--------+------------------+-----------
+ a | numeric |
+ b | double precision |
+Check constraints:
+ "test_inh_check_a_check" CHECK (a::double precision > 10.2::double precision)
+Number of child tables: 1 (Use \d+ to list them.)
+
+\d test_inh_check_child
+ Table "public.test_inh_check_child"
+ Column | Type | Modifiers
+--------+------------------+-----------
+ a | numeric |
+ b | double precision |
+Check constraints:
+ "test_inh_check_a_check" CHECK (a::double precision > 10.2::double precision)
+Inherits: test_inh_check
+
+select relname, conname, coninhcount, conislocal, connoinherit
+ from pg_constraint c, pg_class r
+ where relname like 'test_inh_check%' and c.conrelid = r.oid
+ order by 1, 2;
+ relname | conname | coninhcount | conislocal | connoinherit
+----------------------+------------------------+-------------+------------+--------------
+ test_inh_check | test_inh_check_a_check | 0 | t | f
+ test_inh_check_child | test_inh_check_a_check | 1 | f | f
+(2 rows)
+
+-- also try noinherit, local, and local+inherited cases
+ALTER TABLE test_inh_check ADD CONSTRAINT bnoinherit CHECK (b > 100) NO INHERIT;
+ALTER TABLE test_inh_check_child ADD CONSTRAINT blocal CHECK (b < 1000);
+ALTER TABLE test_inh_check_child ADD CONSTRAINT bmerged CHECK (b > 1);
+ALTER TABLE test_inh_check ADD CONSTRAINT bmerged CHECK (b > 1);
+NOTICE: merging constraint "bmerged" with inherited definition
+\d test_inh_check
+ Table "public.test_inh_check"
+ Column | Type | Modifiers
+--------+------------------+-----------
+ a | numeric |
+ b | double precision |
+Check constraints:
+ "bmerged" CHECK (b > 1::double precision)
+ "bnoinherit" CHECK (b > 100::double precision) NO INHERIT
+ "test_inh_check_a_check" CHECK (a::double precision > 10.2::double precision)
+Number of child tables: 1 (Use \d+ to list them.)
+
+\d test_inh_check_child
+ Table "public.test_inh_check_child"
+ Column | Type | Modifiers
+--------+------------------+-----------
+ a | numeric |
+ b | double precision |
+Check constraints:
+ "blocal" CHECK (b < 1000::double precision)
+ "bmerged" CHECK (b > 1::double precision)
+ "test_inh_check_a_check" CHECK (a::double precision > 10.2::double precision)
+Inherits: test_inh_check
+
+select relname, conname, coninhcount, conislocal, connoinherit
+ from pg_constraint c, pg_class r
+ where relname like 'test_inh_check%' and c.conrelid = r.oid
+ order by 1, 2;
+ relname | conname | coninhcount | conislocal | connoinherit
+----------------------+------------------------+-------------+------------+--------------
+ test_inh_check | bmerged | 0 | t | f
+ test_inh_check | bnoinherit | 0 | t | t
+ test_inh_check | test_inh_check_a_check | 0 | t | f
+ test_inh_check_child | blocal | 0 | t | f
+ test_inh_check_child | bmerged | 1 | t | f
+ test_inh_check_child | test_inh_check_a_check | 1 | f | f
+(6 rows)
+
+ALTER TABLE test_inh_check ALTER COLUMN b TYPE numeric;
+NOTICE: merging constraint "bmerged" with inherited definition
\d test_inh_check
Table "public.test_inh_check"
Column | Type | Modifiers
--------+---------+-----------
a | numeric |
+ b | numeric |
Check constraints:
+ "bmerged" CHECK (b::double precision > 1::double precision)
+ "bnoinherit" CHECK (b::double precision > 100::double precision) NO INHERIT
"test_inh_check_a_check" CHECK (a::double precision > 10.2::double precision)
Number of child tables: 1 (Use \d+ to list them.)
Column | Type | Modifiers
--------+---------+-----------
a | numeric |
+ b | numeric |
Check constraints:
+ "blocal" CHECK (b::double precision < 1000::double precision)
+ "bmerged" CHECK (b::double precision > 1::double precision)
"test_inh_check_a_check" CHECK (a::double precision > 10.2::double precision)
Inherits: test_inh_check
+select relname, conname, coninhcount, conislocal, connoinherit
+ from pg_constraint c, pg_class r
+ where relname like 'test_inh_check%' and c.conrelid = r.oid
+ order by 1, 2;
+ relname | conname | coninhcount | conislocal | connoinherit
+----------------------+------------------------+-------------+------------+--------------
+ test_inh_check | bmerged | 0 | t | f
+ test_inh_check | bnoinherit | 0 | t | t
+ test_inh_check | test_inh_check_a_check | 0 | t | f
+ test_inh_check_child | blocal | 0 | t | f
+ test_inh_check_child | bmerged | 1 | t | f
+ test_inh_check_child | test_inh_check_a_check | 1 | f | f
+(6 rows)
+
-- check for rollback of ANALYZE corrupting table property flags (bug #11638)
CREATE TABLE check_fk_presence_1 (id int PRIMARY KEY, t text);
CREATE TABLE check_fk_presence_2 (id int REFERENCES check_fk_presence_1, t text);
from pg_class
where oid = 'test_storage'::regclass;
--- ALTER TYPE with a check constraint and a child table (bug before Nov 2012)
-CREATE TABLE test_inh_check (a float check (a > 10.2));
+-- ALTER COLUMN TYPE with a check constraint and a child table (bug #13779)
+CREATE TABLE test_inh_check (a float check (a > 10.2), b float);
CREATE TABLE test_inh_check_child() INHERITS(test_inh_check);
+\d test_inh_check
+\d test_inh_check_child
+select relname, conname, coninhcount, conislocal, connoinherit
+ from pg_constraint c, pg_class r
+ where relname like 'test_inh_check%' and c.conrelid = r.oid
+ order by 1, 2;
ALTER TABLE test_inh_check ALTER COLUMN a TYPE numeric;
\d test_inh_check
\d test_inh_check_child
+select relname, conname, coninhcount, conislocal, connoinherit
+ from pg_constraint c, pg_class r
+ where relname like 'test_inh_check%' and c.conrelid = r.oid
+ order by 1, 2;
+-- also try noinherit, local, and local+inherited cases
+ALTER TABLE test_inh_check ADD CONSTRAINT bnoinherit CHECK (b > 100) NO INHERIT;
+ALTER TABLE test_inh_check_child ADD CONSTRAINT blocal CHECK (b < 1000);
+ALTER TABLE test_inh_check_child ADD CONSTRAINT bmerged CHECK (b > 1);
+ALTER TABLE test_inh_check ADD CONSTRAINT bmerged CHECK (b > 1);
+\d test_inh_check
+\d test_inh_check_child
+select relname, conname, coninhcount, conislocal, connoinherit
+ from pg_constraint c, pg_class r
+ where relname like 'test_inh_check%' and c.conrelid = r.oid
+ order by 1, 2;
+ALTER TABLE test_inh_check ALTER COLUMN b TYPE numeric;
+\d test_inh_check
+\d test_inh_check_child
+select relname, conname, coninhcount, conislocal, connoinherit
+ from pg_constraint c, pg_class r
+ where relname like 'test_inh_check%' and c.conrelid = r.oid
+ order by 1, 2;
-- check for rollback of ANALYZE corrupting table property flags (bug #11638)
CREATE TABLE check_fk_presence_1 (id int PRIMARY KEY, t text);