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); - - 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); 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); - - 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 .) 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); - - 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); 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); - - 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); 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