]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/format_type.c
c85d7ea08842cfab8c9a13d6133a43024eaf77b6
[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.28 2002/03/20 19:44:40 tgl 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         char            typtype;
127         bool            is_array;
128         char       *name;
129         char       *buf;
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         /*
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.
150          */
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;
154
155         if (array_base_type != InvalidOid && typlen < 0 && typtype != 'd')
156         {
157                 /* Switch our attention to the array element type */
158                 ReleaseSysCache(tuple);
159                 tuple = SearchSysCache(TYPEOID,
160                                                            ObjectIdGetDatum(array_base_type),
161                                                            0, 0, 0);
162                 if (!HeapTupleIsValid(tuple))
163                 {
164                         if (allow_invalid)
165                                 return pstrdup("???[]");
166                         else
167                                 elog(ERROR, "could not locate data type with oid %u in catalog",
168                                          type_oid);
169                 }
170                 is_array = true;
171                 type_oid = array_base_type;
172         }
173         else
174                 is_array = false;
175
176         switch (type_oid)
177         {
178                 case BITOID:
179                         if (with_typemod)
180                                 buf = psnprintf(5 + MAX_INT32_LEN + 1, "bit(%d)",
181                                                                 (int) typemod);
182                         else if (typemod_given)
183                         {
184                                 /*
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.
188                                  */
189                                 buf = pstrdup("\"bit\"");
190                         }
191                         else
192                                 buf = pstrdup("bit");
193                         break;
194
195                 case BOOLOID:
196                         buf = pstrdup("boolean");
197                         break;
198
199                 case BPCHAROID:
200                         if (with_typemod)
201                                 buf = psnprintf(11 + MAX_INT32_LEN + 1, "character(%d)",
202                                                                 (int) (typemod - VARHDRSZ));
203                         else if (typemod_given)
204                         {
205                                 /*
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.
209                                  */
210                                 buf = pstrdup("bpchar");
211                         }
212                         else
213                                 buf = pstrdup("character");
214                         break;
215
216                 case CHAROID:
217                         /*
218                          * This char type is the single-byte version. You have to
219                          * double-quote it to get at it in the parser.
220                          */
221                         buf = pstrdup("\"char\"");
222                         break;
223
224                 case FLOAT4OID:
225                         buf = pstrdup("real");
226                         break;
227
228                 case FLOAT8OID:
229                         buf = pstrdup("double precision");
230                         break;
231
232                 case INT2OID:
233                         buf = pstrdup("smallint");
234                         break;
235
236                 case INT4OID:
237                         buf = pstrdup("integer");
238                         break;
239
240                 case INT8OID:
241                         buf = pstrdup("bigint");
242                         break;
243
244                 case NUMERICOID:
245                         if (with_typemod)
246                                 buf = psnprintf(10 + 2 * MAX_INT32_LEN + 1, "numeric(%d,%d)",
247                                                                 ((typemod - VARHDRSZ) >> 16) & 0xffff,
248                                                                 (typemod - VARHDRSZ) & 0xffff);
249                         else
250                                 buf = pstrdup("numeric");
251                         break;
252
253                 case INTERVALOID:
254                         if (with_typemod)
255                         {
256                                 int                     fields = typemod >> 16;
257                                 int                     precision = typemod & 0xFFFF;
258                                 const char *fieldstr;
259
260                                 switch (fields)
261                                 {
262                                         case MASK(YEAR):
263                                                 fieldstr = " year";
264                                                 break;
265                                         case MASK(MONTH):
266                                                 fieldstr = " month";
267                                                 break;
268                                         case MASK(DAY):
269                                                 fieldstr = " day";
270                                                 break;
271                                         case MASK(HOUR):
272                                                 fieldstr = " hour";
273                                                 break;
274                                         case MASK(MINUTE):
275                                                 fieldstr = " minute";
276                                                 break;
277                                         case MASK(SECOND):
278                                                 fieldstr = " second";
279                                                 break;
280                                         case MASK(YEAR) | MASK(MONTH):
281                                                 fieldstr = " year to month";
282                                                 break;
283                                         case MASK(DAY) | MASK(HOUR):
284                                                 fieldstr = " day to hour";
285                                                 break;
286                                         case MASK(DAY) | MASK(HOUR) | MASK(MINUTE):
287                                                 fieldstr = " day to minute";
288                                                 break;
289                                         case MASK(DAY) | MASK(HOUR) | MASK(MINUTE) | MASK(SECOND):
290                                                 fieldstr = " day to second";
291                                                 break;
292                                         case MASK(HOUR) | MASK(MINUTE):
293                                                 fieldstr = " hour to minute";
294                                                 break;
295                                         case MASK(HOUR) | MASK(MINUTE) | MASK(SECOND):
296                                                 fieldstr = " hour to second";
297                                                 break;
298                                         case MASK(MINUTE) | MASK(SECOND):
299                                                 fieldstr = " minute to second";
300                                                 break;
301                                         case 0x7FFF:
302                                                 fieldstr = "";
303                                                 break;
304                                         default:
305                                                 elog(LOG, "Invalid INTERVAL typmod 0x%x", typemod);
306                                                 fieldstr = "";
307                                                 break;
308                                 }
309                                 if (precision != 0xFFFF)
310                                         buf = psnprintf(100, "interval(%d)%s",
311                                                                         precision, fieldstr);
312                                 else
313                                         buf = psnprintf(100, "interval%s",
314                                                                         fieldstr);
315                         }
316                         else
317                                 buf = pstrdup("interval");
318                         break;
319
320                 case TIMEOID:
321                         if (with_typemod)
322                                 buf = psnprintf(50, "time(%d) without time zone",
323                                                                 typemod);
324                         else
325                                 buf = pstrdup("time without time zone");
326                         break;
327
328                 case TIMETZOID:
329                         if (with_typemod)
330                                 buf = psnprintf(50, "time(%d) with time zone",
331                                                                 typemod);
332                         else
333                                 buf = pstrdup("time with time zone");
334                         break;
335
336                 case TIMESTAMPOID:
337                         if (with_typemod)
338                                 buf = psnprintf(50, "timestamp(%d) without time zone",
339                                                                 typemod);
340                         else
341                                 buf = pstrdup("timestamp without time zone");
342                         break;
343
344                 case TIMESTAMPTZOID:
345                         if (with_typemod)
346                                 buf = psnprintf(50, "timestamp(%d) with time zone",
347                                                                 typemod);
348                         else
349                                 buf = pstrdup("timestamp with time zone");
350                         break;
351
352                 case VARBITOID:
353                         if (with_typemod)
354                                 buf = psnprintf(13 + MAX_INT32_LEN + 1, "bit varying(%d)",
355                                                                 (int) typemod);
356                         else
357                                 buf = pstrdup("bit varying");
358                         break;
359
360                 case VARCHAROID:
361                         if (with_typemod)
362                                 buf = psnprintf(19 + MAX_INT32_LEN + 1,
363                                                                 "character varying(%d)",
364                                                                 (int) (typemod - VARHDRSZ));
365                         else
366                                 buf = pstrdup("character varying");
367                         break;
368
369                 default:
370                         name = NameStr(((Form_pg_type) GETSTRUCT(tuple))->typname);
371                         /*
372                          * Double-quote the name if it's not a standard identifier.
373                          * Note this is *necessary* for ruleutils.c's use.
374                          */
375                         if (strspn(name, "abcdefghijklmnopqrstuvwxyz0123456789_") != strlen(name)
376                                 || isdigit((unsigned char) name[0]))
377                                 buf = psnprintf(strlen(name) + 3, "\"%s\"", name);
378                         else
379                                 buf = pstrdup(name);
380                         break;
381         }
382
383         if (is_array)
384                 buf = psnprintf(strlen(buf) + 3, "%s[]", buf);
385
386         ReleaseSysCache(tuple);
387
388         return buf;
389 }
390
391
392 /*
393  * type_maximum_size --- determine maximum width of a varlena column
394  *
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.
399  *
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.
403  */
404 int32
405 type_maximum_size(Oid type_oid, int32 typemod)
406 {
407         if (typemod < 0)
408                 return -1;
409
410         switch (type_oid)
411         {
412                 case BPCHAROID:
413                 case VARCHAROID:
414                         /* typemod includes varlena header */
415 #ifdef MULTIBYTE
416                         /* typemod is in characters not bytes */
417                         return (typemod - VARHDRSZ) *
418                                 pg_encoding_max_length(GetDatabaseEncoding())
419                                 + VARHDRSZ;
420 #else
421                         return typemod;
422 #endif
423
424                 case NUMERICOID:
425                         /* precision (ie, max # of digits) is in upper bits of typmod */
426                         if (typemod > VARHDRSZ)
427                         {
428                                 int                     precision = ((typemod - VARHDRSZ) >> 16) & 0xffff;
429
430                                 /* Numeric stores 2 decimal digits/byte, plus header */
431                                 return (precision + 1) / 2 + NUMERIC_HDRSZ;
432                         }
433                         break;
434
435                 case VARBITOID:
436                 case BITOID:
437                         /* typemod is the (max) number of bits */
438                         return (typemod + (BITS_PER_BYTE - 1)) / BITS_PER_BYTE
439                                 + 2 * sizeof(int32);
440         }
441
442         /* Unknown type, or unlimited-width type such as 'text' */
443         return -1;
444 }
445
446
447 /*
448  * oidvectortypes                       - converts a vector of type OIDs to "typname" list
449  *
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.
453  */
454 Datum
455 oidvectortypes(PG_FUNCTION_ARGS)
456 {
457         Oid                *oidArray = (Oid *) PG_GETARG_POINTER(0);
458         char       *result;
459         int                     numargs;
460         int                     num;
461         size_t          total;
462         size_t          left;
463
464         /* Try to guess how many args there are :-( */
465         numargs = 0;
466         for (num = 0; num < FUNC_MAX_ARGS; num++)
467         {
468                 if (oidArray[num] != InvalidOid)
469                         numargs = num + 1;
470         }
471
472         total = 20 * numargs + 1;
473         result = palloc(total);
474         result[0] = '\0';
475         left = total - 1;
476
477         for (num = 0; num < numargs; num++)
478         {
479                 char       *typename = format_type_internal(oidArray[num], -1,
480                                                                                                         false, true);
481                 size_t          slen = strlen(typename);
482
483                 if (left < (slen + 2))
484                 {
485                         total += slen + 2;
486                         result = repalloc(result, total);
487                         left += slen + 2;
488                 }
489
490                 if (num > 0)
491                 {
492                         strcat(result, ", ");
493                         left -= 2;
494                 }
495                 strcat(result, typename);
496                 left -= slen;
497         }
498
499         PG_RETURN_DATUM(_textin(result));
500 }
501
502
503 /* snprintf into a palloc'd string */
504 static char *
505 psnprintf(size_t len, const char *fmt, ...)
506 {
507         va_list         ap;
508         char       *buf;
509
510         buf = palloc(len);
511
512         va_start(ap, fmt);
513         vsnprintf(buf, len, fmt, ap);
514         va_end(ap);
515
516         return buf;
517 }