aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorRobert Haas <rhaas@postgresql.org>2012-02-08 09:33:02 -0500
committerRobert Haas <rhaas@postgresql.org>2012-02-08 09:33:37 -0500
commitc13897983a0006e658fb7c6410d72ca59fb87136 (patch)
tree4e228cac44cab1c1e80173511578ff248346a204 /src/backend
parent1a01560cbb78ff363fc7d70298328aa23f05bfb5 (diff)
downloadpostgresql-c13897983a0006e658fb7c6410d72ca59fb87136.tar.gz
postgresql-c13897983a0006e658fb7c6410d72ca59fb87136.zip
Add transform functions for various temporal typmod coercisions.
This enables ALTER TABLE to skip table and index rebuilds in some cases. Noah Misch, with trivial changes by me.
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/utils/adt/date.c11
-rw-r--r--src/backend/utils/adt/datetime.c35
-rw-r--r--src/backend/utils/adt/timestamp.c86
3 files changed, 132 insertions, 0 deletions
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index bf3b6ea1deb..85e8fd00ff2 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -1210,6 +1210,17 @@ timetypmodout(PG_FUNCTION_ARGS)
}
+/* time_transform()
+ * Flatten calls to time_scale() and timetz_scale() that solely represent
+ * increases in allowed precision.
+ */
+Datum
+time_transform(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_POINTER(TemporalTransform(MAX_TIME_PRECISION,
+ (Node *) PG_GETARG_POINTER(0)));
+}
+
/* time_scale()
* Adjust time type for specified scale factor.
* Used by PostgreSQL type system to stuff columns.
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 07e43a60fe5..f495c3f0109 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -23,6 +23,8 @@
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_clause.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datetime.h"
@@ -4142,6 +4144,39 @@ CheckDateTokenTables(void)
}
/*
+ * Helper 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.
+ */
+Node *
+TemporalTransform(int32 max_precis, Node *node)
+{
+ FuncExpr *expr = (FuncExpr *) node;
+ Node *typmod;
+ Node *ret = NULL;
+
+ if (!IsA(expr, FuncExpr))
+ return ret;
+
+ Assert(list_length(expr->args) == 2);
+ typmod = lsecond(expr->args);
+
+ if (IsA(typmod, Const))
+ {
+ Node *source = linitial(expr->args);
+ int32 old_precis = exprTypmod(source);
+ int32 new_precis = DatumGetInt32(((Const *) typmod)->constvalue);
+
+ if (new_precis == -1 ||
+ new_precis == max_precis ||
+ (old_precis != -1 && new_precis >= old_precis))
+ ret = relabel_to_typmod(source, new_precis);
+ }
+
+ return ret;
+}
+
+/*
* This function gets called during timezone config file load or reload
* to create the final array of timezone tokens. The argument array
* is already sorted in name order. The data is converted to datetkn
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index db8d2c2b109..44a3a922ece 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -27,6 +27,8 @@
#include "funcapi.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_clause.h"
#include "parser/scansup.h"
#include "utils/array.h"
#include "utils/builtins.h"
@@ -308,6 +310,21 @@ timestamptypmodout(PG_FUNCTION_ARGS)
}
+/* timestamp_transform()
+ * Flatten calls to timestamp_scale() and timestamptz_scale() that solely
+ * represent increases in allowed precision.
+ */
+Datum
+timestamp_transform(PG_FUNCTION_ARGS)
+{
+ /*
+ * 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.
+ */
+ PG_RETURN_POINTER(TemporalTransform(MAX_TIMESTAMP_PRECISION,
+ (Node *) PG_GETARG_POINTER(0)));
+}
+
/* timestamp_scale()
* Adjust time type for specified scale factor.
* Used by PostgreSQL type system to stuff columns.
@@ -745,6 +762,18 @@ interval_send(PG_FUNCTION_ARGS)
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
+/*
+ * The interval typmod stores a "range" in its high 16 bits and a "precision"
+ * in its low 16 bits. Both contribute to defining the resolution of the
+ * type. Range addresses resolution granules larger than one second, and
+ * precision specifies resolution below one second. This representation can
+ * express all SQL standard resolutions, but we implement them all in terms of
+ * truncating rightward from some position. Range is a bitmap of permitted
+ * fields, but only the temporally-smallest such field is significant to our
+ * calculations. Precision is a count of sub-second decimal places to retain.
+ * Setting all bits (INTERVAL_FULL_PRECISION) gives the same truncation
+ * semantics as choosing MAX_INTERVAL_PRECISION.
+ */
Datum
intervaltypmodin(PG_FUNCTION_ARGS)
{
@@ -901,6 +930,63 @@ intervaltypmodout(PG_FUNCTION_ARGS)
}
+/* interval_transform()
+ * 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)
+{
+ FuncExpr *expr = (FuncExpr *) PG_GETARG_POINTER(0);
+ Node *typmod;
+ Node *ret = NULL;
+
+ if (!IsA(expr, FuncExpr))
+ PG_RETURN_POINTER(ret);
+
+ Assert(list_length(expr->args) == 2);
+ typmod = lsecond(expr->args);
+
+ if (IsA(typmod, Const))
+ {
+ Node *source = linitial(expr->args);
+ int32 old_typmod = exprTypmod(source);
+ int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
+ int old_range;
+ int old_precis;
+ int new_range = INTERVAL_RANGE(new_typmod);
+ int new_precis = INTERVAL_PRECISION(new_typmod);
+ int new_range_fls;
+
+ if (old_typmod == -1)
+ {
+ old_range = INTERVAL_FULL_RANGE;
+ old_precis = INTERVAL_FULL_PRECISION;
+ }
+ else
+ {
+ old_range = INTERVAL_RANGE(old_typmod);
+ old_precis = INTERVAL_PRECISION(old_typmod);
+ }
+
+ /*
+ * Temporally-smaller fields occupy higher positions in the range
+ * bitmap. Since only the temporally-smallest bit matters for length
+ * coercion purposes, we compare the last-set bits in the ranges.
+ */
+ new_range_fls = fls(new_range);
+ if (new_typmod == -1 ||
+ ((new_range_fls >= SECOND ||
+ new_range_fls >= fls(old_range)) &&
+ (new_precis >= MAX_INTERVAL_PRECISION ||
+ new_precis >= old_precis)))
+ ret = relabel_to_typmod(source, new_typmod);
+ }
+
+ PG_RETURN_POINTER(ret);
+}
+
/* interval_scale()
* Adjust interval type for specified fields.
* Used by PostgreSQL type system to stuff columns.