Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * format_type.c
4 : : * Display type names "nicely".
5 : : *
6 : : *
7 : : * Portions Copyright (c) 1996-2025, 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
9192 peter_e@gmx.net 60 :CBC 52996 : format_type(PG_FUNCTION_ARGS)
61 : : {
62 : : Oid type_oid;
63 : : int32 typemod;
64 : : char *result;
2746 tgl@sss.pgh.pa.us 65 : 52996 : bits16 flags = FORMAT_TYPE_ALLOW_INVALID;
66 : :
67 : : /* Since this function is not strict, we must test for null args */
9192 peter_e@gmx.net 68 [ + + ]: 52996 : if (PG_ARGISNULL(0))
69 : 464 : PG_RETURN_NULL();
70 : :
9147 tgl@sss.pgh.pa.us 71 : 52532 : type_oid = PG_GETARG_OID(0);
72 : :
2746 73 [ + + ]: 52532 : if (PG_ARGISNULL(1))
74 : 22466 : typemod = -1;
75 : : else
76 : : {
77 : 30066 : typemod = PG_GETARG_INT32(1);
78 : 30066 : flags |= FORMAT_TYPE_TYPEMOD_GIVEN;
79 : : }
80 : :
81 : 52532 : result = format_type_extended(type_oid, typemod, flags);
82 : :
6374 83 : 52532 : 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 *
2758 alvherre@alvh.no-ip. 112 : 352379 : 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 : :
1888 michael@paquier.xyz 121 [ + + ]: 352379 : 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 : :
5683 rhaas@postgresql.org 129 : 352360 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
9192 peter_e@gmx.net 130 [ + + ]: 352360 : if (!HeapTupleIsValid(tuple))
131 : : {
1888 michael@paquier.xyz 132 [ - + ]: 2 : if ((flags & FORMAT_TYPE_INVALID_AS_NULL) != 0)
1888 michael@paquier.xyz 133 :UBC 0 : return NULL;
1888 michael@paquier.xyz 134 [ + - ]:CBC 2 : else if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
8794 peter_e@gmx.net 135 : 2 : return pstrdup("???");
136 : : else
8077 tgl@sss.pgh.pa.us 137 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", type_oid);
138 : : }
8529 tgl@sss.pgh.pa.us 139 :CBC 352358 : 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 : 352358 : array_base_type = typeform->typelem;
148 : :
1732 149 [ + + + + ]: 352358 : if (IsTrueArrayType(typeform) &&
2012 150 [ + + ]: 27589 : typeform->typstorage != TYPSTORAGE_PLAIN)
151 : : {
152 : : /* Switch our attention to the array element type */
9060 153 : 27381 : ReleaseSysCache(tuple);
5683 rhaas@postgresql.org 154 : 27381 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(array_base_type));
9192 peter_e@gmx.net 155 [ - + ]: 27381 : if (!HeapTupleIsValid(tuple))
156 : : {
1888 michael@paquier.xyz 157 [ # # ]:UBC 0 : if ((flags & FORMAT_TYPE_INVALID_AS_NULL) != 0)
158 : 0 : return NULL;
159 [ # # ]: 0 : else if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
8794 peter_e@gmx.net 160 : 0 : return pstrdup("???[]");
161 : : else
8077 tgl@sss.pgh.pa.us 162 [ # # ]: 0 : elog(ERROR, "cache lookup failed for type %u", type_oid);
163 : : }
8529 tgl@sss.pgh.pa.us 164 :CBC 27381 : typeform = (Form_pg_type) GETSTRUCT(tuple);
9147 165 : 27381 : type_oid = array_base_type;
8529 166 : 27381 : is_array = true;
167 : : }
168 : : else
9192 peter_e@gmx.net 169 : 324977 : is_array = false;
170 : :
2758 alvherre@alvh.no-ip. 171 [ + + + + ]: 352358 : 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 : : */
8529 tgl@sss.pgh.pa.us 184 : 352358 : buf = NULL; /* flag for no special case */
185 : :
9147 186 [ + + + + : 352358 : switch (type_oid)
+ + + + +
+ + + + +
+ + + + ]
187 : : {
8699 188 : 578 : case BITOID:
189 [ + + ]: 578 : if (with_typemod)
6825 190 : 118 : buf = printTypmod("bit", typemod, typeform->typmodout);
2758 alvherre@alvh.no-ip. 191 [ + + ]: 460 : 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
8692 tgl@sss.pgh.pa.us 200 : 429 : buf = pstrdup("bit");
8699 201 : 578 : break;
202 : :
9147 203 : 6979 : case BOOLOID:
204 : 6979 : buf = pstrdup("boolean");
205 : 6979 : break;
206 : :
207 : 1554 : case BPCHAROID:
208 [ + + ]: 1554 : if (with_typemod)
6825 209 : 418 : buf = printTypmod("character", typemod, typeform->typmodout);
2758 alvherre@alvh.no-ip. 210 [ + + ]: 1136 : 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
8692 tgl@sss.pgh.pa.us 219 : 788 : buf = pstrdup("character");
9147 220 : 1554 : break;
221 : :
222 : 1288 : case FLOAT4OID:
223 : 1288 : buf = pstrdup("real");
224 : 1288 : break;
225 : :
226 : 2751 : case FLOAT8OID:
227 : 2751 : buf = pstrdup("double precision");
228 : 2751 : break;
229 : :
230 : 3728 : case INT2OID:
231 : 3728 : buf = pstrdup("smallint");
232 : 3728 : break;
233 : :
234 : 82349 : case INT4OID:
235 : 82349 : buf = pstrdup("integer");
236 : 82349 : break;
237 : :
238 : 5648 : case INT8OID:
239 : 5648 : buf = pstrdup("bigint");
240 : 5648 : break;
241 : :
242 : 1809 : case NUMERICOID:
243 [ + + ]: 1809 : if (with_typemod)
6825 244 : 185 : buf = printTypmod("numeric", typemod, typeform->typmodout);
245 : : else
9147 246 : 1624 : buf = pstrdup("numeric");
247 : 1809 : break;
248 : :
8719 249 : 776 : case INTERVALOID:
250 [ - + ]: 776 : if (with_typemod)
6825 tgl@sss.pgh.pa.us 251 :UBC 0 : buf = printTypmod("interval", typemod, typeform->typmodout);
252 : : else
8719 tgl@sss.pgh.pa.us 253 :CBC 776 : buf = pstrdup("interval");
254 : 776 : break;
255 : :
8739 256 : 614 : case TIMEOID:
8738 257 [ + + ]: 614 : if (with_typemod)
6825 258 : 5 : buf = printTypmod("time", typemod, typeform->typmodout);
259 : : else
8739 260 : 609 : buf = pstrdup("time without time zone");
261 : 614 : break;
262 : :
9147 263 : 516 : case TIMETZOID:
8738 264 [ + + ]: 516 : if (with_typemod)
6825 265 : 5 : buf = printTypmod("time", typemod, typeform->typmodout);
266 : : else
8739 267 : 511 : buf = pstrdup("time with time zone");
9147 268 : 516 : break;
269 : :
8979 peter_e@gmx.net 270 : 820 : case TIMESTAMPOID:
8738 tgl@sss.pgh.pa.us 271 [ + + ]: 820 : if (with_typemod)
6825 272 : 5 : buf = printTypmod("timestamp", typemod, typeform->typmodout);
273 : : else
8739 274 : 815 : buf = pstrdup("timestamp without time zone");
8744 lockhart@fourpalms.o 275 : 820 : break;
276 : :
277 : 1137 : case TIMESTAMPTZOID:
8738 tgl@sss.pgh.pa.us 278 [ + + ]: 1137 : if (with_typemod)
6825 279 : 5 : buf = printTypmod("timestamp", typemod, typeform->typmodout);
280 : : else
8739 281 : 1132 : buf = pstrdup("timestamp with time zone");
8979 peter_e@gmx.net 282 : 1137 : break;
283 : :
9147 tgl@sss.pgh.pa.us 284 : 450 : case VARBITOID:
285 [ + + ]: 450 : if (with_typemod)
6825 286 : 66 : buf = printTypmod("bit varying", typemod, typeform->typmodout);
287 : : else
9147 288 : 384 : buf = pstrdup("bit varying");
289 : 450 : break;
290 : :
291 : 730 : case VARCHAROID:
292 [ + + ]: 730 : if (with_typemod)
6825 293 : 100 : buf = printTypmod("character varying", typemod, typeform->typmodout);
294 : : else
9147 295 : 630 : buf = pstrdup("character varying");
296 : 730 : break;
297 : :
779 amitlan@postgresql.o 298 : 202 : case JSONOID:
299 : 202 : buf = pstrdup("json");
300 : 202 : break;
301 : : }
302 : :
8529 tgl@sss.pgh.pa.us 303 [ + + ]: 352358 : 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 : :
2758 alvherre@alvh.no-ip. 314 [ + + + + ]: 480832 : if ((flags & FORMAT_TYPE_FORCE_QUALIFY) == 0 &&
315 : 240024 : TypeIsVisible(type_oid))
8529 tgl@sss.pgh.pa.us 316 : 232451 : nspname = NULL;
317 : : else
3806 alvherre@alvh.no-ip. 318 : 8357 : nspname = get_namespace_name_or_temp(typeform->typnamespace);
319 : :
8529 tgl@sss.pgh.pa.us 320 : 240808 : typname = NameStr(typeform->typname);
321 : :
322 : 240808 : buf = quote_qualified_identifier(nspname, typname);
323 : :
6825 324 [ + + ]: 240808 : if (with_typemod)
325 : 3 : buf = printTypmod(buf, typemod, typeform->typmodout);
326 : : }
327 : :
9192 peter_e@gmx.net 328 [ + + ]: 352358 : if (is_array)
4337 tgl@sss.pgh.pa.us 329 : 27381 : buf = psprintf("%s[]", buf);
330 : :
9060 331 : 352358 : ReleaseSysCache(tuple);
332 : :
9190 peter_e@gmx.net 333 : 352358 : 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 *
2758 alvherre@alvh.no-ip. 343 : 246325 : format_type_be(Oid type_oid)
344 : : {
345 : 246325 : 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 : 15788 : format_type_with_typemod(Oid type_oid, int32 typemod)
363 : : {
364 : 15788 : 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 *
6825 tgl@sss.pgh.pa.us 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 [ - + ]: 910 : Assert(typmod >= 0);
377 : :
378 [ - + ]: 910 : if (typmodout == InvalidOid)
379 : : {
380 : : /* Default behavior: just print the integer typmod with parens */
4337 tgl@sss.pgh.pa.us 381 :UBC 0 : res = psprintf("%s(%d)", typname, (int) typmod);
382 : : }
383 : : else
384 : : {
385 : : /* Use the type-specific typmodout procedure */
386 : : char *tmstr;
387 : :
6825 tgl@sss.pgh.pa.us 388 :CBC 910 : tmstr = DatumGetCString(OidFunctionCall1(typmodout,
389 : : Int32GetDatum(typmod)));
4337 390 : 910 : res = psprintf("%s%s", typname, tmstr);
391 : : }
392 : :
6825 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
9143 412 : 311883 : type_maximum_size(Oid type_oid, int32 typemod)
413 : : {
8719 414 [ + + ]: 311883 : if (typemod < 0)
9143 415 : 289211 : return -1;
416 : :
417 [ + + + + ]: 22672 : switch (type_oid)
418 : : {
419 : 12270 : case BPCHAROID:
420 : : case VARCHAROID:
421 : : /* typemod includes varlena header */
422 : :
423 : : /* typemod is in characters not bytes */
8751 424 : 24540 : return (typemod - VARHDRSZ) *
425 : 12270 : pg_encoding_max_length(GetDatabaseEncoding())
426 : 12270 : + VARHDRSZ;
427 : :
9143 428 : 3524 : case NUMERICOID:
5517 rhaas@postgresql.org 429 : 3524 : return numeric_maximum_size(typemod);
430 : :
9143 tgl@sss.pgh.pa.us 431 : 6656 : case VARBITOID:
432 : : case BITOID:
433 : : /* typemod is the (max) number of bits */
8934 bruce@momjian.us 434 : 6656 : return (typemod + (BITS_PER_BYTE - 1)) / BITS_PER_BYTE
9143 tgl@sss.pgh.pa.us 435 : 6656 : + 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
9190 peter_e@gmx.net 447 :UBC 0 : oidvectortypes(PG_FUNCTION_ARGS)
448 : : {
7466 tgl@sss.pgh.pa.us 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 : :
9190 peter_e@gmx.net 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 : : {
2758 alvherre@alvh.no-ip. 463 : 0 : char *typename = format_type_extended(oidArray->values[num], -1,
464 : : FORMAT_TYPE_ALLOW_INVALID);
8692 tgl@sss.pgh.pa.us 465 : 0 : size_t slen = strlen(typename);
466 : :
467 [ # # ]: 0 : if (left < (slen + 2))
468 : : {
469 : 0 : total += slen + 2;
9190 peter_e@gmx.net 470 : 0 : result = repalloc(result, total);
8692 tgl@sss.pgh.pa.us 471 : 0 : left += slen + 2;
472 : : }
473 : :
9190 peter_e@gmx.net 474 [ # # ]: 0 : if (num > 0)
475 : : {
476 : 0 : strcat(result, ", ");
477 : 0 : left -= 2;
478 : : }
479 : 0 : strcat(result, typename);
8692 tgl@sss.pgh.pa.us 480 : 0 : left -= slen;
481 : : }
482 : :
6374 483 : 0 : PG_RETURN_TEXT_P(cstring_to_text(result));
484 : : }
|