summaryrefslogtreecommitdiff
path: root/src/backend/utils/adt
diff options
context:
space:
mode:
authorTom Lane2019-02-09 23:08:48 +0000
committerTom Lane2019-02-09 23:08:48 +0000
commit1fb57af92069ee104c09e2016af9e0e620681be3 (patch)
tree8bb9db8431972a220f6bd6f8b13e663049172583 /src/backend/utils/adt
parent1a8d5afb0dfc5d0dcc6eda0656a34cb1f0cf0bdf (diff)
Create the infrastructure for planner support functions.
Rename/repurpose pg_proc.protransform as "prosupport". The idea is still that it names an internal function that provides knowledge to the planner about the behavior of the function it's attached to; but redesign the API specification so that it's not limited to doing just one thing, but can support an extensible set of requests. The original purpose of simplifying a function call is handled by the first request type to be invented, SupportRequestSimplify. Adjust all the existing transform functions to handle this API, and rename them fron "xxx_transform" to "xxx_support" to reflect the potential generalization of what they do. (Since we never previously provided any way for extensions to add transform functions, this change doesn't create an API break for them.) Also add DDL and pg_dump support for attaching a support function to a user-defined function. Unfortunately, DDL access has to be restricted to superusers, at least for now; but seeing that support functions will pretty much have to be written in C, that limitation is just theoretical. (This support is untested in this patch, but a follow-on patch will add cases that exercise it.) Discussion: https://postgr.es/m/[email protected]
Diffstat (limited to 'src/backend/utils/adt')
-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
7 files changed, 209 insertions, 149 deletions
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);