#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"
}
+/* 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
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.
*/
/* OIDS 1000 - 1999 */
-DATA(insert OID = 1026 ( timezone PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1114 "1186 1184" _null_ _null_ _null_ _null_ timestamptz_izone _null_ _null_ _null_ ));
+DATA(insert OID = 3994 ( timestamp_izone_transform PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ timestamp_izone_transform _null_ _null_ _null_ ));
+DESCR("transform a time zone adjustment");
+DATA(insert OID = 1026 ( timezone PGNSP PGUID 12 1 0 0 timestamp_izone_transform f f f f t f i 2 0 1114 "1186 1184" _null_ _null_ _null_ _null_ timestamptz_izone _null_ _null_ _null_ ));
DESCR("adjust timestamp to new time zone");
DATA(insert OID = 1031 ( aclitemin PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 1033 "2275" _null_ _null_ _null_ _null_ aclitemin _null_ _null_ _null_ ));
DATA(insert OID = 1157 ( timestamptz_gt PGNSP PGUID 12 1 0 0 0 f f f t t f i 2 0 16 "1184 1184" _null_ _null_ _null_ _null_ timestamp_gt _null_ _null_ _null_ ));
DATA(insert OID = 1158 ( to_timestamp PGNSP PGUID 14 1 0 0 0 f f f f t f i 1 0 1184 "701" _null_ _null_ _null_ _null_ "select (''epoch''::pg_catalog.timestamptz + $1 * ''1 second''::pg_catalog.interval)" _null_ _null_ _null_ ));
DESCR("convert UNIX epoch to timestamptz");
-DATA(insert OID = 1159 ( timezone PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1114 "25 1184" _null_ _null_ _null_ _null_ timestamptz_zone _null_ _null_ _null_ ));
+DATA(insert OID = 3995 ( timestamp_zone_transform PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ timestamp_zone_transform _null_ _null_ _null_ ));
+DESCR("transform a time zone adjustment");
+DATA(insert OID = 1159 ( timezone PGNSP PGUID 12 1 0 0 timestamp_zone_transform f f f f t f i 2 0 1114 "25 1184" _null_ _null_ _null_ _null_ timestamptz_zone _null_ _null_ _null_ ));
DESCR("adjust timestamp to new time zone");
DATA(insert OID = 1160 ( interval_in PGNSP PGUID 12 1 0 0 0 f f f f t f s 3 0 1186 "2275 26 23" _null_ _null_ _null_ _null_ interval_in _null_ _null_ _null_ ));
DATA(insert OID = 2059 ( age PGNSP PGUID 14 1 0 0 0 f f f f t f s 1 0 1186 "1114" _null_ _null_ _null_ _null_ "select pg_catalog.age(cast(current_date as timestamp without time zone), $1)" _null_ _null_ _null_ ));
DESCR("date difference from today preserving months and years");
-DATA(insert OID = 2069 ( timezone PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1184 "25 1114" _null_ _null_ _null_ _null_ timestamp_zone _null_ _null_ _null_ ));
+DATA(insert OID = 2069 ( timezone PGNSP PGUID 12 1 0 0 timestamp_zone_transform f f f f t f i 2 0 1184 "25 1114" _null_ _null_ _null_ _null_ timestamp_zone _null_ _null_ _null_ ));
DESCR("adjust timestamp to new time zone");
-DATA(insert OID = 2070 ( timezone PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1184 "1186 1114" _null_ _null_ _null_ _null_ timestamp_izone _null_ _null_ _null_ ));
+DATA(insert OID = 2070 ( timezone PGNSP PGUID 12 1 0 0 timestamp_izone_transform f f f f t f i 2 0 1184 "1186 1114" _null_ _null_ _null_ _null_ timestamp_izone _null_ _null_ _null_ ));
DESCR("adjust timestamp to new time zone");
DATA(insert OID = 2071 ( date_pl_interval PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1114 "1082 1186" _null_ _null_ _null_ _null_ date_pl_interval _null_ _null_ _null_ ));
DATA(insert OID = 2072 ( date_mi_interval PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1114 "1082 1186" _null_ _null_ _null_ _null_ date_mi_interval _null_ _null_ _null_ ));