]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/format_type.c
Replace pg_asprintf() with psprintf().
[postgresql] / src / backend / utils / adt / format_type.c
index c85d7ea08842cfab8c9a13d6133a43024eaf77b6..21d4f8dc555836ccfca4f5c145b38d4d27ec0642 100644 (file)
@@ -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
  *
  *-------------------------------------------------------------------------
  */
 
 #include <ctype.h>
 
-#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));
 }