1 /*-------------------------------------------------------------------------
4 * Display type names "nicely".
7 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.28 2002/03/20 19:44:40 tgl Exp $
13 *-------------------------------------------------------------------------
21 #include "catalog/pg_type.h"
22 #include "utils/builtins.h"
23 #include "utils/datetime.h"
24 #include "utils/numeric.h"
25 #include "utils/syscache.h"
27 #include "mb/pg_wchar.h"
31 #define MASK(b) (1 << (b))
33 #define MAX_INT32_LEN 11
34 #define _textin(str) DirectFunctionCall1(textin, CStringGetDatum(str))
36 static char *format_type_internal(Oid type_oid, int32 typemod,
37 bool typemod_given, bool allow_invalid);
38 static char *psnprintf(size_t len, const char *fmt, ...)
39 /* This lets gcc check the format string for consistency. */
40 __attribute__((format(printf, 2, 3)));
44 * SQL function: format_type(type_oid, typemod)
46 * `type_oid' is from pg_type.oid, `typemod' is from
47 * pg_attribute.atttypmod. This function will get the type name and
48 * format it and the modifier to canonical SQL format, if the type is
49 * a standard type. Otherwise you just get pg_type.typname back,
50 * double quoted if it contains funny characters.
52 * If typemod is NULL then we are formatting a type name in a context where
53 * no typemod is available, eg a function argument or result type. This
54 * yields a slightly different result from specifying typemod = -1 in some
55 * cases. Given typemod = -1 we feel compelled to produce an output that
56 * the parser will interpret as having typemod -1, so that pg_dump will
57 * produce CREATE TABLE commands that recreate the original state. But
58 * given NULL typemod, we assume that the parser's interpretation of
59 * typemod doesn't matter, and so we are willing to output a slightly
60 * "prettier" representation of the same type. For example, type = bpchar
61 * and typemod = NULL gets you "character", whereas typemod = -1 gets you
62 * "bpchar" --- the former will be interpreted as character(1) by the
63 * parser, which does not yield typemod -1.
65 * XXX encoding a meaning in typemod = NULL is ugly; it'd have been
66 * cleaner to make two functions of one and two arguments respectively.
67 * Not worth changing it now, however.
70 format_type(PG_FUNCTION_ARGS)
76 /* Since this function is not strict, we must test for null args */
80 type_oid = PG_GETARG_OID(0);
84 result = format_type_internal(type_oid, -1, false, true);
88 typemod = PG_GETARG_INT32(1);
89 result = format_type_internal(type_oid, typemod, true, true);
92 PG_RETURN_DATUM(_textin(result));
96 * This version is for use within the backend in error messages, etc.
97 * One difference is that it will fail for an invalid type.
99 * The result is always a palloc'd string.
102 format_type_be(Oid type_oid)
104 return format_type_internal(type_oid, -1, false, false);
108 * This version allows a nondefault typemod to be specified.
111 format_type_with_typemod(Oid type_oid, int32 typemod)
113 return format_type_internal(type_oid, typemod, true, false);
119 format_type_internal(Oid type_oid, int32 typemod,
120 bool typemod_given, bool allow_invalid)
122 bool with_typemod = typemod_given && (typemod >= 0);
131 if (type_oid == InvalidOid && allow_invalid)
134 tuple = SearchSysCache(TYPEOID,
135 ObjectIdGetDatum(type_oid),
137 if (!HeapTupleIsValid(tuple))
140 return pstrdup("???");
142 elog(ERROR, "could not locate data type with oid %u in catalog",
147 * Check if it's an array (and not a domain --- we don't want to show
148 * the substructure of a domain type). Fixed-length array types such
149 * as "name" shouldn't get deconstructed either.
151 array_base_type = ((Form_pg_type) GETSTRUCT(tuple))->typelem;
152 typlen = ((Form_pg_type) GETSTRUCT(tuple))->typlen;
153 typtype = ((Form_pg_type) GETSTRUCT(tuple))->typtype;
155 if (array_base_type != InvalidOid && typlen < 0 && typtype != 'd')
157 /* Switch our attention to the array element type */
158 ReleaseSysCache(tuple);
159 tuple = SearchSysCache(TYPEOID,
160 ObjectIdGetDatum(array_base_type),
162 if (!HeapTupleIsValid(tuple))
165 return pstrdup("???[]");
167 elog(ERROR, "could not locate data type with oid %u in catalog",
171 type_oid = array_base_type;
180 buf = psnprintf(5 + MAX_INT32_LEN + 1, "bit(%d)",
182 else if (typemod_given)
185 * bit with typmod -1 is not the same as BIT, which means
186 * BIT(1) per SQL spec. Report it as the quoted typename
187 * so that parser will not assign a bogus typmod.
189 buf = pstrdup("\"bit\"");
192 buf = pstrdup("bit");
196 buf = pstrdup("boolean");
201 buf = psnprintf(11 + MAX_INT32_LEN + 1, "character(%d)",
202 (int) (typemod - VARHDRSZ));
203 else if (typemod_given)
206 * bpchar with typmod -1 is not the same as CHARACTER,
207 * which means CHARACTER(1) per SQL spec. Report it as
208 * bpchar so that parser will not assign a bogus typmod.
210 buf = pstrdup("bpchar");
213 buf = pstrdup("character");
218 * This char type is the single-byte version. You have to
219 * double-quote it to get at it in the parser.
221 buf = pstrdup("\"char\"");
225 buf = pstrdup("real");
229 buf = pstrdup("double precision");
233 buf = pstrdup("smallint");
237 buf = pstrdup("integer");
241 buf = pstrdup("bigint");
246 buf = psnprintf(10 + 2 * MAX_INT32_LEN + 1, "numeric(%d,%d)",
247 ((typemod - VARHDRSZ) >> 16) & 0xffff,
248 (typemod - VARHDRSZ) & 0xffff);
250 buf = pstrdup("numeric");
256 int fields = typemod >> 16;
257 int precision = typemod & 0xFFFF;
258 const char *fieldstr;
275 fieldstr = " minute";
278 fieldstr = " second";
280 case MASK(YEAR) | MASK(MONTH):
281 fieldstr = " year to month";
283 case MASK(DAY) | MASK(HOUR):
284 fieldstr = " day to hour";
286 case MASK(DAY) | MASK(HOUR) | MASK(MINUTE):
287 fieldstr = " day to minute";
289 case MASK(DAY) | MASK(HOUR) | MASK(MINUTE) | MASK(SECOND):
290 fieldstr = " day to second";
292 case MASK(HOUR) | MASK(MINUTE):
293 fieldstr = " hour to minute";
295 case MASK(HOUR) | MASK(MINUTE) | MASK(SECOND):
296 fieldstr = " hour to second";
298 case MASK(MINUTE) | MASK(SECOND):
299 fieldstr = " minute to second";
305 elog(LOG, "Invalid INTERVAL typmod 0x%x", typemod);
309 if (precision != 0xFFFF)
310 buf = psnprintf(100, "interval(%d)%s",
311 precision, fieldstr);
313 buf = psnprintf(100, "interval%s",
317 buf = pstrdup("interval");
322 buf = psnprintf(50, "time(%d) without time zone",
325 buf = pstrdup("time without time zone");
330 buf = psnprintf(50, "time(%d) with time zone",
333 buf = pstrdup("time with time zone");
338 buf = psnprintf(50, "timestamp(%d) without time zone",
341 buf = pstrdup("timestamp without time zone");
346 buf = psnprintf(50, "timestamp(%d) with time zone",
349 buf = pstrdup("timestamp with time zone");
354 buf = psnprintf(13 + MAX_INT32_LEN + 1, "bit varying(%d)",
357 buf = pstrdup("bit varying");
362 buf = psnprintf(19 + MAX_INT32_LEN + 1,
363 "character varying(%d)",
364 (int) (typemod - VARHDRSZ));
366 buf = pstrdup("character varying");
370 name = NameStr(((Form_pg_type) GETSTRUCT(tuple))->typname);
372 * Double-quote the name if it's not a standard identifier.
373 * Note this is *necessary* for ruleutils.c's use.
375 if (strspn(name, "abcdefghijklmnopqrstuvwxyz0123456789_") != strlen(name)
376 || isdigit((unsigned char) name[0]))
377 buf = psnprintf(strlen(name) + 3, "\"%s\"", name);
384 buf = psnprintf(strlen(buf) + 3, "%s[]", buf);
386 ReleaseSysCache(tuple);
393 * type_maximum_size --- determine maximum width of a varlena column
395 * If the max width is indeterminate, return -1. In particular, we return
396 * -1 for any type not known to this routine. We assume the caller has
397 * already determined that the type is a varlena type, so it's not
398 * necessary to look up the type's pg_type tuple here.
400 * This may appear unrelated to format_type(), but in fact the two routines
401 * share knowledge of the encoding of typmod for different types, so it's
402 * convenient to keep them together.
405 type_maximum_size(Oid type_oid, int32 typemod)
414 /* typemod includes varlena header */
416 /* typemod is in characters not bytes */
417 return (typemod - VARHDRSZ) *
418 pg_encoding_max_length(GetDatabaseEncoding())
425 /* precision (ie, max # of digits) is in upper bits of typmod */
426 if (typemod > VARHDRSZ)
428 int precision = ((typemod - VARHDRSZ) >> 16) & 0xffff;
430 /* Numeric stores 2 decimal digits/byte, plus header */
431 return (precision + 1) / 2 + NUMERIC_HDRSZ;
437 /* typemod is the (max) number of bits */
438 return (typemod + (BITS_PER_BYTE - 1)) / BITS_PER_BYTE
442 /* Unknown type, or unlimited-width type such as 'text' */
448 * oidvectortypes - converts a vector of type OIDs to "typname" list
450 * The interface for this function is wrong: it should be told how many
451 * OIDs are significant in the input vector, so that trailing InvalidOid
452 * argument types can be recognized.
455 oidvectortypes(PG_FUNCTION_ARGS)
457 Oid *oidArray = (Oid *) PG_GETARG_POINTER(0);
464 /* Try to guess how many args there are :-( */
466 for (num = 0; num < FUNC_MAX_ARGS; num++)
468 if (oidArray[num] != InvalidOid)
472 total = 20 * numargs + 1;
473 result = palloc(total);
477 for (num = 0; num < numargs; num++)
479 char *typename = format_type_internal(oidArray[num], -1,
481 size_t slen = strlen(typename);
483 if (left < (slen + 2))
486 result = repalloc(result, total);
492 strcat(result, ", ");
495 strcat(result, typename);
499 PG_RETURN_DATUM(_textin(result));
503 /* snprintf into a palloc'd string */
505 psnprintf(size_t len, const char *fmt, ...)
513 vsnprintf(buf, len, fmt, ap);