]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/format_type.c
Add DOMAIN support. Includes manual pages and regression tests, from
[postgresql] / src / backend / utils / adt / format_type.c
1 /*-------------------------------------------------------------------------
2  *
3  * format_type.c
4  *        Display type names "nicely".
5  *
6  *
7  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.27 2002/03/19 02:18:21 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include "postgres.h"
17
18 #include <ctype.h>
19
20 #include "fmgr.h"
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"
26 #ifdef MULTIBYTE
27 #include "mb/pg_wchar.h"
28 #endif
29
30
31 #define MASK(b) (1 << (b))
32
33 #define MAX_INT32_LEN 11
34 #define _textin(str) DirectFunctionCall1(textin, CStringGetDatum(str))
35
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)));
41
42
43 /*
44  * SQL function: format_type(type_oid, typemod)
45  *
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.
51  *
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.
64  *
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.
68  */
69 Datum
70 format_type(PG_FUNCTION_ARGS)
71 {
72         Oid                     type_oid;
73         int32           typemod;
74         char       *result;
75
76         /* Since this function is not strict, we must test for null args */
77         if (PG_ARGISNULL(0))
78                 PG_RETURN_NULL();
79
80         type_oid = PG_GETARG_OID(0);
81
82         if (PG_ARGISNULL(1))
83         {
84                 result = format_type_internal(type_oid, -1, false, true);
85         }
86         else
87         {
88                 typemod = PG_GETARG_INT32(1);
89                 result = format_type_internal(type_oid, typemod, true, true);
90         }
91
92         PG_RETURN_DATUM(_textin(result));
93 }
94
95 /*
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.
98  *
99  * The result is always a palloc'd string.
100  */
101 char *
102 format_type_be(Oid type_oid)
103 {
104         return format_type_internal(type_oid, -1, false, false);
105 }
106
107 /*
108  * This version allows a nondefault typemod to be specified.
109  */
110 char *
111 format_type_with_typemod(Oid type_oid, int32 typemod)
112 {
113         return format_type_internal(type_oid, typemod, true, false);
114 }
115
116
117
118 static char *
119 format_type_internal(Oid type_oid, int32 typemod,
120                                          bool typemod_given, bool allow_invalid)
121 {
122         bool            with_typemod = typemod_given && (typemod >= 0);
123         HeapTuple       tuple;
124         Oid                     array_base_type;
125         int16           typlen;
126         bool            is_array;
127         char       *name;
128         char       *buf;
129         char            typtype;
130
131         if (type_oid == InvalidOid && allow_invalid)
132                 return pstrdup("-");
133
134         tuple = SearchSysCache(TYPEOID,
135                                                    ObjectIdGetDatum(type_oid),
136                                                    0, 0, 0);
137         if (!HeapTupleIsValid(tuple))
138         {
139                 if (allow_invalid)
140                         return pstrdup("???");
141                 else
142                         elog(ERROR, "could not locate data type with oid %u in catalog",
143                                  type_oid);
144         }
145
146         array_base_type = ((Form_pg_type) GETSTRUCT(tuple))->typelem;
147         typlen = ((Form_pg_type) GETSTRUCT(tuple))->typlen;
148         typtype = ((Form_pg_type) GETSTRUCT(tuple))->typtype;
149
150         /*
151          * Domains look alot like arrays, so lets process them first, and return
152          * back to avoid the array and 'standard' formatting procedures that are
153          * use for base types.
154          */
155         if (typtype == 'd') {
156                 name = NameStr(((Form_pg_type) GETSTRUCT(tuple))->typname);
157
158                 /*
159                  * Double-quote the name if it's not a standard identifier.
160                  * Note this is *necessary* for ruleutils.c's use.
161                  */
162                 if (strspn(name, "abcdefghijklmnopqrstuvwxyz0123456789_") != strlen(name)
163                         || isdigit((unsigned char) name[0]))
164                                 buf = psnprintf(strlen(name) + 3, "\"%s\"", name);
165                 else
166                         buf = pstrdup(name);
167
168                 ReleaseSysCache(tuple);
169
170                 return buf;
171         }
172
173         if (array_base_type != InvalidOid && typlen < 0)
174         {
175                 /* Switch our attention to the array element type */
176                 ReleaseSysCache(tuple);
177                 tuple = SearchSysCache(TYPEOID,
178                                                            ObjectIdGetDatum(array_base_type),
179                                                            0, 0, 0);
180                 if (!HeapTupleIsValid(tuple))
181                 {
182                         if (allow_invalid)
183                                 return pstrdup("???[]");
184                         else
185                                 elog(ERROR, "could not locate data type with oid %u in catalog",
186                                          type_oid);
187                 }
188                 is_array = true;
189                 type_oid = array_base_type;
190         }
191         else
192                 is_array = false;
193
194         switch (type_oid)
195         {
196                 case BITOID:
197                         if (with_typemod)
198                                 buf = psnprintf(5 + MAX_INT32_LEN + 1, "bit(%d)",
199                                                                 (int) typemod);
200                         else if (typemod_given)
201                         {
202                                 /*
203                                  * bit with typmod -1 is not the same as BIT, which means
204                                  * BIT(1) per SQL spec.  Report it as the quoted typename
205                                  * so that parser will not assign a bogus typmod.
206                                  */
207                                 buf = pstrdup("\"bit\"");
208                         }
209                         else
210                                 buf = pstrdup("bit");
211                         break;
212
213                 case BOOLOID:
214                         buf = pstrdup("boolean");
215                         break;
216
217                 case BPCHAROID:
218                         if (with_typemod)
219                                 buf = psnprintf(11 + MAX_INT32_LEN + 1, "character(%d)",
220                                                                 (int) (typemod - VARHDRSZ));
221                         else if (typemod_given)
222                         {
223                                 /*
224                                  * bpchar with typmod -1 is not the same as CHARACTER,
225                                  * which means CHARACTER(1) per SQL spec.  Report it as
226                                  * bpchar so that parser will not assign a bogus typmod.
227                                  */
228                                 buf = pstrdup("bpchar");
229                         }
230                         else
231                                 buf = pstrdup("character");
232                         break;
233
234                 case CHAROID:
235                         /*
236                          * This char type is the single-byte version. You have to
237                          * double-quote it to get at it in the parser.
238                          */
239                         buf = pstrdup("\"char\"");
240                         break;
241
242                 case FLOAT4OID:
243                         buf = pstrdup("real");
244                         break;
245
246                 case FLOAT8OID:
247                         buf = pstrdup("double precision");
248                         break;
249
250                 case INT2OID:
251                         buf = pstrdup("smallint");
252                         break;
253
254                 case INT4OID:
255                         buf = pstrdup("integer");
256                         break;
257
258                 case INT8OID:
259                         buf = pstrdup("bigint");
260                         break;
261
262                 case NUMERICOID:
263                         if (with_typemod)
264                                 buf = psnprintf(10 + 2 * MAX_INT32_LEN + 1, "numeric(%d,%d)",
265                                                                 ((typemod - VARHDRSZ) >> 16) & 0xffff,
266                                                                 (typemod - VARHDRSZ) & 0xffff);
267                         else
268                                 buf = pstrdup("numeric");
269                         break;
270
271                 case INTERVALOID:
272                         if (with_typemod)
273                         {
274                                 int                     fields = typemod >> 16;
275                                 int                     precision = typemod & 0xFFFF;
276                                 const char *fieldstr;
277
278                                 switch (fields)
279                                 {
280                                         case MASK(YEAR):
281                                                 fieldstr = " year";
282                                                 break;
283                                         case MASK(MONTH):
284                                                 fieldstr = " month";
285                                                 break;
286                                         case MASK(DAY):
287                                                 fieldstr = " day";
288                                                 break;
289                                         case MASK(HOUR):
290                                                 fieldstr = " hour";
291                                                 break;
292                                         case MASK(MINUTE):
293                                                 fieldstr = " minute";
294                                                 break;
295                                         case MASK(SECOND):
296                                                 fieldstr = " second";
297                                                 break;
298                                         case MASK(YEAR) | MASK(MONTH):
299                                                 fieldstr = " year to month";
300                                                 break;
301                                         case MASK(DAY) | MASK(HOUR):
302                                                 fieldstr = " day to hour";
303                                                 break;
304                                         case MASK(DAY) | MASK(HOUR) | MASK(MINUTE):
305                                                 fieldstr = " day to minute";
306                                                 break;
307                                         case MASK(DAY) | MASK(HOUR) | MASK(MINUTE) | MASK(SECOND):
308                                                 fieldstr = " day to second";
309                                                 break;
310                                         case MASK(HOUR) | MASK(MINUTE):
311                                                 fieldstr = " hour to minute";
312                                                 break;
313                                         case MASK(HOUR) | MASK(MINUTE) | MASK(SECOND):
314                                                 fieldstr = " hour to second";
315                                                 break;
316                                         case MASK(MINUTE) | MASK(SECOND):
317                                                 fieldstr = " minute to second";
318                                                 break;
319                                         case 0x7FFF:
320                                                 fieldstr = "";
321                                                 break;
322                                         default:
323                                                 elog(LOG, "Invalid INTERVAL typmod 0x%x", typemod);
324                                                 fieldstr = "";
325                                                 break;
326                                 }
327                                 if (precision != 0xFFFF)
328                                         buf = psnprintf(100, "interval(%d)%s",
329                                                                         precision, fieldstr);
330                                 else
331                                         buf = psnprintf(100, "interval%s",
332                                                                         fieldstr);
333                         }
334                         else
335                                 buf = pstrdup("interval");
336                         break;
337
338                 case TIMEOID:
339                         if (with_typemod)
340                                 buf = psnprintf(50, "time(%d) without time zone",
341                                                                 typemod);
342                         else
343                                 buf = pstrdup("time without time zone");
344                         break;
345
346                 case TIMETZOID:
347                         if (with_typemod)
348                                 buf = psnprintf(50, "time(%d) with time zone",
349                                                                 typemod);
350                         else
351                                 buf = pstrdup("time with time zone");
352                         break;
353
354                 case TIMESTAMPOID:
355                         if (with_typemod)
356                                 buf = psnprintf(50, "timestamp(%d) without time zone",
357                                                                 typemod);
358                         else
359                                 buf = pstrdup("timestamp without time zone");
360                         break;
361
362                 case TIMESTAMPTZOID:
363                         if (with_typemod)
364                                 buf = psnprintf(50, "timestamp(%d) with time zone",
365                                                                 typemod);
366                         else
367                                 buf = pstrdup("timestamp with time zone");
368                         break;
369
370                 case VARBITOID:
371                         if (with_typemod)
372                                 buf = psnprintf(13 + MAX_INT32_LEN + 1, "bit varying(%d)",
373                                                                 (int) typemod);
374                         else
375                                 buf = pstrdup("bit varying");
376                         break;
377
378                 case VARCHAROID:
379                         if (with_typemod)
380                                 buf = psnprintf(19 + MAX_INT32_LEN + 1,
381                                                                 "character varying(%d)",
382                                                                 (int) (typemod - VARHDRSZ));
383                         else
384                                 buf = pstrdup("character varying");
385                         break;
386
387                 default:
388                         name = NameStr(((Form_pg_type) GETSTRUCT(tuple))->typname);
389                         /*
390                          * Double-quote the name if it's not a standard identifier.
391                          * Note this is *necessary* for ruleutils.c's use.
392                          */
393                         if (strspn(name, "abcdefghijklmnopqrstuvwxyz0123456789_") != strlen(name)
394                                 || isdigit((unsigned char) name[0]))
395                                 buf = psnprintf(strlen(name) + 3, "\"%s\"", name);
396                         else
397                                 buf = pstrdup(name);
398                         break;
399         }
400
401         if (is_array)
402                 buf = psnprintf(strlen(buf) + 3, "%s[]", buf);
403
404         ReleaseSysCache(tuple);
405
406         return buf;
407 }
408
409
410 /*
411  * type_maximum_size --- determine maximum width of a varlena column
412  *
413  * If the max width is indeterminate, return -1.  In particular, we return
414  * -1 for any type not known to this routine.  We assume the caller has
415  * already determined that the type is a varlena type, so it's not
416  * necessary to look up the type's pg_type tuple here.
417  *
418  * This may appear unrelated to format_type(), but in fact the two routines
419  * share knowledge of the encoding of typmod for different types, so it's
420  * convenient to keep them together.
421  */
422 int32
423 type_maximum_size(Oid type_oid, int32 typemod)
424 {
425         if (typemod < 0)
426                 return -1;
427
428         switch (type_oid)
429         {
430                 case BPCHAROID:
431                 case VARCHAROID:
432                         /* typemod includes varlena header */
433 #ifdef MULTIBYTE
434                         /* typemod is in characters not bytes */
435                         return (typemod - VARHDRSZ) *
436                                 pg_encoding_max_length(GetDatabaseEncoding())
437                                 + VARHDRSZ;
438 #else
439                         return typemod;
440 #endif
441
442                 case NUMERICOID:
443                         /* precision (ie, max # of digits) is in upper bits of typmod */
444                         if (typemod > VARHDRSZ)
445                         {
446                                 int                     precision = ((typemod - VARHDRSZ) >> 16) & 0xffff;
447
448                                 /* Numeric stores 2 decimal digits/byte, plus header */
449                                 return (precision + 1) / 2 + NUMERIC_HDRSZ;
450                         }
451                         break;
452
453                 case VARBITOID:
454                 case BITOID:
455                         /* typemod is the (max) number of bits */
456                         return (typemod + (BITS_PER_BYTE - 1)) / BITS_PER_BYTE
457                                 + 2 * sizeof(int32);
458         }
459
460         /* Unknown type, or unlimited-width type such as 'text' */
461         return -1;
462 }
463
464
465 /*
466  * oidvectortypes                       - converts a vector of type OIDs to "typname" list
467  *
468  * The interface for this function is wrong: it should be told how many
469  * OIDs are significant in the input vector, so that trailing InvalidOid
470  * argument types can be recognized.
471  */
472 Datum
473 oidvectortypes(PG_FUNCTION_ARGS)
474 {
475         Oid                *oidArray = (Oid *) PG_GETARG_POINTER(0);
476         char       *result;
477         int                     numargs;
478         int                     num;
479         size_t          total;
480         size_t          left;
481
482         /* Try to guess how many args there are :-( */
483         numargs = 0;
484         for (num = 0; num < FUNC_MAX_ARGS; num++)
485         {
486                 if (oidArray[num] != InvalidOid)
487                         numargs = num + 1;
488         }
489
490         total = 20 * numargs + 1;
491         result = palloc(total);
492         result[0] = '\0';
493         left = total - 1;
494
495         for (num = 0; num < numargs; num++)
496         {
497                 char       *typename = format_type_internal(oidArray[num], -1,
498                                                                                                         false, true);
499                 size_t          slen = strlen(typename);
500
501                 if (left < (slen + 2))
502                 {
503                         total += slen + 2;
504                         result = repalloc(result, total);
505                         left += slen + 2;
506                 }
507
508                 if (num > 0)
509                 {
510                         strcat(result, ", ");
511                         left -= 2;
512                 }
513                 strcat(result, typename);
514                 left -= slen;
515         }
516
517         PG_RETURN_DATUM(_textin(result));
518 }
519
520
521 /* snprintf into a palloc'd string */
522 static char *
523 psnprintf(size_t len, const char *fmt, ...)
524 {
525         va_list         ap;
526         char       *buf;
527
528         buf = palloc(len);
529
530         va_start(ap, fmt);
531         vsnprintf(buf, len, fmt, ap);
532         va_end(ap);
533
534         return buf;
535 }