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 "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
9617 peter_e@gmx.net 62 :CBC 61794 : format_type(PG_FUNCTION_ARGS)
63 : : {
64 : : Oid type_oid;
65 : : int32 typemod;
66 : : char *result;
220 nathan@postgresql.or 67 : 61794 : uint16 flags = FORMAT_TYPE_ALLOW_INVALID;
68 : :
69 : : /* Since this function is not strict, we must test for null args */
9617 peter_e@gmx.net 70 [ + + ]: 61794 : if (PG_ARGISNULL(0))
71 : 502 : PG_RETURN_NULL();
72 : :
9572 tgl@sss.pgh.pa.us 73 : 61292 : type_oid = PG_GETARG_OID(0);
74 : :
3171 75 [ + + ]: 61292 : if (PG_ARGISNULL(1))
76 : 28992 : typemod = -1;
77 : : else
78 : : {
79 : 32300 : typemod = PG_GETARG_INT32(1);
80 : 32300 : flags |= FORMAT_TYPE_TYPEMOD_GIVEN;
81 : : }
82 : :
83 : 61292 : result = format_type_extended(type_oid, typemod, flags);
84 : :
6799 85 : 61292 : 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 *
220 nathan@postgresql.or 114 : 389996 : 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 : :
2313 michael@paquier.xyz 123 [ + + ]: 389996 : 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 : :
6108 rhaas@postgresql.org 131 : 389974 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
9617 peter_e@gmx.net 132 [ - + ]: 389974 : if (!HeapTupleIsValid(tuple))
133 : : {
2313 michael@paquier.xyz 134 [ # # ]:LBC (2) : if ((flags & FORMAT_TYPE_INVALID_AS_NULL) != 0)
2313 michael@paquier.xyz 135 :UBC 0 : return NULL;
2313 michael@paquier.xyz 136 [ # # ]:LBC (2) : else if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
9219 peter_e@gmx.net 137 : (2) : return pstrdup("???");
138 : : else
8502 tgl@sss.pgh.pa.us 139 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", type_oid);
140 : : }
8954 tgl@sss.pgh.pa.us 141 :CBC 389974 : 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 : 389974 : array_base_type = typeform->typelem;
150 : :
2157 151 [ + + + + ]: 389974 : if (IsTrueArrayType(typeform) &&
2437 152 [ + + ]: 39325 : typeform->typstorage != TYPSTORAGE_PLAIN)
153 : : {
154 : : /* Switch our attention to the array element type */
9485 155 : 39084 : ReleaseSysCache(tuple);
6108 rhaas@postgresql.org 156 : 39084 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(array_base_type));
9617 peter_e@gmx.net 157 [ - + ]: 39084 : if (!HeapTupleIsValid(tuple))
158 : : {
2313 michael@paquier.xyz 159 [ # # ]:UBC 0 : if ((flags & FORMAT_TYPE_INVALID_AS_NULL) != 0)
160 : 0 : return NULL;
161 [ # # ]: 0 : else if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
9219 peter_e@gmx.net 162 : 0 : return pstrdup("???[]");
163 : : else
8502 tgl@sss.pgh.pa.us 164 [ # # ]: 0 : elog(ERROR, "cache lookup failed for type %u", type_oid);
165 : : }
8954 tgl@sss.pgh.pa.us 166 :CBC 39084 : typeform = (Form_pg_type) GETSTRUCT(tuple);
9572 167 : 39084 : type_oid = array_base_type;
8954 168 : 39084 : is_array = true;
169 : : }
170 : : else
9617 peter_e@gmx.net 171 : 350890 : is_array = false;
172 : :
3183 alvherre@alvh.no-ip. 173 [ + + + + ]: 389974 : 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 : : */
8954 tgl@sss.pgh.pa.us 186 : 389974 : buf = NULL; /* flag for no special case */
187 : :
9572 188 [ + + + + : 389974 : switch (type_oid)
+ + + + +
+ + + + +
+ + + + ]
189 : : {
9124 190 : 651 : case BITOID:
191 [ + + ]: 651 : if (with_typemod)
7250 192 : 133 : buf = printTypmod("bit", typemod, typeform->typmodout);
3183 alvherre@alvh.no-ip. 193 [ + + ]: 518 : 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
9117 tgl@sss.pgh.pa.us 202 : 483 : buf = pstrdup("bit");
9124 203 : 651 : break;
204 : :
9572 205 : 7209 : case BOOLOID:
206 : 7209 : buf = pstrdup("boolean");
207 : 7209 : break;
208 : :
209 : 1820 : case BPCHAROID:
210 [ + + ]: 1820 : if (with_typemod)
7250 211 : 450 : buf = printTypmod("character", typemod, typeform->typmodout);
3183 alvherre@alvh.no-ip. 212 [ + + ]: 1370 : 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
9117 tgl@sss.pgh.pa.us 221 : 912 : buf = pstrdup("character");
9572 222 : 1820 : break;
223 : :
224 : 1523 : case FLOAT4OID:
225 : 1523 : buf = pstrdup("real");
226 : 1523 : break;
227 : :
228 : 3160 : case FLOAT8OID:
229 : 3160 : buf = pstrdup("double precision");
230 : 3160 : break;
231 : :
232 : 4055 : case INT2OID:
233 : 4055 : buf = pstrdup("smallint");
234 : 4055 : break;
235 : :
236 : 85941 : case INT4OID:
237 : 85941 : buf = pstrdup("integer");
238 : 85941 : break;
239 : :
240 : 6371 : case INT8OID:
241 : 6371 : buf = pstrdup("bigint");
242 : 6371 : break;
243 : :
244 : 2201 : case NUMERICOID:
245 [ + + ]: 2201 : if (with_typemod)
7250 246 : 205 : buf = printTypmod("numeric", typemod, typeform->typmodout);
247 : : else
9572 248 : 1996 : buf = pstrdup("numeric");
249 : 2201 : break;
250 : :
9144 251 : 873 : case INTERVALOID:
252 [ - + ]: 873 : if (with_typemod)
7250 tgl@sss.pgh.pa.us 253 :UBC 0 : buf = printTypmod("interval", typemod, typeform->typmodout);
254 : : else
9144 tgl@sss.pgh.pa.us 255 :CBC 873 : buf = pstrdup("interval");
256 : 873 : break;
257 : :
9164 258 : 711 : case TIMEOID:
9163 259 [ + + ]: 711 : if (with_typemod)
7250 260 : 5 : buf = printTypmod("time", typemod, typeform->typmodout);
261 : : else
9164 262 : 706 : buf = pstrdup("time without time zone");
263 : 711 : break;
264 : :
9572 265 : 594 : case TIMETZOID:
9163 266 [ + + ]: 594 : if (with_typemod)
7250 267 : 5 : buf = printTypmod("time", typemod, typeform->typmodout);
268 : : else
9164 269 : 589 : buf = pstrdup("time with time zone");
9572 270 : 594 : break;
271 : :
9404 peter_e@gmx.net 272 : 917 : case TIMESTAMPOID:
9163 tgl@sss.pgh.pa.us 273 [ + + ]: 917 : if (with_typemod)
7250 274 : 5 : buf = printTypmod("timestamp", typemod, typeform->typmodout);
275 : : else
9164 276 : 912 : buf = pstrdup("timestamp without time zone");
9169 lockhart@fourpalms.o 277 : 917 : break;
278 : :
279 : 1332 : case TIMESTAMPTZOID:
9163 tgl@sss.pgh.pa.us 280 [ + + ]: 1332 : if (with_typemod)
7250 281 : 5 : buf = printTypmod("timestamp", typemod, typeform->typmodout);
282 : : else
9164 283 : 1327 : buf = pstrdup("timestamp with time zone");
9404 peter_e@gmx.net 284 : 1332 : break;
285 : :
9572 tgl@sss.pgh.pa.us 286 : 509 : case VARBITOID:
287 [ + + ]: 509 : if (with_typemod)
7250 288 : 83 : buf = printTypmod("bit varying", typemod, typeform->typmodout);
289 : : else
9572 290 : 426 : buf = pstrdup("bit varying");
291 : 509 : break;
292 : :
293 : 910 : case VARCHAROID:
294 [ + + ]: 910 : if (with_typemod)
7250 295 : 118 : buf = printTypmod("character varying", typemod, typeform->typmodout);
296 : : else
9572 297 : 792 : buf = pstrdup("character varying");
298 : 910 : break;
299 : :
1204 amitlan@postgresql.o 300 : 268 : case JSONOID:
301 : 268 : buf = pstrdup("json");
302 : 268 : break;
303 : : }
304 : :
8954 tgl@sss.pgh.pa.us 305 [ + + ]: 389974 : 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 : :
3183 alvherre@alvh.no-ip. 316 [ + + + + ]: 541774 : if ((flags & FORMAT_TYPE_FORCE_QUALIFY) == 0 &&
317 : 270352 : TypeIsVisible(type_oid))
8954 tgl@sss.pgh.pa.us 318 : 256808 : nspname = NULL;
319 : : else
4231 alvherre@alvh.no-ip. 320 : 14614 : nspname = get_namespace_name_or_temp(typeform->typnamespace);
321 : :
8954 tgl@sss.pgh.pa.us 322 : 271422 : typname = NameStr(typeform->typname);
323 : :
324 : 271422 : buf = quote_qualified_identifier(nspname, typname);
325 : :
7250 326 [ + + ]: 271422 : if (with_typemod)
327 : 4 : buf = printTypmod(buf, typemod, typeform->typmodout);
328 : : }
329 : :
9617 peter_e@gmx.net 330 [ + + ]: 389974 : if (is_array)
4762 tgl@sss.pgh.pa.us 331 : 39084 : buf = psprintf("%s[]", buf);
332 : :
9485 333 : 389974 : ReleaseSysCache(tuple);
334 : :
9615 peter_e@gmx.net 335 : 389974 : 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 *
3183 alvherre@alvh.no-ip. 345 : 251768 : format_type_be(Oid type_oid)
346 : : {
347 : 251768 : 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 : 21861 : format_type_with_typemod(Oid type_oid, int32 typemod)
365 : : {
366 : 21861 : 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 *
7250 tgl@sss.pgh.pa.us 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 [ - + ]: 1013 : Assert(typmod >= 0);
379 : :
380 [ - + ]: 1013 : if (typmodout == InvalidOid)
381 : : {
382 : : /* Default behavior: just print the integer typmod with parens */
331 peter@eisentraut.org 383 :UBC 0 : res = psprintf("%s(%d)", typname, typmod);
384 : : }
385 : : else
386 : : {
387 : : /* Use the type-specific typmodout procedure */
388 : : char *tmstr;
389 : :
7250 tgl@sss.pgh.pa.us 390 :CBC 1013 : tmstr = DatumGetCString(OidFunctionCall1(typmodout,
391 : : Int32GetDatum(typmod)));
4762 392 : 1013 : res = psprintf("%s%s", typname, tmstr);
393 : : }
394 : :
7250 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
9568 414 : 508717 : type_maximum_size(Oid type_oid, int32 typemod)
415 : : {
9144 416 [ + + ]: 508717 : if (typemod < 0)
9568 417 : 474010 : return -1;
418 : :
419 [ + + + + ]: 34707 : switch (type_oid)
420 : : {
421 : 18446 : case BPCHAROID:
422 : : case VARCHAROID:
423 : : /* typemod includes varlena header */
424 : :
425 : : /* typemod is in characters not bytes */
9176 426 : 36892 : return (typemod - VARHDRSZ) *
427 : 18446 : pg_encoding_max_length(GetDatabaseEncoding())
428 : 18446 : + VARHDRSZ;
429 : :
9568 430 : 4892 : case NUMERICOID:
5942 rhaas@postgresql.org 431 : 4892 : return numeric_maximum_size(typemod);
432 : :
9568 tgl@sss.pgh.pa.us 433 : 11001 : case VARBITOID:
434 : : case BITOID:
435 : : /* typemod is the (max) number of bits */
9359 bruce@momjian.us 436 : 11001 : return (typemod + (BITS_PER_BYTE - 1)) / BITS_PER_BYTE
9568 tgl@sss.pgh.pa.us 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
9615 peter_e@gmx.net 449 :UBC 0 : oidvectortypes(PG_FUNCTION_ARGS)
450 : : {
7891 tgl@sss.pgh.pa.us 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 */
269 457 : 0 : check_valid_oidvector(oidArray);
458 : 0 : numargs = oidArray->dim1;
459 : :
21 heikki.linnakangas@i 460 :UNC 0 : initStringInfo(&result);
461 : :
9615 peter_e@gmx.net 462 [ # # ]:UBC 0 : for (num = 0; num < numargs; num++)
463 : : {
464 : : char *typename;
465 : :
21 heikki.linnakangas@i 466 [ # # ]:UNC 0 : CHECK_FOR_INTERRUPTS();
467 : :
468 : 0 : typename = format_type_extended(oidArray->values[num], -1,
469 : : FORMAT_TYPE_ALLOW_INVALID);
470 : :
9615 peter_e@gmx.net 471 [ # # ]:UBC 0 : if (num > 0)
21 heikki.linnakangas@i 472 :UNC 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 : : }
|