Create the infrastructure for planner support functions.
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
This commit is contained in:
parent
1a8d5afb0d
commit
1fb57af920
@ -5146,11 +5146,11 @@ SCRAM-SHA-256$<replaceable><iteration count></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>
|
||||
|
@ -4521,6 +4521,13 @@
|
||||
<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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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 — 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>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
¶llel_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, ¶llel);
|
||||
&proconfig, &procost, &prorows,
|
||||
&prosupport, ¶llel);
|
||||
|
||||
/* 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,
|
||||
¶llel_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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
typmod = (Node *) lsecond(expr->args);
|
||||
|
||||
if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull)
|
||||
if (IsA(rawreq, SupportRequestSimplify))
|
||||
{
|
||||
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;
|
||||
SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
|
||||
FuncExpr *expr = req->fcall;
|
||||
Node *typmod;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
Assert(list_length(expr->args) >= 2);
|
||||
|
||||
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_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);
|
||||
|
@ -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');
|
||||
|
||||
|
@ -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);
|
||||
|
||||
typmod = (Node *) lsecond(expr->args);
|
||||
|
||||
if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull)
|
||||
if (IsA(rawreq, SupportRequestSimplify))
|
||||
{
|
||||
Node *source = (Node *) linitial(expr->args);
|
||||
int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
|
||||
bool noop;
|
||||
SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
|
||||
FuncExpr *expr = req->fcall;
|
||||
Node *typmod;
|
||||
|
||||
if (new_typmod < 0)
|
||||
noop = true;
|
||||
else
|
||||
Assert(list_length(expr->args) >= 2);
|
||||
|
||||
typmod = (Node *) lsecond(expr->args);
|
||||
|
||||
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;
|
||||
Node *source = (Node *) linitial(expr->args);
|
||||
int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
|
||||
bool noop;
|
||||
|
||||
old_least_field = intervaltypmodleastfield(old_typmod);
|
||||
new_least_field = intervaltypmodleastfield(new_typmod);
|
||||
if (old_typmod < 0)
|
||||
old_precis = INTERVAL_FULL_PRECISION;
|
||||
if (new_typmod < 0)
|
||||
noop = true;
|
||||
else
|
||||
old_precis = INTERVAL_PRECISION(old_typmod);
|
||||
new_precis = INTERVAL_PRECISION(new_typmod);
|
||||
{
|
||||
int32 old_typmod = exprTypmod(source);
|
||||
int old_least_field;
|
||||
int new_least_field;
|
||||
int old_precis;
|
||||
int new_precis;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
||||
typmod = (Node *) lsecond(expr->args);
|
||||
|
||||
if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull)
|
||||
if (IsA(rawreq, SupportRequestSimplify))
|
||||
{
|
||||
Node *source = (Node *) linitial(expr->args);
|
||||
int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
|
||||
int32 old_max = exprTypmod(source);
|
||||
int32 new_max = new_typmod;
|
||||
SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
|
||||
FuncExpr *expr = req->fcall;
|
||||
Node *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);
|
||||
Assert(list_length(expr->args) >= 2);
|
||||
|
||||
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);
|
||||
|
@ -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);
|
||||
|
||||
typmod = (Node *) lsecond(expr->args);
|
||||
|
||||
if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull)
|
||||
if (IsA(rawreq, SupportRequestSimplify))
|
||||
{
|
||||
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;
|
||||
SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
|
||||
FuncExpr *expr = req->fcall;
|
||||
Node *typmod;
|
||||
|
||||
if (new_typmod < 0 || (old_typmod >= 0 && old_max <= new_max))
|
||||
ret = relabel_to_typmod(source, new_typmod);
|
||||
Assert(list_length(expr->args) >= 2);
|
||||
|
||||
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);
|
||||
|
@ -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)
|
||||
|
@ -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.*
|
||||
|
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 201902081
|
||||
#define CATALOG_VERSION_NO 201902091
|
||||
|
||||
#endif
|
||||
|
@ -1326,11 +1326,11 @@
|
||||
{ oid => '668', descr => 'adjust char() to typmod length',
|
||||
proname => 'bpchar', prorettype => 'bpchar',
|
||||
proargtypes => 'bpchar int4 bool', prosrc => 'bpchar' },
|
||||
{ oid => '3097', descr => 'transform a varchar length coercion',
|
||||
proname => 'varchar_transform', prorettype => 'internal',
|
||||
proargtypes => 'internal', prosrc => 'varchar_transform' },
|
||||
{ oid => '3097', descr => 'planner support for varchar length coercion',
|
||||
proname => 'varchar_support', prorettype => 'internal',
|
||||
proargtypes => 'internal', prosrc => 'varchar_support' },
|
||||
{ oid => '669', descr => 'adjust varchar() to typmod length',
|
||||
proname => 'varchar', protransform => 'varchar_transform',
|
||||
proname => 'varchar', prosupport => 'varchar_support',
|
||||
prorettype => 'varchar', proargtypes => 'varchar int4 bool',
|
||||
prosrc => 'varchar' },
|
||||
|
||||
@ -1954,13 +1954,9 @@
|
||||
|
||||
# OIDS 1000 - 1999
|
||||
|
||||
{ oid => '3994', descr => 'transform a time zone adjustment',
|
||||
proname => 'timestamp_izone_transform', prorettype => 'internal',
|
||||
proargtypes => 'internal', prosrc => 'timestamp_izone_transform' },
|
||||
{ oid => '1026', descr => 'adjust timestamp to new time zone',
|
||||
proname => 'timezone', protransform => 'timestamp_izone_transform',
|
||||
prorettype => 'timestamp', proargtypes => 'interval timestamptz',
|
||||
prosrc => 'timestamptz_izone' },
|
||||
proname => 'timezone', prorettype => 'timestamp',
|
||||
proargtypes => 'interval timestamptz', prosrc => 'timestamptz_izone' },
|
||||
|
||||
{ oid => '1031', descr => 'I/O',
|
||||
proname => 'aclitemin', provolatile => 's', prorettype => 'aclitem',
|
||||
@ -2190,13 +2186,9 @@
|
||||
{ oid => '1158', descr => 'convert UNIX epoch to timestamptz',
|
||||
proname => 'to_timestamp', prorettype => 'timestamptz',
|
||||
proargtypes => 'float8', prosrc => 'float8_timestamptz' },
|
||||
{ oid => '3995', descr => 'transform a time zone adjustment',
|
||||
proname => 'timestamp_zone_transform', prorettype => 'internal',
|
||||
proargtypes => 'internal', prosrc => 'timestamp_zone_transform' },
|
||||
{ oid => '1159', descr => 'adjust timestamp to new time zone',
|
||||
proname => 'timezone', protransform => 'timestamp_zone_transform',
|
||||
prorettype => 'timestamp', proargtypes => 'text timestamptz',
|
||||
prosrc => 'timestamptz_zone' },
|
||||
proname => 'timezone', prorettype => 'timestamp',
|
||||
proargtypes => 'text timestamptz', prosrc => 'timestamptz_zone' },
|
||||
|
||||
{ oid => '1160', descr => 'I/O',
|
||||
proname => 'interval_in', provolatile => 's', prorettype => 'interval',
|
||||
@ -2301,11 +2293,11 @@
|
||||
|
||||
# OIDS 1200 - 1299
|
||||
|
||||
{ oid => '3918', descr => 'transform an interval length coercion',
|
||||
proname => 'interval_transform', prorettype => 'internal',
|
||||
proargtypes => 'internal', prosrc => 'interval_transform' },
|
||||
{ oid => '3918', descr => 'planner support for interval length coercion',
|
||||
proname => 'interval_support', prorettype => 'internal',
|
||||
proargtypes => 'internal', prosrc => 'interval_support' },
|
||||
{ oid => '1200', descr => 'adjust interval precision',
|
||||
proname => 'interval', protransform => 'interval_transform',
|
||||
proname => 'interval', prosupport => 'interval_support',
|
||||
prorettype => 'interval', proargtypes => 'interval int4',
|
||||
prosrc => 'interval_scale' },
|
||||
|
||||
@ -3713,13 +3705,12 @@
|
||||
{ oid => '1685', descr => 'adjust bit() to typmod length',
|
||||
proname => 'bit', prorettype => 'bit', proargtypes => 'bit int4 bool',
|
||||
prosrc => 'bit' },
|
||||
{ oid => '3158', descr => 'transform a varbit length coercion',
|
||||
proname => 'varbit_transform', prorettype => 'internal',
|
||||
proargtypes => 'internal', prosrc => 'varbit_transform' },
|
||||
{ oid => '3158', descr => 'planner support for varbit length coercion',
|
||||
proname => 'varbit_support', prorettype => 'internal',
|
||||
proargtypes => 'internal', prosrc => 'varbit_support' },
|
||||
{ oid => '1687', descr => 'adjust varbit() to typmod length',
|
||||
proname => 'varbit', protransform => 'varbit_transform',
|
||||
prorettype => 'varbit', proargtypes => 'varbit int4 bool',
|
||||
prosrc => 'varbit' },
|
||||
proname => 'varbit', prosupport => 'varbit_support', prorettype => 'varbit',
|
||||
proargtypes => 'varbit int4 bool', prosrc => 'varbit' },
|
||||
|
||||
{ oid => '1698', descr => 'position of sub-bitstring',
|
||||
proname => 'position', prorettype => 'int4', proargtypes => 'bit bit',
|
||||
@ -4081,11 +4072,11 @@
|
||||
{ oid => '2918', descr => 'I/O typmod',
|
||||
proname => 'numerictypmodout', prorettype => 'cstring', proargtypes => 'int4',
|
||||
prosrc => 'numerictypmodout' },
|
||||
{ oid => '3157', descr => 'transform a numeric length coercion',
|
||||
proname => 'numeric_transform', prorettype => 'internal',
|
||||
proargtypes => 'internal', prosrc => 'numeric_transform' },
|
||||
{ oid => '3157', descr => 'planner support for numeric length coercion',
|
||||
proname => 'numeric_support', prorettype => 'internal',
|
||||
proargtypes => 'internal', prosrc => 'numeric_support' },
|
||||
{ oid => '1703', descr => 'adjust numeric to typmod precision/scale',
|
||||
proname => 'numeric', protransform => 'numeric_transform',
|
||||
proname => 'numeric', prosupport => 'numeric_support',
|
||||
prorettype => 'numeric', proargtypes => 'numeric int4', prosrc => 'numeric' },
|
||||
{ oid => '1704',
|
||||
proname => 'numeric_abs', prorettype => 'numeric', proargtypes => 'numeric',
|
||||
@ -5448,15 +5439,15 @@
|
||||
proname => 'bytea_sortsupport', prorettype => 'void',
|
||||
proargtypes => 'internal', prosrc => 'bytea_sortsupport' },
|
||||
|
||||
{ oid => '3917', descr => 'transform a timestamp length coercion',
|
||||
proname => 'timestamp_transform', prorettype => 'internal',
|
||||
proargtypes => 'internal', prosrc => 'timestamp_transform' },
|
||||
{ oid => '3944', descr => 'transform a time length coercion',
|
||||
proname => 'time_transform', prorettype => 'internal',
|
||||
proargtypes => 'internal', prosrc => 'time_transform' },
|
||||
{ oid => '3917', descr => 'planner support for timestamp length coercion',
|
||||
proname => 'timestamp_support', prorettype => 'internal',
|
||||
proargtypes => 'internal', prosrc => 'timestamp_support' },
|
||||
{ oid => '3944', descr => 'planner support for time length coercion',
|
||||
proname => 'time_support', prorettype => 'internal',
|
||||
proargtypes => 'internal', prosrc => 'time_support' },
|
||||
|
||||
{ oid => '1961', descr => 'adjust timestamp precision',
|
||||
proname => 'timestamp', protransform => 'timestamp_transform',
|
||||
proname => 'timestamp', prosupport => 'timestamp_support',
|
||||
prorettype => 'timestamp', proargtypes => 'timestamp int4',
|
||||
prosrc => 'timestamp_scale' },
|
||||
|
||||
@ -5468,14 +5459,14 @@
|
||||
prosrc => 'oidsmaller' },
|
||||
|
||||
{ oid => '1967', descr => 'adjust timestamptz precision',
|
||||
proname => 'timestamptz', protransform => 'timestamp_transform',
|
||||
proname => 'timestamptz', prosupport => 'timestamp_support',
|
||||
prorettype => 'timestamptz', proargtypes => 'timestamptz int4',
|
||||
prosrc => 'timestamptz_scale' },
|
||||
{ oid => '1968', descr => 'adjust time precision',
|
||||
proname => 'time', protransform => 'time_transform', prorettype => 'time',
|
||||
proname => 'time', prosupport => 'time_support', prorettype => 'time',
|
||||
proargtypes => 'time int4', prosrc => 'time_scale' },
|
||||
{ oid => '1969', descr => 'adjust time with time zone precision',
|
||||
proname => 'timetz', protransform => 'time_transform', prorettype => 'timetz',
|
||||
proname => 'timetz', prosupport => 'time_support', prorettype => 'timetz',
|
||||
proargtypes => 'timetz int4', prosrc => 'timetz_scale' },
|
||||
|
||||
{ oid => '2003',
|
||||
@ -5662,13 +5653,11 @@
|
||||
prosrc => 'select pg_catalog.age(cast(current_date as timestamp without time zone), $1)' },
|
||||
|
||||
{ oid => '2069', descr => 'adjust timestamp to new time zone',
|
||||
proname => 'timezone', protransform => 'timestamp_zone_transform',
|
||||
prorettype => 'timestamptz', proargtypes => 'text timestamp',
|
||||
prosrc => 'timestamp_zone' },
|
||||
proname => 'timezone', prorettype => 'timestamptz',
|
||||
proargtypes => 'text timestamp', prosrc => 'timestamp_zone' },
|
||||
{ oid => '2070', descr => 'adjust timestamp to new time zone',
|
||||
proname => 'timezone', protransform => 'timestamp_izone_transform',
|
||||
prorettype => 'timestamptz', proargtypes => 'interval timestamp',
|
||||
prosrc => 'timestamp_izone' },
|
||||
proname => 'timezone', prorettype => 'timestamptz',
|
||||
proargtypes => 'interval timestamp', prosrc => 'timestamp_izone' },
|
||||
{ oid => '2071',
|
||||
proname => 'date_pl_interval', prorettype => 'timestamp',
|
||||
proargtypes => 'date interval', prosrc => 'date_pl_interval' },
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
/*
|
||||
|
70
src/include/nodes/supportnodes.h
Normal file
70
src/include/nodes/supportnodes.h
Normal file
@ -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 */
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user