Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * parse_type.c
4 : : * handle type operations for parser
5 : : *
6 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/parser/parse_type.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include "access/htup_details.h"
18 : : #include "catalog/namespace.h"
19 : : #include "catalog/pg_type.h"
20 : : #include "lib/stringinfo.h"
21 : : #include "nodes/makefuncs.h"
22 : : #include "parser/parse_type.h"
23 : : #include "parser/parser.h"
24 : : #include "utils/array.h"
25 : : #include "utils/builtins.h"
26 : : #include "utils/lsyscache.h"
27 : : #include "utils/syscache.h"
28 : :
29 : : static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName,
30 : : Type typ);
31 : :
32 : :
33 : : /*
34 : : * LookupTypeName
35 : : * Wrapper for typical case.
36 : : */
37 : : Type
1714 noah@leadboat.com 38 :CBC 316185 : LookupTypeName(ParseState *pstate, const TypeName *typeName,
39 : : int32 *typmod_p, bool missing_ok)
40 : : {
41 : 316185 : return LookupTypeNameExtended(pstate,
42 : : typeName, typmod_p, true, missing_ok);
43 : : }
44 : :
45 : : /*
46 : : * LookupTypeNameExtended
47 : : * Given a TypeName object, lookup the pg_type syscache entry of the type.
48 : : * Returns NULL if no such type can be found. If the type is found,
49 : : * the typmod value represented in the TypeName struct is computed and
50 : : * stored into *typmod_p.
51 : : *
52 : : * NB: on success, the caller must ReleaseSysCache the type tuple when done
53 : : * with it.
54 : : *
55 : : * NB: direct callers of this function MUST check typisdefined before assuming
56 : : * that the type is fully valid. Most code should go through typenameType
57 : : * or typenameTypeId instead.
58 : : *
59 : : * typmod_p can be passed as NULL if the caller does not care to know the
60 : : * typmod value, but the typmod decoration (if any) will be validated anyway,
61 : : * except in the case where the type is not found. Note that if the type is
62 : : * found but is a shell, and there is typmod decoration, an error will be
63 : : * thrown --- this is intentional.
64 : : *
65 : : * If temp_ok is false, ignore types in the temporary namespace. Pass false
66 : : * when the caller will decide, using goodness of fit criteria, whether the
67 : : * typeName is actually a type or something else. If typeName always denotes
68 : : * a type (or denotes nothing), pass true.
69 : : *
70 : : * pstate is only used for error location info, and may be NULL.
71 : : */
72 : : Type
73 : 343378 : LookupTypeNameExtended(ParseState *pstate,
74 : : const TypeName *typeName, int32 *typmod_p,
75 : : bool temp_ok, bool missing_ok)
76 : : {
77 : : Oid typoid;
78 : : HeapTuple tup;
79 : : int32 typmod;
80 : :
5386 peter_e@gmx.net 81 [ + + ]: 343378 : if (typeName->names == NIL)
82 : : {
83 : : /* We have the OID already if it's an internally generated TypeName */
84 : 77047 : typoid = typeName->typeOid;
85 : : }
86 [ + + ]: 266331 : else if (typeName->pct_type)
87 : : {
88 : : /* Handle %TYPE reference to type of an existing field */
89 : 12 : RangeVar *rel = makeRangeVar(NULL, NULL, typeName->location);
8052 tgl@sss.pgh.pa.us 90 : 12 : char *field = NULL;
91 : : Oid relid;
92 : : AttrNumber attnum;
93 : :
94 : : /* deconstruct the name list */
5386 peter_e@gmx.net 95 [ - + + - : 12 : switch (list_length(typeName->names))
- ]
96 : : {
8052 tgl@sss.pgh.pa.us 97 :UBC 0 : case 1:
7575 98 [ # # ]: 0 : ereport(ERROR,
99 : : (errcode(ERRCODE_SYNTAX_ERROR),
100 : : errmsg("improper %%TYPE reference (too few dotted names): %s",
101 : : NameListToString(typeName->names)),
102 : : parser_errposition(pstate, typeName->location)));
103 : : break;
8052 tgl@sss.pgh.pa.us 104 :CBC 9 : case 2:
5386 peter_e@gmx.net 105 : 9 : rel->relname = strVal(linitial(typeName->names));
106 : 9 : field = strVal(lsecond(typeName->names));
8052 tgl@sss.pgh.pa.us 107 : 9 : break;
8052 tgl@sss.pgh.pa.us 108 :GBC 3 : case 3:
5386 peter_e@gmx.net 109 : 3 : rel->schemaname = strVal(linitial(typeName->names));
110 : 3 : rel->relname = strVal(lsecond(typeName->names));
111 : 3 : field = strVal(lthird(typeName->names));
8052 tgl@sss.pgh.pa.us 112 : 3 : break;
8052 tgl@sss.pgh.pa.us 113 :UBC 0 : case 4:
5386 peter_e@gmx.net 114 : 0 : rel->catalogname = strVal(linitial(typeName->names));
115 : 0 : rel->schemaname = strVal(lsecond(typeName->names));
116 : 0 : rel->relname = strVal(lthird(typeName->names));
117 : 0 : field = strVal(lfourth(typeName->names));
8052 tgl@sss.pgh.pa.us 118 : 0 : break;
119 : 0 : default:
7575 120 [ # # ]: 0 : ereport(ERROR,
121 : : (errcode(ERRCODE_SYNTAX_ERROR),
122 : : errmsg("improper %%TYPE reference (too many dotted names): %s",
123 : : NameListToString(typeName->names)),
124 : : parser_errposition(pstate, typeName->location)));
125 : : break;
126 : : }
127 : :
128 : : /*
129 : : * Look up the field.
130 : : *
131 : : * XXX: As no lock is taken here, this might fail in the presence of
132 : : * concurrent DDL. But taking a lock would carry a performance
133 : : * penalty and would also require a permissions check.
134 : : */
3734 alvherre@alvh.no-ip. 135 :CBC 12 : relid = RangeVarGetRelid(rel, NoLock, missing_ok);
8052 tgl@sss.pgh.pa.us 136 : 12 : attnum = get_attnum(relid, field);
137 [ - + ]: 12 : if (attnum == InvalidAttrNumber)
138 : : {
3734 alvherre@alvh.no-ip. 139 [ # # ]:UBC 0 : if (missing_ok)
140 : 0 : typoid = InvalidOid;
141 : : else
142 [ # # ]: 0 : ereport(ERROR,
143 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
144 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
145 : : field, rel->relname),
146 : : parser_errposition(pstate, typeName->location)));
147 : : }
148 : : else
149 : : {
3734 alvherre@alvh.no-ip. 150 :CBC 12 : typoid = get_atttype(relid, attnum);
151 : :
152 : : /* this construct should never have an array indicator */
153 [ - + ]: 12 : Assert(typeName->arrayBounds == NIL);
154 : :
155 : : /* emit nuisance notice (intentionally not errposition'd) */
156 [ + - ]: 12 : ereport(NOTICE,
157 : : (errmsg("type reference %s converted to %s",
158 : : TypeNameToString(typeName),
159 : : format_type_be(typoid))));
160 : : }
161 : : }
162 : : else
163 : : {
164 : : /* Normal reference to a type name */
165 : : char *schemaname;
166 : : char *typname;
167 : :
168 : : /* deconstruct the name list */
5386 peter_e@gmx.net 169 : 266319 : DeconstructQualifiedName(typeName->names, &schemaname, &typname);
170 : :
8052 tgl@sss.pgh.pa.us 171 [ + + ]: 266313 : if (schemaname)
172 : : {
173 : : /* Look in specific schema only */
174 : : Oid namespaceId;
175 : : ParseCallbackState pcbstate;
176 : :
3315 alvherre@alvh.no-ip. 177 : 111757 : setup_parser_errposition_callback(&pcbstate, pstate, typeName->location);
178 : :
3734 179 : 111757 : namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
180 [ + + ]: 111754 : if (OidIsValid(namespaceId))
1972 andres@anarazel.de 181 : 111706 : typoid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
182 : : PointerGetDatum(typname),
183 : : ObjectIdGetDatum(namespaceId));
184 : : else
3734 alvherre@alvh.no-ip. 185 : 48 : typoid = InvalidOid;
186 : :
3315 187 : 111754 : cancel_parser_errposition_callback(&pcbstate);
188 : : }
189 : : else
190 : : {
191 : : /* Unqualified type name, so search the search path */
1714 noah@leadboat.com 192 : 154556 : typoid = TypenameGetTypidExtended(typname, temp_ok);
193 : : }
194 : :
195 : : /* If an array reference, return the array type instead */
5386 peter_e@gmx.net 196 [ + + ]: 266310 : if (typeName->arrayBounds != NIL)
5999 tgl@sss.pgh.pa.us 197 : 6096 : typoid = get_array_type(typoid);
198 : : }
199 : :
200 [ + + ]: 343369 : if (!OidIsValid(typoid))
201 : : {
202 [ + + ]: 27075 : if (typmod_p)
203 : 22 : *typmod_p = -1;
204 : 27075 : return NULL;
205 : : }
206 : :
5173 rhaas@postgresql.org 207 : 316294 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typoid));
5999 tgl@sss.pgh.pa.us 208 [ - + ]: 316294 : if (!HeapTupleIsValid(tup)) /* should not happen */
5999 tgl@sss.pgh.pa.us 209 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", typoid);
210 : :
5386 peter_e@gmx.net 211 :CBC 316294 : typmod = typenameTypeMod(pstate, typeName, (Type) tup);
212 : :
5999 tgl@sss.pgh.pa.us 213 [ + + ]: 316288 : if (typmod_p)
214 : 236615 : *typmod_p = typmod;
215 : :
216 : 316288 : return (Type) tup;
217 : : }
218 : :
219 : : /*
220 : : * LookupTypeNameOid
221 : : * Given a TypeName object, lookup the pg_type syscache entry of the type.
222 : : * Returns InvalidOid if no such type can be found. If the type is found,
223 : : * return its Oid.
224 : : *
225 : : * NB: direct callers of this function need to be aware that the type OID
226 : : * returned may correspond to a shell type. Most code should go through
227 : : * typenameTypeId instead.
228 : : *
229 : : * pstate is only used for error location info, and may be NULL.
230 : : */
231 : : Oid
3734 alvherre@alvh.no-ip. 232 : 10338 : LookupTypeNameOid(ParseState *pstate, const TypeName *typeName, bool missing_ok)
233 : : {
234 : : Oid typoid;
235 : : Type tup;
236 : :
237 : 10338 : tup = LookupTypeName(pstate, typeName, NULL, missing_ok);
238 [ + + ]: 10338 : if (tup == NULL)
239 : : {
240 [ + + ]: 90 : if (!missing_ok)
241 [ + - ]: 16 : ereport(ERROR,
242 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
243 : : errmsg("type \"%s\" does not exist",
244 : : TypeNameToString(typeName)),
245 : : parser_errposition(pstate, typeName->location)));
246 : :
247 : 74 : return InvalidOid;
248 : : }
249 : :
1972 andres@anarazel.de 250 : 10248 : typoid = ((Form_pg_type) GETSTRUCT(tup))->oid;
3734 alvherre@alvh.no-ip. 251 : 10248 : ReleaseSysCache(tup);
252 : :
253 : 10248 : return typoid;
254 : : }
255 : :
256 : : /*
257 : : * typenameType - given a TypeName, return a Type structure and typmod
258 : : *
259 : : * This is equivalent to LookupTypeName, except that this will report
260 : : * a suitable error message if the type cannot be found or is not defined.
261 : : * Callers of this can therefore assume the result is a fully valid type.
262 : : */
263 : : Type
4785 tgl@sss.pgh.pa.us 264 : 271438 : typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p)
265 : : {
266 : : Type tup;
267 : :
3734 alvherre@alvh.no-ip. 268 : 271438 : tup = LookupTypeName(pstate, typeName, typmod_p, false);
5999 tgl@sss.pgh.pa.us 269 [ + + ]: 271435 : if (tup == NULL)
7575 270 [ + - ]: 16 : ereport(ERROR,
271 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
272 : : errmsg("type \"%s\" does not exist",
273 : : TypeNameToString(typeName)),
274 : : parser_errposition(pstate, typeName->location)));
5999 275 [ + + ]: 271419 : if (!((Form_pg_type) GETSTRUCT(tup))->typisdefined)
7575 276 [ + - ]: 3 : ereport(ERROR,
277 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
278 : : errmsg("type \"%s\" is only a shell",
279 : : TypeNameToString(typeName)),
280 : : parser_errposition(pstate, typeName->location)));
5999 281 : 271416 : return tup;
282 : : }
283 : :
284 : : /*
285 : : * typenameTypeId - given a TypeName, return the type's OID
286 : : *
287 : : * This is similar to typenameType, but we only hand back the type OID
288 : : * not the syscache entry.
289 : : */
290 : : Oid
4920 peter_e@gmx.net 291 : 5138 : typenameTypeId(ParseState *pstate, const TypeName *typeName)
292 : : {
293 : : Oid typoid;
294 : : Type tup;
295 : :
4785 tgl@sss.pgh.pa.us 296 : 5138 : tup = typenameType(pstate, typeName, NULL);
1972 andres@anarazel.de 297 : 5131 : typoid = ((Form_pg_type) GETSTRUCT(tup))->oid;
5999 tgl@sss.pgh.pa.us 298 : 5131 : ReleaseSysCache(tup);
299 : :
8052 300 : 5131 : return typoid;
301 : : }
302 : :
303 : : /*
304 : : * typenameTypeIdAndMod - given a TypeName, return the type's OID and typmod
305 : : *
306 : : * This is equivalent to typenameType, but we only hand back the type OID
307 : : * and typmod, not the syscache entry.
308 : : */
309 : : void
4920 peter_e@gmx.net 310 : 233941 : typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName,
311 : : Oid *typeid_p, int32 *typmod_p)
312 : : {
313 : : Type tup;
314 : :
4785 tgl@sss.pgh.pa.us 315 : 233941 : tup = typenameType(pstate, typeName, typmod_p);
1972 andres@anarazel.de 316 : 233939 : *typeid_p = ((Form_pg_type) GETSTRUCT(tup))->oid;
4920 peter_e@gmx.net 317 : 233939 : ReleaseSysCache(tup);
318 : 233939 : }
319 : :
320 : : /*
321 : : * typenameTypeMod - given a TypeName, return the internal typmod value
322 : : *
323 : : * This will throw an error if the TypeName includes type modifiers that are
324 : : * illegal for the data type.
325 : : *
326 : : * The actual type OID represented by the TypeName must already have been
327 : : * looked up, and is passed as "typ".
328 : : *
329 : : * pstate is only used for error location info, and may be NULL.
330 : : */
331 : : static int32
5386 332 : 316294 : typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ)
333 : : {
334 : : int32 result;
335 : : Oid typmodin;
336 : : Datum *datums;
337 : : int n;
338 : : ListCell *l;
339 : : ArrayType *arrtypmod;
340 : : ParseCallbackState pcbstate;
341 : :
342 : : /* Return prespecified typmod if no typmod expressions */
343 [ + + ]: 316294 : if (typeName->typmods == NIL)
344 : 312305 : return typeName->typemod;
345 : :
346 : : /*
347 : : * Else, type had better accept typmods. We give a special error message
348 : : * for the shell-type case, since a shell couldn't possibly have a
349 : : * typmodin function.
350 : : */
5999 tgl@sss.pgh.pa.us 351 [ - + ]: 3989 : if (!((Form_pg_type) GETSTRUCT(typ))->typisdefined)
5999 tgl@sss.pgh.pa.us 352 [ # # ]:UBC 0 : ereport(ERROR,
353 : : (errcode(ERRCODE_SYNTAX_ERROR),
354 : : errmsg("type modifier cannot be specified for shell type \"%s\"",
355 : : TypeNameToString(typeName)),
356 : : parser_errposition(pstate, typeName->location)));
357 : :
5999 tgl@sss.pgh.pa.us 358 :CBC 3989 : typmodin = ((Form_pg_type) GETSTRUCT(typ))->typmodin;
359 : :
6315 360 [ - + ]: 3989 : if (typmodin == InvalidOid)
6315 tgl@sss.pgh.pa.us 361 [ # # ]:UBC 0 : ereport(ERROR,
362 : : (errcode(ERRCODE_SYNTAX_ERROR),
363 : : errmsg("type modifier is not allowed for type \"%s\"",
364 : : TypeNameToString(typeName)),
365 : : parser_errposition(pstate, typeName->location)));
366 : :
367 : : /*
368 : : * Convert the list of raw-grammar-output expressions to a cstring array.
369 : : * Currently, we allow simple numeric constants, string literals, and
370 : : * identifiers; possibly this list could be extended.
371 : : */
5386 peter_e@gmx.net 372 :CBC 3989 : datums = (Datum *) palloc(list_length(typeName->typmods) * sizeof(Datum));
6315 tgl@sss.pgh.pa.us 373 : 3989 : n = 0;
5386 peter_e@gmx.net 374 [ + - + + : 8943 : foreach(l, typeName->typmods)
+ + ]
375 : : {
5995 bruce@momjian.us 376 : 4954 : Node *tm = (Node *) lfirst(l);
377 : 4954 : char *cstr = NULL;
378 : :
6148 tgl@sss.pgh.pa.us 379 [ + - ]: 4954 : if (IsA(tm, A_Const))
380 : : {
5995 bruce@momjian.us 381 : 4954 : A_Const *ac = (A_Const *) tm;
382 : :
6148 tgl@sss.pgh.pa.us 383 [ + - ]: 4954 : if (IsA(&ac->val, Integer))
384 : : {
821 peter@eisentraut.org 385 : 4954 : cstr = psprintf("%ld", (long) intVal(&ac->val));
386 : : }
948 peter@eisentraut.org 387 [ # # ]:UBC 0 : else if (IsA(&ac->val, Float))
388 : : {
389 : : /* we can just use the string representation directly. */
821 390 : 0 : cstr = ac->val.fval.fval;
391 : : }
948 392 [ # # ]: 0 : else if (IsA(&ac->val, String))
393 : : {
394 : : /* we can just use the string representation directly. */
821 395 : 0 : cstr = strVal(&ac->val);
396 : : }
397 : : }
6148 tgl@sss.pgh.pa.us 398 [ # # ]: 0 : else if (IsA(tm, ColumnRef))
399 : : {
5995 bruce@momjian.us 400 : 0 : ColumnRef *cr = (ColumnRef *) tm;
401 : :
5706 tgl@sss.pgh.pa.us 402 [ # # ]: 0 : if (list_length(cr->fields) == 1 &&
403 [ # # ]: 0 : IsA(linitial(cr->fields), String))
6148 404 : 0 : cstr = strVal(linitial(cr->fields));
405 : : }
6148 tgl@sss.pgh.pa.us 406 [ - + ]:CBC 4954 : if (!cstr)
6315 tgl@sss.pgh.pa.us 407 [ # # ]:UBC 0 : ereport(ERROR,
408 : : (errcode(ERRCODE_SYNTAX_ERROR),
409 : : errmsg("type modifiers must be simple constants or identifiers"),
410 : : parser_errposition(pstate, typeName->location)));
6148 tgl@sss.pgh.pa.us 411 :CBC 4954 : datums[n++] = CStringGetDatum(cstr);
412 : : }
413 : :
653 peter@eisentraut.org 414 : 3989 : arrtypmod = construct_array_builtin(datums, n, CSTRINGOID);
415 : :
416 : : /* arrange to report location if type's typmodin function fails */
5386 peter_e@gmx.net 417 : 3989 : setup_parser_errposition_callback(&pcbstate, pstate, typeName->location);
418 : :
6315 tgl@sss.pgh.pa.us 419 : 3989 : result = DatumGetInt32(OidFunctionCall1(typmodin,
420 : : PointerGetDatum(arrtypmod)));
421 : :
5704 422 : 3983 : cancel_parser_errposition_callback(&pcbstate);
423 : :
6315 424 : 3983 : pfree(datums);
425 : 3983 : pfree(arrtypmod);
426 : :
427 : 3983 : return result;
428 : : }
429 : :
430 : : /*
431 : : * appendTypeNameToBuffer
432 : : * Append a string representing the name of a TypeName to a StringInfo.
433 : : * This is the shared guts of TypeNameToString and TypeNameListToString.
434 : : *
435 : : * NB: this must work on TypeNames that do not describe any actual type;
436 : : * it is mostly used for reporting lookup errors.
437 : : */
438 : : static void
5386 peter_e@gmx.net 439 : 2722 : appendTypeNameToBuffer(const TypeName *typeName, StringInfo string)
440 : : {
441 [ + - ]: 2722 : if (typeName->names != NIL)
442 : : {
443 : : /* Emit possibly-qualified name as-is */
444 : : ListCell *l;
445 : :
446 [ + - + + : 5549 : foreach(l, typeName->names)
+ + ]
447 : : {
448 [ + + ]: 2827 : if (l != list_head(typeName->names))
5999 tgl@sss.pgh.pa.us 449 : 105 : appendStringInfoChar(string, '.');
450 : 2827 : appendStringInfoString(string, strVal(lfirst(l)));
451 : : }
452 : : }
453 : : else
454 : : {
455 : : /* Look up internally-specified type */
5386 peter_e@gmx.net 456 :UBC 0 : appendStringInfoString(string, format_type_be(typeName->typeOid));
457 : : }
458 : :
459 : : /*
460 : : * Add decoration as needed, but only for fields considered by
461 : : * LookupTypeName
462 : : */
5386 peter_e@gmx.net 463 [ + + ]:CBC 2722 : if (typeName->pct_type)
5999 tgl@sss.pgh.pa.us 464 : 12 : appendStringInfoString(string, "%TYPE");
465 : :
5386 peter_e@gmx.net 466 [ + + ]: 2722 : if (typeName->arrayBounds != NIL)
5999 tgl@sss.pgh.pa.us 467 : 3 : appendStringInfoString(string, "[]");
468 : 2722 : }
469 : :
470 : : /*
471 : : * TypeNameToString
472 : : * Produce a string representing the name of a TypeName.
473 : : *
474 : : * NB: this must work on TypeNames that do not describe any actual type;
475 : : * it is mostly used for reporting lookup errors.
476 : : */
477 : : char *
5386 peter_e@gmx.net 478 : 2710 : TypeNameToString(const TypeName *typeName)
479 : : {
480 : : StringInfoData string;
481 : :
5999 tgl@sss.pgh.pa.us 482 : 2710 : initStringInfo(&string);
5386 peter_e@gmx.net 483 : 2710 : appendTypeNameToBuffer(typeName, &string);
5999 tgl@sss.pgh.pa.us 484 : 2710 : return string.data;
485 : : }
486 : :
487 : : /*
488 : : * TypeNameListToString
489 : : * Produce a string representing the name(s) of a List of TypeNames
490 : : */
491 : : char *
492 : 20 : TypeNameListToString(List *typenames)
493 : : {
494 : : StringInfoData string;
495 : : ListCell *l;
496 : :
497 : 20 : initStringInfo(&string);
498 [ + + + + : 32 : foreach(l, typenames)
+ + ]
499 : : {
2561 500 : 12 : TypeName *typeName = lfirst_node(TypeName, l);
501 : :
5999 502 [ + + ]: 12 : if (l != list_head(typenames))
503 : 6 : appendStringInfoChar(&string, ',');
5386 peter_e@gmx.net 504 : 12 : appendTypeNameToBuffer(typeName, &string);
505 : : }
5999 tgl@sss.pgh.pa.us 506 : 20 : return string.data;
507 : : }
508 : :
509 : : /*
510 : : * LookupCollation
511 : : *
512 : : * Look up collation by name, return OID, with support for error location.
513 : : */
514 : : Oid
4785 515 : 4265 : LookupCollation(ParseState *pstate, List *collnames, int location)
516 : : {
517 : : Oid colloid;
518 : : ParseCallbackState pcbstate;
519 : :
520 [ + + ]: 4265 : if (pstate)
521 : 4060 : setup_parser_errposition_callback(&pcbstate, pstate, location);
522 : :
523 : 4265 : colloid = get_collation_oid(collnames, false);
524 : :
525 [ + + ]: 4259 : if (pstate)
526 : 4054 : cancel_parser_errposition_callback(&pcbstate);
527 : :
528 : 4259 : return colloid;
529 : : }
530 : :
531 : : /*
532 : : * GetColumnDefCollation
533 : : *
534 : : * Get the collation to be used for a column being defined, given the
535 : : * ColumnDef node and the previously-determined column type OID.
536 : : *
537 : : * pstate is only used for error location purposes, and can be NULL.
538 : : */
539 : : Oid
80 peter@eisentraut.org 540 :GNC 109573 : GetColumnDefCollation(ParseState *pstate, const ColumnDef *coldef, Oid typeOid)
541 : : {
542 : : Oid result;
4785 tgl@sss.pgh.pa.us 543 :CBC 109573 : Oid typcollation = get_typcollation(typeOid);
3797 544 : 109573 : int location = coldef->location;
545 : :
4785 546 [ + + ]: 109573 : if (coldef->collClause)
547 : : {
548 : : /* We have a raw COLLATE clause, so look up the collation */
549 : 205 : location = coldef->collClause->location;
4783 550 : 205 : result = LookupCollation(pstate, coldef->collClause->collname,
551 : : location);
552 : : }
4785 553 [ + + ]: 109368 : else if (OidIsValid(coldef->collOid))
554 : : {
555 : : /* Precooked collation spec, use that */
556 : 35044 : result = coldef->collOid;
557 : : }
558 : : else
559 : : {
560 : : /* Use the type's default collation if any */
561 : 74324 : result = typcollation;
562 : : }
563 : :
564 : : /* Complain if COLLATE is applied to an uncollatable type */
565 [ + + - + ]: 109573 : if (OidIsValid(result) && !OidIsValid(typcollation))
4785 tgl@sss.pgh.pa.us 566 [ # # ]:UBC 0 : ereport(ERROR,
567 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
568 : : errmsg("collations are not supported by type %s",
569 : : format_type_be(typeOid)),
570 : : parser_errposition(pstate, location)));
571 : :
4785 tgl@sss.pgh.pa.us 572 :CBC 109573 : return result;
573 : : }
574 : :
575 : : /* return a Type structure, given a type id */
576 : : /* NB: caller must ReleaseSysCache the type tuple when done with it */
577 : : Type
9637 bruce@momjian.us 578 : 321734 : typeidType(Oid id)
579 : : {
580 : : HeapTuple tup;
581 : :
5173 rhaas@postgresql.org 582 : 321734 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(id));
8550 tgl@sss.pgh.pa.us 583 [ - + ]: 321734 : if (!HeapTupleIsValid(tup))
7575 tgl@sss.pgh.pa.us 584 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", id);
9357 bruce@momjian.us 585 :CBC 321734 : return (Type) tup;
586 : : }
587 : :
588 : : /* given type (as type struct), return the type OID */
589 : : Oid
9637 590 : 33005 : typeTypeId(Type tp)
591 : : {
7575 tgl@sss.pgh.pa.us 592 [ - + ]: 33005 : if (tp == NULL) /* probably useless */
9596 bruce@momjian.us 593 [ # # ]:UBC 0 : elog(ERROR, "typeTypeId() called with NULL type struct");
1972 andres@anarazel.de 594 :CBC 33005 : return ((Form_pg_type) GETSTRUCT(tp))->oid;
595 : : }
596 : :
597 : : /* given type (as type struct), return the length of type */
598 : : int16
9637 bruce@momjian.us 599 : 311701 : typeLen(Type t)
600 : : {
601 : : Form_pg_type typ;
602 : :
9357 603 : 311701 : typ = (Form_pg_type) GETSTRUCT(t);
604 : 311701 : return typ->typlen;
605 : : }
606 : :
607 : : /* given type (as type struct), return its 'byval' attribute */
608 : : bool
9637 609 : 311701 : typeByVal(Type t)
610 : : {
611 : : Form_pg_type typ;
612 : :
9357 613 : 311701 : typ = (Form_pg_type) GETSTRUCT(t);
614 : 311701 : return typ->typbyval;
615 : : }
616 : :
617 : : /* given type (as type struct), return the type's name */
618 : : char *
9637 bruce@momjian.us 619 :UBC 0 : typeTypeName(Type t)
620 : : {
621 : : Form_pg_type typ;
622 : :
9357 623 : 0 : typ = (Form_pg_type) GETSTRUCT(t);
624 : : /* pstrdup here because result may need to outlive the syscache entry */
8713 tgl@sss.pgh.pa.us 625 : 0 : return pstrdup(NameStr(typ->typname));
626 : : }
627 : :
628 : : /* given type (as type struct), return its 'typrelid' attribute */
629 : : Oid
8550 tgl@sss.pgh.pa.us 630 :CBC 333 : typeTypeRelid(Type typ)
631 : : {
632 : : Form_pg_type typtup;
633 : :
634 : 333 : typtup = (Form_pg_type) GETSTRUCT(typ);
635 : 333 : return typtup->typrelid;
636 : : }
637 : :
638 : : /* given type (as type struct), return its 'typcollation' attribute */
639 : : Oid
4755 640 : 311701 : typeTypeCollation(Type typ)
641 : : {
642 : : Form_pg_type typtup;
643 : :
644 : 311701 : typtup = (Form_pg_type) GETSTRUCT(typ);
645 : 311701 : return typtup->typcollation;
646 : : }
647 : :
648 : : /*
649 : : * Given a type structure and a string, returns the internal representation
650 : : * of that string. The "string" can be NULL to perform conversion of a NULL
651 : : * (which might result in failure, if the input function rejects NULLs).
652 : : */
653 : : Datum
9019 654 : 311701 : stringTypeDatum(Type tp, char *string, int32 atttypmod)
655 : : {
5847 656 : 311701 : Form_pg_type typform = (Form_pg_type) GETSTRUCT(tp);
657 : 311701 : Oid typinput = typform->typinput;
658 : 311701 : Oid typioparam = getTypeIOParam(tp);
659 : :
3006 660 : 311701 : return OidInputFunctionCall(typinput, string, typioparam, atttypmod);
661 : : }
662 : :
663 : : /*
664 : : * Given a typeid, return the type's typrelid (associated relation), if any.
665 : : * Returns InvalidOid if type is not a composite type.
666 : : */
667 : : Oid
9637 bruce@momjian.us 668 : 6572 : typeidTypeRelid(Oid type_id)
669 : : {
670 : : HeapTuple typeTuple;
671 : : Form_pg_type type;
672 : : Oid result;
673 : :
5173 rhaas@postgresql.org 674 : 6572 : typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id));
9637 bruce@momjian.us 675 [ - + ]: 6572 : if (!HeapTupleIsValid(typeTuple))
7575 tgl@sss.pgh.pa.us 676 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", type_id);
9357 bruce@momjian.us 677 :CBC 6572 : type = (Form_pg_type) GETSTRUCT(typeTuple);
8550 tgl@sss.pgh.pa.us 678 : 6572 : result = type->typrelid;
679 : 6572 : ReleaseSysCache(typeTuple);
2362 680 : 6572 : return result;
681 : : }
682 : :
683 : : /*
684 : : * Given a typeid, return the type's typrelid (associated relation), if any.
685 : : * Returns InvalidOid if type is not a composite type or a domain over one.
686 : : * This is the same as typeidTypeRelid(getBaseType(type_id)), but faster.
687 : : */
688 : : Oid
689 : 734321 : typeOrDomainTypeRelid(Oid type_id)
690 : : {
691 : : HeapTuple typeTuple;
692 : : Form_pg_type type;
693 : : Oid result;
694 : :
695 : : for (;;)
696 : : {
697 : 759112 : typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id));
698 [ - + ]: 759112 : if (!HeapTupleIsValid(typeTuple))
2362 tgl@sss.pgh.pa.us 699 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", type_id);
2362 tgl@sss.pgh.pa.us 700 :CBC 759112 : type = (Form_pg_type) GETSTRUCT(typeTuple);
701 [ + + ]: 759112 : if (type->typtype != TYPTYPE_DOMAIN)
702 : : {
703 : : /* Not a domain, so done looking through domains */
704 : 734321 : break;
705 : : }
706 : : /* It is a domain, so examine the base type instead */
707 : 24791 : type_id = type->typbasetype;
708 : 24791 : ReleaseSysCache(typeTuple);
709 : : }
710 : 734321 : result = type->typrelid;
711 : 734321 : ReleaseSysCache(typeTuple);
8550 712 : 734321 : return result;
713 : : }
714 : :
715 : : /*
716 : : * error context callback for parse failure during parseTypeString()
717 : : */
718 : : static void
7482 719 : 3 : pts_error_callback(void *arg)
720 : : {
721 : 3 : const char *str = (const char *) arg;
722 : :
723 : 3 : errcontext("invalid type name \"%s\"", str);
724 : 3 : }
725 : :
726 : : /*
727 : : * Given a string that is supposed to be a SQL-compatible type declaration,
728 : : * such as "int4" or "integer" or "character varying(32)", parse
729 : : * the string and return the result as a TypeName.
730 : : *
731 : : * If the string cannot be parsed as a type, an error is raised,
732 : : * unless escontext is an ErrorSaveContext node, in which case we may
733 : : * fill that and return NULL. But note that the ErrorSaveContext option
734 : : * is mostly aspirational at present: errors detected by the main
735 : : * grammar, rather than here, will still be thrown.
736 : : */
737 : : TypeName *
474 738 : 5195 : typeStringToTypeName(const char *str, Node *escontext)
739 : : {
740 : : List *raw_parsetree_list;
741 : : TypeName *typeName;
742 : : ErrorContextCallback ptserrcontext;
743 : :
744 : : /* make sure we give useful error for empty input */
283 michael@paquier.xyz 745 [ - + ]:GNC 5195 : if (strspn(str, " \t\n\r\f\v") == strlen(str))
7482 tgl@sss.pgh.pa.us 746 :UBC 0 : goto fail;
747 : :
748 : : /*
749 : : * Setup error traceback support in case of ereport() during parse
750 : : */
7482 tgl@sss.pgh.pa.us 751 :CBC 5195 : ptserrcontext.callback = pts_error_callback;
1902 peter@eisentraut.org 752 : 5195 : ptserrcontext.arg = unconstify(char *, str);
7482 tgl@sss.pgh.pa.us 753 : 5195 : ptserrcontext.previous = error_context_stack;
754 : 5195 : error_context_stack = &ptserrcontext;
755 : :
1196 756 : 5195 : raw_parsetree_list = raw_parser(str, RAW_PARSE_TYPE_NAME);
757 : :
7482 758 : 5192 : error_context_stack = ptserrcontext.previous;
759 : :
760 : : /* We should get back exactly one TypeName node. */
1196 761 [ - + ]: 5192 : Assert(list_length(raw_parsetree_list) == 1);
762 : 5192 : typeName = linitial_node(TypeName, raw_parsetree_list);
763 : :
764 : : /* The grammar allows SETOF in TypeName, but we don't want that here. */
5386 peter_e@gmx.net 765 [ - + ]: 5192 : if (typeName->setof)
7060 tgl@sss.pgh.pa.us 766 :UBC 0 : goto fail;
767 : :
3400 alvherre@alvh.no-ip. 768 :CBC 5192 : return typeName;
769 : :
3400 alvherre@alvh.no-ip. 770 :UBC 0 : fail:
474 tgl@sss.pgh.pa.us 771 [ # # ]: 0 : ereturn(escontext, NULL,
772 : : (errcode(ERRCODE_SYNTAX_ERROR),
773 : : errmsg("invalid type name \"%s\"", str)));
774 : : }
775 : :
776 : : /*
777 : : * Given a string that is supposed to be a SQL-compatible type declaration,
778 : : * such as "int4" or "integer" or "character varying(32)", parse
779 : : * the string and convert it to a type OID and type modifier.
780 : : *
781 : : * If escontext is an ErrorSaveContext node, then errors are reported by
782 : : * filling escontext and returning false, instead of throwing them.
783 : : */
784 : : bool
474 tgl@sss.pgh.pa.us 785 :CBC 1675 : parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
786 : : Node *escontext)
787 : : {
788 : : TypeName *typeName;
789 : : Type tup;
790 : :
791 : 1675 : typeName = typeStringToTypeName(str, escontext);
792 [ - + ]: 1672 : if (typeName == NULL)
474 tgl@sss.pgh.pa.us 793 :UBC 0 : return false;
794 : :
474 tgl@sss.pgh.pa.us 795 :CBC 1672 : tup = LookupTypeName(NULL, typeName, typmod_p,
796 [ + + + - ]: 1672 : (escontext && IsA(escontext, ErrorSaveContext)));
3659 rhaas@postgresql.org 797 [ + + ]: 1660 : if (tup == NULL)
798 : : {
474 tgl@sss.pgh.pa.us 799 [ + + ]: 20 : ereturn(escontext, false,
800 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
801 : : errmsg("type \"%s\" does not exist",
802 : : TypeNameToString(typeName))));
803 : : }
804 : : else
805 : : {
1972 andres@anarazel.de 806 : 1640 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(tup);
807 : :
808 [ - + ]: 1640 : if (!typ->typisdefined)
809 : : {
474 tgl@sss.pgh.pa.us 810 :UBC 0 : ReleaseSysCache(tup);
811 [ # # ]: 0 : ereturn(escontext, false,
812 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
813 : : errmsg("type \"%s\" is only a shell",
814 : : TypeNameToString(typeName))));
815 : : }
1972 andres@anarazel.de 816 :CBC 1640 : *typeid_p = typ->oid;
3659 rhaas@postgresql.org 817 : 1640 : ReleaseSysCache(tup);
818 : : }
819 : :
474 tgl@sss.pgh.pa.us 820 : 1640 : return true;
821 : : }
|