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-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
9382 peter_e@gmx.net 60 :CBC 53474 : format_type(PG_FUNCTION_ARGS)
61 : : {
62 : : Oid type_oid;
63 : : int32 typemod;
64 : : char *result;
2936 tgl@sss.pgh.pa.us 65 : 53474 : bits16 flags = FORMAT_TYPE_ALLOW_INVALID;
66 : :
67 : : /* Since this function is not strict, we must test for null args */
9382 peter_e@gmx.net 68 [ + + ]: 53474 : if (PG_ARGISNULL(0))
69 : 458 : PG_RETURN_NULL();
70 : :
9337 tgl@sss.pgh.pa.us 71 : 53016 : type_oid = PG_GETARG_OID(0);
72 : :
2936 73 [ + + ]: 53016 : if (PG_ARGISNULL(1))
74 : 22788 : typemod = -1;
75 : : else
76 : : {
77 : 30228 : typemod = PG_GETARG_INT32(1);
78 : 30228 : flags |= FORMAT_TYPE_TYPEMOD_GIVEN;
79 : : }
80 : :
81 : 53016 : result = format_type_extended(type_oid, typemod, flags);
82 : :
6564 83 : 53016 : 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 *
2948 alvherre@alvh.no-ip. 112 : 362336 : 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 : :
2078 michael@paquier.xyz 121 [ + + ]: 362336 : 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 : :
5873 rhaas@postgresql.org 129 : 362317 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
9382 peter_e@gmx.net 130 [ - + ]: 362317 : if (!HeapTupleIsValid(tuple))
131 : : {
2078 michael@paquier.xyz 132 [ # # ]:UBC 0 : if ((flags & FORMAT_TYPE_INVALID_AS_NULL) != 0)
133 : 0 : return NULL;
134 [ # # ]: 0 : else if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
8984 peter_e@gmx.net 135 : 0 : return pstrdup("???");
136 : : else
8267 tgl@sss.pgh.pa.us 137 [ # # ]: 0 : elog(ERROR, "cache lookup failed for type %u", type_oid);
138 : : }
8719 tgl@sss.pgh.pa.us 139 :CBC 362317 : 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 : 362317 : array_base_type = typeform->typelem;
148 : :
1922 149 [ + + + + ]: 362317 : if (IsTrueArrayType(typeform) &&
2202 150 [ + + ]: 31703 : typeform->typstorage != TYPSTORAGE_PLAIN)
151 : : {
152 : : /* Switch our attention to the array element type */
9250 153 : 31494 : ReleaseSysCache(tuple);
5873 rhaas@postgresql.org 154 : 31494 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(array_base_type));
9382 peter_e@gmx.net 155 [ - + ]: 31494 : if (!HeapTupleIsValid(tuple))
156 : : {
2078 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)
8984 peter_e@gmx.net 160 : 0 : return pstrdup("???[]");
161 : : else
8267 tgl@sss.pgh.pa.us 162 [ # # ]: 0 : elog(ERROR, "cache lookup failed for type %u", type_oid);
163 : : }
8719 tgl@sss.pgh.pa.us 164 :CBC 31494 : typeform = (Form_pg_type) GETSTRUCT(tuple);
9337 165 : 31494 : type_oid = array_base_type;
8719 166 : 31494 : is_array = true;
167 : : }
168 : : else
9382 peter_e@gmx.net 169 : 330823 : is_array = false;
170 : :
2948 alvherre@alvh.no-ip. 171 [ + + + + ]: 362317 : 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 : : */
8719 tgl@sss.pgh.pa.us 184 : 362317 : buf = NULL; /* flag for no special case */
185 : :
9337 186 [ + + + + : 362317 : switch (type_oid)
+ + + + +
+ + + + +
+ + + + ]
187 : : {
8889 188 : 573 : case BITOID:
189 [ + + ]: 573 : if (with_typemod)
7015 190 : 112 : buf = printTypmod("bit", typemod, typeform->typmodout);
2948 alvherre@alvh.no-ip. 191 [ + + ]: 461 : 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
8882 tgl@sss.pgh.pa.us 200 : 430 : buf = pstrdup("bit");
8889 201 : 573 : break;
202 : :
9337 203 : 7051 : case BOOLOID:
204 : 7051 : buf = pstrdup("boolean");
205 : 7051 : break;
206 : :
207 : 1565 : case BPCHAROID:
208 [ + + ]: 1565 : if (with_typemod)
7015 209 : 418 : buf = printTypmod("character", typemod, typeform->typmodout);
2948 alvherre@alvh.no-ip. 210 [ + + ]: 1147 : 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
8882 tgl@sss.pgh.pa.us 219 : 793 : buf = pstrdup("character");
9337 220 : 1565 : break;
221 : :
222 : 1298 : case FLOAT4OID:
223 : 1298 : buf = pstrdup("real");
224 : 1298 : break;
225 : :
226 : 2728 : case FLOAT8OID:
227 : 2728 : buf = pstrdup("double precision");
228 : 2728 : break;
229 : :
230 : 3756 : case INT2OID:
231 : 3756 : buf = pstrdup("smallint");
232 : 3756 : break;
233 : :
234 : 82644 : case INT4OID:
235 : 82644 : buf = pstrdup("integer");
236 : 82644 : break;
237 : :
238 : 5584 : case INT8OID:
239 : 5584 : buf = pstrdup("bigint");
240 : 5584 : break;
241 : :
242 : 1820 : case NUMERICOID:
243 [ + + ]: 1820 : if (with_typemod)
7015 244 : 185 : buf = printTypmod("numeric", typemod, typeform->typmodout);
245 : : else
9337 246 : 1635 : buf = pstrdup("numeric");
247 : 1820 : break;
248 : :
8909 249 : 771 : case INTERVALOID:
250 [ - + ]: 771 : if (with_typemod)
7015 tgl@sss.pgh.pa.us 251 :UBC 0 : buf = printTypmod("interval", typemod, typeform->typmodout);
252 : : else
8909 tgl@sss.pgh.pa.us 253 :CBC 771 : buf = pstrdup("interval");
254 : 771 : break;
255 : :
8929 256 : 615 : case TIMEOID:
8928 257 [ + + ]: 615 : if (with_typemod)
7015 258 : 5 : buf = printTypmod("time", typemod, typeform->typmodout);
259 : : else
8929 260 : 610 : buf = pstrdup("time without time zone");
261 : 615 : break;
262 : :
9337 263 : 517 : case TIMETZOID:
8928 264 [ + + ]: 517 : if (with_typemod)
7015 265 : 5 : buf = printTypmod("time", typemod, typeform->typmodout);
266 : : else
8929 267 : 512 : buf = pstrdup("time with time zone");
9337 268 : 517 : break;
269 : :
9169 peter_e@gmx.net 270 : 824 : case TIMESTAMPOID:
8928 tgl@sss.pgh.pa.us 271 [ + + ]: 824 : if (with_typemod)
7015 272 : 5 : buf = printTypmod("timestamp", typemod, typeform->typmodout);
273 : : else
8929 274 : 819 : buf = pstrdup("timestamp without time zone");
8934 lockhart@fourpalms.o 275 : 824 : break;
276 : :
277 : 1183 : case TIMESTAMPTZOID:
8928 tgl@sss.pgh.pa.us 278 [ + + ]: 1183 : if (with_typemod)
7015 279 : 5 : buf = printTypmod("timestamp", typemod, typeform->typmodout);
280 : : else
8929 281 : 1178 : buf = pstrdup("timestamp with time zone");
9169 peter_e@gmx.net 282 : 1183 : break;
283 : :
9337 tgl@sss.pgh.pa.us 284 : 451 : case VARBITOID:
285 [ + + ]: 451 : if (with_typemod)
7015 286 : 66 : buf = printTypmod("bit varying", typemod, typeform->typmodout);
287 : : else
9337 288 : 385 : buf = pstrdup("bit varying");
289 : 451 : break;
290 : :
291 : 738 : case VARCHAROID:
292 [ + + ]: 738 : if (with_typemod)
7015 293 : 106 : buf = printTypmod("character varying", typemod, typeform->typmodout);
294 : : else
9337 295 : 632 : buf = pstrdup("character varying");
296 : 738 : break;
297 : :
969 amitlan@postgresql.o 298 : 210 : case JSONOID:
299 : 210 : buf = pstrdup("json");
300 : 210 : break;
301 : : }
302 : :
8719 tgl@sss.pgh.pa.us 303 [ + + ]: 362317 : 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 : :
2948 alvherre@alvh.no-ip. 314 [ + + + + ]: 499886 : if ((flags & FORMAT_TYPE_FORCE_QUALIFY) == 0 &&
315 : 249512 : TypeIsVisible(type_oid))
8719 tgl@sss.pgh.pa.us 316 : 237537 : nspname = NULL;
317 : : else
3996 alvherre@alvh.no-ip. 318 : 12837 : nspname = get_namespace_name_or_temp(typeform->typnamespace);
319 : :
8719 tgl@sss.pgh.pa.us 320 : 250374 : typname = NameStr(typeform->typname);
321 : :
322 : 250374 : buf = quote_qualified_identifier(nspname, typname);
323 : :
7015 324 [ + + ]: 250374 : if (with_typemod)
325 : 3 : buf = printTypmod(buf, typemod, typeform->typmodout);
326 : : }
327 : :
9382 peter_e@gmx.net 328 [ + + ]: 362317 : if (is_array)
4527 tgl@sss.pgh.pa.us 329 : 31494 : buf = psprintf("%s[]", buf);
330 : :
9250 331 : 362317 : ReleaseSysCache(tuple);
332 : :
9380 peter_e@gmx.net 333 : 362317 : 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 *
2948 alvherre@alvh.no-ip. 343 : 247518 : format_type_be(Oid type_oid)
344 : : {
345 : 247518 : 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 : 16154 : format_type_with_typemod(Oid type_oid, int32 typemod)
363 : : {
364 : 16154 : 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 *
7015 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 */
96 peter@eisentraut.org 381 :UNC 0 : res = psprintf("%s(%d)", typname, typmod);
382 : : }
383 : : else
384 : : {
385 : : /* Use the type-specific typmodout procedure */
386 : : char *tmstr;
387 : :
7015 tgl@sss.pgh.pa.us 388 :CBC 910 : tmstr = DatumGetCString(OidFunctionCall1(typmodout,
389 : : Int32GetDatum(typmod)));
4527 390 : 910 : res = psprintf("%s%s", typname, tmstr);
391 : : }
392 : :
7015 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
9333 412 : 357738 : type_maximum_size(Oid type_oid, int32 typemod)
413 : : {
8909 414 [ + + ]: 357738 : if (typemod < 0)
9333 415 : 333525 : return -1;
416 : :
417 [ + + + + ]: 24213 : switch (type_oid)
418 : : {
419 : 13802 : case BPCHAROID:
420 : : case VARCHAROID:
421 : : /* typemod includes varlena header */
422 : :
423 : : /* typemod is in characters not bytes */
8941 424 : 27604 : return (typemod - VARHDRSZ) *
425 : 13802 : pg_encoding_max_length(GetDatabaseEncoding())
426 : 13802 : + VARHDRSZ;
427 : :
9333 428 : 3524 : case NUMERICOID:
5707 rhaas@postgresql.org 429 : 3524 : return numeric_maximum_size(typemod);
430 : :
9333 tgl@sss.pgh.pa.us 431 : 6665 : case VARBITOID:
432 : : case BITOID:
433 : : /* typemod is the (max) number of bits */
9124 bruce@momjian.us 434 : 6665 : return (typemod + (BITS_PER_BYTE - 1)) / BITS_PER_BYTE
9333 tgl@sss.pgh.pa.us 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
9380 peter_e@gmx.net 447 :UBC 0 : oidvectortypes(PG_FUNCTION_ARGS)
448 : : {
7656 tgl@sss.pgh.pa.us 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 */
34 457 : 0 : check_valid_oidvector(oidArray);
458 : 0 : numargs = oidArray->dim1;
459 : :
9380 peter_e@gmx.net 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 : : {
2948 alvherre@alvh.no-ip. 467 : 0 : char *typename = format_type_extended(oidArray->values[num], -1,
468 : : FORMAT_TYPE_ALLOW_INVALID);
8882 tgl@sss.pgh.pa.us 469 : 0 : size_t slen = strlen(typename);
470 : :
471 [ # # ]: 0 : if (left < (slen + 2))
472 : : {
473 : 0 : total += slen + 2;
9380 peter_e@gmx.net 474 : 0 : result = repalloc(result, total);
8882 tgl@sss.pgh.pa.us 475 : 0 : left += slen + 2;
476 : : }
477 : :
9380 peter_e@gmx.net 478 [ # # ]: 0 : if (num > 0)
479 : : {
480 : 0 : strcat(result, ", ");
481 : 0 : left -= 2;
482 : : }
483 : 0 : strcat(result, typename);
8882 tgl@sss.pgh.pa.us 484 : 0 : left -= slen;
485 : : }
486 : :
6564 487 : 0 : PG_RETURN_TEXT_P(cstring_to_text(result));
488 : : }
|