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
60 96380 : format_type(PG_FUNCTION_ARGS)
61 : {
62 : Oid type_oid;
63 : int32 typemod;
64 : char *result;
65 96380 : bits16 flags = FORMAT_TYPE_ALLOW_INVALID;
66 :
67 : /* Since this function is not strict, we must test for null args */
68 96380 : if (PG_ARGISNULL(0))
69 862 : PG_RETURN_NULL();
70 :
71 95518 : type_oid = PG_GETARG_OID(0);
72 :
73 95518 : if (PG_ARGISNULL(1))
74 41522 : typemod = -1;
75 : else
76 : {
77 53996 : typemod = PG_GETARG_INT32(1);
78 53996 : flags |= FORMAT_TYPE_TYPEMOD_GIVEN;
79 : }
80 :
81 95518 : result = format_type_extended(type_oid, typemod, flags);
82 :
83 95518 : 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 682904 : 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 682904 : if (type_oid == InvalidOid)
122 : {
123 38 : if ((flags & FORMAT_TYPE_INVALID_AS_NULL) != 0)
124 18 : return NULL;
125 20 : else if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
126 20 : return pstrdup("-");
127 : }
128 :
129 682866 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
130 682866 : 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 682866 : 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 682866 : array_base_type = typeform->typelem;
148 :
149 682866 : if (IsTrueArrayType(typeform) &&
150 54924 : typeform->typstorage != TYPSTORAGE_PLAIN)
151 : {
152 : /* Switch our attention to the array element type */
153 54538 : ReleaseSysCache(tuple);
154 54538 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(array_base_type));
155 54538 : 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 54538 : typeform = (Form_pg_type) GETSTRUCT(tuple);
165 54538 : type_oid = array_base_type;
166 54538 : is_array = true;
167 : }
168 : else
169 628328 : is_array = false;
170 :
171 682866 : 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 682866 : buf = NULL; /* flag for no special case */
185 :
186 682866 : switch (type_oid)
187 : {
188 880 : case BITOID:
189 880 : if (with_typemod)
190 220 : buf = printTypmod("bit", typemod, typeform->typmodout);
191 660 : 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 598 : buf = pstrdup("bit");
201 880 : break;
202 :
203 13620 : case BOOLOID:
204 13620 : buf = pstrdup("boolean");
205 13620 : break;
206 :
207 2864 : case BPCHAROID:
208 2864 : if (with_typemod)
209 836 : buf = printTypmod("character", typemod, typeform->typmodout);
210 2028 : 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 1332 : buf = pstrdup("character");
220 2864 : break;
221 :
222 2218 : case FLOAT4OID:
223 2218 : buf = pstrdup("real");
224 2218 : break;
225 :
226 4742 : case FLOAT8OID:
227 4742 : buf = pstrdup("double precision");
228 4742 : break;
229 :
230 6404 : case INT2OID:
231 6404 : buf = pstrdup("smallint");
232 6404 : break;
233 :
234 159830 : case INT4OID:
235 159830 : buf = pstrdup("integer");
236 159830 : break;
237 :
238 9906 : case INT8OID:
239 9906 : buf = pstrdup("bigint");
240 9906 : break;
241 :
242 3332 : case NUMERICOID:
243 3332 : if (with_typemod)
244 370 : buf = printTypmod("numeric", typemod, typeform->typmodout);
245 : else
246 2962 : buf = pstrdup("numeric");
247 3332 : break;
248 :
249 1246 : case INTERVALOID:
250 1246 : if (with_typemod)
251 0 : buf = printTypmod("interval", typemod, typeform->typmodout);
252 : else
253 1246 : buf = pstrdup("interval");
254 1246 : break;
255 :
256 932 : case TIMEOID:
257 932 : if (with_typemod)
258 10 : buf = printTypmod("time", typemod, typeform->typmodout);
259 : else
260 922 : buf = pstrdup("time without time zone");
261 932 : break;
262 :
263 792 : case TIMETZOID:
264 792 : if (with_typemod)
265 10 : buf = printTypmod("time", typemod, typeform->typmodout);
266 : else
267 782 : buf = pstrdup("time with time zone");
268 792 : break;
269 :
270 1244 : case TIMESTAMPOID:
271 1244 : if (with_typemod)
272 10 : buf = printTypmod("timestamp", typemod, typeform->typmodout);
273 : else
274 1234 : buf = pstrdup("timestamp without time zone");
275 1244 : break;
276 :
277 1858 : case TIMESTAMPTZOID:
278 1858 : if (with_typemod)
279 10 : buf = printTypmod("timestamp", typemod, typeform->typmodout);
280 : else
281 1848 : buf = pstrdup("timestamp with time zone");
282 1858 : break;
283 :
284 652 : case VARBITOID:
285 652 : if (with_typemod)
286 132 : buf = printTypmod("bit varying", typemod, typeform->typmodout);
287 : else
288 520 : buf = pstrdup("bit varying");
289 652 : break;
290 :
291 1374 : case VARCHAROID:
292 1374 : if (with_typemod)
293 192 : buf = printTypmod("character varying", typemod, typeform->typmodout);
294 : else
295 1182 : buf = pstrdup("character varying");
296 1374 : break;
297 :
298 404 : case JSONOID:
299 404 : buf = pstrdup("json");
300 404 : break;
301 : }
302 :
303 682866 : 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 941152 : if ((flags & FORMAT_TYPE_FORCE_QUALIFY) == 0 &&
315 469826 : TypeIsVisible(type_oid))
316 451514 : nspname = NULL;
317 : else
318 19812 : nspname = get_namespace_name_or_temp(typeform->typnamespace);
319 :
320 471326 : typname = NameStr(typeform->typname);
321 :
322 471326 : buf = quote_qualified_identifier(nspname, typname);
323 :
324 471326 : if (with_typemod)
325 6 : buf = printTypmod(buf, typemod, typeform->typmodout);
326 : }
327 :
328 682866 : if (is_array)
329 54538 : buf = psprintf("%s[]", buf);
330 :
331 682866 : ReleaseSysCache(tuple);
332 :
333 682866 : 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 483868 : format_type_be(Oid type_oid)
344 : {
345 483868 : 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 892 : format_type_be_qualified(Oid type_oid)
354 : {
355 892 : 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 30136 : format_type_with_typemod(Oid type_oid, int32 typemod)
363 : {
364 30136 : 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 1796 : 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 1796 : if (typmodout == InvalidOid)
379 : {
380 : /* Default behavior: just print the integer typmod with parens */
381 0 : res = psprintf("%s(%d)", typname, (int) typmod);
382 : }
383 : else
384 : {
385 : /* Use the type-specific typmodout procedure */
386 : char *tmstr;
387 :
388 1796 : tmstr = DatumGetCString(OidFunctionCall1(typmodout,
389 : Int32GetDatum(typmod)));
390 1796 : res = psprintf("%s%s", typname, tmstr);
391 : }
392 :
393 1796 : 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 536920 : type_maximum_size(Oid type_oid, int32 typemod)
413 : {
414 536920 : if (typemod < 0)
415 509680 : return -1;
416 :
417 27240 : switch (type_oid)
418 : {
419 18418 : case BPCHAROID:
420 : case VARCHAROID:
421 : /* typemod includes varlena header */
422 :
423 : /* typemod is in characters not bytes */
424 36836 : return (typemod - VARHDRSZ) *
425 18418 : pg_encoding_max_length(GetDatabaseEncoding())
426 18418 : + VARHDRSZ;
427 :
428 7164 : case NUMERICOID:
429 7164 : return numeric_maximum_size(typemod);
430 :
431 1238 : case VARBITOID:
432 : case BITOID:
433 : /* typemod is the (max) number of bits */
434 1238 : return (typemod + (BITS_PER_BYTE - 1)) / BITS_PER_BYTE
435 1238 : + 2 * sizeof(int32);
436 : }
437 :
438 : /* Unknown type, or unlimited-width type such as 'text' */
439 420 : 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 0 : int numargs = oidArray->dim1;
452 : int num;
453 : size_t total;
454 : size_t left;
455 :
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 : {
463 0 : char *typename = format_type_extended(oidArray->values[num], -1,
464 : FORMAT_TYPE_ALLOW_INVALID);
465 0 : size_t slen = strlen(typename);
466 :
467 0 : if (left < (slen + 2))
468 : {
469 0 : total += slen + 2;
470 0 : result = repalloc(result, total);
471 0 : left += slen + 2;
472 : }
473 :
474 0 : if (num > 0)
475 : {
476 0 : strcat(result, ", ");
477 0 : left -= 2;
478 : }
479 0 : strcat(result, typename);
480 0 : left -= slen;
481 : }
482 :
483 0 : PG_RETURN_TEXT_P(cstring_to_text(result));
484 : }
|