+ pktypoid[attnum] = fktypoid[attnum] = InvalidOid;
- foreach(fkclist, fkconstraints)
+ /*
+ * 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)
{
- fkconstraint = (FkConstraint *) lfirst(fkclist);
+ Ident *fkattr = lfirst(fkattrs);
- /*
- * If the constraint has no name, set it to
- *
- */
- if (fkconstraint->constr_name == NULL)
- fkconstraint->constr_name = "";
+ if (attnum >= INDEX_MAX_KEYS)
+ elog(ERROR, "Can only have %d keys in a foreign key",
+ INDEX_MAX_KEYS);
+ fktypoid[attnum++] = transformFkeyGetColType(cxt,
+ fkattr->name);
+ }
- /*
- * Check to see if the attributes mentioned by the constraint
- * actually exist on this table.
- */
- if (fkconstraint->fk_attrs != NIL)
+ /*
+ * If the attribute list for the referenced table was omitted,
+ * lookup the definition of the primary key.
+ */
+ if (fkconstraint->pk_attrs == NIL)
+ {
+ if (strcmp(fkconstraint->pktable_name, cxt->relname) != 0)
+ transformFkeyGetPrimaryKey(fkconstraint, pktypoid);
+ else if (cxt->pkey != NULL)
{
- int found = 0;
- int attnum=0;
- List *cols;
- List *fkattrs;
- Ident *fkattr = NULL;
- ColumnDef *col;
-
- foreach(fkattrs, fkconstraint->fk_attrs)
- {
- found = 0;
- fkattr = lfirst(fkattrs);
- foreach(cols, stmt->tableElts)
- {
- col = lfirst(cols);
- if (strcmp(col->colname, fkattr->name) == 0)
- {
- char *buff=TypeNameToInternalName(col->typename);
- Oid type=typenameTypeId(buff);
- if (!OidIsValid(type)) {
- elog(ERROR, "Unable to lookup type %s", col->typename->name);
- }
- fktypoid[attnum++]=type;
- found = 1;
- break;
- }
- }
- if (!found) {
- List *inher;
- List *inhRelnames = stmt->inhRelnames;
- Relation rel;
+ /* Use the to-be-created primary key */
+ List *attr;
- foreach(inher, inhRelnames)
- {
- Value *inh = lfirst(inher);
- int count;
-
- Assert(IsA(inh, String));
- rel = heap_openr(strVal(inh), AccessShareLock);
- if (rel->rd_rel->relkind != RELKIND_RELATION)
- elog(ERROR, "inherited table \"%s\" is not a relation",
- strVal(inh));
- for (count = 0; count < rel->rd_att->natts; count++)
- {
- char *name = NameStr(rel->rd_att->attrs[count]->attname);
-
- if (strcmp(fkattr->name, name) == 0)
- {
- fktypoid[attnum++]=rel->rd_att->attrs[count]->atttypid;
- found = 1;
- break;
- }
- }
- heap_close(rel, NoLock);
- if (found)
- break;
- }
- }
- if (!found)
- break;
+ attnum=0;
+ foreach(attr, cxt->pkey->indexParams)
+ {
+ IndexElem *ielem = lfirst(attr);
+ Ident *pkattr = (Ident *) makeNode(Ident);
+
+ pkattr->name = pstrdup(ielem->name);
+ pkattr->indirection = NIL;
+ pkattr->isRel = false;
+ fkconstraint->pk_attrs = lappend(fkconstraint->pk_attrs,
+ pkattr);
+ if (attnum >= INDEX_MAX_KEYS)
+ elog(ERROR, "Can only have %d keys in a foreign key",
+ INDEX_MAX_KEYS);
+ pktypoid[attnum++] = transformFkeyGetColType(cxt,
+ ielem->name);
}
- if (!found)
- elog(ERROR, "columns referenced in foreign key constraint not found.");
}
-
- /*
- * If the attribute list for the referenced table was omitted,
- * lookup for the definition of the primary key. If the
- * referenced table is this table, use the definition we found
- * above, rather than looking to the system tables.
- *
- */
- if (fkconstraint->fk_attrs != NIL && fkconstraint->pk_attrs == NIL)
+ else
{
- if (strcmp(fkconstraint->pktable_name, stmt->relname) != 0)
+ /* In ALTER TABLE case, primary key may already exist */
+ if (OidIsValid(cxt->relOid))
transformFkeyGetPrimaryKey(fkconstraint, pktypoid);
- else if (pkey != NULL)
- {
- List *pkey_attr = pkey->indexParams;
- List *attr;
- List *findattr;
- IndexElem *ielem;
- Ident *pkattr;
- int attnum=0;
- ColumnDef *col;
-
- foreach(attr, pkey_attr)
- {
- ielem = lfirst(attr);
- pkattr = (Ident *) makeNode(Ident);
- pkattr->name = pstrdup(ielem->name);
- pkattr->indirection = NIL;
- pkattr->isRel = false;
- fkconstraint->pk_attrs = lappend(fkconstraint->pk_attrs, pkattr);
- foreach (findattr, stmt->tableElts) {
- col=lfirst(findattr);
- if (strcmp(col->colname, ielem->name)==0) {
- char *buff=TypeNameToInternalName(col->typename);
- Oid type=typenameTypeId(buff);
- if (!OidIsValid(type)) {
- elog(ERROR, "Unable to lookup type %s", col->typename->name);
- }
- pktypoid[attnum++]=type; /* need to convert typename */
- break;
- }
- }
- }
- }
else
- {
elog(ERROR, "PRIMARY KEY for referenced table \"%s\" not found",
fkconstraint->pktable_name);
- }
}
+ }
+ else
+ {
+ /* Validate the specified referenced key list */
+ if (strcmp(fkconstraint->pktable_name, cxt->relname) != 0)
+ transformFkeyCheckAttrs(fkconstraint, pktypoid);
else
{
- if (strcmp(fkconstraint->pktable_name, stmt->relname) != 0)
- transformFkeyCheckAttrs(fkconstraint, pktypoid);
- else
+ /* Look for a matching new unique/primary constraint */
+ List *index;
+ bool found = false;
+
+ foreach(index, cxt->alist)
{
- /* Get a unique/pk constraint from above */
- List *index;
- int found = 0;
+ IndexStmt *ind = lfirst(index);
+ List *pkattrs;
- foreach(index, ilist)
+ if (!ind->unique)
+ continue;
+ if (length(ind->indexParams) !=
+ length(fkconstraint->pk_attrs))
+ continue;
+ attnum=0;
+ foreach(pkattrs, fkconstraint->pk_attrs)
{
- IndexStmt *ind = lfirst(index);
- IndexElem *indparm;
+ Ident *pkattr = lfirst(pkattrs);
List *indparms;
- List *pkattrs;
- List *findattr;
- ColumnDef *col;
- Ident *pkattr;
- if (ind->unique)
+ found = false;
+ foreach(indparms, ind->indexParams)
{
- int count = 0;
- int attnum=0;
-
- foreach(indparms, ind->indexParams)
- count++;
- if (count != length(fkconstraint->pk_attrs))
- found = 0;
- else
+ IndexElem *indparm = lfirst(indparms);
+
+ if (strcmp(indparm->name, pkattr->name) == 0)
{
- foreach(pkattrs, fkconstraint->pk_attrs)
- {
- found = 0;
- pkattr = lfirst(pkattrs);
- foreach(indparms, ind->indexParams)
- {
- indparm = lfirst(indparms);
- if (strcmp(indparm->name, pkattr->name) == 0)
- {
- foreach (findattr, stmt->tableElts) {
- col=lfirst(findattr);
- if (strcmp(col->colname, indparm->name)==0) {
- char *buff=TypeNameToInternalName(col->typename);
- Oid type=typenameTypeId(buff);
- if (!OidIsValid(type)) {
- elog(ERROR, "Unable to lookup type %s", col->typename->name);
- }
- pktypoid[attnum++]=type;
- found=1;
- break;
- }
- }
- if (!found) {
- List *inher;
- List *inhRelnames=stmt->inhRelnames;
- Relation rel;
- foreach (inher, inhRelnames) {
- Value *inh=lfirst(inher);
- int count;
- Assert(IsA(inh, String));
- rel=heap_openr(strVal(inh), AccessShareLock);
- if (rel->rd_rel->relkind!=RELKIND_RELATION)
- elog(ERROR, "inherited table \"%s\" is not a relation", strVal(inh));
- for (count=0; countrd_att->natts; count++) {
- char *name=NameStr(rel->rd_att->attrs[count]->attname);
- if (strcmp(pkattr->name, name)==0) {
- pktypoid[attnum++]=rel->rd_att->attrs[count]->atttypid;
- found=1;
- break;
- }
- }
- heap_close(rel, NoLock);
- if (found)
- break;
- }
- }
- break;
- }
- }
- if (!found)
- break;
- }
+ found = true;
+ break;
}
}
- if (found)
+ 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->name);
}
- if (!found)
+ 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_name);
}
}
+ }
- for (i = 0; i < INDEX_MAX_KEYS && fktypoid[i] != 0; i++) {
- /*
- * fktypoid[i] is the foreign key table's i'th element's type oid
- * pktypoid[i] is the primary key table's i'th element's type oid
- * We let oper() do our work for us, including elog(ERROR) if the
- * types don't compare with =
- */
- Operator o=oper("=", fktypoid[i], pktypoid[i], false);
- ReleaseSysCache(o);
- }
+ /* Be sure referencing and referenced column types are comparable */
+ for (i = 0; i < INDEX_MAX_KEYS && fktypoid[i] != 0; i++)
+ {
/*
- * Build a CREATE CONSTRAINT TRIGGER statement for the CHECK
- * action.
- *
+ * fktypoid[i] is the foreign key table's i'th element's type oid
+ * pktypoid[i] is the primary key table's i'th element's type oid
+ * We let oper() do our work for us, including elog(ERROR) if the
+ * types don't compare with =
*/
- fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->constr_name;
- fk_trigger->relname = stmt->relname;
- fk_trigger->funcname = "RI_FKey_check_ins";
- fk_trigger->before = false;
- fk_trigger->row = true;
- fk_trigger->actions[0] = 'i';
- fk_trigger->actions[1] = 'u';
- fk_trigger->actions[2] = '\0';
- fk_trigger->lang = NULL;
- fk_trigger->text = NULL;
-
- fk_trigger->attr = NIL;
- fk_trigger->when = NULL;
- fk_trigger->isconstraint = true;
- fk_trigger->deferrable = fkconstraint->deferrable;
- fk_trigger->initdeferred = fkconstraint->initdeferred;
- fk_trigger->constrrelname = fkconstraint->pktable_name;
-
- fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(stmt->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable_name));
- fk_trigger->args = lappend(fk_trigger->args,
+ Operator o=oper("=", fktypoid[i], pktypoid[i], false);
+ ReleaseSysCache(o);
+ }
+
+ /*
+ * Build a CREATE CONSTRAINT TRIGGER statement for the CHECK
+ * action.
+ */
+ fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
+ fk_trigger->trigname = fkconstraint->constr_name;
+ fk_trigger->relname = cxt->relname;
+ fk_trigger->funcname = "RI_FKey_check_ins";
+ fk_trigger->before = false;
+ fk_trigger->row = true;
+ fk_trigger->actions[0] = 'i';
+ fk_trigger->actions[1] = 'u';
+ fk_trigger->actions[2] = '\0';
+ fk_trigger->lang = NULL;
+ fk_trigger->text = NULL;
+
+ fk_trigger->attr = NIL;
+ fk_trigger->when = NULL;
+ fk_trigger->isconstraint = true;
+ fk_trigger->deferrable = fkconstraint->deferrable;
+ fk_trigger->initdeferred = fkconstraint->initdeferred;
+ fk_trigger->constrrelname = fkconstraint->pktable_name;
+
+ fk_trigger->args = NIL;
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->constr_name));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(cxt->relname));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->pktable_name));
+ fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->match_type));
- fk_attr = fkconstraint->fk_attrs;
- pk_attr = fkconstraint->pk_attrs;
- if (length(fk_attr) != length(pk_attr))
- {
- elog(NOTICE, "Illegal FOREIGN KEY definition REFERENCES \"%s\"",
- fkconstraint->pktable_name);
- elog(ERROR, "number of key attributes in referenced table must be equal to foreign key");
- }
- while (fk_attr != NIL)
- {
- id = (Ident *) lfirst(fk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
+ fk_attr = fkconstraint->fk_attrs;
+ pk_attr = fkconstraint->pk_attrs;
+ if (length(fk_attr) != length(pk_attr))
+ elog(ERROR, "number of key attributes in referenced table must be equal to foreign key"
+ "\n\tIllegal FOREIGN KEY definition references \"%s\"",
+ fkconstraint->pktable_name);
+
+ while (fk_attr != NIL)
+ {
+ id = (Ident *) lfirst(fk_attr);
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(id->name));
- id = (Ident *) lfirst(pk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
+ id = (Ident *) lfirst(pk_attr);
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(id->name));
- fk_attr = lnext(fk_attr);
- pk_attr = lnext(pk_attr);
- }
+ fk_attr = lnext(fk_attr);
+ pk_attr = lnext(pk_attr);
+ }
- extras_after = lappend(extras_after, (Node *) fk_trigger);
+ fkactions = lappend(fkactions, (Node *) fk_trigger);
- /*
- * Build a CREATE CONSTRAINT TRIGGER statement for the ON
- * DELETE action fired on the PK table !!!
- *
- */
- fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->constr_name;
- fk_trigger->relname = fkconstraint->pktable_name;
- fk_trigger->before = false;
- fk_trigger->row = true;
- fk_trigger->actions[0] = 'd';
- fk_trigger->actions[1] = '\0';
- fk_trigger->lang = NULL;
- fk_trigger->text = NULL;
-
- fk_trigger->attr = NIL;
- fk_trigger->when = NULL;
- fk_trigger->isconstraint = true;
- fk_trigger->deferrable = fkconstraint->deferrable;
- fk_trigger->initdeferred = fkconstraint->initdeferred;
- fk_trigger->constrrelname = stmt->relname;
- switch ((fkconstraint->actions & FKCONSTR_ON_DELETE_MASK)
- >> FKCONSTR_ON_DELETE_SHIFT)
- {
- case FKCONSTR_ON_KEY_NOACTION:
- fk_trigger->funcname = "RI_FKey_noaction_del";
- break;
- case FKCONSTR_ON_KEY_RESTRICT:
- fk_trigger->deferrable = false;
- fk_trigger->initdeferred = false;
- fk_trigger->funcname = "RI_FKey_restrict_del";
- break;
- case FKCONSTR_ON_KEY_CASCADE:
- fk_trigger->funcname = "RI_FKey_cascade_del";
- break;
- case FKCONSTR_ON_KEY_SETNULL:
- fk_trigger->funcname = "RI_FKey_setnull_del";
- break;
- case FKCONSTR_ON_KEY_SETDEFAULT:
- fk_trigger->funcname = "RI_FKey_setdefault_del";
- break;
- default:
- elog(ERROR, "Only one ON DELETE action can be specified for FOREIGN KEY constraint");
- break;
- }
+ /*
+ * Build a CREATE CONSTRAINT TRIGGER statement for the ON
+ * DELETE action fired on the PK table !!!
+ */
+ fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
+ fk_trigger->trigname = fkconstraint->constr_name;
+ fk_trigger->relname = fkconstraint->pktable_name;
+ fk_trigger->before = false;
+ fk_trigger->row = true;
+ fk_trigger->actions[0] = 'd';
+ fk_trigger->actions[1] = '\0';
+ fk_trigger->lang = NULL;
+ fk_trigger->text = NULL;
+
+ fk_trigger->attr = NIL;
+ fk_trigger->when = NULL;
+ fk_trigger->isconstraint = true;
+ fk_trigger->deferrable = fkconstraint->deferrable;
+ fk_trigger->initdeferred = fkconstraint->initdeferred;
+ fk_trigger->constrrelname = cxt->relname;
+ switch ((fkconstraint->actions & FKCONSTR_ON_DELETE_MASK)
+ >> FKCONSTR_ON_DELETE_SHIFT)
+ {
+ case FKCONSTR_ON_KEY_NOACTION:
+ fk_trigger->funcname = "RI_FKey_noaction_del";
+ break;
+ case FKCONSTR_ON_KEY_RESTRICT:
+ fk_trigger->deferrable = false;
+ fk_trigger->initdeferred = false;
+ fk_trigger->funcname = "RI_FKey_restrict_del";
+ break;
+ case FKCONSTR_ON_KEY_CASCADE:
+ fk_trigger->funcname = "RI_FKey_cascade_del";
+ break;
+ case FKCONSTR_ON_KEY_SETNULL:
+ fk_trigger->funcname = "RI_FKey_setnull_del";
+ break;
+ case FKCONSTR_ON_KEY_SETDEFAULT:
+ fk_trigger->funcname = "RI_FKey_setdefault_del";
+ break;
+ default:
+ elog(ERROR, "Only one ON DELETE action can be specified for FOREIGN KEY constraint");
+ break;
+ }
- fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(stmt->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable_name));
- fk_trigger->args = lappend(fk_trigger->args,
+ fk_trigger->args = NIL;
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->constr_name));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(cxt->relname));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->pktable_name));
+ fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->match_type));
- fk_attr = fkconstraint->fk_attrs;
- pk_attr = fkconstraint->pk_attrs;
- while (fk_attr != NIL)
- {
- id = (Ident *) lfirst(fk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
+ fk_attr = fkconstraint->fk_attrs;
+ pk_attr = fkconstraint->pk_attrs;
+ while (fk_attr != NIL)
+ {
+ id = (Ident *) lfirst(fk_attr);
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(id->name));
- id = (Ident *) lfirst(pk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
+ id = (Ident *) lfirst(pk_attr);
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(id->name));
- fk_attr = lnext(fk_attr);
- pk_attr = lnext(pk_attr);
- }
+ fk_attr = lnext(fk_attr);
+ pk_attr = lnext(pk_attr);
+ }
- extras_after = lappend(extras_after, (Node *) fk_trigger);
+ fkactions = lappend(fkactions, (Node *) fk_trigger);
- /*
- * Build a CREATE CONSTRAINT TRIGGER statement for the ON
- * UPDATE action fired on the PK table !!!
- *
- */
- fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->constr_name;
- fk_trigger->relname = fkconstraint->pktable_name;
- fk_trigger->before = false;
- fk_trigger->row = true;
- fk_trigger->actions[0] = 'u';
- fk_trigger->actions[1] = '\0';
- fk_trigger->lang = NULL;
- fk_trigger->text = NULL;
-
- fk_trigger->attr = NIL;
- fk_trigger->when = NULL;
- fk_trigger->isconstraint = true;
- fk_trigger->deferrable = fkconstraint->deferrable;
- fk_trigger->initdeferred = fkconstraint->initdeferred;
- fk_trigger->constrrelname = stmt->relname;
- switch ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK)
- >> FKCONSTR_ON_UPDATE_SHIFT)
- {
- case FKCONSTR_ON_KEY_NOACTION:
- fk_trigger->funcname = "RI_FKey_noaction_upd";
- break;
- case FKCONSTR_ON_KEY_RESTRICT:
- fk_trigger->deferrable = false;
- fk_trigger->initdeferred = false;
- fk_trigger->funcname = "RI_FKey_restrict_upd";
- break;
- case FKCONSTR_ON_KEY_CASCADE:
- fk_trigger->funcname = "RI_FKey_cascade_upd";
- break;
- case FKCONSTR_ON_KEY_SETNULL:
- fk_trigger->funcname = "RI_FKey_setnull_upd";
- break;
- case FKCONSTR_ON_KEY_SETDEFAULT:
- fk_trigger->funcname = "RI_FKey_setdefault_upd";
- break;
- default:
- elog(ERROR, "Only one ON UPDATE action can be specified for FOREIGN KEY constraint");
- break;
- }
+ /*
+ * Build a CREATE CONSTRAINT TRIGGER statement for the ON
+ * UPDATE action fired on the PK table !!!
+ */
+ fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
+ fk_trigger->trigname = fkconstraint->constr_name;
+ fk_trigger->relname = fkconstraint->pktable_name;
+ fk_trigger->before = false;
+ fk_trigger->row = true;
+ fk_trigger->actions[0] = 'u';
+ fk_trigger->actions[1] = '\0';
+ fk_trigger->lang = NULL;
+ fk_trigger->text = NULL;
+
+ fk_trigger->attr = NIL;
+ fk_trigger->when = NULL;
+ fk_trigger->isconstraint = true;
+ fk_trigger->deferrable = fkconstraint->deferrable;
+ fk_trigger->initdeferred = fkconstraint->initdeferred;
+ fk_trigger->constrrelname = cxt->relname;
+ switch ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK)
+ >> FKCONSTR_ON_UPDATE_SHIFT)
+ {
+ case FKCONSTR_ON_KEY_NOACTION:
+ fk_trigger->funcname = "RI_FKey_noaction_upd";
+ break;
+ case FKCONSTR_ON_KEY_RESTRICT:
+ fk_trigger->deferrable = false;
+ fk_trigger->initdeferred = false;
+ fk_trigger->funcname = "RI_FKey_restrict_upd";
+ break;
+ case FKCONSTR_ON_KEY_CASCADE:
+ fk_trigger->funcname = "RI_FKey_cascade_upd";
+ break;
+ case FKCONSTR_ON_KEY_SETNULL:
+ fk_trigger->funcname = "RI_FKey_setnull_upd";
+ break;
+ case FKCONSTR_ON_KEY_SETDEFAULT:
+ fk_trigger->funcname = "RI_FKey_setdefault_upd";
+ break;
+ default:
+ elog(ERROR, "Only one ON UPDATE action can be specified for FOREIGN KEY constraint");
+ break;
+ }
- fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(stmt->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable_name));
- fk_trigger->args = lappend(fk_trigger->args,
+ fk_trigger->args = NIL;
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->constr_name));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(cxt->relname));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->pktable_name));
+ fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->match_type));
- fk_attr = fkconstraint->fk_attrs;
- pk_attr = fkconstraint->pk_attrs;
- while (fk_attr != NIL)
- {
- id = (Ident *) lfirst(fk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- id = (Ident *) lfirst(pk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
+ fk_attr = fkconstraint->fk_attrs;
+ pk_attr = fkconstraint->pk_attrs;
+ while (fk_attr != NIL)
+ {
+ id = (Ident *) lfirst(fk_attr);
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(id->name));
- fk_attr = lnext(fk_attr);
- pk_attr = lnext(pk_attr);
- }
+ id = (Ident *) lfirst(pk_attr);
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(id->name));
- extras_after = lappend(extras_after, (Node *) fk_trigger);
+ fk_attr = lnext(fk_attr);
+ pk_attr = lnext(pk_attr);
}
- }
- return q;
-} /* transformCreateStmt() */
+ fkactions = lappend(fkactions, (Node *) fk_trigger);
+ }
+ /*
+ * 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);
+}
/*
* transformIndexStmt -
/*
* tranformAlterTableStmt -
* transform an Alter Table Statement
- *
*/
static Query *
transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt)
{
+ CreateStmtContext cxt;
Query *qry;
- qry = makeNode(Query);
- qry->commandType = CMD_UTILITY;
-
/*
- * The only subtypes that currently have special handling are 'A'dd
- * column and Add 'C'onstraint. In addition, right now only Foreign
- * Key 'C'onstraints have a special transformation.
- *
+ * 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.
*/
switch (stmt->subtype)
{
case 'A':
- transformColumnType(pstate, (ColumnDef *) stmt->def);
+ cxt.stmtType = "ALTER TABLE";
+ cxt.relname = stmt->relname;
+ cxt.inhRelnames = NIL;
+ cxt.istemp = is_temp_rel_name(stmt->relname);
+ cxt.relOid = GetSysCacheOid(RELNAME,
+ PointerGetDatum(stmt->relname),
+ 0, 0, 0);
+ cxt.columns = NIL;
+ cxt.ckconstraints = NIL;
+ cxt.fkconstraints = NIL;
+ cxt.ixconstraints = NIL;
+ cxt.blist = NIL;
+ cxt.alist = NIL;
+ cxt.pkey = NULL;
+
+ Assert(IsA(stmt->def, ColumnDef));
+ transformColumnDefinition(pstate, &cxt,
+ (ColumnDef *) stmt->def);
+
+ transformIndexConstraints(pstate, &cxt);
+ transformFKConstraints(pstate, &cxt);
+
+ ((ColumnDef *) stmt->def)->constraints = cxt.ckconstraints;
+ extras_before = cxt.blist;
+ extras_after = cxt.alist;
break;
- case 'C':
- if (stmt->def && IsA(stmt->def, FkConstraint))
- {
- CreateTrigStmt *fk_trigger;
- List *fk_attr;
- List *pk_attr;
- Ident *id;
- FkConstraint *fkconstraint;
-
- extras_after = NIL;
- elog(NOTICE, "ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s)");
-
- fkconstraint = (FkConstraint *) stmt->def;
-
- /*
- * If the constraint has no name, set it to
- *
- */
- if (fkconstraint->constr_name == NULL)
- fkconstraint->constr_name = "";
-
- /*
- * If the attribute list for the referenced table was
- * omitted, lookup for the definition of the primary key
- *
- */
- if (fkconstraint->fk_attrs != NIL && fkconstraint->pk_attrs == NIL) {
- Oid pktypoid[INDEX_MAX_KEYS];
- transformFkeyGetPrimaryKey(fkconstraint, pktypoid);
- }
-
- /*
- * Build a CREATE CONSTRAINT TRIGGER statement for the
- * CHECK action.
- *
- */
- fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->constr_name;
- fk_trigger->relname = stmt->relname;
- fk_trigger->funcname = "RI_FKey_check_ins";
- fk_trigger->before = false;
- fk_trigger->row = true;
- fk_trigger->actions[0] = 'i';
- fk_trigger->actions[1] = 'u';
- fk_trigger->actions[2] = '\0';
- fk_trigger->lang = NULL;
- fk_trigger->text = NULL;
-
- fk_trigger->attr = NIL;
- fk_trigger->when = NULL;
- fk_trigger->isconstraint = true;
- fk_trigger->deferrable = fkconstraint->deferrable;
- fk_trigger->initdeferred = fkconstraint->initdeferred;
- fk_trigger->constrrelname = fkconstraint->pktable_name;
-
- fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(stmt->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->match_type));
- fk_attr = fkconstraint->fk_attrs;
- pk_attr = fkconstraint->pk_attrs;
- if (length(fk_attr) != length(pk_attr))
- {
- elog(NOTICE, "Illegal FOREIGN KEY definition REFERENCES \"%s\"",
- fkconstraint->pktable_name);
- elog(ERROR, "number of key attributes in referenced table must be equal to foreign key");
- }
- while (fk_attr != NIL)
- {
- id = (Ident *) lfirst(fk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- id = (Ident *) lfirst(pk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- fk_attr = lnext(fk_attr);
- pk_attr = lnext(pk_attr);
- }
-
- extras_after = lappend(extras_after, (Node *) fk_trigger);
-
- /*
- * Build a CREATE CONSTRAINT TRIGGER statement for the ON
- * DELETE action fired on the PK table !!!
- *
- */
- fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->constr_name;
- fk_trigger->relname = fkconstraint->pktable_name;
- switch ((fkconstraint->actions & FKCONSTR_ON_DELETE_MASK)
- >> FKCONSTR_ON_DELETE_SHIFT)
- {
- case FKCONSTR_ON_KEY_NOACTION:
- fk_trigger->funcname = "RI_FKey_noaction_del";
- break;
- case FKCONSTR_ON_KEY_RESTRICT:
- fk_trigger->funcname = "RI_FKey_restrict_del";
- break;
- case FKCONSTR_ON_KEY_CASCADE:
- fk_trigger->funcname = "RI_FKey_cascade_del";
- break;
- case FKCONSTR_ON_KEY_SETNULL:
- fk_trigger->funcname = "RI_FKey_setnull_del";
- break;
- case FKCONSTR_ON_KEY_SETDEFAULT:
- fk_trigger->funcname = "RI_FKey_setdefault_del";
- break;
- default:
- elog(ERROR, "Only one ON DELETE action can be specified for FOREIGN KEY constraint");
- break;
- }
- fk_trigger->before = false;
- fk_trigger->row = true;
- fk_trigger->actions[0] = 'd';
- fk_trigger->actions[1] = '\0';
- fk_trigger->lang = NULL;
- fk_trigger->text = NULL;
-
- fk_trigger->attr = NIL;
- fk_trigger->when = NULL;
- fk_trigger->isconstraint = true;
- fk_trigger->deferrable = fkconstraint->deferrable;
- fk_trigger->initdeferred = fkconstraint->initdeferred;
- fk_trigger->constrrelname = stmt->relname;
-
- fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(stmt->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->match_type));
- fk_attr = fkconstraint->fk_attrs;
- pk_attr = fkconstraint->pk_attrs;
- while (fk_attr != NIL)
- {
- id = (Ident *) lfirst(fk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- id = (Ident *) lfirst(pk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- fk_attr = lnext(fk_attr);
- pk_attr = lnext(pk_attr);
- }
- extras_after = lappend(extras_after, (Node *) fk_trigger);
-
- /*
- * Build a CREATE CONSTRAINT TRIGGER statement for the ON
- * UPDATE action fired on the PK table !!!
- *
- */
- fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->constr_name;
- fk_trigger->relname = fkconstraint->pktable_name;
- switch ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK)
- >> FKCONSTR_ON_UPDATE_SHIFT)
- {
- case FKCONSTR_ON_KEY_NOACTION:
- fk_trigger->funcname = "RI_FKey_noaction_upd";
- break;
- case FKCONSTR_ON_KEY_RESTRICT:
- fk_trigger->funcname = "RI_FKey_restrict_upd";
- break;
- case FKCONSTR_ON_KEY_CASCADE:
- fk_trigger->funcname = "RI_FKey_cascade_upd";
- break;
- case FKCONSTR_ON_KEY_SETNULL:
- fk_trigger->funcname = "RI_FKey_setnull_upd";
- break;
- case FKCONSTR_ON_KEY_SETDEFAULT:
- fk_trigger->funcname = "RI_FKey_setdefault_upd";
- break;
- default:
- elog(ERROR, "Only one ON UPDATE action can be specified for FOREIGN KEY constraint");
- break;
- }
- fk_trigger->before = false;
- fk_trigger->row = true;
- fk_trigger->actions[0] = 'u';
- fk_trigger->actions[1] = '\0';
- fk_trigger->lang = NULL;
- fk_trigger->text = NULL;
-
- fk_trigger->attr = NIL;
- fk_trigger->when = NULL;
- fk_trigger->isconstraint = true;
- fk_trigger->deferrable = fkconstraint->deferrable;
- fk_trigger->initdeferred = fkconstraint->initdeferred;
- fk_trigger->constrrelname = stmt->relname;
-
- fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(stmt->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->match_type));
- fk_attr = fkconstraint->fk_attrs;
- pk_attr = fkconstraint->pk_attrs;
- while (fk_attr != NIL)
- {
- id = (Ident *) lfirst(fk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- id = (Ident *) lfirst(pk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
+ case 'C':
+ cxt.stmtType = "ALTER TABLE";
+ cxt.relname = stmt->relname;
+ cxt.inhRelnames = NIL;
+ cxt.istemp = is_temp_rel_name(stmt->relname);
+ cxt.relOid = GetSysCacheOid(RELNAME,
+ PointerGetDatum(stmt->relname),
+ 0, 0, 0);
+ cxt.columns = NIL;
+ cxt.ckconstraints = NIL;
+ cxt.fkconstraints = NIL;
+ cxt.ixconstraints = NIL;
+ cxt.blist = NIL;
+ cxt.alist = NIL;
+ cxt.pkey = NULL;
+
+ if (IsA(stmt->def, Constraint))
+ transformTableConstraint(pstate, &cxt,
+ (Constraint *) stmt->def);
+ else if (IsA(stmt->def, FkConstraint))
+ cxt.fkconstraints = lappend(cxt.fkconstraints, stmt->def);
+ else
+ elog(ERROR, "Unexpected node type in ALTER TABLE ADD CONSTRAINT");
- fk_attr = lnext(fk_attr);
- pk_attr = lnext(pk_attr);
- }
+ transformIndexConstraints(pstate, &cxt);
+ transformFKConstraints(pstate, &cxt);
- extras_after = lappend(extras_after, (Node *) fk_trigger);
- }
+ Assert(cxt.columns == NIL);
+ stmt->def = (Node *) nconc(cxt.ckconstraints, cxt.fkconstraints);
+ extras_before = cxt.blist;
+ extras_after = cxt.alist;
break;
+
default:
break;
}
+
+ qry = makeNode(Query);
+ qry->commandType = CMD_UTILITY;
qry->utilityStmt = (Node *) stmt;
+
return qry;
}
/*
* transformFkeyCheckAttrs -
*
- * Try to make sure that the attributes of a referenced table
+ * Make sure that the attributes of a referenced table
* belong to a unique (or primary key) constraint.
- *
*/
static void
transformFkeyCheckAttrs(FkConstraint *fkconstraint, Oid *pktypoid)
* Open the referenced table and get the attributes list
*/
pkrel = heap_openr(fkconstraint->pktable_name, AccessShareLock);
- if (pkrel == NULL)
- elog(ERROR, "referenced table \"%s\" not found",
- fkconstraint->pktable_name);
pkrel_attrs = pkrel->rd_att->attrs;
/*
ObjectIdGetDatum(indexoid),
0, 0, 0);
if (!HeapTupleIsValid(indexTuple))
- elog(ERROR, "transformFkeyGetPrimaryKey: index %u not found",
+ elog(ERROR, "transformFkeyCheckAttrs: index %u not found",
indexoid);
indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
List *attrl;
int attnum=0;
- for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++);
+ for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++)
+ ;
if (i != length(fkconstraint->pk_attrs))
found = false;
else
*
* 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)
* Open the referenced table and get the attributes list
*/
pkrel = heap_openr(fkconstraint->pktable_name, AccessShareLock);
- if (pkrel == NULL)
- elog(ERROR, "referenced table \"%s\" not found",
- fkconstraint->pktable_name);
pkrel_attrs = pkrel->rd_att->attrs;
/*
int pkattno = indexStruct->indkey[i];
Ident *pkattr = makeNode(Ident);
- pkattr->name = DatumGetCString(DirectFunctionCall1(nameout,
- NameGetDatum(&(pkrel_attrs[pkattno - 1]->attname))));
+ pkattr->name = pstrdup(NameStr(pkrel_attrs[pkattno-1]->attname));
pkattr->indirection = NIL;
pkattr->isRel = false;
- pktypoid[attnum++]=pkrel_attrs[pkattno-1]->atttypid;
+ pktypoid[attnum++] = pkrel_attrs[pkattno-1]->atttypid;
fkconstraint->pk_attrs = lappend(fkconstraint->pk_attrs, pkattr);
}
heap_close(pkrel, AccessShareLock);
}
+/*
+ * relationHasPrimaryKey -
+ *
+ * See whether an existing relation has a primary key.
+ */
+static bool
+relationHasPrimaryKey(char *relname)
+{
+ bool result = false;
+ Relation rel;
+ List *indexoidlist,
+ *indexoidscan;
+
+ rel = heap_openr(relname, AccessShareLock);
+
+ /*
+ * 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(rel);
+
+ foreach(indexoidscan, indexoidlist)
+ {
+ Oid indexoid = lfirsti(indexoidscan);
+ HeapTuple indexTuple;
+
+ indexTuple = SearchSysCache(INDEXRELID,
+ ObjectIdGetDatum(indexoid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(indexTuple))
+ elog(ERROR, "relationHasPrimaryKey: index %u not found",
+ indexoid);
+ result = ((Form_pg_index) GETSTRUCT(indexTuple))->indisprimary;
+ ReleaseSysCache(indexTuple);
+ if (result)
+ break;
+ }
+
+ freeList(indexoidlist);
+
+ heap_close(rel, AccessShareLock);
+
+ return result;
+}
+
+/*
+ * 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;
+
+ /* First look for column among the newly-created columns */
+ foreach(cols, cxt->columns)
+ {
+ ColumnDef *col = lfirst(cols);
+
+ if (strcmp(col->colname, colname) == 0)
+ {
+ char *buff = TypeNameToInternalName(col->typename);
+
+ result = typenameTypeId(buff);
+ if (!OidIsValid(result))
+ elog(ERROR, "Unable to lookup type %s",
+ col->typename->name);
+ return result;
+ }
+ }
+ /* Look for column among inherited columns (if CREATE TABLE case) */
+ foreach(inher, cxt->inhRelnames)
+ {
+ Value *inh = lfirst(inher);
+ Relation rel;
+ int count;
+
+ Assert(IsA(inh, String));
+ rel = heap_openr(strVal(inh), AccessShareLock);
+ if (rel->rd_rel->relkind != RELKIND_RELATION)
+ elog(ERROR, "inherited table \"%s\" is not a relation",
+ strVal(inh));
+ for (count = 0; count < rel->rd_att->natts; count++)
+ {
+ char *name = NameStr(rel->rd_att->attrs[count]->attname);
+
+ if (strcmp(name, colname) == 0)
+ {
+ result = rel->rd_att->attrs[count]->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 = SearchSysCache(ATTNAME,
+ ObjectIdGetDatum(cxt->relOid),
+ PointerGetDatum(colname),
+ 0, 0);
+ 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