LCOV - code coverage report
Current view: top level - src/backend/utils/adt - format_type.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 81.2 % 176 143
Test Date: 2026-02-28 00:15:00 Functions: 87.5 % 8 7
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-2026, 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        51643 : format_type(PG_FUNCTION_ARGS)
      61              : {
      62              :     Oid         type_oid;
      63              :     int32       typemod;
      64              :     char       *result;
      65        51643 :     bits16      flags = FORMAT_TYPE_ALLOW_INVALID;
      66              : 
      67              :     /* Since this function is not strict, we must test for null args */
      68        51643 :     if (PG_ARGISNULL(0))
      69          458 :         PG_RETURN_NULL();
      70              : 
      71        51185 :     type_oid = PG_GETARG_OID(0);
      72              : 
      73        51185 :     if (PG_ARGISNULL(1))
      74        21098 :         typemod = -1;
      75              :     else
      76              :     {
      77        30087 :         typemod = PG_GETARG_INT32(1);
      78        30087 :         flags |= FORMAT_TYPE_TYPEMOD_GIVEN;
      79              :     }
      80              : 
      81        51185 :     result = format_type_extended(type_oid, typemod, flags);
      82              : 
      83        51185 :     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       359794 : 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       359794 :     if (type_oid == InvalidOid)
     122              :     {
     123           19 :         if ((flags & FORMAT_TYPE_INVALID_AS_NULL) != 0)
     124            9 :             return NULL;
     125           10 :         else if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
     126           10 :             return pstrdup("-");
     127              :     }
     128              : 
     129       359775 :     tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
     130       359775 :     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       359775 :     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       359775 :     array_base_type = typeform->typelem;
     148              : 
     149       359775 :     if (IsTrueArrayType(typeform) &&
     150        30699 :         typeform->typstorage != TYPSTORAGE_PLAIN)
     151              :     {
     152              :         /* Switch our attention to the array element type */
     153        30492 :         ReleaseSysCache(tuple);
     154        30492 :         tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(array_base_type));
     155        30492 :         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        30492 :         typeform = (Form_pg_type) GETSTRUCT(tuple);
     165        30492 :         type_oid = array_base_type;
     166        30492 :         is_array = true;
     167              :     }
     168              :     else
     169       329283 :         is_array = false;
     170              : 
     171       359775 :     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       359775 :     buf = NULL;                 /* flag for no special case */
     185              : 
     186       359775 :     switch (type_oid)
     187              :     {
     188          571 :         case BITOID:
     189          571 :             if (with_typemod)
     190          112 :                 buf = printTypmod("bit", typemod, typeform->typmodout);
     191          459 :             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          428 :                 buf = pstrdup("bit");
     201          571 :             break;
     202              : 
     203         7016 :         case BOOLOID:
     204         7016 :             buf = pstrdup("boolean");
     205         7016 :             break;
     206              : 
     207         1563 :         case BPCHAROID:
     208         1563 :             if (with_typemod)
     209          418 :                 buf = printTypmod("character", typemod, typeform->typmodout);
     210         1145 :             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          791 :                 buf = pstrdup("character");
     220         1563 :             break;
     221              : 
     222         1296 :         case FLOAT4OID:
     223         1296 :             buf = pstrdup("real");
     224         1296 :             break;
     225              : 
     226         2726 :         case FLOAT8OID:
     227         2726 :             buf = pstrdup("double precision");
     228         2726 :             break;
     229              : 
     230         3754 :         case INT2OID:
     231         3754 :             buf = pstrdup("smallint");
     232         3754 :             break;
     233              : 
     234        82539 :         case INT4OID:
     235        82539 :             buf = pstrdup("integer");
     236        82539 :             break;
     237              : 
     238         5562 :         case INT8OID:
     239         5562 :             buf = pstrdup("bigint");
     240         5562 :             break;
     241              : 
     242         1817 :         case NUMERICOID:
     243         1817 :             if (with_typemod)
     244          185 :                 buf = printTypmod("numeric", typemod, typeform->typmodout);
     245              :             else
     246         1632 :                 buf = pstrdup("numeric");
     247         1817 :             break;
     248              : 
     249          769 :         case INTERVALOID:
     250          769 :             if (with_typemod)
     251            0 :                 buf = printTypmod("interval", typemod, typeform->typmodout);
     252              :             else
     253          769 :                 buf = pstrdup("interval");
     254          769 :             break;
     255              : 
     256          613 :         case TIMEOID:
     257          613 :             if (with_typemod)
     258            5 :                 buf = printTypmod("time", typemod, typeform->typmodout);
     259              :             else
     260          608 :                 buf = pstrdup("time without time zone");
     261          613 :             break;
     262              : 
     263          515 :         case TIMETZOID:
     264          515 :             if (with_typemod)
     265            5 :                 buf = printTypmod("time", typemod, typeform->typmodout);
     266              :             else
     267          510 :                 buf = pstrdup("time with time zone");
     268          515 :             break;
     269              : 
     270          822 :         case TIMESTAMPOID:
     271          822 :             if (with_typemod)
     272            5 :                 buf = printTypmod("timestamp", typemod, typeform->typmodout);
     273              :             else
     274          817 :                 buf = pstrdup("timestamp without time zone");
     275          822 :             break;
     276              : 
     277         1172 :         case TIMESTAMPTZOID:
     278         1172 :             if (with_typemod)
     279            5 :                 buf = printTypmod("timestamp", typemod, typeform->typmodout);
     280              :             else
     281         1167 :                 buf = pstrdup("timestamp with time zone");
     282         1172 :             break;
     283              : 
     284          449 :         case VARBITOID:
     285          449 :             if (with_typemod)
     286           66 :                 buf = printTypmod("bit varying", typemod, typeform->typmodout);
     287              :             else
     288          383 :                 buf = pstrdup("bit varying");
     289          449 :             break;
     290              : 
     291          734 :         case VARCHAROID:
     292          734 :             if (with_typemod)
     293          106 :                 buf = printTypmod("character varying", typemod, typeform->typmodout);
     294              :             else
     295          628 :                 buf = pstrdup("character varying");
     296          734 :             break;
     297              : 
     298          206 :         case JSONOID:
     299          206 :             buf = pstrdup("json");
     300          206 :             break;
     301              :     }
     302              : 
     303       359775 :     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       495270 :         if ((flags & FORMAT_TYPE_FORCE_QUALIFY) == 0 &&
     315       247234 :             TypeIsVisible(type_oid))
     316       235719 :             nspname = NULL;
     317              :         else
     318        12317 :             nspname = get_namespace_name_or_temp(typeform->typnamespace);
     319              : 
     320       248036 :         typname = NameStr(typeform->typname);
     321              : 
     322       248036 :         buf = quote_qualified_identifier(nspname, typname);
     323              : 
     324       248036 :         if (with_typemod)
     325            3 :             buf = printTypmod(buf, typemod, typeform->typmodout);
     326              :     }
     327              : 
     328       359775 :     if (is_array)
     329        30492 :         buf = psprintf("%s[]", buf);
     330              : 
     331       359775 :     ReleaseSysCache(tuple);
     332              : 
     333       359775 :     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       247215 : format_type_be(Oid type_oid)
     344              : {
     345       247215 :     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          449 : format_type_be_qualified(Oid type_oid)
     354              : {
     355          449 :     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        16059 : format_type_with_typemod(Oid type_oid, int32 typemod)
     363              : {
     364        16059 :     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          910 : 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          910 :     if (typmodout == InvalidOid)
     379              :     {
     380              :         /* Default behavior: just print the integer typmod with parens */
     381            0 :         res = psprintf("%s(%d)", typname, typmod);
     382              :     }
     383              :     else
     384              :     {
     385              :         /* Use the type-specific typmodout procedure */
     386              :         char       *tmstr;
     387              : 
     388          910 :         tmstr = DatumGetCString(OidFunctionCall1(typmodout,
     389              :                                                  Int32GetDatum(typmod)));
     390          910 :         res = psprintf("%s%s", typname, tmstr);
     391              :     }
     392              : 
     393          910 :     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       344364 : type_maximum_size(Oid type_oid, int32 typemod)
     413              : {
     414       344364 :     if (typemod < 0)
     415       319947 :         return -1;
     416              : 
     417        24417 :     switch (type_oid)
     418              :     {
     419        13983 :         case BPCHAROID:
     420              :         case VARCHAROID:
     421              :             /* typemod includes varlena header */
     422              : 
     423              :             /* typemod is in characters not bytes */
     424        27966 :             return (typemod - VARHDRSZ) *
     425        13983 :                 pg_encoding_max_length(GetDatabaseEncoding())
     426        13983 :                 + VARHDRSZ;
     427              : 
     428         3547 :         case NUMERICOID:
     429         3547 :             return numeric_maximum_size(typemod);
     430              : 
     431         6665 :         case VARBITOID:
     432              :         case BITOID:
     433              :             /* typemod is the (max) number of bits */
     434         6665 :             return (typemod + (BITS_PER_BYTE - 1)) / BITS_PER_BYTE
     435         6665 :                 + 2 * sizeof(int32);
     436              :     }
     437              : 
     438              :     /* Unknown type, or unlimited-width type such as 'text' */
     439          222 :     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              :     int         numargs;
     452              :     int         num;
     453              :     size_t      total;
     454              :     size_t      left;
     455              : 
     456              :     /* validate input before fetching dim1 */
     457            0 :     check_valid_oidvector(oidArray);
     458            0 :     numargs = oidArray->dim1;
     459              : 
     460            0 :     total = 20 * numargs + 1;
     461            0 :     result = palloc(total);
     462            0 :     result[0] = '\0';
     463            0 :     left = total - 1;
     464              : 
     465            0 :     for (num = 0; num < numargs; num++)
     466              :     {
     467            0 :         char       *typename = format_type_extended(oidArray->values[num], -1,
     468              :                                                     FORMAT_TYPE_ALLOW_INVALID);
     469            0 :         size_t      slen = strlen(typename);
     470              : 
     471            0 :         if (left < (slen + 2))
     472              :         {
     473            0 :             total += slen + 2;
     474            0 :             result = repalloc(result, total);
     475            0 :             left += slen + 2;
     476              :         }
     477              : 
     478            0 :         if (num > 0)
     479              :         {
     480            0 :             strcat(result, ", ");
     481            0 :             left -= 2;
     482              :         }
     483            0 :         strcat(result, typename);
     484            0 :         left -= slen;
     485              :     }
     486              : 
     487            0 :     PG_RETURN_TEXT_P(cstring_to_text(result));
     488              : }
        

Generated by: LCOV version 2.0-1