aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils')
-rw-r--r--src/backend/utils/adt/arrayutils.c30
-rw-r--r--src/backend/utils/adt/date.c89
-rw-r--r--src/backend/utils/adt/format_type.c145
-rw-r--r--src/backend/utils/adt/numeric.c65
-rw-r--r--src/backend/utils/adt/timestamp.c244
-rw-r--r--src/backend/utils/adt/varbit.c87
-rw-r--r--src/backend/utils/adt/varchar.c89
-rw-r--r--src/backend/utils/cache/lsyscache.c56
-rw-r--r--src/backend/utils/misc/guc.c8
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,