X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Futils%2Fadt%2Fformat_type.c;h=21d4f8dc555836ccfca4f5c145b38d4d27ec0642;hb=2c66f9924c1162bfba27c77004ccf42fb6ea188d;hp=c85d7ea08842cfab8c9a13d6133a43024eaf77b6;hpb=337b22cb473f1c5cca011a511c488d20e153eec4;p=postgresql diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c index c85d7ea088..21d4f8dc55 100644 --- a/src/backend/utils/adt/format_type.c +++ b/src/backend/utils/adt/format_type.c @@ -4,11 +4,11 @@ * Display type names "nicely". * * - * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.28 2002/03/20 19:44:40 tgl Exp $ + * src/backend/utils/adt/format_type.c * *------------------------------------------------------------------------- */ @@ -17,27 +17,21 @@ #include -#include "fmgr.h" +#include "access/htup_details.h" +#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" -#ifdef MULTIBYTE #include "mb/pg_wchar.h" -#endif - - -#define MASK(b) (1 << (b)) #define MAX_INT32_LEN 11 -#define _textin(str) DirectFunctionCall1(textin, CStringGetDatum(str)) static char *format_type_internal(Oid type_oid, int32 typemod, - bool typemod_given, bool allow_invalid); -static char *psnprintf(size_t len, const char *fmt, ...) -/* This lets gcc check the format string for consistency. */ -__attribute__((format(printf, 2, 3))); + bool typemod_given, bool allow_invalid, + bool force_qualify); +static char *printTypmod(const char *typname, int32 typmod, Oid typmodout); /* @@ -47,17 +41,17 @@ __attribute__((format(printf, 2, 3))); * pg_attribute.atttypmod. This function will get the type name and * format it and the modifier to canonical SQL format, if the type is * 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 or matches a keyword. * * If typemod is NULL then we are formatting a type name in a context where - * no typemod is available, eg a function argument or result type. This + * no typemod is available, eg a function argument or result type. This * yields a slightly different result from specifying typemod = -1 in some * cases. Given typemod = -1 we feel compelled to produce an output that * the parser will interpret as having typemod -1, so that pg_dump will - * produce CREATE TABLE commands that recreate the original state. But + * 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 + * "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. @@ -80,16 +74,14 @@ format_type(PG_FUNCTION_ARGS) type_oid = PG_GETARG_OID(0); if (PG_ARGISNULL(1)) - { - result = format_type_internal(type_oid, -1, false, true); - } + result = format_type_internal(type_oid, -1, false, true, false); else { typemod = PG_GETARG_INT32(1); - result = format_type_internal(type_oid, typemod, true, true); + result = format_type_internal(type_oid, typemod, true, true, false); } - PG_RETURN_DATUM(_textin(result)); + PG_RETURN_TEXT_P(cstring_to_text(result)); } /* @@ -101,7 +93,13 @@ format_type(PG_FUNCTION_ARGS) char * format_type_be(Oid type_oid) { - return format_type_internal(type_oid, -1, false, false); + return format_type_internal(type_oid, -1, false, false, false); +} + +char * +format_type_be_qualified(Oid type_oid) +{ + return format_type_internal(type_oid, -1, false, false, true); } /* @@ -110,83 +108,88 @@ format_type_be(Oid type_oid) char * format_type_with_typemod(Oid type_oid, int32 typemod) { - return format_type_internal(type_oid, typemod, true, false); + return format_type_internal(type_oid, typemod, true, false, false); } - - static char * format_type_internal(Oid type_oid, int32 typemod, - bool typemod_given, bool allow_invalid) + bool typemod_given, bool allow_invalid, + bool force_qualify) { bool with_typemod = typemod_given && (typemod >= 0); HeapTuple tuple; + Form_pg_type typeform; Oid array_base_type; - int16 typlen; - char typtype; bool is_array; - char *name; char *buf; if (type_oid == InvalidOid && allow_invalid) return pstrdup("-"); - tuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(type_oid), - 0, 0, 0); + tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid)); if (!HeapTupleIsValid(tuple)) { if (allow_invalid) return pstrdup("???"); else - elog(ERROR, "could not locate data type with oid %u in catalog", - type_oid); + elog(ERROR, "cache lookup failed for type %u", type_oid); } + typeform = (Form_pg_type) GETSTRUCT(tuple); /* - * Check if it's an array (and not a domain --- we don't want to show - * the substructure of a domain type). Fixed-length array types such - * as "name" shouldn't get deconstructed either. + * Check if it's a regular (variable length) array type. Fixed-length + * array types such as "name" shouldn't get deconstructed. As of Postgres + * 8.1, rather than checking typlen we check the toast property, and don't + * deconstruct "plain storage" array types --- this is because we don't + * want to show oidvector as oid[]. */ - array_base_type = ((Form_pg_type) GETSTRUCT(tuple))->typelem; - typlen = ((Form_pg_type) GETSTRUCT(tuple))->typlen; - typtype = ((Form_pg_type) GETSTRUCT(tuple))->typtype; + array_base_type = typeform->typelem; - if (array_base_type != InvalidOid && typlen < 0 && typtype != 'd') + if (array_base_type != InvalidOid && + typeform->typstorage != 'p') { /* Switch our attention to the array element type */ ReleaseSysCache(tuple); - tuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(array_base_type), - 0, 0, 0); + tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(array_base_type)); if (!HeapTupleIsValid(tuple)) { if (allow_invalid) return pstrdup("???[]"); else - elog(ERROR, "could not locate data type with oid %u in catalog", - type_oid); + elog(ERROR, "cache lookup failed for type %u", type_oid); } - is_array = true; + typeform = (Form_pg_type) GETSTRUCT(tuple); type_oid = array_base_type; + is_array = true; } else is_array = false; + /* + * See if we want to special-case the output for certain built-in types. + * Note that these special cases should all correspond to special + * productions in gram.y, to ensure that the type name will be taken as a + * system type, not a user type of the same name. + * + * If we do not provide a special-case output here, the type name will be + * handled the same way as a user type name --- in particular, it will be + * double-quoted if it matches any lexer keyword. This behavior is + * essential for some cases, such as types "bit" and "char". + */ + buf = NULL; /* flag for no special case */ + switch (type_oid) { 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) { /* * bit with typmod -1 is not the same as BIT, which means - * BIT(1) per SQL spec. Report it as the quoted typename - * so that parser will not assign a bogus typmod. + * BIT(1) per SQL spec. Report it as the quoted typename so + * that parser will not assign a bogus typmod. */ - buf = pstrdup("\"bit\""); } else buf = pstrdup("bit"); @@ -198,29 +201,19 @@ 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) { /* - * bpchar with typmod -1 is not the same as CHARACTER, - * which means CHARACTER(1) per SQL spec. Report it as - * bpchar so that parser will not assign a bogus typmod. + * bpchar with typmod -1 is not the same as CHARACTER, which + * means CHARACTER(1) per SQL spec. Report it as bpchar so + * that parser will not assign a bogus typmod. */ - buf = pstrdup("bpchar"); } else buf = pstrdup("character"); break; - case CHAROID: - /* - * This char type is the single-byte version. You have to - * double-quote it to get at it in the parser. - */ - buf = pstrdup("\"char\""); - break; - case FLOAT4OID: buf = pstrdup("real"); break; @@ -243,145 +236,87 @@ 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 = typemod >> 16; - int precision = typemod & 0xFFFF; - const char *fieldstr; - - switch (fields) - { - case MASK(YEAR): - fieldstr = " year"; - break; - case MASK(MONTH): - fieldstr = " month"; - break; - case MASK(DAY): - fieldstr = " day"; - break; - case MASK(HOUR): - fieldstr = " hour"; - break; - case MASK(MINUTE): - fieldstr = " minute"; - break; - case MASK(SECOND): - fieldstr = " second"; - break; - case MASK(YEAR) | MASK(MONTH): - fieldstr = " year to month"; - break; - case MASK(DAY) | MASK(HOUR): - fieldstr = " day to hour"; - break; - case MASK(DAY) | MASK(HOUR) | MASK(MINUTE): - fieldstr = " day to minute"; - break; - case MASK(DAY) | MASK(HOUR) | MASK(MINUTE) | MASK(SECOND): - fieldstr = " day to second"; - break; - case MASK(HOUR) | MASK(MINUTE): - fieldstr = " hour to minute"; - break; - case MASK(HOUR) | MASK(MINUTE) | MASK(SECOND): - fieldstr = " hour to second"; - break; - case MASK(MINUTE) | MASK(SECOND): - fieldstr = " minute to second"; - break; - case 0x7FFF: - fieldstr = ""; - break; - default: - elog(LOG, "Invalid INTERVAL typmod 0x%x", typemod); - fieldstr = ""; - break; - } - if (precision != 0xFFFF) - 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; + } - default: - 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) - || isdigit((unsigned char) name[0])) - buf = psnprintf(strlen(name) + 3, "\"%s\"", name); - else - buf = pstrdup(name); - break; + if (buf == NULL) + { + /* + * Default handling: report the name as it appears in the catalog. + * Here, we must qualify the name if it is not visible in the search + * path, and we must double-quote it if it's not a standard identifier + * or if it matches any keyword. + */ + char *nspname; + char *typname; + + if (!force_qualify && TypeIsVisible(type_oid)) + nspname = NULL; + else + nspname = get_namespace_name(typeform->typnamespace); + + typname = NameStr(typeform->typname); + + buf = quote_qualified_identifier(nspname, typname); + + if (with_typemod) + buf = printTypmod(buf, typemod, typeform->typmodout); } if (is_array) - buf = psnprintf(strlen(buf) + 3, "%s[]", buf); + buf = psprintf("%s[]", buf); ReleaseSysCache(tuple); @@ -390,16 +325,48 @@ format_type_internal(Oid type_oid, int32 typemod, /* - * type_maximum_size --- determine maximum width of a varlena column + * 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 = psprintf("%s(%d)", typname, (int) typmod); + } + else + { + /* Use the type-specific typmodout procedure */ + char *tmstr; + + tmstr = DatumGetCString(OidFunctionCall1(typmodout, + Int32GetDatum(typmod))); + res = psprintf("%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 * -1 for any type not known to this routine. We assume the caller has - * already determined that the type is a varlena type, so it's not + * already determined that the type is a variable-width type, so it's not * necessary to look up the type's pg_type tuple here. * * 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) @@ -412,25 +379,14 @@ type_maximum_size(Oid type_oid, int32 typemod) case BPCHAROID: case VARCHAROID: /* typemod includes varlena header */ -#ifdef MULTIBYTE + /* typemod is in characters not bytes */ return (typemod - VARHDRSZ) * pg_encoding_max_length(GetDatabaseEncoding()) + VARHDRSZ; -#else - return typemod; -#endif case NUMERICOID: - /* precision (ie, max # of digits) is in upper bits of typmod */ - if (typemod > VARHDRSZ) - { - int precision = ((typemod - VARHDRSZ) >> 16) & 0xffff; - - /* Numeric stores 2 decimal digits/byte, plus header */ - return (precision + 1) / 2 + NUMERIC_HDRSZ; - } - break; + return numeric_maximum_size(typemod); case VARBITOID: case BITOID: @@ -446,29 +402,17 @@ type_maximum_size(Oid type_oid, int32 typemod) /* * oidvectortypes - converts a vector of type OIDs to "typname" list - * - * The interface for this function is wrong: it should be told how many - * OIDs are significant in the input vector, so that trailing InvalidOid - * argument types can be recognized. */ Datum oidvectortypes(PG_FUNCTION_ARGS) { - Oid *oidArray = (Oid *) PG_GETARG_POINTER(0); + oidvector *oidArray = (oidvector *) PG_GETARG_POINTER(0); char *result; - int numargs; + int numargs = oidArray->dim1; int num; size_t total; size_t left; - /* Try to guess how many args there are :-( */ - numargs = 0; - for (num = 0; num < FUNC_MAX_ARGS; num++) - { - if (oidArray[num] != InvalidOid) - numargs = num + 1; - } - total = 20 * numargs + 1; result = palloc(total); result[0] = '\0'; @@ -476,8 +420,8 @@ oidvectortypes(PG_FUNCTION_ARGS) for (num = 0; num < numargs; num++) { - char *typename = format_type_internal(oidArray[num], -1, - false, true); + char *typename = format_type_internal(oidArray->values[num], -1, + false, true, false); size_t slen = strlen(typename); if (left < (slen + 2)) @@ -496,22 +440,5 @@ oidvectortypes(PG_FUNCTION_ARGS) left -= slen; } - 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; + PG_RETURN_TEXT_P(cstring_to_text(result)); }