From 1fb57af92069ee104c09e2016af9e0e620681be3 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 9 Feb 2019 18:08:48 -0500 Subject: [PATCH] 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 --- doc/src/sgml/catalogs.sgml | 6 +- doc/src/sgml/keywords.sgml | 7 + doc/src/sgml/ref/alter_function.sgml | 19 +++ doc/src/sgml/ref/create_function.sgml | 14 ++ doc/src/sgml/xfunc.sgml | 120 ++++++++++----- doc/src/sgml/xoper.sgml | 12 ++ src/backend/catalog/pg_aggregate.c | 1 + src/backend/catalog/pg_depend.c | 52 +++++-- src/backend/catalog/pg_proc.c | 12 +- src/backend/commands/functioncmds.c | 88 ++++++++++- src/backend/commands/proclang.c | 3 + src/backend/commands/typecmds.c | 1 + src/backend/optimizer/util/clauses.c | 23 ++- src/backend/parser/gram.y | 7 +- src/backend/utils/adt/date.c | 23 ++- src/backend/utils/adt/datetime.c | 17 ++- src/backend/utils/adt/numeric.c | 69 +++++---- src/backend/utils/adt/ruleutils.c | 15 ++ src/backend/utils/adt/timestamp.c | 138 +++++++++--------- src/backend/utils/adt/varbit.c | 48 +++--- src/backend/utils/adt/varchar.c | 48 +++--- src/bin/pg_dump/pg_dump.c | 39 ++++- src/bin/pg_dump/t/002_pg_dump.pl | 22 ++- src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.dat | 81 +++++----- src/include/catalog/pg_proc.h | 5 +- src/include/nodes/nodes.h | 3 +- src/include/nodes/supportnodes.h | 70 +++++++++ src/include/parser/kwlist.h | 1 + src/include/utils/datetime.h | 2 +- .../expected/create_transform.out | 2 +- .../test_ddl_deparse/sql/create_transform.sql | 2 +- src/test/regress/expected/alter_table.out | 5 +- src/test/regress/expected/object_address.out | 2 +- src/test/regress/expected/oidjoins.out | 10 +- src/test/regress/expected/opr_sanity.out | 4 +- src/test/regress/sql/alter_table.sql | 5 +- src/test/regress/sql/object_address.sql | 2 +- src/test/regress/sql/oidjoins.sql | 6 +- src/test/regress/sql/opr_sanity.sql | 4 +- src/tools/findoidjoins/README | 2 +- 41 files changed, 698 insertions(+), 294 deletions(-) create mode 100644 src/include/nodes/supportnodes.h diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index af4d0625ea..6dd0700da7 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5146,11 +5146,11 @@ SCRAM-SHA-256$<iteration count>:&l - protransform + prosupport regproc pg_proc.oid - Calls to this function can be simplified by this other function - (see ) + Optional planner support function for this function + (see ) diff --git a/doc/src/sgml/keywords.sgml b/doc/src/sgml/keywords.sgml index a37d0b756b..fa32a88b8e 100644 --- a/doc/src/sgml/keywords.sgml +++ b/doc/src/sgml/keywords.sgml @@ -4521,6 +4521,13 @@ reserved reserved + + SUPPORT + non-reserved + + + + SYMMETRIC reserved diff --git a/doc/src/sgml/ref/alter_function.sgml b/doc/src/sgml/ref/alter_function.sgml index d8747e0748..03ffa5945a 100644 --- a/doc/src/sgml/ref/alter_function.sgml +++ b/doc/src/sgml/ref/alter_function.sgml @@ -40,6 +40,7 @@ ALTER FUNCTION name [ ( [ [ execution_cost ROWS result_rows + SUPPORT support_function SET configuration_parameter { TO | = } { value | DEFAULT } SET configuration_parameter FROM CURRENT RESET configuration_parameter @@ -248,6 +249,24 @@ ALTER FUNCTION name [ ( [ [ support_function + + + + Set or change the planner support function to use for this function. + See for details. You must be + superuser to use this option. + + + + This option cannot be used to remove the support function altogether, + since it must name a new support function. Use CREATE OR + REPLACE FUNCTION if you need to do that. + + + + configuration_parameter value diff --git a/doc/src/sgml/ref/create_function.sgml b/doc/src/sgml/ref/create_function.sgml index 4072543184..dd6a2f7304 100644 --- a/doc/src/sgml/ref/create_function.sgml +++ b/doc/src/sgml/ref/create_function.sgml @@ -33,6 +33,7 @@ CREATE [ OR REPLACE ] FUNCTION | PARALLEL { UNSAFE | RESTRICTED | SAFE } | COST execution_cost | ROWS result_rows + | SUPPORT support_function | SET configuration_parameter { TO value | = value | FROM CURRENT } | AS 'definition' | AS 'obj_file', 'link_symbol' @@ -477,6 +478,19 @@ CREATE [ OR REPLACE ] FUNCTION + + SUPPORT support_function + + + + The name (optionally schema-qualified) of a planner support + function to use for this function. See + for details. + You must be superuser to use this option. + + + + configuration_parameter value diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index e18272c33a..d70aa6eed7 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -3241,40 +3241,6 @@ CREATE FUNCTION make_array(anyelement) RETURNS anyarray - - Transform Functions - - - Some function calls can be simplified during planning based on - properties specific to the function. For example, - int4mul(n, 1) could be simplified to just n. - To define such function-specific optimizations, write a - transform function and place its OID in the - protransform field of the primary function's - pg_proc entry. The transform function must have the SQL - signature protransform(internal) RETURNS internal. The - argument, actually FuncExpr *, 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 NULL - pointer (not a SQL null). - - - - We make no guarantee that PostgreSQL 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. - - - - 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. - - - Shared Memory and LWLocks @@ -3388,3 +3354,89 @@ if (!ptr) + + + Function Optimization Information + + + optimization information + for functions + + + + By default, a function is just a black box 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. + + + + Some basic facts can be supplied by declarative annotations provided in + the command. Most important of + these is the function's volatility + category (IMMUTABLE, STABLE, + or VOLATILE); one should always be careful to + specify this correctly when defining a function. + The parallel safety property (PARALLEL + UNSAFE, PARALLEL RESTRICTED, or + PARALLEL SAFE) 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. + + + + It is also possible to attach a planner support + function to a SQL-callable function (called + its target function), 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. + + + + A planner support function must have the SQL signature + +supportfn(internal) returns internal + + It is attached to its target function by specifying + the SUPPORT clause when creating the target function. + + + + The details of the API for planner support functions can be found in + file src/include/nodes/supportnodes.h in the + PostgreSQL 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. + + + + Some function calls can be simplified during planning based on + properties specific to the function. For example, + int4mul(n, 1) could be simplified to + just n. This type of transformation can be + performed by a planner support function, by having it implement + the SupportRequestSimplify 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, n * 1 would also be simplified to + n. + (But note that this is just an example; this particular + optimization is not actually performed by + standard PostgreSQL.) + We make no guarantee that PostgreSQL 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. + + diff --git a/doc/src/sgml/xoper.sgml b/doc/src/sgml/xoper.sgml index 2f5560ac50..260e43c645 100644 --- a/doc/src/sgml/xoper.sgml +++ b/doc/src/sgml/xoper.sgml @@ -78,6 +78,11 @@ SELECT (a + b) AS c FROM test_complex; Operator Optimization Information + + optimization information + for operators + + A PostgreSQL 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. + + 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 for more information. + + <literal>COMMUTATOR</literal> diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index cc3806e85d..19e3171bf7 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -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; diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index 2b8f651c99..23b01f841e 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -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, diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index db780616e6..557e0ea1f1 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -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); diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 9a2f1a85b4..4f62e48d98 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -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); diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index c2e9e41c07..59c4e8dfd0 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -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; diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index fa7161ef9d..448926db12 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -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 */ diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index d7ff17c363..002c29a5f5 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -32,6 +32,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "nodes/supportnodes.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/optimizer.h" @@ -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) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index c1faf4152c..ef6bbe35d7 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -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 diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 3810e4a978..cf5a1c6039 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -24,6 +24,7 @@ #include "access/xact.h" #include "libpq/pqformat.h" #include "miscadmin.h" +#include "nodes/supportnodes.h" #include "parser/scansup.h" #include "utils/array.h" #include "utils/builtins.h" @@ -1341,15 +1342,25 @@ make_time(PG_FUNCTION_ARGS) } -/* time_transform() - * Flatten calls to time_scale() and timetz_scale() that solely represent - * increases in allowed precision. +/* time_support() + * + * Planner support function for the time_scale() and timetz_scale() + * length coercion functions (we need not distinguish them here). */ Datum -time_transform(PG_FUNCTION_ARGS) +time_support(PG_FUNCTION_ARGS) { - PG_RETURN_POINTER(TemporalTransform(MAX_TIME_PRECISION, - (Node *) PG_GETARG_POINTER(0))); + Node *rawreq = (Node *) PG_GETARG_POINTER(0); + Node *ret = NULL; + + if (IsA(rawreq, SupportRequestSimplify)) + { + SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq; + + ret = TemporalSimplify(MAX_TIME_PRECISION, (Node *) req->fcall); + } + + PG_RETURN_POINTER(ret); } /* time_scale() diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 61dbd057be..0068e71d11 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -4462,16 +4462,23 @@ CheckDateTokenTables(void) } /* - * Common code for temporal protransform functions. Types time, timetz, - * timestamp and timestamptz each have a range of allowed precisions. An - * unspecified precision is rigorously equivalent to the highest specifiable - * precision. + * Common code for temporal prosupport functions: simplify, if possible, + * a call to a temporal type's length-coercion function. + * + * Types time, timetz, timestamp and timestamptz each have a range of allowed + * precisions. An unspecified precision is rigorously equivalent to the + * highest specifiable precision. We can replace the function call with a + * no-op RelabelType if it is coercing to the same or higher precision as the + * input is known to have. + * + * The input Node is always a FuncExpr, but to reduce the #include footprint + * of datetime.h, we declare it as Node *. * * Note: timestamp_scale throws an error when the typmod is out of range, but * we can't get there from a cast: our typmodin will have caught it already. */ Node * -TemporalTransform(int32 max_precis, Node *node) +TemporalSimplify(int32 max_precis, Node *node) { FuncExpr *expr = castNode(FuncExpr, node); Node *ret = NULL; diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 45cd1a0664..1c9deebc1d 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -34,6 +34,7 @@ #include "libpq/pqformat.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "nodes/supportnodes.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/float.h" @@ -890,45 +891,53 @@ numeric_send(PG_FUNCTION_ARGS) /* - * numeric_transform() - + * numeric_support() * - * Flatten calls to numeric's length coercion function that solely represent - * increases in allowable precision. Scale changes mutate every datum, so - * they are unoptimizable. Some values, e.g. 1E-1001, can only fit into an - * unconstrained numeric, so a change from an unconstrained numeric to any - * constrained numeric is also unoptimizable. + * Planner support function for the numeric() length coercion function. + * + * Flatten calls that solely represent increases in allowable precision. + * Scale changes mutate every datum, so they are unoptimizable. Some values, + * e.g. 1E-1001, can only fit into an unconstrained numeric, so a change from + * an unconstrained numeric to any constrained numeric is also unoptimizable. */ Datum -numeric_transform(PG_FUNCTION_ARGS) +numeric_support(PG_FUNCTION_ARGS) { - FuncExpr *expr = castNode(FuncExpr, PG_GETARG_POINTER(0)); + Node *rawreq = (Node *) PG_GETARG_POINTER(0); Node *ret = NULL; - Node *typmod; - Assert(list_length(expr->args) >= 2); + if (IsA(rawreq, SupportRequestSimplify)) + { + SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq; + FuncExpr *expr = req->fcall; + Node *typmod; - typmod = (Node *) lsecond(expr->args); + Assert(list_length(expr->args) >= 2); - if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull) - { - Node *source = (Node *) linitial(expr->args); - int32 old_typmod = exprTypmod(source); - int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue); - int32 old_scale = (old_typmod - VARHDRSZ) & 0xffff; - int32 new_scale = (new_typmod - VARHDRSZ) & 0xffff; - int32 old_precision = (old_typmod - VARHDRSZ) >> 16 & 0xffff; - int32 new_precision = (new_typmod - VARHDRSZ) >> 16 & 0xffff; + typmod = (Node *) lsecond(expr->args); - /* - * If new_typmod < VARHDRSZ, the destination is unconstrained; that's - * always OK. If old_typmod >= VARHDRSZ, the source is constrained, - * and we're OK if the scale is unchanged and the precision is not - * decreasing. See further notes in function header comment. - */ - if (new_typmod < (int32) VARHDRSZ || - (old_typmod >= (int32) VARHDRSZ && - new_scale == old_scale && new_precision >= old_precision)) - ret = relabel_to_typmod(source, new_typmod); + if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull) + { + Node *source = (Node *) linitial(expr->args); + int32 old_typmod = exprTypmod(source); + int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue); + int32 old_scale = (old_typmod - VARHDRSZ) & 0xffff; + int32 new_scale = (new_typmod - VARHDRSZ) & 0xffff; + int32 old_precision = (old_typmod - VARHDRSZ) >> 16 & 0xffff; + int32 new_precision = (new_typmod - VARHDRSZ) >> 16 & 0xffff; + + /* + * If new_typmod < VARHDRSZ, the destination is unconstrained; + * that's always OK. If old_typmod >= VARHDRSZ, the source is + * constrained, and we're OK if the scale is unchanged and the + * precision is not decreasing. See further notes in function + * header comment. + */ + if (new_typmod < (int32) VARHDRSZ || + (old_typmod >= (int32) VARHDRSZ && + new_scale == old_scale && new_precision >= old_precision)) + ret = relabel_to_typmod(source, new_typmod); + } } PG_RETURN_POINTER(ret); diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index e1fbe494d5..9fd1ebf3e5 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -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'); diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 7befb6a7e2..e0ef2f7861 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -29,6 +29,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "nodes/supportnodes.h" #include "parser/scansup.h" #include "utils/array.h" #include "utils/builtins.h" @@ -297,15 +298,26 @@ timestamptypmodout(PG_FUNCTION_ARGS) } -/* timestamp_transform() - * Flatten calls to timestamp_scale() and timestamptz_scale() that solely - * represent increases in allowed precision. +/* + * timestamp_support() + * + * Planner support function for the timestamp_scale() and timestamptz_scale() + * length coercion functions (we need not distinguish them here). */ Datum -timestamp_transform(PG_FUNCTION_ARGS) +timestamp_support(PG_FUNCTION_ARGS) { - PG_RETURN_POINTER(TemporalTransform(MAX_TIMESTAMP_PRECISION, - (Node *) PG_GETARG_POINTER(0))); + Node *rawreq = (Node *) PG_GETARG_POINTER(0); + Node *ret = NULL; + + if (IsA(rawreq, SupportRequestSimplify)) + { + SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq; + + ret = TemporalSimplify(MAX_TIMESTAMP_PRECISION, (Node *) req->fcall); + } + + PG_RETURN_POINTER(ret); } /* timestamp_scale() @@ -1235,59 +1247,69 @@ intervaltypmodleastfield(int32 typmod) } -/* interval_transform() +/* + * interval_support() + * + * Planner support function for interval_scale(). + * * Flatten superfluous calls to interval_scale(). The interval typmod is * complex to permit accepting and regurgitating all SQL standard variations. * For truncation purposes, it boils down to a single, simple granularity. */ Datum -interval_transform(PG_FUNCTION_ARGS) +interval_support(PG_FUNCTION_ARGS) { - FuncExpr *expr = castNode(FuncExpr, PG_GETARG_POINTER(0)); + Node *rawreq = (Node *) PG_GETARG_POINTER(0); Node *ret = NULL; - Node *typmod; - Assert(list_length(expr->args) >= 2); + if (IsA(rawreq, SupportRequestSimplify)) + { + SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq; + FuncExpr *expr = req->fcall; + Node *typmod; - typmod = (Node *) lsecond(expr->args); + Assert(list_length(expr->args) >= 2); - if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull) - { - Node *source = (Node *) linitial(expr->args); - int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue); - bool noop; + typmod = (Node *) lsecond(expr->args); - if (new_typmod < 0) - noop = true; - else + if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull) { - int32 old_typmod = exprTypmod(source); - int old_least_field; - int new_least_field; - int old_precis; - int new_precis; - - old_least_field = intervaltypmodleastfield(old_typmod); - new_least_field = intervaltypmodleastfield(new_typmod); - if (old_typmod < 0) - old_precis = INTERVAL_FULL_PRECISION; + Node *source = (Node *) linitial(expr->args); + int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue); + bool noop; + + if (new_typmod < 0) + noop = true; else - old_precis = INTERVAL_PRECISION(old_typmod); - new_precis = INTERVAL_PRECISION(new_typmod); - - /* - * Cast is a no-op if least field stays the same or decreases - * while precision stays the same or increases. But precision, - * which is to say, sub-second precision, only affects ranges that - * include SECOND. - */ - noop = (new_least_field <= old_least_field) && - (old_least_field > 0 /* SECOND */ || - new_precis >= MAX_INTERVAL_PRECISION || - new_precis >= old_precis); + { + int32 old_typmod = exprTypmod(source); + int old_least_field; + int new_least_field; + int old_precis; + int new_precis; + + old_least_field = intervaltypmodleastfield(old_typmod); + new_least_field = intervaltypmodleastfield(new_typmod); + if (old_typmod < 0) + old_precis = INTERVAL_FULL_PRECISION; + else + old_precis = INTERVAL_PRECISION(old_typmod); + new_precis = INTERVAL_PRECISION(new_typmod); + + /* + * Cast is a no-op if least field stays the same or decreases + * while precision stays the same or increases. But + * precision, which is to say, sub-second precision, only + * affects ranges that include SECOND. + */ + noop = (new_least_field <= old_least_field) && + (old_least_field > 0 /* SECOND */ || + new_precis >= MAX_INTERVAL_PRECISION || + new_precis >= old_precis); + } + if (noop) + ret = relabel_to_typmod(source, new_typmod); } - if (noop) - ret = relabel_to_typmod(source, new_typmod); } PG_RETURN_POINTER(ret); @@ -1359,7 +1381,7 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod) * can't do it consistently. (We cannot enforce a range limit on the * highest expected field, since we do not have any equivalent of * SQL's .) If we ever decide to - * revisit this, interval_transform will likely require adjusting. + * revisit this, interval_support will likely require adjusting. * * Note: before PG 8.4 we interpreted a limited set of fields as * actually causing a "modulo" operation on a given value, potentially @@ -5020,18 +5042,6 @@ interval_part(PG_FUNCTION_ARGS) } -/* timestamp_zone_transform() - * The original optimization here caused problems by relabeling Vars that - * could be matched to index entries. It might be possible to resurrect it - * at some point by teaching the planner to be less cavalier with RelabelType - * nodes, but that will take careful analysis. - */ -Datum -timestamp_zone_transform(PG_FUNCTION_ARGS) -{ - PG_RETURN_POINTER(NULL); -} - /* timestamp_zone() * Encode timestamp type with specified time zone. * This function is just timestamp2timestamptz() except instead of @@ -5125,18 +5135,6 @@ timestamp_zone(PG_FUNCTION_ARGS) PG_RETURN_TIMESTAMPTZ(result); } -/* timestamp_izone_transform() - * The original optimization here caused problems by relabeling Vars that - * could be matched to index entries. It might be possible to resurrect it - * at some point by teaching the planner to be less cavalier with RelabelType - * nodes, but that will take careful analysis. - */ -Datum -timestamp_izone_transform(PG_FUNCTION_ARGS) -{ - PG_RETURN_POINTER(NULL); -} - /* timestamp_izone() * Encode timestamp type with specified time interval as time zone. */ diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c index 1585da0d0e..fdcc62096c 100644 --- a/src/backend/utils/adt/varbit.c +++ b/src/backend/utils/adt/varbit.c @@ -20,6 +20,7 @@ #include "common/int.h" #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" +#include "nodes/supportnodes.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -672,32 +673,41 @@ varbit_send(PG_FUNCTION_ARGS) } /* - * varbit_transform() - * Flatten calls to varbit's length coercion function that set the new maximum - * length >= the previous maximum length. We can ignore the isExplicit - * argument, since that only affects truncation cases. + * varbit_support() + * + * Planner support function for the varbit() length coercion function. + * + * Currently, the only interesting thing we can do is flatten calls that set + * the new maximum length >= the previous maximum length. We can ignore the + * isExplicit argument, since that only affects truncation cases. */ Datum -varbit_transform(PG_FUNCTION_ARGS) +varbit_support(PG_FUNCTION_ARGS) { - FuncExpr *expr = castNode(FuncExpr, PG_GETARG_POINTER(0)); + Node *rawreq = (Node *) PG_GETARG_POINTER(0); Node *ret = NULL; - Node *typmod; - Assert(list_length(expr->args) >= 2); + if (IsA(rawreq, SupportRequestSimplify)) + { + SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq; + FuncExpr *expr = req->fcall; + Node *typmod; - typmod = (Node *) lsecond(expr->args); + Assert(list_length(expr->args) >= 2); - if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull) - { - Node *source = (Node *) linitial(expr->args); - int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue); - int32 old_max = exprTypmod(source); - int32 new_max = new_typmod; - - /* Note: varbit() treats typmod 0 as invalid, so we do too */ - if (new_max <= 0 || (old_max > 0 && old_max <= new_max)) - ret = relabel_to_typmod(source, new_typmod); + typmod = (Node *) lsecond(expr->args); + + if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull) + { + Node *source = (Node *) linitial(expr->args); + int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue); + int32 old_max = exprTypmod(source); + int32 new_max = new_typmod; + + /* Note: varbit() treats typmod 0 as invalid, so we do too */ + if (new_max <= 0 || (old_max > 0 && old_max <= new_max)) + ret = relabel_to_typmod(source, new_typmod); + } } PG_RETURN_POINTER(ret); diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c index 5cf927e27f..c866af022f 100644 --- a/src/backend/utils/adt/varchar.c +++ b/src/backend/utils/adt/varchar.c @@ -21,6 +21,7 @@ #include "catalog/pg_type.h" #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" +#include "nodes/supportnodes.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varlena.h" @@ -547,32 +548,41 @@ varcharsend(PG_FUNCTION_ARGS) /* - * varchar_transform() - * Flatten calls to varchar's length coercion function that set the new maximum - * length >= the previous maximum length. We can ignore the isExplicit - * argument, since that only affects truncation cases. + * varchar_support() + * + * Planner support function for the varchar() length coercion function. + * + * Currently, the only interesting thing we can do is flatten calls that set + * the new maximum length >= the previous maximum length. We can ignore the + * isExplicit argument, since that only affects truncation cases. */ Datum -varchar_transform(PG_FUNCTION_ARGS) +varchar_support(PG_FUNCTION_ARGS) { - FuncExpr *expr = castNode(FuncExpr, PG_GETARG_POINTER(0)); + Node *rawreq = (Node *) PG_GETARG_POINTER(0); Node *ret = NULL; - Node *typmod; - Assert(list_length(expr->args) >= 2); + if (IsA(rawreq, SupportRequestSimplify)) + { + SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq; + FuncExpr *expr = req->fcall; + Node *typmod; - typmod = (Node *) lsecond(expr->args); + Assert(list_length(expr->args) >= 2); - if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull) - { - Node *source = (Node *) linitial(expr->args); - int32 old_typmod = exprTypmod(source); - int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue); - int32 old_max = old_typmod - VARHDRSZ; - int32 new_max = new_typmod - VARHDRSZ; - - if (new_typmod < 0 || (old_typmod >= 0 && old_max <= new_max)) - ret = relabel_to_typmod(source, new_typmod); + typmod = (Node *) lsecond(expr->args); + + if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull) + { + Node *source = (Node *) linitial(expr->args); + int32 old_typmod = exprTypmod(source); + int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue); + int32 old_max = old_typmod - VARHDRSZ; + int32 new_max = new_typmod - VARHDRSZ; + + if (new_typmod < 0 || (old_typmod >= 0 && old_max <= new_max)) + ret = relabel_to_typmod(source, new_typmod); + } } PG_RETURN_POINTER(ret); diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index b6030f56ae..9edc7b9a02 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -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) diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 7eca15898e..bb4ae9fde2 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -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.* diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 5eb4f6f172..9233fb934e 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201902081 +#define CATALOG_VERSION_NO 201902091 #endif diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 93e3e16f01..1f5352ce54 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -1326,11 +1326,11 @@ { oid => '668', descr => 'adjust char() to typmod length', proname => 'bpchar', prorettype => 'bpchar', proargtypes => 'bpchar int4 bool', prosrc => 'bpchar' }, -{ oid => '3097', descr => 'transform a varchar length coercion', - proname => 'varchar_transform', prorettype => 'internal', - proargtypes => 'internal', prosrc => 'varchar_transform' }, +{ oid => '3097', descr => 'planner support for varchar length coercion', + proname => 'varchar_support', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'varchar_support' }, { oid => '669', descr => 'adjust varchar() to typmod length', - proname => 'varchar', protransform => 'varchar_transform', + proname => 'varchar', prosupport => 'varchar_support', prorettype => 'varchar', proargtypes => 'varchar int4 bool', prosrc => 'varchar' }, @@ -1954,13 +1954,9 @@ # OIDS 1000 - 1999 -{ oid => '3994', descr => 'transform a time zone adjustment', - proname => 'timestamp_izone_transform', prorettype => 'internal', - proargtypes => 'internal', prosrc => 'timestamp_izone_transform' }, { oid => '1026', descr => 'adjust timestamp to new time zone', - proname => 'timezone', protransform => 'timestamp_izone_transform', - prorettype => 'timestamp', proargtypes => 'interval timestamptz', - prosrc => 'timestamptz_izone' }, + proname => 'timezone', prorettype => 'timestamp', + proargtypes => 'interval timestamptz', prosrc => 'timestamptz_izone' }, { oid => '1031', descr => 'I/O', proname => 'aclitemin', provolatile => 's', prorettype => 'aclitem', @@ -2190,13 +2186,9 @@ { oid => '1158', descr => 'convert UNIX epoch to timestamptz', proname => 'to_timestamp', prorettype => 'timestamptz', proargtypes => 'float8', prosrc => 'float8_timestamptz' }, -{ oid => '3995', descr => 'transform a time zone adjustment', - proname => 'timestamp_zone_transform', prorettype => 'internal', - proargtypes => 'internal', prosrc => 'timestamp_zone_transform' }, { oid => '1159', descr => 'adjust timestamp to new time zone', - proname => 'timezone', protransform => 'timestamp_zone_transform', - prorettype => 'timestamp', proargtypes => 'text timestamptz', - prosrc => 'timestamptz_zone' }, + proname => 'timezone', prorettype => 'timestamp', + proargtypes => 'text timestamptz', prosrc => 'timestamptz_zone' }, { oid => '1160', descr => 'I/O', proname => 'interval_in', provolatile => 's', prorettype => 'interval', @@ -2301,11 +2293,11 @@ # OIDS 1200 - 1299 -{ oid => '3918', descr => 'transform an interval length coercion', - proname => 'interval_transform', prorettype => 'internal', - proargtypes => 'internal', prosrc => 'interval_transform' }, +{ oid => '3918', descr => 'planner support for interval length coercion', + proname => 'interval_support', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'interval_support' }, { oid => '1200', descr => 'adjust interval precision', - proname => 'interval', protransform => 'interval_transform', + proname => 'interval', prosupport => 'interval_support', prorettype => 'interval', proargtypes => 'interval int4', prosrc => 'interval_scale' }, @@ -3713,13 +3705,12 @@ { oid => '1685', descr => 'adjust bit() to typmod length', proname => 'bit', prorettype => 'bit', proargtypes => 'bit int4 bool', prosrc => 'bit' }, -{ oid => '3158', descr => 'transform a varbit length coercion', - proname => 'varbit_transform', prorettype => 'internal', - proargtypes => 'internal', prosrc => 'varbit_transform' }, +{ oid => '3158', descr => 'planner support for varbit length coercion', + proname => 'varbit_support', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'varbit_support' }, { oid => '1687', descr => 'adjust varbit() to typmod length', - proname => 'varbit', protransform => 'varbit_transform', - prorettype => 'varbit', proargtypes => 'varbit int4 bool', - prosrc => 'varbit' }, + proname => 'varbit', prosupport => 'varbit_support', prorettype => 'varbit', + proargtypes => 'varbit int4 bool', prosrc => 'varbit' }, { oid => '1698', descr => 'position of sub-bitstring', proname => 'position', prorettype => 'int4', proargtypes => 'bit bit', @@ -4081,11 +4072,11 @@ { oid => '2918', descr => 'I/O typmod', proname => 'numerictypmodout', prorettype => 'cstring', proargtypes => 'int4', prosrc => 'numerictypmodout' }, -{ oid => '3157', descr => 'transform a numeric length coercion', - proname => 'numeric_transform', prorettype => 'internal', - proargtypes => 'internal', prosrc => 'numeric_transform' }, +{ oid => '3157', descr => 'planner support for numeric length coercion', + proname => 'numeric_support', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'numeric_support' }, { oid => '1703', descr => 'adjust numeric to typmod precision/scale', - proname => 'numeric', protransform => 'numeric_transform', + proname => 'numeric', prosupport => 'numeric_support', prorettype => 'numeric', proargtypes => 'numeric int4', prosrc => 'numeric' }, { oid => '1704', proname => 'numeric_abs', prorettype => 'numeric', proargtypes => 'numeric', @@ -5448,15 +5439,15 @@ proname => 'bytea_sortsupport', prorettype => 'void', proargtypes => 'internal', prosrc => 'bytea_sortsupport' }, -{ oid => '3917', descr => 'transform a timestamp length coercion', - proname => 'timestamp_transform', prorettype => 'internal', - proargtypes => 'internal', prosrc => 'timestamp_transform' }, -{ oid => '3944', descr => 'transform a time length coercion', - proname => 'time_transform', prorettype => 'internal', - proargtypes => 'internal', prosrc => 'time_transform' }, +{ oid => '3917', descr => 'planner support for timestamp length coercion', + proname => 'timestamp_support', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'timestamp_support' }, +{ oid => '3944', descr => 'planner support for time length coercion', + proname => 'time_support', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'time_support' }, { oid => '1961', descr => 'adjust timestamp precision', - proname => 'timestamp', protransform => 'timestamp_transform', + proname => 'timestamp', prosupport => 'timestamp_support', prorettype => 'timestamp', proargtypes => 'timestamp int4', prosrc => 'timestamp_scale' }, @@ -5468,14 +5459,14 @@ prosrc => 'oidsmaller' }, { oid => '1967', descr => 'adjust timestamptz precision', - proname => 'timestamptz', protransform => 'timestamp_transform', + proname => 'timestamptz', prosupport => 'timestamp_support', prorettype => 'timestamptz', proargtypes => 'timestamptz int4', prosrc => 'timestamptz_scale' }, { oid => '1968', descr => 'adjust time precision', - proname => 'time', protransform => 'time_transform', prorettype => 'time', + proname => 'time', prosupport => 'time_support', prorettype => 'time', proargtypes => 'time int4', prosrc => 'time_scale' }, { oid => '1969', descr => 'adjust time with time zone precision', - proname => 'timetz', protransform => 'time_transform', prorettype => 'timetz', + proname => 'timetz', prosupport => 'time_support', prorettype => 'timetz', proargtypes => 'timetz int4', prosrc => 'timetz_scale' }, { oid => '2003', @@ -5662,13 +5653,11 @@ prosrc => 'select pg_catalog.age(cast(current_date as timestamp without time zone), $1)' }, { oid => '2069', descr => 'adjust timestamp to new time zone', - proname => 'timezone', protransform => 'timestamp_zone_transform', - prorettype => 'timestamptz', proargtypes => 'text timestamp', - prosrc => 'timestamp_zone' }, + proname => 'timezone', prorettype => 'timestamptz', + proargtypes => 'text timestamp', prosrc => 'timestamp_zone' }, { oid => '2070', descr => 'adjust timestamp to new time zone', - proname => 'timezone', protransform => 'timestamp_izone_transform', - prorettype => 'timestamptz', proargtypes => 'interval timestamp', - prosrc => 'timestamp_izone' }, + proname => 'timezone', prorettype => 'timestamptz', + proargtypes => 'interval timestamp', prosrc => 'timestamp_izone' }, { oid => '2071', proname => 'date_pl_interval', prorettype => 'timestamp', proargtypes => 'date interval', prosrc => 'date_pl_interval' }, diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c2bb9516dc..e5270d2ff1 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -53,8 +53,8 @@ CATALOG(pg_proc,1255,ProcedureRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(81,Proce /* element type of variadic array, or 0 */ Oid provariadic BKI_DEFAULT(0) BKI_LOOKUP(pg_type); - /* transforms calls to it during planning */ - regproc protransform BKI_DEFAULT(0) BKI_LOOKUP(pg_proc); + /* planner support function for this function, or 0 if none */ + regproc prosupport BKI_DEFAULT(0) BKI_LOOKUP(pg_proc); /* see PROKIND_ categories below */ char prokind BKI_DEFAULT(f); @@ -201,6 +201,7 @@ extern ObjectAddress ProcedureCreate(const char *procedureName, List *parameterDefaults, Datum trftypes, Datum proconfig, + Oid prosupport, float4 procost, float4 prorows); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 3c003b0690..0d2d1889e9 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -506,7 +506,8 @@ typedef enum NodeTag T_IndexAmRoutine, /* in access/amapi.h */ T_TsmRoutine, /* in access/tsmapi.h */ T_ForeignKeyCacheInfo, /* in utils/rel.h */ - T_CallContext /* in nodes/parsenodes.h */ + T_CallContext, /* in nodes/parsenodes.h */ + T_SupportRequestSimplify /* in nodes/supportnodes.h */ } NodeTag; /* diff --git a/src/include/nodes/supportnodes.h b/src/include/nodes/supportnodes.h new file mode 100644 index 0000000000..1f7d02b5ee --- /dev/null +++ b/src/include/nodes/supportnodes.h @@ -0,0 +1,70 @@ +/*------------------------------------------------------------------------- + * + * supportnodes.h + * Definitions for planner support functions. + * + * This file defines the API for "planner support functions", which + * are SQL functions (normally written in C) that can be attached to + * another "target" function to give the system additional knowledge + * about the target function. All the current capabilities have to do + * with planning queries that use the target function, though it is + * possible that future extensions will add functionality to be invoked + * by the parser or executor. + * + * A support function must have the SQL signature + * supportfn(internal) returns internal + * The argument is a pointer to one of the Node types defined in this file. + * The result is usually also a Node pointer, though its type depends on + * which capability is being invoked. In all cases, a NULL pointer result + * (that's PG_RETURN_POINTER(NULL), not PG_RETURN_NULL()) indicates that + * the support function cannot do anything useful for the given request. + * Support functions must return a NULL pointer, not fail, if they do not + * recognize the request node type or cannot handle the given case; this + * allows for future extensions of the set of request cases. + * + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/nodes/supportnodes.h + * + *------------------------------------------------------------------------- + */ +#ifndef SUPPORTNODES_H +#define SUPPORTNODES_H + +#include "nodes/primnodes.h" + +struct PlannerInfo; /* avoid including relation.h here */ + + +/* + * The Simplify request allows the support function to perform plan-time + * simplification of a call to its target function. For example, a varchar + * length coercion that does not decrease the allowed length of its argument + * could be replaced by a RelabelType node, or "x + 0" could be replaced by + * "x". This is invoked during the planner's constant-folding pass, so the + * function's arguments can be presumed already simplified. + * + * The planner's PlannerInfo "root" is typically not needed, but can be + * consulted if it's necessary to obtain info about Vars present in + * the given node tree. Beware that root could be NULL in some usages. + * + * "fcall" will be a FuncExpr invoking the support function's target + * function. (This is true even if the original parsetree node was an + * operator call; a FuncExpr is synthesized for this purpose.) + * + * The result should be a semantically-equivalent transformed node tree, + * or NULL if no simplification could be performed. Do *not* return or + * modify *fcall, as it isn't really a separately allocated Node. But + * it's okay to use fcall->args, or parts of it, in the result tree. + */ +typedef struct SupportRequestSimplify +{ + NodeTag type; + + struct PlannerInfo *root; /* Planner's infrastructure */ + FuncExpr *fcall; /* Function call to be simplified */ +} SupportRequestSimplify; + +#endif /* SUPPORTNODES_H */ diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index adeb834ce8..f05444008c 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.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) diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h index f5ec9bbd7e..87f819e76e 100644 --- a/src/include/utils/datetime.h +++ b/src/include/utils/datetime.h @@ -330,7 +330,7 @@ extern int DecodeUnits(int field, char *lowtoken, int *val); extern int j2day(int jd); -extern Node *TemporalTransform(int32 max_precis, Node *node); +extern Node *TemporalSimplify(int32 max_precis, Node *node); extern bool CheckDateTokenTables(void); diff --git a/src/test/modules/test_ddl_deparse/expected/create_transform.out b/src/test/modules/test_ddl_deparse/expected/create_transform.out index 0d1cc360f4..da7fea2d09 100644 --- a/src/test/modules/test_ddl_deparse/expected/create_transform.out +++ b/src/test/modules/test_ddl_deparse/expected/create_transform.out @@ -7,7 +7,7 @@ -- internal and as return argument the datatype of the transform done. -- pl/plpgsql does not authorize the use of internal as data type. CREATE TRANSFORM FOR int LANGUAGE SQL ( - FROM SQL WITH FUNCTION varchar_transform(internal), + FROM SQL WITH FUNCTION varchar_support(internal), TO SQL WITH FUNCTION int4recv(internal)); NOTICE: DDL test: type simple, tag CREATE TRANSFORM DROP TRANSFORM FOR int LANGUAGE SQL; diff --git a/src/test/modules/test_ddl_deparse/sql/create_transform.sql b/src/test/modules/test_ddl_deparse/sql/create_transform.sql index 096870233f..132fc5af04 100644 --- a/src/test/modules/test_ddl_deparse/sql/create_transform.sql +++ b/src/test/modules/test_ddl_deparse/sql/create_transform.sql @@ -8,7 +8,7 @@ -- internal and as return argument the datatype of the transform done. -- pl/plpgsql does not authorize the use of internal as data type. CREATE TRANSFORM FOR int LANGUAGE SQL ( - FROM SQL WITH FUNCTION varchar_transform(internal), + FROM SQL WITH FUNCTION varchar_support(internal), TO SQL WITH FUNCTION int4recv(internal)); DROP TRANSFORM FOR int LANGUAGE SQL; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 7bb8ca9128..4db792cf2f 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -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); diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out index 4085e451e4..c89ec06cb4 100644 --- a/src/test/regress/expected/object_address.out +++ b/src/test/regress/expected/object_address.out @@ -38,7 +38,7 @@ CREATE USER MAPPING FOR regress_addr_user SERVER "integer"; ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regress_addr_user; ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user REVOKE DELETE ON TABLES FROM regress_addr_user; CREATE TRANSFORM FOR int LANGUAGE SQL ( - FROM SQL WITH FUNCTION varchar_transform(internal), + FROM SQL WITH FUNCTION varchar_support(internal), TO SQL WITH FUNCTION int4recv(internal)); CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable; CREATE SUBSCRIPTION addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE); diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out index ef268d348e..4edc8175aa 100644 --- a/src/test/regress/expected/oidjoins.out +++ b/src/test/regress/expected/oidjoins.out @@ -809,12 +809,12 @@ WHERE provariadic != 0 AND ------+------------- (0 rows) -SELECT ctid, protransform +SELECT ctid, prosupport FROM pg_catalog.pg_proc fk -WHERE protransform != 0 AND - NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.protransform); - ctid | protransform -------+-------------- +WHERE prosupport != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prosupport); + ctid | prosupport +------+------------ (0 rows) SELECT ctid, prorettype diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 7328095b6f..ce25ee044a 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -453,10 +453,10 @@ WHERE proallargtypes IS NOT NULL AND -----+---------+-------------+----------------+------------- (0 rows) --- Check for protransform functions with the wrong signature +-- Check for prosupport functions with the wrong signature SELECT p1.oid, p1.proname, p2.oid, p2.proname FROM pg_proc AS p1, pg_proc AS p2 -WHERE p2.oid = p1.protransform AND +WHERE p2.oid = p1.prosupport AND (p2.prorettype != 'internal'::regtype OR p2.proretset OR p2.pronargs != 1 OR p2.proargtypes[0] != 'internal'::regtype); oid | proname | oid | proname diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index a498e4e99c..d80643037d 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -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); diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql index d7df322873..fd79465f72 100644 --- a/src/test/regress/sql/object_address.sql +++ b/src/test/regress/sql/object_address.sql @@ -41,7 +41,7 @@ CREATE USER MAPPING FOR regress_addr_user SERVER "integer"; ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regress_addr_user; ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user REVOKE DELETE ON TABLES FROM regress_addr_user; CREATE TRANSFORM FOR int LANGUAGE SQL ( - FROM SQL WITH FUNCTION varchar_transform(internal), + FROM SQL WITH FUNCTION varchar_support(internal), TO SQL WITH FUNCTION int4recv(internal)); CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable; CREATE SUBSCRIPTION addr_sub CONNECTION '' PUBLICATION bar WITH (connect = false, slot_name = NONE); diff --git a/src/test/regress/sql/oidjoins.sql b/src/test/regress/sql/oidjoins.sql index c8291d3973..dbe4a5857d 100644 --- a/src/test/regress/sql/oidjoins.sql +++ b/src/test/regress/sql/oidjoins.sql @@ -405,10 +405,10 @@ SELECT ctid, provariadic FROM pg_catalog.pg_proc fk WHERE provariadic != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.provariadic); -SELECT ctid, protransform +SELECT ctid, prosupport FROM pg_catalog.pg_proc fk -WHERE protransform != 0 AND - NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.protransform); +WHERE prosupport != 0 AND + NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.prosupport); SELECT ctid, prorettype FROM pg_catalog.pg_proc fk WHERE prorettype != 0 AND diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index 8544cbe62e..e2014fc2b5 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -353,10 +353,10 @@ WHERE proallargtypes IS NOT NULL AND FROM generate_series(1, array_length(proallargtypes, 1)) g(i) WHERE proargmodes IS NULL OR proargmodes[i] IN ('i', 'b', 'v')); --- Check for protransform functions with the wrong signature +-- Check for prosupport functions with the wrong signature SELECT p1.oid, p1.proname, p2.oid, p2.proname FROM pg_proc AS p1, pg_proc AS p2 -WHERE p2.oid = p1.protransform AND +WHERE p2.oid = p1.prosupport AND (p2.prorettype != 'internal'::regtype OR p2.proretset OR p2.pronargs != 1 OR p2.proargtypes[0] != 'internal'::regtype); diff --git a/src/tools/findoidjoins/README b/src/tools/findoidjoins/README index 305454ab9a..e5fc3104d3 100644 --- a/src/tools/findoidjoins/README +++ b/src/tools/findoidjoins/README @@ -161,7 +161,7 @@ Join pg_catalog.pg_proc.pronamespace => pg_catalog.pg_namespace.oid Join pg_catalog.pg_proc.proowner => pg_catalog.pg_authid.oid Join pg_catalog.pg_proc.prolang => pg_catalog.pg_language.oid Join pg_catalog.pg_proc.provariadic => pg_catalog.pg_type.oid -Join pg_catalog.pg_proc.protransform => pg_catalog.pg_proc.oid +Join pg_catalog.pg_proc.prosupport => pg_catalog.pg_proc.oid Join pg_catalog.pg_proc.prorettype => pg_catalog.pg_type.oid Join pg_catalog.pg_range.rngtypid => pg_catalog.pg_type.oid Join pg_catalog.pg_range.rngsubtype => pg_catalog.pg_type.oid -- 2.40.0