summaryrefslogtreecommitdiff
path: root/src/backend/parser/analyze.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r--src/backend/parser/analyze.c454
1 files changed, 38 insertions, 416 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 663ae22d942..fe058125a04 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.249 2002/09/18 21:35:21 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.250 2002/09/22 00:37:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -117,10 +117,7 @@ static List *getSetColTypes(ParseState *pstate, Node *node);
static void transformForUpdate(Query *qry, List *forUpdate);
static void transformConstraintAttrs(List *constraintList);
static void transformColumnType(ParseState *pstate, ColumnDef *column);
-static void transformFkeyCheckAttrs(FkConstraint *fkconstraint, Oid *pktypoid);
-static void transformFkeyGetPrimaryKey(FkConstraint *fkconstraint, Oid *pktypoid);
static bool relationHasPrimaryKey(Oid relationOid);
-static Oid transformFkeyGetColType(CreateStmtContext *cxt, char *colname);
static void release_pstate_resources(ParseState *pstate);
static FromExpr *makeFromExpr(List *fromlist, Node *quals);
@@ -1301,189 +1298,42 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
static void
transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
{
- List *fkactions = NIL;
- List *fkclist;
-
if (cxt->fkconstraints == NIL)
return;
elog(NOTICE, "%s will create implicit trigger(s) for FOREIGN KEY check(s)",
cxt->stmtType);
- foreach(fkclist, cxt->fkconstraints)
+ /*
+ * For ALTER TABLE ADD CONSTRAINT, nothing to do. For CREATE TABLE,
+ * gin up an ALTER TABLE ADD CONSTRAINT command to execute after
+ * the basic CREATE TABLE is complete.
+ *
+ * Note: the ADD CONSTRAINT command must also execute after any index
+ * creation commands. Thus, this should run after
+ * transformIndexConstraints, so that the CREATE INDEX commands are
+ * already in cxt->alist.
+ */
+ if (strcmp(cxt->stmtType, "CREATE TABLE") == 0)
{
- FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
- Oid pktypoid[INDEX_MAX_KEYS];
- Oid fktypoid[INDEX_MAX_KEYS];
- int i;
- int attnum;
- List *fkattrs;
-
- for (attnum = 0; attnum < INDEX_MAX_KEYS; attnum++)
- pktypoid[attnum] = fktypoid[attnum] = InvalidOid;
-
- /*
- * Look up the referencing attributes to make sure they exist (or
- * will exist) in this table, and remember their type OIDs.
- */
- attnum = 0;
- foreach(fkattrs, fkconstraint->fk_attrs)
- {
- char *fkattr = strVal(lfirst(fkattrs));
+ AlterTableStmt *alterstmt = makeNode(AlterTableStmt);
+ List *fkclist;
- if (attnum >= INDEX_MAX_KEYS)
- elog(ERROR, "Can only have %d keys in a foreign key",
- INDEX_MAX_KEYS);
- fktypoid[attnum++] = transformFkeyGetColType(cxt, fkattr);
- }
+ alterstmt->subtype = 'c'; /* preprocessed add constraint */
+ alterstmt->relation = cxt->relation;
+ alterstmt->name = NULL;
+ alterstmt->def = (Node *) cxt->fkconstraints;
- /*
- * If the attribute list for the referenced table was omitted,
- * lookup the definition of the primary key.
- */
- if (fkconstraint->pk_attrs == NIL)
+ /* Don't need to scan the table contents in this case */
+ foreach(fkclist, cxt->fkconstraints)
{
- if (strcmp(fkconstraint->pktable->relname, cxt->relation->relname) != 0)
- transformFkeyGetPrimaryKey(fkconstraint, pktypoid);
- else if (cxt->pkey != NULL)
- {
- /* Use the to-be-created primary key */
- List *attr;
+ FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
- attnum = 0;
- foreach(attr, cxt->pkey->indexParams)
- {
- IndexElem *ielem = lfirst(attr);
- char *iname = ielem->name;
-
- Assert(iname); /* no func index here */
- fkconstraint->pk_attrs = lappend(fkconstraint->pk_attrs,
- makeString(iname));
- if (attnum >= INDEX_MAX_KEYS)
- elog(ERROR, "Can only have %d keys in a foreign key",
- INDEX_MAX_KEYS);
- pktypoid[attnum++] = transformFkeyGetColType(cxt,
- iname);
- }
- }
- else
- {
- /* In ALTER TABLE case, primary key may already exist */
- if (OidIsValid(cxt->relOid))
- transformFkeyGetPrimaryKey(fkconstraint, pktypoid);
- else
- elog(ERROR, "PRIMARY KEY for referenced table \"%s\" not found",
- fkconstraint->pktable->relname);
- }
- }
- else
- {
- /* Validate the specified referenced key list */
- if (strcmp(fkconstraint->pktable->relname, cxt->relation->relname) != 0)
- transformFkeyCheckAttrs(fkconstraint, pktypoid);
- else
- {
- /* Look for a matching new unique/primary constraint */
- List *index;
- bool found = false;
-
- foreach(index, cxt->alist)
- {
- IndexStmt *ind = lfirst(index);
- List *pkattrs;
-
- if (!ind->unique)
- continue;
- if (length(ind->indexParams) !=
- length(fkconstraint->pk_attrs))
- continue;
- attnum = 0;
- foreach(pkattrs, fkconstraint->pk_attrs)
- {
- char *pkattr = strVal(lfirst(pkattrs));
- List *indparms;
-
- found = false;
- foreach(indparms, ind->indexParams)
- {
- IndexElem *indparm = lfirst(indparms);
-
- if (indparm->name &&
- strcmp(indparm->name, pkattr) == 0)
- {
- found = true;
- break;
- }
- }
- if (!found)
- break;
- if (attnum >= INDEX_MAX_KEYS)
- elog(ERROR, "Can only have %d keys in a foreign key",
- INDEX_MAX_KEYS);
- pktypoid[attnum++] = transformFkeyGetColType(cxt,
- pkattr);
- }
- if (found)
- break;
- }
- if (!found)
- {
- /*
- * In ALTER TABLE case, such an index may already
- * exist
- */
- if (OidIsValid(cxt->relOid))
- transformFkeyCheckAttrs(fkconstraint, pktypoid);
- else
- elog(ERROR, "UNIQUE constraint matching given keys for referenced table \"%s\" not found",
- fkconstraint->pktable->relname);
- }
- }
- }
-
- /* Be sure referencing and referenced column types are comparable */
- for (i = 0; i < INDEX_MAX_KEYS && fktypoid[i] != 0; 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);
- }
-
- /*
- * For ALTER TABLE ADD CONSTRAINT, we're done. For CREATE TABLE,
- * gin up an ALTER TABLE ADD CONSTRAINT command to execute after
- * the basic CREATE TABLE is complete.
- */
- if (strcmp(cxt->stmtType, "CREATE TABLE") == 0)
- {
- AlterTableStmt *alterstmt = makeNode(AlterTableStmt);
-
- alterstmt->subtype = 'c'; /* preprocessed add constraint */
- alterstmt->relation = cxt->relation;
- alterstmt->name = NULL;
- alterstmt->def = (Node *) makeList1(fkconstraint);
-
- /* Don't need to scan the table contents in this case */
fkconstraint->skip_validation = true;
-
- fkactions = lappend(fkactions, (Node *) alterstmt);
}
- }
- /*
- * Attach completed list of extra actions to cxt->alist. We cannot do
- * this earlier, because we assume above that cxt->alist still holds
- * only IndexStmts.
- */
- cxt->alist = nconc(cxt->alist, fkactions);
+ cxt->alist = lappend(cxt->alist, (Node *) alterstmt);
+ }
}
/*
@@ -2375,6 +2225,7 @@ static Query *
transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
List **extras_before, List **extras_after)
{
+ Relation rel;
CreateStmtContext cxt;
Query *qry;
@@ -2382,14 +2233,20 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
* The only subtypes that currently require parse transformation
* handling are 'A'dd column and Add 'C'onstraint. These largely
* re-use code from CREATE TABLE.
+ *
+ * If we need to do any parse transformation, get exclusive lock on
+ * the relation to make sure it won't change before we execute the
+ * command.
*/
switch (stmt->subtype)
{
case 'A':
+ rel = heap_openrv(stmt->relation, AccessExclusiveLock);
+
cxt.stmtType = "ALTER TABLE";
cxt.relation = stmt->relation;
cxt.inhRelations = NIL;
- cxt.relOid = RangeVarGetRelid(stmt->relation, false);
+ cxt.relOid = RelationGetRelid(rel);
cxt.hasoids = SearchSysCacheExists(ATTNUM,
ObjectIdGetDatum(cxt.relOid),
Int16GetDatum(ObjectIdAttributeNumber),
@@ -2412,13 +2269,17 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
((ColumnDef *) stmt->def)->constraints = cxt.ckconstraints;
*extras_before = nconc(*extras_before, cxt.blist);
*extras_after = nconc(cxt.alist, *extras_after);
+
+ heap_close(rel, NoLock); /* close rel, keep lock */
break;
case 'C':
+ rel = heap_openrv(stmt->relation, AccessExclusiveLock);
+
cxt.stmtType = "ALTER TABLE";
cxt.relation = stmt->relation;
cxt.inhRelations = NIL;
- cxt.relOid = RangeVarGetRelid(stmt->relation, false);
+ cxt.relOid = RelationGetRelid(rel);
cxt.hasoids = SearchSysCacheExists(ATTNUM,
ObjectIdGetDatum(cxt.relOid),
Int16GetDatum(ObjectIdAttributeNumber),
@@ -2446,6 +2307,8 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
stmt->def = (Node *) nconc(cxt.ckconstraints, cxt.fkconstraints);
*extras_before = nconc(*extras_before, cxt.blist);
*extras_after = nconc(cxt.alist, *extras_after);
+
+ heap_close(rel, NoLock); /* close rel, keep lock */
break;
case 'c':
@@ -2675,174 +2538,6 @@ transformForUpdate(Query *qry, List *forUpdate)
/*
- * transformFkeyCheckAttrs -
- *
- * Make sure that the attributes of a referenced table
- * belong to a unique (or primary key) constraint.
- */
-static void
-transformFkeyCheckAttrs(FkConstraint *fkconstraint, Oid *pktypoid)
-{
- Relation pkrel;
- List *indexoidlist,
- *indexoidscan;
- int i;
- bool found = false;
-
- /*
- * Open the referenced table
- */
- pkrel = heap_openrv(fkconstraint->pktable, AccessShareLock);
-
- if (pkrel->rd_rel->relkind != RELKIND_RELATION)
- elog(ERROR, "Referenced relation \"%s\" is not a table",
- fkconstraint->pktable->relname);
-
- /*
- * Get the list of index OIDs for the table from the relcache, and
- * look up each one in the pg_index syscache for each unique one, and
- * then compare the attributes we were given to those defined.
- */
- indexoidlist = RelationGetIndexList(pkrel);
-
- foreach(indexoidscan, indexoidlist)
- {
- Oid indexoid = lfirsti(indexoidscan);
- HeapTuple indexTuple;
- Form_pg_index indexStruct;
-
- found = false;
- 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);
-
- if (indexStruct->indisunique)
- {
- for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++)
- ;
- if (i == length(fkconstraint->pk_attrs))
- {
- /* go through the fkconstraint->pk_attrs list */
- List *attrl;
- int attnum = 0;
-
- foreach(attrl, fkconstraint->pk_attrs)
- {
- char *attrname = strVal(lfirst(attrl));
-
- found = false;
- for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++)
- {
- int pkattno = indexStruct->indkey[i];
-
- if (namestrcmp(attnumAttName(pkrel, pkattno),
- attrname) == 0)
- {
- pktypoid[attnum++] = attnumTypeId(pkrel, pkattno);
- found = true;
- break;
- }
- }
- if (!found)
- break;
- }
- }
- }
- ReleaseSysCache(indexTuple);
- if (found)
- break;
- }
- if (!found)
- elog(ERROR, "UNIQUE constraint matching given keys for referenced table \"%s\" not found",
- fkconstraint->pktable->relname);
-
- freeList(indexoidlist);
- heap_close(pkrel, AccessShareLock);
-}
-
-
-/*
- * transformFkeyGetPrimaryKey -
- *
- * Try to find the primary key attributes of a referenced table if
- * the column list in the REFERENCES specification was omitted.
- */
-static void
-transformFkeyGetPrimaryKey(FkConstraint *fkconstraint, Oid *pktypoid)
-{
- Relation pkrel;
- List *indexoidlist,
- *indexoidscan;
- HeapTuple indexTuple = NULL;
- Form_pg_index indexStruct = NULL;
- int i;
- int attnum = 0;
-
- /*
- * Open the referenced table
- */
- pkrel = heap_openrv(fkconstraint->pktable, AccessShareLock);
-
- if (pkrel->rd_rel->relkind != RELKIND_RELATION)
- elog(ERROR, "Referenced relation \"%s\" is not a table",
- fkconstraint->pktable->relname);
-
- /*
- * 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)
- 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",
- fkconstraint->pktable->relname);
-
- /*
- * Now build the list of PK attributes from the indkey definition
- * using the attribute names of the PK relation descriptor
- */
- for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++)
- {
- int pkattno = indexStruct->indkey[i];
-
- pktypoid[attnum++] = attnumTypeId(pkrel, pkattno);
- fkconstraint->pk_attrs = lappend(fkconstraint->pk_attrs,
- makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
- }
-
- ReleaseSysCache(indexTuple);
-
- heap_close(pkrel, AccessShareLock);
-}
-
-/*
* relationHasPrimaryKey -
*
* See whether an existing relation has a primary key.
@@ -2889,79 +2584,6 @@ relationHasPrimaryKey(Oid relationOid)
}
/*
- * transformFkeyGetColType -
- *
- * Find a referencing column by name, and return its type OID.
- * Error if it can't be found.
- */
-static Oid
-transformFkeyGetColType(CreateStmtContext *cxt, char *colname)
-{
- List *cols;
- List *inher;
- Oid result;
- Form_pg_attribute sysatt;
-
- /* First look for column among the newly-created columns */
- foreach(cols, cxt->columns)
- {
- ColumnDef *col = lfirst(cols);
-
- if (strcmp(col->colname, colname) == 0)
- return typenameTypeId(col->typename);
- }
- /* Perhaps it's a system column name */
- sysatt = SystemAttributeByName(colname, cxt->hasoids);
- if (sysatt)
- return sysatt->atttypid;
- /* Look for column among inherited columns (if CREATE TABLE case) */
- foreach(inher, cxt->inhRelations)
- {
- RangeVar *inh = lfirst(inher);
- Relation rel;
- int count;
-
- Assert(IsA(inh, RangeVar));
- rel = heap_openrv(inh, AccessShareLock);
- if (rel->rd_rel->relkind != RELKIND_RELATION)
- elog(ERROR, "inherited table \"%s\" is not a relation",
- inh->relname);
- for (count = 0; count < rel->rd_att->natts; count++)
- {
- Form_pg_attribute inhattr = rel->rd_att->attrs[count];
- char *inhname = NameStr(inhattr->attname);
-
- if (inhattr->attisdropped)
- continue;
- if (strcmp(inhname, colname) == 0)
- {
- result = inhattr->atttypid;
- heap_close(rel, NoLock);
- return result;
- }
- }
- heap_close(rel, NoLock);
- }
- /* Look for column among existing columns (if ALTER TABLE case) */
- if (OidIsValid(cxt->relOid))
- {
- HeapTuple atttuple;
-
- atttuple = SearchSysCacheAttName(cxt->relOid, colname);
- if (HeapTupleIsValid(atttuple))
- {
- result = ((Form_pg_attribute) GETSTRUCT(atttuple))->atttypid;
- ReleaseSysCache(atttuple);
- return result;
- }
- }
-
- elog(ERROR, "%s: column \"%s\" referenced in foreign key constraint does not exist",
- cxt->stmtType, colname);
- return InvalidOid; /* keep compiler quiet */
-}
-
-/*
* Preprocess a list of column constraint clauses
* to attach constraint attributes to their primary constraint nodes
* and detect inconsistent/misplaced constraint attributes.