1 /*-------------------------------------------------------------------------
2 *
3 * format_type.c
4 * Display type names "nicely".
5 *
6 *
7 * Portions Copyright (c) 1996-2018, 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 "utils/builtins.h"
24 #include "utils/lsyscache.h"
25 #include "utils/numeric.h"
26 #include "utils/syscache.h"
27 #include "mb/pg_wchar.h"
28
29 #define MAX_INT32_LEN 11
30
31 static char *printTypmod(const char *typname, int32 typmod, Oid typmodout);
32
33
34 /*
35 * SQL function: format_type(type_oid, typemod)
36 *
37 * `type_oid' is from pg_type.oid, `typemod' is from
38 * pg_attribute.atttypmod. This function will get the type name and
39 * format it and the modifier to canonical SQL format, if the type is
40 * a standard type. Otherwise you just get pg_type.typname back,
41 * double quoted if it contains funny characters or matches a keyword.
42 *
43 * If typemod is NULL then we are formatting a type name in a context where
44 * no typemod is available, eg a function argument or result type. This
45 * yields a slightly different result from specifying typemod = -1 in some
46 * cases. Given typemod = -1 we feel compelled to produce an output that
47 * the parser will interpret as having typemod -1, so that pg_dump will
48 * produce CREATE TABLE commands that recreate the original state. But
49 * given NULL typemod, we assume that the parser's interpretation of
50 * typemod doesn't matter, and so we are willing to output a slightly
51 * "prettier" representation of the same type. For example, type = bpchar
52 * and typemod = NULL gets you "character", whereas typemod = -1 gets you
53 * "bpchar" --- the former will be interpreted as character(1) by the
54 * parser, which does not yield typemod -1.
55 *
56 * XXX encoding a meaning in typemod = NULL is ugly; it'd have been
57 * cleaner to make two functions of one and two arguments respectively.
58 * Not worth changing it now, however.
59 */
60 Datum
format_type(PG_FUNCTION_ARGS)61 format_type(PG_FUNCTION_ARGS)
62 {
63 Oid type_oid;
64 int32 typemod;
65 char *result;
66 bits16 flags = FORMAT_TYPE_ALLOW_INVALID;
67
68 /* Since this function is not strict, we must test for null args */
69 if (PG_ARGISNULL(0))
70 PG_RETURN_NULL();
71
72 type_oid = PG_GETARG_OID(0);
73
74 if (PG_ARGISNULL(1))
75 typemod = -1;
76 else
77 {
78 typemod = PG_GETARG_INT32(1);
79 flags |= FORMAT_TYPE_TYPEMOD_GIVEN;
80 }
81
82 result = format_type_extended(type_oid, typemod, flags);
83
84 PG_RETURN_TEXT_P(cstring_to_text(result));
85 }
86
87 /*
88 * format_type_extended
89 * Generate a possibly-qualified type name.
90 *
91 * The default behavior is to only qualify if the type is not in the search
92 * path, to ignore the given typmod, and to raise an error if a non-existent
93 * type_oid is given.
94 *
95 * The following bits in 'flags' modify the behavior:
96 * - FORMAT_TYPE_TYPEMOD_GIVEN
97 * include the typmod in the output (typmod could still be -1 though)
98 * - FORMAT_TYPE_ALLOW_INVALID
99 * if the type OID is invalid or unknown, return ??? or such instead
100 * of failing
101 * - FORMAT_TYPE_FORCE_QUALIFY
102 * always schema-qualify type names, regardless of search_path
103 *
104 * Note that TYPEMOD_GIVEN is not interchangeable with "typemod == -1";
105 * see the comments above for format_type().
106 *
107 * Returns a palloc'd string.
108 */
109 char *
format_type_extended(Oid type_oid,int32 typemod,bits16 flags)110 format_type_extended(Oid type_oid, int32 typemod, bits16 flags)
111 {
112 HeapTuple tuple;
113 Form_pg_type typeform;
114 Oid array_base_type;
115 bool is_array;
116 char *buf;
117 bool with_typemod;
118
119 if (type_oid == InvalidOid && (flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
120 return pstrdup("-");
121
122 tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
123 if (!HeapTupleIsValid(tuple))
124 {
125 if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
126 return pstrdup("???");
127 else
128 elog(ERROR, "cache lookup failed for type %u", type_oid);
129 }
130 typeform = (Form_pg_type) GETSTRUCT(tuple);
131
132 /*
133 * Check if it's a regular (variable length) array type. Fixed-length
134 * array types such as "name" shouldn't get deconstructed. As of Postgres
135 * 8.1, rather than checking typlen we check the toast property, and don't
136 * deconstruct "plain storage" array types --- this is because we don't
137 * want to show oidvector as oid[].
138 */
139 array_base_type = typeform->typelem;
140
141 if (array_base_type != InvalidOid && typeform->typstorage != 'p')
142 {
143 /* Switch our attention to the array element type */
144 ReleaseSysCache(tuple);
145 tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(array_base_type));
146 if (!HeapTupleIsValid(tuple))
147 {
148 if ((flags & FORMAT_TYPE_ALLOW_INVALID) != 0)
149 return pstrdup("???[]");
150 else
151 elog(ERROR, "cache lookup failed for type %u", type_oid);
152 }
153 typeform = (Form_pg_type) GETSTRUCT(tuple);
154 type_oid = array_base_type;
155 is_array = true;
156 }
157 else
158 is_array = false;
159
160 with_typemod = (flags & FORMAT_TYPE_TYPEMOD_GIVEN) != 0 && (typemod >= 0);
161
162 /*
163 * See if we want to special-case the output for certain built-in types.
164 * Note that these special cases should all correspond to special
165 * productions in gram.y, to ensure that the type name will be taken as a
166 * system type, not a user type of the same name.
167 *
168 * If we do not provide a special-case output here, the type name will be
169 * handled the same way as a user type name --- in particular, it will be
170 * double-quoted if it matches any lexer keyword. This behavior is
171 * essential for some cases, such as types "bit" and "char".
172 */
173 buf = NULL; /* flag for no special case */
174
175 switch (type_oid)
176 {
177 case BITOID:
178 if (with_typemod)
179 buf = printTypmod("bit", typemod, typeform->typmodout);
180 else if ((flags & FORMAT_TYPE_TYPEMOD_GIVEN) != 0)
181 {
182 /*
183 * bit with typmod -1 is not the same as BIT, which means
184 * BIT(1) per SQL spec. Report it as the quoted typename so
185 * that parser will not assign a bogus typmod.
186 */
187 }
188 else
189 buf = pstrdup("bit");
190 break;
191
192 case BOOLOID:
193 buf = pstrdup("boolean");
194 break;
195
196 case BPCHAROID:
197 if (with_typemod)
198 buf = printTypmod("character", typemod, typeform->typmodout);
199 else if ((flags & FORMAT_TYPE_TYPEMOD_GIVEN) != 0)
200 {
201 /*
202 * bpchar with typmod -1 is not the same as CHARACTER, which
203 * means CHARACTER(1) per SQL spec. Report it as bpchar so
204 * that parser will not assign a bogus typmod.
205 */
206 }
207 else
208 buf = pstrdup("character");
209 break;
210
211 case FLOAT4OID:
212 buf = pstrdup("real");
213 break;
214
215 case FLOAT8OID:
216 buf = pstrdup("double precision");
217 break;
218
219 case INT2OID:
220 buf = pstrdup("smallint");
221 break;
222
223 case INT4OID:
224 buf = pstrdup("integer");
225 break;
226
227 case INT8OID:
228 buf = pstrdup("bigint");
229 break;
230
231 case NUMERICOID:
232 if (with_typemod)
233 buf = printTypmod("numeric", typemod, typeform->typmodout);
234 else
235 buf = pstrdup("numeric");
236 break;
237
238 case INTERVALOID:
239 if (with_typemod)
240 buf = printTypmod("interval", typemod, typeform->typmodout);
241 else
242 buf = pstrdup("interval");
243 break;
244
245 case TIMEOID:
246 if (with_typemod)
247 buf = printTypmod("time", typemod, typeform->typmodout);
248 else
249 buf = pstrdup("time without time zone");
250 break;
251
252 case TIMETZOID:
253 if (with_typemod)
254 buf = printTypmod("time", typemod, typeform->typmodout);
255 else
256 buf = pstrdup("time with time zone");
257 break;
258
259 case TIMESTAMPOID:
260 if (with_typemod)
261 buf = printTypmod("timestamp", typemod, typeform->typmodout);
262 else
263 buf = pstrdup("timestamp without time zone");
264 break;
265
266 case TIMESTAMPTZOID:
267 if (with_typemod)
268 buf = printTypmod("timestamp", typemod, typeform->typmodout);
269 else
270 buf = pstrdup("timestamp with time zone");
271 break;
272
273 case VARBITOID:
274 if (with_typemod)
275 buf = printTypmod("bit varying", typemod, typeform->typmodout);
276 else
277 buf = pstrdup("bit varying");
278 break;
279
280 case VARCHAROID:
281 if (with_typemod)
282 buf = printTypmod("character varying", typemod, typeform->typmodout);
283 else
284 buf = pstrdup("character varying");
285 break;
286 }
287
288 if (buf == NULL)
289 {
290 /*
291 * Default handling: report the name as it appears in the catalog.
292 * Here, we must qualify the name if it is not visible in the search
293 * path or if caller requests it; and we must double-quote it if it's
294 * not a standard identifier or if it matches any keyword.
295 */
296 char *nspname;
297 char *typname;
298
299 if ((flags & FORMAT_TYPE_FORCE_QUALIFY) == 0 &&
300 TypeIsVisible(type_oid))
301 nspname = NULL;
302 else
303 nspname = get_namespace_name_or_temp(typeform->typnamespace);
304
305 typname = NameStr(typeform->typname);
306
307 buf = quote_qualified_identifier(nspname, typname);
308
309 if (with_typemod)
310 buf = printTypmod(buf, typemod, typeform->typmodout);
311 }
312
313 if (is_array)
314 buf = psprintf("%s[]", buf);
315
316 ReleaseSysCache(tuple);
317
318 return buf;
319 }
320
321 /*
322 * This version is for use within the backend in error messages, etc.
323 * One difference is that it will fail for an invalid type.
324 *
325 * The result is always a palloc'd string.
326 */
327 char *
format_type_be(Oid type_oid)328 format_type_be(Oid type_oid)
329 {
330 return format_type_extended(type_oid, -1, 0);
331 }
332
333 /*
334 * This version returns a name that is always qualified (unless it's one
335 * of the SQL-keyword type names, such as TIMESTAMP WITH TIME ZONE).
336 */
337 char *
format_type_be_qualified(Oid type_oid)338 format_type_be_qualified(Oid type_oid)
339 {
340 return format_type_extended(type_oid, -1, FORMAT_TYPE_FORCE_QUALIFY);
341 }
342
343 /*
344 * This version allows a nondefault typemod to be specified.
345 */
346 char *
format_type_with_typemod(Oid type_oid,int32 typemod)347 format_type_with_typemod(Oid type_oid, int32 typemod)
348 {
349 return format_type_extended(type_oid, typemod, FORMAT_TYPE_TYPEMOD_GIVEN);
350 }
351
352 /*
353 * Add typmod decoration to the basic type name
354 */
355 static char *
printTypmod(const char * typname,int32 typmod,Oid typmodout)356 printTypmod(const char *typname, int32 typmod, Oid typmodout)
357 {
358 char *res;
359
360 /* Shouldn't be called if typmod is -1 */
361 Assert(typmod >= 0);
362
363 if (typmodout == InvalidOid)
364 {
365 /* Default behavior: just print the integer typmod with parens */
366 res = psprintf("%s(%d)", typname, (int) typmod);
367 }
368 else
369 {
370 /* Use the type-specific typmodout procedure */
371 char *tmstr;
372
373 tmstr = DatumGetCString(OidFunctionCall1(typmodout,
374 Int32GetDatum(typmod)));
375 res = psprintf("%s%s", typname, tmstr);
376 }
377
378 return res;
379 }
380
381
382 /*
383 * type_maximum_size --- determine maximum width of a variable-width column
384 *
385 * If the max width is indeterminate, return -1. In particular, we return
386 * -1 for any type not known to this routine. We assume the caller has
387 * already determined that the type is a variable-width type, so it's not
388 * necessary to look up the type's pg_type tuple here.
389 *
390 * This may appear unrelated to format_type(), but in fact the two routines
391 * share knowledge of the encoding of typmod for different types, so it's
392 * convenient to keep them together. (XXX now that most of this knowledge
393 * has been pushed out of format_type into the typmodout functions, it's
394 * interesting to wonder if it's worth trying to factor this code too...)
395 */
396 int32
type_maximum_size(Oid type_oid,int32 typemod)397 type_maximum_size(Oid type_oid, int32 typemod)
398 {
399 if (typemod < 0)
400 return -1;
401
402 switch (type_oid)
403 {
404 case BPCHAROID:
405 case VARCHAROID:
406 /* typemod includes varlena header */
407
408 /* typemod is in characters not bytes */
409 return (typemod - VARHDRSZ) *
410 pg_encoding_max_length(GetDatabaseEncoding())
411 + VARHDRSZ;
412
413 case NUMERICOID:
414 return numeric_maximum_size(typemod);
415
416 case VARBITOID:
417 case BITOID:
418 /* typemod is the (max) number of bits */
419 return (typemod + (BITS_PER_BYTE - 1)) / BITS_PER_BYTE
420 + 2 * sizeof(int32);
421 }
422
423 /* Unknown type, or unlimited-width type such as 'text' */
424 return -1;
425 }
426
427
428 /*
429 * oidvectortypes - converts a vector of type OIDs to "typname" list
430 */
431 Datum
oidvectortypes(PG_FUNCTION_ARGS)432 oidvectortypes(PG_FUNCTION_ARGS)
433 {
434 oidvector *oidArray = (oidvector *) PG_GETARG_POINTER(0);
435 char *result;
436 int numargs = oidArray->dim1;
437 int num;
438 size_t total;
439 size_t left;
440
441 total = 20 * numargs + 1;
442 result = palloc(total);
443 result[0] = '\0';
444 left = total - 1;
445
446 for (num = 0; num < numargs; num++)
447 {
448 char *typename = format_type_extended(oidArray->values[num], -1,
449 FORMAT_TYPE_ALLOW_INVALID);
450 size_t slen = strlen(typename);
451
452 if (left < (slen + 2))
453 {
454 total += slen + 2;
455 result = repalloc(result, total);
456 left += slen + 2;
457 }
458
459 if (num > 0)
460 {
461 strcat(result, ", ");
462 left -= 2;
463 }
464 strcat(result, typename);
465 left -= slen;
466 }
467
468 PG_RETURN_TEXT_P(cstring_to_text(result));
469 }
470