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 : : }
|