]> granicus.if.org Git - postgresql/commitdiff
Create the infrastructure for planner support functions.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 9 Feb 2019 23:08:48 +0000 (18:08 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 9 Feb 2019 23:08:48 +0000 (18:08 -0500)
Rename/repurpose pg_proc.protransform as "prosupport".  The idea is
still that it names an internal function that provides knowledge to
the planner about the behavior of the function it's attached to;
but redesign the API specification so that it's not limited to doing
just one thing, but can support an extensible set of requests.

The original purpose of simplifying a function call is handled by
the first request type to be invented, SupportRequestSimplify.
Adjust all the existing transform functions to handle this API,
and rename them fron "xxx_transform" to "xxx_support" to reflect
the potential generalization of what they do.  (Since we never
previously provided any way for extensions to add transform functions,
this change doesn't create an API break for them.)

Also add DDL and pg_dump support for attaching a support function to a
user-defined function.  Unfortunately, DDL access has to be restricted
to superusers, at least for now; but seeing that support functions
will pretty much have to be written in C, that limitation is just
theoretical.  (This support is untested in this patch, but a follow-on
patch will add cases that exercise it.)

Discussion: https://postgr.es/m/15193.1548028093@sss.pgh.pa.us

41 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/keywords.sgml
doc/src/sgml/ref/alter_function.sgml
doc/src/sgml/ref/create_function.sgml
doc/src/sgml/xfunc.sgml
doc/src/sgml/xoper.sgml
src/backend/catalog/pg_aggregate.c
src/backend/catalog/pg_depend.c
src/backend/catalog/pg_proc.c
src/backend/commands/functioncmds.c
src/backend/commands/proclang.c
src/backend/commands/typecmds.c
src/backend/optimizer/util/clauses.c
src/backend/parser/gram.y
src/backend/utils/adt/date.c
src/backend/utils/adt/datetime.c
src/backend/utils/adt/numeric.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/adt/timestamp.c
src/backend/utils/adt/varbit.c
src/backend/utils/adt/varchar.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/t/002_pg_dump.pl
src/include/catalog/catversion.h
src/include/catalog/pg_proc.dat
src/include/catalog/pg_proc.h
src/include/nodes/nodes.h
src/include/nodes/supportnodes.h [new file with mode: 0644]
src/include/parser/kwlist.h
src/include/utils/datetime.h
src/test/modules/test_ddl_deparse/expected/create_transform.out
src/test/modules/test_ddl_deparse/sql/create_transform.sql
src/test/regress/expected/alter_table.out
src/test/regress/expected/object_address.out
src/test/regress/expected/oidjoins.out
src/test/regress/expected/opr_sanity.out
src/test/regress/sql/alter_table.sql
src/test/regress/sql/object_address.sql
src/test/regress/sql/oidjoins.sql
src/test/regress/sql/opr_sanity.sql
src/tools/findoidjoins/README

index af4d0625eab6c51eb4b36295c16953f88bd2fe0d..6dd0700da752619209c1cb33036918e33a8753f5 100644 (file)
@@ -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>
index a37d0b756bdeccd95b925336db9e937d43754f6a..fa32a88b8e2218ed594f43eefef84572a1849f0e 100644 (file)
     <entry>reserved</entry>
     <entry>reserved</entry>
    </row>
+   <row>
+    <entry><token>SUPPORT</token></entry>
+    <entry>non-reserved</entry>
+    <entry></entry>
+    <entry></entry>
+    <entry></entry>
+   </row>
    <row>
     <entry><token>SYMMETRIC</token></entry>
     <entry>reserved</entry>
index d8747e074821f9a1d91debd210368de102f7659a..03ffa5945a2a6a3917a8bcb14544a9cb5908a5ec 100644 (file)
@@ -40,6 +40,7 @@ ALTER FUNCTION <replaceable>name</replaceable> [ ( [ [ <replaceable class="param
     PARALLEL { UNSAFE | RESTRICTED | SAFE }
     COST <replaceable class="parameter">execution_cost</replaceable>
     ROWS <replaceable class="parameter">result_rows</replaceable>
+    SUPPORT <replaceable class="parameter">support_function</replaceable>
     SET <replaceable class="parameter">configuration_parameter</replaceable> { TO | = } { <replaceable class="parameter">value</replaceable> | DEFAULT }
     SET <replaceable class="parameter">configuration_parameter</replaceable> FROM CURRENT
     RESET <replaceable class="parameter">configuration_parameter</replaceable>
@@ -248,6 +249,24 @@ ALTER FUNCTION <replaceable>name</replaceable> [ ( [ [ <replaceable class="param
      </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>SUPPORT</literal> <replaceable class="parameter">support_function</replaceable></term>
+
+    <listitem>
+     <para>
+      Set or change the planner support function to use for this function.
+      See <xref linkend="xfunc-optimization"/> for details.  You must be
+      superuser to use this option.
+     </para>
+
+     <para>
+      This option cannot be used to remove the support function altogether,
+      since it must name a new support function.  Use <command>CREATE OR
+      REPLACE FUNCTION</command> if you need to do that.
+     </para>
+    </listitem>
+   </varlistentry>
+
      <varlistentry>
       <term><replaceable>configuration_parameter</replaceable></term>
       <term><replaceable>value</replaceable></term>
index 40725431842fbf8a6e56454d7eb49ae1e5297c75..dd6a2f73049b49bedfcc43c44c8420512d5f78f7 100644 (file)
@@ -33,6 +33,7 @@ CREATE [ OR REPLACE ] FUNCTION
     | PARALLEL { UNSAFE | RESTRICTED | SAFE }
     | COST <replaceable class="parameter">execution_cost</replaceable>
     | ROWS <replaceable class="parameter">result_rows</replaceable>
+    | SUPPORT <replaceable class="parameter">support_function</replaceable>
     | SET <replaceable class="parameter">configuration_parameter</replaceable> { TO <replaceable class="parameter">value</replaceable> | = <replaceable class="parameter">value</replaceable> | FROM CURRENT }
     | AS '<replaceable class="parameter">definition</replaceable>'
     | AS '<replaceable class="parameter">obj_file</replaceable>', '<replaceable class="parameter">link_symbol</replaceable>'
@@ -477,6 +478,19 @@ CREATE [ OR REPLACE ] FUNCTION
      </listitem>
     </varlistentry>
 
+    <varlistentry>
+     <term><literal>SUPPORT</literal> <replaceable class="parameter">support_function</replaceable></term>
+
+     <listitem>
+      <para>
+       The name (optionally schema-qualified) of a <firstterm>planner support
+       function</firstterm> to use for this function.  See
+       <xref linkend="xfunc-optimization"/> for details.
+       You must be superuser to use this option.
+      </para>
+     </listitem>
+    </varlistentry>
+
     <varlistentry>
      <term><replaceable>configuration_parameter</replaceable></term>
      <term><replaceable>value</replaceable></term>
index e18272c33a19c33ec623a0a0f9162ef5dab713f1..d70aa6eed786e965a465adbb8c3e6ff3307c9576 100644 (file)
@@ -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>
index 2f5560ac5059099e8b93063f573baa8c4fbbccb5..260e43c64597ede8c8b56261d41d0d9d3c953930 100644 (file)
@@ -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>
 
index cc3806e85d571178ce330b1d50a8f66c0dca2716..19e3171bf7df4ce257a688f2cf4fafa35e82c56a 100644 (file)
@@ -632,6 +632,7 @@ AggregateCreate(const char *aggName,
                                                         parameterDefaults, /* parameterDefaults */
                                                         PointerGetDatum(NULL), /* trftypes */
                                                         PointerGetDatum(NULL), /* proconfig */
+                                                        InvalidOid,    /* no prosupport */
                                                         1, /* procost */
                                                         0);    /* prorows */
        procOid = myself.objectId;
index 2b8f651c9986e8186495ded6356ce4706166babc..23b01f841e4bc3fc689ca6dd46276a231bc031ea 100644 (file)
@@ -286,9 +286,12 @@ deleteDependencyRecordsForClass(Oid classId, Oid objectId,
  * newRefObjectId is the new referenced object (must be of class refClassId).
  *
  * Note the lack of objsubid parameters.  If there are subobject references
- * they will all be readjusted.
+ * they will all be readjusted.  Also, there is an expectation that we are
+ * dealing with NORMAL dependencies: if we have to replace an (implicit)
+ * dependency on a pinned object with an explicit dependency on an unpinned
+ * one, the new one will be NORMAL.
  *
- * Returns the number of records updated.
+ * Returns the number of records updated -- zero indicates a problem.
  */
 long
 changeDependencyFor(Oid classId, Oid objectId,
@@ -301,35 +304,52 @@ changeDependencyFor(Oid classId, Oid objectId,
        SysScanDesc scan;
        HeapTuple       tup;
        ObjectAddress objAddr;
+       ObjectAddress depAddr;
+       bool            oldIsPinned;
        bool            newIsPinned;
 
        depRel = table_open(DependRelationId, RowExclusiveLock);
 
        /*
-        * If oldRefObjectId is pinned, there won't be any dependency entries on
-        * it --- we can't cope in that case.  (This isn't really worth expending
-        * code to fix, in current usage; it just means you can't rename stuff out
-        * of pg_catalog, which would likely be a bad move anyway.)
+        * Check to see if either oldRefObjectId or newRefObjectId is pinned.
+        * Pinned objects should not have any dependency entries pointing to them,
+        * so in these cases we should add or remove a pg_depend entry, or do
+        * nothing at all, rather than update an entry as in the normal case.
         */
        objAddr.classId = refClassId;
        objAddr.objectId = oldRefObjectId;
        objAddr.objectSubId = 0;
 
-       if (isObjectPinned(&objAddr, depRel))
-               ereport(ERROR,
-                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("cannot remove dependency on %s because it is a system object",
-                                               getObjectDescription(&objAddr))));
+       oldIsPinned = isObjectPinned(&objAddr, depRel);
 
-       /*
-        * We can handle adding a dependency on something pinned, though, since
-        * that just means deleting the dependency entry.
-        */
        objAddr.objectId = newRefObjectId;
 
        newIsPinned = isObjectPinned(&objAddr, depRel);
 
-       /* Now search for dependency records */
+       if (oldIsPinned)
+       {
+               table_close(depRel, RowExclusiveLock);
+
+               /*
+                * If both are pinned, we need do nothing.  However, return 1 not 0,
+                * else callers will think this is an error case.
+                */
+               if (newIsPinned)
+                       return 1;
+
+               /*
+                * There is no old dependency record, but we should insert a new one.
+                * Assume a normal dependency is wanted.
+                */
+               depAddr.classId = classId;
+               depAddr.objectId = objectId;
+               depAddr.objectSubId = 0;
+               recordDependencyOn(&depAddr, &objAddr, DEPENDENCY_NORMAL);
+
+               return 1;
+       }
+
+       /* There should be existing dependency record(s), so search. */
        ScanKeyInit(&key[0],
                                Anum_pg_depend_classid,
                                BTEqualStrategyNumber, F_OIDEQ,
index db780616e6212726ce9f9edeaaffb7da49b9138d..557e0ea1f1446e43685db99763a2a4f5509d5db1 100644 (file)
@@ -88,6 +88,7 @@ ProcedureCreate(const char *procedureName,
                                List *parameterDefaults,
                                Datum trftypes,
                                Datum proconfig,
+                               Oid prosupport,
                                float4 procost,
                                float4 prorows)
 {
@@ -319,7 +320,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(prosupport);
        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);
@@ -656,6 +657,15 @@ ProcedureCreate(const char *procedureName,
                recordDependencyOnExpr(&myself, (Node *) parameterDefaults,
                                                           NIL, DEPENDENCY_NORMAL);
 
+       /* dependency on support function, if any */
+       if (OidIsValid(prosupport))
+       {
+               referenced.classId = ProcedureRelationId;
+               referenced.objectId = prosupport;
+               referenced.objectSubId = 0;
+               recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+       }
+
        /* dependency on owner */
        if (!is_update)
                recordDependencyOnOwner(ProcedureRelationId, retval, proowner);
index 9a2f1a85b47a693d7cc6f2e8c0989980024c029f..4f62e48d98f3176366ecc1ed23744f115091f67b 100644 (file)
@@ -479,6 +479,7 @@ compute_common_attribute(ParseState *pstate,
                                                 List **set_items,
                                                 DefElem **cost_item,
                                                 DefElem **rows_item,
+                                                DefElem **support_item,
                                                 DefElem **parallel_item)
 {
        if (strcmp(defel->defname, "volatility") == 0)
@@ -537,6 +538,15 @@ compute_common_attribute(ParseState *pstate,
 
                *rows_item = defel;
        }
+       else if (strcmp(defel->defname, "support") == 0)
+       {
+               if (is_procedure)
+                       goto procedure_error;
+               if (*support_item)
+                       goto duplicate_error;
+
+               *support_item = defel;
+       }
        else if (strcmp(defel->defname, "parallel") == 0)
        {
                if (is_procedure)
@@ -635,6 +645,45 @@ update_proconfig_value(ArrayType *a, List *set_items)
        return a;
 }
 
+static Oid
+interpret_func_support(DefElem *defel)
+{
+       List       *procName = defGetQualifiedName(defel);
+       Oid                     procOid;
+       Oid                     argList[1];
+
+       /*
+        * Support functions always take one INTERNAL argument and return
+        * INTERNAL.
+        */
+       argList[0] = INTERNALOID;
+
+       procOid = LookupFuncName(procName, 1, argList, true);
+       if (!OidIsValid(procOid))
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                                errmsg("function %s does not exist",
+                                               func_signature_string(procName, 1, NIL, argList))));
+
+       if (get_func_rettype(procOid) != INTERNALOID)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("support function %s must return type %s",
+                                               NameListToString(procName), "internal")));
+
+       /*
+        * Someday we might want an ACL check here; but for now, we insist that
+        * you be superuser to specify a support function, so privilege on the
+        * support function is moot.
+        */
+       if (!superuser())
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("must be superuser to specify a support function")));
+
+       return procOid;
+}
+
 
 /*
  * Dissect the list of options assembled in gram.y into function
@@ -655,6 +704,7 @@ compute_function_attributes(ParseState *pstate,
                                                        ArrayType **proconfig,
                                                        float4 *procost,
                                                        float4 *prorows,
+                                                       Oid *prosupport,
                                                        char *parallel_p)
 {
        ListCell   *option;
@@ -669,6 +719,7 @@ compute_function_attributes(ParseState *pstate,
        List       *set_items = NIL;
        DefElem    *cost_item = NULL;
        DefElem    *rows_item = NULL;
+       DefElem    *support_item = NULL;
        DefElem    *parallel_item = NULL;
 
        foreach(option, options)
@@ -726,6 +777,7 @@ compute_function_attributes(ParseState *pstate,
                                                                                  &set_items,
                                                                                  &cost_item,
                                                                                  &rows_item,
+                                                                                 &support_item,
                                                                                  &parallel_item))
                {
                        /* recognized common option */
@@ -788,6 +840,8 @@ compute_function_attributes(ParseState *pstate,
                                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                         errmsg("ROWS must be positive")));
        }
+       if (support_item)
+               *prosupport = interpret_func_support(support_item);
        if (parallel_item)
                *parallel_p = interpret_func_parallel(parallel_item);
 }
@@ -893,6 +947,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
        ArrayType  *proconfig;
        float4          procost;
        float4          prorows;
+       Oid                     prosupport;
        HeapTuple       languageTuple;
        Form_pg_language languageStruct;
        List       *as_clause;
@@ -917,6 +972,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
        proconfig = NULL;
        procost = -1;                           /* indicates not set */
        prorows = -1;                           /* indicates not set */
+       prosupport = InvalidOid;
        parallel = PROPARALLEL_UNSAFE;
 
        /* Extract non-default attributes from stmt->options list */
@@ -926,7 +982,8 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
                                                                &as_clause, &language, &transformDefElem,
                                                                &isWindowFunc, &volatility,
                                                                &isStrict, &security, &isLeakProof,
-                                                               &proconfig, &procost, &prorows, &parallel);
+                                                               &proconfig, &procost, &prorows,
+                                                               &prosupport, &parallel);
 
        /* Look up the language and validate permissions */
        languageTuple = SearchSysCache1(LANGNAME, PointerGetDatum(language));
@@ -1113,6 +1170,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
                                                   parameterDefaults,
                                                   PointerGetDatum(trftypes),
                                                   PointerGetDatum(proconfig),
+                                                  prosupport,
                                                   procost,
                                                   prorows);
 }
@@ -1187,6 +1245,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
        List       *set_items = NIL;
        DefElem    *cost_item = NULL;
        DefElem    *rows_item = NULL;
+       DefElem    *support_item = NULL;
        DefElem    *parallel_item = NULL;
        ObjectAddress address;
 
@@ -1194,6 +1253,8 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 
        funcOid = LookupFuncWithArgs(stmt->objtype, stmt->func, false);
 
+       ObjectAddressSet(address, ProcedureRelationId, funcOid);
+
        tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(funcOid));
        if (!HeapTupleIsValid(tup)) /* should not happen */
                elog(ERROR, "cache lookup failed for function %u", funcOid);
@@ -1228,6 +1289,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
                                                                         &set_items,
                                                                         &cost_item,
                                                                         &rows_item,
+                                                                        &support_item,
                                                                         &parallel_item) == false)
                        elog(ERROR, "option \"%s\" not recognized", defel->defname);
        }
@@ -1266,6 +1328,28 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
                                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                         errmsg("ROWS is not applicable when function does not return a set")));
        }
+       if (support_item)
+       {
+               /* interpret_func_support handles the privilege check */
+               Oid                     newsupport = interpret_func_support(support_item);
+
+               /* Add or replace dependency on support function */
+               if (OidIsValid(procForm->prosupport))
+                       changeDependencyFor(ProcedureRelationId, funcOid,
+                                                               ProcedureRelationId, procForm->prosupport,
+                                                               newsupport);
+               else
+               {
+                       ObjectAddress referenced;
+
+                       referenced.classId = ProcedureRelationId;
+                       referenced.objectId = newsupport;
+                       referenced.objectSubId = 0;
+                       recordDependencyOn(&address, &referenced, DEPENDENCY_NORMAL);
+               }
+
+               procForm->prosupport = newsupport;
+       }
        if (set_items)
        {
                Datum           datum;
@@ -1308,8 +1392,6 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 
        InvokeObjectPostAlterHook(ProcedureRelationId, funcOid, 0);
 
-       ObjectAddressSet(address, ProcedureRelationId, funcOid);
-
        table_close(rel, NoLock);
        heap_freetuple(tup);
 
index c2e9e41c07b68a13d017c594ecdf72e0f43ed08a..59c4e8dfd0909074c3ea10adab3dca56d43a34ad 100644 (file)
@@ -141,6 +141,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
                                                                          NIL,
                                                                          PointerGetDatum(NULL),
                                                                          PointerGetDatum(NULL),
+                                                                         InvalidOid,
                                                                          1,
                                                                          0);
                        handlerOid = tmpAddr.objectId;
@@ -180,6 +181,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
                                                                                  NIL,
                                                                                  PointerGetDatum(NULL),
                                                                                  PointerGetDatum(NULL),
+                                                                                 InvalidOid,
                                                                                  1,
                                                                                  0);
                                inlineOid = tmpAddr.objectId;
@@ -222,6 +224,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
                                                                                  NIL,
                                                                                  PointerGetDatum(NULL),
                                                                                  PointerGetDatum(NULL),
+                                                                                 InvalidOid,
                                                                                  1,
                                                                                  0);
                                valOid = tmpAddr.objectId;
index fa7161ef9d92c6d180edb9da726cf488aff27097..448926db12573980cf86db0fd1e10d05aa644d12 100644 (file)
@@ -1664,6 +1664,7 @@ makeRangeConstructors(const char *name, Oid namespace,
                                                                 NIL,   /* parameterDefaults */
                                                                 PointerGetDatum(NULL), /* trftypes */
                                                                 PointerGetDatum(NULL), /* proconfig */
+                                                                InvalidOid,    /* prosupport */
                                                                 1.0,   /* procost */
                                                                 0.0);  /* prorows */
 
index d7ff17c363dd3e53852bc5bb628648dc0ec37d69..002c29a5f5d5c43f7c721eb21f5022038256067d 100644 (file)
@@ -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"
@@ -3985,13 +3986,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;
@@ -4005,9 +4009,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)
index c1faf4152ca170a45a0d6a55687bdc93fdb24f44..ef6bbe35d7f756e9aa006739f65cc6f620ed69ac 100644 (file)
@@ -676,7 +676,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
        SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW
        SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P
        START STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P
-       SUBSCRIPTION SUBSTRING SYMMETRIC SYSID SYSTEM_P
+       SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P
 
        TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN
        TIES TIME TIMESTAMP TO TRAILING TRANSACTION TRANSFORM
@@ -7834,6 +7834,10 @@ common_func_opt_item:
                                {
                                        $$ = makeDefElem("rows", (Node *)$2, @1);
                                }
+                       | SUPPORT any_name
+                               {
+                                       $$ = makeDefElem("support", (Node *)$2, @1);
+                               }
                        | FunctionSetResetClause
                                {
                                        /* we abuse the normal content of a DefElem here */
@@ -15164,6 +15168,7 @@ unreserved_keyword:
                        | STRICT_P
                        | STRIP_P
                        | SUBSCRIPTION
+                       | SUPPORT
                        | SYSID
                        | SYSTEM_P
                        | TABLES
index 3810e4a97851c39e1610099216648020f73f6152..cf5a1c6039e4730a3e401508747f1e318a1072d6 100644 (file)
@@ -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()
index 61dbd057bec4ecb9a31c95093f0b6e00e71aa692..0068e71d11a113378acea5de875d925a0bc0f626 100644 (file)
@@ -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;
index 45cd1a0664fe7cc7118f2be58c0e475ad84f2e08..1c9deebc1dda18c3f1897c2bd804fa8365c0d48d 100644 (file)
@@ -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);
index e1fbe494d5d3b0cc0eaba2fbd11f2a178a92229c..9fd1ebf3e562332112bf3f2d424099429b303970 100644 (file)
@@ -2638,6 +2638,21 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
        if (proc->prorows > 0 && proc->prorows != 1000)
                appendStringInfo(&buf, " ROWS %g", proc->prorows);
 
+       if (proc->prosupport)
+       {
+               Oid                     argtypes[1];
+
+               /*
+                * We should qualify the support function's name if it wouldn't be
+                * resolved by lookup in the current search path.
+                */
+               argtypes[0] = INTERNALOID;
+               appendStringInfo(&buf, " SUPPORT %s",
+                                                generate_function_name(proc->prosupport, 1,
+                                                                                               NIL, argtypes,
+                                                                                               false, NULL, EXPR_KIND_NONE));
+       }
+
        if (oldlen != buf.len)
                appendStringInfoChar(&buf, '\n');
 
index 7befb6a7e28df8d89afbadf3a2c3400fac30efaa..e0ef2f78616395f7e2760e71157ad4f7392d1d6a 100644 (file)
@@ -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.
  */
index 1585da0d0e123ba1952e43d075a317bfe1e6a200..fdcc62096cf7a45433ae9fce92ed85fbd6cc6e8d 100644 (file)
@@ -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);
index 5cf927e27f264880b755aac94fc88b4fdd28a9cc..c866af022f2bbc5bd0dff60830c9c2a2036fd761 100644 (file)
@@ -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);
index b6030f56ae15cbe480a7dc9b1656f47c7236f881..9edc7b9a020b5d716d69ff6abb127820bad7bd1b 100644 (file)
@@ -11446,6 +11446,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
        char       *proconfig;
        char       *procost;
        char       *prorows;
+       char       *prosupport;
        char       *proparallel;
        char       *lanname;
        char       *rettypename;
@@ -11468,7 +11469,26 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
        asPart = createPQExpBuffer();
 
        /* Fetch function-specific details */
-       if (fout->remoteVersion >= 110000)
+       if (fout->remoteVersion >= 120000)
+       {
+               /*
+                * prosupport was added in 12
+                */
+               appendPQExpBuffer(query,
+                                                 "SELECT proretset, prosrc, probin, "
+                                                 "pg_catalog.pg_get_function_arguments(oid) AS funcargs, "
+                                                 "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
+                                                 "pg_catalog.pg_get_function_result(oid) AS funcresult, "
+                                                 "array_to_string(protrftypes, ' ') AS protrftypes, "
+                                                 "prokind, provolatile, proisstrict, prosecdef, "
+                                                 "proleakproof, proconfig, procost, prorows, "
+                                                 "prosupport, proparallel, "
+                                                 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
+                                                 "FROM pg_catalog.pg_proc "
+                                                 "WHERE oid = '%u'::pg_catalog.oid",
+                                                 finfo->dobj.catId.oid);
+       }
+       else if (fout->remoteVersion >= 110000)
        {
                /*
                 * prokind was added in 11
@@ -11481,7 +11501,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
                                                  "array_to_string(protrftypes, ' ') AS protrftypes, "
                                                  "prokind, provolatile, proisstrict, prosecdef, "
                                                  "proleakproof, proconfig, procost, prorows, "
-                                                 "proparallel, "
+                                                 "'-' AS prosupport, proparallel, "
                                                  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
                                                  "FROM pg_catalog.pg_proc "
                                                  "WHERE oid = '%u'::pg_catalog.oid",
@@ -11501,7 +11521,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
                                                  "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind, "
                                                  "provolatile, proisstrict, prosecdef, "
                                                  "proleakproof, proconfig, procost, prorows, "
-                                                 "proparallel, "
+                                                 "'-' AS prosupport, proparallel, "
                                                  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
                                                  "FROM pg_catalog.pg_proc "
                                                  "WHERE oid = '%u'::pg_catalog.oid",
@@ -11521,6 +11541,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
                                                  "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind, "
                                                  "provolatile, proisstrict, prosecdef, "
                                                  "proleakproof, proconfig, procost, prorows, "
+                                                 "'-' AS prosupport, "
                                                  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
                                                  "FROM pg_catalog.pg_proc "
                                                  "WHERE oid = '%u'::pg_catalog.oid",
@@ -11539,6 +11560,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
                                                  "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind, "
                                                  "provolatile, proisstrict, prosecdef, "
                                                  "proleakproof, proconfig, procost, prorows, "
+                                                 "'-' AS prosupport, "
                                                  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
                                                  "FROM pg_catalog.pg_proc "
                                                  "WHERE oid = '%u'::pg_catalog.oid",
@@ -11559,6 +11581,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
                                                  "provolatile, proisstrict, prosecdef, "
                                                  "false AS proleakproof, "
                                                  " proconfig, procost, prorows, "
+                                                 "'-' AS prosupport, "
                                                  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
                                                  "FROM pg_catalog.pg_proc "
                                                  "WHERE oid = '%u'::pg_catalog.oid",
@@ -11573,6 +11596,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
                                                  "provolatile, proisstrict, prosecdef, "
                                                  "false AS proleakproof, "
                                                  "proconfig, procost, prorows, "
+                                                 "'-' AS prosupport, "
                                                  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
                                                  "FROM pg_catalog.pg_proc "
                                                  "WHERE oid = '%u'::pg_catalog.oid",
@@ -11587,6 +11611,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
                                                  "provolatile, proisstrict, prosecdef, "
                                                  "false AS proleakproof, "
                                                  "null AS proconfig, 0 AS procost, 0 AS prorows, "
+                                                 "'-' AS prosupport, "
                                                  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
                                                  "FROM pg_catalog.pg_proc "
                                                  "WHERE oid = '%u'::pg_catalog.oid",
@@ -11603,6 +11628,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
                                                  "provolatile, proisstrict, prosecdef, "
                                                  "false AS proleakproof, "
                                                  "null AS proconfig, 0 AS procost, 0 AS prorows, "
+                                                 "'-' AS prosupport, "
                                                  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
                                                  "FROM pg_catalog.pg_proc "
                                                  "WHERE oid = '%u'::pg_catalog.oid",
@@ -11640,6 +11666,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
        proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
        procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
        prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
+       prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
 
        if (PQfnumber(res, "proparallel") != -1)
                proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
@@ -11853,6 +11880,12 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
                strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
                appendPQExpBuffer(q, " ROWS %s", prorows);
 
+       if (strcmp(prosupport, "-") != 0)
+       {
+               /* We rely on regprocout to provide quoting and qualification */
+               appendPQExpBuffer(q, " SUPPORT %s", prosupport);
+       }
+
        if (proparallel != NULL && proparallel[0] != PROPARALLEL_UNSAFE)
        {
                if (proparallel[0] == PROPARALLEL_SAFE)
index 7eca15898e553ff493d7658fd6d439d8c63518c9..bb4ae9fde25d168f7d7030158e589c9fad8597ba 100644 (file)
@@ -1774,6 +1774,20 @@ my %tests = (
                unlike => { exclude_dump_test_schema => 1, },
        },
 
+       'CREATE FUNCTION ... SUPPORT' => {
+               create_order => 41,
+               create_sql =>
+                 'CREATE FUNCTION dump_test.func_with_support() RETURNS int LANGUAGE sql AS $$ SELECT 1 $$ SUPPORT varchar_support;',
+               regexp => qr/^
+                       \QCREATE FUNCTION dump_test.func_with_support() RETURNS integer\E
+                       \n\s+\QLANGUAGE sql SUPPORT varchar_support\E
+                       \n\s+AS\ \$\$\Q SELECT 1 \E\$\$;
+                       /xm,
+               like =>
+                 { %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
+               unlike => { exclude_dump_test_schema => 1, },
+       },
+
        'CREATE PROCEDURE dump_test.ptest1' => {
                create_order => 41,
                create_sql   => 'CREATE PROCEDURE dump_test.ptest1(a int)
@@ -1883,9 +1897,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 +2894,7 @@ my %tests = (
                                                   procost,
                                                   prorows,
                                                   provariadic,
-                                                  protransform,
+                                                  prosupport,
                                                   prokind,
                                                   prosecdef,
                                                   proleakproof,
@@ -2912,7 +2926,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.*
index 5eb4f6f172a461e7cc7f80cd16a17641ed505b7d..9233fb934e8a0dc27c8d0599631173985e00cf76 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201902081
+#define CATALOG_VERSION_NO     201902091
 
 #endif
index 93e3e16f01cc2dbf0c7dc025c527ad7a41ac9194..1f5352ce5439e904aad86849c5734a768843cd5c 100644 (file)
 { 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' },
 
 
 # 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',
 { 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',
 
 # 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' },
 
 { 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',
 { 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',
   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' },
 
   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',
   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' },
index c2bb9516dc3fe6c43cdbd122f7c02f13ac11eefe..e5270d2ff116780acbd12fde95ff7447b1f908cc 100644 (file)
@@ -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);
@@ -201,6 +201,7 @@ extern ObjectAddress ProcedureCreate(const char *procedureName,
                                List *parameterDefaults,
                                Datum trftypes,
                                Datum proconfig,
+                               Oid prosupport,
                                float4 procost,
                                float4 prorows);
 
index 3c003b06906ae8cd3c50157827db88f54ac9dae7..0d2d1889e9d5baa427ecd13b73ff7e242624e9f8 100644 (file)
@@ -506,7 +506,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 (file)
index 0000000..1f7d02b
--- /dev/null
@@ -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 */
index adeb834ce82422940cb89798ee18457c17c10853..f05444008c4680c2f671847a759dc0851e63a797 100644 (file)
@@ -387,6 +387,7 @@ PG_KEYWORD("strict", STRICT_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("strip", STRIP_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("subscription", SUBSCRIPTION, UNRESERVED_KEYWORD)
 PG_KEYWORD("substring", SUBSTRING, COL_NAME_KEYWORD)
+PG_KEYWORD("support", SUPPORT, UNRESERVED_KEYWORD)
 PG_KEYWORD("symmetric", SYMMETRIC, RESERVED_KEYWORD)
 PG_KEYWORD("sysid", SYSID, UNRESERVED_KEYWORD)
 PG_KEYWORD("system", SYSTEM_P, UNRESERVED_KEYWORD)
index f5ec9bbd7e038d64d83852361e97c9ba53f645b7..87f819e76e0640aa69e265e52a0081140d2d0083 100644 (file)
@@ -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);
 
index 0d1cc360f4ed1c29638447aff0617f9d8e328380..da7fea2d091da0204447e0a139392b543a958e42 100644 (file)
@@ -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;
index 096870233f821f411edaf1291de41b0a00354f79..132fc5af04990f0eb146efc9bc9346f792788d6d 100644 (file)
@@ -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;
index 7bb8ca912851b959c77d7473178ce44c9bcf7b6a..4db792cf2ffe47960f6ba6f16ff0e6279e08357e 100644 (file)
@@ -3050,10 +3050,9 @@ DETAIL:  System catalog modifications are currently disallowed.
 -- instead create in public first, move to catalog
 CREATE TABLE new_system_table(id serial primary key, othercol text);
 ALTER TABLE new_system_table SET SCHEMA pg_catalog;
--- XXX: it's currently impossible to move relations out of pg_catalog
 ALTER TABLE new_system_table SET SCHEMA public;
-ERROR:  cannot remove dependency on schema pg_catalog because it is a system object
--- move back, will be ignored -- already there
+ALTER TABLE new_system_table SET SCHEMA pg_catalog;
+-- will be ignored -- already there:
 ALTER TABLE new_system_table SET SCHEMA pg_catalog;
 ALTER TABLE new_system_table RENAME TO old_system_table;
 CREATE INDEX old_system_table__othercol ON old_system_table (othercol);
index 4085e451e49e4111ebc2637afebee09da35d0901..c89ec06cb43564e2514ee4d1eee0d9a3627d7267 100644 (file)
@@ -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);
index ef268d348eb8806e49f6e59de0fef3462ceaf26d..4edc8175aa21e0c69f159b78ad89bf04e9e754ea 100644 (file)
@@ -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
index 7328095b6fef6562cc3131f261a956f993ec4ee4..ce25ee044a0c6c9c1433b33751218567bb8afea9 100644 (file)
@@ -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 
index a498e4e99c80e1cfbbdfe254d32077e469e9e68e..d80643037d3b855b96a0365e345ae5ed152717d0 100644 (file)
@@ -1896,10 +1896,9 @@ CREATE TABLE pg_catalog.new_system_table();
 -- instead create in public first, move to catalog
 CREATE TABLE new_system_table(id serial primary key, othercol text);
 ALTER TABLE new_system_table SET SCHEMA pg_catalog;
-
--- XXX: it's currently impossible to move relations out of pg_catalog
 ALTER TABLE new_system_table SET SCHEMA public;
--- move back, will be ignored -- already there
+ALTER TABLE new_system_table SET SCHEMA pg_catalog;
+-- will be ignored -- already there:
 ALTER TABLE new_system_table SET SCHEMA pg_catalog;
 ALTER TABLE new_system_table RENAME TO old_system_table;
 CREATE INDEX old_system_table__othercol ON old_system_table (othercol);
index d7df322873dae941017880220f8cb64e7bd40838..fd79465f72724e2a38285cc5b23c44bcb961c7af 100644 (file)
@@ -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);
index c8291d3973af06efa2faae5395f320b3baa23ccb..dbe4a5857d9ca5dad57c68f636b226f6f72a306b 100644 (file)
@@ -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
index 8544cbe62e8327cad7a8394e9b66072e7d669d4b..e2014fc2b5e8c593f7bcbb231f354b3193583ccf 100644 (file)
@@ -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);
 
index 305454ab9ad2c0fbb1714f18bed0c7f2416b5584..e5fc3104d3b92143f64a5a0efa27b7837f1c4807 100644 (file)
@@ -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