diff options
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/adt/arrayutils.c | 30 | ||||
-rw-r--r-- | src/backend/utils/adt/date.c | 89 | ||||
-rw-r--r-- | src/backend/utils/adt/format_type.c | 145 | ||||
-rw-r--r-- | src/backend/utils/adt/numeric.c | 65 | ||||
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 244 | ||||
-rw-r--r-- | src/backend/utils/adt/varbit.c | 87 | ||||
-rw-r--r-- | src/backend/utils/adt/varchar.c | 89 | ||||
-rw-r--r-- | src/backend/utils/cache/lsyscache.c | 56 | ||||
-rw-r--r-- | src/backend/utils/misc/guc.c | 8 |
9 files changed, 708 insertions, 105 deletions
diff --git a/src/backend/utils/adt/arrayutils.c b/src/backend/utils/adt/arrayutils.c index 2a732ad63bb..2913d0a1d8b 100644 --- a/src/backend/utils/adt/arrayutils.c +++ b/src/backend/utils/adt/arrayutils.c @@ -8,13 +8,14 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/arrayutils.c,v 1.21 2006/03/05 15:58:41 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/arrayutils.c,v 1.22 2006/12/30 21:21:54 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "catalog/pg_type.h" #include "utils/array.h" #include "utils/memutils.h" @@ -188,3 +189,30 @@ mda_next_tuple(int n, int *curr, const int *span) return -1; } + +/* + * ArrayGetTypmods: verify that argument is a 1-D integer array, + * return its length and a pointer to the first contained integer. + */ +int32 * +ArrayGetTypmods(ArrayType *arr, int *n) +{ + if (ARR_ELEMTYPE(arr) != INT4OID) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), + errmsg("typmod array must be type integer[]"))); + + if (ARR_NDIM(arr) != 1) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + errmsg("typmod array must be one-dimensional"))); + + if (ARR_HASNULL(arr)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("typmod array must not contain nulls"))); + + *n = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)); + + return (int32 *) ARR_DATA_PTR(arr); +} diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 9efc7125b19..e2781dec485 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.125 2006/07/14 14:52:23 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.126 2006/12/30 21:21:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,6 +24,7 @@ #include "libpq/pqformat.h" #include "miscadmin.h" #include "parser/scansup.h" +#include "utils/array.h" #include "utils/builtins.h" #include "utils/date.h" #include "utils/nabstime.h" @@ -43,6 +44,60 @@ static int tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result); static int tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result); static void AdjustTimeForTypmod(TimeADT *time, int32 typmod); + +/* common code for timetypmodin and timetztypmodin */ +static int32 +anytime_typmodin(bool istz, ArrayType *ta) +{ + int32 typmod; + int32 *tl; + int n; + + tl = ArrayGetTypmods(ta, &n); + + /* + * we're not too tense about good error message here because grammar + * shouldn't allow wrong number of modifiers for TIME + */ + if (n != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid type modifier"))); + + if (*tl < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("TIME(%d)%s precision must not be negative", + *tl, (istz ? " WITH TIME ZONE" : "")))); + if (*tl > MAX_TIME_PRECISION) + { + ereport(WARNING, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("TIME(%d)%s precision reduced to maximum allowed, %d", + *tl, (istz ? " WITH TIME ZONE" : "" ), + MAX_TIME_PRECISION))); + typmod = MAX_TIME_PRECISION; + } else + typmod = *tl; + + return typmod; +} + +/* common code for timetypmodout and timetztypmodout */ +static char * +anytime_typmodout(bool istz, int32 typmod) +{ + char *res = (char *) palloc(64); + const char *tz = istz ? " with time zone" : " without time zone"; + + if (typmod >= 0) + snprintf(res, 64, "(%d)%s", (int) typmod, tz); + else + snprintf(res, 64, "%s", tz); + return res; +} + + /***************************************************************************** * Date ADT *****************************************************************************/ @@ -1029,6 +1084,22 @@ time_send(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } +Datum +timetypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anytime_typmodin(false, ta)); +} + +Datum +timetypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anytime_typmodout(false, typmod)); +} + /* time_scale() * Adjust time type for specified scale factor. @@ -1830,6 +1901,22 @@ timetz_send(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } +Datum +timetztypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anytime_typmodin(true, ta)); +} + +Datum +timetztypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anytime_typmodout(true, typmod)); +} + /* timetz2tm() * Convert TIME WITH TIME ZONE data type to POSIX time structure. diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c index 22d9bc156f6..d2b6323f0f2 100644 --- a/src/backend/utils/adt/format_type.c +++ b/src/backend/utils/adt/format_type.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/format_type.c,v 1.44 2006/07/14 14:52:24 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/format_type.c,v 1.45 2006/12/30 21:21:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,7 +20,6 @@ #include "catalog/namespace.h" #include "catalog/pg_type.h" #include "utils/builtins.h" -#include "utils/datetime.h" #include "utils/lsyscache.h" #include "utils/numeric.h" #include "utils/syscache.h" @@ -31,6 +30,7 @@ static char *format_type_internal(Oid type_oid, int32 typemod, bool typemod_given, bool allow_invalid); +static char *printTypmod(const char *typname, int32 typmod, Oid typmodout); static char * psnprintf(size_t len, const char *fmt,...) /* This lets gcc check the format string for consistency. */ @@ -186,8 +186,7 @@ format_type_internal(Oid type_oid, int32 typemod, { case BITOID: if (with_typemod) - buf = psnprintf(5 + MAX_INT32_LEN + 1, "bit(%d)", - (int) typemod); + buf = printTypmod("bit", typemod, typeform->typmodout); else if (typemod_given) { /* @@ -206,8 +205,7 @@ format_type_internal(Oid type_oid, int32 typemod, case BPCHAROID: if (with_typemod) - buf = psnprintf(11 + MAX_INT32_LEN + 1, "character(%d)", - (int) (typemod - VARHDRSZ)); + buf = printTypmod("character", typemod, typeform->typmodout); else if (typemod_given) { /* @@ -242,136 +240,56 @@ format_type_internal(Oid type_oid, int32 typemod, case NUMERICOID: if (with_typemod) - buf = psnprintf(10 + 2 * MAX_INT32_LEN + 1, "numeric(%d,%d)", - ((typemod - VARHDRSZ) >> 16) & 0xffff, - (typemod - VARHDRSZ) & 0xffff); + buf = printTypmod("numeric", typemod, typeform->typmodout); else buf = pstrdup("numeric"); break; case INTERVALOID: if (with_typemod) - { - int fields = INTERVAL_RANGE(typemod); - int precision = INTERVAL_PRECISION(typemod); - const char *fieldstr; - - switch (fields) - { - case INTERVAL_MASK(YEAR): - fieldstr = " year"; - break; - case INTERVAL_MASK(MONTH): - fieldstr = " month"; - break; - case INTERVAL_MASK(DAY): - fieldstr = " day"; - break; - case INTERVAL_MASK(HOUR): - fieldstr = " hour"; - break; - case INTERVAL_MASK(MINUTE): - fieldstr = " minute"; - break; - case INTERVAL_MASK(SECOND): - fieldstr = " second"; - break; - case INTERVAL_MASK(YEAR) - | INTERVAL_MASK(MONTH): - fieldstr = " year to month"; - break; - case INTERVAL_MASK(DAY) - | INTERVAL_MASK(HOUR): - fieldstr = " day to hour"; - break; - case INTERVAL_MASK(DAY) - | INTERVAL_MASK(HOUR) - | INTERVAL_MASK(MINUTE): - fieldstr = " day to minute"; - break; - case INTERVAL_MASK(DAY) - | INTERVAL_MASK(HOUR) - | INTERVAL_MASK(MINUTE) - | INTERVAL_MASK(SECOND): - fieldstr = " day to second"; - break; - case INTERVAL_MASK(HOUR) - | INTERVAL_MASK(MINUTE): - fieldstr = " hour to minute"; - break; - case INTERVAL_MASK(HOUR) - | INTERVAL_MASK(MINUTE) - | INTERVAL_MASK(SECOND): - fieldstr = " hour to second"; - break; - case INTERVAL_MASK(MINUTE) - | INTERVAL_MASK(SECOND): - fieldstr = " minute to second"; - break; - case INTERVAL_FULL_RANGE: - fieldstr = ""; - break; - default: - elog(ERROR, "invalid INTERVAL typmod: 0x%x", typemod); - fieldstr = ""; - break; - } - if (precision != INTERVAL_FULL_PRECISION) - buf = psnprintf(100, "interval(%d)%s", - precision, fieldstr); - else - buf = psnprintf(100, "interval%s", - fieldstr); - } + buf = printTypmod("interval", typemod, typeform->typmodout); else buf = pstrdup("interval"); break; case TIMEOID: if (with_typemod) - buf = psnprintf(50, "time(%d) without time zone", - typemod); + buf = printTypmod("time", typemod, typeform->typmodout); else buf = pstrdup("time without time zone"); break; case TIMETZOID: if (with_typemod) - buf = psnprintf(50, "time(%d) with time zone", - typemod); + buf = printTypmod("time", typemod, typeform->typmodout); else buf = pstrdup("time with time zone"); break; case TIMESTAMPOID: if (with_typemod) - buf = psnprintf(50, "timestamp(%d) without time zone", - typemod); + buf = printTypmod("timestamp", typemod, typeform->typmodout); else buf = pstrdup("timestamp without time zone"); break; case TIMESTAMPTZOID: if (with_typemod) - buf = psnprintf(50, "timestamp(%d) with time zone", - typemod); + buf = printTypmod("timestamp", typemod, typeform->typmodout); else buf = pstrdup("timestamp with time zone"); break; case VARBITOID: if (with_typemod) - buf = psnprintf(13 + MAX_INT32_LEN + 1, "bit varying(%d)", - (int) typemod); + buf = printTypmod("bit varying", typemod, typeform->typmodout); else buf = pstrdup("bit varying"); break; case VARCHAROID: if (with_typemod) - buf = psnprintf(19 + MAX_INT32_LEN + 1, - "character varying(%d)", - (int) (typemod - VARHDRSZ)); + buf = printTypmod("character varying", typemod, typeform->typmodout); else buf = pstrdup("character varying"); break; @@ -396,6 +314,9 @@ format_type_internal(Oid type_oid, int32 typemod, typname = NameStr(typeform->typname); buf = quote_qualified_identifier(nspname, typname); + + if (with_typemod) + buf = printTypmod(buf, typemod, typeform->typmodout); } if (is_array) @@ -408,6 +329,38 @@ format_type_internal(Oid type_oid, int32 typemod, /* + * Add typmod decoration to the basic type name + */ +static char * +printTypmod(const char *typname, int32 typmod, Oid typmodout) +{ + char *res; + + /* Shouldn't be called if typmod is -1 */ + Assert(typmod >= 0); + + if (typmodout == InvalidOid) + { + /* Default behavior: just print the integer typmod with parens */ + res = psnprintf(strlen(typname) + MAX_INT32_LEN + 3, "%s(%d)", + typname, (int) typmod); + } + else + { + /* Use the type-specific typmodout procedure */ + char *tmstr; + + tmstr = DatumGetCString(OidFunctionCall1(typmodout, + Int32GetDatum(typmod))); + res = psnprintf(strlen(typname) + strlen(tmstr) + 1, "%s%s", + typname, tmstr); + } + + return res; +} + + +/* * type_maximum_size --- determine maximum width of a variable-width column * * If the max width is indeterminate, return -1. In particular, we return @@ -417,7 +370,9 @@ format_type_internal(Oid type_oid, int32 typemod, * * This may appear unrelated to format_type(), but in fact the two routines * share knowledge of the encoding of typmod for different types, so it's - * convenient to keep them together. + * convenient to keep them together. (XXX now that most of this knowledge + * has been pushed out of format_type into the typmodout functions, it's + * interesting to wonder if it's worth trying to factor this code too...) */ int32 type_maximum_size(Oid type_oid, int32 typemod) diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 35b0221b85d..11dd881011f 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -14,7 +14,7 @@ * Copyright (c) 1998-2006, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.96 2006/10/04 00:29:59 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.97 2006/12/30 21:21:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -470,7 +470,7 @@ numeric_send(PG_FUNCTION_ARGS) * scale of the attribute have to be applied on the value. */ Datum -numeric (PG_FUNCTION_ARGS) +numeric(PG_FUNCTION_ARGS) { Numeric num = PG_GETARG_NUMERIC(0); int32 typmod = PG_GETARG_INT32(1); @@ -537,6 +537,67 @@ numeric (PG_FUNCTION_ARGS) PG_RETURN_NUMERIC(new); } +Datum +numerictypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + int32 *tl; + int n; + int32 typmod; + + tl = ArrayGetTypmods(ta, &n); + + if (n == 2) + { + if (tl[0] < 1 || tl[0] > NUMERIC_MAX_PRECISION) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("NUMERIC precision %d must be between 1 and %d", + tl[0], NUMERIC_MAX_PRECISION))); + if (tl[1] < 0 || tl[1] > tl[0]) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("NUMERIC scale %d must be between 0 and precision %d", + tl[1], tl[0]))); + typmod = ((tl[0] << 16) | tl[1]) + VARHDRSZ; + } + else if (n == 1) + { + if (tl[0] < 1 || tl[0] > NUMERIC_MAX_PRECISION) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("NUMERIC precision %d must be between 1 and %d", + tl[0], NUMERIC_MAX_PRECISION))); + /* scale defaults to zero */ + typmod = (tl[0] << 16) + VARHDRSZ; + } + else + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid NUMERIC type modifier"))); + typmod = 0; /* keep compiler quiet */ + } + + PG_RETURN_INT32(typmod); +} + +Datum +numerictypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + char *res = (char *) palloc(64); + + if (typmod >= 0) + snprintf(res, 64, "(%d,%d)", + ((typmod - VARHDRSZ) >> 16) & 0xffff, + (typmod - VARHDRSZ) & 0xffff); + else + *res = '\0'; + + PG_RETURN_CSTRING(res); +} + /* ---------------------------------------------------------------------- * diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index f94413e3f32..f9b0bb2c992 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.169 2006/11/11 01:14:19 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.170 2006/12/30 21:21:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -56,6 +56,60 @@ static void AdjustIntervalForTypmod(Interval *interval, int32 typmod); static TimestampTz timestamp2timestamptz(Timestamp timestamp); +/* common code for timestamptypmodin and timestamptztypmodin */ +static int32 +anytimestamp_typmodin(bool istz, ArrayType *ta) +{ + int32 typmod; + int32 *tl; + int n; + + tl = ArrayGetTypmods(ta, &n); + + /* + * we're not too tense about good error message here because grammar + * shouldn't allow wrong number of modifiers for TIMESTAMP + */ + if (n != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid type modifier"))); + + if (*tl < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("TIMESTAMP(%d)%s precision must not be negative", + *tl, (istz ? " WITH TIME ZONE" : "")))); + if (*tl > MAX_TIMESTAMP_PRECISION) + { + ereport(WARNING, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("TIMESTAMP(%d)%s precision reduced to maximum allowed, %d", + *tl, (istz ? " WITH TIME ZONE" : ""), + MAX_TIMESTAMP_PRECISION))); + typmod = MAX_TIMESTAMP_PRECISION; + } else + typmod = *tl; + + return typmod; +} + +/* common code for timestamptypmodout and timestamptztypmodout */ +static char * +anytimestamp_typmodout(bool istz, int32 typmod) +{ + char *res = (char *) palloc(64); + const char *tz = istz ? " with time zone" : " without time zone"; + + if (typmod >= 0) + snprintf(res, 64, "(%d)%s", (int) typmod, tz); + else + snprintf(res, 64, "%s", tz); + + return res; +} + + /***************************************************************************** * USER I/O ROUTINES * *****************************************************************************/ @@ -215,6 +269,22 @@ timestamp_send(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } +Datum +timestamptypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anytimestamp_typmodin(false, ta)); +} + +Datum +timestamptypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anytimestamp_typmodout(false, typmod)); +} + /* timestamp_scale() * Adjust time type for specified scale factor. @@ -461,6 +531,22 @@ timestamptz_send(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } +Datum +timestamptztypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anytimestamp_typmodin(true, ta)); +} + +Datum +timestamptztypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anytimestamp_typmodout(true, typmod)); +} + /* timestamptz_scale() * Adjust time type for specified scale factor. @@ -625,6 +711,162 @@ interval_send(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } +Datum +intervaltypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + int32 *tl; + int n; + int32 typmod; + + tl = ArrayGetTypmods(ta, &n); + + /* + * tl[0] - opt_interval + * tl[1] - Iconst (optional) + * + * Note we must validate tl[0] even though it's normally guaranteed + * correct by the grammar --- consider SELECT 'foo'::"interval"(1000). + */ + if (n > 0) + { + switch (tl[0]) + { + case INTERVAL_MASK(YEAR): + case INTERVAL_MASK(MONTH): + case INTERVAL_MASK(DAY): + case INTERVAL_MASK(HOUR): + case INTERVAL_MASK(MINUTE): + case INTERVAL_MASK(SECOND): + case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH): + case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR): + case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): + case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): + case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): + case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): + case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): + case INTERVAL_FULL_RANGE: + /* all OK */ + break; + default: + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid INTERVAL type modifier"))); + } + } + + if (n == 1) + { + if (tl[0] != INTERVAL_FULL_RANGE) + typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, tl[0]); + else + typmod = -1; + } + else if (n == 2) + { + if (tl[1] < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("INTERVAL(%d) precision must not be negative", + tl[1]))); + if (tl[1] > MAX_INTERVAL_PRECISION) + { + ereport(WARNING, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d", + tl[1], MAX_INTERVAL_PRECISION))); + typmod = INTERVAL_TYPMOD(MAX_INTERVAL_PRECISION, tl[0]); + } + else + typmod = INTERVAL_TYPMOD(tl[1], tl[0]); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid INTERVAL type modifier"))); + typmod = 0; /* keep compiler quiet */ + } + + PG_RETURN_INT32(typmod); +} + +Datum +intervaltypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + char *res = (char *) palloc(64); + int fields; + int precision; + const char *fieldstr; + + if (typmod < 0) + { + *res = '\0'; + PG_RETURN_CSTRING(res); + } + + fields = INTERVAL_RANGE(typmod); + precision = INTERVAL_PRECISION(typmod); + + switch (fields) + { + case INTERVAL_MASK(YEAR): + fieldstr = " year"; + break; + case INTERVAL_MASK(MONTH): + fieldstr = " month"; + break; + case INTERVAL_MASK(DAY): + fieldstr = " day"; + break; + case INTERVAL_MASK(HOUR): + fieldstr = " hour"; + break; + case INTERVAL_MASK(MINUTE): + fieldstr = " minute"; + break; + case INTERVAL_MASK(SECOND): + fieldstr = " second"; + break; + case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH): + fieldstr = " year to month"; + break; + case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR): + fieldstr = " day to hour"; + break; + case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): + fieldstr = " day to minute"; + break; + case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): + fieldstr = " day to second"; + break; + case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): + fieldstr = " hour to minute"; + break; + case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): + fieldstr = " hour to second"; + break; + case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): + fieldstr = " minute to second"; + break; + case INTERVAL_FULL_RANGE: + fieldstr = ""; + break; + default: + elog(ERROR, "invalid INTERVAL typmod: 0x%x", typmod); + fieldstr = ""; + break; + } + + if (precision != INTERVAL_FULL_PRECISION) + snprintf(res, 64, "(%d)%s", precision, fieldstr); + else + snprintf(res, 64, "%s", fieldstr); + + PG_RETURN_CSTRING(res); +} + /* interval_scale() * Adjust interval type for specified fields. diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c index e0a67d340ef..4a810b955da 100644 --- a/src/backend/utils/adt/varbit.c +++ b/src/backend/utils/adt/varbit.c @@ -9,19 +9,71 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/varbit.c,v 1.50 2006/07/14 14:52:24 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/varbit.c,v 1.51 2006/12/30 21:21:54 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "access/htup.h" #include "libpq/pqformat.h" +#include "utils/array.h" #include "utils/varbit.h" #define HEXDIG(z) ((z)<10 ? ((z)+'0') : ((z)-10+'A')) +/* common code for bittypmodin and varbittypmodin */ +static int32 +anybit_typmodin(ArrayType *ta, const char *typename) +{ + int32 typmod; + int32 *tl; + int n; + + tl = ArrayGetTypmods(ta, &n); + + /* + * we're not too tense about good error message here because grammar + * shouldn't allow wrong number of modifiers for BIT + */ + if (n != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid type modifier"))); + + if (*tl < 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("length for type %s must be at least 1", + typename))); + if (*tl > (MaxAttrSize * BITS_PER_BYTE)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("length for type %s cannot exceed %d", + typename, MaxAttrSize * BITS_PER_BYTE))); + + typmod = *tl; + + return typmod; +} + +/* common code for bittypmodout and varbittypmodout */ +static char * +anybit_typmodout(int32 typmod) +{ + char *res = (char *) palloc(64); + + if (typmod >= 0) + snprintf(res, 64, "(%d)", typmod); + else + *res = '\0'; + + return res; +} + + /*---------- * attypmod -- contains the length of the bit string in bits, or for * varying bits the maximum length. @@ -325,6 +377,23 @@ bit(PG_FUNCTION_ARGS) PG_RETURN_VARBIT_P(result); } +Datum +bittypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anybit_typmodin(ta, "bit")); +} + +Datum +bittypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anybit_typmodout(typmod)); +} + + /* * varbit_in - * converts a string to the internal representation of a bitstring. @@ -603,6 +672,22 @@ varbit(PG_FUNCTION_ARGS) PG_RETURN_VARBIT_P(result); } +Datum +varbittypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anybit_typmodin(ta, "varbit")); +} + +Datum +varbittypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anybit_typmodout(typmod)); +} + /* * Comparison operators diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c index 937cf96ebef..9cc2f5e34e3 100644 --- a/src/backend/utils/adt/varchar.c +++ b/src/backend/utils/adt/varchar.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/varchar.c,v 1.119 2006/10/04 00:30:00 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/varchar.c,v 1.120 2006/12/30 21:21:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,10 +17,65 @@ #include "access/hash.h" #include "libpq/pqformat.h" +#include "utils/array.h" #include "utils/builtins.h" #include "mb/pg_wchar.h" +/* common code for bpchartypmodin and varchartypmodin */ +static int32 +anychar_typmodin(ArrayType *ta, const char *typename) +{ + int32 typmod; + int32 *tl; + int n; + + tl = ArrayGetTypmods(ta, &n); + + /* + * we're not too tense about good error message here because grammar + * shouldn't allow wrong number of modifiers for CHAR + */ + if (n != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid type modifier"))); + + if (*tl < 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("length for type %s must be at least 1", typename))); + if (*tl > MaxAttrSize) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("length for type %s cannot exceed %d", + typename, MaxAttrSize))); + + /* + * For largely historical reasons, the typmod is VARHDRSZ plus the + * number of characters; there is enough client-side code that knows + * about that that we'd better not change it. + */ + typmod = VARHDRSZ + *tl; + + return typmod; +} + +/* common code for bpchartypmodout and varchartypmodout */ +static char * +anychar_typmodout(int32 typmod) +{ + char *res = (char *) palloc(64); + + if (typmod > VARHDRSZ) + snprintf(res, 64, "(%d)", (int) (typmod - VARHDRSZ)); + else + *res = '\0'; + + return res; +} + + /* * CHAR() and VARCHAR() types are part of the ANSI SQL standard. CHAR() * is for blank-padded string whose length is specified in CREATE TABLE. @@ -359,6 +414,22 @@ name_bpchar(PG_FUNCTION_ARGS) PG_RETURN_BPCHAR_P(result); } +Datum +bpchartypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anychar_typmodin(ta, "char")); +} + +Datum +bpchartypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anychar_typmodout(typmod)); +} + /***************************************************************************** * varchar - varchar(n) @@ -536,6 +607,22 @@ varchar(PG_FUNCTION_ARGS) PG_RETURN_VARCHAR_P(result); } +Datum +varchartypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anychar_typmodin(ta, "varchar")); +} + +Datum +varchartypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anychar_typmodout(typmod)); +} + /***************************************************************************** * Exported functions diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 824ef4a1efe..8c4cbef66cb 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.139 2006/12/23 00:43:11 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.140 2006/12/30 21:21:54 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -2016,6 +2016,60 @@ getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena) ReleaseSysCache(typeTuple); } +/* + * get_typmodin + * + * Given the type OID, return the type's typmodin procedure, if any. + */ +Oid +get_typmodin(Oid typid) +{ + HeapTuple tp; + + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + Oid result; + + result = typtup->typmodin; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; +} + +#ifdef NOT_USED +/* + * get_typmodout + * + * Given the type OID, return the type's typmodout procedure, if any. + */ +Oid +get_typmodout(Oid typid) +{ + HeapTuple tp; + + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + Oid result; + + result = typtup->typmodout; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; +} +#endif /* NOT_USED */ + /* ---------- STATISTICS CACHE ---------- */ diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 6c6ff1fb5a3..377c6d87c3a 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut <peter_e@gmx.net>. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.363 2006/12/23 00:52:40 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.364 2006/12/30 21:21:54 tgl Exp $ * *-------------------------------------------------------------------- */ @@ -45,6 +45,7 @@ #include "parser/gramparse.h" #include "parser/parse_expr.h" #include "parser/parse_relation.h" +#include "parser/parse_type.h" #include "parser/scansup.h" #include "pgstat.h" #include "postmaster/autovacuum.h" @@ -4523,14 +4524,17 @@ flatten_set_variable_args(const char *name, List *args) * to interval and back to normalize the value and account * for any typmod. */ + int32 typmod; Datum interval; char *intervalout; + typmod = typenameTypeMod(NULL, arg->typename, INTERVALOID); + interval = DirectFunctionCall3(interval_in, CStringGetDatum(val), ObjectIdGetDatum(InvalidOid), - Int32GetDatum(arg->typename->typmod)); + Int32GetDatum(typmod)); intervalout = DatumGetCString(DirectFunctionCall1(interval_out, |