LCOV - code coverage report
Current view: top level - src/backend/utils/adt - format_type.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 143 175 81.7 %
Date: 2024-03-29 12:13:04 Functions: 7 8 87.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * format_type.c
       4             :  *    Display type names "nicely".
       5             :  *
       6             :  *
       7             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/utils/adt/format_type.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : 
      16             : #include "postgres.h"
      17             : 
      18             : #include <ctype.h>
      19             : 
      20             : #include "access/htup_details.h"
      21             : #include "catalog/namespace.h"
      22             : #include "catalog/pg_type.h"
      23             : #include "mb/pg_wchar.h"
      24             : #include "utils/builtins.h"
      25             : #include "utils/fmgroids.h"
      26             : #include "utils/lsyscache.h"
      27             : #include "utils/numeric.h"
      28             : #include "utils/syscache.h"
      29             : 
      30             : static char *printTypmod(const char *typname, int32 typmod, Oid typmodout);
      31             : 
      32             : 
      33             : /*
      34             :  * SQL function: format_type(type_oid, typemod)
      35             :  *
      36             :  * `type_oid' is from pg_type.oid, `typemod' is from
      37             :  * pg_attribute.atttypmod. This function will get the type name and
      38             :  * format it and the modifier to canonical SQL format, if the type is
      39             :  * a standard type. Otherwise you just get pg_type.typname back,
      40             :  * double quoted if it contains funny characters or matches a keyword.
      41             :  *
      42             :  * If typemod is NULL then we are formatting a type name in a context where
      43             :  * no typemod is available, eg a function argument or result type.  This
      44             :  * yields a slightly different result from specifying typemod = -1 in some
      45             :  * cases.  Given typemod = -1 we feel compelled to produce an output that
      46             :  * the parser will interpret as having typemod -1, so that pg_dump will
      47             :  * produce CREATE TABLE commands that recreate the original state.  But
      48             :  * given NULL typemod, we assume that the parser's interpretation of
      49             :  * typemod doesn't matter, and so we are willing to output a slightly
      50             :  * "prettier" representation of the same type.  For example, type = bpchar
      51             :  * and typemod = NULL gets you "character", whereas typemod = -1 gets you
      52             :  * "bpchar" --- the former will be interpreted as character(1) by the
      53             :  * parser, which does not yield typemod -1.
      54             :  *
      55             :  * XXX encoding a meaning in typemod = NULL is ugly; it'd have been
      56             :  * cleaner to make two functions of one and two arguments respectively.
      57             :  * Not worth changing it now, however.
      58             :  */
      59             : Datum
      60       86674 : format_type(PG_FUNCTION_ARGS)
      61             : {
      62             :     Oid         type_oid;
      63             :     int32       typemod;
      64             :     char       *result;
      65       86674 :     bits16      flags = FORMAT_TYPE_ALLOW_INVALID;
      66             : 
      67             :     /* Since this function is not strict, we must test for null args */
      68       86674 :     if (PG_ARGISNULL(0))
      69         862 :         PG_RETURN_NULL();
      70             : 
      71       85812 :     type_oid = PG_GETARG_OID(0);
      72             : 
      73       85812 :     if (PG_ARGISNULL(1))
      74       38344 :         typemod = -1;
      75             :     else
      76             :     {
      77       47468 :         typemod = PG_GETARG_INT32(1);
      78       47468 :         flags |= FORMAT_TYPE_TYPEMOD_GIVEN;
      79             :     }
      80             : 
      81       85812 :     result = format_type_extended(type_oid, typemod, flags);
      82             : 
      83       85812 :     PG_RETURN_TEXT_P(cstring_to_text(result));
      84             : }
      85             : 
      86             : /*
      87             :  * format_type_extended
      88             :  *      Generate a possibly-qualified type name.
      89             :  *
      90             :  * The default behavior is to only qualify if the type is not in the search
      91             :  * path, to ignore the given typmod, and to raise an error if a non-existent
      92             :  * type_oid is given.
      93             :  *
      94             :  * The following bits in 'flags' modify the behavior:
      95             :  * - FORMAT_TYPE_TYPEMOD_GIVEN
      96             :  *          include the typmod in the output (typmod could still be -1 though)
      97             :  * - FORMAT_TYPE_ALLOW_INVALID
      98             :  *          if the type OID is invalid or unknown, return ??? or such instead
      99             :  *          of failing
     100             :  * - FORMAT_TYPE_INVALID_AS_NULL
     101             :  *          if the type OID is invalid or unknown, return NULL instead of ???
     102             :  *          or such
     103             :  * - FORMAT_TYPE_FORCE_QUALIFY
     104             :  *          always schema-qualify type names, regardless of search_path
     105             :  *
     106             :  * Note that TYPEMOD_GIVEN is not interchangeable with "typemod == -1";
     107             :  * see the comments above for format_type().
     108             :  *
     109             :  * Returns a palloc'd string, or NULL.
     110             :  */
     111             : char *
     112      629206 : format_type_extended(Oid type_oid, int32 typemod, bits16 flags)
     113             : {
     114             :     HeapTuple   tuple;
     115             :     Form_pg_type typeform;
     116             :     Oid         array_base_type;
     117             :     bool        is_array;
     118             :     char       *buf;
     119             :     bool        with_typemod;
     120             : 
     121      629206 :     if (type_oid == InvalidOid)
     122             :     {
     123          38 :         if ((flags & FORMAT_TYPE_INVALID_AS_NULL) != 0)
     124          18 :             return NULL;
     125          20 :         else if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
     126          20 :             return pstrdup("-");
     127             :     }
     128             : 
     129      629168 :     tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
     130      629168 :     if (!HeapTupleIsValid(tuple))
     131             :     {
     132           0 :         if ((flags & FORMAT_TYPE_INVALID_AS_NULL) != 0)
     133           0 :             return NULL;
     134           0 :         else if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
     135           0 :             return pstrdup("???");
     136             :         else
     137           0 :             elog(ERROR, "cache lookup failed for type %u", type_oid);
     138             :     }
     139      629168 :     typeform = (Form_pg_type) GETSTRUCT(tuple);
     140             : 
     141             :     /*
     142             :      * Check if it's a "true" array type.  Pseudo-array types such as "name"
     143             :      * shouldn't get deconstructed.  Also check the toast property, and don't
     144             :      * deconstruct "plain storage" array types --- this is because we don't
     145             :      * want to show oidvector as oid[].
     146             :      */
     147      629168 :     array_base_type = typeform->typelem;
     148             : 
     149      629168 :     if (IsTrueArrayType(typeform) &&
     150       48280 :         typeform->typstorage != TYPSTORAGE_PLAIN)
     151             :     {
     152             :         /* Switch our attention to the array element type */
     153       48056 :         ReleaseSysCache(tuple);
     154       48056 :         tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(array_base_type));
     155       48056 :         if (!HeapTupleIsValid(tuple))
     156             :         {
     157           0 :             if ((flags & FORMAT_TYPE_INVALID_AS_NULL) != 0)
     158           0 :                 return NULL;
     159           0 :             else if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
     160           0 :                 return pstrdup("???[]");
     161             :             else
     162           0 :                 elog(ERROR, "cache lookup failed for type %u", type_oid);
     163             :         }
     164       48056 :         typeform = (Form_pg_type) GETSTRUCT(tuple);
     165       48056 :         type_oid = array_base_type;
     166       48056 :         is_array = true;
     167             :     }
     168             :     else
     169      581112 :         is_array = false;
     170             : 
     171      629168 :     with_typemod = (flags & FORMAT_TYPE_TYPEMOD_GIVEN) != 0 && (typemod >= 0);
     172             : 
     173             :     /*
     174             :      * See if we want to special-case the output for certain built-in types.
     175             :      * Note that these special cases should all correspond to special
     176             :      * productions in gram.y, to ensure that the type name will be taken as a
     177             :      * system type, not a user type of the same name.
     178             :      *
     179             :      * If we do not provide a special-case output here, the type name will be
     180             :      * handled the same way as a user type name --- in particular, it will be
     181             :      * double-quoted if it matches any lexer keyword.  This behavior is
     182             :      * essential for some cases, such as types "bit" and "char".
     183             :      */
     184      629168 :     buf = NULL;                 /* flag for no special case */
     185             : 
     186      629168 :     switch (type_oid)
     187             :     {
     188         686 :         case BITOID:
     189         686 :             if (with_typemod)
     190         220 :                 buf = printTypmod("bit", typemod, typeform->typmodout);
     191         466 :             else if ((flags & FORMAT_TYPE_TYPEMOD_GIVEN) != 0)
     192             :             {
     193             :                 /*
     194             :                  * bit with typmod -1 is not the same as BIT, which means
     195             :                  * BIT(1) per SQL spec.  Report it as the quoted typename so
     196             :                  * that parser will not assign a bogus typmod.
     197             :                  */
     198             :             }
     199             :             else
     200         404 :                 buf = pstrdup("bit");
     201         686 :             break;
     202             : 
     203       12784 :         case BOOLOID:
     204       12784 :             buf = pstrdup("boolean");
     205       12784 :             break;
     206             : 
     207        2538 :         case BPCHAROID:
     208        2538 :             if (with_typemod)
     209         790 :                 buf = printTypmod("character", typemod, typeform->typmodout);
     210        1748 :             else if ((flags & FORMAT_TYPE_TYPEMOD_GIVEN) != 0)
     211             :             {
     212             :                 /*
     213             :                  * bpchar with typmod -1 is not the same as CHARACTER, which
     214             :                  * means CHARACTER(1) per SQL spec.  Report it as bpchar so
     215             :                  * that parser will not assign a bogus typmod.
     216             :                  */
     217             :             }
     218             :             else
     219        1074 :                 buf = pstrdup("character");
     220        2538 :             break;
     221             : 
     222        1672 :         case FLOAT4OID:
     223        1672 :             buf = pstrdup("real");
     224        1672 :             break;
     225             : 
     226        4150 :         case FLOAT8OID:
     227        4150 :             buf = pstrdup("double precision");
     228        4150 :             break;
     229             : 
     230        3514 :         case INT2OID:
     231        3514 :             buf = pstrdup("smallint");
     232        3514 :             break;
     233             : 
     234      155554 :         case INT4OID:
     235      155554 :             buf = pstrdup("integer");
     236      155554 :             break;
     237             : 
     238        7660 :         case INT8OID:
     239        7660 :             buf = pstrdup("bigint");
     240        7660 :             break;
     241             : 
     242        2982 :         case NUMERICOID:
     243        2982 :             if (with_typemod)
     244         370 :                 buf = printTypmod("numeric", typemod, typeform->typmodout);
     245             :             else
     246        2612 :                 buf = pstrdup("numeric");
     247        2982 :             break;
     248             : 
     249         892 :         case INTERVALOID:
     250         892 :             if (with_typemod)
     251           0 :                 buf = printTypmod("interval", typemod, typeform->typmodout);
     252             :             else
     253         892 :                 buf = pstrdup("interval");
     254         892 :             break;
     255             : 
     256         696 :         case TIMEOID:
     257         696 :             if (with_typemod)
     258          10 :                 buf = printTypmod("time", typemod, typeform->typmodout);
     259             :             else
     260         686 :                 buf = pstrdup("time without time zone");
     261         696 :             break;
     262             : 
     263         626 :         case TIMETZOID:
     264         626 :             if (with_typemod)
     265          10 :                 buf = printTypmod("time", typemod, typeform->typmodout);
     266             :             else
     267         616 :                 buf = pstrdup("time with time zone");
     268         626 :             break;
     269             : 
     270         876 :         case TIMESTAMPOID:
     271         876 :             if (with_typemod)
     272          10 :                 buf = printTypmod("timestamp", typemod, typeform->typmodout);
     273             :             else
     274         866 :                 buf = pstrdup("timestamp without time zone");
     275         876 :             break;
     276             : 
     277        1260 :         case TIMESTAMPTZOID:
     278        1260 :             if (with_typemod)
     279          10 :                 buf = printTypmod("timestamp", typemod, typeform->typmodout);
     280             :             else
     281        1250 :                 buf = pstrdup("timestamp with time zone");
     282        1260 :             break;
     283             : 
     284         526 :         case VARBITOID:
     285         526 :             if (with_typemod)
     286         132 :                 buf = printTypmod("bit varying", typemod, typeform->typmodout);
     287             :             else
     288         394 :                 buf = pstrdup("bit varying");
     289         526 :             break;
     290             : 
     291        1358 :         case VARCHAROID:
     292        1358 :             if (with_typemod)
     293         180 :                 buf = printTypmod("character varying", typemod, typeform->typmodout);
     294             :             else
     295        1178 :                 buf = pstrdup("character varying");
     296        1358 :             break;
     297             : 
     298         268 :         case JSONOID:
     299         268 :             buf = pstrdup("json");
     300         268 :             break;
     301             :     }
     302             : 
     303      629168 :     if (buf == NULL)
     304             :     {
     305             :         /*
     306             :          * Default handling: report the name as it appears in the catalog.
     307             :          * Here, we must qualify the name if it is not visible in the search
     308             :          * path or if caller requests it; and we must double-quote it if it's
     309             :          * not a standard identifier or if it matches any keyword.
     310             :          */
     311             :         char       *nspname;
     312             :         char       *typname;
     313             : 
     314      862228 :         if ((flags & FORMAT_TYPE_FORCE_QUALIFY) == 0 &&
     315      430366 :             TypeIsVisible(type_oid))
     316      420212 :             nspname = NULL;
     317             :         else
     318       11650 :             nspname = get_namespace_name_or_temp(typeform->typnamespace);
     319             : 
     320      431862 :         typname = NameStr(typeform->typname);
     321             : 
     322      431862 :         buf = quote_qualified_identifier(nspname, typname);
     323             : 
     324      431862 :         if (with_typemod)
     325           6 :             buf = printTypmod(buf, typemod, typeform->typmodout);
     326             :     }
     327             : 
     328      629168 :     if (is_array)
     329       48056 :         buf = psprintf("%s[]", buf);
     330             : 
     331      629168 :     ReleaseSysCache(tuple);
     332             : 
     333      629168 :     return buf;
     334             : }
     335             : 
     336             : /*
     337             :  * This version is for use within the backend in error messages, etc.
     338             :  * One difference is that it will fail for an invalid type.
     339             :  *
     340             :  * The result is always a palloc'd string.
     341             :  */
     342             : char *
     343      453802 : format_type_be(Oid type_oid)
     344             : {
     345      453802 :     return format_type_extended(type_oid, -1, 0);
     346             : }
     347             : 
     348             : /*
     349             :  * This version returns a name that is always qualified (unless it's one
     350             :  * of the SQL-keyword type names, such as TIMESTAMP WITH TIME ZONE).
     351             :  */
     352             : char *
     353         886 : format_type_be_qualified(Oid type_oid)
     354             : {
     355         886 :     return format_type_extended(type_oid, -1, FORMAT_TYPE_FORCE_QUALIFY);
     356             : }
     357             : 
     358             : /*
     359             :  * This version allows a nondefault typemod to be specified.
     360             :  */
     361             : char *
     362       27546 : format_type_with_typemod(Oid type_oid, int32 typemod)
     363             : {
     364       27546 :     return format_type_extended(type_oid, typemod, FORMAT_TYPE_TYPEMOD_GIVEN);
     365             : }
     366             : 
     367             : /*
     368             :  * Add typmod decoration to the basic type name
     369             :  */
     370             : static char *
     371        1738 : printTypmod(const char *typname, int32 typmod, Oid typmodout)
     372             : {
     373             :     char       *res;
     374             : 
     375             :     /* Shouldn't be called if typmod is -1 */
     376             :     Assert(typmod >= 0);
     377             : 
     378        1738 :     if (typmodout == InvalidOid)
     379             :     {
     380             :         /* Default behavior: just print the integer typmod with parens */
     381           0 :         res = psprintf("%s(%d)", typname, (int) typmod);
     382             :     }
     383             :     else
     384             :     {
     385             :         /* Use the type-specific typmodout procedure */
     386             :         char       *tmstr;
     387             : 
     388        1738 :         tmstr = DatumGetCString(OidFunctionCall1(typmodout,
     389             :                                                  Int32GetDatum(typmod)));
     390        1738 :         res = psprintf("%s%s", typname, tmstr);
     391             :     }
     392             : 
     393        1738 :     return res;
     394             : }
     395             : 
     396             : 
     397             : /*
     398             :  * type_maximum_size --- determine maximum width of a variable-width column
     399             :  *
     400             :  * If the max width is indeterminate, return -1.  In particular, we return
     401             :  * -1 for any type not known to this routine.  We assume the caller has
     402             :  * already determined that the type is a variable-width type, so it's not
     403             :  * necessary to look up the type's pg_type tuple here.
     404             :  *
     405             :  * This may appear unrelated to format_type(), but in fact the two routines
     406             :  * share knowledge of the encoding of typmod for different types, so it's
     407             :  * convenient to keep them together.  (XXX now that most of this knowledge
     408             :  * has been pushed out of format_type into the typmodout functions, it's
     409             :  * interesting to wonder if it's worth trying to factor this code too...)
     410             :  */
     411             : int32
     412      502006 : type_maximum_size(Oid type_oid, int32 typemod)
     413             : {
     414      502006 :     if (typemod < 0)
     415      473670 :         return -1;
     416             : 
     417       28336 :     switch (type_oid)
     418             :     {
     419       19108 :         case BPCHAROID:
     420             :         case VARCHAROID:
     421             :             /* typemod includes varlena header */
     422             : 
     423             :             /* typemod is in characters not bytes */
     424       38216 :             return (typemod - VARHDRSZ) *
     425       19108 :                 pg_encoding_max_length(GetDatabaseEncoding())
     426       19108 :                 + VARHDRSZ;
     427             : 
     428        7582 :         case NUMERICOID:
     429        7582 :             return numeric_maximum_size(typemod);
     430             : 
     431        1226 :         case VARBITOID:
     432             :         case BITOID:
     433             :             /* typemod is the (max) number of bits */
     434        1226 :             return (typemod + (BITS_PER_BYTE - 1)) / BITS_PER_BYTE
     435        1226 :                 + 2 * sizeof(int32);
     436             :     }
     437             : 
     438             :     /* Unknown type, or unlimited-width type such as 'text' */
     439         420 :     return -1;
     440             : }
     441             : 
     442             : 
     443             : /*
     444             :  * oidvectortypes           - converts a vector of type OIDs to "typname" list
     445             :  */
     446             : Datum
     447           0 : oidvectortypes(PG_FUNCTION_ARGS)
     448             : {
     449           0 :     oidvector  *oidArray = (oidvector *) PG_GETARG_POINTER(0);
     450             :     char       *result;
     451           0 :     int         numargs = oidArray->dim1;
     452             :     int         num;
     453             :     size_t      total;
     454             :     size_t      left;
     455             : 
     456           0 :     total = 20 * numargs + 1;
     457           0 :     result = palloc(total);
     458           0 :     result[0] = '\0';
     459           0 :     left = total - 1;
     460             : 
     461           0 :     for (num = 0; num < numargs; num++)
     462             :     {
     463           0 :         char       *typename = format_type_extended(oidArray->values[num], -1,
     464             :                                                     FORMAT_TYPE_ALLOW_INVALID);
     465           0 :         size_t      slen = strlen(typename);
     466             : 
     467           0 :         if (left < (slen + 2))
     468             :         {
     469           0 :             total += slen + 2;
     470           0 :             result = repalloc(result, total);
     471           0 :             left += slen + 2;
     472             :         }
     473             : 
     474           0 :         if (num > 0)
     475             :         {
     476           0 :             strcat(result, ", ");
     477           0 :             left -= 2;
     478             :         }
     479           0 :         strcat(result, typename);
     480           0 :         left -= slen;
     481             :     }
     482             : 
     483           0 :     PG_RETURN_TEXT_P(cstring_to_text(result));
     484             : }

Generated by: LCOV version 1.14