exec_rt_fetch((relinfo)->ri_RangeTableIndex, estate)->extraUpdatedCols))
/* Local function prototypes */
-static void ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid);
static void SetTriggerFlags(TriggerDesc *trigdesc, Trigger *trigger);
static bool GetTupleForTrigger(EState *estate,
EPQState *epqstate,
* When called on partitioned tables, this function recurses to create the
* trigger on all the partitions, except if isInternal is true, in which
* case caller is expected to execute recursion on its own.
- *
- * Note: can return InvalidObjectAddress if we decided to not create a trigger
- * at all, but a foreign-key constraint. This is a kluge for backwards
- * compatibility.
*/
ObjectAddress
CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
NameListToString(stmt->funcname), "trigger")));
}
- /*
- * If the command is a user-entered CREATE CONSTRAINT TRIGGER command that
- * references one of the built-in RI_FKey trigger functions, assume it is
- * from a dump of a pre-7.3 foreign key constraint, and take steps to
- * convert this legacy representation into a regular foreign key
- * constraint. Ugly, but necessary for loading old dump files.
- */
- if (stmt->isconstraint && !isInternal &&
- list_length(stmt->args) >= 6 &&
- (list_length(stmt->args) % 2) == 0 &&
- RI_FKey_trigger_type(funcoid) != RI_TRIGGER_NONE)
- {
- /* Keep lock on target rel until end of xact */
- table_close(rel, NoLock);
-
- ConvertTriggerToFK(stmt, funcoid);
-
- return InvalidObjectAddress;
- }
-
/*
* If it's a user-entered CREATE CONSTRAINT TRIGGER command, make a
* corresponding pg_constraint entry.
}
-/*
- * Convert legacy (pre-7.3) CREATE CONSTRAINT TRIGGER commands into
- * full-fledged foreign key constraints.
- *
- * The conversion is complex because a pre-7.3 foreign key involved three
- * separate triggers, which were reported separately in dumps. While the
- * single trigger on the referencing table adds no new information, we need
- * to know the trigger functions of both of the triggers on the referenced
- * table to build the constraint declaration. Also, due to lack of proper
- * dependency checking pre-7.3, it is possible that the source database had
- * an incomplete set of triggers resulting in an only partially enforced
- * FK constraint. (This would happen if one of the tables had been dropped
- * and re-created, but only if the DB had been affected by a 7.0 pg_dump bug
- * that caused loss of tgconstrrelid information.) We choose to translate to
- * an FK constraint only when we've seen all three triggers of a set. This is
- * implemented by storing unmatched items in a list in TopMemoryContext.
- * We match triggers together by comparing the trigger arguments (which
- * include constraint name, table and column names, so should be good enough).
- */
-typedef struct
-{
- List *args; /* list of (T_String) Values or NIL */
- Oid funcoids[3]; /* OIDs of trigger functions */
- /* The three function OIDs are stored in the order update, delete, child */
-} OldTriggerInfo;
-
-static void
-ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
-{
- static List *info_list = NIL;
-
- static const char *const funcdescr[3] = {
- gettext_noop("Found referenced table's UPDATE trigger."),
- gettext_noop("Found referenced table's DELETE trigger."),
- gettext_noop("Found referencing table's trigger.")
- };
-
- char *constr_name;
- char *fk_table_name;
- char *pk_table_name;
- char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
- List *fk_attrs = NIL;
- List *pk_attrs = NIL;
- StringInfoData buf;
- int funcnum;
- OldTriggerInfo *info = NULL;
- ListCell *l;
- int i;
-
- /* Parse out the trigger arguments */
- constr_name = strVal(linitial(stmt->args));
- fk_table_name = strVal(lsecond(stmt->args));
- pk_table_name = strVal(lthird(stmt->args));
- i = 0;
- foreach(l, stmt->args)
- {
- Value *arg = (Value *) lfirst(l);
-
- i++;
- if (i < 4) /* skip constraint and table names */
- continue;
- if (i == 4) /* handle match type */
- {
- if (strcmp(strVal(arg), "FULL") == 0)
- fk_matchtype = FKCONSTR_MATCH_FULL;
- else
- fk_matchtype = FKCONSTR_MATCH_SIMPLE;
- continue;
- }
- if (i % 2)
- fk_attrs = lappend(fk_attrs, arg);
- else
- pk_attrs = lappend(pk_attrs, arg);
- }
-
- /* Prepare description of constraint for use in messages */
- initStringInfo(&buf);
- appendStringInfo(&buf, "FOREIGN KEY %s(",
- quote_identifier(fk_table_name));
- i = 0;
- foreach(l, fk_attrs)
- {
- Value *arg = (Value *) lfirst(l);
-
- if (i++ > 0)
- appendStringInfoChar(&buf, ',');
- appendStringInfoString(&buf, quote_identifier(strVal(arg)));
- }
- appendStringInfo(&buf, ") REFERENCES %s(",
- quote_identifier(pk_table_name));
- i = 0;
- foreach(l, pk_attrs)
- {
- Value *arg = (Value *) lfirst(l);
-
- if (i++ > 0)
- appendStringInfoChar(&buf, ',');
- appendStringInfoString(&buf, quote_identifier(strVal(arg)));
- }
- appendStringInfoChar(&buf, ')');
-
- /* Identify class of trigger --- update, delete, or referencing-table */
- switch (funcoid)
- {
- case F_RI_FKEY_CASCADE_UPD:
- case F_RI_FKEY_RESTRICT_UPD:
- case F_RI_FKEY_SETNULL_UPD:
- case F_RI_FKEY_SETDEFAULT_UPD:
- case F_RI_FKEY_NOACTION_UPD:
- funcnum = 0;
- break;
-
- case F_RI_FKEY_CASCADE_DEL:
- case F_RI_FKEY_RESTRICT_DEL:
- case F_RI_FKEY_SETNULL_DEL:
- case F_RI_FKEY_SETDEFAULT_DEL:
- case F_RI_FKEY_NOACTION_DEL:
- funcnum = 1;
- break;
-
- default:
- funcnum = 2;
- break;
- }
-
- /* See if we have a match to this trigger */
- foreach(l, info_list)
- {
- info = (OldTriggerInfo *) lfirst(l);
- if (info->funcoids[funcnum] == InvalidOid &&
- equal(info->args, stmt->args))
- {
- info->funcoids[funcnum] = funcoid;
- break;
- }
- }
-
- if (l == NULL)
- {
- /* First trigger of set, so create a new list entry */
- MemoryContext oldContext;
-
- ereport(NOTICE,
- (errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
- constr_name, buf.data),
- errdetail_internal("%s", _(funcdescr[funcnum]))));
- oldContext = MemoryContextSwitchTo(TopMemoryContext);
- info = (OldTriggerInfo *) palloc0(sizeof(OldTriggerInfo));
- info->args = copyObject(stmt->args);
- info->funcoids[funcnum] = funcoid;
- info_list = lappend(info_list, info);
- MemoryContextSwitchTo(oldContext);
- }
- else if (info->funcoids[0] == InvalidOid ||
- info->funcoids[1] == InvalidOid ||
- info->funcoids[2] == InvalidOid)
- {
- /* Second trigger of set */
- ereport(NOTICE,
- (errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
- constr_name, buf.data),
- errdetail_internal("%s", _(funcdescr[funcnum]))));
- }
- else
- {
- /* OK, we have a set, so make the FK constraint ALTER TABLE cmd */
- AlterTableStmt *atstmt = makeNode(AlterTableStmt);
- AlterTableCmd *atcmd = makeNode(AlterTableCmd);
- Constraint *fkcon = makeNode(Constraint);
- PlannedStmt *wrapper = makeNode(PlannedStmt);
-
- ereport(NOTICE,
- (errmsg("converting trigger group into constraint \"%s\" %s",
- constr_name, buf.data),
- errdetail_internal("%s", _(funcdescr[funcnum]))));
- fkcon->contype = CONSTR_FOREIGN;
- fkcon->location = -1;
- if (funcnum == 2)
- {
- /* This trigger is on the FK table */
- atstmt->relation = stmt->relation;
- if (stmt->constrrel)
- fkcon->pktable = stmt->constrrel;
- else
- {
- /* Work around ancient pg_dump bug that omitted constrrel */
- fkcon->pktable = makeRangeVar(NULL, pk_table_name, -1);
- }
- }
- else
- {
- /* This trigger is on the PK table */
- fkcon->pktable = stmt->relation;
- if (stmt->constrrel)
- atstmt->relation = stmt->constrrel;
- else
- {
- /* Work around ancient pg_dump bug that omitted constrrel */
- atstmt->relation = makeRangeVar(NULL, fk_table_name, -1);
- }
- }
- atstmt->cmds = list_make1(atcmd);
- atstmt->relkind = OBJECT_TABLE;
- atcmd->subtype = AT_AddConstraint;
- atcmd->def = (Node *) fkcon;
- if (strcmp(constr_name, "<unnamed>") == 0)
- fkcon->conname = NULL;
- else
- fkcon->conname = constr_name;
- fkcon->fk_attrs = fk_attrs;
- fkcon->pk_attrs = pk_attrs;
- fkcon->fk_matchtype = fk_matchtype;
- switch (info->funcoids[0])
- {
- case F_RI_FKEY_NOACTION_UPD:
- fkcon->fk_upd_action = FKCONSTR_ACTION_NOACTION;
- break;
- case F_RI_FKEY_CASCADE_UPD:
- fkcon->fk_upd_action = FKCONSTR_ACTION_CASCADE;
- break;
- case F_RI_FKEY_RESTRICT_UPD:
- fkcon->fk_upd_action = FKCONSTR_ACTION_RESTRICT;
- break;
- case F_RI_FKEY_SETNULL_UPD:
- fkcon->fk_upd_action = FKCONSTR_ACTION_SETNULL;
- break;
- case F_RI_FKEY_SETDEFAULT_UPD:
- fkcon->fk_upd_action = FKCONSTR_ACTION_SETDEFAULT;
- break;
- default:
- /* can't get here because of earlier checks */
- elog(ERROR, "confused about RI update function");
- }
- switch (info->funcoids[1])
- {
- case F_RI_FKEY_NOACTION_DEL:
- fkcon->fk_del_action = FKCONSTR_ACTION_NOACTION;
- break;
- case F_RI_FKEY_CASCADE_DEL:
- fkcon->fk_del_action = FKCONSTR_ACTION_CASCADE;
- break;
- case F_RI_FKEY_RESTRICT_DEL:
- fkcon->fk_del_action = FKCONSTR_ACTION_RESTRICT;
- break;
- case F_RI_FKEY_SETNULL_DEL:
- fkcon->fk_del_action = FKCONSTR_ACTION_SETNULL;
- break;
- case F_RI_FKEY_SETDEFAULT_DEL:
- fkcon->fk_del_action = FKCONSTR_ACTION_SETDEFAULT;
- break;
- default:
- /* can't get here because of earlier checks */
- elog(ERROR, "confused about RI delete function");
- }
- fkcon->deferrable = stmt->deferrable;
- fkcon->initdeferred = stmt->initdeferred;
- fkcon->skip_validation = false;
- fkcon->initially_valid = true;
-
- /* finally, wrap it in a dummy PlannedStmt */
- wrapper->commandType = CMD_UTILITY;
- wrapper->canSetTag = false;
- wrapper->utilityStmt = (Node *) atstmt;
- wrapper->stmt_location = -1;
- wrapper->stmt_len = -1;
-
- /* ... and execute it */
- ProcessUtility(wrapper,
- "(generated ALTER TABLE ADD FOREIGN KEY command)",
- PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
- None_Receiver, NULL);
-
- /* Remove the matched item from the list */
- info_list = list_delete_ptr(info_list, info);
- pfree(info);
- /* We leak the copied args ... not worth worrying about */
- }
-}
-
/*
* Guts of trigger deletion.
*/