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.
This commit is contained in:
Robert Haas 2012-02-08 09:33:02 -05:00
parent 1a01560cbb
commit c13897983a
8 changed files with 150 additions and 6 deletions

View File

@ -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.

View File

@ -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"
@ -4141,6 +4143,39 @@ CheckDateTokenTables(void)
return ok;
}
/*
* 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

View File

@ -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.

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201202072
#define CATALOG_VERSION_NO 201202081
#endif

View File

@ -1257,7 +1257,9 @@ DESCR("date difference preserving months and years");
/* OIDS 1200 - 1299 */
DATA(insert OID = 1200 ( interval PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 1186 "1186 23" _null_ _null_ _null_ _null_ interval_scale _null_ _null_ _null_ ));
DATA(insert OID = 3918 ( interval_transform PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ interval_transform _null_ _null_ _null_ ));
DESCR("transform an interval length coercion");
DATA(insert OID = 1200 ( interval PGNSP PGUID 12 1 0 0 3918 f f f t f i 2 0 1186 "1186 23" _null_ _null_ _null_ _null_ interval_scale _null_ _null_ _null_ ));
DESCR("adjust interval precision");
DATA(insert OID = 1215 ( obj_description PGNSP PGUID 14 100 0 0 0 f f f t f s 2 0 25 "26 19" _null_ _null_ _null_ _null_ "select description from pg_catalog.pg_description where objoid = $1 and classoid = (select oid from pg_catalog.pg_class where relname = $2 and relnamespace = PGNSP) and objsubid = 0" _null_ _null_ _null_ ));
@ -2732,7 +2734,9 @@ DATA(insert OID = 1953 ( byteane PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16
DATA(insert OID = 1954 ( byteacmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "17 17" _null_ _null_ _null_ _null_ byteacmp _null_ _null_ _null_ ));
DESCR("less-equal-greater");
DATA(insert OID = 1961 ( timestamp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 1114 "1114 23" _null_ _null_ _null_ _null_ timestamp_scale _null_ _null_ _null_ ));
DATA(insert OID = 3917 ( timestamp_transform PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ timestamp_transform _null_ _null_ _null_ ));
DESCR("transform a timestamp length coercion");
DATA(insert OID = 1961 ( timestamp PGNSP PGUID 12 1 0 0 3917 f f f t f i 2 0 1114 "1114 23" _null_ _null_ _null_ _null_ timestamp_scale _null_ _null_ _null_ ));
DESCR("adjust timestamp precision");
DATA(insert OID = 1965 ( oidlarger PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 26 "26 26" _null_ _null_ _null_ _null_ oidlarger _null_ _null_ _null_ ));
@ -2740,11 +2744,13 @@ DESCR("larger of two");
DATA(insert OID = 1966 ( oidsmaller PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 26 "26 26" _null_ _null_ _null_ _null_ oidsmaller _null_ _null_ _null_ ));
DESCR("smaller of two");
DATA(insert OID = 1967 ( timestamptz PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 1184 "1184 23" _null_ _null_ _null_ _null_ timestamptz_scale _null_ _null_ _null_ ));
DATA(insert OID = 1967 ( timestamptz PGNSP PGUID 12 1 0 0 3917 f f f t f i 2 0 1184 "1184 23" _null_ _null_ _null_ _null_ timestamptz_scale _null_ _null_ _null_ ));
DESCR("adjust timestamptz precision");
DATA(insert OID = 1968 ( time PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 1083 "1083 23" _null_ _null_ _null_ _null_ time_scale _null_ _null_ _null_ ));
DATA(insert OID = 3944 ( time_transform PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ time_transform _null_ _null_ _null_ ));
DESCR("transform a time length coercion");
DATA(insert OID = 1968 ( time PGNSP PGUID 12 1 0 0 3944 f f f t f i 2 0 1083 "1083 23" _null_ _null_ _null_ _null_ time_scale _null_ _null_ _null_ ));
DESCR("adjust time precision");
DATA(insert OID = 1969 ( timetz PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 1266 "1266 23" _null_ _null_ _null_ _null_ timetz_scale _null_ _null_ _null_ ));
DATA(insert OID = 1969 ( timetz PGNSP PGUID 12 1 0 0 3944 f f f t f i 2 0 1266 "1266 23" _null_ _null_ _null_ _null_ timetz_scale _null_ _null_ _null_ ));
DESCR("adjust time with time zone precision");
DATA(insert OID = 2003 ( textanycat PGNSP PGUID 14 1 0 0 0 f f f t f v 2 0 25 "25 2776" _null_ _null_ _null_ _null_ "select $1 || $2::pg_catalog.text" _null_ _null_ _null_ ));

View File

@ -154,6 +154,7 @@ extern Datum time_recv(PG_FUNCTION_ARGS);
extern Datum time_send(PG_FUNCTION_ARGS);
extern Datum timetypmodin(PG_FUNCTION_ARGS);
extern Datum timetypmodout(PG_FUNCTION_ARGS);
extern Datum time_transform(PG_FUNCTION_ARGS);
extern Datum time_scale(PG_FUNCTION_ARGS);
extern Datum time_eq(PG_FUNCTION_ARGS);
extern Datum time_ne(PG_FUNCTION_ARGS);

View File

@ -16,6 +16,7 @@
#ifndef DATETIME_H
#define DATETIME_H
#include "nodes/nodes.h"
#include "utils/timestamp.h"
/* this struct is declared in utils/tzparser.h: */
@ -298,6 +299,8 @@ extern int DecodeUnits(int field, char *lowtoken, int *val);
extern int j2day(int jd);
extern Node *TemporalTransform(int32 max_precis, Node *node);
extern bool CheckDateTokenTables(void);
extern void ConvertTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl,

View File

@ -100,6 +100,7 @@ extern Datum timestamp_recv(PG_FUNCTION_ARGS);
extern Datum timestamp_send(PG_FUNCTION_ARGS);
extern Datum timestamptypmodin(PG_FUNCTION_ARGS);
extern Datum timestamptypmodout(PG_FUNCTION_ARGS);
extern Datum timestamp_transform(PG_FUNCTION_ARGS);
extern Datum timestamp_scale(PG_FUNCTION_ARGS);
extern Datum timestamp_eq(PG_FUNCTION_ARGS);
extern Datum timestamp_ne(PG_FUNCTION_ARGS);
@ -136,6 +137,7 @@ extern Datum interval_recv(PG_FUNCTION_ARGS);
extern Datum interval_send(PG_FUNCTION_ARGS);
extern Datum intervaltypmodin(PG_FUNCTION_ARGS);
extern Datum intervaltypmodout(PG_FUNCTION_ARGS);
extern Datum interval_transform(PG_FUNCTION_ARGS);
extern Datum interval_scale(PG_FUNCTION_ARGS);
extern Datum interval_eq(PG_FUNCTION_ARGS);
extern Datum interval_ne(PG_FUNCTION_ARGS);