diff --git a/configure b/configure index bfc7a1f..7e1b74f 100755 *** a/configure --- b/configure *************** *** 20773,20779 **** LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'` ! for ac_func in crypt getopt getrusage inet_aton random rint srandom strdup strerror strlcat strlcpy strtol strtoul do as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` { $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 --- 20773,20780 ---- ! ! for ac_func in crypt fls getopt getrusage inet_aton random rint srandom strdup strerror strlcat strlcpy strtol strtoul do as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` { $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 diff --git a/conindex 54ca820..b684512 100644 *** a/configure.in --- b/configure.in *************** *** 1319,1325 **** fi pgac_save_LIBS="$LIBS" LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'` ! AC_REPLACE_FUNCS([crypt getopt getrusage inet_aton random rint srandom strdup strerror strlcat strlcpy strtol strtoul]) case $host_os in --- 1319,1325 ---- pgac_save_LIBS="$LIBS" LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'` ! AC_REPLACE_FUNCS([crypt fls getopt getrusage inet_aton random rint srandom strdup strerror strlcat strlcpy strtol strtoul]) case $host_os in diff --git a/src/baindex ff4e304..1c84e87 100644 *** a/src/backend/utils/adt/date.c --- b/src/backend/utils/adt/date.c *************** *** 1210,1215 **** timetypmodout(PG_FUNCTION_ARGS) --- 1210,1226 ---- } + /* 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/index dad41fc..fd118c4 100644 *** a/src/backend/utils/adt/datetime.c --- b/src/backend/utils/adt/datetime.c *************** *** 23,28 **** --- 23,30 ---- #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,4147 **** CheckDateTokenTables(void) --- 4144,4182 ---- } /* + * 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/timeindex 450dcd4..c7bd1e3 100644 *** a/src/backend/utils/adt/timestamp.c --- b/src/backend/utils/adt/timestamp.c *************** *** 27,32 **** --- 27,34 ---- #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,313 **** timestamptypmodout(PG_FUNCTION_ARGS) --- 310,330 ---- } + /* 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,750 **** interval_send(PG_FUNCTION_ARGS) --- 762,779 ---- 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,906 **** intervaltypmodout(PG_FUNCTION_ARGS) --- 930,993 ---- } + /* 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. diff --git a/src/include/catalog/pg_procindex c893c3a..49cde6b 100644 *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** *** 1253,1259 **** 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_ )); 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_ )); --- 1253,1261 ---- /* OIDS 1200 - 1299 */ ! DATA(insert OID = 3148 ( 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 3148 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_ )); *************** *** 2712,2718 **** 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_ )); 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_ )); --- 2714,2722 ---- 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 = 3147 ( 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 3147 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_ )); *************** *** 2720,2730 **** 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_ )); 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_ )); 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_ )); 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_ )); --- 2724,2736 ---- 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 3147 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 = 3146 ( 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 3146 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 3146 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_ )); diff --git a/src/include/pg_config.hindex ef467b7..0411716 100644 *** a/src/include/pg_config.h.in --- b/src/include/pg_config.h.in *************** *** 158,163 **** --- 158,166 ---- /* Define to 1 if you have the `fdatasync' function. */ #undef HAVE_FDATASYNC + /* Define to 1 if you have the `fls' function. */ + #undef HAVE_FLS + /* Define to 1 if you have the `fpclass' function. */ #undef HAVE_FPCLASS diff --git a/src/include/port.h bindex eceb4bf..edcc41f 100644 *** a/src/include/port.h --- b/src/include/port.h *************** *** 396,401 **** extern double pg_erand48(unsigned short xseed[3]); --- 396,405 ---- extern long pg_lrand48(void); extern void pg_srand48(long seed); + #ifndef HAVE_FLS + extern int fls(int mask); + #endif + #ifndef HAVE_FSEEKO #define fseeko(a, b, c) fseek(a, b, c) #define ftello(a) ftell(a) diff --git a/src/include/index 13e0db4..cc6f381 100644 *** a/src/include/utils/date.h --- b/src/include/utils/date.h *************** *** 154,159 **** extern Datum time_recv(PG_FUNCTION_ARGS); --- 154,160 ---- 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); diff --git a/src/include/utils/index cd9edda..a3d90fe 100644 *** a/src/include/utils/datetime.h --- b/src/include/utils/datetime.h *************** *** 16,21 **** --- 16,22 ---- #ifndef DATETIME_H #define DATETIME_H + #include "nodes/nodes.h" #include "utils/timestamp.h" /* this struct is declared in utils/tzparser.h: */ *************** *** 298,303 **** extern int DecodeUnits(int field, char *lowtoken, int *val); --- 299,306 ---- extern int j2day(int jd); + extern Node *TemporalTransform(int32 max_precis, Node *node); + extern bool CheckDateTokenTables(void); extern void ConvertTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl, diff --git a/src/include/utils/timeindex 7661744..4006504 100644 *** a/src/include/utils/timestamp.h --- b/src/include/utils/timestamp.h *************** *** 100,105 **** extern Datum timestamp_recv(PG_FUNCTION_ARGS); --- 100,106 ---- 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,141 **** extern Datum interval_recv(PG_FUNCTION_ARGS); --- 137,143 ---- 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); diff --git a/src/port/fls.c b/src/ponew file mode 100644 index 0000000..53b2b15 *** /dev/null --- b/src/port/fls.c *************** *** 0 **** --- 1,51 ---- + /* + * src/port/fls.c + * + * $FreeBSD: /usr/local/www/cvsroot/FreeBSD/src/lib/libc/string/fls.c,v 1.3 2007/01/09 00:28:12 imp Exp $ + */ + + /*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + #include "c.h" + + /* + * Find Last Set bit + */ + int + fls(int mask) + { + int bit; + + if (mask == 0) + return (0); + for (bit = 1; mask != 1; bit++) + mask = (unsigned int)mask >> 1; + return (bit); + } diff --git a/src/testindex 301cc4b..323db24 100644