Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * format_type.c
4 : * Display type names "nicely".
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2023, 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 76352 : format_type(PG_FUNCTION_ARGS)
61 : {
62 : Oid type_oid;
63 : int32 typemod;
64 : char *result;
65 76352 : bits16 flags = FORMAT_TYPE_ALLOW_INVALID;
66 :
67 : /* Since this function is not strict, we must test for null args */
68 76352 : if (PG_ARGISNULL(0))
69 862 : PG_RETURN_NULL();
70 :
71 75490 : type_oid = PG_GETARG_OID(0);
72 :
73 75490 : if (PG_ARGISNULL(1))
74 34038 : typemod = -1;
75 : else
76 : {
77 41452 : typemod = PG_GETARG_INT32(1);
78 41452 : flags |= FORMAT_TYPE_TYPEMOD_GIVEN;
79 : }
80 :
81 75490 : result = format_type_extended(type_oid, typemod, flags);
82 :
83 75490 : 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 596464 : 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 596464 : 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 596426 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
130 596426 : 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 596426 : 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 596426 : array_base_type = typeform->typelem;
148 :
149 596426 : if (IsTrueArrayType(typeform) &&
150 44196 : typeform->typstorage != TYPSTORAGE_PLAIN)
151 : {
152 : /* Switch our attention to the array element type */
153 44062 : ReleaseSysCache(tuple);
154 44062 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(array_base_type));
155 44062 : 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 44062 : typeform = (Form_pg_type) GETSTRUCT(tuple);
165 44062 : type_oid = array_base_type;
166 44062 : is_array = true;
167 : }
168 : else
169 552364 : is_array = false;
170 :
171 596426 : 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 596426 : buf = NULL; /* flag for no special case */
185 :
186 596426 : switch (type_oid)
187 : {
188 568 : case BITOID:
189 568 : if (with_typemod)
190 214 : buf = printTypmod("bit", typemod, typeform->typmodout);
191 354 : 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 298 : buf = pstrdup("bit");
201 568 : break;
202 :
203 12362 : case BOOLOID:
204 12362 : buf = pstrdup("boolean");
205 12362 : break;
206 :
207 2364 : case BPCHAROID:
208 2364 : if (with_typemod)
209 778 : buf = printTypmod("character", typemod, typeform->typmodout);
210 1586 : 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 924 : buf = pstrdup("character");
220 2364 : break;
221 :
222 1348 : case FLOAT4OID:
223 1348 : buf = pstrdup("real");
224 1348 : break;
225 :
226 3696 : case FLOAT8OID:
227 3696 : buf = pstrdup("double precision");
228 3696 : break;
229 :
230 2080 : case INT2OID:
231 2080 : buf = pstrdup("smallint");
232 2080 : break;
233 :
234 151776 : case INT4OID:
235 151776 : buf = pstrdup("integer");
236 151776 : break;
237 :
238 6466 : case INT8OID:
239 6466 : buf = pstrdup("bigint");
240 6466 : break;
241 :
242 2728 : case NUMERICOID:
243 2728 : if (with_typemod)
244 370 : buf = printTypmod("numeric", typemod, typeform->typmodout);
245 : else
246 2358 : buf = pstrdup("numeric");
247 2728 : break;
248 :
249 626 : case INTERVALOID:
250 626 : if (with_typemod)
251 0 : buf = printTypmod("interval", typemod, typeform->typmodout);
252 : else
253 626 : buf = pstrdup("interval");
254 626 : break;
255 :
256 576 : case TIMEOID:
257 576 : if (with_typemod)
258 10 : buf = printTypmod("time", typemod, typeform->typmodout);
259 : else
260 566 : buf = pstrdup("time without time zone");
261 576 : break;
262 :
263 514 : case TIMETZOID:
264 514 : if (with_typemod)
265 10 : buf = printTypmod("time", typemod, typeform->typmodout);
266 : else
267 504 : buf = pstrdup("time with time zone");
268 514 : break;
269 :
270 630 : case TIMESTAMPOID:
271 630 : if (with_typemod)
272 10 : buf = printTypmod("timestamp", typemod, typeform->typmodout);
273 : else
274 620 : buf = pstrdup("timestamp without time zone");
275 630 : break;
276 :
277 884 : case TIMESTAMPTZOID:
278 884 : if (with_typemod)
279 10 : buf = printTypmod("timestamp", typemod, typeform->typmodout);
280 : else
281 874 : buf = pstrdup("timestamp with time zone");
282 884 : break;
283 :
284 366 : case VARBITOID:
285 366 : if (with_typemod)
286 132 : buf = printTypmod("bit varying", typemod, typeform->typmodout);
287 : else
288 234 : buf = pstrdup("bit varying");
289 366 : break;
290 :
291 1220 : case VARCHAROID:
292 1220 : if (with_typemod)
293 174 : buf = printTypmod("character varying", typemod, typeform->typmodout);
294 : else
295 1046 : buf = pstrdup("character varying");
296 1220 : break;
297 : }
298 :
299 596426 : if (buf == NULL)
300 : {
301 : /*
302 : * Default handling: report the name as it appears in the catalog.
303 : * Here, we must qualify the name if it is not visible in the search
304 : * path or if caller requests it; and we must double-quote it if it's
305 : * not a standard identifier or if it matches any keyword.
306 : */
307 : char *nspname;
308 : char *typname;
309 :
310 816808 : if ((flags & FORMAT_TYPE_FORCE_QUALIFY) == 0 &&
311 407868 : TypeIsVisible(type_oid))
312 399198 : nspname = NULL;
313 : else
314 9742 : nspname = get_namespace_name_or_temp(typeform->typnamespace);
315 :
316 408940 : typname = NameStr(typeform->typname);
317 :
318 408940 : buf = quote_qualified_identifier(nspname, typname);
319 :
320 408940 : if (with_typemod)
321 6 : buf = printTypmod(buf, typemod, typeform->typmodout);
322 : }
323 :
324 596426 : if (is_array)
325 44062 : buf = psprintf("%s[]", buf);
326 :
327 596426 : ReleaseSysCache(tuple);
328 :
329 596426 : return buf;
330 : }
331 :
332 : /*
333 : * This version is for use within the backend in error messages, etc.
334 : * One difference is that it will fail for an invalid type.
335 : *
336 : * The result is always a palloc'd string.
337 : */
338 : char *
339 437042 : format_type_be(Oid type_oid)
340 : {
341 437042 : return format_type_extended(type_oid, -1, 0);
342 : }
343 :
344 : /*
345 : * This version returns a name that is always qualified (unless it's one
346 : * of the SQL-keyword type names, such as TIMESTAMP WITH TIME ZONE).
347 : */
348 : char *
349 848 : format_type_be_qualified(Oid type_oid)
350 : {
351 848 : return format_type_extended(type_oid, -1, FORMAT_TYPE_FORCE_QUALIFY);
352 : }
353 :
354 : /*
355 : * This version allows a nondefault typemod to be specified.
356 : */
357 : char *
358 25880 : format_type_with_typemod(Oid type_oid, int32 typemod)
359 : {
360 25880 : return format_type_extended(type_oid, typemod, FORMAT_TYPE_TYPEMOD_GIVEN);
361 : }
362 :
363 : /*
364 : * Add typmod decoration to the basic type name
365 : */
366 : static char *
367 1714 : printTypmod(const char *typname, int32 typmod, Oid typmodout)
368 : {
369 : char *res;
370 :
371 : /* Shouldn't be called if typmod is -1 */
372 : Assert(typmod >= 0);
373 :
374 1714 : if (typmodout == InvalidOid)
375 : {
376 : /* Default behavior: just print the integer typmod with parens */
377 0 : res = psprintf("%s(%d)", typname, (int) typmod);
378 : }
379 : else
380 : {
381 : /* Use the type-specific typmodout procedure */
382 : char *tmstr;
383 :
384 1714 : tmstr = DatumGetCString(OidFunctionCall1(typmodout,
385 : Int32GetDatum(typmod)));
386 1714 : res = psprintf("%s%s", typname, tmstr);
387 : }
388 :
389 1714 : return res;
390 : }
391 :
392 :
393 : /*
394 : * type_maximum_size --- determine maximum width of a variable-width column
395 : *
396 : * If the max width is indeterminate, return -1. In particular, we return
397 : * -1 for any type not known to this routine. We assume the caller has
398 : * already determined that the type is a variable-width type, so it's not
399 : * necessary to look up the type's pg_type tuple here.
400 : *
401 : * This may appear unrelated to format_type(), but in fact the two routines
402 : * share knowledge of the encoding of typmod for different types, so it's
403 : * convenient to keep them together. (XXX now that most of this knowledge
404 : * has been pushed out of format_type into the typmodout functions, it's
405 : * interesting to wonder if it's worth trying to factor this code too...)
406 : */
407 : int32
408 590454 : type_maximum_size(Oid type_oid, int32 typemod)
409 : {
410 590454 : if (typemod < 0)
411 562762 : return -1;
412 :
413 27692 : switch (type_oid)
414 : {
415 18898 : case BPCHAROID:
416 : case VARCHAROID:
417 : /* typemod includes varlena header */
418 :
419 : /* typemod is in characters not bytes */
420 37796 : return (typemod - VARHDRSZ) *
421 18898 : pg_encoding_max_length(GetDatabaseEncoding())
422 18898 : + VARHDRSZ;
423 :
424 7158 : case NUMERICOID:
425 7158 : return numeric_maximum_size(typemod);
426 :
427 1216 : case VARBITOID:
428 : case BITOID:
429 : /* typemod is the (max) number of bits */
430 1216 : return (typemod + (BITS_PER_BYTE - 1)) / BITS_PER_BYTE
431 1216 : + 2 * sizeof(int32);
432 : }
433 :
434 : /* Unknown type, or unlimited-width type such as 'text' */
435 420 : return -1;
436 : }
437 :
438 :
439 : /*
440 : * oidvectortypes - converts a vector of type OIDs to "typname" list
441 : */
442 : Datum
443 0 : oidvectortypes(PG_FUNCTION_ARGS)
444 : {
445 0 : oidvector *oidArray = (oidvector *) PG_GETARG_POINTER(0);
446 : char *result;
447 0 : int numargs = oidArray->dim1;
448 : int num;
449 : size_t total;
450 : size_t left;
451 :
452 0 : total = 20 * numargs + 1;
453 0 : result = palloc(total);
454 0 : result[0] = '\0';
455 0 : left = total - 1;
456 :
457 0 : for (num = 0; num < numargs; num++)
458 : {
459 0 : char *typename = format_type_extended(oidArray->values[num], -1,
460 : FORMAT_TYPE_ALLOW_INVALID);
461 0 : size_t slen = strlen(typename);
462 :
463 0 : if (left < (slen + 2))
464 : {
465 0 : total += slen + 2;
466 0 : result = repalloc(result, total);
467 0 : left += slen + 2;
468 : }
469 :
470 0 : if (num > 0)
471 : {
472 0 : strcat(result, ", ");
473 0 : left -= 2;
474 : }
475 0 : strcat(result, typename);
476 0 : left -= slen;
477 : }
478 :
479 0 : PG_RETURN_TEXT_P(cstring_to_text(result));
480 : }
|