</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>
<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>
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>
</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>
| 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>'
</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>
</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>
</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 — 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>
<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
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>
parameterDefaults, /* parameterDefaults */
PointerGetDatum(NULL), /* trftypes */
PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* no prosupport */
1, /* procost */
0); /* prorows */
procOid = myself.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,
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,
List *parameterDefaults,
Datum trftypes,
Datum proconfig,
+ Oid prosupport,
float4 procost,
float4 prorows)
{
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);
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);
List **set_items,
DefElem **cost_item,
DefElem **rows_item,
+ DefElem **support_item,
DefElem **parallel_item)
{
if (strcmp(defel->defname, "volatility") == 0)
*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)
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
ArrayType **proconfig,
float4 *procost,
float4 *prorows,
+ Oid *prosupport,
char *parallel_p)
{
ListCell *option;
List *set_items = NIL;
DefElem *cost_item = NULL;
DefElem *rows_item = NULL;
+ DefElem *support_item = NULL;
DefElem *parallel_item = NULL;
foreach(option, options)
&set_items,
&cost_item,
&rows_item,
+ &support_item,
¶llel_item))
{
/* recognized common option */
(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);
}
ArrayType *proconfig;
float4 procost;
float4 prorows;
+ Oid prosupport;
HeapTuple languageTuple;
Form_pg_language languageStruct;
List *as_clause;
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 */
&as_clause, &language, &transformDefElem,
&isWindowFunc, &volatility,
&isStrict, &security, &isLeakProof,
- &proconfig, &procost, &prorows, ¶llel);
+ &proconfig, &procost, &prorows,
+ &prosupport, ¶llel);
/* Look up the language and validate permissions */
languageTuple = SearchSysCache1(LANGNAME, PointerGetDatum(language));
parameterDefaults,
PointerGetDatum(trftypes),
PointerGetDatum(proconfig),
+ prosupport,
procost,
prorows);
}
List *set_items = NIL;
DefElem *cost_item = NULL;
DefElem *rows_item = NULL;
+ DefElem *support_item = NULL;
DefElem *parallel_item = NULL;
ObjectAddress address;
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);
&set_items,
&cost_item,
&rows_item,
+ &support_item,
¶llel_item) == false)
elog(ERROR, "option \"%s\" not recognized", defel->defname);
}
(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;
InvokeObjectPostAlterHook(ProcedureRelationId, funcOid, 0);
- ObjectAddressSet(address, ProcedureRelationId, funcOid);
-
table_close(rel, NoLock);
heap_freetuple(tup);
NIL,
PointerGetDatum(NULL),
PointerGetDatum(NULL),
+ InvalidOid,
1,
0);
handlerOid = tmpAddr.objectId;
NIL,
PointerGetDatum(NULL),
PointerGetDatum(NULL),
+ InvalidOid,
1,
0);
inlineOid = tmpAddr.objectId;
NIL,
PointerGetDatum(NULL),
PointerGetDatum(NULL),
+ InvalidOid,
1,
0);
valOid = tmpAddr.objectId;
NIL, /* parameterDefaults */
PointerGetDatum(NULL), /* trftypes */
PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
1.0, /* procost */
0.0); /* prorows */
#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"
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;
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)
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
{
$$ = makeDefElem("rows", (Node *)$2, @1);
}
+ | SUPPORT any_name
+ {
+ $$ = makeDefElem("support", (Node *)$2, @1);
+ }
| FunctionSetResetClause
{
/* we abuse the normal content of a DefElem here */
| STRICT_P
| STRIP_P
| SUBSCRIPTION
+ | SUPPORT
| SYSID
| SYSTEM_P
| TABLES
#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"
}
-/* 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()
}
/*
- * 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;
#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"
/*
- * 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);
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');
#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"
}
-/* 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()
}
-/* 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);
* 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
}
-/* 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
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.
*/
#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"
}
/*
- * 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);
#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"
/*
- * 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);
char *proconfig;
char *procost;
char *prorows;
+ char *prosupport;
char *proparallel;
char *lanname;
char *rettypename;
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
"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",
"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",
"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",
"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",
"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",
"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",
"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",
"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",
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"));
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)
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)
'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, },
},
procost,
prorows,
provariadic,
- protransform,
+ prosupport,
prokind,
prosecdef,
proleakproof,
\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.*
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201902081
+#define CATALOG_VERSION_NO 201902091
#endif
{ 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' },
/* 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);
List *parameterDefaults,
Datum trftypes,
Datum proconfig,
+ Oid prosupport,
float4 procost,
float4 prorows);
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;
/*
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
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)
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);
-- 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;
-- 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;
-- 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);
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);
------+-------------
(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
-----+---------+-------------+----------------+-------------
(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
-- 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);
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);
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
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);
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