Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * format_type.c
4 : * Display type names "nicely".
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2024, 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 103920 : format_type(PG_FUNCTION_ARGS)
61 : {
62 : Oid type_oid;
63 : int32 typemod;
64 : char *result;
65 103920 : bits16 flags = FORMAT_TYPE_ALLOW_INVALID;
66 :
67 : /* Since this function is not strict, we must test for null args */
68 103920 : if (PG_ARGISNULL(0))
69 862 : PG_RETURN_NULL();
70 :
71 103058 : type_oid = PG_GETARG_OID(0);
72 :
73 103058 : if (PG_ARGISNULL(1))
74 49246 : typemod = -1;
75 : else
76 : {
77 53812 : typemod = PG_GETARG_INT32(1);
78 53812 : flags |= FORMAT_TYPE_TYPEMOD_GIVEN;
79 : }
80 :
81 103058 : result = format_type_extended(type_oid, typemod, flags);
82 :
83 103058 : 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 689126 : 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 689126 : 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 689088 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
130 689088 : if (!HeapTupleIsValid(tuple))
131 : {
132 4 : if ((flags & FORMAT_TYPE_INVALID_AS_NULL) != 0)
133 0 : return NULL;
134 4 : else if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
135 4 : return pstrdup("???");
136 : else
137 0 : elog(ERROR, "cache lookup failed for type %u", type_oid);
138 : }
139 689084 : 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 689084 : array_base_type = typeform->typelem;
148 :
149 689084 : if (IsTrueArrayType(typeform) &&
150 58416 : typeform->typstorage != TYPSTORAGE_PLAIN)
151 : {
152 : /* Switch our attention to the array element type */
153 58032 : ReleaseSysCache(tuple);
154 58032 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(array_base_type));
155 58032 : 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 58032 : typeform = (Form_pg_type) GETSTRUCT(tuple);
165 58032 : type_oid = array_base_type;
166 58032 : is_array = true;
167 : }
168 : else
169 631052 : is_array = false;
170 :
171 689084 : 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 689084 : buf = NULL; /* flag for no special case */
185 :
186 689084 : switch (type_oid)
187 : {
188 890 : case BITOID:
189 890 : if (with_typemod)
190 220 : buf = printTypmod("bit", typemod, typeform->typmodout);
191 670 : 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 608 : buf = pstrdup("bit");
201 890 : break;
202 :
203 13612 : case BOOLOID:
204 13612 : buf = pstrdup("boolean");
205 13612 : break;
206 :
207 2868 : case BPCHAROID:
208 2868 : if (with_typemod)
209 830 : buf = printTypmod("character", typemod, typeform->typmodout);
210 2038 : 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 1342 : buf = pstrdup("character");
220 2868 : break;
221 :
222 2228 : case FLOAT4OID:
223 2228 : buf = pstrdup("real");
224 2228 : break;
225 :
226 4730 : case FLOAT8OID:
227 4730 : buf = pstrdup("double precision");
228 4730 : break;
229 :
230 6366 : case INT2OID:
231 6366 : buf = pstrdup("smallint");
232 6366 : break;
233 :
234 159514 : case INT4OID:
235 159514 : buf = pstrdup("integer");
236 159514 : break;
237 :
238 9822 : case INT8OID:
239 9822 : buf = pstrdup("bigint");
240 9822 : break;
241 :
242 3260 : case NUMERICOID:
243 3260 : if (with_typemod)
244 370 : buf = printTypmod("numeric", typemod, typeform->typmodout);
245 : else
246 2890 : buf = pstrdup("numeric");
247 3260 : break;
248 :
249 1232 : case INTERVALOID:
250 1232 : if (with_typemod)
251 0 : buf = printTypmod("interval", typemod, typeform->typmodout);
252 : else
253 1232 : buf = pstrdup("interval");
254 1232 : break;
255 :
256 888 : case TIMEOID:
257 888 : if (with_typemod)
258 10 : buf = printTypmod("time", typemod, typeform->typmodout);
259 : else
260 878 : buf = pstrdup("time without time zone");
261 888 : break;
262 :
263 802 : case TIMETZOID:
264 802 : if (with_typemod)
265 10 : buf = printTypmod("time", typemod, typeform->typmodout);
266 : else
267 792 : buf = pstrdup("time with time zone");
268 802 : break;
269 :
270 1248 : case TIMESTAMPOID:
271 1248 : if (with_typemod)
272 10 : buf = printTypmod("timestamp", typemod, typeform->typmodout);
273 : else
274 1238 : buf = pstrdup("timestamp without time zone");
275 1248 : break;
276 :
277 1856 : case TIMESTAMPTZOID:
278 1856 : if (with_typemod)
279 10 : buf = printTypmod("timestamp", typemod, typeform->typmodout);
280 : else
281 1846 : buf = pstrdup("timestamp with time zone");
282 1856 : break;
283 :
284 662 : case VARBITOID:
285 662 : if (with_typemod)
286 132 : buf = printTypmod("bit varying", typemod, typeform->typmodout);
287 : else
288 530 : buf = pstrdup("bit varying");
289 662 : break;
290 :
291 1386 : case VARCHAROID:
292 1386 : if (with_typemod)
293 192 : buf = printTypmod("character varying", typemod, typeform->typmodout);
294 : else
295 1194 : buf = pstrdup("character varying");
296 1386 : break;
297 :
298 416 : case JSONOID:
299 416 : buf = pstrdup("json");
300 416 : break;
301 : }
302 :
303 689084 : 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 954624 : if ((flags & FORMAT_TYPE_FORCE_QUALIFY) == 0 &&
315 476562 : TypeIsVisible(type_oid))
316 456854 : nspname = NULL;
317 : else
318 21208 : nspname = get_namespace_name_or_temp(typeform->typnamespace);
319 :
320 478062 : typname = NameStr(typeform->typname);
321 :
322 478062 : buf = quote_qualified_identifier(nspname, typname);
323 :
324 478062 : if (with_typemod)
325 6 : buf = printTypmod(buf, typemod, typeform->typmodout);
326 : }
327 :
328 689084 : if (is_array)
329 58032 : buf = psprintf("%s[]", buf);
330 :
331 689084 : ReleaseSysCache(tuple);
332 :
333 689084 : 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 486234 : format_type_be(Oid type_oid)
344 : {
345 486234 : 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 29558 : format_type_with_typemod(Oid type_oid, int32 typemod)
363 : {
364 29558 : 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 1790 : 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 1790 : 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 1790 : tmstr = DatumGetCString(OidFunctionCall1(typmodout,
389 : Int32GetDatum(typmod)));
390 1790 : res = psprintf("%s%s", typname, tmstr);
391 : }
392 :
393 1790 : 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 534282 : type_maximum_size(Oid type_oid, int32 typemod)
413 : {
414 534282 : if (typemod < 0)
415 506282 : return -1;
416 :
417 28000 : switch (type_oid)
418 : {
419 19178 : case BPCHAROID:
420 : case VARCHAROID:
421 : /* typemod includes varlena header */
422 :
423 : /* typemod is in characters not bytes */
424 38356 : return (typemod - VARHDRSZ) *
425 19178 : pg_encoding_max_length(GetDatabaseEncoding())
426 19178 : + 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 : }
|