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-2025, 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
2224 noah@leadboat.com 38 :CBC 393220 : LookupTypeName(ParseState *pstate, const TypeName *typeName,
39 : : int32 *typmod_p, bool missing_ok)
40 : : {
41 : 393220 : 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 : 424083 : 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 : :
5896 peter_e@gmx.net 81 [ + + ]: 424083 : if (typeName->names == NIL)
82 : : {
83 : : /* We have the OID already if it's an internally generated TypeName */
84 : 95709 : typoid = typeName->typeOid;
85 : : }
86 [ + + ]: 328374 : 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);
8562 tgl@sss.pgh.pa.us 90 : 12 : char *field = NULL;
91 : : Oid relid;
92 : : AttrNumber attnum;
93 : :
94 : : /* deconstruct the name list */
5896 peter_e@gmx.net 95 [ - + + - : 12 : switch (list_length(typeName->names))
- ]
96 : : {
8562 tgl@sss.pgh.pa.us 97 :UBC 0 : case 1:
8085 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;
8562 tgl@sss.pgh.pa.us 104 :CBC 9 : case 2:
5896 peter_e@gmx.net 105 : 9 : rel->relname = strVal(linitial(typeName->names));
106 : 9 : field = strVal(lsecond(typeName->names));
8562 tgl@sss.pgh.pa.us 107 : 9 : break;
108 : 3 : case 3:
5896 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));
8562 tgl@sss.pgh.pa.us 112 : 3 : break;
8562 tgl@sss.pgh.pa.us 113 :UBC 0 : case 4:
5896 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));
8562 tgl@sss.pgh.pa.us 118 : 0 : break;
119 : 0 : default:
8085 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 : : */
4244 alvherre@alvh.no-ip. 135 :CBC 12 : relid = RangeVarGetRelid(rel, NoLock, missing_ok);
8562 tgl@sss.pgh.pa.us 136 : 12 : attnum = get_attnum(relid, field);
137 [ - + ]: 12 : if (attnum == InvalidAttrNumber)
138 : : {
4244 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 : : {
4244 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 */
5896 peter_e@gmx.net 169 : 328362 : DeconstructQualifiedName(typeName->names, &schemaname, &typname);
170 : :
8562 tgl@sss.pgh.pa.us 171 [ + + ]: 328356 : if (schemaname)
172 : : {
173 : : /* Look in specific schema only */
174 : : Oid namespaceId;
175 : : ParseCallbackState pcbstate;
176 : :
3825 alvherre@alvh.no-ip. 177 : 149088 : setup_parser_errposition_callback(&pcbstate, pstate, typeName->location);
178 : :
4244 179 : 149088 : namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
180 [ + + ]: 149085 : if (OidIsValid(namespaceId))
2482 andres@anarazel.de 181 : 149037 : typoid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
182 : : PointerGetDatum(typname),
183 : : ObjectIdGetDatum(namespaceId));
184 : : else
4244 alvherre@alvh.no-ip. 185 : 48 : typoid = InvalidOid;
186 : :
3825 187 : 149085 : cancel_parser_errposition_callback(&pcbstate);
188 : : }
189 : : else
190 : : {
191 : : /* Unqualified type name, so search the search path */
2224 noah@leadboat.com 192 : 179268 : typoid = TypenameGetTypidExtended(typname, temp_ok);
193 : : }
194 : :
195 : : /* If an array reference, return the array type instead */
5896 peter_e@gmx.net 196 [ + + ]: 328353 : if (typeName->arrayBounds != NIL)
6509 tgl@sss.pgh.pa.us 197 : 7740 : typoid = get_array_type(typoid);
198 : : }
199 : :
200 [ + + ]: 424074 : if (!OidIsValid(typoid))
201 : : {
202 [ + + ]: 30690 : if (typmod_p)
203 : 28 : *typmod_p = -1;
204 : 30690 : return NULL;
205 : : }
206 : :
5683 rhaas@postgresql.org 207 : 393384 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typoid));
6509 tgl@sss.pgh.pa.us 208 [ - + ]: 393384 : if (!HeapTupleIsValid(tup)) /* should not happen */
6509 tgl@sss.pgh.pa.us 209 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", typoid);
210 : :
5896 peter_e@gmx.net 211 :CBC 393384 : typmod = typenameTypeMod(pstate, typeName, (Type) tup);
212 : :
6509 tgl@sss.pgh.pa.us 213 [ + + ]: 393378 : if (typmod_p)
214 : 297653 : *typmod_p = typmod;
215 : :
216 : 393378 : 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
4244 alvherre@alvh.no-ip. 232 : 14487 : LookupTypeNameOid(ParseState *pstate, const TypeName *typeName, bool missing_ok)
233 : : {
234 : : Oid typoid;
235 : : Type tup;
236 : :
237 : 14487 : tup = LookupTypeName(pstate, typeName, NULL, missing_ok);
238 [ + + ]: 14487 : 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 : :
2482 andres@anarazel.de 250 : 14397 : typoid = ((Form_pg_type) GETSTRUCT(tup))->oid;
4244 alvherre@alvh.no-ip. 251 : 14397 : ReleaseSysCache(tup);
252 : :
253 : 14397 : 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
5295 tgl@sss.pgh.pa.us 264 : 335876 : typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p)
265 : : {
266 : : Type tup;
267 : :
4244 alvherre@alvh.no-ip. 268 : 335876 : tup = LookupTypeName(pstate, typeName, typmod_p, false);
6509 tgl@sss.pgh.pa.us 269 [ + + ]: 335873 : if (tup == NULL)
8085 270 [ + - ]: 25 : ereport(ERROR,
271 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
272 : : errmsg("type \"%s\" does not exist",
273 : : TypeNameToString(typeName)),
274 : : parser_errposition(pstate, typeName->location)));
6509 275 [ + + ]: 335848 : if (!((Form_pg_type) GETSTRUCT(tup))->typisdefined)
8085 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)));
6509 281 : 335845 : 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
5430 peter_e@gmx.net 291 : 6155 : typenameTypeId(ParseState *pstate, const TypeName *typeName)
292 : : {
293 : : Oid typoid;
294 : : Type tup;
295 : :
5295 tgl@sss.pgh.pa.us 296 : 6155 : tup = typenameType(pstate, typeName, NULL);
2482 andres@anarazel.de 297 : 6148 : typoid = ((Form_pg_type) GETSTRUCT(tup))->oid;
6509 tgl@sss.pgh.pa.us 298 : 6148 : ReleaseSysCache(tup);
299 : :
8562 300 : 6148 : 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
5430 peter_e@gmx.net 310 : 294748 : typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName,
311 : : Oid *typeid_p, int32 *typmod_p)
312 : : {
313 : : Type tup;
314 : :
5295 tgl@sss.pgh.pa.us 315 : 294748 : tup = typenameType(pstate, typeName, typmod_p);
2482 andres@anarazel.de 316 : 294743 : *typeid_p = ((Form_pg_type) GETSTRUCT(tup))->oid;
5430 peter_e@gmx.net 317 : 294743 : ReleaseSysCache(tup);
318 : 294743 : }
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
5896 332 : 393384 : 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 [ + + ]: 393384 : if (typeName->typmods == NIL)
344 : 389560 : 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 : : */
6509 tgl@sss.pgh.pa.us 351 [ - + ]: 3824 : if (!((Form_pg_type) GETSTRUCT(typ))->typisdefined)
6509 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 : :
6509 tgl@sss.pgh.pa.us 358 :CBC 3824 : typmodin = ((Form_pg_type) GETSTRUCT(typ))->typmodin;
359 : :
6825 360 [ - + ]: 3824 : if (typmodin == InvalidOid)
6825 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 : : */
5896 peter_e@gmx.net 372 :CBC 3824 : datums = (Datum *) palloc(list_length(typeName->typmods) * sizeof(Datum));
6825 tgl@sss.pgh.pa.us 373 : 3824 : n = 0;
5896 peter_e@gmx.net 374 [ + - + + : 8615 : foreach(l, typeName->typmods)
+ + ]
375 : : {
6505 bruce@momjian.us 376 : 4791 : Node *tm = (Node *) lfirst(l);
377 : 4791 : char *cstr = NULL;
378 : :
6658 tgl@sss.pgh.pa.us 379 [ + - ]: 4791 : if (IsA(tm, A_Const))
380 : : {
6505 bruce@momjian.us 381 : 4791 : A_Const *ac = (A_Const *) tm;
382 : :
6658 tgl@sss.pgh.pa.us 383 [ + - ]: 4791 : if (IsA(&ac->val, Integer))
384 : : {
1331 peter@eisentraut.org 385 : 4791 : cstr = psprintf("%ld", (long) intVal(&ac->val));
386 : : }
1458 peter@eisentraut.org 387 [ # # ]:UBC 0 : else if (IsA(&ac->val, Float))
388 : : {
389 : : /* we can just use the string representation directly. */
1331 390 : 0 : cstr = ac->val.fval.fval;
391 : : }
1458 392 [ # # ]: 0 : else if (IsA(&ac->val, String))
393 : : {
394 : : /* we can just use the string representation directly. */
1331 395 : 0 : cstr = strVal(&ac->val);
396 : : }
397 : : }
6658 tgl@sss.pgh.pa.us 398 [ # # ]: 0 : else if (IsA(tm, ColumnRef))
399 : : {
6505 bruce@momjian.us 400 : 0 : ColumnRef *cr = (ColumnRef *) tm;
401 : :
6216 tgl@sss.pgh.pa.us 402 [ # # ]: 0 : if (list_length(cr->fields) == 1 &&
403 [ # # ]: 0 : IsA(linitial(cr->fields), String))
6658 404 : 0 : cstr = strVal(linitial(cr->fields));
405 : : }
6658 tgl@sss.pgh.pa.us 406 [ - + ]:CBC 4791 : if (!cstr)
6825 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)));
6658 tgl@sss.pgh.pa.us 411 :CBC 4791 : datums[n++] = CStringGetDatum(cstr);
412 : : }
413 : :
1163 peter@eisentraut.org 414 : 3824 : arrtypmod = construct_array_builtin(datums, n, CSTRINGOID);
415 : :
416 : : /* arrange to report location if type's typmodin function fails */
5896 peter_e@gmx.net 417 : 3824 : setup_parser_errposition_callback(&pcbstate, pstate, typeName->location);
418 : :
6825 tgl@sss.pgh.pa.us 419 : 3824 : result = DatumGetInt32(OidFunctionCall1(typmodin,
420 : : PointerGetDatum(arrtypmod)));
421 : :
6214 422 : 3818 : cancel_parser_errposition_callback(&pcbstate);
423 : :
6825 424 : 3818 : pfree(datums);
425 : 3818 : pfree(arrtypmod);
426 : :
427 : 3818 : 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
5896 peter_e@gmx.net 439 : 3322 : appendTypeNameToBuffer(const TypeName *typeName, StringInfo string)
440 : : {
441 [ + - ]: 3322 : if (typeName->names != NIL)
442 : : {
443 : : /* Emit possibly-qualified name as-is */
444 : : ListCell *l;
445 : :
446 [ + - + + : 6749 : foreach(l, typeName->names)
+ + ]
447 : : {
448 [ + + ]: 3427 : if (l != list_head(typeName->names))
6509 tgl@sss.pgh.pa.us 449 : 105 : appendStringInfoChar(string, '.');
450 : 3427 : appendStringInfoString(string, strVal(lfirst(l)));
451 : : }
452 : : }
453 : : else
454 : : {
455 : : /* Look up internally-specified type */
5896 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 : : */
5896 peter_e@gmx.net 463 [ + + ]:CBC 3322 : if (typeName->pct_type)
6509 tgl@sss.pgh.pa.us 464 : 12 : appendStringInfoString(string, "%TYPE");
465 : :
5896 peter_e@gmx.net 466 [ + + ]: 3322 : if (typeName->arrayBounds != NIL)
6509 tgl@sss.pgh.pa.us 467 : 3 : appendStringInfoString(string, "[]");
468 : 3322 : }
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 *
5896 peter_e@gmx.net 478 : 3310 : TypeNameToString(const TypeName *typeName)
479 : : {
480 : : StringInfoData string;
481 : :
6509 tgl@sss.pgh.pa.us 482 : 3310 : initStringInfo(&string);
5896 peter_e@gmx.net 483 : 3310 : appendTypeNameToBuffer(typeName, &string);
6509 tgl@sss.pgh.pa.us 484 : 3310 : 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 : : {
3071 500 : 12 : TypeName *typeName = lfirst_node(TypeName, l);
501 : :
6509 502 [ + + ]: 12 : if (l != list_head(typenames))
503 : 6 : appendStringInfoChar(&string, ',');
5896 peter_e@gmx.net 504 : 12 : appendTypeNameToBuffer(typeName, &string);
505 : : }
6509 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
5295 515 : 5038 : LookupCollation(ParseState *pstate, List *collnames, int location)
516 : : {
517 : : Oid colloid;
518 : : ParseCallbackState pcbstate;
519 : :
520 [ + + ]: 5038 : if (pstate)
521 : 4785 : setup_parser_errposition_callback(&pcbstate, pstate, location);
522 : :
523 : 5038 : colloid = get_collation_oid(collnames, false);
524 : :
525 [ + + ]: 5032 : if (pstate)
526 : 4779 : cancel_parser_errposition_callback(&pcbstate);
527 : :
528 : 5032 : 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
590 peter@eisentraut.org 540 : 130719 : GetColumnDefCollation(ParseState *pstate, const ColumnDef *coldef, Oid typeOid)
541 : : {
542 : : Oid result;
5295 tgl@sss.pgh.pa.us 543 : 130719 : Oid typcollation = get_typcollation(typeOid);
4307 544 : 130719 : int location = coldef->location;
545 : :
5295 546 [ + + ]: 130719 : if (coldef->collClause)
547 : : {
548 : : /* We have a raw COLLATE clause, so look up the collation */
549 : 259 : location = coldef->collClause->location;
5293 550 : 259 : result = LookupCollation(pstate, coldef->collClause->collname,
551 : : location);
552 : : }
5295 553 [ + + ]: 130460 : else if (OidIsValid(coldef->collOid))
554 : : {
555 : : /* Precooked collation spec, use that */
556 : 44275 : result = coldef->collOid;
557 : : }
558 : : else
559 : : {
560 : : /* Use the type's default collation if any */
561 : 86185 : result = typcollation;
562 : : }
563 : :
564 : : /* Complain if COLLATE is applied to an uncollatable type */
565 [ + + + + ]: 130719 : if (OidIsValid(result) && !OidIsValid(typcollation))
566 [ + - ]: 3 : 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 : :
572 : 130716 : 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
10147 bruce@momjian.us 578 : 383849 : typeidType(Oid id)
579 : : {
580 : : HeapTuple tup;
581 : :
5683 rhaas@postgresql.org 582 : 383849 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(id));
9060 tgl@sss.pgh.pa.us 583 [ - + ]: 383849 : if (!HeapTupleIsValid(tup))
8085 tgl@sss.pgh.pa.us 584 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", id);
9867 bruce@momjian.us 585 :CBC 383849 : return (Type) tup;
586 : : }
587 : :
588 : : /* given type (as type struct), return the type OID */
589 : : Oid
10147 590 : 41507 : typeTypeId(Type tp)
591 : : {
8085 tgl@sss.pgh.pa.us 592 [ - + ]: 41507 : if (tp == NULL) /* probably useless */
10106 bruce@momjian.us 593 [ # # ]:UBC 0 : elog(ERROR, "typeTypeId() called with NULL type struct");
2482 andres@anarazel.de 594 :CBC 41507 : return ((Form_pg_type) GETSTRUCT(tp))->oid;
595 : : }
596 : :
597 : : /* given type (as type struct), return the length of type */
598 : : int16
10147 bruce@momjian.us 599 : 374375 : typeLen(Type t)
600 : : {
601 : : Form_pg_type typ;
602 : :
9867 603 : 374375 : typ = (Form_pg_type) GETSTRUCT(t);
604 : 374375 : return typ->typlen;
605 : : }
606 : :
607 : : /* given type (as type struct), return its 'byval' attribute */
608 : : bool
10147 609 : 374375 : typeByVal(Type t)
610 : : {
611 : : Form_pg_type typ;
612 : :
9867 613 : 374375 : typ = (Form_pg_type) GETSTRUCT(t);
614 : 374375 : return typ->typbyval;
615 : : }
616 : :
617 : : /* given type (as type struct), return the type's name */
618 : : char *
10147 bruce@momjian.us 619 :UBC 0 : typeTypeName(Type t)
620 : : {
621 : : Form_pg_type typ;
622 : :
9867 623 : 0 : typ = (Form_pg_type) GETSTRUCT(t);
624 : : /* pstrdup here because result may need to outlive the syscache entry */
9223 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
9060 tgl@sss.pgh.pa.us 630 :CBC 410 : typeTypeRelid(Type typ)
631 : : {
632 : : Form_pg_type typtup;
633 : :
634 : 410 : typtup = (Form_pg_type) GETSTRUCT(typ);
635 : 410 : return typtup->typrelid;
636 : : }
637 : :
638 : : /* given type (as type struct), return its 'typcollation' attribute */
639 : : Oid
5265 640 : 374375 : typeTypeCollation(Type typ)
641 : : {
642 : : Form_pg_type typtup;
643 : :
644 : 374375 : typtup = (Form_pg_type) GETSTRUCT(typ);
645 : 374375 : 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
9529 654 : 374375 : stringTypeDatum(Type tp, char *string, int32 atttypmod)
655 : : {
6357 656 : 374375 : Form_pg_type typform = (Form_pg_type) GETSTRUCT(tp);
657 : 374375 : Oid typinput = typform->typinput;
658 : 374375 : Oid typioparam = getTypeIOParam(tp);
659 : :
3516 660 : 374375 : 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
10147 bruce@momjian.us 668 : 6766 : typeidTypeRelid(Oid type_id)
669 : : {
670 : : HeapTuple typeTuple;
671 : : Form_pg_type type;
672 : : Oid result;
673 : :
5683 rhaas@postgresql.org 674 : 6766 : typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id));
10147 bruce@momjian.us 675 [ - + ]: 6766 : if (!HeapTupleIsValid(typeTuple))
8085 tgl@sss.pgh.pa.us 676 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", type_id);
9867 bruce@momjian.us 677 :CBC 6766 : type = (Form_pg_type) GETSTRUCT(typeTuple);
9060 tgl@sss.pgh.pa.us 678 : 6766 : result = type->typrelid;
679 : 6766 : ReleaseSysCache(typeTuple);
2872 680 : 6766 : 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 : 856089 : typeOrDomainTypeRelid(Oid type_id)
690 : : {
691 : : HeapTuple typeTuple;
692 : : Form_pg_type type;
693 : : Oid result;
694 : :
695 : : for (;;)
696 : : {
697 : 887077 : typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id));
698 [ - + ]: 887077 : if (!HeapTupleIsValid(typeTuple))
2872 tgl@sss.pgh.pa.us 699 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", type_id);
2872 tgl@sss.pgh.pa.us 700 :CBC 887077 : type = (Form_pg_type) GETSTRUCT(typeTuple);
701 [ + + ]: 887077 : if (type->typtype != TYPTYPE_DOMAIN)
702 : : {
703 : : /* Not a domain, so done looking through domains */
704 : 856089 : break;
705 : : }
706 : : /* It is a domain, so examine the base type instead */
707 : 30988 : type_id = type->typbasetype;
708 : 30988 : ReleaseSysCache(typeTuple);
709 : : }
710 : 856089 : result = type->typrelid;
711 : 856089 : ReleaseSysCache(typeTuple);
9060 712 : 856089 : return result;
713 : : }
714 : :
715 : : /*
716 : : * error context callback for parse failure during parseTypeString()
717 : : */
718 : : static void
7992 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 *
984 738 : 4947 : 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 */
793 michael@paquier.xyz 745 [ - + ]: 4947 : if (strspn(str, " \t\n\r\f\v") == strlen(str))
7992 tgl@sss.pgh.pa.us 746 :UBC 0 : goto fail;
747 : :
748 : : /*
749 : : * Setup error traceback support in case of ereport() during parse
750 : : */
7992 tgl@sss.pgh.pa.us 751 :CBC 4947 : ptserrcontext.callback = pts_error_callback;
2412 peter@eisentraut.org 752 : 4947 : ptserrcontext.arg = unconstify(char *, str);
7992 tgl@sss.pgh.pa.us 753 : 4947 : ptserrcontext.previous = error_context_stack;
754 : 4947 : error_context_stack = &ptserrcontext;
755 : :
1706 756 : 4947 : raw_parsetree_list = raw_parser(str, RAW_PARSE_TYPE_NAME);
757 : :
7992 758 : 4944 : error_context_stack = ptserrcontext.previous;
759 : :
760 : : /* We should get back exactly one TypeName node. */
1706 761 [ - + ]: 4944 : Assert(list_length(raw_parsetree_list) == 1);
762 : 4944 : typeName = linitial_node(TypeName, raw_parsetree_list);
763 : :
764 : : /* The grammar allows SETOF in TypeName, but we don't want that here. */
5896 peter_e@gmx.net 765 [ - + ]: 4944 : if (typeName->setof)
7570 tgl@sss.pgh.pa.us 766 :UBC 0 : goto fail;
767 : :
3910 alvherre@alvh.no-ip. 768 :CBC 4944 : return typeName;
769 : :
3910 alvherre@alvh.no-ip. 770 :UBC 0 : fail:
984 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
984 tgl@sss.pgh.pa.us 785 :CBC 1685 : parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
786 : : Node *escontext)
787 : : {
788 : : TypeName *typeName;
789 : : Type tup;
790 : :
791 : 1685 : typeName = typeStringToTypeName(str, escontext);
792 [ - + ]: 1682 : if (typeName == NULL)
984 tgl@sss.pgh.pa.us 793 :UBC 0 : return false;
794 : :
984 tgl@sss.pgh.pa.us 795 :CBC 1682 : tup = LookupTypeName(NULL, typeName, typmod_p,
796 [ + + + - ]: 1682 : (escontext && IsA(escontext, ErrorSaveContext)));
4169 rhaas@postgresql.org 797 [ + + ]: 1670 : if (tup == NULL)
798 : : {
984 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 : : {
2482 andres@anarazel.de 806 : 1650 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(tup);
807 : :
808 [ - + ]: 1650 : if (!typ->typisdefined)
809 : : {
984 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 : : }
2482 andres@anarazel.de 816 :CBC 1650 : *typeid_p = typ->oid;
4169 rhaas@postgresql.org 817 : 1650 : ReleaseSysCache(tup);
818 : : }
819 : :
984 tgl@sss.pgh.pa.us 820 : 1650 : return true;
821 : : }
|