aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/pg_aggregate.c1
-rw-r--r--src/backend/catalog/pg_depend.c52
-rw-r--r--src/backend/catalog/pg_proc.c12
-rw-r--r--src/backend/commands/functioncmds.c88
-rw-r--r--src/backend/commands/proclang.c3
-rw-r--r--src/backend/commands/typecmds.c1
-rw-r--r--src/backend/optimizer/util/clauses.c23
-rw-r--r--src/backend/parser/gram.y7
-rw-r--r--src/backend/utils/adt/date.c23
-rw-r--r--src/backend/utils/adt/datetime.c17
-rw-r--r--src/backend/utils/adt/numeric.c69
-rw-r--r--src/backend/utils/adt/ruleutils.c15
-rw-r--r--src/backend/utils/adt/timestamp.c138
-rw-r--r--src/backend/utils/adt/varbit.c48
-rw-r--r--src/backend/utils/adt/varchar.c48
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,
&parallel_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, &parallel);
+ &proconfig, &procost, &prorows,
+ &prosupport, &parallel);
/* 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,
&parallel_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);