LCOV - code coverage report
Current view: top level - src/backend/utils/adt - format_type.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 85.1 % 168 143
Test Date: 2026-10-17 16:15:42 Functions: 87.5 % 8 7
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 77.5 % 102 79

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

Generated by: LCOV version 2.0-1