aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/timestamp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/timestamp.c')
-rw-r--r--src/backend/utils/adt/timestamp.c128
1 files changed, 128 insertions, 0 deletions
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 67e0cf95434..723c67087e5 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -27,6 +27,7 @@
#include "funcapi.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
+#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/scansup.h"
#include "utils/array.h"
@@ -4874,6 +4875,87 @@ interval_part(PG_FUNCTION_ARGS)
}
+/* timestamp_zone_transform()
+ * If the zone argument of a timestamp_zone() or timestamptz_zone() call is a
+ * plan-time constant denoting a zone equivalent to UTC, the call will always
+ * return its second argument unchanged. Simplify the expression tree
+ * accordingly. Civil time zones almost never qualify, because jurisdictions
+ * that follow UTC today have not done so continuously.
+ */
+Datum
+timestamp_zone_transform(PG_FUNCTION_ARGS)
+{
+ Node *func_node = (Node *) PG_GETARG_POINTER(0);
+ FuncExpr *expr = (FuncExpr *) func_node;
+ Node *ret = NULL;
+ Node *zone_node;
+
+ Assert(IsA(expr, FuncExpr));
+ Assert(list_length(expr->args) == 2);
+
+ zone_node = (Node *) linitial(expr->args);
+
+ if (IsA(zone_node, Const) &&!((Const *) zone_node)->constisnull)
+ {
+ text *zone = DatumGetTextPP(((Const *) zone_node)->constvalue);
+ char tzname[TZ_STRLEN_MAX + 1];
+ char *lowzone;
+ int type,
+ abbrev_offset;
+ pg_tz *tzp;
+ bool noop = false;
+
+ /*
+ * If the timezone is forever UTC+0, the FuncExpr function call is a
+ * no-op for all possible timestamps. This passage mirrors code in
+ * timestamp_zone().
+ */
+ text_to_cstring_buffer(zone, tzname, sizeof(tzname));
+ lowzone = downcase_truncate_identifier(tzname,
+ strlen(tzname),
+ false);
+ type = DecodeTimezoneAbbrev(0, lowzone, &abbrev_offset, &tzp);
+ if (type == TZ || type == DTZ)
+ noop = (abbrev_offset == 0);
+ else if (type == DYNTZ)
+ {
+ /*
+ * An abbreviation of a single-offset timezone ought not to be
+ * configured as a DYNTZ, so don't bother checking.
+ */
+ }
+ else
+ {
+ long tzname_offset;
+
+ tzp = pg_tzset(tzname);
+ if (tzp && pg_get_timezone_offset(tzp, &tzname_offset))
+ noop = (tzname_offset == 0);
+ }
+
+ if (noop)
+ {
+ Node *timestamp = (Node *) lsecond(expr->args);
+
+ /* Strip any existing RelabelType node(s) */
+ while (timestamp && IsA(timestamp, RelabelType))
+ timestamp = (Node *) ((RelabelType *) timestamp)->arg;
+
+ /*
+ * Replace the FuncExpr with its timestamp argument, relabeled as
+ * though the function call had computed it.
+ */
+ ret = (Node *) makeRelabelType((Expr *) timestamp,
+ exprType(func_node),
+ exprTypmod(func_node),
+ exprCollation(func_node),
+ COERCE_EXPLICIT_CAST);
+ }
+ }
+
+ PG_RETURN_POINTER(ret);
+}
+
/* timestamp_zone()
* Encode timestamp type with specified time zone.
* This function is just timestamp2timestamptz() except instead of
@@ -4963,6 +5045,52 @@ timestamp_zone(PG_FUNCTION_ARGS)
PG_RETURN_TIMESTAMPTZ(result);
}
+/* timestamp_izone_transform()
+ * If we deduce at plan time that a particular timestamp_izone() or
+ * timestamptz_izone() call can only compute tz=0, the call will always return
+ * its second argument unchanged. Simplify the expression tree accordingly.
+ */
+Datum
+timestamp_izone_transform(PG_FUNCTION_ARGS)
+{
+ Node *func_node = (Node *) PG_GETARG_POINTER(0);
+ FuncExpr *expr = (FuncExpr *) func_node;
+ Node *ret = NULL;
+ Node *zone_node;
+
+ Assert(IsA(expr, FuncExpr));
+ Assert(list_length(expr->args) == 2);
+
+ zone_node = (Node *) linitial(expr->args);
+
+ if (IsA(zone_node, Const) &&!((Const *) zone_node)->constisnull)
+ {
+ Interval *zone;
+
+ zone = DatumGetIntervalP(((Const *) zone_node)->constvalue);
+ if (zone->month == 0 && zone->day == 0 && zone->time == 0)
+ {
+ Node *timestamp = (Node *) lsecond(expr->args);
+
+ /* Strip any existing RelabelType node(s) */
+ while (timestamp && IsA(timestamp, RelabelType))
+ timestamp = (Node *) ((RelabelType *) timestamp)->arg;
+
+ /*
+ * Replace the FuncExpr with its timestamp argument, relabeled as
+ * though the function call had computed it.
+ */
+ ret = (Node *) makeRelabelType((Expr *) timestamp,
+ exprType(func_node),
+ exprTypmod(func_node),
+ exprCollation(func_node),
+ COERCE_EXPLICIT_CAST);
+ }
+ }
+
+ PG_RETURN_POINTER(ret);
+}
+
/* timestamp_izone()
* Encode timestamp type with specified time interval as time zone.
*/