aboutsummaryrefslogtreecommitdiff
path: root/src/backend/catalog/heap.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/catalog/heap.c')
-rw-r--r--src/backend/catalog/heap.c491
1 files changed, 99 insertions, 392 deletions
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index d9b1adfe12a..2a0d82aedd7 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2161,57 +2161,6 @@ StoreRelCheck(Relation rel, const char *ccname, Node *expr,
}
/*
- * Store a NOT NULL constraint for the given relation
- *
- * The OID of the new constraint is returned.
- */
-static Oid
-StoreRelNotNull(Relation rel, const char *nnname, AttrNumber attnum,
- bool is_validated, bool is_local, int inhcount,
- bool is_no_inherit)
-{
- int16 attNos;
- Oid constrOid;
-
- /* We only ever store one column per constraint */
- attNos = attnum;
-
- constrOid =
- CreateConstraintEntry(nnname,
- RelationGetNamespace(rel),
- CONSTRAINT_NOTNULL,
- false,
- false,
- is_validated,
- InvalidOid,
- RelationGetRelid(rel),
- &attNos,
- 1,
- 1,
- InvalidOid, /* not a domain constraint */
- InvalidOid, /* no associated index */
- InvalidOid, /* Foreign key fields */
- NULL,
- NULL,
- NULL,
- NULL,
- 0,
- ' ',
- ' ',
- NULL,
- 0,
- ' ',
- NULL, /* not an exclusion constraint */
- NULL,
- NULL,
- is_local,
- inhcount,
- is_no_inherit,
- false);
- return constrOid;
-}
-
-/*
* Store defaults and constraints (passed as a list of CookedConstraint).
*
* Each CookedConstraint struct is modified to store the new catalog tuple OID.
@@ -2255,14 +2204,6 @@ StoreConstraints(Relation rel, List *cooked_constraints, bool is_internal)
is_internal);
numchecks++;
break;
-
- case CONSTR_NOTNULL:
- con->conoid =
- StoreRelNotNull(rel, con->name, con->attnum,
- !con->skip_validation, con->is_local,
- con->inhcount, con->is_no_inherit);
- break;
-
default:
elog(ERROR, "unrecognized constraint type: %d",
(int) con->contype);
@@ -2318,7 +2259,6 @@ AddRelationNewConstraints(Relation rel,
ParseNamespaceItem *nsitem;
int numchecks;
List *checknames;
- List *nnnames;
ListCell *cell;
Node *expr;
CookedConstraint *cooked;
@@ -2404,179 +2344,130 @@ AddRelationNewConstraints(Relation rel,
*/
numchecks = numoldchecks;
checknames = NIL;
- nnnames = NIL;
foreach(cell, newConstraints)
{
Constraint *cdef = (Constraint *) lfirst(cell);
+ char *ccname;
Oid constrOid;
- if (cdef->contype == CONSTR_CHECK)
+ if (cdef->contype != CONSTR_CHECK)
+ continue;
+
+ if (cdef->raw_expr != NULL)
{
- char *ccname;
+ Assert(cdef->cooked_expr == NULL);
/*
- * XXX Should we detect the case with CHECK (foo IS NOT NULL) and
- * handle it as a NOT NULL constraint?
+ * Transform raw parsetree to executable expression, and verify
+ * it's valid as a CHECK constraint.
*/
-
- if (cdef->raw_expr != NULL)
- {
- Assert(cdef->cooked_expr == NULL);
-
- /*
- * Transform raw parsetree to executable expression, and
- * verify it's valid as a CHECK constraint.
- */
- expr = cookConstraint(pstate, cdef->raw_expr,
- RelationGetRelationName(rel));
- }
- else
- {
- Assert(cdef->cooked_expr != NULL);
-
- /*
- * Here, we assume the parser will only pass us valid CHECK
- * expressions, so we do no particular checking.
- */
- expr = stringToNode(cdef->cooked_expr);
- }
+ expr = cookConstraint(pstate, cdef->raw_expr,
+ RelationGetRelationName(rel));
+ }
+ else
+ {
+ Assert(cdef->cooked_expr != NULL);
/*
- * Check name uniqueness, or generate a name if none was given.
+ * Here, we assume the parser will only pass us valid CHECK
+ * expressions, so we do no particular checking.
*/
- if (cdef->conname != NULL)
- {
- ListCell *cell2;
+ expr = stringToNode(cdef->cooked_expr);
+ }
- ccname = cdef->conname;
- /* Check against other new constraints */
- /* Needed because we don't do CommandCounterIncrement in loop */
- foreach(cell2, checknames)
- {
- if (strcmp((char *) lfirst(cell2), ccname) == 0)
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_OBJECT),
- errmsg("check constraint \"%s\" already exists",
- ccname)));
- }
+ /*
+ * Check name uniqueness, or generate a name if none was given.
+ */
+ if (cdef->conname != NULL)
+ {
+ ListCell *cell2;
- /* save name for future checks */
- checknames = lappend(checknames, ccname);
-
- /*
- * Check against pre-existing constraints. If we are allowed
- * to merge with an existing constraint, there's no more to do
- * here. (We omit the duplicate constraint from the result,
- * which is what ATAddCheckConstraint wants.)
- */
- if (MergeWithExistingConstraint(rel, ccname, expr,
- allow_merge, is_local,
- cdef->initially_valid,
- cdef->is_no_inherit))
- continue;
- }
- else
+ ccname = cdef->conname;
+ /* Check against other new constraints */
+ /* Needed because we don't do CommandCounterIncrement in loop */
+ foreach(cell2, checknames)
{
- /*
- * When generating a name, we want to create "tab_col_check"
- * for a column constraint and "tab_check" for a table
- * constraint. We no longer have any info about the syntactic
- * positioning of the constraint phrase, so we approximate
- * this by seeing whether the expression references more than
- * one column. (If the user played by the rules, the result
- * is the same...)
- *
- * Note: pull_var_clause() doesn't descend into sublinks, but
- * we eliminated those above; and anyway this only needs to be
- * an approximate answer.
- */
- List *vars;
- char *colname;
-
- vars = pull_var_clause(expr, 0);
-
- /* eliminate duplicates */
- vars = list_union(NIL, vars);
-
- if (list_length(vars) == 1)
- colname = get_attname(RelationGetRelid(rel),
- ((Var *) linitial(vars))->varattno,
- true);
- else
- colname = NULL;
-
- ccname = ChooseConstraintName(RelationGetRelationName(rel),
- colname,
- "check",
- RelationGetNamespace(rel),
- checknames);
-
- /* save name for future checks */
- checknames = lappend(checknames, ccname);
+ if (strcmp((char *) lfirst(cell2), ccname) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("check constraint \"%s\" already exists",
+ ccname)));
}
+ /* save name for future checks */
+ checknames = lappend(checknames, ccname);
+
/*
- * OK, store it.
+ * Check against pre-existing constraints. If we are allowed to
+ * merge with an existing constraint, there's no more to do here.
+ * (We omit the duplicate constraint from the result, which is
+ * what ATAddCheckConstraint wants.)
*/
- constrOid =
- StoreRelCheck(rel, ccname, expr, cdef->initially_valid, is_local,
- is_local ? 0 : 1, cdef->is_no_inherit, is_internal);
-
- numchecks++;
-
- cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
- cooked->contype = CONSTR_CHECK;
- cooked->conoid = constrOid;
- cooked->name = ccname;
- cooked->attnum = 0;
- cooked->expr = expr;
- cooked->skip_validation = cdef->skip_validation;
- cooked->is_local = is_local;
- cooked->inhcount = is_local ? 0 : 1;
- cooked->is_no_inherit = cdef->is_no_inherit;
- cookedConstraints = lappend(cookedConstraints, cooked);
+ if (MergeWithExistingConstraint(rel, ccname, expr,
+ allow_merge, is_local,
+ cdef->initially_valid,
+ cdef->is_no_inherit))
+ continue;
}
- else if (cdef->contype == CONSTR_NOTNULL)
+ else
{
- CookedConstraint *nncooked;
- AttrNumber colnum;
- char *nnname;
+ /*
+ * When generating a name, we want to create "tab_col_check" for a
+ * column constraint and "tab_check" for a table constraint. We
+ * no longer have any info about the syntactic positioning of the
+ * constraint phrase, so we approximate this by seeing whether the
+ * expression references more than one column. (If the user
+ * played by the rules, the result is the same...)
+ *
+ * Note: pull_var_clause() doesn't descend into sublinks, but we
+ * eliminated those above; and anyway this only needs to be an
+ * approximate answer.
+ */
+ List *vars;
+ char *colname;
+
+ vars = pull_var_clause(expr, 0);
- colnum = get_attnum(RelationGetRelid(rel),
- cdef->colname);
- if (colnum == InvalidAttrNumber)
- elog(ERROR, "invalid column name \"%s\"", cdef->colname);
+ /* eliminate duplicates */
+ vars = list_union(NIL, vars);
- if (cdef->conname)
- nnname = cdef->conname; /* verify clash? */
+ if (list_length(vars) == 1)
+ colname = get_attname(RelationGetRelid(rel),
+ ((Var *) linitial(vars))->varattno,
+ true);
else
- nnname = ChooseConstraintName(RelationGetRelationName(rel),
- cdef->colname,
- "not_null",
- RelationGetNamespace(rel),
- nnnames);
- nnnames = lappend(nnnames, nnname);
-
- constrOid =
- StoreRelNotNull(rel, nnname, colnum,
- cdef->initially_valid,
- is_local,
- is_local ? 0 : 1,
- cdef->is_no_inherit);
-
- nncooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
- nncooked->contype = CONSTR_NOTNULL;
- nncooked->conoid = constrOid;
- nncooked->name = nnname;
- nncooked->attnum = colnum;
- nncooked->expr = NULL;
- nncooked->skip_validation = cdef->skip_validation;
- nncooked->is_local = is_local;
- nncooked->inhcount = is_local ? 0 : 1;
- nncooked->is_no_inherit = cdef->is_no_inherit;
-
- cookedConstraints = lappend(cookedConstraints, nncooked);
+ colname = NULL;
+
+ ccname = ChooseConstraintName(RelationGetRelationName(rel),
+ colname,
+ "check",
+ RelationGetNamespace(rel),
+ checknames);
+
+ /* save name for future checks */
+ checknames = lappend(checknames, ccname);
}
+
+ /*
+ * OK, store it.
+ */
+ constrOid =
+ StoreRelCheck(rel, ccname, expr, cdef->initially_valid, is_local,
+ is_local ? 0 : 1, cdef->is_no_inherit, is_internal);
+
+ numchecks++;
+
+ cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
+ cooked->contype = CONSTR_CHECK;
+ cooked->conoid = constrOid;
+ cooked->name = ccname;
+ cooked->attnum = 0;
+ cooked->expr = expr;
+ cooked->skip_validation = cdef->skip_validation;
+ cooked->is_local = is_local;
+ cooked->inhcount = is_local ? 0 : 1;
+ cooked->is_no_inherit = cdef->is_no_inherit;
+ cookedConstraints = lappend(cookedConstraints, cooked);
}
/*
@@ -2746,190 +2637,6 @@ MergeWithExistingConstraint(Relation rel, const char *ccname, Node *expr,
return found;
}
-/* list_sort comparator to sort CookedConstraint by attnum */
-static int
-list_cookedconstr_attnum_cmp(const ListCell *p1, const ListCell *p2)
-{
- AttrNumber v1 = ((CookedConstraint *) lfirst(p1))->attnum;
- AttrNumber v2 = ((CookedConstraint *) lfirst(p2))->attnum;
-
- if (v1 < v2)
- return -1;
- if (v1 > v2)
- return 1;
- return 0;
-}
-
-/*
- * Create the NOT NULL constraints for the relation
- *
- * These come from two sources: the 'constraints' list (of Constraint) is
- * specified directly by the user; the 'old_notnulls' list (of
- * CookedConstraint) comes from inheritance. We create one constraint
- * for each column, giving priority to user-specified ones, and setting
- * inhcount according to how many parents cause each column to get a
- * NOT NULL constraint. If a user-specified name clashes with another
- * user-specified name, an error is raised.
- *
- * Note that inherited constraints have two shapes: those coming from another
- * NOT NULL constraint in the parent, which have a name already, and those
- * coming from a PRIMARY KEY in the parent, which don't. Any name specified
- * in a parent is disregarded in case of a conflict.
- *
- * Returns a list of AttrNumber for columns that need to have the attnotnull
- * flag set.
- */
-List *
-AddRelationNotNullConstraints(Relation rel, List *constraints,
- List *old_notnulls)
-{
- List *nnnames = NIL;
- List *givennames = NIL;
- List *nncols = NIL;
- ListCell *lc;
-
- /*
- * First, create all NOT NULLs that are directly specified by the user.
- * Note that inheritance might have given us another source for each, so
- * we must scan the old_notnulls list and increment inhcount for each
- * element with identical attnum. We delete from there any element that
- * we process.
- */
- foreach(lc, constraints)
- {
- Constraint *constr = lfirst_node(Constraint, lc);
- AttrNumber attnum;
- char *conname;
- bool is_local = true;
- int inhcount = 0;
- ListCell *lc2;
-
- attnum = get_attnum(RelationGetRelid(rel), constr->colname);
-
- foreach(lc2, old_notnulls)
- {
- CookedConstraint *old = (CookedConstraint *) lfirst(lc2);
-
- if (old->attnum == attnum)
- {
- inhcount++;
- old_notnulls = foreach_delete_current(old_notnulls, lc2);
- }
- }
-
- /*
- * Determine a constraint name, which may have been specified by the
- * user, or raise an error if a conflict exists with another
- * user-specified name.
- */
- if (constr->conname)
- {
- foreach(lc2, givennames)
- {
- if (strcmp(lfirst(lc2), conname) == 0)
- ereport(ERROR,
- errmsg("constraint name \"%s\" is already in use in relation \"%s\"",
- constr->conname,
- RelationGetRelationName(rel)));
- }
-
- conname = constr->conname;
- givennames = lappend(givennames, conname);
- }
- else
- conname = ChooseConstraintName(RelationGetRelationName(rel),
- get_attname(RelationGetRelid(rel),
- attnum, false),
- "not_null",
- RelationGetNamespace(rel),
- nnnames);
- nnnames = lappend(nnnames, conname);
-
- StoreRelNotNull(rel, conname,
- attnum, true, is_local,
- inhcount, constr->is_no_inherit);
-
- nncols = lappend_int(nncols, attnum);
- }
-
- /*
- * If any column remains in the additional_notnulls list, we must create a
- * NOT NULL constraint marked not-local. Because multiple parents could
- * specify a NOT NULL for the same column, we must count how many there
- * are and set inhcount accordingly, deleting elements we've already
- * processed.
- *
- * We don't use foreach() here because we have two nested loops over the
- * cooked constraint list, with possible element deletions in the inner one.
- * If we used foreach_delete_current() it could only fix up the state of one
- * of the loops, so it seems cleaner to use looping over list indexes for
- * both loops. Note that any deletion will happen beyond where the outer
- * loop is, so its index never needs adjustment.
- */
- list_sort(old_notnulls, list_cookedconstr_attnum_cmp);
- for (int outerpos = 0; outerpos < list_length(old_notnulls); outerpos++)
- {
- CookedConstraint *cooked;
- char *conname = NULL;
- int inhcount = 1;
- ListCell *lc2;
-
- cooked = (CookedConstraint *) list_nth(old_notnulls, outerpos);
-
- /* We just preserve the first constraint name we come across, if any */
- if (conname == NULL && cooked->name)
- conname = cooked->name;
-
- for (int restpos = outerpos + 1; restpos < list_length(old_notnulls);)
- {
- CookedConstraint *other;
-
- other = (CookedConstraint *) list_nth(old_notnulls, restpos);
- if (other->attnum == cooked->attnum)
- {
- if (conname == NULL && other->name)
- conname = other->name;
-
- inhcount++;
- old_notnulls = list_delete_nth_cell(old_notnulls, restpos);
- }
- else
- restpos++;
- }
-
- /* If we got a name, make sure it isn't one we've already used */
- if (conname != NULL)
- {
- foreach(lc2, nnnames)
- {
- if (strcmp(lfirst(lc2), conname) == 0)
- {
- conname = NULL;
- break;
- }
- }
- }
-
- /* and choose a name, if needed */
- if (conname == NULL)
- conname = ChooseConstraintName(RelationGetRelationName(rel),
- get_attname(RelationGetRelid(rel),
- cooked->attnum, false),
- "not_null",
- RelationGetNamespace(rel),
- nnnames);
- nnnames = lappend(nnnames, conname);
-
- StoreRelNotNull(rel, conname, cooked->attnum, true,
- false, inhcount,
- cooked->is_no_inherit);
-
- nncols = lappend_int(nncols, cooked->attnum);
- }
-
- return nncols;
-}
-
/*
* Update the count of constraints in the relation's pg_class tuple.
*