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