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);

		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;

		/* ... and execute it */
		ProcessUtility((Node *) atstmt,
					   "(generated ALTER TABLE ADD FOREIGN KEY command)",
					   PROCESS_UTILITY_SUBCOMMAND, 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 */
	}
}