diff options
| author | Tom Lane | 2002-09-18 21:35:25 +0000 |
|---|---|---|
| committer | Tom Lane | 2002-09-18 21:35:25 +0000 |
| commit | b26dfb95222fddd25322bdddf3a5a58d3392d8b1 (patch) | |
| tree | 757cf0bafab985d38a5c84d3afebe5edd34c4f27 /src/backend/utils | |
| parent | cc70ba2e4daa78ba99619770e19beb06de3dfd1c (diff) | |
Extend pg_cast castimplicit column to a three-way value; this allows us
to be flexible about assignment casts without introducing ambiguity in
operator/function resolution. Introduce a well-defined promotion hierarchy
for numeric datatypes (int2->int4->int8->numeric->float4->float8).
Change make_const to initially label numeric literals as int4, int8, or
numeric (never float8 anymore).
Explicitly mark Func and RelabelType nodes to indicate whether they came
from a function call, explicit cast, or implicit cast; use this to do
reverse-listing more accurately and without so many heuristics.
Explicit casts to char, varchar, bit, varbit will truncate or pad without
raising an error (the pre-7.2 behavior), while assigning to a column without
any explicit cast will still raise an error for wrong-length data like 7.3.
This more nearly follows the SQL spec than 7.2 behavior (we should be
reporting a 'completion condition' in the explicit-cast cases, but we have
no mechanism for that, so just do silent truncation).
Fix some problems with enforcement of typmod for array elements;
it didn't work at all in 'UPDATE ... SET array[n] = foo', for example.
Provide a generalized array_length_coerce() function to replace the
specialized per-array-type functions that used to be needed (and were
missing for NUMERIC as well as all the datetime types).
Add missing conversions int8<->float4, text<->numeric, oid<->int8.
initdb forced.
Diffstat (limited to 'src/backend/utils')
| -rw-r--r-- | src/backend/utils/adt/arrayfuncs.c | 69 | ||||
| -rw-r--r-- | src/backend/utils/adt/int8.c | 137 | ||||
| -rw-r--r-- | src/backend/utils/adt/numeric.c | 46 | ||||
| -rw-r--r-- | src/backend/utils/adt/regproc.c | 68 | ||||
| -rw-r--r-- | src/backend/utils/adt/ruleutils.c | 202 | ||||
| -rw-r--r-- | src/backend/utils/adt/varbit.c | 121 | ||||
| -rw-r--r-- | src/backend/utils/adt/varchar.c | 130 | ||||
| -rw-r--r-- | src/backend/utils/cache/lsyscache.c | 47 |
8 files changed, 422 insertions, 398 deletions
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 5d53eca999f..f8643388970 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.80 2002/09/04 20:31:27 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.81 2002/09/18 21:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,6 +19,7 @@ #include "access/tupmacs.h" #include "catalog/catalog.h" #include "catalog/pg_type.h" +#include "parser/parse_coerce.h" #include "utils/array.h" #include "utils/memutils.h" #include "utils/syscache.h" @@ -755,6 +756,72 @@ array_out(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(retval); } +/*------------------------------------------------------------------------- + * array_length_coerce : + * Apply the element type's length-coercion routine to each element + * of the given array. + *------------------------------------------------------------------------- + */ +Datum +array_length_coerce(PG_FUNCTION_ARGS) +{ + ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); + int32 len = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + FmgrInfo *fmgr_info = fcinfo->flinfo; + FmgrInfo *element_finfo; + FunctionCallInfoData locfcinfo; + + /* If no typmod is provided, shortcircuit the whole thing */ + if (len < 0) + PG_RETURN_ARRAYTYPE_P(v); + + /* + * We arrange to look up the element type's coercion function only + * once per series of calls. + */ + if (fmgr_info->fn_extra == NULL) + { + Oid funcId; + int nargs; + + fmgr_info->fn_extra = MemoryContextAlloc(fmgr_info->fn_mcxt, + sizeof(FmgrInfo)); + element_finfo = (FmgrInfo *) fmgr_info->fn_extra; + + funcId = find_typmod_coercion_function(ARR_ELEMTYPE(v), &nargs); + + if (OidIsValid(funcId)) + fmgr_info_cxt(funcId, element_finfo, fmgr_info->fn_mcxt); + else + element_finfo->fn_oid = InvalidOid; + } + else + element_finfo = (FmgrInfo *) fmgr_info->fn_extra; + + /* + * If we didn't find a coercion function, return the array unmodified + * (this should not happen in the normal course of things, but might + * happen if this function is called manually). + */ + if (element_finfo->fn_oid == InvalidOid) + PG_RETURN_ARRAYTYPE_P(v); + + /* + * Use array_map to apply the function to each array element. + * + * Note: we pass isExplicit whether or not the function wants it ... + */ + MemSet(&locfcinfo, 0, sizeof(locfcinfo)); + locfcinfo.flinfo = element_finfo; + locfcinfo.nargs = 3; + locfcinfo.arg[0] = PointerGetDatum(v); + locfcinfo.arg[1] = Int32GetDatum(len); + locfcinfo.arg[2] = BoolGetDatum(isExplicit); + + return array_map(&locfcinfo, ARR_ELEMTYPE(v), ARR_ELEMTYPE(v)); +} + /*----------------------------------------------------------------------------- * array_dims : * returns the dimensions of the array pointed to by "v", as a "text" diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c index 267ad821037..8a346cd8b83 100644 --- a/src/backend/utils/adt/int8.c +++ b/src/backend/utils/adt/int8.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/int8.c,v 1.41 2002/09/04 20:31:28 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/int8.c,v 1.42 2002/09/18 21:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -48,14 +48,16 @@ * Formatting and conversion routines. *---------------------------------------------------------*/ -/* int8in() +/* + * scanint8 --- try to parse a string into an int8. + * + * If errorOK is false, elog a useful error message if the string is bad. + * If errorOK is true, just return "false" for bad input. */ -Datum -int8in(PG_FUNCTION_ARGS) +bool +scanint8(const char *str, bool errorOK, int64 *result) { - char *str = PG_GETARG_CSTRING(0); - int64 result; - char *ptr = str; + const char *ptr = str; int64 tmp = 0; int sign = 1; @@ -63,8 +65,11 @@ int8in(PG_FUNCTION_ARGS) * Do our own scan, rather than relying on sscanf which might be * broken for long long. */ - while (*ptr && isspace((unsigned char) *ptr)) /* skip leading spaces */ + + /* skip leading spaces */ + while (*ptr && isspace((unsigned char) *ptr)) ptr++; + /* handle sign */ if (*ptr == '-') { @@ -79,28 +84,61 @@ int8in(PG_FUNCTION_ARGS) #ifndef INT64_IS_BUSTED if (strcmp(ptr, "9223372036854775808") == 0) { - result = -INT64CONST(0x7fffffffffffffff) - 1; - PG_RETURN_INT64(result); + *result = -INT64CONST(0x7fffffffffffffff) - 1; + return true; } #endif } else if (*ptr == '+') ptr++; - if (!isdigit((unsigned char) *ptr)) /* require at least one digit */ - elog(ERROR, "Bad int8 external representation \"%s\"", str); - while (*ptr && isdigit((unsigned char) *ptr)) /* process digits */ + + /* require at least one digit */ + if (!isdigit((unsigned char) *ptr)) + { + if (errorOK) + return false; + else + elog(ERROR, "Bad int8 external representation \"%s\"", str); + } + + /* process digits */ + while (*ptr && isdigit((unsigned char) *ptr)) { int64 newtmp = tmp * 10 + (*ptr++ - '0'); if ((newtmp / 10) != tmp) /* overflow? */ - elog(ERROR, "int8 value out of range: \"%s\"", str); + { + if (errorOK) + return false; + else + elog(ERROR, "int8 value out of range: \"%s\"", str); + } tmp = newtmp; } - if (*ptr) /* trailing junk? */ - elog(ERROR, "Bad int8 external representation \"%s\"", str); - result = (sign < 0) ? -tmp : tmp; + /* trailing junk? */ + if (*ptr) + { + if (errorOK) + return false; + else + elog(ERROR, "Bad int8 external representation \"%s\"", str); + } + *result = (sign < 0) ? -tmp : tmp; + + return true; +} + +/* int8in() + */ +Datum +int8in(PG_FUNCTION_ARGS) +{ + char *str = PG_GETARG_CSTRING(0); + int64 result; + + (void) scanint8(str, false, &result); PG_RETURN_INT64(result); } @@ -747,7 +785,7 @@ i8tod(PG_FUNCTION_ARGS) } /* dtoi8() - * Convert double float to 8-byte integer. + * Convert float8 to 8-byte integer. */ Datum dtoi8(PG_FUNCTION_ARGS) @@ -771,9 +809,67 @@ dtoi8(PG_FUNCTION_ARGS) PG_RETURN_INT64(result); } -/* text_int8() +Datum +i8tof(PG_FUNCTION_ARGS) +{ + int64 val = PG_GETARG_INT64(0); + float4 result; + + result = val; + + PG_RETURN_FLOAT4(result); +} + +/* ftoi8() + * Convert float4 to 8-byte integer. */ Datum +ftoi8(PG_FUNCTION_ARGS) +{ + float4 val = PG_GETARG_FLOAT4(0); + int64 result; + float8 dval; + + /* Round val to nearest integer (but it's still in float form) */ + dval = rint(val); + + /* + * Does it fit in an int64? Avoid assuming that we have handy + * constants defined for the range boundaries, instead test for + * overflow by reverse-conversion. + */ + result = (int64) dval; + + if ((float8) result != dval) + elog(ERROR, "Floating point conversion to int8 is out of range"); + + PG_RETURN_INT64(result); +} + +Datum +i8tooid(PG_FUNCTION_ARGS) +{ + int64 val = PG_GETARG_INT64(0); + Oid result; + + result = (Oid) val; + + /* Test for overflow by reverse-conversion. */ + if ((int64) result != val) + elog(ERROR, "int8 conversion to OID is out of range"); + + PG_RETURN_OID(result); +} + +Datum +oidtoi8(PG_FUNCTION_ARGS) +{ + Oid val = PG_GETARG_OID(0); + + PG_RETURN_INT64((int64) val); +} + +Datum text_int8(PG_FUNCTION_ARGS) { text *str = PG_GETARG_TEXT_P(0); @@ -793,9 +889,6 @@ text_int8(PG_FUNCTION_ARGS) return result; } - -/* int8_text() - */ Datum int8_text(PG_FUNCTION_ARGS) { diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 228c43c6c46..4ea0fec1c1a 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -5,7 +5,7 @@ * * 1998 Jan Wieck * - * $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.53 2002/09/04 20:31:28 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.54 2002/09/18 21:35:22 tgl Exp $ * * ---------- */ @@ -1709,6 +1709,50 @@ numeric_float4(PG_FUNCTION_ARGS) } +Datum +text_numeric(PG_FUNCTION_ARGS) +{ + text *str = PG_GETARG_TEXT_P(0); + int len; + char *s; + Datum result; + + len = (VARSIZE(str) - VARHDRSZ); + s = palloc(len + 1); + memcpy(s, VARDATA(str), len); + *(s + len) = '\0'; + + result = DirectFunctionCall3(numeric_in, CStringGetDatum(s), + ObjectIdGetDatum(0), Int32GetDatum(-1)); + + pfree(s); + + return result; +} + +Datum +numeric_text(PG_FUNCTION_ARGS) +{ + /* val is numeric, but easier to leave it as Datum */ + Datum val = PG_GETARG_DATUM(0); + char *s; + int len; + text *result; + + s = DatumGetCString(DirectFunctionCall1(numeric_out, val)); + len = strlen(s); + + result = (text *) palloc(VARHDRSZ + len); + + VARATT_SIZEP(result) = len + VARHDRSZ; + memcpy(VARDATA(result), s, len); + + pfree(s); + + PG_RETURN_TEXT_P(result); +} + + /* ---------------------------------------------------------------------- * * Aggregate functions diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c index 22c93c431a0..638f5293ed2 100644 --- a/src/backend/utils/adt/regproc.c +++ b/src/backend/utils/adt/regproc.c @@ -13,7 +13,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.75 2002/09/04 20:31:28 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.76 2002/09/18 21:35:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -49,7 +49,7 @@ static void parseNameAndArgTypes(const char *string, const char *caller, /* * regprocin - converts "proname" to proc OID * - * We also accept a numeric OID, mostly for historical reasons. + * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_proc entry. @@ -71,15 +71,8 @@ regprocin(PG_FUNCTION_ARGS) pro_name_or_oid[0] <= '9' && strspn(pro_name_or_oid, "0123456789") == strlen(pro_name_or_oid)) { - Oid searchOid; - - searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(pro_name_or_oid))); - result = (RegProcedure) GetSysCacheOid(PROCOID, - ObjectIdGetDatum(searchOid), - 0, 0, 0); - if (!RegProcedureIsValid(result)) - elog(ERROR, "No procedure with oid %s", pro_name_or_oid); PG_RETURN_OID(result); } @@ -211,7 +204,7 @@ regprocout(PG_FUNCTION_ARGS) /* * regprocedurein - converts "proname(args)" to proc OID * - * We also accept a numeric OID, mostly for historical reasons. + * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_proc entry. @@ -235,15 +228,8 @@ regprocedurein(PG_FUNCTION_ARGS) pro_name_or_oid[0] <= '9' && strspn(pro_name_or_oid, "0123456789") == strlen(pro_name_or_oid)) { - Oid searchOid; - - searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(pro_name_or_oid))); - result = (RegProcedure) GetSysCacheOid(PROCOID, - ObjectIdGetDatum(searchOid), - 0, 0, 0); - if (!RegProcedureIsValid(result)) - elog(ERROR, "No procedure with oid %s", pro_name_or_oid); PG_RETURN_OID(result); } @@ -361,7 +347,7 @@ regprocedureout(PG_FUNCTION_ARGS) /* * regoperin - converts "oprname" to operator OID * - * We also accept a numeric OID, mostly for historical reasons. + * We also accept a numeric OID, for symmetry with the output routine. * * '0' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_operator entry. @@ -383,15 +369,8 @@ regoperin(PG_FUNCTION_ARGS) opr_name_or_oid[0] <= '9' && strspn(opr_name_or_oid, "0123456789") == strlen(opr_name_or_oid)) { - Oid searchOid; - - searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(opr_name_or_oid))); - result = GetSysCacheOid(OPEROID, - ObjectIdGetDatum(searchOid), - 0, 0, 0); - if (!OidIsValid(result)) - elog(ERROR, "No operator with oid %s", opr_name_or_oid); PG_RETURN_OID(result); } @@ -531,7 +510,7 @@ regoperout(PG_FUNCTION_ARGS) /* * regoperatorin - converts "oprname(args)" to operator OID * - * We also accept a numeric OID, mostly for historical reasons. + * We also accept a numeric OID, for symmetry with the output routine. * * '0' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_operator entry. @@ -556,15 +535,8 @@ regoperatorin(PG_FUNCTION_ARGS) opr_name_or_oid[0] <= '9' && strspn(opr_name_or_oid, "0123456789") == strlen(opr_name_or_oid)) { - Oid searchOid; - - searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(opr_name_or_oid))); - result = GetSysCacheOid(OPEROID, - ObjectIdGetDatum(searchOid), - 0, 0, 0); - if (!OidIsValid(result)) - elog(ERROR, "No operator with oid %s", opr_name_or_oid); PG_RETURN_OID(result); } @@ -698,7 +670,7 @@ regoperatorout(PG_FUNCTION_ARGS) /* * regclassin - converts "classname" to class OID * - * We also accept a numeric OID, mostly for historical reasons. + * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_class entry. @@ -719,15 +691,8 @@ regclassin(PG_FUNCTION_ARGS) class_name_or_oid[0] <= '9' && strspn(class_name_or_oid, "0123456789") == strlen(class_name_or_oid)) { - Oid searchOid; - - searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(class_name_or_oid))); - result = GetSysCacheOid(RELOID, - ObjectIdGetDatum(searchOid), - 0, 0, 0); - if (!OidIsValid(result)) - elog(ERROR, "No class with oid %s", class_name_or_oid); PG_RETURN_OID(result); } @@ -843,7 +808,7 @@ regclassout(PG_FUNCTION_ARGS) /* * regtypein - converts "typename" to type OID * - * We also accept a numeric OID, mostly for historical reasons. + * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_type entry. @@ -870,15 +835,8 @@ regtypein(PG_FUNCTION_ARGS) typ_name_or_oid[0] <= '9' && strspn(typ_name_or_oid, "0123456789") == strlen(typ_name_or_oid)) { - Oid searchOid; - - searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(typ_name_or_oid))); - result = GetSysCacheOid(TYPEOID, - ObjectIdGetDatum(searchOid), - 0, 0, 0); - if (!OidIsValid(result)) - elog(ERROR, "No type with oid %s", typ_name_or_oid); PG_RETURN_OID(result); } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 740dde36dd4..9f21a609f3d 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * back to source text * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.121 2002/09/04 20:31:28 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.122 2002/09/18 21:35:23 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -152,7 +152,6 @@ static void get_oper_expr(Expr *expr, deparse_context *context); static void get_func_expr(Expr *expr, deparse_context *context); static void get_agg_expr(Aggref *aggref, deparse_context *context); static Node *strip_type_coercion(Node *expr, Oid resultType); -static void get_tle_expr(TargetEntry *tle, deparse_context *context); static void get_const_expr(Const *constval, deparse_context *context); static void get_sublink_expr(Node *node, deparse_context *context); static void get_from_clause(Query *query, deparse_context *context); @@ -1430,7 +1429,6 @@ get_basic_select_query(Query *query, deparse_context *context, sep = ", "; colno++; - /* Do NOT use get_tle_expr here; see its comments! */ get_rule_expr(tle->expr, context); /* @@ -1644,7 +1642,7 @@ get_insert_query_def(Query *query, deparse_context *context) appendStringInfo(buf, sep); sep = ", "; - get_tle_expr(tle, context); + get_rule_expr(tle->expr, context); } appendStringInfoChar(buf, ')'); } @@ -1694,7 +1692,7 @@ get_update_query_def(Query *query, deparse_context *context) if (!tleIsArrayAssign(tle)) appendStringInfo(buf, "%s = ", quote_identifier(tle->resdom->resname)); - get_tle_expr(tle, context); + get_rule_expr(tle->expr, context); } /* Add the FROM clause if needed */ @@ -2106,12 +2104,29 @@ get_rule_expr(Node *node, deparse_context *context) case T_RelabelType: { RelabelType *relabel = (RelabelType *) node; + Node *arg = relabel->arg; - appendStringInfoChar(buf, '('); - get_rule_expr(relabel->arg, context); - appendStringInfo(buf, ")::%s", + if (relabel->relabelformat == COERCE_IMPLICIT_CAST) + { + /* don't show an implicit cast */ + get_rule_expr(arg, context); + } + else + { + /* + * Strip off any type coercions on the input, so we don't + * print redundancies like x::bpchar::character(8). + * + * XXX Are there any cases where this is a bad idea? + */ + arg = strip_type_coercion(arg, relabel->resulttype); + + appendStringInfoChar(buf, '('); + get_rule_expr(arg, context); + appendStringInfo(buf, ")::%s", format_type_with_typemod(relabel->resulttype, - relabel->resulttypmod)); + relabel->resulttypmod)); + } } break; @@ -2305,21 +2320,33 @@ get_func_expr(Expr *expr, deparse_context *context) StringInfo buf = context->buf; Func *func = (Func *) (expr->oper); Oid funcoid = func->funcid; - int32 coercedTypmod; Oid argtypes[FUNC_MAX_ARGS]; int nargs; List *l; char *sep; /* - * Check to see if function is a length-coercion function for some - * datatype. If so, display the operation as a type cast. + * If the function call came from an implicit coercion, then just show + * the first argument. + */ + if (func->funcformat == COERCE_IMPLICIT_CAST) + { + get_rule_expr((Node *) lfirst(expr->args), context); + return; + } + + /* + * If the function call came from an explicit cast, then show + * the first argument plus an explicit cast operation. */ - if (exprIsLengthCoercion((Node *) expr, &coercedTypmod)) + if (func->funcformat == COERCE_EXPLICIT_CAST) { Node *arg = lfirst(expr->args); - Oid rettype = get_func_rettype(funcoid); - char *typdesc; + Oid rettype = expr->typeOid; + int32 coercedTypmod; + + /* Get the typmod if this is a length-coercion function */ + (void) exprIsLengthCoercion((Node *) expr, &coercedTypmod); /* * Strip off any type coercions on the input, so we don't print @@ -2331,17 +2358,8 @@ get_func_expr(Expr *expr, deparse_context *context) appendStringInfoChar(buf, '('); get_rule_expr(arg, context); - - /* - * Show typename with appropriate length decoration. Note that - * since exprIsLengthCoercion succeeded, the function's output - * type is the right thing to report. Also note we don't need to - * quote the result of format_type_with_typemod: it takes care of - * double-quoting any identifier that needs it. - */ - typdesc = format_type_with_typemod(rettype, coercedTypmod); - appendStringInfo(buf, ")::%s", typdesc); - pfree(typdesc); + appendStringInfo(buf, ")::%s", + format_type_with_typemod(rettype, coercedTypmod)); return; } @@ -2393,15 +2411,14 @@ get_agg_expr(Aggref *aggref, deparse_context *context) /* * strip_type_coercion - * Strip any type coercions at the top of the given expression tree, - * as long as they are coercions to the given datatype. + * Strip any type coercion at the top of the given expression tree, + * if it is a coercion to the given datatype. * - * A RelabelType node is always a type coercion. A function call is - * also considered a type coercion if it has one argument and there is - * a cast declared that uses it. + * We use this to avoid printing two levels of coercion in situations where + * the expression tree has a length-coercion node atop a type-coercion node. * - * XXX It'd be better if the parsetree retained some explicit indication - * of the coercion, so we didn't need these heuristics. + * Note: avoid stripping a length-coercion node, since two successive + * coercions to different lengths aren't a no-op. */ static Node * strip_type_coercion(Node *expr, Oid resultType) @@ -2409,58 +2426,24 @@ strip_type_coercion(Node *expr, Oid resultType) if (expr == NULL || exprType(expr) != resultType) return expr; - if (IsA(expr, RelabelType)) - return strip_type_coercion(((RelabelType *) expr)->arg, resultType); + if (IsA(expr, RelabelType) && + ((RelabelType *) expr)->resulttypmod == -1) + return ((RelabelType *) expr)->arg; if (IsA(expr, Expr) && ((Expr *) expr)->opType == FUNC_EXPR) { - Func *func; - HeapTuple procTuple; - HeapTuple castTuple; - Form_pg_proc procStruct; - Form_pg_cast castStruct; + Func *func = (Func *) (((Expr *) expr)->oper); - func = (Func *) (((Expr *) expr)->oper); Assert(IsA(func, Func)); - if (length(((Expr *) expr)->args) != 1) - return expr; - /* Lookup the function in pg_proc */ - procTuple = SearchSysCache(PROCOID, - ObjectIdGetDatum(func->funcid), - 0, 0, 0); - if (!HeapTupleIsValid(procTuple)) - elog(ERROR, "cache lookup for proc %u failed", func->funcid); - procStruct = (Form_pg_proc) GETSTRUCT(procTuple); - /* Double-check func has one arg and correct result type */ - if (procStruct->pronargs != 1 || - procStruct->prorettype != resultType) - { - ReleaseSysCache(procTuple); - return expr; - } - /* See if function has is actually declared as a cast */ - castTuple = SearchSysCache(CASTSOURCETARGET, - ObjectIdGetDatum(procStruct->proargtypes[0]), - ObjectIdGetDatum(procStruct->prorettype), - 0, 0); - if (!HeapTupleIsValid(castTuple)) - { - ReleaseSysCache(procTuple); - return expr; - } - /* It must also be an implicit cast. */ - castStruct = (Form_pg_cast) GETSTRUCT(castTuple); - if (!castStruct->castimplicit) - { - ReleaseSysCache(procTuple); - ReleaseSysCache(castTuple); + if (func->funcformat != COERCE_EXPLICIT_CAST && + func->funcformat != COERCE_IMPLICIT_CAST) + return expr; /* don't absorb into upper coercion */ + + if (exprIsLengthCoercion(expr, NULL)) return expr; - } - /* Okay, it is indeed a type-coercion function */ - ReleaseSysCache(procTuple); - ReleaseSysCache(castTuple); - return strip_type_coercion(lfirst(((Expr *) expr)->args), resultType); + + return (Node *) lfirst(((Expr *) expr)->args); } return expr; @@ -2468,43 +2451,6 @@ strip_type_coercion(Node *expr, Oid resultType) /* ---------- - * get_tle_expr - * - * In an INSERT or UPDATE targetlist item, the parser may have inserted - * a length-coercion function call to coerce the value to the right - * length for the target column. We want to suppress the output of - * that function call, otherwise dump/reload/dump... would blow up the - * expression by adding more and more layers of length-coercion calls. - * - * As of 7.0, this hack is no longer absolutely essential, because the parser - * is now smart enough not to add a redundant length coercion function call. - * But we still suppress the function call just for neatness of displayed - * rules. - * - * Note that this hack must NOT be applied to SELECT targetlist items; - * any length coercion appearing there is something the user actually wrote. - * ---------- - */ -static void -get_tle_expr(TargetEntry *tle, deparse_context *context) -{ - Expr *expr = (Expr *) (tle->expr); - int32 coercedTypmod; - - /* - * If top level is a length coercion to the correct length, suppress - * it; else dump the expression normally. - */ - if (tle->resdom->restypmod >= 0 && - exprIsLengthCoercion((Node *) expr, &coercedTypmod) && - coercedTypmod == tle->resdom->restypmod) - get_rule_expr((Node *) lfirst(expr->args), context); - else - get_rule_expr(tle->expr, context); -} - - -/* ---------- * get_const_expr * * Make a string representation of a Const @@ -2518,6 +2464,8 @@ get_const_expr(Const *constval, deparse_context *context) Form_pg_type typeStruct; char *extval; char *valptr; + bool isfloat = false; + bool needlabel; if (constval->constisnull) { @@ -2563,8 +2511,12 @@ get_const_expr(Const *constval, deparse_context *context) * NaN, so we need not get too crazy about pattern * matching here. */ - if (strspn(extval, "0123456789 +-eE.") == strlen(extval)) + if (strspn(extval, "0123456789+-eE.") == strlen(extval)) + { appendStringInfo(buf, extval); + if (strcspn(extval, "eE.") != strlen(extval)) + isfloat = true; /* it looks like a float */ + } else appendStringInfo(buf, "'%s'", extval); } @@ -2609,20 +2561,30 @@ get_const_expr(Const *constval, deparse_context *context) pfree(extval); + /* + * Append ::typename unless the constant will be implicitly typed as + * the right type when it is read in. XXX this code has to be kept + * in sync with the behavior of the parser, especially make_const. + */ switch (constval->consttype) { case BOOLOID: case INT4OID: - case FLOAT8OID: case UNKNOWNOID: /* These types can be left unlabeled */ + needlabel = false; + break; + case NUMERICOID: + /* Float-looking constants will be typed as numeric */ + needlabel = !isfloat; break; default: - appendStringInfo(buf, "::%s", - format_type_with_typemod(constval->consttype, - -1)); + needlabel = true; break; } + if (needlabel) + appendStringInfo(buf, "::%s", + format_type_with_typemod(constval->consttype, -1)); ReleaseSysCache(typetup); } diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c index f0c31a3961b..715a99863fd 100644 --- a/src/backend/utils/adt/varbit.c +++ b/src/backend/utils/adt/varbit.c @@ -9,7 +9,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/varbit.c,v 1.25 2002/09/04 20:31:29 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/varbit.c,v 1.26 2002/09/18 21:35:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -35,6 +35,11 @@ * data section -- private data section for the bits data structures * bitlength -- length of the bit string in bits * bitdata -- bit string, most significant byte first + * + * The length of the bitdata vector should always be exactly as many + * bytes as are needed for the given bitlength. If the bitlength is + * not a multiple of 8, the extra low-order padding bits of the last + * byte must be zeroes. *---------- */ @@ -104,7 +109,7 @@ bit_in(PG_FUNCTION_ARGS) len = VARBITTOTALLEN(atttypmod); result = (VarBit *) palloc(len); /* set to 0 so that *r is always initialised and string is zero-padded */ - memset(result, 0, len); + MemSet(result, 0, len); VARATT_SIZEP(result) = len; VARBITLEN(result) = atttypmod; @@ -203,50 +208,52 @@ bit_out(PG_FUNCTION_ARGS) /* bit() * Converts a bit() type to a specific internal length. * len is the bitlength specified in the column definition. + * + * If doing implicit cast, raise error when source data is wrong length. + * If doing explicit cast, silently truncate or zero-pad to specified length. */ Datum bit(PG_FUNCTION_ARGS) { VarBit *arg = PG_GETARG_VARBIT_P(0); int32 len = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + VarBit *result; + int rlen; + int ipad; + bits8 mask; /* No work if typmod is invalid or supplied data matches it already */ if (len <= 0 || len == VARBITLEN(arg)) PG_RETURN_VARBIT_P(arg); - else + + if (!isExplicit) elog(ERROR, "Bit string length %d does not match type BIT(%d)", VARBITLEN(arg), len); - return 0; /* quiet compiler */ -} -/* _bit() - * Converts an array of bit() elements to a specific internal length. - * len is the bitlength specified in the column definition. - */ -Datum -_bit(PG_FUNCTION_ARGS) -{ - ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); - int32 len = PG_GETARG_INT32(1); - FunctionCallInfoData locfcinfo; + rlen = VARBITTOTALLEN(len); + result = (VarBit *) palloc(rlen); + /* set to 0 so that string is zero-padded */ + MemSet(result, 0, rlen); + VARATT_SIZEP(result) = rlen; + VARBITLEN(result) = len; + + memcpy(VARBITS(result), VARBITS(arg), + Min(VARBITBYTES(result), VARBITBYTES(arg))); /* - * Since bit() is a built-in function, we should only need to look it - * up once per run. + * Make sure last byte is zero-padded if needed. This is useless but + * safe if source data was shorter than target length (we assume the + * last byte of the source data was itself correctly zero-padded). */ - static FmgrInfo bit_finfo; - - if (bit_finfo.fn_oid == InvalidOid) - fmgr_info_cxt(F_BIT, &bit_finfo, TopMemoryContext); - - MemSet(&locfcinfo, 0, sizeof(locfcinfo)); - locfcinfo.flinfo = &bit_finfo; - locfcinfo.nargs = 2; - /* We assume we are "strict" and need not worry about null inputs */ - locfcinfo.arg[0] = PointerGetDatum(v); - locfcinfo.arg[1] = Int32GetDatum(len); + ipad = VARBITPAD(result); + if (ipad > 0) + { + mask = BITMASK << ipad; + *(VARBITS(result) + VARBITBYTES(result) - 1) &= mask; + } - return array_map(&locfcinfo, BITOID, BITOID); + PG_RETURN_VARBIT_P(result); } /* @@ -311,7 +318,7 @@ varbit_in(PG_FUNCTION_ARGS) len = VARBITTOTALLEN(bitlen); result = (VarBit *) palloc(len); /* set to 0 so that *r is always initialised and string is zero-padded */ - memset(result, 0, len); + MemSet(result, 0, len); VARATT_SIZEP(result) = len; VARBITLEN(result) = Min(bitlen, atttypmod); @@ -406,20 +413,26 @@ varbit_out(PG_FUNCTION_ARGS) /* varbit() * Converts a varbit() type to a specific internal length. * len is the maximum bitlength specified in the column definition. + * + * If doing implicit cast, raise error when source data is too long. + * If doing explicit cast, silently truncate to max length. */ Datum varbit(PG_FUNCTION_ARGS) { VarBit *arg = PG_GETARG_VARBIT_P(0); int32 len = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); VarBit *result; int rlen; + int ipad; + bits8 mask; /* No work if typmod is invalid or supplied data matches it already */ if (len <= 0 || len >= VARBITLEN(arg)) PG_RETURN_VARBIT_P(arg); - if (len < VARBITLEN(arg)) + if (!isExplicit) elog(ERROR, "Bit string too long for type BIT VARYING(%d)", len); rlen = VARBITTOTALLEN(len); @@ -429,37 +442,15 @@ varbit(PG_FUNCTION_ARGS) memcpy(VARBITS(result), VARBITS(arg), VARBITBYTES(result)); - PG_RETURN_VARBIT_P(result); -} - -/* _varbit() - * Converts an array of bit() elements to a specific internal length. - * len is the maximum bitlength specified in the column definition. - */ -Datum -_varbit(PG_FUNCTION_ARGS) -{ - ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); - int32 len = PG_GETARG_INT32(1); - FunctionCallInfoData locfcinfo; - - /* - * Since varbit() is a built-in function, we should only need to look - * it up once per run. - */ - static FmgrInfo varbit_finfo; - - if (varbit_finfo.fn_oid == InvalidOid) - fmgr_info_cxt(F_VARBIT, &varbit_finfo, TopMemoryContext); - - MemSet(&locfcinfo, 0, sizeof(locfcinfo)); - locfcinfo.flinfo = &varbit_finfo; - locfcinfo.nargs = 2; - /* We assume we are "strict" and need not worry about null inputs */ - locfcinfo.arg[0] = PointerGetDatum(v); - locfcinfo.arg[1] = Int32GetDatum(len); + /* Make sure last byte is zero-padded if needed */ + ipad = VARBITPAD(result); + if (ipad > 0) + { + mask = BITMASK << ipad; + *(VARBITS(result) + VARBITBYTES(result) - 1) &= mask; + } - return array_map(&locfcinfo, VARBITOID, VARBITOID); + PG_RETURN_VARBIT_P(result); } @@ -978,7 +969,7 @@ bitshiftleft(PG_FUNCTION_ARGS) /* If we shifted all the bits out, return an all-zero string */ if (shft >= VARBITLEN(arg)) { - memset(r, 0, VARBITBYTES(arg)); + MemSet(r, 0, VARBITBYTES(arg)); PG_RETURN_VARBIT_P(result); } @@ -991,7 +982,7 @@ bitshiftleft(PG_FUNCTION_ARGS) /* Special case: we can do a memcpy */ len = VARBITBYTES(arg) - byte_shift; memcpy(r, p, len); - memset(r + len, 0, byte_shift); + MemSet(r + len, 0, byte_shift); } else { @@ -1037,7 +1028,7 @@ bitshiftright(PG_FUNCTION_ARGS) /* If we shifted all the bits out, return an all-zero string */ if (shft >= VARBITLEN(arg)) { - memset(r, 0, VARBITBYTES(arg)); + MemSet(r, 0, VARBITBYTES(arg)); PG_RETURN_VARBIT_P(result); } @@ -1046,7 +1037,7 @@ bitshiftright(PG_FUNCTION_ARGS) p = VARBITS(arg); /* Set the first part of the result to 0 */ - memset(r, 0, byte_shift); + MemSet(r, 0, byte_shift); r += byte_shift; if (ishift == 0) diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c index 780daf75655..03579f437b6 100644 --- a/src/backend/utils/adt/varchar.c +++ b/src/backend/utils/adt/varchar.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.94 2002/09/04 20:31:29 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.95 2002/09/18 21:35:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -165,21 +165,28 @@ bpcharout(PG_FUNCTION_ARGS) /* - * Converts a CHARACTER type to the specified size. maxlen is the new - * declared length plus VARHDRSZ bytes. Truncation - * rules see bpcharin() above. + * Converts a CHARACTER type to the specified size. + * + * maxlen is the typmod, ie, declared length plus VARHDRSZ bytes. + * isExplicit is true if this is for an explicit cast to char(N). + * + * Truncation rules: for an explicit cast, silently truncate to the given + * length; for an implicit cast, raise error unless extra characters are + * all spaces. (This is sort-of per SQL: the spec would actually have us + * raise a "completion condition" for the explicit cast case, but Postgres + * hasn't got such a concept.) */ Datum bpchar(PG_FUNCTION_ARGS) { BpChar *source = PG_GETARG_BPCHAR_P(0); int32 maxlen = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); BpChar *result; int32 len; char *r; char *s; int i; - int charlen; /* number of charcters in the input string * + VARHDRSZ */ @@ -188,7 +195,7 @@ bpchar(PG_FUNCTION_ARGS) charlen = pg_mbstrlen_with_len(VARDATA(source), len - VARHDRSZ) + VARHDRSZ; /* No work if typmod is invalid or supplied data matches it already */ - if (maxlen < (int32) VARHDRSZ || len == maxlen) + if (maxlen < (int32) VARHDRSZ || charlen == maxlen) PG_RETURN_BPCHAR_P(source); if (charlen > maxlen) @@ -199,10 +206,13 @@ bpchar(PG_FUNCTION_ARGS) maxmblen = pg_mbcharcliplen(VARDATA(source), len - VARHDRSZ, maxlen - VARHDRSZ) + VARHDRSZ; - for (i = maxmblen - VARHDRSZ; i < len - VARHDRSZ; i++) - if (*(VARDATA(source) + i) != ' ') - elog(ERROR, "value too long for type character(%d)", - maxlen - VARHDRSZ); + if (!isExplicit) + { + for (i = maxmblen - VARHDRSZ; i < len - VARHDRSZ; i++) + if (*(VARDATA(source) + i) != ' ') + elog(ERROR, "value too long for type character(%d)", + maxlen - VARHDRSZ); + } len = maxmblen; @@ -238,37 +248,6 @@ bpchar(PG_FUNCTION_ARGS) } -/* _bpchar() - * Converts an array of char() elements to a specific internal length. - * len is the length specified in () plus VARHDRSZ bytes. - */ -Datum -_bpchar(PG_FUNCTION_ARGS) -{ - ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); - int32 len = PG_GETARG_INT32(1); - FunctionCallInfoData locfcinfo; - - /* - * Since bpchar() is a built-in function, we should only need to look - * it up once per run. - */ - static FmgrInfo bpchar_finfo; - - if (bpchar_finfo.fn_oid == InvalidOid) - fmgr_info_cxt(F_BPCHAR, &bpchar_finfo, TopMemoryContext); - - MemSet(&locfcinfo, 0, sizeof(locfcinfo)); - locfcinfo.flinfo = &bpchar_finfo; - locfcinfo.nargs = 2; - /* We assume we are "strict" and need not worry about null inputs */ - locfcinfo.arg[0] = PointerGetDatum(v); - locfcinfo.arg[1] = Int32GetDatum(len); - - return array_map(&locfcinfo, BPCHAROID, BPCHAROID); -} - - /* char_bpchar() * Convert char to bpchar(1). */ @@ -354,9 +333,9 @@ name_bpchar(PG_FUNCTION_ARGS) * Note that atttypmod is regarded as the number of characters, which * is not necessarily the same as the number of bytes. * - * If the C string is too long, - * raise an error, unless the extra characters are spaces, in which - * case they're truncated. (per SQL) */ + * If the C string is too long, raise an error, unless the extra characters + * are spaces, in which case they're truncated. (per SQL) + */ Datum varcharin(PG_FUNCTION_ARGS) { @@ -428,17 +407,26 @@ varcharout(PG_FUNCTION_ARGS) /* - * Converts a VARCHAR type to the specified size. maxlen is the new - * declared length plus VARHDRSZ bytes. Truncation - * rules see varcharin() above. + * Converts a VARCHAR type to the specified size. + * + * maxlen is the typmod, ie, declared length plus VARHDRSZ bytes. + * isExplicit is true if this is for an explicit cast to varchar(N). + * + * Truncation rules: for an explicit cast, silently truncate to the given + * length; for an implicit cast, raise error unless extra characters are + * all spaces. (This is sort-of per SQL: the spec would actually have us + * raise a "completion condition" for the explicit cast case, but Postgres + * hasn't got such a concept.) */ Datum varchar(PG_FUNCTION_ARGS) { VarChar *source = PG_GETARG_VARCHAR_P(0); int32 maxlen = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); VarChar *result; int32 len; + size_t maxmblen; int i; len = VARSIZE(source); @@ -448,21 +436,19 @@ varchar(PG_FUNCTION_ARGS) /* only reach here if string is too long... */ - { - size_t maxmblen; - - /* truncate multibyte string preserving multibyte boundary */ - maxmblen = pg_mbcharcliplen(VARDATA(source), len - VARHDRSZ, - maxlen - VARHDRSZ) + VARHDRSZ; + /* truncate multibyte string preserving multibyte boundary */ + maxmblen = pg_mbcharcliplen(VARDATA(source), len - VARHDRSZ, + maxlen - VARHDRSZ); - for (i = maxmblen - VARHDRSZ; i < len - VARHDRSZ; i++) + if (!isExplicit) + { + for (i = maxmblen; i < len - VARHDRSZ; i++) if (*(VARDATA(source) + i) != ' ') elog(ERROR, "value too long for type character varying(%d)", maxlen - VARHDRSZ); - - len = maxmblen; } + len = maxmblen + VARHDRSZ; result = palloc(len); VARATT_SIZEP(result) = len; memcpy(VARDATA(result), VARDATA(source), len - VARHDRSZ); @@ -471,38 +457,6 @@ varchar(PG_FUNCTION_ARGS) } -/* _varchar() - * Converts an array of varchar() elements to the specified size. - * len is the length specified in () plus VARHDRSZ bytes. - */ -Datum -_varchar(PG_FUNCTION_ARGS) -{ - ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); - int32 len = PG_GETARG_INT32(1); - FunctionCallInfoData locfcinfo; - - /* - * Since varchar() is a built-in function, we should only need to look - * it up once per run. - */ - static FmgrInfo varchar_finfo; - - if (varchar_finfo.fn_oid == InvalidOid) - fmgr_info_cxt(F_VARCHAR, &varchar_finfo, TopMemoryContext); - - MemSet(&locfcinfo, 0, sizeof(locfcinfo)); - locfcinfo.flinfo = &varchar_finfo; - locfcinfo.nargs = 2; - /* We assume we are "strict" and need not worry about null inputs */ - locfcinfo.arg[0] = PointerGetDatum(v); - locfcinfo.arg[1] = Int32GetDatum(len); - - return array_map(&locfcinfo, VARCHAROID, VARCHAROID); -} - - - /***************************************************************************** * Exported functions *****************************************************************************/ diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 2672ed3aada..c8a038d8a7d 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 - * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.83 2002/09/04 20:31:30 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.84 2002/09/18 21:35:23 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -1074,51 +1074,6 @@ getBaseType(Oid typid) } /* - * getBaseTypeMod - * If the given type is a domain, return the typmod it applies to - * its base type; otherwise return the specified original typmod. - */ -int32 -getBaseTypeMod(Oid typid, int32 typmod) -{ - /* - * We loop to find the bottom base type in a stack of domains. - */ - for (;;) - { - HeapTuple tup; - Form_pg_type typTup; - - tup = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "getBaseTypeMod: failed to lookup type %u", typid); - typTup = (Form_pg_type) GETSTRUCT(tup); - if (typTup->typtype != 'd') - { - /* Not a domain, so done */ - ReleaseSysCache(tup); - break; - } - - /* - * The typmod applied to a domain should always be -1. - * - * We substitute the domain's typmod as we switch attention to the - * base type. - */ - Assert(typmod < 0); - - typid = typTup->typbasetype; - typmod = typTup->typtypmod; - ReleaseSysCache(tup); - } - - return typmod; -} - -/* * get_typavgwidth * * Given a type OID and a typmod value (pass -1 if typmod is unknown), |
