diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/catalog/pg_aggregate.c | 1 | ||||
-rw-r--r-- | src/backend/catalog/pg_depend.c | 52 | ||||
-rw-r--r-- | src/backend/catalog/pg_proc.c | 12 | ||||
-rw-r--r-- | src/backend/commands/functioncmds.c | 88 | ||||
-rw-r--r-- | src/backend/commands/proclang.c | 3 | ||||
-rw-r--r-- | src/backend/commands/typecmds.c | 1 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 23 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 7 | ||||
-rw-r--r-- | src/backend/utils/adt/date.c | 23 | ||||
-rw-r--r-- | src/backend/utils/adt/datetime.c | 17 | ||||
-rw-r--r-- | src/backend/utils/adt/numeric.c | 69 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 15 | ||||
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 138 | ||||
-rw-r--r-- | src/backend/utils/adt/varbit.c | 48 | ||||
-rw-r--r-- | src/backend/utils/adt/varchar.c | 48 |
15 files changed, 369 insertions, 176 deletions
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index cc3806e85d5..19e3171bf7d 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 2b8f651c998..23b01f841e4 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 db780616e62..557e0ea1f14 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 9a2f1a85b47..4f62e48d98f 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 c2e9e41c07b..59c4e8dfd09 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 fa7161ef9d9..448926db125 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 d7ff17c363d..002c29a5f5d 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 c1faf4152ca..ef6bbe35d7f 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 3810e4a9785..cf5a1c6039e 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 61dbd057bec..0068e71d11a 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 45cd1a0664f..1c9deebc1dd 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 e1fbe494d5d..9fd1ebf3e56 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 7befb6a7e28..e0ef2f78616 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 <interval leading field precision>.) If we ever decide to - * revisit this, interval_transform will likely require adjusting. + * revisit this, interval_support will likely require adjusting. * * Note: before PG 8.4 we interpreted a limited set of fields as * actually causing a "modulo" operation on a given value, potentially @@ -5020,18 +5042,6 @@ interval_part(PG_FUNCTION_ARGS) } -/* timestamp_zone_transform() - * The original optimization here caused problems by relabeling Vars that - * could be matched to index entries. It might be possible to resurrect it - * at some point by teaching the planner to be less cavalier with RelabelType - * nodes, but that will take careful analysis. - */ -Datum -timestamp_zone_transform(PG_FUNCTION_ARGS) -{ - PG_RETURN_POINTER(NULL); -} - /* timestamp_zone() * Encode timestamp type with specified time zone. * This function is just timestamp2timestamptz() except instead of @@ -5125,18 +5135,6 @@ timestamp_zone(PG_FUNCTION_ARGS) PG_RETURN_TIMESTAMPTZ(result); } -/* timestamp_izone_transform() - * The original optimization here caused problems by relabeling Vars that - * could be matched to index entries. It might be possible to resurrect it - * at some point by teaching the planner to be less cavalier with RelabelType - * nodes, but that will take careful analysis. - */ -Datum -timestamp_izone_transform(PG_FUNCTION_ARGS) -{ - PG_RETURN_POINTER(NULL); -} - /* timestamp_izone() * Encode timestamp type with specified time interval as time zone. */ diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c index 1585da0d0e1..fdcc62096cf 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 5cf927e27f2..c866af022f2 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); |