summaryrefslogtreecommitdiff
path: root/src/backend/commands/tablecmds.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r--src/backend/commands/tablecmds.c767
1 files changed, 516 insertions, 251 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 380287caa37..c79cd1f9edd 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.41 2002/09/12 21:16:42 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.42 2002/09/22 00:37:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -41,6 +41,7 @@
#include "parser/gramparse.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
+#include "parser/parse_oper.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
@@ -59,10 +60,19 @@ static int findAttrByName(const char *attributeName, List *schema);
static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass);
static void CheckTupleType(Form_pg_class tuple_class);
static bool needs_toast_table(Relation rel);
+static void AlterTableAddCheckConstraint(Relation rel, Constraint *constr);
+static void AlterTableAddForeignKeyConstraint(Relation rel,
+ FkConstraint *fkconstraint);
+static int transformColumnNameList(Oid relId, List *colList,
+ const char *stmtname,
+ int16 *attnums, Oid *atttypids);
+static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
+ List **attnamelist,
+ int16 *attnums, Oid *atttypids);
+static Oid transformFkeyCheckAttrs(Relation pkrel,
+ int numattrs, int16 *attnums);
static void validateForeignKeyConstraint(FkConstraint *fkconstraint,
Relation rel, Relation pkrel);
-static Oid createForeignKeyConstraint(Relation rel, Relation pkrel,
- FkConstraint *fkconstraint);
static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
Oid constrOid);
static char *fkMatchTypeToString(char match_type);
@@ -2513,6 +2523,22 @@ AlterTableAddConstraint(Oid myrelid, bool recurse,
Constraint *constr = (Constraint *) newConstraint;
/*
+ * Assign or validate constraint name
+ */
+ if (constr->name)
+ {
+ if (ConstraintNameIsUsed(RelationGetRelid(rel),
+ RelationGetNamespace(rel),
+ constr->name))
+ elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
+ constr->name, RelationGetRelationName(rel));
+ }
+ else
+ constr->name = GenerateConstraintName(RelationGetRelid(rel),
+ RelationGetNamespace(rel),
+ &counter);
+
+ /*
* Currently, we only expect to see CONSTR_CHECK nodes
* arriving here (see the preprocessing done in
* parser/analyze.c). Use a switch anyway to make it
@@ -2521,130 +2547,8 @@ AlterTableAddConstraint(Oid myrelid, bool recurse,
switch (constr->contype)
{
case CONSTR_CHECK:
- {
- ParseState *pstate;
- bool successful = true;
- HeapScanDesc scan;
- ExprContext *econtext;
- TupleTableSlot *slot;
- HeapTuple tuple;
- RangeTblEntry *rte;
- List *qual;
- Node *expr;
-
- /*
- * Assign or validate constraint name
- */
- if (constr->name)
- {
- if (ConstraintNameIsUsed(RelationGetRelid(rel),
- RelationGetNamespace(rel),
- constr->name))
- elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
- constr->name,
- RelationGetRelationName(rel));
- }
- else
- constr->name = GenerateConstraintName(RelationGetRelid(rel),
- RelationGetNamespace(rel),
- &counter);
-
- /*
- * We need to make a parse state and range
- * table to allow us to transformExpr and
- * fix_opids to get a version of the
- * expression we can pass to ExecQual
- */
- pstate = make_parsestate(NULL);
- rte = addRangeTableEntryForRelation(pstate,
- myrelid,
- makeAlias(RelationGetRelationName(rel), NIL),
- false,
- true);
- addRTEtoQuery(pstate, rte, true, true);
-
- /*
- * Convert the A_EXPR in raw_expr into an
- * EXPR
- */
- expr = transformExpr(pstate, constr->raw_expr);
-
- /*
- * Make sure it yields a boolean result.
- */
- expr = coerce_to_boolean(expr, "CHECK");
-
- /*
- * Make sure no outside relations are
- * referred to.
- */
- if (length(pstate->p_rtable) != 1)
- elog(ERROR, "Only relation '%s' can be referenced in CHECK",
- RelationGetRelationName(rel));
-
- /*
- * No subplans or aggregates, either...
- */
- if (contain_subplans(expr))
- elog(ERROR, "cannot use subselect in CHECK constraint expression");
- if (contain_agg_clause(expr))
- elog(ERROR, "cannot use aggregate function in CHECK constraint expression");
-
- /*
- * Might as well try to reduce any
- * constant expressions.
- */
- expr = eval_const_expressions(expr);
-
- /* And fix the opids */
- fix_opids(expr);
-
- qual = makeList1(expr);
-
- /* Make tuple slot to hold tuples */
- slot = MakeTupleTableSlot();
- ExecSetSlotDescriptor(slot, RelationGetDescr(rel), false);
- /* Make an expression context for ExecQual */
- econtext = MakeExprContext(slot, CurrentMemoryContext);
-
- /*
- * Scan through the rows now, checking the
- * expression at each row.
- */
- scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
-
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
- {
- ExecStoreTuple(tuple, slot, InvalidBuffer, false);
- if (!ExecQual(qual, econtext, true))
- {
- successful = false;
- break;
- }
- ResetExprContext(econtext);
- }
-
- heap_endscan(scan);
-
- FreeExprContext(econtext);
- pfree(slot);
-
- if (!successful)
- elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s",
- constr->name);
-
- /*
- * Call AddRelationRawConstraints to do
- * the real adding -- It duplicates some
- * of the above, but does not check the
- * validity of the constraint against
- * tuples already in the table.
- */
- AddRelationRawConstraints(rel, NIL,
- makeList1(constr));
-
- break;
- }
+ AlterTableAddCheckConstraint(rel, constr);
+ break;
default:
elog(ERROR, "ALTER TABLE / ADD CONSTRAINT is not implemented for that constraint type.");
}
@@ -2653,8 +2557,6 @@ AlterTableAddConstraint(Oid myrelid, bool recurse,
case T_FkConstraint:
{
FkConstraint *fkconstraint = (FkConstraint *) newConstraint;
- Relation pkrel;
- Oid constrOid;
/*
* Assign or validate constraint name
@@ -2673,74 +2575,504 @@ AlterTableAddConstraint(Oid myrelid, bool recurse,
RelationGetNamespace(rel),
&counter);
- /*
- * Grab an exclusive lock on the pk table, so that
- * someone doesn't delete rows out from under us.
- * (Although a lesser lock would do for that purpose,
- * we'll need exclusive lock anyway to add triggers to
- * the pk table; trying to start with a lesser lock
- * will just create a risk of deadlock.)
- */
- pkrel = heap_openrv(fkconstraint->pktable,
- AccessExclusiveLock);
+ AlterTableAddForeignKeyConstraint(rel, fkconstraint);
- /*
- * Validity checks
- */
- if (pkrel->rd_rel->relkind != RELKIND_RELATION)
- elog(ERROR, "referenced relation \"%s\" is not a table",
- RelationGetRelationName(pkrel));
+ break;
+ }
+ default:
+ elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed");
+ }
- if (!allowSystemTableMods
- && IsSystemRelation(pkrel))
- elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
- RelationGetRelationName(pkrel));
+ /* If we have multiple constraints to make, bump CC between 'em */
+ if (lnext(listptr))
+ CommandCounterIncrement();
+ }
- /* XXX shouldn't there be a permission check too? */
+ /* Close rel, but keep lock till commit */
+ heap_close(rel, NoLock);
+}
- if (isTempNamespace(RelationGetNamespace(pkrel)) &&
- !isTempNamespace(RelationGetNamespace(rel)))
- elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint");
+/*
+ * Add a check constraint to a single table
+ *
+ * Subroutine for AlterTableAddConstraint. Must already hold exclusive
+ * lock on the rel, and have done appropriate validity/permissions checks
+ * for it.
+ */
+static void
+AlterTableAddCheckConstraint(Relation rel, Constraint *constr)
+{
+ ParseState *pstate;
+ bool successful = true;
+ HeapScanDesc scan;
+ ExprContext *econtext;
+ TupleTableSlot *slot;
+ HeapTuple tuple;
+ RangeTblEntry *rte;
+ List *qual;
+ Node *expr;
- /*
- * Check that the constraint is satisfied by existing
- * rows (we can skip this during table creation).
- *
- * NOTE: we assume parser has already checked for
- * existence of an appropriate unique index on the
- * referenced relation, and that the column datatypes
- * are comparable.
- */
- if (!fkconstraint->skip_validation)
- validateForeignKeyConstraint(fkconstraint, rel, pkrel);
+ /*
+ * We need to make a parse state and range
+ * table to allow us to transformExpr and
+ * fix_opids to get a version of the
+ * expression we can pass to ExecQual
+ */
+ pstate = make_parsestate(NULL);
+ rte = addRangeTableEntryForRelation(pstate,
+ RelationGetRelid(rel),
+ makeAlias(RelationGetRelationName(rel), NIL),
+ false,
+ true);
+ addRTEtoQuery(pstate, rte, true, true);
- /*
- * Record the FK constraint in pg_constraint.
- */
- constrOid = createForeignKeyConstraint(rel, pkrel,
- fkconstraint);
+ /*
+ * Convert the A_EXPR in raw_expr into an EXPR
+ */
+ expr = transformExpr(pstate, constr->raw_expr);
- /*
- * Create the triggers that will enforce the
- * constraint.
- */
- createForeignKeyTriggers(rel, fkconstraint, constrOid);
+ /*
+ * Make sure it yields a boolean result.
+ */
+ expr = coerce_to_boolean(expr, "CHECK");
- /*
- * Close pk table, but keep lock until we've
- * committed.
- */
- heap_close(pkrel, NoLock);
+ /*
+ * Make sure no outside relations are referred to.
+ */
+ if (length(pstate->p_rtable) != 1)
+ elog(ERROR, "Only relation '%s' can be referenced in CHECK",
+ RelationGetRelationName(rel));
- break;
+ /*
+ * No subplans or aggregates, either...
+ */
+ if (contain_subplans(expr))
+ elog(ERROR, "cannot use subselect in CHECK constraint expression");
+ if (contain_agg_clause(expr))
+ elog(ERROR, "cannot use aggregate function in CHECK constraint expression");
+
+ /*
+ * Might as well try to reduce any constant expressions.
+ */
+ expr = eval_const_expressions(expr);
+
+ /* And fix the opids */
+ fix_opids(expr);
+
+ qual = makeList1(expr);
+
+ /* Make tuple slot to hold tuples */
+ slot = MakeTupleTableSlot();
+ ExecSetSlotDescriptor(slot, RelationGetDescr(rel), false);
+ /* Make an expression context for ExecQual */
+ econtext = MakeExprContext(slot, CurrentMemoryContext);
+
+ /*
+ * Scan through the rows now, checking the expression at each row.
+ */
+ scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ ExecStoreTuple(tuple, slot, InvalidBuffer, false);
+ if (!ExecQual(qual, econtext, true))
+ {
+ successful = false;
+ break;
+ }
+ ResetExprContext(econtext);
+ }
+
+ heap_endscan(scan);
+
+ FreeExprContext(econtext);
+ pfree(slot);
+
+ if (!successful)
+ elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s",
+ constr->name);
+
+ /*
+ * Call AddRelationRawConstraints to do
+ * the real adding -- It duplicates some
+ * of the above, but does not check the
+ * validity of the constraint against
+ * tuples already in the table.
+ */
+ AddRelationRawConstraints(rel, NIL, makeList1(constr));
+}
+
+/*
+ * Add a foreign-key constraint to a single table
+ *
+ * Subroutine for AlterTableAddConstraint. Must already hold exclusive
+ * lock on the rel, and have done appropriate validity/permissions checks
+ * for it.
+ */
+static void
+AlterTableAddForeignKeyConstraint(Relation rel, FkConstraint *fkconstraint)
+{
+ const char *stmtname;
+ Relation pkrel;
+ AclResult aclresult;
+ int16 pkattnum[INDEX_MAX_KEYS];
+ int16 fkattnum[INDEX_MAX_KEYS];
+ Oid pktypoid[INDEX_MAX_KEYS];
+ Oid fktypoid[INDEX_MAX_KEYS];
+ int i;
+ int numfks,
+ numpks;
+ Oid indexOid;
+ Oid constrOid;
+
+ /* cheat a little to discover statement type for error messages */
+ stmtname = fkconstraint->skip_validation ? "CREATE TABLE" : "ALTER TABLE";
+
+ /*
+ * Grab an exclusive lock on the pk table, so that
+ * someone doesn't delete rows out from under us.
+ * (Although a lesser lock would do for that purpose,
+ * we'll need exclusive lock anyway to add triggers to
+ * the pk table; trying to start with a lesser lock
+ * will just create a risk of deadlock.)
+ */
+ pkrel = heap_openrv(fkconstraint->pktable, AccessExclusiveLock);
+
+ /*
+ * Validity and permissions checks
+ *
+ * Note: REFERENCES permissions checks are redundant with CREATE TRIGGER,
+ * but we may as well error out sooner instead of later.
+ */
+ if (pkrel->rd_rel->relkind != RELKIND_RELATION)
+ elog(ERROR, "referenced relation \"%s\" is not a table",
+ RelationGetRelationName(pkrel));
+
+ if (!allowSystemTableMods
+ && IsSystemRelation(pkrel))
+ elog(ERROR, "%s: relation \"%s\" is a system catalog",
+ stmtname, RelationGetRelationName(pkrel));
+
+ aclresult = pg_class_aclcheck(RelationGetRelid(pkrel), GetUserId(),
+ ACL_REFERENCES);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, RelationGetRelationName(pkrel));
+
+ aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
+ ACL_REFERENCES);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, RelationGetRelationName(rel));
+
+ if (isTempNamespace(RelationGetNamespace(pkrel)) &&
+ !isTempNamespace(RelationGetNamespace(rel)))
+ elog(ERROR, "%s: Unable to reference temporary table from permanent table constraint",
+ stmtname);
+
+ /*
+ * Look up the referencing attributes to make sure they
+ * exist, and record their attnums and type OIDs.
+ */
+ for (i = 0; i < INDEX_MAX_KEYS; i++)
+ {
+ pkattnum[i] = fkattnum[i] = 0;
+ pktypoid[i] = fktypoid[i] = InvalidOid;
+ }
+
+ numfks = transformColumnNameList(RelationGetRelid(rel),
+ fkconstraint->fk_attrs,
+ stmtname,
+ fkattnum, fktypoid);
+
+ /*
+ * If the attribute list for the referenced table was omitted,
+ * lookup the definition of the primary key and use it. Otherwise,
+ * validate the supplied attribute list. In either case, discover
+ * the index OID and the attnums and type OIDs of the attributes.
+ */
+ if (fkconstraint->pk_attrs == NIL)
+ {
+ numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
+ &fkconstraint->pk_attrs,
+ pkattnum, pktypoid);
+ }
+ else
+ {
+ numpks = transformColumnNameList(RelationGetRelid(pkrel),
+ fkconstraint->pk_attrs,
+ stmtname,
+ pkattnum, pktypoid);
+ /* Look for an index matching the column list */
+ indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum);
+ }
+
+ /* Be sure referencing and referenced column types are comparable */
+ if (numfks != numpks)
+ elog(ERROR, "%s: number of referencing and referenced attributes for foreign key disagree",
+ stmtname);
+
+ for (i = 0; i < numpks; i++)
+ {
+ /*
+ * fktypoid[i] is the foreign key table's i'th element's type
+ * pktypoid[i] is the primary key table's i'th element's type
+ *
+ * We let oper() do our work for us, including elog(ERROR) if the
+ * types don't compare with =
+ */
+ Operator o = oper(makeList1(makeString("=")),
+ fktypoid[i], pktypoid[i], false);
+
+ ReleaseSysCache(o);
+ }
+
+ /*
+ * Check that the constraint is satisfied by existing
+ * rows (we can skip this during table creation).
+ */
+ if (!fkconstraint->skip_validation)
+ validateForeignKeyConstraint(fkconstraint, rel, pkrel);
+
+ /*
+ * Record the FK constraint in pg_constraint.
+ */
+ constrOid = CreateConstraintEntry(fkconstraint->constr_name,
+ RelationGetNamespace(rel),
+ CONSTRAINT_FOREIGN,
+ fkconstraint->deferrable,
+ fkconstraint->initdeferred,
+ RelationGetRelid(rel),
+ fkattnum,
+ numfks,
+ InvalidOid, /* not a domain constraint */
+ RelationGetRelid(pkrel),
+ pkattnum,
+ numpks,
+ fkconstraint->fk_upd_action,
+ fkconstraint->fk_del_action,
+ fkconstraint->fk_matchtype,
+ indexOid,
+ NULL, /* no check constraint */
+ NULL,
+ NULL);
+
+ /*
+ * Create the triggers that will enforce the constraint.
+ */
+ createForeignKeyTriggers(rel, fkconstraint, constrOid);
+
+ /*
+ * Close pk table, but keep lock until we've committed.
+ */
+ heap_close(pkrel, NoLock);
+}
+
+
+/*
+ * transformColumnNameList - transform list of column names
+ *
+ * Lookup each name and return its attnum and type OID
+ */
+static int
+transformColumnNameList(Oid relId, List *colList,
+ const char *stmtname,
+ int16 *attnums, Oid *atttypids)
+{
+ List *l;
+ int attnum;
+
+ attnum = 0;
+ foreach(l, colList)
+ {
+ char *attname = strVal(lfirst(l));
+ HeapTuple atttuple;
+
+ atttuple = SearchSysCacheAttName(relId, attname);
+ if (!HeapTupleIsValid(atttuple))
+ elog(ERROR, "%s: column \"%s\" referenced in foreign key constraint does not exist",
+ stmtname, attname);
+ if (attnum >= INDEX_MAX_KEYS)
+ elog(ERROR, "Can only have %d keys in a foreign key",
+ INDEX_MAX_KEYS);
+ attnums[attnum] = ((Form_pg_attribute) GETSTRUCT(atttuple))->attnum;
+ atttypids[attnum] = ((Form_pg_attribute) GETSTRUCT(atttuple))->atttypid;
+ ReleaseSysCache(atttuple);
+ attnum++;
+ }
+
+ return attnum;
+}
+
+/*
+ * transformFkeyGetPrimaryKey -
+ *
+ * Look up the names, attnums, and types of the primary key attributes
+ * for the pkrel. Used when the column list in the REFERENCES specification
+ * is omitted.
+ */
+static int
+transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
+ List **attnamelist,
+ int16 *attnums, Oid *atttypids)
+{
+ List *indexoidlist,
+ *indexoidscan;
+ HeapTuple indexTuple = NULL;
+ Form_pg_index indexStruct = NULL;
+ int i;
+
+ /*
+ * Get the list of index OIDs for the table from the relcache, and
+ * look up each one in the pg_index syscache until we find one marked
+ * primary key (hopefully there isn't more than one such).
+ */
+ indexoidlist = RelationGetIndexList(pkrel);
+
+ foreach(indexoidscan, indexoidlist)
+ {
+ Oid indexoid = lfirsti(indexoidscan);
+
+ indexTuple = SearchSysCache(INDEXRELID,
+ ObjectIdGetDatum(indexoid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(indexTuple))
+ elog(ERROR, "transformFkeyGetPrimaryKey: index %u not found",
+ indexoid);
+ indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
+ if (indexStruct->indisprimary)
+ {
+ *indexOid = indexoid;
+ break;
+ }
+ ReleaseSysCache(indexTuple);
+ indexStruct = NULL;
+ }
+
+ freeList(indexoidlist);
+
+ /*
+ * Check that we found it
+ */
+ if (indexStruct == NULL)
+ elog(ERROR, "PRIMARY KEY for referenced table \"%s\" not found",
+ RelationGetRelationName(pkrel));
+
+ /*
+ * Now build the list of PK attributes from the indkey definition
+ */
+ *attnamelist = NIL;
+ for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++)
+ {
+ int pkattno = indexStruct->indkey[i];
+
+ attnums[i] = pkattno;
+ atttypids[i] = attnumTypeId(pkrel, pkattno);
+ *attnamelist = lappend(*attnamelist,
+ makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
+ }
+
+ ReleaseSysCache(indexTuple);
+
+ return i;
+}
+
+/*
+ * transformFkeyCheckAttrs -
+ *
+ * Make sure that the attributes of a referenced table belong to a unique
+ * (or primary key) constraint. Return the OID of the index supporting
+ * the constraint.
+ */
+static Oid
+transformFkeyCheckAttrs(Relation pkrel,
+ int numattrs, int16 *attnums)
+{
+ Oid indexoid = InvalidOid;
+ bool found = false;
+ List *indexoidlist,
+ *indexoidscan;
+
+ /*
+ * Get the list of index OIDs for the table from the relcache, and
+ * look up each one in the pg_index syscache, and match unique indexes
+ * to the list of attnums we are given.
+ */
+ indexoidlist = RelationGetIndexList(pkrel);
+
+ foreach(indexoidscan, indexoidlist)
+ {
+ HeapTuple indexTuple;
+ Form_pg_index indexStruct;
+ int i, j;
+
+ indexoid = lfirsti(indexoidscan);
+ indexTuple = SearchSysCache(INDEXRELID,
+ ObjectIdGetDatum(indexoid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(indexTuple))
+ elog(ERROR, "transformFkeyCheckAttrs: index %u not found",
+ indexoid);
+ indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
+
+ /*
+ * Must be unique, not a functional index, and not a partial index
+ */
+ if (indexStruct->indisunique &&
+ indexStruct->indproc == InvalidOid &&
+ VARSIZE(&indexStruct->indpred) <= VARHDRSZ)
+ {
+ for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++)
+ ;
+ if (i == numattrs)
+ {
+ /*
+ * The given attnum list may match the index columns in any
+ * order. Check that each list is a subset of the other.
+ */
+ for (i = 0; i < numattrs; i++)
+ {
+ found = false;
+ for (j = 0; j < numattrs; j++)
+ {
+ if (attnums[i] == indexStruct->indkey[j])
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ break;
}
- default:
- elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed");
+ if (found)
+ {
+ for (i = 0; i < numattrs; i++)
+ {
+ found = false;
+ for (j = 0; j < numattrs; j++)
+ {
+ if (attnums[j] == indexStruct->indkey[i])
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ break;
+ }
+ }
+ }
}
+ ReleaseSysCache(indexTuple);
+ if (found)
+ break;
}
- /* Close rel, but keep lock till commit */
- heap_close(rel, NoLock);
+ if (!found)
+ elog(ERROR, "UNIQUE constraint matching given keys for referenced table \"%s\" not found",
+ RelationGetRelationName(pkrel));
+
+ freeList(indexoidlist);
+
+ return indexoid;
}
/*
@@ -2835,73 +3167,6 @@ validateForeignKeyConstraint(FkConstraint *fkconstraint,
}
/*
- * Record an FK constraint in pg_constraint.
- */
-static Oid
-createForeignKeyConstraint(Relation rel, Relation pkrel,
- FkConstraint *fkconstraint)
-{
- int16 *fkattr;
- int16 *pkattr;
- int fkcount;
- int pkcount;
- List *l;
- int i;
-
- /* Convert foreign-key attr names to attr number array */
- fkcount = length(fkconstraint->fk_attrs);
- fkattr = (int16 *) palloc(fkcount * sizeof(int16));
- i = 0;
- foreach(l, fkconstraint->fk_attrs)
- {
- char *id = strVal(lfirst(l));
- AttrNumber attno;
-
- attno = get_attnum(RelationGetRelid(rel), id);
- if (attno == InvalidAttrNumber)
- elog(ERROR, "Relation \"%s\" has no column \"%s\"",
- RelationGetRelationName(rel), id);
- fkattr[i++] = attno;
- }
-
- /* The same for the referenced primary key attrs */
- pkcount = length(fkconstraint->pk_attrs);
- pkattr = (int16 *) palloc(pkcount * sizeof(int16));
- i = 0;
- foreach(l, fkconstraint->pk_attrs)
- {
- char *id = strVal(lfirst(l));
- AttrNumber attno;
-
- attno = get_attnum(RelationGetRelid(pkrel), id);
- if (attno == InvalidAttrNumber)
- elog(ERROR, "Relation \"%s\" has no column \"%s\"",
- RelationGetRelationName(pkrel), id);
- pkattr[i++] = attno;
- }
-
- /* Now we can make the pg_constraint entry */
- return CreateConstraintEntry(fkconstraint->constr_name,
- RelationGetNamespace(rel),
- CONSTRAINT_FOREIGN,
- fkconstraint->deferrable,
- fkconstraint->initdeferred,
- RelationGetRelid(rel),
- fkattr,
- fkcount,
- InvalidOid, /* not a domain constraint */
- RelationGetRelid(pkrel),
- pkattr,
- pkcount,
- fkconstraint->fk_upd_action,
- fkconstraint->fk_del_action,
- fkconstraint->fk_matchtype,
- NULL, /* no check constraint */
- NULL,
- NULL);
-}
-
-/*
* Create the triggers that implement an FK constraint.
*/
static void