diff options
Diffstat (limited to 'src/backend/catalog/heap.c')
-rw-r--r-- | src/backend/catalog/heap.c | 491 |
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. * |