Retiring support for pre-7.3 FK constraint triggers

Started by Daniel Gustafssonalmost 6 years ago13 messages
#1Daniel Gustafsson
daniel@yesql.se
1 attachment(s)

While looking at the tg_updatedcols patch I happened to notice that we still
support pre-7.3 constraint triggers by converting them on the fly. AFAICT this
requires a pre-7.3 dump to hit.

This was added in late 2007 in a2899ebdc28080eab0f4bb0b8a5f30aa7bb31a89 due to
a report from the field, but I doubt this codepath is excercised much today (if
at all).

Having code which is untested and not excercised by developers (or users, if my
assumption holds), yet being reachable by SQL, runs the risk of introducing
subtle bugs. Is there a usecase for keeping it, or can/should it be removed in
14? That would still leave a lot of supported versions to upgrade to in case
there are users to need this. Unless there are immediate -1's, I'll park this
in a CF for v14.

cheers ./daniel

Attachments:

remove_pre73_fks.patchapplication/octet-stream; name=remove_pre73_fks.patch; x-unix-mode=0644Download
From dfb8b37048b67392e86f20399d54859c31fcd9b6 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Thu, 5 Mar 2020 14:34:53 +0100
Subject: [PATCH] Remove support for pre-7.3 constraint trigger conversion

To handle pre-7.3 constraint triggers, commit a2899ebdc28080eab0f4
added support for automatically converting them. This is unlikely
to be used today, so retire the functionality.
---
 src/backend/commands/trigger.c | 300 ---------------------------------
 1 file changed, 300 deletions(-)

diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 6e8b7223fe..4e79e74127 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -80,7 +80,6 @@ static int	MyTriggerDepth = 0;
 			   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,
@@ -719,26 +718,6 @@ 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.
@@ -1206,285 +1185,6 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 }
 
 
-/*
- * 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.
  */
-- 
2.21.1 (Apple Git-122.3)

#2Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Daniel Gustafsson (#1)
Re: Retiring support for pre-7.3 FK constraint triggers

On 2020-Mar-05, Daniel Gustafsson wrote:

While looking at the tg_updatedcols patch I happened to notice that we still
support pre-7.3 constraint triggers by converting them on the fly. AFAICT this
requires a pre-7.3 dump to hit.

This was added in late 2007 in a2899ebdc28080eab0f4bb0b8a5f30aa7bb31a89 due to
a report from the field, but I doubt this codepath is excercised much today (if
at all).

pg_dump's support for server versions prior to 8.0 was removed by commit
64f3524e2c8d (Oct 2016) so it seems fair to remove this too. If people
need to upgrade from anything older than 7.3, they can do an intermediate jump.

Having code which is untested and not excercised by developers (or users, if my
assumption holds), yet being reachable by SQL, runs the risk of introducing
subtle bugs. Is there a usecase for keeping it, or can/should it be removed in
14? That would still leave a lot of supported versions to upgrade to in case
there are users to need this. Unless there are immediate -1's, I'll park this
in a CF for v14.

I know it's a late in the cycle for patches in commitfest, but why not
consider this for pg13 nonetheless? It seems simple enough. Also, per
https://coverage.postgresql.org/src/backend/commands/trigger.c.gcov.html
this is the only large chunk of uncovered code in commands/trigger.c.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#3Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#2)
Re: Retiring support for pre-7.3 FK constraint triggers

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

On 2020-Mar-05, Daniel Gustafsson wrote:

While looking at the tg_updatedcols patch I happened to notice that we still
support pre-7.3 constraint triggers by converting them on the fly. AFAICT this
requires a pre-7.3 dump to hit.

I know it's a late in the cycle for patches in commitfest, but why not
consider this for pg13 nonetheless? It seems simple enough. Also, per
https://coverage.postgresql.org/src/backend/commands/trigger.c.gcov.html
this is the only large chunk of uncovered code in commands/trigger.c.

+1 --- I think this fits in well with my nearby proposal to remove
OPAQUE, which is also only relevant for pre-7.3 dumps. Let's just
nuke that stuff.

regards, tom lane

#4David Steele
david@pgmasters.net
In reply to: Tom Lane (#3)
Re: Retiring support for pre-7.3 FK constraint triggers

On 3/5/20 9:42 AM, Tom Lane wrote:

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

On 2020-Mar-05, Daniel Gustafsson wrote:

While looking at the tg_updatedcols patch I happened to notice that we still
support pre-7.3 constraint triggers by converting them on the fly. AFAICT this
requires a pre-7.3 dump to hit.

I know it's a late in the cycle for patches in commitfest, but why not
consider this for pg13 nonetheless? It seems simple enough. Also, per
https://coverage.postgresql.org/src/backend/commands/trigger.c.gcov.html
this is the only large chunk of uncovered code in commands/trigger.c.

+1 --- I think this fits in well with my nearby proposal to remove
OPAQUE, which is also only relevant for pre-7.3 dumps. Let's just
nuke that stuff.

+1. CF entry added:

https://commitfest.postgresql.org/27/2506

Regards,
--
-David
david@pgmasters.net

#5Daniel Gustafsson
daniel@yesql.se
In reply to: Tom Lane (#3)
Re: Retiring support for pre-7.3 FK constraint triggers

On 5 Mar 2020, at 15:42, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

On 2020-Mar-05, Daniel Gustafsson wrote:
While looking at the tg_updatedcols patch I happened to notice that we still
support pre-7.3 constraint triggers by converting them on the fly. AFAICT this
requires a pre-7.3 dump to hit.

I know it's a late in the cycle for patches in commitfest, but why not
consider this for pg13 nonetheless? It seems simple enough. Also, per
https://coverage.postgresql.org/src/backend/commands/trigger.c.gcov.html
this is the only large chunk of uncovered code in commands/trigger.c.

+1 --- I think this fits in well with my nearby proposal to remove
OPAQUE, which is also only relevant for pre-7.3 dumps. Let's just
nuke that stuff.

Sounds good. I was opting for 14 to not violate the no new patches in an ongoing CF policy, but if there is concensus from committers then +1 from me.

cheers ./daniel

#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Daniel Gustafsson (#5)
Re: Retiring support for pre-7.3 FK constraint triggers

Daniel Gustafsson <daniel@yesql.se> writes:

On 5 Mar 2020, at 15:42, Tom Lane <tgl@sss.pgh.pa.us> wrote:

+1 --- I think this fits in well with my nearby proposal to remove
OPAQUE, which is also only relevant for pre-7.3 dumps. Let's just
nuke that stuff.

Sounds good. I was opting for 14 to not violate the no new patches in an ongoing CF policy, but if there is concensus from committers then +1 from me.

As long as we're thinking of zapping code that is long past its sell-by
date, I propose getting rid of this stanza in indexcmds.c, which
basically causes CREATE INDEX to ignore certain opclass specifications:

/*
* Release 7.0 removed network_ops, timespan_ops, and datetime_ops, so we
* ignore those opclass names so the default *_ops is used. This can be
* removed in some later release. bjm 2000/02/07
*
* Release 7.1 removes lztext_ops, so suppress that too for a while. tgl
* 2000/07/30
*
* Release 7.2 renames timestamp_ops to timestamptz_ops, so suppress that
* too for awhile. I'm starting to think we need a better approach. tgl
* 2000/10/01
*
* Release 8.0 removes bigbox_ops (which was dead code for a long while
* anyway). tgl 2003/11/11
*/
if (list_length(opclass) == 1)
{
char *claname = strVal(linitial(opclass));

if (strcmp(claname, "network_ops") == 0 ||
strcmp(claname, "timespan_ops") == 0 ||
strcmp(claname, "datetime_ops") == 0 ||
strcmp(claname, "lztext_ops") == 0 ||
strcmp(claname, "timestamp_ops") == 0 ||
strcmp(claname, "bigbox_ops") == 0)
opclass = NIL;
}

At some point, the risk that this causes problems for developers of
new opclasses must outweigh the value of silently upgrading old dumps.
I think if we're zapping other pre-7.3-compatibility hacks for that
purpose, this one could go too.

Elsewhere in indexcmds.c, there's this:

/*
* Hack to provide more-or-less-transparent updating of old RTREE
* indexes to GiST: if RTREE is requested and not found, use GIST.
*/
if (strcmp(accessMethodName, "rtree") == 0)
{
ereport(NOTICE,
(errmsg("substituting access method \"gist\" for obsolete method \"rtree\"")));
accessMethodName = "gist";
tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
}

which dates to 8.2 (2a8d3d83e of 2005-11-07). This is less bad than the
other thing, since it won't affect the behavior of any command that
wouldn't otherwise just fail; but maybe its time has passed as well?
Although Alvaro's point comparing these behaviors to pg_dump's support
cutoff of 8.0 suggests that maybe we should leave this one for now.

regards, tom lane

#7Vik Fearing
vik@postgresfriends.org
In reply to: Tom Lane (#6)
Re: Retiring support for pre-7.3 FK constraint triggers

On 05/03/2020 16:33, Tom Lane wrote:

Elsewhere in indexcmds.c, there's this:

/*
* Hack to provide more-or-less-transparent updating of old RTREE
* indexes to GiST: if RTREE is requested and not found, use GIST.
*/
if (strcmp(accessMethodName, "rtree") == 0)
{
ereport(NOTICE,
(errmsg("substituting access method \"gist\" for obsolete method \"rtree\"")));
accessMethodName = "gist";
tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
}

Aww, this one is in my list of gotcha trivia questions.

That's not a reason not to remove it, of course.
--
Vik Fearing

#8Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Tom Lane (#6)
Re: Retiring support for pre-7.3 FK constraint triggers

On 2020-Mar-05, Tom Lane wrote:

As long as we're thinking of zapping code that is long past its sell-by
date, I propose getting rid of this stanza in indexcmds.c, which
basically causes CREATE INDEX to ignore certain opclass specifications:

I agree, this should be fine to remove.

Elsewhere in indexcmds.c, there's this:

/*
* Hack to provide more-or-less-transparent updating of old RTREE
* indexes to GiST: if RTREE is requested and not found, use GIST.
*/
if (strcmp(accessMethodName, "rtree") == 0)
{
ereport(NOTICE,
(errmsg("substituting access method \"gist\" for obsolete method \"rtree\"")));
accessMethodName = "gist";
tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
}

which dates to 8.2 (2a8d3d83e of 2005-11-07). This is less bad than the
other thing, since it won't affect the behavior of any command that
wouldn't otherwise just fail; but maybe its time has passed as well?
Although Alvaro's point comparing these behaviors to pg_dump's support
cutoff of 8.0 suggests that maybe we should leave this one for now.

Yeah, dunno, 'rtree' is even immortalized in tests; commit f2e403803fe6
as recently as March 2019 was seen modifying that.

(Another curious factoid is that SQLite supports something that vaguely
looks rtreeish https://sqlite.org/rtree.html -- However, because it
doesn't use the same syntax Postgres uses, it's not a point against
removing our hack.)

I guess we can wait a couple years more on that one, since it's not
damaging anything anyway.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#9Daniel Gustafsson
daniel@yesql.se
In reply to: Alvaro Herrera (#8)
2 attachment(s)
Re: Retiring support for pre-7.3 FK constraint triggers

On 5 Mar 2020, at 19:36, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:

On 2020-Mar-05, Tom Lane wrote:

As long as we're thinking of zapping code that is long past its sell-by
date, I propose getting rid of this stanza in indexcmds.c, which
basically causes CREATE INDEX to ignore certain opclass specifications:

I agree, this should be fine to remove.

The attached patchset removes this stanza as well.

When poking around here I realized that defGetStringList was also left unused.
It was added with the logical decoding code but the single callsite has since
been removed. As it's published in a header we might not want to remove it,
but I figured I'd bring it up as were talking about removing code.

cheers ./daniel

Attachments:

0002-Remove-handling-of-removed-_ops-classes.patchapplication/octet-stream; name=0002-Remove-handling-of-removed-_ops-classes.patch; x-unix-mode=0644Download
From 7c20cf8a51d8111730cbf361273f828361fbab75 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Thu, 5 Mar 2020 21:29:02 +0100
Subject: [PATCH 2/3] Remove handling of removed _ops classes

opclasses removed since long were silently handled by dropping them
for the default, but it's now time to remove this workaround ans the
opclasses in question were removed a long time ago.
---
 src/backend/commands/indexcmds.c | 28 ----------------------------
 1 file changed, 28 deletions(-)

diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index ec20ba38d1..a28ea6fe89 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1850,34 +1850,6 @@ ResolveOpClass(List *opclass, Oid attrType,
 	Oid			opClassId,
 				opInputType;
 
-	/*
-	 * Release 7.0 removed network_ops, timespan_ops, and datetime_ops, so we
-	 * ignore those opclass names so the default *_ops is used.  This can be
-	 * removed in some later release.  bjm 2000/02/07
-	 *
-	 * Release 7.1 removes lztext_ops, so suppress that too for a while.  tgl
-	 * 2000/07/30
-	 *
-	 * Release 7.2 renames timestamp_ops to timestamptz_ops, so suppress that
-	 * too for awhile.  I'm starting to think we need a better approach. tgl
-	 * 2000/10/01
-	 *
-	 * Release 8.0 removes bigbox_ops (which was dead code for a long while
-	 * anyway).  tgl 2003/11/11
-	 */
-	if (list_length(opclass) == 1)
-	{
-		char	   *claname = strVal(linitial(opclass));
-
-		if (strcmp(claname, "network_ops") == 0 ||
-			strcmp(claname, "timespan_ops") == 0 ||
-			strcmp(claname, "datetime_ops") == 0 ||
-			strcmp(claname, "lztext_ops") == 0 ||
-			strcmp(claname, "timestamp_ops") == 0 ||
-			strcmp(claname, "bigbox_ops") == 0)
-			opclass = NIL;
-	}
-
 	if (opclass == NIL)
 	{
 		/* no operator class specified, so find the default */
-- 
2.21.1 (Apple Git-122.3)

0001-Remove-support-for-pre-7.3-constraint-trigger-conver.patchapplication/octet-stream; name=0001-Remove-support-for-pre-7.3-constraint-trigger-conver.patch; x-unix-mode=0644Download
From dfb8b37048b67392e86f20399d54859c31fcd9b6 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Thu, 5 Mar 2020 14:34:53 +0100
Subject: [PATCH 1/3] Remove support for pre-7.3 constraint trigger conversion

To handle pre-7.3 constraint triggers, commit a2899ebdc28080eab0f4
added support for automatically converting them. This is unlikely
to be used today, so retire the functionality.
---
 src/backend/commands/trigger.c | 300 ---------------------------------
 1 file changed, 300 deletions(-)

diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 6e8b7223fe..4e79e74127 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -80,7 +80,6 @@ static int	MyTriggerDepth = 0;
 			   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,
@@ -719,26 +718,6 @@ 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.
@@ -1206,285 +1185,6 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 }
 
 
-/*
- * 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.
  */
-- 
2.21.1 (Apple Git-122.3)

#10Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#8)
Re: Retiring support for pre-7.3 FK constraint triggers

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

On 2020-Mar-05, Tom Lane wrote:

As long as we're thinking of zapping code that is long past its sell-by
date, I propose getting rid of this stanza in indexcmds.c, which
basically causes CREATE INDEX to ignore certain opclass specifications:

I agree, this should be fine to remove.

Done.

which dates to 8.2 (2a8d3d83e of 2005-11-07). This is less bad than the
other thing, since it won't affect the behavior of any command that
wouldn't otherwise just fail; but maybe its time has passed as well?
Although Alvaro's point comparing these behaviors to pg_dump's support
cutoff of 8.0 suggests that maybe we should leave this one for now.

Yeah, dunno, 'rtree' is even immortalized in tests; commit f2e403803fe6
as recently as March 2019 was seen modifying that.

Hah, I didn't realize we actually had code coverage for that!

I guess we can wait a couple years more on that one, since it's not
damaging anything anyway.

Agreed, I left it be.

regards, tom lane

#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Daniel Gustafsson (#1)
Re: Retiring support for pre-7.3 FK constraint triggers

Daniel Gustafsson <daniel@yesql.se> writes:

Having code which is untested and not excercised by developers (or users, if my
assumption holds), yet being reachable by SQL, runs the risk of introducing
subtle bugs. Is there a usecase for keeping it, or can/should it be removed in
14? That would still leave a lot of supported versions to upgrade to in case
there are users to need this.

Pushed. Looking at the original commit, I noticed one now-obsolete
comment that should also be removed, so I did that.

regards, tom lane

#12Tom Lane
tgl@sss.pgh.pa.us
In reply to: Daniel Gustafsson (#9)
Re: Retiring support for pre-7.3 FK constraint triggers

Daniel Gustafsson <daniel@yesql.se> writes:

When poking around here I realized that defGetStringList was also left unused.
It was added with the logical decoding code but the single callsite has since
been removed. As it's published in a header we might not want to remove it,
but I figured I'd bring it up as were talking about removing code.

Hm. Kind of inclined to leave it, since somebody will probably need it
again someday.

regards, tom lane

#13Daniel Gustafsson
daniel@yesql.se
In reply to: Tom Lane (#11)
Re: Retiring support for pre-7.3 FK constraint triggers

On 5 Mar 2020, at 21:52, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Daniel Gustafsson <daniel@yesql.se> writes:

Having code which is untested and not excercised by developers (or users, if my
assumption holds), yet being reachable by SQL, runs the risk of introducing
subtle bugs. Is there a usecase for keeping it, or can/should it be removed in
14? That would still leave a lot of supported versions to upgrade to in case
there are users to need this.

Pushed. Looking at the original commit, I noticed one now-obsolete
comment that should also be removed, so I did that.

Thanks, I was looking around but totally missed that comment.

cheers ./daniel