diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index af4d062..6dd0700 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -5146,11 +5146,11 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
      </row>
 
      <row>
-      <entry><structfield>protransform</structfield></entry>
+      <entry><structfield>prosupport</structfield></entry>
       <entry><type>regproc</type></entry>
       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
-      <entry>Calls to this function can be simplified by this other function
-       (see <xref linkend="xfunc-transform-functions"/>)</entry>
+      <entry>Optional planner support function for this function
+       (see <xref linkend="xfunc-optimization"/>)</entry>
      </row>
 
      <row>
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index e18272c..d70aa6e 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -3241,40 +3241,6 @@ CREATE FUNCTION make_array(anyelement) RETURNS anyarray
     </para>
    </sect2>
 
-   <sect2 id="xfunc-transform-functions">
-    <title>Transform Functions</title>
-
-    <para>
-     Some function calls can be simplified during planning based on
-     properties specific to the function.  For example,
-     <literal>int4mul(n, 1)</literal> could be simplified to just <literal>n</literal>.
-     To define such function-specific optimizations, write a
-     <firstterm>transform function</firstterm> and place its OID in the
-     <structfield>protransform</structfield> field of the primary function's
-     <structname>pg_proc</structname> entry.  The transform function must have the SQL
-     signature <literal>protransform(internal) RETURNS internal</literal>.  The
-     argument, actually <type>FuncExpr *</type>, is a dummy node representing a
-     call to the primary function.  If the transform function's study of the
-     expression tree proves that a simplified expression tree can substitute
-     for all possible concrete calls represented thereby, build and return
-     that simplified expression.  Otherwise, return a <literal>NULL</literal>
-     pointer (<emphasis>not</emphasis> a SQL null).
-    </para>
-
-    <para>
-     We make no guarantee that <productname>PostgreSQL</productname> will never call the
-     primary function in cases that the transform function could simplify.
-     Ensure rigorous equivalence between the simplified expression and an
-     actual call to the primary function.
-    </para>
-
-    <para>
-     Currently, this facility is not exposed to users at the SQL level
-     because of security concerns, so it is only practical to use for
-     optimizing built-in functions.
-    </para>
-   </sect2>
-
    <sect2>
     <title>Shared Memory and LWLocks</title>
 
@@ -3388,3 +3354,89 @@ if (!ptr)
    </sect2>
 
   </sect1>
+
+  <sect1 id="xfunc-optimization">
+   <title>Function Optimization Information</title>
+
+  <indexterm zone="xfunc-optimization">
+   <primary>optimization information</primary>
+   <secondary>for functions</secondary>
+  </indexterm>
+
+   <para>
+    By default, a function is just a <quote>black box</quote> that the
+    database system knows very little about the behavior of.  However,
+    that means that queries using the function may be executed much less
+    efficiently than they could be.  It is possible to supply additional
+    knowledge that helps the planner optimize function calls.
+   </para>
+
+   <para>
+    Some basic facts can be supplied by declarative annotations provided in
+    the <xref linkend="sql-createfunction"/> command.  Most important of
+    these is the function's <link linkend="xfunc-volatility">volatility
+    category</link> (<literal>IMMUTABLE</literal>, <literal>STABLE</literal>,
+    or <literal>VOLATILE</literal>); one should always be careful to
+    specify this correctly when defining a function.
+    The parallel safety property (<literal>PARALLEL
+    UNSAFE</literal>, <literal>PARALLEL RESTRICTED</literal>, or
+    <literal>PARALLEL SAFE</literal>) must also be specified if you hope
+    to use the function in parallelized queries.
+    It can also be useful to specify the function's estimated execution
+    cost, and/or the number of rows a set-returning function is estimated
+    to return.  However, the declarative way of specifying those two
+    facts only allows specifying a constant value, which is often
+    inadequate.
+   </para>
+
+   <para>
+    It is also possible to attach a <firstterm>planner support
+    function</firstterm> to a SQL-callable function (called
+    its <firstterm>target function</firstterm>), and thereby provide
+    knowledge about the target function that is too complex to be
+    represented declaratively.  Planner support functions have to be
+    written in C (although their target functions might not be), so this is
+    an advanced feature that relatively few people will use.
+   </para>
+
+   <para>
+    A planner support function must have the SQL signature
+<programlisting>
+supportfn(internal) returns internal
+</programlisting>
+    It is attached to its target function by specifying
+    the <literal>SUPPORT</literal> clause when creating the target function.
+   </para>
+
+   <para>
+    The details of the API for planner support functions can be found in
+    file <filename>src/include/nodes/supportnodes.h</filename> in the
+    <productname>PostgreSQL</productname> source code.  Here we provide
+    just an overview of what planner support functions can do.
+    The set of possible requests to a support function is extensible,
+    so more things might be possible in future versions.
+   </para>
+
+   <para>
+    Some function calls can be simplified during planning based on
+    properties specific to the function.  For example,
+    <literal>int4mul(n, 1)</literal> could be simplified to
+    just <literal>n</literal>.  This type of transformation can be
+    performed by a planner support function, by having it implement
+    the <literal>SupportRequestSimplify</literal> request type.
+    The support function will be called for each instance of its target
+    function found in a query parse tree.  If it finds that the particular
+    call can be simplified into some other form, it can build and return a
+    parse tree representing that expression.  This will automatically work
+    for operators based on the function, too &mdash; in the example just
+    given, <literal>n * 1</literal> would also be simplified to
+    <literal>n</literal>.
+    (But note that this is just an example; this particular
+    optimization is not actually performed by
+    standard <productname>PostgreSQL</productname>.)
+    We make no guarantee that <productname>PostgreSQL</productname> will
+    never call the target function in cases that the support function could
+    simplify.  Ensure rigorous equivalence between the simplified
+    expression and an actual execution of the target function.
+   </para>
+  </sect1>
diff --git a/doc/src/sgml/xoper.sgml b/doc/src/sgml/xoper.sgml
index 2f5560a..260e43c 100644
--- a/doc/src/sgml/xoper.sgml
+++ b/doc/src/sgml/xoper.sgml
@@ -78,6 +78,11 @@ SELECT (a + b) AS c FROM test_complex;
   <sect1 id="xoper-optimization">
    <title>Operator Optimization Information</title>
 
+  <indexterm zone="xoper-optimization">
+   <primary>optimization information</primary>
+   <secondary>for operators</secondary>
+  </indexterm>
+
    <para>
     A <productname>PostgreSQL</productname> operator definition can include
     several optional clauses that tell the system useful things about how
@@ -97,6 +102,13 @@ SELECT (a + b) AS c FROM test_complex;
     the ones that release &version; understands.
    </para>
 
+   <para>
+    It is also possible to attach a planner support function to the function
+    that underlies an operator, providing another way of telling the system
+    about the behavior of the operator.
+    See <xref linkend="xfunc-optimization"/> for more information.
+   </para>
+
    <sect2>
     <title><literal>COMMUTATOR</literal></title>
 
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index db78061..3a86f1e 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -319,7 +319,7 @@ ProcedureCreate(const char *procedureName,
 	values[Anum_pg_proc_procost - 1] = Float4GetDatum(procost);
 	values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows);
 	values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType);
-	values[Anum_pg_proc_protransform - 1] = ObjectIdGetDatum(InvalidOid);
+	values[Anum_pg_proc_prosupport - 1] = ObjectIdGetDatum(InvalidOid);
 	values[Anum_pg_proc_prokind - 1] = CharGetDatum(prokind);
 	values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);
 	values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 86c346b..1f60be2 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -32,6 +32,7 @@
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "nodes/supportnodes.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
 #include "optimizer/optimizer.h"
@@ -4046,13 +4047,16 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
 								args, funcvariadic,
 								func_tuple, context);
 
-	if (!newexpr && allow_non_const && OidIsValid(func_form->protransform))
+	if (!newexpr && allow_non_const && OidIsValid(func_form->prosupport))
 	{
 		/*
-		 * Build a dummy FuncExpr node containing the simplified arg list.  We
-		 * use this approach to present a uniform interface to the transform
-		 * function regardless of how the function is actually being invoked.
+		 * Build a SupportRequestSimplify node to pass to the support
+		 * function, pointing to a dummy FuncExpr node containing the
+		 * simplified arg list.  We use this approach to present a uniform
+		 * interface to the support function regardless of how the target
+		 * function is actually being invoked.
 		 */
+		SupportRequestSimplify req;
 		FuncExpr	fexpr;
 
 		fexpr.xpr.type = T_FuncExpr;
@@ -4066,9 +4070,16 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
 		fexpr.args = args;
 		fexpr.location = -1;
 
+		req.type = T_SupportRequestSimplify;
+		req.root = context->root;
+		req.fcall = &fexpr;
+
 		newexpr = (Expr *)
-			DatumGetPointer(OidFunctionCall1(func_form->protransform,
-											 PointerGetDatum(&fexpr)));
+			DatumGetPointer(OidFunctionCall1(func_form->prosupport,
+											 PointerGetDatum(&req)));
+
+		/* catch a possible API misunderstanding */
+		Assert(newexpr != (Expr *) &fexpr);
 	}
 
 	if (!newexpr && allow_non_const)
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 3810e4a..cf5a1c6 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -24,6 +24,7 @@
 #include "access/xact.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
+#include "nodes/supportnodes.h"
 #include "parser/scansup.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
@@ -1341,15 +1342,25 @@ make_time(PG_FUNCTION_ARGS)
 }
 
 
-/* time_transform()
- * Flatten calls to time_scale() and timetz_scale() that solely represent
- * increases in allowed precision.
+/* time_support()
+ *
+ * Planner support function for the time_scale() and timetz_scale()
+ * length coercion functions (we need not distinguish them here).
  */
 Datum
-time_transform(PG_FUNCTION_ARGS)
+time_support(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_POINTER(TemporalTransform(MAX_TIME_PRECISION,
-										(Node *) PG_GETARG_POINTER(0)));
+	Node	   *rawreq = (Node *) PG_GETARG_POINTER(0);
+	Node	   *ret = NULL;
+
+	if (IsA(rawreq, SupportRequestSimplify))
+	{
+		SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
+
+		ret = TemporalSimplify(MAX_TIME_PRECISION, (Node *) req->fcall);
+	}
+
+	PG_RETURN_POINTER(ret);
 }
 
 /* time_scale()
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 61dbd05..0068e71 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -4462,16 +4462,23 @@ CheckDateTokenTables(void)
 }
 
 /*
- * Common code for temporal protransform functions.  Types time, timetz,
- * timestamp and timestamptz each have a range of allowed precisions.  An
- * unspecified precision is rigorously equivalent to the highest specifiable
- * precision.
+ * Common code for temporal prosupport functions: simplify, if possible,
+ * a call to a temporal type's length-coercion function.
+ *
+ * Types time, timetz, timestamp and timestamptz each have a range of allowed
+ * precisions.  An unspecified precision is rigorously equivalent to the
+ * highest specifiable precision.  We can replace the function call with a
+ * no-op RelabelType if it is coercing to the same or higher precision as the
+ * input is known to have.
+ *
+ * The input Node is always a FuncExpr, but to reduce the #include footprint
+ * of datetime.h, we declare it as Node *.
  *
  * Note: timestamp_scale throws an error when the typmod is out of range, but
  * we can't get there from a cast: our typmodin will have caught it already.
  */
 Node *
-TemporalTransform(int32 max_precis, Node *node)
+TemporalSimplify(int32 max_precis, Node *node)
 {
 	FuncExpr   *expr = castNode(FuncExpr, node);
 	Node	   *ret = NULL;
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 45cd1a0..1c9deeb 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -34,6 +34,7 @@
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
+#include "nodes/supportnodes.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/float.h"
@@ -890,45 +891,53 @@ numeric_send(PG_FUNCTION_ARGS)
 
 
 /*
- * numeric_transform() -
+ * numeric_support()
  *
- * Flatten calls to numeric's length coercion function that solely represent
- * increases in allowable precision.  Scale changes mutate every datum, so
- * they are unoptimizable.  Some values, e.g. 1E-1001, can only fit into an
- * unconstrained numeric, so a change from an unconstrained numeric to any
- * constrained numeric is also unoptimizable.
+ * Planner support function for the numeric() length coercion function.
+ *
+ * Flatten calls that solely represent increases in allowable precision.
+ * Scale changes mutate every datum, so they are unoptimizable.  Some values,
+ * e.g. 1E-1001, can only fit into an unconstrained numeric, so a change from
+ * an unconstrained numeric to any constrained numeric is also unoptimizable.
  */
 Datum
-numeric_transform(PG_FUNCTION_ARGS)
+numeric_support(PG_FUNCTION_ARGS)
 {
-	FuncExpr   *expr = castNode(FuncExpr, PG_GETARG_POINTER(0));
+	Node	   *rawreq = (Node *) PG_GETARG_POINTER(0);
 	Node	   *ret = NULL;
-	Node	   *typmod;
 
-	Assert(list_length(expr->args) >= 2);
+	if (IsA(rawreq, SupportRequestSimplify))
+	{
+		SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
+		FuncExpr   *expr = req->fcall;
+		Node	   *typmod;
 
-	typmod = (Node *) lsecond(expr->args);
+		Assert(list_length(expr->args) >= 2);
 
-	if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull)
-	{
-		Node	   *source = (Node *) linitial(expr->args);
-		int32		old_typmod = exprTypmod(source);
-		int32		new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
-		int32		old_scale = (old_typmod - VARHDRSZ) & 0xffff;
-		int32		new_scale = (new_typmod - VARHDRSZ) & 0xffff;
-		int32		old_precision = (old_typmod - VARHDRSZ) >> 16 & 0xffff;
-		int32		new_precision = (new_typmod - VARHDRSZ) >> 16 & 0xffff;
+		typmod = (Node *) lsecond(expr->args);
 
-		/*
-		 * If new_typmod < VARHDRSZ, the destination is unconstrained; that's
-		 * always OK.  If old_typmod >= VARHDRSZ, the source is constrained,
-		 * and we're OK if the scale is unchanged and the precision is not
-		 * decreasing.  See further notes in function header comment.
-		 */
-		if (new_typmod < (int32) VARHDRSZ ||
-			(old_typmod >= (int32) VARHDRSZ &&
-			 new_scale == old_scale && new_precision >= old_precision))
-			ret = relabel_to_typmod(source, new_typmod);
+		if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull)
+		{
+			Node	   *source = (Node *) linitial(expr->args);
+			int32		old_typmod = exprTypmod(source);
+			int32		new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
+			int32		old_scale = (old_typmod - VARHDRSZ) & 0xffff;
+			int32		new_scale = (new_typmod - VARHDRSZ) & 0xffff;
+			int32		old_precision = (old_typmod - VARHDRSZ) >> 16 & 0xffff;
+			int32		new_precision = (new_typmod - VARHDRSZ) >> 16 & 0xffff;
+
+			/*
+			 * If new_typmod < VARHDRSZ, the destination is unconstrained;
+			 * that's always OK.  If old_typmod >= VARHDRSZ, the source is
+			 * constrained, and we're OK if the scale is unchanged and the
+			 * precision is not decreasing.  See further notes in function
+			 * header comment.
+			 */
+			if (new_typmod < (int32) VARHDRSZ ||
+				(old_typmod >= (int32) VARHDRSZ &&
+				 new_scale == old_scale && new_precision >= old_precision))
+				ret = relabel_to_typmod(source, new_typmod);
+		}
 	}
 
 	PG_RETURN_POINTER(ret);
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 7befb6a..e0ef2f7 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -29,6 +29,7 @@
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "nodes/supportnodes.h"
 #include "parser/scansup.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
@@ -297,15 +298,26 @@ timestamptypmodout(PG_FUNCTION_ARGS)
 }
 
 
-/* timestamp_transform()
- * Flatten calls to timestamp_scale() and timestamptz_scale() that solely
- * represent increases in allowed precision.
+/*
+ * timestamp_support()
+ *
+ * Planner support function for the timestamp_scale() and timestamptz_scale()
+ * length coercion functions (we need not distinguish them here).
  */
 Datum
-timestamp_transform(PG_FUNCTION_ARGS)
+timestamp_support(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_POINTER(TemporalTransform(MAX_TIMESTAMP_PRECISION,
-										(Node *) PG_GETARG_POINTER(0)));
+	Node	   *rawreq = (Node *) PG_GETARG_POINTER(0);
+	Node	   *ret = NULL;
+
+	if (IsA(rawreq, SupportRequestSimplify))
+	{
+		SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
+
+		ret = TemporalSimplify(MAX_TIMESTAMP_PRECISION, (Node *) req->fcall);
+	}
+
+	PG_RETURN_POINTER(ret);
 }
 
 /* timestamp_scale()
@@ -1235,59 +1247,69 @@ intervaltypmodleastfield(int32 typmod)
 }
 
 
-/* interval_transform()
+/*
+ * interval_support()
+ *
+ * Planner support function for interval_scale().
+ *
  * Flatten superfluous calls to interval_scale().  The interval typmod is
  * complex to permit accepting and regurgitating all SQL standard variations.
  * For truncation purposes, it boils down to a single, simple granularity.
  */
 Datum
-interval_transform(PG_FUNCTION_ARGS)
+interval_support(PG_FUNCTION_ARGS)
 {
-	FuncExpr   *expr = castNode(FuncExpr, PG_GETARG_POINTER(0));
+	Node	   *rawreq = (Node *) PG_GETARG_POINTER(0);
 	Node	   *ret = NULL;
-	Node	   *typmod;
 
-	Assert(list_length(expr->args) >= 2);
+	if (IsA(rawreq, SupportRequestSimplify))
+	{
+		SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
+		FuncExpr   *expr = req->fcall;
+		Node	   *typmod;
 
-	typmod = (Node *) lsecond(expr->args);
+		Assert(list_length(expr->args) >= 2);
 
-	if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull)
-	{
-		Node	   *source = (Node *) linitial(expr->args);
-		int32		new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
-		bool		noop;
+		typmod = (Node *) lsecond(expr->args);
 
-		if (new_typmod < 0)
-			noop = true;
-		else
+		if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull)
 		{
-			int32		old_typmod = exprTypmod(source);
-			int			old_least_field;
-			int			new_least_field;
-			int			old_precis;
-			int			new_precis;
-
-			old_least_field = intervaltypmodleastfield(old_typmod);
-			new_least_field = intervaltypmodleastfield(new_typmod);
-			if (old_typmod < 0)
-				old_precis = INTERVAL_FULL_PRECISION;
+			Node	   *source = (Node *) linitial(expr->args);
+			int32		new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
+			bool		noop;
+
+			if (new_typmod < 0)
+				noop = true;
 			else
-				old_precis = INTERVAL_PRECISION(old_typmod);
-			new_precis = INTERVAL_PRECISION(new_typmod);
-
-			/*
-			 * Cast is a no-op if least field stays the same or decreases
-			 * while precision stays the same or increases.  But precision,
-			 * which is to say, sub-second precision, only affects ranges that
-			 * include SECOND.
-			 */
-			noop = (new_least_field <= old_least_field) &&
-				(old_least_field > 0 /* SECOND */ ||
-				 new_precis >= MAX_INTERVAL_PRECISION ||
-				 new_precis >= old_precis);
+			{
+				int32		old_typmod = exprTypmod(source);
+				int			old_least_field;
+				int			new_least_field;
+				int			old_precis;
+				int			new_precis;
+
+				old_least_field = intervaltypmodleastfield(old_typmod);
+				new_least_field = intervaltypmodleastfield(new_typmod);
+				if (old_typmod < 0)
+					old_precis = INTERVAL_FULL_PRECISION;
+				else
+					old_precis = INTERVAL_PRECISION(old_typmod);
+				new_precis = INTERVAL_PRECISION(new_typmod);
+
+				/*
+				 * Cast is a no-op if least field stays the same or decreases
+				 * while precision stays the same or increases.  But
+				 * precision, which is to say, sub-second precision, only
+				 * affects ranges that include SECOND.
+				 */
+				noop = (new_least_field <= old_least_field) &&
+					(old_least_field > 0 /* SECOND */ ||
+					 new_precis >= MAX_INTERVAL_PRECISION ||
+					 new_precis >= old_precis);
+			}
+			if (noop)
+				ret = relabel_to_typmod(source, new_typmod);
 		}
-		if (noop)
-			ret = relabel_to_typmod(source, new_typmod);
 	}
 
 	PG_RETURN_POINTER(ret);
@@ -1359,7 +1381,7 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
 		 * can't do it consistently.  (We cannot enforce a range limit on the
 		 * highest expected field, since we do not have any equivalent of
 		 * SQL's <interval leading field precision>.)  If we ever decide to
-		 * revisit this, interval_transform will likely require adjusting.
+		 * revisit this, interval_support will likely require adjusting.
 		 *
 		 * Note: before PG 8.4 we interpreted a limited set of fields as
 		 * actually causing a "modulo" operation on a given value, potentially
@@ -5020,18 +5042,6 @@ interval_part(PG_FUNCTION_ARGS)
 }
 
 
-/* timestamp_zone_transform()
- * The original optimization here caused problems by relabeling Vars that
- * could be matched to index entries.  It might be possible to resurrect it
- * at some point by teaching the planner to be less cavalier with RelabelType
- * nodes, but that will take careful analysis.
- */
-Datum
-timestamp_zone_transform(PG_FUNCTION_ARGS)
-{
-	PG_RETURN_POINTER(NULL);
-}
-
 /*	timestamp_zone()
  *	Encode timestamp type with specified time zone.
  *	This function is just timestamp2timestamptz() except instead of
@@ -5125,18 +5135,6 @@ timestamp_zone(PG_FUNCTION_ARGS)
 	PG_RETURN_TIMESTAMPTZ(result);
 }
 
-/* timestamp_izone_transform()
- * The original optimization here caused problems by relabeling Vars that
- * could be matched to index entries.  It might be possible to resurrect it
- * at some point by teaching the planner to be less cavalier with RelabelType
- * nodes, but that will take careful analysis.
- */
-Datum
-timestamp_izone_transform(PG_FUNCTION_ARGS)
-{
-	PG_RETURN_POINTER(NULL);
-}
-
 /* timestamp_izone()
  * Encode timestamp type with specified time interval as time zone.
  */
diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c
index 1585da0..fdcc620 100644
--- a/src/backend/utils/adt/varbit.c
+++ b/src/backend/utils/adt/varbit.c
@@ -20,6 +20,7 @@
 #include "common/int.h"
 #include "libpq/pqformat.h"
 #include "nodes/nodeFuncs.h"
+#include "nodes/supportnodes.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/varbit.h"
@@ -672,32 +673,41 @@ varbit_send(PG_FUNCTION_ARGS)
 }
 
 /*
- * varbit_transform()
- * Flatten calls to varbit's length coercion function that set the new maximum
- * length >= the previous maximum length.  We can ignore the isExplicit
- * argument, since that only affects truncation cases.
+ * varbit_support()
+ *
+ * Planner support function for the varbit() length coercion function.
+ *
+ * Currently, the only interesting thing we can do is flatten calls that set
+ * the new maximum length >= the previous maximum length.  We can ignore the
+ * isExplicit argument, since that only affects truncation cases.
  */
 Datum
-varbit_transform(PG_FUNCTION_ARGS)
+varbit_support(PG_FUNCTION_ARGS)
 {
-	FuncExpr   *expr = castNode(FuncExpr, PG_GETARG_POINTER(0));
+	Node	   *rawreq = (Node *) PG_GETARG_POINTER(0);
 	Node	   *ret = NULL;
-	Node	   *typmod;
 
-	Assert(list_length(expr->args) >= 2);
+	if (IsA(rawreq, SupportRequestSimplify))
+	{
+		SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
+		FuncExpr   *expr = req->fcall;
+		Node	   *typmod;
 
-	typmod = (Node *) lsecond(expr->args);
+		Assert(list_length(expr->args) >= 2);
 
-	if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull)
-	{
-		Node	   *source = (Node *) linitial(expr->args);
-		int32		new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
-		int32		old_max = exprTypmod(source);
-		int32		new_max = new_typmod;
-
-		/* Note: varbit() treats typmod 0 as invalid, so we do too */
-		if (new_max <= 0 || (old_max > 0 && old_max <= new_max))
-			ret = relabel_to_typmod(source, new_typmod);
+		typmod = (Node *) lsecond(expr->args);
+
+		if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull)
+		{
+			Node	   *source = (Node *) linitial(expr->args);
+			int32		new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
+			int32		old_max = exprTypmod(source);
+			int32		new_max = new_typmod;
+
+			/* Note: varbit() treats typmod 0 as invalid, so we do too */
+			if (new_max <= 0 || (old_max > 0 && old_max <= new_max))
+				ret = relabel_to_typmod(source, new_typmod);
+		}
 	}
 
 	PG_RETURN_POINTER(ret);
diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index 5cf927e..c866af0 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -21,6 +21,7 @@
 #include "catalog/pg_type.h"
 #include "libpq/pqformat.h"
 #include "nodes/nodeFuncs.h"
+#include "nodes/supportnodes.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/varlena.h"
@@ -547,32 +548,41 @@ varcharsend(PG_FUNCTION_ARGS)
 
 
 /*
- * varchar_transform()
- * Flatten calls to varchar's length coercion function that set the new maximum
- * length >= the previous maximum length.  We can ignore the isExplicit
- * argument, since that only affects truncation cases.
+ * varchar_support()
+ *
+ * Planner support function for the varchar() length coercion function.
+ *
+ * Currently, the only interesting thing we can do is flatten calls that set
+ * the new maximum length >= the previous maximum length.  We can ignore the
+ * isExplicit argument, since that only affects truncation cases.
  */
 Datum
-varchar_transform(PG_FUNCTION_ARGS)
+varchar_support(PG_FUNCTION_ARGS)
 {
-	FuncExpr   *expr = castNode(FuncExpr, PG_GETARG_POINTER(0));
+	Node	   *rawreq = (Node *) PG_GETARG_POINTER(0);
 	Node	   *ret = NULL;
-	Node	   *typmod;
 
-	Assert(list_length(expr->args) >= 2);
+	if (IsA(rawreq, SupportRequestSimplify))
+	{
+		SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
+		FuncExpr   *expr = req->fcall;
+		Node	   *typmod;
 
-	typmod = (Node *) lsecond(expr->args);
+		Assert(list_length(expr->args) >= 2);
 
-	if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull)
-	{
-		Node	   *source = (Node *) linitial(expr->args);
-		int32		old_typmod = exprTypmod(source);
-		int32		new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
-		int32		old_max = old_typmod - VARHDRSZ;
-		int32		new_max = new_typmod - VARHDRSZ;
-
-		if (new_typmod < 0 || (old_typmod >= 0 && old_max <= new_max))
-			ret = relabel_to_typmod(source, new_typmod);
+		typmod = (Node *) lsecond(expr->args);
+
+		if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull)
+		{
+			Node	   *source = (Node *) linitial(expr->args);
+			int32		old_typmod = exprTypmod(source);
+			int32		new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
+			int32		old_max = old_typmod - VARHDRSZ;
+			int32		new_max = new_typmod - VARHDRSZ;
+
+			if (new_typmod < 0 || (old_typmod >= 0 && old_max <= new_max))
+				ret = relabel_to_typmod(source, new_typmod);
+		}
 	}
 
 	PG_RETURN_POINTER(ret);
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 245fcbf..4ff358a 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -1883,9 +1883,9 @@ my %tests = (
 	'CREATE TRANSFORM FOR int' => {
 		create_order => 34,
 		create_sql =>
-		  'CREATE TRANSFORM FOR int LANGUAGE SQL (FROM SQL WITH FUNCTION varchar_transform(internal), TO SQL WITH FUNCTION int4recv(internal));',
+		  'CREATE TRANSFORM FOR int LANGUAGE SQL (FROM SQL WITH FUNCTION varchar_support(internal), TO SQL WITH FUNCTION int4recv(internal));',
 		regexp =>
-		  qr/CREATE TRANSFORM FOR integer LANGUAGE sql \(FROM SQL WITH FUNCTION pg_catalog\.varchar_transform\(internal\), TO SQL WITH FUNCTION pg_catalog\.int4recv\(internal\)\);/m,
+		  qr/CREATE TRANSFORM FOR integer LANGUAGE sql \(FROM SQL WITH FUNCTION pg_catalog\.varchar_support\(internal\), TO SQL WITH FUNCTION pg_catalog\.int4recv\(internal\)\);/m,
 		like => { %full_runs, section_pre_data => 1, },
 	},
 
@@ -2880,7 +2880,7 @@ my %tests = (
 						   procost,
 						   prorows,
 						   provariadic,
-						   protransform,
+						   prosupport,
 						   prokind,
 						   prosecdef,
 						   proleakproof,
@@ -2912,7 +2912,7 @@ my %tests = (
 		\QGRANT SELECT(procost) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
 		\QGRANT SELECT(prorows) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
 		\QGRANT SELECT(provariadic) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
-		\QGRANT SELECT(protransform) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
+		\QGRANT SELECT(prosupport) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
 		\QGRANT SELECT(prokind) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
 		\QGRANT SELECT(prosecdef) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
 		\QGRANT SELECT(proleakproof) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 3ecc2e1..e5cb5bb 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -1326,11 +1326,11 @@
 { oid => '668', descr => 'adjust char() to typmod length',
   proname => 'bpchar', prorettype => 'bpchar',
   proargtypes => 'bpchar int4 bool', prosrc => 'bpchar' },
-{ oid => '3097', descr => 'transform a varchar length coercion',
-  proname => 'varchar_transform', prorettype => 'internal',
-  proargtypes => 'internal', prosrc => 'varchar_transform' },
+{ oid => '3097', descr => 'planner support for varchar length coercion',
+  proname => 'varchar_support', prorettype => 'internal',
+  proargtypes => 'internal', prosrc => 'varchar_support' },
 { oid => '669', descr => 'adjust varchar() to typmod length',
-  proname => 'varchar', protransform => 'varchar_transform',
+  proname => 'varchar', prosupport => 'varchar_support',
   prorettype => 'varchar', proargtypes => 'varchar int4 bool',
   prosrc => 'varchar' },
 
@@ -1954,13 +1954,9 @@
 
 # OIDS 1000 - 1999
 
-{ oid => '3994', descr => 'transform a time zone adjustment',
-  proname => 'timestamp_izone_transform', prorettype => 'internal',
-  proargtypes => 'internal', prosrc => 'timestamp_izone_transform' },
 { oid => '1026', descr => 'adjust timestamp to new time zone',
-  proname => 'timezone', protransform => 'timestamp_izone_transform',
-  prorettype => 'timestamp', proargtypes => 'interval timestamptz',
-  prosrc => 'timestamptz_izone' },
+  proname => 'timezone', prorettype => 'timestamp',
+  proargtypes => 'interval timestamptz', prosrc => 'timestamptz_izone' },
 
 { oid => '1031', descr => 'I/O',
   proname => 'aclitemin', provolatile => 's', prorettype => 'aclitem',
@@ -2190,13 +2186,9 @@
 { oid => '1158', descr => 'convert UNIX epoch to timestamptz',
   proname => 'to_timestamp', prorettype => 'timestamptz',
   proargtypes => 'float8', prosrc => 'float8_timestamptz' },
-{ oid => '3995', descr => 'transform a time zone adjustment',
-  proname => 'timestamp_zone_transform', prorettype => 'internal',
-  proargtypes => 'internal', prosrc => 'timestamp_zone_transform' },
 { oid => '1159', descr => 'adjust timestamp to new time zone',
-  proname => 'timezone', protransform => 'timestamp_zone_transform',
-  prorettype => 'timestamp', proargtypes => 'text timestamptz',
-  prosrc => 'timestamptz_zone' },
+  proname => 'timezone', prorettype => 'timestamp',
+  proargtypes => 'text timestamptz', prosrc => 'timestamptz_zone' },
 
 { oid => '1160', descr => 'I/O',
   proname => 'interval_in', provolatile => 's', prorettype => 'interval',
@@ -2301,11 +2293,11 @@
 
 # OIDS 1200 - 1299
 
-{ oid => '3918', descr => 'transform an interval length coercion',
-  proname => 'interval_transform', prorettype => 'internal',
-  proargtypes => 'internal', prosrc => 'interval_transform' },
+{ oid => '3918', descr => 'planner support for interval length coercion',
+  proname => 'interval_support', prorettype => 'internal',
+  proargtypes => 'internal', prosrc => 'interval_support' },
 { oid => '1200', descr => 'adjust interval precision',
-  proname => 'interval', protransform => 'interval_transform',
+  proname => 'interval', prosupport => 'interval_support',
   prorettype => 'interval', proargtypes => 'interval int4',
   prosrc => 'interval_scale' },
 
@@ -3713,13 +3705,12 @@
 { oid => '1685', descr => 'adjust bit() to typmod length',
   proname => 'bit', prorettype => 'bit', proargtypes => 'bit int4 bool',
   prosrc => 'bit' },
-{ oid => '3158', descr => 'transform a varbit length coercion',
-  proname => 'varbit_transform', prorettype => 'internal',
-  proargtypes => 'internal', prosrc => 'varbit_transform' },
+{ oid => '3158', descr => 'planner support for varbit length coercion',
+  proname => 'varbit_support', prorettype => 'internal',
+  proargtypes => 'internal', prosrc => 'varbit_support' },
 { oid => '1687', descr => 'adjust varbit() to typmod length',
-  proname => 'varbit', protransform => 'varbit_transform',
-  prorettype => 'varbit', proargtypes => 'varbit int4 bool',
-  prosrc => 'varbit' },
+  proname => 'varbit', prosupport => 'varbit_support', prorettype => 'varbit',
+  proargtypes => 'varbit int4 bool', prosrc => 'varbit' },
 
 { oid => '1698', descr => 'position of sub-bitstring',
   proname => 'position', prorettype => 'int4', proargtypes => 'bit bit',
@@ -4081,11 +4072,11 @@
 { oid => '2918', descr => 'I/O typmod',
   proname => 'numerictypmodout', prorettype => 'cstring', proargtypes => 'int4',
   prosrc => 'numerictypmodout' },
-{ oid => '3157', descr => 'transform a numeric length coercion',
-  proname => 'numeric_transform', prorettype => 'internal',
-  proargtypes => 'internal', prosrc => 'numeric_transform' },
+{ oid => '3157', descr => 'planner support for numeric length coercion',
+  proname => 'numeric_support', prorettype => 'internal',
+  proargtypes => 'internal', prosrc => 'numeric_support' },
 { oid => '1703', descr => 'adjust numeric to typmod precision/scale',
-  proname => 'numeric', protransform => 'numeric_transform',
+  proname => 'numeric', prosupport => 'numeric_support',
   prorettype => 'numeric', proargtypes => 'numeric int4', prosrc => 'numeric' },
 { oid => '1704',
   proname => 'numeric_abs', prorettype => 'numeric', proargtypes => 'numeric',
@@ -5448,15 +5439,15 @@
   proname => 'bytea_sortsupport', prorettype => 'void',
   proargtypes => 'internal', prosrc => 'bytea_sortsupport' },
 
-{ oid => '3917', descr => 'transform a timestamp length coercion',
-  proname => 'timestamp_transform', prorettype => 'internal',
-  proargtypes => 'internal', prosrc => 'timestamp_transform' },
-{ oid => '3944', descr => 'transform a time length coercion',
-  proname => 'time_transform', prorettype => 'internal',
-  proargtypes => 'internal', prosrc => 'time_transform' },
+{ oid => '3917', descr => 'planner support for timestamp length coercion',
+  proname => 'timestamp_support', prorettype => 'internal',
+  proargtypes => 'internal', prosrc => 'timestamp_support' },
+{ oid => '3944', descr => 'planner support for time length coercion',
+  proname => 'time_support', prorettype => 'internal',
+  proargtypes => 'internal', prosrc => 'time_support' },
 
 { oid => '1961', descr => 'adjust timestamp precision',
-  proname => 'timestamp', protransform => 'timestamp_transform',
+  proname => 'timestamp', prosupport => 'timestamp_support',
   prorettype => 'timestamp', proargtypes => 'timestamp int4',
   prosrc => 'timestamp_scale' },
 
@@ -5468,14 +5459,14 @@
   prosrc => 'oidsmaller' },
 
 { oid => '1967', descr => 'adjust timestamptz precision',
-  proname => 'timestamptz', protransform => 'timestamp_transform',
+  proname => 'timestamptz', prosupport => 'timestamp_support',
   prorettype => 'timestamptz', proargtypes => 'timestamptz int4',
   prosrc => 'timestamptz_scale' },
 { oid => '1968', descr => 'adjust time precision',
-  proname => 'time', protransform => 'time_transform', prorettype => 'time',
+  proname => 'time', prosupport => 'time_support', prorettype => 'time',
   proargtypes => 'time int4', prosrc => 'time_scale' },
 { oid => '1969', descr => 'adjust time with time zone precision',
-  proname => 'timetz', protransform => 'time_transform', prorettype => 'timetz',
+  proname => 'timetz', prosupport => 'time_support', prorettype => 'timetz',
   proargtypes => 'timetz int4', prosrc => 'timetz_scale' },
 
 { oid => '2003',
@@ -5662,13 +5653,11 @@
   prosrc => 'select pg_catalog.age(cast(current_date as timestamp without time zone), $1)' },
 
 { oid => '2069', descr => 'adjust timestamp to new time zone',
-  proname => 'timezone', protransform => 'timestamp_zone_transform',
-  prorettype => 'timestamptz', proargtypes => 'text timestamp',
-  prosrc => 'timestamp_zone' },
+  proname => 'timezone', prorettype => 'timestamptz',
+  proargtypes => 'text timestamp', prosrc => 'timestamp_zone' },
 { oid => '2070', descr => 'adjust timestamp to new time zone',
-  proname => 'timezone', protransform => 'timestamp_izone_transform',
-  prorettype => 'timestamptz', proargtypes => 'interval timestamp',
-  prosrc => 'timestamp_izone' },
+  proname => 'timezone', prorettype => 'timestamptz',
+  proargtypes => 'interval timestamp', prosrc => 'timestamp_izone' },
 { oid => '2071',
   proname => 'date_pl_interval', prorettype => 'timestamp',
   proargtypes => 'date interval', prosrc => 'date_pl_interval' },
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c2bb951..b433769 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -53,8 +53,8 @@ CATALOG(pg_proc,1255,ProcedureRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(81,Proce
 	/* element type of variadic array, or 0 */
 	Oid			provariadic BKI_DEFAULT(0) BKI_LOOKUP(pg_type);
 
-	/* transforms calls to it during planning */
-	regproc		protransform BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
+	/* planner support function for this function, or 0 if none */
+	regproc		prosupport BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
 
 	/* see PROKIND_ categories below */
 	char		prokind BKI_DEFAULT(f);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index fbe2dc1..3d3adc2 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -505,7 +505,8 @@ typedef enum NodeTag
 	T_IndexAmRoutine,			/* in access/amapi.h */
 	T_TsmRoutine,				/* in access/tsmapi.h */
 	T_ForeignKeyCacheInfo,		/* in utils/rel.h */
-	T_CallContext				/* in nodes/parsenodes.h */
+	T_CallContext,				/* in nodes/parsenodes.h */
+	T_SupportRequestSimplify	/* in nodes/supportnodes.h */
 } NodeTag;
 
 /*
diff --git a/src/include/nodes/supportnodes.h b/src/include/nodes/supportnodes.h
new file mode 100644
index 0000000..1f7d02b
--- /dev/null
+++ b/src/include/nodes/supportnodes.h
@@ -0,0 +1,70 @@
+/*-------------------------------------------------------------------------
+ *
+ * supportnodes.h
+ *	  Definitions for planner support functions.
+ *
+ * This file defines the API for "planner support functions", which
+ * are SQL functions (normally written in C) that can be attached to
+ * another "target" function to give the system additional knowledge
+ * about the target function.  All the current capabilities have to do
+ * with planning queries that use the target function, though it is
+ * possible that future extensions will add functionality to be invoked
+ * by the parser or executor.
+ *
+ * A support function must have the SQL signature
+ *		supportfn(internal) returns internal
+ * The argument is a pointer to one of the Node types defined in this file.
+ * The result is usually also a Node pointer, though its type depends on
+ * which capability is being invoked.  In all cases, a NULL pointer result
+ * (that's PG_RETURN_POINTER(NULL), not PG_RETURN_NULL()) indicates that
+ * the support function cannot do anything useful for the given request.
+ * Support functions must return a NULL pointer, not fail, if they do not
+ * recognize the request node type or cannot handle the given case; this
+ * allows for future extensions of the set of request cases.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/nodes/supportnodes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SUPPORTNODES_H
+#define SUPPORTNODES_H
+
+#include "nodes/primnodes.h"
+
+struct PlannerInfo;				/* avoid including relation.h here */
+
+
+/*
+ * The Simplify request allows the support function to perform plan-time
+ * simplification of a call to its target function.  For example, a varchar
+ * length coercion that does not decrease the allowed length of its argument
+ * could be replaced by a RelabelType node, or "x + 0" could be replaced by
+ * "x".  This is invoked during the planner's constant-folding pass, so the
+ * function's arguments can be presumed already simplified.
+ *
+ * The planner's PlannerInfo "root" is typically not needed, but can be
+ * consulted if it's necessary to obtain info about Vars present in
+ * the given node tree.  Beware that root could be NULL in some usages.
+ *
+ * "fcall" will be a FuncExpr invoking the support function's target
+ * function.  (This is true even if the original parsetree node was an
+ * operator call; a FuncExpr is synthesized for this purpose.)
+ *
+ * The result should be a semantically-equivalent transformed node tree,
+ * or NULL if no simplification could be performed.  Do *not* return or
+ * modify *fcall, as it isn't really a separately allocated Node.  But
+ * it's okay to use fcall->args, or parts of it, in the result tree.
+ */
+typedef struct SupportRequestSimplify
+{
+	NodeTag		type;
+
+	struct PlannerInfo *root;	/* Planner's infrastructure */
+	FuncExpr   *fcall;			/* Function call to be simplified */
+} SupportRequestSimplify;
+
+#endif							/* SUPPORTNODES_H */
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index f5ec9bb..87f819e 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -330,7 +330,7 @@ extern int	DecodeUnits(int field, char *lowtoken, int *val);
 
 extern int	j2day(int jd);
 
-extern Node *TemporalTransform(int32 max_precis, Node *node);
+extern Node *TemporalSimplify(int32 max_precis, Node *node);
 
 extern bool CheckDateTokenTables(void);
 
diff --git a/src/test/modules/test_ddl_deparse/expected/create_transform.out b/src/test/modules/test_ddl_deparse/expected/create_transform.out
index 0d1cc36..da7fea2 100644
--- a/src/test/modules/test_ddl_deparse/expected/create_transform.out
+++ b/src/test/modules/test_ddl_deparse/expected/create_transform.out
@@ -7,7 +7,7 @@
 -- internal and as return argument the datatype of the transform done.
 -- pl/plpgsql does not authorize the use of internal as data type.
 CREATE TRANSFORM FOR int LANGUAGE SQL (
-    FROM SQL WITH FUNCTION varchar_transform(internal),
+    FROM SQL WITH FUNCTION varchar_support(internal),
     TO SQL WITH FUNCTION int4recv(internal));
 NOTICE:  DDL test: type simple, tag CREATE TRANSFORM
 DROP TRANSFORM FOR int LANGUAGE SQL;
diff --git a/src/test/modules/test_ddl_deparse/sql/create_transform.sql b/src/test/modules/test_ddl_deparse/sql/create_transform.sql
index 0968702..132fc5a 100644
--- a/src/test/modules/test_ddl_deparse/sql/create_transform.sql
+++ b/src/test/modules/test_ddl_deparse/sql/create_transform.sql
@@ -8,7 +8,7 @@
 -- internal and as return argument the datatype of the transform done.
 -- pl/plpgsql does not authorize the use of internal as data type.
 CREATE TRANSFORM FOR int LANGUAGE SQL (
-    FROM SQL WITH FUNCTION varchar_transform(internal),
+    FROM SQL WITH FUNCTION varchar_support(internal),
     TO SQL WITH FUNCTION int4recv(internal));
 
 DROP TRANSFORM FOR int LANGUAGE SQL;
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 4085e45..c89ec06 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -38,7 +38,7 @@ CREATE USER MAPPING FOR regress_addr_user SERVER "integer";
 ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regress_addr_user;
 ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user REVOKE DELETE ON TABLES FROM regress_addr_user;
 CREATE TRANSFORM FOR int LANGUAGE SQL (
-	FROM SQL WITH FUNCTION varchar_transform(internal),
+	FROM SQL WITH FUNCTION varchar_support(internal),
 	TO SQL WITH FUNCTION int4recv(internal));
 CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable;
 CREATE SUBSCRIPTION addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE);
diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out
index ef268d3..4edc817 100644
--- a/src/test/regress/expected/oidjoins.out
+++ b/src/test/regress/expected/oidjoins.out
@@ -809,12 +809,12 @@ WHERE	provariadic != 0 AND
 ------+-------------
 (0 rows)
 
-SELECT	ctid, protransform
+SELECT	ctid, prosupport
 FROM	pg_catalog.pg_proc fk
-WHERE	protransform != 0 AND
-	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.protransform);
- ctid | protransform 
-------+--------------
+WHERE	prosupport != 0 AND
+	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prosupport);
+ ctid | prosupport 
+------+------------
 (0 rows)
 
 SELECT	ctid, prorettype
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 7328095..ce25ee0 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -453,10 +453,10 @@ WHERE proallargtypes IS NOT NULL AND
 -----+---------+-------------+----------------+-------------
 (0 rows)
 
--- Check for protransform functions with the wrong signature
+-- Check for prosupport functions with the wrong signature
 SELECT p1.oid, p1.proname, p2.oid, p2.proname
 FROM pg_proc AS p1, pg_proc AS p2
-WHERE p2.oid = p1.protransform AND
+WHERE p2.oid = p1.prosupport AND
     (p2.prorettype != 'internal'::regtype OR p2.proretset OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
  oid | proname | oid | proname 
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index d7df322..fd79465 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -41,7 +41,7 @@ CREATE USER MAPPING FOR regress_addr_user SERVER "integer";
 ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regress_addr_user;
 ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user REVOKE DELETE ON TABLES FROM regress_addr_user;
 CREATE TRANSFORM FOR int LANGUAGE SQL (
-	FROM SQL WITH FUNCTION varchar_transform(internal),
+	FROM SQL WITH FUNCTION varchar_support(internal),
 	TO SQL WITH FUNCTION int4recv(internal));
 CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable;
 CREATE SUBSCRIPTION addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE);
diff --git a/src/test/regress/sql/oidjoins.sql b/src/test/regress/sql/oidjoins.sql
index c8291d3..dbe4a58 100644
--- a/src/test/regress/sql/oidjoins.sql
+++ b/src/test/regress/sql/oidjoins.sql
@@ -405,10 +405,10 @@ SELECT	ctid, provariadic
 FROM	pg_catalog.pg_proc fk
 WHERE	provariadic != 0 AND
 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.provariadic);
-SELECT	ctid, protransform
+SELECT	ctid, prosupport
 FROM	pg_catalog.pg_proc fk
-WHERE	protransform != 0 AND
-	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.protransform);
+WHERE	prosupport != 0 AND
+	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prosupport);
 SELECT	ctid, prorettype
 FROM	pg_catalog.pg_proc fk
 WHERE	prorettype != 0 AND
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 8544cbe..e2014fc 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -353,10 +353,10 @@ WHERE proallargtypes IS NOT NULL AND
         FROM generate_series(1, array_length(proallargtypes, 1)) g(i)
         WHERE proargmodes IS NULL OR proargmodes[i] IN ('i', 'b', 'v'));
 
--- Check for protransform functions with the wrong signature
+-- Check for prosupport functions with the wrong signature
 SELECT p1.oid, p1.proname, p2.oid, p2.proname
 FROM pg_proc AS p1, pg_proc AS p2
-WHERE p2.oid = p1.protransform AND
+WHERE p2.oid = p1.prosupport AND
     (p2.prorettype != 'internal'::regtype OR p2.proretset OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
 
diff --git a/src/tools/findoidjoins/README b/src/tools/findoidjoins/README
index 305454a..e5fc310 100644
--- a/src/tools/findoidjoins/README
+++ b/src/tools/findoidjoins/README
@@ -161,7 +161,7 @@ Join pg_catalog.pg_proc.pronamespace => pg_catalog.pg_namespace.oid
 Join pg_catalog.pg_proc.proowner => pg_catalog.pg_authid.oid
 Join pg_catalog.pg_proc.prolang => pg_catalog.pg_language.oid
 Join pg_catalog.pg_proc.provariadic => pg_catalog.pg_type.oid
-Join pg_catalog.pg_proc.protransform => pg_catalog.pg_proc.oid
+Join pg_catalog.pg_proc.prosupport => pg_catalog.pg_proc.oid
 Join pg_catalog.pg_proc.prorettype => pg_catalog.pg_type.oid
 Join pg_catalog.pg_range.rngtypid => pg_catalog.pg_type.oid
 Join pg_catalog.pg_range.rngsubtype => pg_catalog.pg_type.oid
