Tweak format_type so that we get good behavior for both column type

display (with a typemod) and function arg/result type display (without
a typemod).
This commit is contained in:
Tom Lane 2001-11-19 19:51:20 +00:00
parent 40015cdaae
commit ed1ff84750
2 changed files with 78 additions and 49 deletions

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.22 2001/11/12 21:04:46 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.23 2001/11/19 19:51:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -27,29 +27,17 @@
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
#endif #endif
#define MASK(b) (1 << (b)) #define MASK(b) (1 << (b))
#define MAX_INT32_LEN 11 #define MAX_INT32_LEN 11
#define _textin(str) DirectFunctionCall1(textin, CStringGetDatum(str)) #define _textin(str) DirectFunctionCall1(textin, CStringGetDatum(str))
static char *format_type_internal(Oid type_oid, int32 typemod,
static char *format_type_internal(Oid type_oid, int32 typemod, bool allow_invalid); bool typemod_given, bool allow_invalid);
static char *psnprintf(size_t len, const char *fmt, ...)
/* This lets gcc check the format string for consistency. */
static char * __attribute__((format(printf, 2, 3)));
psnprintf(size_t len, const char *fmt,...)
{
va_list ap;
char *buf;
buf = palloc(len);
va_start(ap, fmt);
vsnprintf(buf, len, fmt, ap);
va_end(ap);
return buf;
}
/* /*
@ -61,11 +49,22 @@ psnprintf(size_t len, const char *fmt,...)
* a standard type. Otherwise you just get pg_type.typname back, * a standard type. Otherwise you just get pg_type.typname back,
* double quoted if it contains funny characters. * double quoted if it contains funny characters.
* *
* If typemod is null (in the SQL sense) then you won't get any * If typemod is NULL then we are formatting a type name in a context where
* "..(x)" type qualifiers. The result is not technically correct, * no typemod is available, eg a function argument or result type. This
* because the various types interpret missing type modifiers * yields a slightly different result from specifying typemod = -1 in some
* differently, but it can be used as a convenient way to format * cases. Given typemod = -1 we feel compelled to produce an output that
* system catalogs, e.g., pg_aggregate, in psql. * the parser will interpret as having typemod -1, so that pg_dump will
* produce CREATE TABLE commands that recreate the original state. But
* given NULL typemod, we assume that the parser's interpretation of
* typemod doesn't matter, and so we are willing to output a slightly
* "prettier" representation of the same type. For example, type = bpchar
* and typemod = NULL gets you "character", whereas typemod = -1 gets you
* "bpchar" --- the former will be interpreted as character(1) by the
* parser, which does not yield typemod -1.
*
* XXX encoding a meaning in typemod = NULL is ugly; it'd have been
* cleaner to make two functions of one and two arguments respectively.
* Not worth changing it now, however.
*/ */
Datum Datum
format_type(PG_FUNCTION_ARGS) format_type(PG_FUNCTION_ARGS)
@ -74,17 +73,21 @@ format_type(PG_FUNCTION_ARGS)
int32 typemod; int32 typemod;
char *result; char *result;
/* Since this function is not strict, we must test for null args */
if (PG_ARGISNULL(0)) if (PG_ARGISNULL(0))
PG_RETURN_NULL(); PG_RETURN_NULL();
type_oid = PG_GETARG_OID(0); type_oid = PG_GETARG_OID(0);
if (!PG_ARGISNULL(1)) if (PG_ARGISNULL(1))
typemod = PG_GETARG_INT32(1); {
result = format_type_internal(type_oid, -1, false, true);
}
else else
typemod = -1; /* default typmod */ {
typemod = PG_GETARG_INT32(1);
result = format_type_internal(type_oid, typemod, true); result = format_type_internal(type_oid, typemod, true, true);
}
PG_RETURN_DATUM(_textin(result)); PG_RETURN_DATUM(_textin(result));
} }
@ -98,7 +101,7 @@ format_type(PG_FUNCTION_ARGS)
char * char *
format_type_be(Oid type_oid) format_type_be(Oid type_oid)
{ {
return format_type_internal(type_oid, -1, false); return format_type_internal(type_oid, -1, false, false);
} }
/* /*
@ -107,15 +110,16 @@ format_type_be(Oid type_oid)
char * char *
format_type_with_typemod(Oid type_oid, int32 typemod) format_type_with_typemod(Oid type_oid, int32 typemod)
{ {
return format_type_internal(type_oid, typemod, false); return format_type_internal(type_oid, typemod, true, false);
} }
static char * static char *
format_type_internal(Oid type_oid, int32 typemod, bool allow_invalid) format_type_internal(Oid type_oid, int32 typemod,
bool typemod_given, bool allow_invalid)
{ {
bool with_typemod = (typemod >= 0); bool with_typemod = typemod_given && (typemod >= 0);
HeapTuple tuple; HeapTuple tuple;
Oid array_base_type; Oid array_base_type;
int16 typlen; int16 typlen;
@ -140,7 +144,7 @@ format_type_internal(Oid type_oid, int32 typemod, bool allow_invalid)
array_base_type = ((Form_pg_type) GETSTRUCT(tuple))->typelem; array_base_type = ((Form_pg_type) GETSTRUCT(tuple))->typelem;
typlen = ((Form_pg_type) GETSTRUCT(tuple))->typlen; typlen = ((Form_pg_type) GETSTRUCT(tuple))->typlen;
if (array_base_type != 0 && typlen < 0) if (array_base_type != InvalidOid && typlen < 0)
{ {
/* Switch our attention to the array element type */ /* Switch our attention to the array element type */
ReleaseSysCache(tuple); ReleaseSysCache(tuple);
@ -167,15 +171,17 @@ format_type_internal(Oid type_oid, int32 typemod, bool allow_invalid)
if (with_typemod) if (with_typemod)
buf = psnprintf(5 + MAX_INT32_LEN + 1, "bit(%d)", buf = psnprintf(5 + MAX_INT32_LEN + 1, "bit(%d)",
(int) typemod); (int) typemod);
else else if (typemod_given)
{ {
/* /*
* bit with no typmod is not the same as BIT, which means * bit with typmod -1 is not the same as BIT, which means
* BIT(1) per SQL spec. Report it as the quoted typename * BIT(1) per SQL spec. Report it as the quoted typename
* so that parser will not assign a bogus typmod. * so that parser will not assign a bogus typmod.
*/ */
buf = pstrdup("\"bit\""); buf = pstrdup("\"bit\"");
} }
else
buf = pstrdup("bit");
break; break;
case BOOLOID: case BOOLOID:
@ -186,15 +192,17 @@ format_type_internal(Oid type_oid, int32 typemod, bool allow_invalid)
if (with_typemod) if (with_typemod)
buf = psnprintf(11 + MAX_INT32_LEN + 1, "character(%d)", buf = psnprintf(11 + MAX_INT32_LEN + 1, "character(%d)",
(int) (typemod - VARHDRSZ)); (int) (typemod - VARHDRSZ));
else else if (typemod_given)
{ {
/* /*
* bpchar with no typmod is not the same as CHARACTER, * bpchar with typmod -1 is not the same as CHARACTER,
* which means CHARACTER(1) per SQL spec. Report it as * which means CHARACTER(1) per SQL spec. Report it as
* bpchar so that parser will not assign a bogus typmod. * bpchar so that parser will not assign a bogus typmod.
*/ */
buf = pstrdup("bpchar"); buf = pstrdup("bpchar");
} }
else
buf = pstrdup("character");
break; break;
case CHAROID: case CHAROID:
@ -352,6 +360,10 @@ format_type_internal(Oid type_oid, int32 typemod, bool allow_invalid)
default: default:
name = NameStr(((Form_pg_type) GETSTRUCT(tuple))->typname); name = NameStr(((Form_pg_type) GETSTRUCT(tuple))->typname);
/*
* Double-quote the name if it's not a standard identifier.
* Note this is *necessary* for ruleutils.c's use.
*/
if (strspn(name, "abcdefghijklmnopqrstuvwxyz0123456789_") != strlen(name) if (strspn(name, "abcdefghijklmnopqrstuvwxyz0123456789_") != strlen(name)
|| isdigit((unsigned char) name[0])) || isdigit((unsigned char) name[0]))
buf = psnprintf(strlen(name) + 3, "\"%s\"", name); buf = psnprintf(strlen(name) + 3, "\"%s\"", name);
@ -456,13 +468,15 @@ oidvectortypes(PG_FUNCTION_ARGS)
for (num = 0; num < numargs; num++) for (num = 0; num < numargs; num++)
{ {
char *typename = format_type_internal(oidArray[num], -1, true); char *typename = format_type_internal(oidArray[num], -1,
false, true);
size_t slen = strlen(typename);
if (left < strlen(typename) + 2) if (left < (slen + 2))
{ {
total += strlen(typename) + 2; total += slen + 2;
result = repalloc(result, total); result = repalloc(result, total);
left += strlen(typename) + 2; left += slen + 2;
} }
if (num > 0) if (num > 0)
@ -471,8 +485,25 @@ oidvectortypes(PG_FUNCTION_ARGS)
left -= 2; left -= 2;
} }
strcat(result, typename); strcat(result, typename);
left -= strlen(typename); left -= slen;
} }
PG_RETURN_DATUM(_textin(result)); PG_RETURN_DATUM(_textin(result));
} }
/* snprintf into a palloc'd string */
static char *
psnprintf(size_t len, const char *fmt, ...)
{
va_list ap;
char *buf;
buf = palloc(len);
va_start(ap, fmt);
vsnprintf(buf, len, fmt, ap);
va_end(ap);
return buf;
}

View File

@ -3,7 +3,7 @@
* back to source text * back to source text
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.86 2001/10/25 05:49:45 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.87 2001/11/19 19:51:20 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
@ -2054,11 +2054,9 @@ get_func_expr(Expr *expr, deparse_context *context)
/* /*
* Show typename with appropriate length decoration. Note that * Show typename with appropriate length decoration. Note that
* since exprIsLengthCoercion succeeded, the function's output * since exprIsLengthCoercion succeeded, the function's output
* type is the right thing to use. * type is the right thing to report. Also note we don't need
* * to quote the result of format_type_with_typemod: it takes
* XXX In general it is incorrect to quote the result of * care of double-quoting any identifier that needs it.
* format_type_with_typemod, but are there any special cases where
* we should do so?
*/ */
typdesc = format_type_with_typemod(procStruct->prorettype, typdesc = format_type_with_typemod(procStruct->prorettype,
coercedTypmod); coercedTypmod);