|
27 | 27 | #include "funcapi.h"
|
28 | 28 | #include "libpq/pqformat.h"
|
29 | 29 | #include "miscadmin.h"
|
| 30 | +#include "nodes/makefuncs.h" |
30 | 31 | #include "nodes/nodeFuncs.h"
|
31 | 32 | #include "parser/scansup.h"
|
32 | 33 | #include "utils/array.h"
|
@@ -4874,6 +4875,87 @@ interval_part(PG_FUNCTION_ARGS)
|
4874 | 4875 | }
|
4875 | 4876 |
|
4876 | 4877 |
|
| 4878 | +/* timestamp_zone_transform() |
| 4879 | + * If the zone argument of a timestamp_zone() or timestamptz_zone() call is a |
| 4880 | + * plan-time constant denoting a zone equivalent to UTC, the call will always |
| 4881 | + * return its second argument unchanged. Simplify the expression tree |
| 4882 | + * accordingly. Civil time zones almost never qualify, because jurisdictions |
| 4883 | + * that follow UTC today have not done so continuously. |
| 4884 | + */ |
| 4885 | +Datum |
| 4886 | +timestamp_zone_transform(PG_FUNCTION_ARGS) |
| 4887 | +{ |
| 4888 | + Node *func_node = (Node *) PG_GETARG_POINTER(0); |
| 4889 | + FuncExpr *expr = (FuncExpr *) func_node; |
| 4890 | + Node *ret = NULL; |
| 4891 | + Node *zone_node; |
| 4892 | + |
| 4893 | + Assert(IsA(expr, FuncExpr)); |
| 4894 | + Assert(list_length(expr->args) == 2); |
| 4895 | + |
| 4896 | + zone_node = (Node *) linitial(expr->args); |
| 4897 | + |
| 4898 | + if (IsA(zone_node, Const) &&!((Const *) zone_node)->constisnull) |
| 4899 | + { |
| 4900 | + text *zone = DatumGetTextPP(((Const *) zone_node)->constvalue); |
| 4901 | + char tzname[TZ_STRLEN_MAX + 1]; |
| 4902 | + char *lowzone; |
| 4903 | + int type, |
| 4904 | + abbrev_offset; |
| 4905 | + pg_tz *tzp; |
| 4906 | + bool noop = false; |
| 4907 | + |
| 4908 | + /* |
| 4909 | + * If the timezone is forever UTC+0, the FuncExpr function call is a |
| 4910 | + * no-op for all possible timestamps. This passage mirrors code in |
| 4911 | + * timestamp_zone(). |
| 4912 | + */ |
| 4913 | + text_to_cstring_buffer(zone, tzname, sizeof(tzname)); |
| 4914 | + lowzone = downcase_truncate_identifier(tzname, |
| 4915 | + strlen(tzname), |
| 4916 | + false); |
| 4917 | + type = DecodeTimezoneAbbrev(0, lowzone, &abbrev_offset, &tzp); |
| 4918 | + if (type == TZ || type == DTZ) |
| 4919 | + noop = (abbrev_offset == 0); |
| 4920 | + else if (type == DYNTZ) |
| 4921 | + { |
| 4922 | + /* |
| 4923 | + * An abbreviation of a single-offset timezone ought not to be |
| 4924 | + * configured as a DYNTZ, so don't bother checking. |
| 4925 | + */ |
| 4926 | + } |
| 4927 | + else |
| 4928 | + { |
| 4929 | + long tzname_offset; |
| 4930 | + |
| 4931 | + tzp = pg_tzset(tzname); |
| 4932 | + if (tzp && pg_get_timezone_offset(tzp, &tzname_offset)) |
| 4933 | + noop = (tzname_offset == 0); |
| 4934 | + } |
| 4935 | + |
| 4936 | + if (noop) |
| 4937 | + { |
| 4938 | + Node *timestamp = (Node *) lsecond(expr->args); |
| 4939 | + |
| 4940 | + /* Strip any existing RelabelType node(s) */ |
| 4941 | + while (timestamp && IsA(timestamp, RelabelType)) |
| 4942 | + timestamp = (Node *) ((RelabelType *) timestamp)->arg; |
| 4943 | + |
| 4944 | + /* |
| 4945 | + * Replace the FuncExpr with its timestamp argument, relabeled as |
| 4946 | + * though the function call had computed it. |
| 4947 | + */ |
| 4948 | + ret = (Node *) makeRelabelType((Expr *) timestamp, |
| 4949 | + exprType(func_node), |
| 4950 | + exprTypmod(func_node), |
| 4951 | + exprCollation(func_node), |
| 4952 | + COERCE_EXPLICIT_CAST); |
| 4953 | + } |
| 4954 | + } |
| 4955 | + |
| 4956 | + PG_RETURN_POINTER(ret); |
| 4957 | +} |
| 4958 | + |
4877 | 4959 | /* timestamp_zone()
|
4878 | 4960 | * Encode timestamp type with specified time zone.
|
4879 | 4961 | * This function is just timestamp2timestamptz() except instead of
|
@@ -4963,6 +5045,52 @@ timestamp_zone(PG_FUNCTION_ARGS)
|
4963 | 5045 | PG_RETURN_TIMESTAMPTZ(result);
|
4964 | 5046 | }
|
4965 | 5047 |
|
| 5048 | +/* timestamp_izone_transform() |
| 5049 | + * If we deduce at plan time that a particular timestamp_izone() or |
| 5050 | + * timestamptz_izone() call can only compute tz=0, the call will always return |
| 5051 | + * its second argument unchanged. Simplify the expression tree accordingly. |
| 5052 | + */ |
| 5053 | +Datum |
| 5054 | +timestamp_izone_transform(PG_FUNCTION_ARGS) |
| 5055 | +{ |
| 5056 | + Node *func_node = (Node *) PG_GETARG_POINTER(0); |
| 5057 | + FuncExpr *expr = (FuncExpr *) func_node; |
| 5058 | + Node *ret = NULL; |
| 5059 | + Node *zone_node; |
| 5060 | + |
| 5061 | + Assert(IsA(expr, FuncExpr)); |
| 5062 | + Assert(list_length(expr->args) == 2); |
| 5063 | + |
| 5064 | + zone_node = (Node *) linitial(expr->args); |
| 5065 | + |
| 5066 | + if (IsA(zone_node, Const) &&!((Const *) zone_node)->constisnull) |
| 5067 | + { |
| 5068 | + Interval *zone; |
| 5069 | + |
| 5070 | + zone = DatumGetIntervalP(((Const *) zone_node)->constvalue); |
| 5071 | + if (zone->month == 0 && zone->day == 0 && zone->time == 0) |
| 5072 | + { |
| 5073 | + Node *timestamp = (Node *) lsecond(expr->args); |
| 5074 | + |
| 5075 | + /* Strip any existing RelabelType node(s) */ |
| 5076 | + while (timestamp && IsA(timestamp, RelabelType)) |
| 5077 | + timestamp = (Node *) ((RelabelType *) timestamp)->arg; |
| 5078 | + |
| 5079 | + /* |
| 5080 | + * Replace the FuncExpr with its timestamp argument, relabeled as |
| 5081 | + * though the function call had computed it. |
| 5082 | + */ |
| 5083 | + ret = (Node *) makeRelabelType((Expr *) timestamp, |
| 5084 | + exprType(func_node), |
| 5085 | + exprTypmod(func_node), |
| 5086 | + exprCollation(func_node), |
| 5087 | + COERCE_EXPLICIT_CAST); |
| 5088 | + } |
| 5089 | + } |
| 5090 | + |
| 5091 | + PG_RETURN_POINTER(ret); |
| 5092 | +} |
| 5093 | + |
4966 | 5094 | /* timestamp_izone()
|
4967 | 5095 | * Encode timestamp type with specified time interval as time zone.
|
4968 | 5096 | */
|
|
0 commit comments