Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * functioncmds.c
4 : *
5 : * Routines for CREATE and DROP FUNCTION commands and CREATE and DROP
6 : * CAST commands.
7 : *
8 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
9 : * Portions Copyright (c) 1994, Regents of the University of California
10 : *
11 : *
12 : * IDENTIFICATION
13 : * src/backend/commands/functioncmds.c
14 : *
15 : * DESCRIPTION
16 : * These routines take the parse tree and pick out the
17 : * appropriate arguments/flags, and pass the results to the
18 : * corresponding "FooDefine" routines (in src/catalog) that do
19 : * the actual catalog-munging. These routines also verify permission
20 : * of the user to execute the command.
21 : *
22 : * NOTES
23 : * These things must be defined and committed in the following order:
24 : * "create function":
25 : * input/output, recv/send procedures
26 : * "create type":
27 : * type
28 : * "create operator":
29 : * operators
30 : *
31 : *-------------------------------------------------------------------------
32 : */
33 : #include "postgres.h"
34 :
35 : #include "access/htup_details.h"
36 : #include "access/table.h"
37 : #include "catalog/catalog.h"
38 : #include "catalog/dependency.h"
39 : #include "catalog/indexing.h"
40 : #include "catalog/objectaccess.h"
41 : #include "catalog/pg_aggregate.h"
42 : #include "catalog/pg_cast.h"
43 : #include "catalog/pg_language.h"
44 : #include "catalog/pg_namespace.h"
45 : #include "catalog/pg_proc.h"
46 : #include "catalog/pg_transform.h"
47 : #include "catalog/pg_type.h"
48 : #include "commands/defrem.h"
49 : #include "commands/extension.h"
50 : #include "commands/proclang.h"
51 : #include "executor/executor.h"
52 : #include "executor/functions.h"
53 : #include "funcapi.h"
54 : #include "miscadmin.h"
55 : #include "optimizer/optimizer.h"
56 : #include "parser/analyze.h"
57 : #include "parser/parse_coerce.h"
58 : #include "parser/parse_collate.h"
59 : #include "parser/parse_expr.h"
60 : #include "parser/parse_func.h"
61 : #include "parser/parse_type.h"
62 : #include "pgstat.h"
63 : #include "tcop/pquery.h"
64 : #include "tcop/utility.h"
65 : #include "utils/acl.h"
66 : #include "utils/builtins.h"
67 : #include "utils/guc.h"
68 : #include "utils/lsyscache.h"
69 : #include "utils/rel.h"
70 : #include "utils/snapmgr.h"
71 : #include "utils/syscache.h"
72 : #include "utils/typcache.h"
73 :
74 : /*
75 : * Examine the RETURNS clause of the CREATE FUNCTION statement
76 : * and return information about it as *prorettype_p and *returnsSet.
77 : *
78 : * This is more complex than the average typename lookup because we want to
79 : * allow a shell type to be used, or even created if the specified return type
80 : * doesn't exist yet. (Without this, there's no way to define the I/O procs
81 : * for a new type.) But SQL function creation won't cope, so error out if
82 : * the target language is SQL. (We do this here, not in the SQL-function
83 : * validator, so as not to produce a NOTICE and then an ERROR for the same
84 : * condition.)
85 : */
86 : static void
87 19622 : compute_return_type(TypeName *returnType, Oid languageOid,
88 : Oid *prorettype_p, bool *returnsSet_p)
89 : {
90 : Oid rettype;
91 : Type typtup;
92 : AclResult aclresult;
93 :
94 19622 : typtup = LookupTypeName(NULL, returnType, NULL, false);
95 :
96 19622 : if (typtup)
97 : {
98 19548 : if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
99 : {
100 154 : if (languageOid == SQLlanguageId)
101 0 : ereport(ERROR,
102 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
103 : errmsg("SQL function cannot return shell type %s",
104 : TypeNameToString(returnType))));
105 : else
106 154 : ereport(NOTICE,
107 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
108 : errmsg("return type %s is only a shell",
109 : TypeNameToString(returnType))));
110 : }
111 19548 : rettype = typeTypeId(typtup);
112 19548 : ReleaseSysCache(typtup);
113 : }
114 : else
115 : {
116 74 : char *typnam = TypeNameToString(returnType);
117 : Oid namespaceId;
118 : char *typname;
119 : ObjectAddress address;
120 :
121 : /*
122 : * Only C-coded functions can be I/O functions. We enforce this
123 : * restriction here mainly to prevent littering the catalogs with
124 : * shell types due to simple typos in user-defined function
125 : * definitions.
126 : */
127 74 : if (languageOid != INTERNALlanguageId &&
128 : languageOid != ClanguageId)
129 0 : ereport(ERROR,
130 : (errcode(ERRCODE_UNDEFINED_OBJECT),
131 : errmsg("type \"%s\" does not exist", typnam)));
132 :
133 : /* Reject if there's typmod decoration, too */
134 74 : if (returnType->typmods != NIL)
135 0 : ereport(ERROR,
136 : (errcode(ERRCODE_SYNTAX_ERROR),
137 : errmsg("type modifier cannot be specified for shell type \"%s\"",
138 : typnam)));
139 :
140 : /* Otherwise, go ahead and make a shell type */
141 74 : ereport(NOTICE,
142 : (errcode(ERRCODE_UNDEFINED_OBJECT),
143 : errmsg("type \"%s\" is not yet defined", typnam),
144 : errdetail("Creating a shell type definition.")));
145 74 : namespaceId = QualifiedNameGetCreationNamespace(returnType->names,
146 : &typname);
147 74 : aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(),
148 : ACL_CREATE);
149 74 : if (aclresult != ACLCHECK_OK)
150 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
151 0 : get_namespace_name(namespaceId));
152 74 : address = TypeShellMake(typname, namespaceId, GetUserId());
153 74 : rettype = address.objectId;
154 : Assert(OidIsValid(rettype));
155 : }
156 :
157 19622 : aclresult = object_aclcheck(TypeRelationId, rettype, GetUserId(), ACL_USAGE);
158 19622 : if (aclresult != ACLCHECK_OK)
159 6 : aclcheck_error_type(aclresult, rettype);
160 :
161 19616 : *prorettype_p = rettype;
162 19616 : *returnsSet_p = returnType->setof;
163 19616 : }
164 :
165 : /*
166 : * Interpret the function parameter list of a CREATE FUNCTION,
167 : * CREATE PROCEDURE, or CREATE AGGREGATE statement.
168 : *
169 : * Input parameters:
170 : * parameters: list of FunctionParameter structs
171 : * languageOid: OID of function language (InvalidOid if it's CREATE AGGREGATE)
172 : * objtype: identifies type of object being created
173 : *
174 : * Results are stored into output parameters. parameterTypes must always
175 : * be created, but the other arrays/lists can be NULL pointers if not needed.
176 : * variadicArgType is set to the variadic array type if there's a VARIADIC
177 : * parameter (there can be only one); or to InvalidOid if not.
178 : * requiredResultType is set to InvalidOid if there are no OUT parameters,
179 : * else it is set to the OID of the implied result type.
180 : */
181 : void
182 20988 : interpret_function_parameter_list(ParseState *pstate,
183 : List *parameters,
184 : Oid languageOid,
185 : ObjectType objtype,
186 : oidvector **parameterTypes,
187 : List **parameterTypes_list,
188 : ArrayType **allParameterTypes,
189 : ArrayType **parameterModes,
190 : ArrayType **parameterNames,
191 : List **inParameterNames_list,
192 : List **parameterDefaults,
193 : Oid *variadicArgType,
194 : Oid *requiredResultType)
195 : {
196 20988 : int parameterCount = list_length(parameters);
197 : Oid *inTypes;
198 20988 : int inCount = 0;
199 : Datum *allTypes;
200 : Datum *paramModes;
201 : Datum *paramNames;
202 20988 : int outCount = 0;
203 20988 : int varCount = 0;
204 20988 : bool have_names = false;
205 20988 : bool have_defaults = false;
206 : ListCell *x;
207 : int i;
208 :
209 20988 : *variadicArgType = InvalidOid; /* default result */
210 20988 : *requiredResultType = InvalidOid; /* default result */
211 :
212 20988 : inTypes = (Oid *) palloc(parameterCount * sizeof(Oid));
213 20988 : allTypes = (Datum *) palloc(parameterCount * sizeof(Datum));
214 20988 : paramModes = (Datum *) palloc(parameterCount * sizeof(Datum));
215 20988 : paramNames = (Datum *) palloc0(parameterCount * sizeof(Datum));
216 20988 : *parameterDefaults = NIL;
217 :
218 : /* Scan the list and extract data into work arrays */
219 20988 : i = 0;
220 65336 : foreach(x, parameters)
221 : {
222 44408 : FunctionParameter *fp = (FunctionParameter *) lfirst(x);
223 44408 : TypeName *t = fp->argType;
224 44408 : FunctionParameterMode fpmode = fp->mode;
225 44408 : bool isinput = false;
226 : Oid toid;
227 : Type typtup;
228 : AclResult aclresult;
229 :
230 : /* For our purposes here, a defaulted mode spec is identical to IN */
231 44408 : if (fpmode == FUNC_PARAM_DEFAULT)
232 31304 : fpmode = FUNC_PARAM_IN;
233 :
234 44408 : typtup = LookupTypeName(NULL, t, NULL, false);
235 44408 : if (typtup)
236 : {
237 44408 : if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
238 : {
239 : /* As above, hard error if language is SQL */
240 222 : if (languageOid == SQLlanguageId)
241 0 : ereport(ERROR,
242 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
243 : errmsg("SQL function cannot accept shell type %s",
244 : TypeNameToString(t))));
245 : /* We don't allow creating aggregates on shell types either */
246 222 : else if (objtype == OBJECT_AGGREGATE)
247 0 : ereport(ERROR,
248 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
249 : errmsg("aggregate cannot accept shell type %s",
250 : TypeNameToString(t))));
251 : else
252 222 : ereport(NOTICE,
253 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
254 : errmsg("argument type %s is only a shell",
255 : TypeNameToString(t))));
256 : }
257 44408 : toid = typeTypeId(typtup);
258 44408 : ReleaseSysCache(typtup);
259 : }
260 : else
261 : {
262 0 : ereport(ERROR,
263 : (errcode(ERRCODE_UNDEFINED_OBJECT),
264 : errmsg("type %s does not exist",
265 : TypeNameToString(t))));
266 : toid = InvalidOid; /* keep compiler quiet */
267 : }
268 :
269 44408 : aclresult = object_aclcheck(TypeRelationId, toid, GetUserId(), ACL_USAGE);
270 44408 : if (aclresult != ACLCHECK_OK)
271 12 : aclcheck_error_type(aclresult, toid);
272 :
273 44396 : if (t->setof)
274 : {
275 0 : if (objtype == OBJECT_AGGREGATE)
276 0 : ereport(ERROR,
277 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
278 : errmsg("aggregates cannot accept set arguments")));
279 0 : else if (objtype == OBJECT_PROCEDURE)
280 0 : ereport(ERROR,
281 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
282 : errmsg("procedures cannot accept set arguments")));
283 : else
284 0 : ereport(ERROR,
285 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
286 : errmsg("functions cannot accept set arguments")));
287 : }
288 :
289 : /* handle input parameters */
290 44396 : if (fpmode != FUNC_PARAM_OUT && fpmode != FUNC_PARAM_TABLE)
291 : {
292 : /* other input parameters can't follow a VARIADIC parameter */
293 34574 : if (varCount > 0)
294 0 : ereport(ERROR,
295 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
296 : errmsg("VARIADIC parameter must be the last input parameter")));
297 34574 : inTypes[inCount++] = toid;
298 34574 : isinput = true;
299 34574 : if (parameterTypes_list)
300 34048 : *parameterTypes_list = lappend_oid(*parameterTypes_list, toid);
301 : }
302 :
303 : /* handle output parameters */
304 44396 : if (fpmode != FUNC_PARAM_IN && fpmode != FUNC_PARAM_VARIADIC)
305 : {
306 9978 : if (objtype == OBJECT_PROCEDURE)
307 : {
308 : /*
309 : * We disallow OUT-after-VARIADIC only for procedures. While
310 : * such a case causes no confusion in ordinary function calls,
311 : * it would cause confusion in a CALL statement.
312 : */
313 136 : if (varCount > 0)
314 6 : ereport(ERROR,
315 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
316 : errmsg("VARIADIC parameter must be the last parameter")));
317 : /* Procedures with output parameters always return RECORD */
318 130 : *requiredResultType = RECORDOID;
319 : }
320 9842 : else if (outCount == 0) /* save first output param's type */
321 2044 : *requiredResultType = toid;
322 9972 : outCount++;
323 : }
324 :
325 44390 : if (fpmode == FUNC_PARAM_VARIADIC)
326 : {
327 420 : *variadicArgType = toid;
328 420 : varCount++;
329 : /* validate variadic parameter type */
330 420 : switch (toid)
331 : {
332 70 : case ANYARRAYOID:
333 : case ANYCOMPATIBLEARRAYOID:
334 : case ANYOID:
335 : /* okay */
336 70 : break;
337 350 : default:
338 350 : if (!OidIsValid(get_element_type(toid)))
339 0 : ereport(ERROR,
340 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
341 : errmsg("VARIADIC parameter must be an array")));
342 350 : break;
343 : }
344 43970 : }
345 :
346 44390 : allTypes[i] = ObjectIdGetDatum(toid);
347 :
348 44390 : paramModes[i] = CharGetDatum(fpmode);
349 :
350 44390 : if (fp->name && fp->name[0])
351 : {
352 : ListCell *px;
353 :
354 : /*
355 : * As of Postgres 9.0 we disallow using the same name for two
356 : * input or two output function parameters. Depending on the
357 : * function's language, conflicting input and output names might
358 : * be bad too, but we leave it to the PL to complain if so.
359 : */
360 114970 : foreach(px, parameters)
361 : {
362 114970 : FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
363 : FunctionParameterMode prevfpmode;
364 :
365 114970 : if (prevfp == fp)
366 24766 : break;
367 : /* as above, default mode is IN */
368 90204 : prevfpmode = prevfp->mode;
369 90204 : if (prevfpmode == FUNC_PARAM_DEFAULT)
370 17172 : prevfpmode = FUNC_PARAM_IN;
371 : /* pure in doesn't conflict with pure out */
372 90204 : if ((fpmode == FUNC_PARAM_IN ||
373 16786 : fpmode == FUNC_PARAM_VARIADIC) &&
374 16728 : (prevfpmode == FUNC_PARAM_OUT ||
375 : prevfpmode == FUNC_PARAM_TABLE))
376 58 : continue;
377 90146 : if ((prevfpmode == FUNC_PARAM_IN ||
378 33790 : prevfpmode == FUNC_PARAM_VARIADIC) &&
379 17206 : (fpmode == FUNC_PARAM_OUT ||
380 : fpmode == FUNC_PARAM_TABLE))
381 17018 : continue;
382 73128 : if (prevfp->name && prevfp->name[0] &&
383 73102 : strcmp(prevfp->name, fp->name) == 0)
384 24 : ereport(ERROR,
385 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
386 : errmsg("parameter name \"%s\" used more than once",
387 : fp->name)));
388 : }
389 :
390 24766 : paramNames[i] = CStringGetTextDatum(fp->name);
391 24766 : have_names = true;
392 : }
393 :
394 44366 : if (inParameterNames_list)
395 43840 : *inParameterNames_list = lappend(*inParameterNames_list, makeString(fp->name ? fp->name : pstrdup("")));
396 :
397 44366 : if (fp->defexpr)
398 : {
399 : Node *def;
400 :
401 4886 : if (!isinput)
402 6 : ereport(ERROR,
403 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
404 : errmsg("only input parameters can have default values")));
405 :
406 4880 : def = transformExpr(pstate, fp->defexpr,
407 : EXPR_KIND_FUNCTION_DEFAULT);
408 4880 : def = coerce_to_specific_type(pstate, def, toid, "DEFAULT");
409 4880 : assign_expr_collations(pstate, def);
410 :
411 : /*
412 : * Make sure no variables are referred to (this is probably dead
413 : * code now that add_missing_from is history).
414 : */
415 9760 : if (pstate->p_rtable != NIL ||
416 4880 : contain_var_clause(def))
417 0 : ereport(ERROR,
418 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
419 : errmsg("cannot use table references in parameter default value")));
420 :
421 : /*
422 : * transformExpr() should have already rejected subqueries,
423 : * aggregates, and window functions, based on the EXPR_KIND_ for a
424 : * default expression.
425 : *
426 : * It can't return a set either --- but coerce_to_specific_type
427 : * already checked that for us.
428 : *
429 : * Note: the point of these restrictions is to ensure that an
430 : * expression that, on its face, hasn't got subplans, aggregates,
431 : * etc cannot suddenly have them after function default arguments
432 : * are inserted.
433 : */
434 :
435 4880 : *parameterDefaults = lappend(*parameterDefaults, def);
436 4880 : have_defaults = true;
437 : }
438 : else
439 : {
440 39480 : if (isinput && have_defaults)
441 6 : ereport(ERROR,
442 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
443 : errmsg("input parameters after one with a default value must also have defaults")));
444 :
445 : /*
446 : * For procedures, we also can't allow OUT parameters after one
447 : * with a default, because the same sort of confusion arises in a
448 : * CALL statement.
449 : */
450 39474 : if (objtype == OBJECT_PROCEDURE && have_defaults)
451 6 : ereport(ERROR,
452 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
453 : errmsg("procedure OUT parameters cannot appear after one with a default value")));
454 : }
455 :
456 44348 : i++;
457 : }
458 :
459 : /* Now construct the proper outputs as needed */
460 20928 : *parameterTypes = buildoidvector(inTypes, inCount);
461 :
462 20928 : if (outCount > 0 || varCount > 0)
463 : {
464 2208 : *allParameterTypes = construct_array_builtin(allTypes, parameterCount, OIDOID);
465 2208 : *parameterModes = construct_array_builtin(paramModes, parameterCount, CHAROID);
466 2208 : if (outCount > 1)
467 1876 : *requiredResultType = RECORDOID;
468 : /* otherwise we set requiredResultType correctly above */
469 : }
470 : else
471 : {
472 18720 : *allParameterTypes = NULL;
473 18720 : *parameterModes = NULL;
474 : }
475 :
476 20928 : if (have_names)
477 : {
478 31384 : for (i = 0; i < parameterCount; i++)
479 : {
480 24928 : if (paramNames[i] == PointerGetDatum(NULL))
481 216 : paramNames[i] = CStringGetTextDatum("");
482 : }
483 6456 : *parameterNames = construct_array_builtin(paramNames, parameterCount, TEXTOID);
484 : }
485 : else
486 14472 : *parameterNames = NULL;
487 20928 : }
488 :
489 :
490 : /*
491 : * Recognize one of the options that can be passed to both CREATE
492 : * FUNCTION and ALTER FUNCTION and return it via one of the out
493 : * parameters. Returns true if the passed option was recognized. If
494 : * the out parameter we were going to assign to points to non-NULL,
495 : * raise a duplicate-clause error. (We don't try to detect duplicate
496 : * SET parameters though --- if you're redundant, the last one wins.)
497 : */
498 : static bool
499 36872 : compute_common_attribute(ParseState *pstate,
500 : bool is_procedure,
501 : DefElem *defel,
502 : DefElem **volatility_item,
503 : DefElem **strict_item,
504 : DefElem **security_item,
505 : DefElem **leakproof_item,
506 : List **set_items,
507 : DefElem **cost_item,
508 : DefElem **rows_item,
509 : DefElem **support_item,
510 : DefElem **parallel_item)
511 : {
512 36872 : if (strcmp(defel->defname, "volatility") == 0)
513 : {
514 11064 : if (is_procedure)
515 0 : goto procedure_error;
516 11064 : if (*volatility_item)
517 0 : errorConflictingDefElem(defel, pstate);
518 :
519 11064 : *volatility_item = defel;
520 : }
521 25808 : else if (strcmp(defel->defname, "strict") == 0)
522 : {
523 11020 : if (is_procedure)
524 12 : goto procedure_error;
525 11008 : if (*strict_item)
526 0 : errorConflictingDefElem(defel, pstate);
527 :
528 11008 : *strict_item = defel;
529 : }
530 14788 : else if (strcmp(defel->defname, "security") == 0)
531 : {
532 60 : if (*security_item)
533 0 : errorConflictingDefElem(defel, pstate);
534 :
535 60 : *security_item = defel;
536 : }
537 14728 : else if (strcmp(defel->defname, "leakproof") == 0)
538 : {
539 58 : if (is_procedure)
540 0 : goto procedure_error;
541 58 : if (*leakproof_item)
542 0 : errorConflictingDefElem(defel, pstate);
543 :
544 58 : *leakproof_item = defel;
545 : }
546 14670 : else if (strcmp(defel->defname, "set") == 0)
547 : {
548 112 : *set_items = lappend(*set_items, defel->arg);
549 : }
550 14558 : else if (strcmp(defel->defname, "cost") == 0)
551 : {
552 3458 : if (is_procedure)
553 0 : goto procedure_error;
554 3458 : if (*cost_item)
555 0 : errorConflictingDefElem(defel, pstate);
556 :
557 3458 : *cost_item = defel;
558 : }
559 11100 : else if (strcmp(defel->defname, "rows") == 0)
560 : {
561 464 : if (is_procedure)
562 0 : goto procedure_error;
563 464 : if (*rows_item)
564 0 : errorConflictingDefElem(defel, pstate);
565 :
566 464 : *rows_item = defel;
567 : }
568 10636 : else if (strcmp(defel->defname, "support") == 0)
569 : {
570 94 : if (is_procedure)
571 0 : goto procedure_error;
572 94 : if (*support_item)
573 0 : errorConflictingDefElem(defel, pstate);
574 :
575 94 : *support_item = defel;
576 : }
577 10542 : else if (strcmp(defel->defname, "parallel") == 0)
578 : {
579 10542 : if (is_procedure)
580 0 : goto procedure_error;
581 10542 : if (*parallel_item)
582 0 : errorConflictingDefElem(defel, pstate);
583 :
584 10542 : *parallel_item = defel;
585 : }
586 : else
587 0 : return false;
588 :
589 : /* Recognized an option */
590 36860 : return true;
591 :
592 12 : procedure_error:
593 12 : ereport(ERROR,
594 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
595 : errmsg("invalid attribute in procedure definition"),
596 : parser_errposition(pstate, defel->location)));
597 : return false;
598 : }
599 :
600 : static char
601 11064 : interpret_func_volatility(DefElem *defel)
602 : {
603 11064 : char *str = strVal(defel->arg);
604 :
605 11064 : if (strcmp(str, "immutable") == 0)
606 7908 : return PROVOLATILE_IMMUTABLE;
607 3156 : else if (strcmp(str, "stable") == 0)
608 1852 : return PROVOLATILE_STABLE;
609 1304 : else if (strcmp(str, "volatile") == 0)
610 1304 : return PROVOLATILE_VOLATILE;
611 : else
612 : {
613 0 : elog(ERROR, "invalid volatility \"%s\"", str);
614 : return 0; /* keep compiler quiet */
615 : }
616 : }
617 :
618 : static char
619 10542 : interpret_func_parallel(DefElem *defel)
620 : {
621 10542 : char *str = strVal(defel->arg);
622 :
623 10542 : if (strcmp(str, "safe") == 0)
624 9754 : return PROPARALLEL_SAFE;
625 788 : else if (strcmp(str, "unsafe") == 0)
626 24 : return PROPARALLEL_UNSAFE;
627 764 : else if (strcmp(str, "restricted") == 0)
628 764 : return PROPARALLEL_RESTRICTED;
629 : else
630 : {
631 0 : ereport(ERROR,
632 : (errcode(ERRCODE_SYNTAX_ERROR),
633 : errmsg("parameter \"parallel\" must be SAFE, RESTRICTED, or UNSAFE")));
634 : return PROPARALLEL_UNSAFE; /* keep compiler quiet */
635 : }
636 : }
637 :
638 : /*
639 : * Update a proconfig value according to a list of VariableSetStmt items.
640 : *
641 : * The input and result may be NULL to signify a null entry.
642 : */
643 : static ArrayType *
644 80 : update_proconfig_value(ArrayType *a, List *set_items)
645 : {
646 : ListCell *l;
647 :
648 192 : foreach(l, set_items)
649 : {
650 112 : VariableSetStmt *sstmt = lfirst_node(VariableSetStmt, l);
651 :
652 112 : if (sstmt->kind == VAR_RESET_ALL)
653 12 : a = NULL;
654 : else
655 : {
656 100 : char *valuestr = ExtractSetVariableArgs(sstmt);
657 :
658 100 : if (valuestr)
659 100 : a = GUCArrayAdd(a, sstmt->name, valuestr);
660 : else /* RESET */
661 0 : a = GUCArrayDelete(a, sstmt->name);
662 : }
663 : }
664 :
665 80 : return a;
666 : }
667 :
668 : static Oid
669 94 : interpret_func_support(DefElem *defel)
670 : {
671 94 : List *procName = defGetQualifiedName(defel);
672 : Oid procOid;
673 : Oid argList[1];
674 :
675 : /*
676 : * Support functions always take one INTERNAL argument and return
677 : * INTERNAL.
678 : */
679 94 : argList[0] = INTERNALOID;
680 :
681 94 : procOid = LookupFuncName(procName, 1, argList, true);
682 94 : if (!OidIsValid(procOid))
683 0 : ereport(ERROR,
684 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
685 : errmsg("function %s does not exist",
686 : func_signature_string(procName, 1, NIL, argList))));
687 :
688 94 : if (get_func_rettype(procOid) != INTERNALOID)
689 0 : ereport(ERROR,
690 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
691 : errmsg("support function %s must return type %s",
692 : NameListToString(procName), "internal")));
693 :
694 : /*
695 : * Someday we might want an ACL check here; but for now, we insist that
696 : * you be superuser to specify a support function, so privilege on the
697 : * support function is moot.
698 : */
699 94 : if (!superuser())
700 0 : ereport(ERROR,
701 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
702 : errmsg("must be superuser to specify a support function")));
703 :
704 94 : return procOid;
705 : }
706 :
707 :
708 : /*
709 : * Dissect the list of options assembled in gram.y into function
710 : * attributes.
711 : */
712 : static void
713 20478 : compute_function_attributes(ParseState *pstate,
714 : bool is_procedure,
715 : List *options,
716 : List **as,
717 : char **language,
718 : Node **transform,
719 : bool *windowfunc_p,
720 : char *volatility_p,
721 : bool *strict_p,
722 : bool *security_definer,
723 : bool *leakproof_p,
724 : ArrayType **proconfig,
725 : float4 *procost,
726 : float4 *prorows,
727 : Oid *prosupport,
728 : char *parallel_p)
729 : {
730 : ListCell *option;
731 20478 : DefElem *as_item = NULL;
732 20478 : DefElem *language_item = NULL;
733 20478 : DefElem *transform_item = NULL;
734 20478 : DefElem *windowfunc_item = NULL;
735 20478 : DefElem *volatility_item = NULL;
736 20478 : DefElem *strict_item = NULL;
737 20478 : DefElem *security_item = NULL;
738 20478 : DefElem *leakproof_item = NULL;
739 20478 : List *set_items = NIL;
740 20478 : DefElem *cost_item = NULL;
741 20478 : DefElem *rows_item = NULL;
742 20478 : DefElem *support_item = NULL;
743 20478 : DefElem *parallel_item = NULL;
744 :
745 93326 : foreach(option, options)
746 : {
747 72860 : DefElem *defel = (DefElem *) lfirst(option);
748 :
749 72860 : if (strcmp(defel->defname, "as") == 0)
750 : {
751 16046 : if (as_item)
752 0 : errorConflictingDefElem(defel, pstate);
753 16046 : as_item = defel;
754 : }
755 56814 : else if (strcmp(defel->defname, "language") == 0)
756 : {
757 20418 : if (language_item)
758 0 : errorConflictingDefElem(defel, pstate);
759 20418 : language_item = defel;
760 : }
761 36396 : else if (strcmp(defel->defname, "transform") == 0)
762 : {
763 118 : if (transform_item)
764 0 : errorConflictingDefElem(defel, pstate);
765 118 : transform_item = defel;
766 : }
767 36278 : else if (strcmp(defel->defname, "window") == 0)
768 : {
769 20 : if (windowfunc_item)
770 0 : errorConflictingDefElem(defel, pstate);
771 20 : if (is_procedure)
772 6 : ereport(ERROR,
773 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
774 : errmsg("invalid attribute in procedure definition"),
775 : parser_errposition(pstate, defel->location)));
776 14 : windowfunc_item = defel;
777 : }
778 36258 : else if (compute_common_attribute(pstate,
779 : is_procedure,
780 : defel,
781 : &volatility_item,
782 : &strict_item,
783 : &security_item,
784 : &leakproof_item,
785 : &set_items,
786 : &cost_item,
787 : &rows_item,
788 : &support_item,
789 : ¶llel_item))
790 : {
791 : /* recognized common option */
792 36252 : continue;
793 : }
794 : else
795 0 : elog(ERROR, "option \"%s\" not recognized",
796 : defel->defname);
797 : }
798 :
799 20466 : if (as_item)
800 16046 : *as = (List *) as_item->arg;
801 20466 : if (language_item)
802 20406 : *language = strVal(language_item->arg);
803 20466 : if (transform_item)
804 118 : *transform = transform_item->arg;
805 20466 : if (windowfunc_item)
806 14 : *windowfunc_p = boolVal(windowfunc_item->arg);
807 20466 : if (volatility_item)
808 11030 : *volatility_p = interpret_func_volatility(volatility_item);
809 20466 : if (strict_item)
810 10984 : *strict_p = boolVal(strict_item->arg);
811 20466 : if (security_item)
812 48 : *security_definer = boolVal(security_item->arg);
813 20466 : if (leakproof_item)
814 34 : *leakproof_p = boolVal(leakproof_item->arg);
815 20466 : if (set_items)
816 62 : *proconfig = update_proconfig_value(NULL, set_items);
817 20466 : if (cost_item)
818 : {
819 3446 : *procost = defGetNumeric(cost_item);
820 3446 : if (*procost <= 0)
821 0 : ereport(ERROR,
822 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
823 : errmsg("COST must be positive")));
824 : }
825 20466 : if (rows_item)
826 : {
827 464 : *prorows = defGetNumeric(rows_item);
828 464 : if (*prorows <= 0)
829 0 : ereport(ERROR,
830 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
831 : errmsg("ROWS must be positive")));
832 : }
833 20466 : if (support_item)
834 88 : *prosupport = interpret_func_support(support_item);
835 20466 : if (parallel_item)
836 10064 : *parallel_p = interpret_func_parallel(parallel_item);
837 20466 : }
838 :
839 :
840 : /*
841 : * For a dynamically linked C language object, the form of the clause is
842 : *
843 : * AS <object file name> [, <link symbol name> ]
844 : *
845 : * In all other cases
846 : *
847 : * AS <object reference, or sql code>
848 : */
849 : static void
850 20380 : interpret_AS_clause(Oid languageOid, const char *languageName,
851 : char *funcname, List *as, Node *sql_body_in,
852 : List *parameterTypes, List *inParameterNames,
853 : char **prosrc_str_p, char **probin_str_p,
854 : Node **sql_body_out,
855 : const char *queryString)
856 : {
857 20380 : if (!sql_body_in && !as)
858 0 : ereport(ERROR,
859 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
860 : errmsg("no function body specified")));
861 :
862 20380 : if (sql_body_in && as)
863 6 : ereport(ERROR,
864 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
865 : errmsg("duplicate function body specified")));
866 :
867 20374 : if (sql_body_in && languageOid != SQLlanguageId)
868 0 : ereport(ERROR,
869 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
870 : errmsg("inline SQL function body only valid for language SQL")));
871 :
872 20374 : *sql_body_out = NULL;
873 :
874 20374 : if (languageOid == ClanguageId)
875 : {
876 : /*
877 : * For "C" language, store the file name in probin and, when given,
878 : * the link symbol name in prosrc. If link symbol is omitted,
879 : * substitute procedure name. We also allow link symbol to be
880 : * specified as "-", since that was the habit in PG versions before
881 : * 8.4, and there might be dump files out there that don't translate
882 : * that back to "omitted".
883 : */
884 4540 : *probin_str_p = strVal(linitial(as));
885 4540 : if (list_length(as) == 1)
886 2274 : *prosrc_str_p = funcname;
887 : else
888 : {
889 2266 : *prosrc_str_p = strVal(lsecond(as));
890 2266 : if (strcmp(*prosrc_str_p, "-") == 0)
891 0 : *prosrc_str_p = funcname;
892 : }
893 : }
894 15834 : else if (sql_body_in)
895 : {
896 : SQLFunctionParseInfoPtr pinfo;
897 :
898 4420 : pinfo = (SQLFunctionParseInfoPtr) palloc0(sizeof(SQLFunctionParseInfo));
899 :
900 4420 : pinfo->fname = funcname;
901 4420 : pinfo->nargs = list_length(parameterTypes);
902 4420 : pinfo->argtypes = (Oid *) palloc(pinfo->nargs * sizeof(Oid));
903 4420 : pinfo->argnames = (char **) palloc(pinfo->nargs * sizeof(char *));
904 13346 : for (int i = 0; i < list_length(parameterTypes); i++)
905 : {
906 8932 : char *s = strVal(list_nth(inParameterNames, i));
907 :
908 8932 : pinfo->argtypes[i] = list_nth_oid(parameterTypes, i);
909 8932 : if (IsPolymorphicType(pinfo->argtypes[i]))
910 6 : ereport(ERROR,
911 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
912 : errmsg("SQL function with unquoted function body cannot have polymorphic arguments")));
913 :
914 8926 : if (s[0] != '\0')
915 1386 : pinfo->argnames[i] = s;
916 : else
917 7540 : pinfo->argnames[i] = NULL;
918 : }
919 :
920 4414 : if (IsA(sql_body_in, List))
921 : {
922 626 : List *stmts = linitial_node(List, castNode(List, sql_body_in));
923 : ListCell *lc;
924 626 : List *transformed_stmts = NIL;
925 :
926 1244 : foreach(lc, stmts)
927 : {
928 624 : Node *stmt = lfirst(lc);
929 : Query *q;
930 624 : ParseState *pstate = make_parsestate(NULL);
931 :
932 624 : pstate->p_sourcetext = queryString;
933 624 : sql_fn_parser_setup(pstate, pinfo);
934 624 : q = transformStmt(pstate, stmt);
935 624 : if (q->commandType == CMD_UTILITY)
936 6 : ereport(ERROR,
937 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
938 : errmsg("%s is not yet supported in unquoted SQL function body",
939 : GetCommandTagName(CreateCommandTag(q->utilityStmt))));
940 618 : transformed_stmts = lappend(transformed_stmts, q);
941 618 : free_parsestate(pstate);
942 : }
943 :
944 620 : *sql_body_out = (Node *) list_make1(transformed_stmts);
945 : }
946 : else
947 : {
948 : Query *q;
949 3788 : ParseState *pstate = make_parsestate(NULL);
950 :
951 3788 : pstate->p_sourcetext = queryString;
952 3788 : sql_fn_parser_setup(pstate, pinfo);
953 3788 : q = transformStmt(pstate, sql_body_in);
954 3782 : if (q->commandType == CMD_UTILITY)
955 0 : ereport(ERROR,
956 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
957 : errmsg("%s is not yet supported in unquoted SQL function body",
958 : GetCommandTagName(CreateCommandTag(q->utilityStmt))));
959 3782 : free_parsestate(pstate);
960 :
961 3782 : *sql_body_out = (Node *) q;
962 : }
963 :
964 : /*
965 : * We must put something in prosrc. For the moment, just record an
966 : * empty string. It might be useful to store the original text of the
967 : * CREATE FUNCTION statement --- but to make actual use of that in
968 : * error reports, we'd also have to adjust readfuncs.c to not throw
969 : * away node location fields when reading prosqlbody.
970 : */
971 4402 : *prosrc_str_p = pstrdup("");
972 :
973 : /* But we definitely don't need probin. */
974 4402 : *probin_str_p = NULL;
975 : }
976 : else
977 : {
978 : /* Everything else wants the given string in prosrc. */
979 11414 : *prosrc_str_p = strVal(linitial(as));
980 11414 : *probin_str_p = NULL;
981 :
982 11414 : if (list_length(as) != 1)
983 6 : ereport(ERROR,
984 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
985 : errmsg("only one AS item needed for language \"%s\"",
986 : languageName)));
987 :
988 11408 : if (languageOid == INTERNALlanguageId)
989 : {
990 : /*
991 : * In PostgreSQL versions before 6.5, the SQL name of the created
992 : * function could not be different from the internal name, and
993 : * "prosrc" wasn't used. So there is code out there that does
994 : * CREATE FUNCTION xyz AS '' LANGUAGE internal. To preserve some
995 : * modicum of backwards compatibility, accept an empty "prosrc"
996 : * value as meaning the supplied SQL function name.
997 : */
998 3346 : if (strlen(*prosrc_str_p) == 0)
999 0 : *prosrc_str_p = funcname;
1000 : }
1001 : }
1002 20350 : }
1003 :
1004 :
1005 : /*
1006 : * CreateFunction
1007 : * Execute a CREATE FUNCTION (or CREATE PROCEDURE) utility statement.
1008 : */
1009 : ObjectAddress
1010 20478 : CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
1011 : {
1012 : char *probin_str;
1013 : char *prosrc_str;
1014 : Node *prosqlbody;
1015 : Oid prorettype;
1016 : bool returnsSet;
1017 : char *language;
1018 : Oid languageOid;
1019 : Oid languageValidator;
1020 20478 : Node *transformDefElem = NULL;
1021 : char *funcname;
1022 : Oid namespaceId;
1023 : AclResult aclresult;
1024 : oidvector *parameterTypes;
1025 20478 : List *parameterTypes_list = NIL;
1026 : ArrayType *allParameterTypes;
1027 : ArrayType *parameterModes;
1028 : ArrayType *parameterNames;
1029 20478 : List *inParameterNames_list = NIL;
1030 : List *parameterDefaults;
1031 : Oid variadicArgType;
1032 20478 : List *trftypes_list = NIL;
1033 : ArrayType *trftypes;
1034 : Oid requiredResultType;
1035 : bool isWindowFunc,
1036 : isStrict,
1037 : security,
1038 : isLeakProof;
1039 : char volatility;
1040 : ArrayType *proconfig;
1041 : float4 procost;
1042 : float4 prorows;
1043 : Oid prosupport;
1044 : HeapTuple languageTuple;
1045 : Form_pg_language languageStruct;
1046 : List *as_clause;
1047 : char parallel;
1048 :
1049 : /* Convert list of names to a name and namespace */
1050 20478 : namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname,
1051 : &funcname);
1052 :
1053 : /* Check we have creation rights in target namespace */
1054 20478 : aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(), ACL_CREATE);
1055 20478 : if (aclresult != ACLCHECK_OK)
1056 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
1057 0 : get_namespace_name(namespaceId));
1058 :
1059 : /* Set default attributes */
1060 20478 : as_clause = NIL;
1061 20478 : language = NULL;
1062 20478 : isWindowFunc = false;
1063 20478 : isStrict = false;
1064 20478 : security = false;
1065 20478 : isLeakProof = false;
1066 20478 : volatility = PROVOLATILE_VOLATILE;
1067 20478 : proconfig = NULL;
1068 20478 : procost = -1; /* indicates not set */
1069 20478 : prorows = -1; /* indicates not set */
1070 20478 : prosupport = InvalidOid;
1071 20478 : parallel = PROPARALLEL_UNSAFE;
1072 :
1073 : /* Extract non-default attributes from stmt->options list */
1074 20478 : compute_function_attributes(pstate,
1075 20478 : stmt->is_procedure,
1076 : stmt->options,
1077 : &as_clause, &language, &transformDefElem,
1078 : &isWindowFunc, &volatility,
1079 : &isStrict, &security, &isLeakProof,
1080 : &proconfig, &procost, &prorows,
1081 : &prosupport, ¶llel);
1082 :
1083 20466 : if (!language)
1084 : {
1085 60 : if (stmt->sql_body)
1086 60 : language = "sql";
1087 : else
1088 0 : ereport(ERROR,
1089 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
1090 : errmsg("no language specified")));
1091 : }
1092 :
1093 : /* Look up the language and validate permissions */
1094 20466 : languageTuple = SearchSysCache1(LANGNAME, PointerGetDatum(language));
1095 20466 : if (!HeapTupleIsValid(languageTuple))
1096 0 : ereport(ERROR,
1097 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1098 : errmsg("language \"%s\" does not exist", language),
1099 : (extension_file_exists(language) ?
1100 : errhint("Use CREATE EXTENSION to load the language into the database.") : 0)));
1101 :
1102 20466 : languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
1103 20466 : languageOid = languageStruct->oid;
1104 :
1105 20466 : if (languageStruct->lanpltrusted)
1106 : {
1107 : /* if trusted language, need USAGE privilege */
1108 12038 : aclresult = object_aclcheck(LanguageRelationId, languageOid, GetUserId(), ACL_USAGE);
1109 12038 : if (aclresult != ACLCHECK_OK)
1110 8 : aclcheck_error(aclresult, OBJECT_LANGUAGE,
1111 8 : NameStr(languageStruct->lanname));
1112 : }
1113 : else
1114 : {
1115 : /* if untrusted language, must be superuser */
1116 8428 : if (!superuser())
1117 0 : aclcheck_error(ACLCHECK_NO_PRIV, OBJECT_LANGUAGE,
1118 0 : NameStr(languageStruct->lanname));
1119 : }
1120 :
1121 20458 : languageValidator = languageStruct->lanvalidator;
1122 :
1123 20458 : ReleaseSysCache(languageTuple);
1124 :
1125 : /*
1126 : * Only superuser is allowed to create leakproof functions because
1127 : * leakproof functions can see tuples which have not yet been filtered out
1128 : * by security barrier views or row-level security policies.
1129 : */
1130 20458 : if (isLeakProof && !superuser())
1131 6 : ereport(ERROR,
1132 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1133 : errmsg("only superuser can define a leakproof function")));
1134 :
1135 20452 : if (transformDefElem)
1136 : {
1137 : ListCell *lc;
1138 :
1139 240 : foreach(lc, castNode(List, transformDefElem))
1140 : {
1141 122 : Oid typeid = typenameTypeId(NULL,
1142 122 : lfirst_node(TypeName, lc));
1143 122 : Oid elt = get_base_element_type(typeid);
1144 :
1145 122 : typeid = elt ? elt : typeid;
1146 :
1147 122 : get_transform_oid(typeid, languageOid, false);
1148 122 : trftypes_list = lappend_oid(trftypes_list, typeid);
1149 : }
1150 : }
1151 :
1152 : /*
1153 : * Convert remaining parameters of CREATE to form wanted by
1154 : * ProcedureCreate.
1155 : */
1156 20452 : interpret_function_parameter_list(pstate,
1157 : stmt->parameters,
1158 : languageOid,
1159 20452 : stmt->is_procedure ? OBJECT_PROCEDURE : OBJECT_FUNCTION,
1160 : ¶meterTypes,
1161 : ¶meterTypes_list,
1162 : &allParameterTypes,
1163 : ¶meterModes,
1164 : ¶meterNames,
1165 : &inParameterNames_list,
1166 : ¶meterDefaults,
1167 : &variadicArgType,
1168 : &requiredResultType);
1169 :
1170 20398 : if (stmt->is_procedure)
1171 : {
1172 : Assert(!stmt->returnType);
1173 304 : prorettype = requiredResultType ? requiredResultType : VOIDOID;
1174 304 : returnsSet = false;
1175 : }
1176 20094 : else if (stmt->returnType)
1177 : {
1178 : /* explicit RETURNS clause */
1179 19622 : compute_return_type(stmt->returnType, languageOid,
1180 : &prorettype, &returnsSet);
1181 19616 : if (OidIsValid(requiredResultType) && prorettype != requiredResultType)
1182 12 : ereport(ERROR,
1183 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
1184 : errmsg("function result type must be %s because of OUT parameters",
1185 : format_type_be(requiredResultType))));
1186 : }
1187 472 : else if (OidIsValid(requiredResultType))
1188 : {
1189 : /* default RETURNS clause from OUT parameters */
1190 472 : prorettype = requiredResultType;
1191 472 : returnsSet = false;
1192 : }
1193 : else
1194 : {
1195 0 : ereport(ERROR,
1196 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
1197 : errmsg("function result type must be specified")));
1198 : /* Alternative possibility: default to RETURNS VOID */
1199 : prorettype = VOIDOID;
1200 : returnsSet = false;
1201 : }
1202 :
1203 20380 : if (trftypes_list != NIL)
1204 : {
1205 : ListCell *lc;
1206 : Datum *arr;
1207 : int i;
1208 :
1209 118 : arr = palloc(list_length(trftypes_list) * sizeof(Datum));
1210 118 : i = 0;
1211 240 : foreach(lc, trftypes_list)
1212 122 : arr[i++] = ObjectIdGetDatum(lfirst_oid(lc));
1213 118 : trftypes = construct_array_builtin(arr, list_length(trftypes_list), OIDOID);
1214 : }
1215 : else
1216 : {
1217 : /* store SQL NULL instead of empty array */
1218 20262 : trftypes = NULL;
1219 : }
1220 :
1221 20380 : interpret_AS_clause(languageOid, language, funcname, as_clause, stmt->sql_body,
1222 : parameterTypes_list, inParameterNames_list,
1223 : &prosrc_str, &probin_str, &prosqlbody,
1224 : pstate->p_sourcetext);
1225 :
1226 : /*
1227 : * Set default values for COST and ROWS depending on other parameters;
1228 : * reject ROWS if it's not returnsSet. NB: pg_dump knows these default
1229 : * values, keep it in sync if you change them.
1230 : */
1231 20350 : if (procost < 0)
1232 : {
1233 : /* SQL and PL-language functions are assumed more expensive */
1234 16904 : if (languageOid == INTERNALlanguageId ||
1235 : languageOid == ClanguageId)
1236 7506 : procost = 1;
1237 : else
1238 9398 : procost = 100;
1239 : }
1240 20350 : if (prorows < 0)
1241 : {
1242 19886 : if (returnsSet)
1243 1608 : prorows = 1000;
1244 : else
1245 18278 : prorows = 0; /* dummy value if not returnsSet */
1246 : }
1247 464 : else if (!returnsSet)
1248 0 : ereport(ERROR,
1249 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1250 : errmsg("ROWS is not applicable when function does not return a set")));
1251 :
1252 : /*
1253 : * And now that we have all the parameters, and know we're permitted to do
1254 : * so, go ahead and create the function.
1255 : */
1256 40700 : return ProcedureCreate(funcname,
1257 : namespaceId,
1258 20350 : stmt->replace,
1259 : returnsSet,
1260 : prorettype,
1261 : GetUserId(),
1262 : languageOid,
1263 : languageValidator,
1264 : prosrc_str, /* converted to text later */
1265 : probin_str, /* converted to text later */
1266 : prosqlbody,
1267 20350 : stmt->is_procedure ? PROKIND_PROCEDURE : (isWindowFunc ? PROKIND_WINDOW : PROKIND_FUNCTION),
1268 : security,
1269 : isLeakProof,
1270 : isStrict,
1271 : volatility,
1272 : parallel,
1273 : parameterTypes,
1274 : PointerGetDatum(allParameterTypes),
1275 : PointerGetDatum(parameterModes),
1276 : PointerGetDatum(parameterNames),
1277 : parameterDefaults,
1278 : PointerGetDatum(trftypes),
1279 : PointerGetDatum(proconfig),
1280 : prosupport,
1281 : procost,
1282 : prorows);
1283 : }
1284 :
1285 : /*
1286 : * Guts of function deletion.
1287 : *
1288 : * Note: this is also used for aggregate deletion, since the OIDs of
1289 : * both functions and aggregates point to pg_proc.
1290 : */
1291 : void
1292 6432 : RemoveFunctionById(Oid funcOid)
1293 : {
1294 : Relation relation;
1295 : HeapTuple tup;
1296 : char prokind;
1297 :
1298 : /*
1299 : * Delete the pg_proc tuple.
1300 : */
1301 6432 : relation = table_open(ProcedureRelationId, RowExclusiveLock);
1302 :
1303 6432 : tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcOid));
1304 6432 : if (!HeapTupleIsValid(tup)) /* should not happen */
1305 0 : elog(ERROR, "cache lookup failed for function %u", funcOid);
1306 :
1307 6432 : prokind = ((Form_pg_proc) GETSTRUCT(tup))->prokind;
1308 :
1309 6432 : CatalogTupleDelete(relation, &tup->t_self);
1310 :
1311 6432 : ReleaseSysCache(tup);
1312 :
1313 6432 : table_close(relation, RowExclusiveLock);
1314 :
1315 6432 : pgstat_drop_function(funcOid);
1316 :
1317 : /*
1318 : * If there's a pg_aggregate tuple, delete that too.
1319 : */
1320 6432 : if (prokind == PROKIND_AGGREGATE)
1321 : {
1322 118 : relation = table_open(AggregateRelationId, RowExclusiveLock);
1323 :
1324 118 : tup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(funcOid));
1325 118 : if (!HeapTupleIsValid(tup)) /* should not happen */
1326 0 : elog(ERROR, "cache lookup failed for pg_aggregate tuple for function %u", funcOid);
1327 :
1328 118 : CatalogTupleDelete(relation, &tup->t_self);
1329 :
1330 118 : ReleaseSysCache(tup);
1331 :
1332 118 : table_close(relation, RowExclusiveLock);
1333 : }
1334 6432 : }
1335 :
1336 : /*
1337 : * Implements the ALTER FUNCTION utility command (except for the
1338 : * RENAME and OWNER clauses, which are handled as part of the generic
1339 : * ALTER framework).
1340 : */
1341 : ObjectAddress
1342 632 : AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
1343 : {
1344 : HeapTuple tup;
1345 : Oid funcOid;
1346 : Form_pg_proc procForm;
1347 : bool is_procedure;
1348 : Relation rel;
1349 : ListCell *l;
1350 632 : DefElem *volatility_item = NULL;
1351 632 : DefElem *strict_item = NULL;
1352 632 : DefElem *security_def_item = NULL;
1353 632 : DefElem *leakproof_item = NULL;
1354 632 : List *set_items = NIL;
1355 632 : DefElem *cost_item = NULL;
1356 632 : DefElem *rows_item = NULL;
1357 632 : DefElem *support_item = NULL;
1358 632 : DefElem *parallel_item = NULL;
1359 : ObjectAddress address;
1360 :
1361 632 : rel = table_open(ProcedureRelationId, RowExclusiveLock);
1362 :
1363 632 : funcOid = LookupFuncWithArgs(stmt->objtype, stmt->func, false);
1364 :
1365 614 : ObjectAddressSet(address, ProcedureRelationId, funcOid);
1366 :
1367 614 : tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(funcOid));
1368 614 : if (!HeapTupleIsValid(tup)) /* should not happen */
1369 0 : elog(ERROR, "cache lookup failed for function %u", funcOid);
1370 :
1371 614 : procForm = (Form_pg_proc) GETSTRUCT(tup);
1372 :
1373 : /* Permission check: must own function */
1374 614 : if (!object_ownercheck(ProcedureRelationId, funcOid, GetUserId()))
1375 0 : aclcheck_error(ACLCHECK_NOT_OWNER, stmt->objtype,
1376 0 : NameListToString(stmt->func->objname));
1377 :
1378 614 : if (procForm->prokind == PROKIND_AGGREGATE)
1379 0 : ereport(ERROR,
1380 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1381 : errmsg("\"%s\" is an aggregate function",
1382 : NameListToString(stmt->func->objname))));
1383 :
1384 614 : is_procedure = (procForm->prokind == PROKIND_PROCEDURE);
1385 :
1386 : /* Examine requested actions. */
1387 1222 : foreach(l, stmt->actions)
1388 : {
1389 614 : DefElem *defel = (DefElem *) lfirst(l);
1390 :
1391 614 : if (compute_common_attribute(pstate,
1392 : is_procedure,
1393 : defel,
1394 : &volatility_item,
1395 : &strict_item,
1396 : &security_def_item,
1397 : &leakproof_item,
1398 : &set_items,
1399 : &cost_item,
1400 : &rows_item,
1401 : &support_item,
1402 608 : ¶llel_item) == false)
1403 0 : elog(ERROR, "option \"%s\" not recognized", defel->defname);
1404 : }
1405 :
1406 608 : if (volatility_item)
1407 34 : procForm->provolatile = interpret_func_volatility(volatility_item);
1408 608 : if (strict_item)
1409 24 : procForm->proisstrict = boolVal(strict_item->arg);
1410 608 : if (security_def_item)
1411 12 : procForm->prosecdef = boolVal(security_def_item->arg);
1412 608 : if (leakproof_item)
1413 : {
1414 24 : procForm->proleakproof = boolVal(leakproof_item->arg);
1415 24 : if (procForm->proleakproof && !superuser())
1416 6 : ereport(ERROR,
1417 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1418 : errmsg("only superuser can define a leakproof function")));
1419 : }
1420 602 : if (cost_item)
1421 : {
1422 12 : procForm->procost = defGetNumeric(cost_item);
1423 12 : if (procForm->procost <= 0)
1424 0 : ereport(ERROR,
1425 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1426 : errmsg("COST must be positive")));
1427 : }
1428 602 : if (rows_item)
1429 : {
1430 0 : procForm->prorows = defGetNumeric(rows_item);
1431 0 : if (procForm->prorows <= 0)
1432 0 : ereport(ERROR,
1433 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1434 : errmsg("ROWS must be positive")));
1435 0 : if (!procForm->proretset)
1436 0 : ereport(ERROR,
1437 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1438 : errmsg("ROWS is not applicable when function does not return a set")));
1439 : }
1440 602 : if (support_item)
1441 : {
1442 : /* interpret_func_support handles the privilege check */
1443 6 : Oid newsupport = interpret_func_support(support_item);
1444 :
1445 : /* Add or replace dependency on support function */
1446 6 : if (OidIsValid(procForm->prosupport))
1447 : {
1448 0 : if (changeDependencyFor(ProcedureRelationId, funcOid,
1449 : ProcedureRelationId, procForm->prosupport,
1450 : newsupport) != 1)
1451 0 : elog(ERROR, "could not change support dependency for function %s",
1452 : get_func_name(funcOid));
1453 : }
1454 : else
1455 : {
1456 : ObjectAddress referenced;
1457 :
1458 6 : referenced.classId = ProcedureRelationId;
1459 6 : referenced.objectId = newsupport;
1460 6 : referenced.objectSubId = 0;
1461 6 : recordDependencyOn(&address, &referenced, DEPENDENCY_NORMAL);
1462 : }
1463 :
1464 6 : procForm->prosupport = newsupport;
1465 : }
1466 602 : if (parallel_item)
1467 478 : procForm->proparallel = interpret_func_parallel(parallel_item);
1468 602 : if (set_items)
1469 : {
1470 : Datum datum;
1471 : bool isnull;
1472 : ArrayType *a;
1473 : Datum repl_val[Natts_pg_proc];
1474 : bool repl_null[Natts_pg_proc];
1475 : bool repl_repl[Natts_pg_proc];
1476 :
1477 : /* extract existing proconfig setting */
1478 18 : datum = SysCacheGetAttr(PROCOID, tup, Anum_pg_proc_proconfig, &isnull);
1479 18 : a = isnull ? NULL : DatumGetArrayTypeP(datum);
1480 :
1481 : /* update according to each SET or RESET item, left to right */
1482 18 : a = update_proconfig_value(a, set_items);
1483 :
1484 : /* update the tuple */
1485 18 : memset(repl_repl, false, sizeof(repl_repl));
1486 18 : repl_repl[Anum_pg_proc_proconfig - 1] = true;
1487 :
1488 18 : if (a == NULL)
1489 : {
1490 12 : repl_val[Anum_pg_proc_proconfig - 1] = (Datum) 0;
1491 12 : repl_null[Anum_pg_proc_proconfig - 1] = true;
1492 : }
1493 : else
1494 : {
1495 6 : repl_val[Anum_pg_proc_proconfig - 1] = PointerGetDatum(a);
1496 6 : repl_null[Anum_pg_proc_proconfig - 1] = false;
1497 : }
1498 :
1499 18 : tup = heap_modify_tuple(tup, RelationGetDescr(rel),
1500 : repl_val, repl_null, repl_repl);
1501 : }
1502 : /* DO NOT put more touches of procForm below here; it's now dangling. */
1503 :
1504 : /* Do the update */
1505 602 : CatalogTupleUpdate(rel, &tup->t_self, tup);
1506 :
1507 602 : InvokeObjectPostAlterHook(ProcedureRelationId, funcOid, 0);
1508 :
1509 602 : table_close(rel, NoLock);
1510 602 : heap_freetuple(tup);
1511 :
1512 602 : return address;
1513 : }
1514 :
1515 :
1516 : /*
1517 : * CREATE CAST
1518 : */
1519 : ObjectAddress
1520 270 : CreateCast(CreateCastStmt *stmt)
1521 : {
1522 : Oid sourcetypeid;
1523 : Oid targettypeid;
1524 : char sourcetyptype;
1525 : char targettyptype;
1526 : Oid funcid;
1527 270 : Oid incastid = InvalidOid;
1528 270 : Oid outcastid = InvalidOid;
1529 : int nargs;
1530 : char castcontext;
1531 : char castmethod;
1532 : HeapTuple tuple;
1533 : AclResult aclresult;
1534 : ObjectAddress myself;
1535 :
1536 270 : sourcetypeid = typenameTypeId(NULL, stmt->sourcetype);
1537 270 : targettypeid = typenameTypeId(NULL, stmt->targettype);
1538 270 : sourcetyptype = get_typtype(sourcetypeid);
1539 270 : targettyptype = get_typtype(targettypeid);
1540 :
1541 : /* No pseudo-types allowed */
1542 270 : if (sourcetyptype == TYPTYPE_PSEUDO)
1543 0 : ereport(ERROR,
1544 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1545 : errmsg("source data type %s is a pseudo-type",
1546 : TypeNameToString(stmt->sourcetype))));
1547 :
1548 270 : if (targettyptype == TYPTYPE_PSEUDO)
1549 0 : ereport(ERROR,
1550 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1551 : errmsg("target data type %s is a pseudo-type",
1552 : TypeNameToString(stmt->targettype))));
1553 :
1554 : /* Permission check */
1555 270 : if (!object_ownercheck(TypeRelationId, sourcetypeid, GetUserId())
1556 12 : && !object_ownercheck(TypeRelationId, targettypeid, GetUserId()))
1557 0 : ereport(ERROR,
1558 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1559 : errmsg("must be owner of type %s or type %s",
1560 : format_type_be(sourcetypeid),
1561 : format_type_be(targettypeid))));
1562 :
1563 270 : aclresult = object_aclcheck(TypeRelationId, sourcetypeid, GetUserId(), ACL_USAGE);
1564 270 : if (aclresult != ACLCHECK_OK)
1565 6 : aclcheck_error_type(aclresult, sourcetypeid);
1566 :
1567 264 : aclresult = object_aclcheck(TypeRelationId, targettypeid, GetUserId(), ACL_USAGE);
1568 264 : if (aclresult != ACLCHECK_OK)
1569 0 : aclcheck_error_type(aclresult, targettypeid);
1570 :
1571 : /* Domains are allowed for historical reasons, but we warn */
1572 264 : if (sourcetyptype == TYPTYPE_DOMAIN)
1573 6 : ereport(WARNING,
1574 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1575 : errmsg("cast will be ignored because the source data type is a domain")));
1576 :
1577 258 : else if (targettyptype == TYPTYPE_DOMAIN)
1578 0 : ereport(WARNING,
1579 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1580 : errmsg("cast will be ignored because the target data type is a domain")));
1581 :
1582 : /* Determine the cast method */
1583 264 : if (stmt->func != NULL)
1584 96 : castmethod = COERCION_METHOD_FUNCTION;
1585 168 : else if (stmt->inout)
1586 6 : castmethod = COERCION_METHOD_INOUT;
1587 : else
1588 162 : castmethod = COERCION_METHOD_BINARY;
1589 :
1590 264 : if (castmethod == COERCION_METHOD_FUNCTION)
1591 : {
1592 : Form_pg_proc procstruct;
1593 :
1594 96 : funcid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->func, false);
1595 :
1596 96 : tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
1597 96 : if (!HeapTupleIsValid(tuple))
1598 0 : elog(ERROR, "cache lookup failed for function %u", funcid);
1599 :
1600 96 : procstruct = (Form_pg_proc) GETSTRUCT(tuple);
1601 96 : nargs = procstruct->pronargs;
1602 96 : if (nargs < 1 || nargs > 3)
1603 0 : ereport(ERROR,
1604 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1605 : errmsg("cast function must take one to three arguments")));
1606 96 : if (!IsBinaryCoercibleWithCast(sourcetypeid,
1607 : procstruct->proargtypes.values[0],
1608 : &incastid))
1609 0 : ereport(ERROR,
1610 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1611 : errmsg("argument of cast function must match or be binary-coercible from source data type")));
1612 96 : if (nargs > 1 && procstruct->proargtypes.values[1] != INT4OID)
1613 0 : ereport(ERROR,
1614 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1615 : errmsg("second argument of cast function must be type %s",
1616 : "integer")));
1617 96 : if (nargs > 2 && procstruct->proargtypes.values[2] != BOOLOID)
1618 0 : ereport(ERROR,
1619 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1620 : errmsg("third argument of cast function must be type %s",
1621 : "boolean")));
1622 96 : if (!IsBinaryCoercibleWithCast(procstruct->prorettype,
1623 : targettypeid,
1624 : &outcastid))
1625 0 : ereport(ERROR,
1626 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1627 : errmsg("return data type of cast function must match or be binary-coercible to target data type")));
1628 :
1629 : /*
1630 : * Restricting the volatility of a cast function may or may not be a
1631 : * good idea in the abstract, but it definitely breaks many old
1632 : * user-defined types. Disable this check --- tgl 2/1/03
1633 : */
1634 : #ifdef NOT_USED
1635 : if (procstruct->provolatile == PROVOLATILE_VOLATILE)
1636 : ereport(ERROR,
1637 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1638 : errmsg("cast function must not be volatile")));
1639 : #endif
1640 96 : if (procstruct->prokind != PROKIND_FUNCTION)
1641 0 : ereport(ERROR,
1642 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1643 : errmsg("cast function must be a normal function")));
1644 96 : if (procstruct->proretset)
1645 0 : ereport(ERROR,
1646 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1647 : errmsg("cast function must not return a set")));
1648 :
1649 96 : ReleaseSysCache(tuple);
1650 : }
1651 : else
1652 : {
1653 168 : funcid = InvalidOid;
1654 168 : nargs = 0;
1655 : }
1656 :
1657 264 : if (castmethod == COERCION_METHOD_BINARY)
1658 : {
1659 : int16 typ1len;
1660 : int16 typ2len;
1661 : bool typ1byval;
1662 : bool typ2byval;
1663 : char typ1align;
1664 : char typ2align;
1665 :
1666 : /*
1667 : * Must be superuser to create binary-compatible casts, since
1668 : * erroneous casts can easily crash the backend.
1669 : */
1670 162 : if (!superuser())
1671 0 : ereport(ERROR,
1672 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1673 : errmsg("must be superuser to create a cast WITHOUT FUNCTION")));
1674 :
1675 : /*
1676 : * Also, insist that the types match as to size, alignment, and
1677 : * pass-by-value attributes; this provides at least a crude check that
1678 : * they have similar representations. A pair of types that fail this
1679 : * test should certainly not be equated.
1680 : */
1681 162 : get_typlenbyvalalign(sourcetypeid, &typ1len, &typ1byval, &typ1align);
1682 162 : get_typlenbyvalalign(targettypeid, &typ2len, &typ2byval, &typ2align);
1683 162 : if (typ1len != typ2len ||
1684 162 : typ1byval != typ2byval ||
1685 162 : typ1align != typ2align)
1686 0 : ereport(ERROR,
1687 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1688 : errmsg("source and target data types are not physically compatible")));
1689 :
1690 : /*
1691 : * We know that composite, enum and array types are never binary-
1692 : * compatible with each other. They all have OIDs embedded in them.
1693 : *
1694 : * Theoretically you could build a user-defined base type that is
1695 : * binary-compatible with a composite, enum, or array type. But we
1696 : * disallow that too, as in practice such a cast is surely a mistake.
1697 : * You can always work around that by writing a cast function.
1698 : */
1699 162 : if (sourcetyptype == TYPTYPE_COMPOSITE ||
1700 : targettyptype == TYPTYPE_COMPOSITE)
1701 0 : ereport(ERROR,
1702 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1703 : errmsg("composite data types are not binary-compatible")));
1704 :
1705 162 : if (sourcetyptype == TYPTYPE_ENUM ||
1706 : targettyptype == TYPTYPE_ENUM)
1707 0 : ereport(ERROR,
1708 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1709 : errmsg("enum data types are not binary-compatible")));
1710 :
1711 324 : if (OidIsValid(get_element_type(sourcetypeid)) ||
1712 162 : OidIsValid(get_element_type(targettypeid)))
1713 0 : ereport(ERROR,
1714 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1715 : errmsg("array data types are not binary-compatible")));
1716 :
1717 : /*
1718 : * We also disallow creating binary-compatibility casts involving
1719 : * domains. Casting from a domain to its base type is already
1720 : * allowed, and casting the other way ought to go through domain
1721 : * coercion to permit constraint checking. Again, if you're intent on
1722 : * having your own semantics for that, create a no-op cast function.
1723 : *
1724 : * NOTE: if we were to relax this, the above checks for composites
1725 : * etc. would have to be modified to look through domains to their
1726 : * base types.
1727 : */
1728 162 : if (sourcetyptype == TYPTYPE_DOMAIN ||
1729 : targettyptype == TYPTYPE_DOMAIN)
1730 0 : ereport(ERROR,
1731 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1732 : errmsg("domain data types must not be marked binary-compatible")));
1733 : }
1734 :
1735 : /*
1736 : * Allow source and target types to be same only for length coercion
1737 : * functions. We assume a multi-arg function does length coercion.
1738 : */
1739 264 : if (sourcetypeid == targettypeid && nargs < 2)
1740 0 : ereport(ERROR,
1741 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1742 : errmsg("source data type and target data type are the same")));
1743 :
1744 : /* convert CoercionContext enum to char value for castcontext */
1745 264 : switch (stmt->context)
1746 : {
1747 30 : case COERCION_IMPLICIT:
1748 30 : castcontext = COERCION_CODE_IMPLICIT;
1749 30 : break;
1750 58 : case COERCION_ASSIGNMENT:
1751 58 : castcontext = COERCION_CODE_ASSIGNMENT;
1752 58 : break;
1753 : /* COERCION_PLPGSQL is intentionally not covered here */
1754 176 : case COERCION_EXPLICIT:
1755 176 : castcontext = COERCION_CODE_EXPLICIT;
1756 176 : break;
1757 0 : default:
1758 0 : elog(ERROR, "unrecognized CoercionContext: %d", stmt->context);
1759 : castcontext = 0; /* keep compiler quiet */
1760 : break;
1761 : }
1762 :
1763 264 : myself = CastCreate(sourcetypeid, targettypeid, funcid, incastid, outcastid,
1764 : castcontext, castmethod, DEPENDENCY_NORMAL);
1765 264 : return myself;
1766 : }
1767 :
1768 :
1769 : static void
1770 80 : check_transform_function(Form_pg_proc procstruct)
1771 : {
1772 80 : if (procstruct->provolatile == PROVOLATILE_VOLATILE)
1773 0 : ereport(ERROR,
1774 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1775 : errmsg("transform function must not be volatile")));
1776 80 : if (procstruct->prokind != PROKIND_FUNCTION)
1777 0 : ereport(ERROR,
1778 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1779 : errmsg("transform function must be a normal function")));
1780 80 : if (procstruct->proretset)
1781 0 : ereport(ERROR,
1782 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1783 : errmsg("transform function must not return a set")));
1784 80 : if (procstruct->pronargs != 1)
1785 0 : ereport(ERROR,
1786 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1787 : errmsg("transform function must take one argument")));
1788 80 : if (procstruct->proargtypes.values[0] != INTERNALOID)
1789 2 : ereport(ERROR,
1790 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1791 : errmsg("first argument of transform function must be type %s",
1792 : "internal")));
1793 78 : }
1794 :
1795 :
1796 : /*
1797 : * CREATE TRANSFORM
1798 : */
1799 : ObjectAddress
1800 50 : CreateTransform(CreateTransformStmt *stmt)
1801 : {
1802 : Oid typeid;
1803 : char typtype;
1804 : Oid langid;
1805 : Oid fromsqlfuncid;
1806 : Oid tosqlfuncid;
1807 : AclResult aclresult;
1808 : Form_pg_proc procstruct;
1809 : Datum values[Natts_pg_transform];
1810 50 : bool nulls[Natts_pg_transform] = {0};
1811 50 : bool replaces[Natts_pg_transform] = {0};
1812 : Oid transformid;
1813 : HeapTuple tuple;
1814 : HeapTuple newtuple;
1815 : Relation relation;
1816 : ObjectAddress myself,
1817 : referenced;
1818 : ObjectAddresses *addrs;
1819 : bool is_replace;
1820 :
1821 : /*
1822 : * Get the type
1823 : */
1824 50 : typeid = typenameTypeId(NULL, stmt->type_name);
1825 48 : typtype = get_typtype(typeid);
1826 :
1827 48 : if (typtype == TYPTYPE_PSEUDO)
1828 0 : ereport(ERROR,
1829 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1830 : errmsg("data type %s is a pseudo-type",
1831 : TypeNameToString(stmt->type_name))));
1832 :
1833 48 : if (typtype == TYPTYPE_DOMAIN)
1834 0 : ereport(ERROR,
1835 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1836 : errmsg("data type %s is a domain",
1837 : TypeNameToString(stmt->type_name))));
1838 :
1839 48 : if (!object_ownercheck(TypeRelationId, typeid, GetUserId()))
1840 0 : aclcheck_error_type(ACLCHECK_NOT_OWNER, typeid);
1841 :
1842 48 : aclresult = object_aclcheck(TypeRelationId, typeid, GetUserId(), ACL_USAGE);
1843 48 : if (aclresult != ACLCHECK_OK)
1844 0 : aclcheck_error_type(aclresult, typeid);
1845 :
1846 : /*
1847 : * Get the language
1848 : */
1849 48 : langid = get_language_oid(stmt->lang, false);
1850 :
1851 46 : aclresult = object_aclcheck(LanguageRelationId, langid, GetUserId(), ACL_USAGE);
1852 46 : if (aclresult != ACLCHECK_OK)
1853 0 : aclcheck_error(aclresult, OBJECT_LANGUAGE, stmt->lang);
1854 :
1855 : /*
1856 : * Get the functions
1857 : */
1858 46 : if (stmt->fromsql)
1859 : {
1860 44 : fromsqlfuncid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->fromsql, false);
1861 :
1862 44 : if (!object_ownercheck(ProcedureRelationId, fromsqlfuncid, GetUserId()))
1863 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION, NameListToString(stmt->fromsql->objname));
1864 :
1865 44 : aclresult = object_aclcheck(ProcedureRelationId, fromsqlfuncid, GetUserId(), ACL_EXECUTE);
1866 44 : if (aclresult != ACLCHECK_OK)
1867 0 : aclcheck_error(aclresult, OBJECT_FUNCTION, NameListToString(stmt->fromsql->objname));
1868 :
1869 44 : tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fromsqlfuncid));
1870 44 : if (!HeapTupleIsValid(tuple))
1871 0 : elog(ERROR, "cache lookup failed for function %u", fromsqlfuncid);
1872 44 : procstruct = (Form_pg_proc) GETSTRUCT(tuple);
1873 44 : if (procstruct->prorettype != INTERNALOID)
1874 2 : ereport(ERROR,
1875 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1876 : errmsg("return data type of FROM SQL function must be %s",
1877 : "internal")));
1878 42 : check_transform_function(procstruct);
1879 40 : ReleaseSysCache(tuple);
1880 : }
1881 : else
1882 2 : fromsqlfuncid = InvalidOid;
1883 :
1884 42 : if (stmt->tosql)
1885 : {
1886 38 : tosqlfuncid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->tosql, false);
1887 :
1888 38 : if (!object_ownercheck(ProcedureRelationId, tosqlfuncid, GetUserId()))
1889 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION, NameListToString(stmt->tosql->objname));
1890 :
1891 38 : aclresult = object_aclcheck(ProcedureRelationId, tosqlfuncid, GetUserId(), ACL_EXECUTE);
1892 38 : if (aclresult != ACLCHECK_OK)
1893 0 : aclcheck_error(aclresult, OBJECT_FUNCTION, NameListToString(stmt->tosql->objname));
1894 :
1895 38 : tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(tosqlfuncid));
1896 38 : if (!HeapTupleIsValid(tuple))
1897 0 : elog(ERROR, "cache lookup failed for function %u", tosqlfuncid);
1898 38 : procstruct = (Form_pg_proc) GETSTRUCT(tuple);
1899 38 : if (procstruct->prorettype != typeid)
1900 0 : ereport(ERROR,
1901 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1902 : errmsg("return data type of TO SQL function must be the transform data type")));
1903 38 : check_transform_function(procstruct);
1904 38 : ReleaseSysCache(tuple);
1905 : }
1906 : else
1907 4 : tosqlfuncid = InvalidOid;
1908 :
1909 : /*
1910 : * Ready to go
1911 : */
1912 42 : values[Anum_pg_transform_trftype - 1] = ObjectIdGetDatum(typeid);
1913 42 : values[Anum_pg_transform_trflang - 1] = ObjectIdGetDatum(langid);
1914 42 : values[Anum_pg_transform_trffromsql - 1] = ObjectIdGetDatum(fromsqlfuncid);
1915 42 : values[Anum_pg_transform_trftosql - 1] = ObjectIdGetDatum(tosqlfuncid);
1916 :
1917 42 : relation = table_open(TransformRelationId, RowExclusiveLock);
1918 :
1919 42 : tuple = SearchSysCache2(TRFTYPELANG,
1920 : ObjectIdGetDatum(typeid),
1921 : ObjectIdGetDatum(langid));
1922 42 : if (HeapTupleIsValid(tuple))
1923 : {
1924 8 : Form_pg_transform form = (Form_pg_transform) GETSTRUCT(tuple);
1925 :
1926 8 : if (!stmt->replace)
1927 2 : ereport(ERROR,
1928 : (errcode(ERRCODE_DUPLICATE_OBJECT),
1929 : errmsg("transform for type %s language \"%s\" already exists",
1930 : format_type_be(typeid),
1931 : stmt->lang)));
1932 :
1933 6 : replaces[Anum_pg_transform_trffromsql - 1] = true;
1934 6 : replaces[Anum_pg_transform_trftosql - 1] = true;
1935 :
1936 6 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
1937 6 : CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
1938 :
1939 6 : transformid = form->oid;
1940 6 : ReleaseSysCache(tuple);
1941 6 : is_replace = true;
1942 : }
1943 : else
1944 : {
1945 34 : transformid = GetNewOidWithIndex(relation, TransformOidIndexId,
1946 : Anum_pg_transform_oid);
1947 34 : values[Anum_pg_transform_oid - 1] = ObjectIdGetDatum(transformid);
1948 34 : newtuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
1949 34 : CatalogTupleInsert(relation, newtuple);
1950 34 : is_replace = false;
1951 : }
1952 :
1953 40 : if (is_replace)
1954 6 : deleteDependencyRecordsFor(TransformRelationId, transformid, true);
1955 :
1956 40 : addrs = new_object_addresses();
1957 :
1958 : /* make dependency entries */
1959 40 : ObjectAddressSet(myself, TransformRelationId, transformid);
1960 :
1961 : /* dependency on language */
1962 40 : ObjectAddressSet(referenced, LanguageRelationId, langid);
1963 40 : add_exact_object_address(&referenced, addrs);
1964 :
1965 : /* dependency on type */
1966 40 : ObjectAddressSet(referenced, TypeRelationId, typeid);
1967 40 : add_exact_object_address(&referenced, addrs);
1968 :
1969 : /* dependencies on functions */
1970 40 : if (OidIsValid(fromsqlfuncid))
1971 : {
1972 38 : ObjectAddressSet(referenced, ProcedureRelationId, fromsqlfuncid);
1973 38 : add_exact_object_address(&referenced, addrs);
1974 : }
1975 40 : if (OidIsValid(tosqlfuncid))
1976 : {
1977 36 : ObjectAddressSet(referenced, ProcedureRelationId, tosqlfuncid);
1978 36 : add_exact_object_address(&referenced, addrs);
1979 : }
1980 :
1981 40 : record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
1982 40 : free_object_addresses(addrs);
1983 :
1984 : /* dependency on extension */
1985 40 : recordDependencyOnCurrentExtension(&myself, is_replace);
1986 :
1987 : /* Post creation hook for new transform */
1988 40 : InvokeObjectPostCreateHook(TransformRelationId, transformid, 0);
1989 :
1990 40 : heap_freetuple(newtuple);
1991 :
1992 40 : table_close(relation, RowExclusiveLock);
1993 :
1994 40 : return myself;
1995 : }
1996 :
1997 :
1998 : /*
1999 : * get_transform_oid - given type OID and language OID, look up a transform OID
2000 : *
2001 : * If missing_ok is false, throw an error if the transform is not found. If
2002 : * true, just return InvalidOid.
2003 : */
2004 : Oid
2005 66624 : get_transform_oid(Oid type_id, Oid lang_id, bool missing_ok)
2006 : {
2007 : Oid oid;
2008 :
2009 66624 : oid = GetSysCacheOid2(TRFTYPELANG, Anum_pg_transform_oid,
2010 : ObjectIdGetDatum(type_id),
2011 : ObjectIdGetDatum(lang_id));
2012 66624 : if (!OidIsValid(oid) && !missing_ok)
2013 0 : ereport(ERROR,
2014 : (errcode(ERRCODE_UNDEFINED_OBJECT),
2015 : errmsg("transform for type %s language \"%s\" does not exist",
2016 : format_type_be(type_id),
2017 : get_language_name(lang_id, false))));
2018 66624 : return oid;
2019 : }
2020 :
2021 :
2022 : /*
2023 : * Subroutine for ALTER FUNCTION/AGGREGATE SET SCHEMA/RENAME
2024 : *
2025 : * Is there a function with the given name and signature already in the given
2026 : * namespace? If so, raise an appropriate error message.
2027 : */
2028 : void
2029 124 : IsThereFunctionInNamespace(const char *proname, int pronargs,
2030 : oidvector *proargtypes, Oid nspOid)
2031 : {
2032 : /* check for duplicate name (more friendly than unique-index failure) */
2033 124 : if (SearchSysCacheExists3(PROCNAMEARGSNSP,
2034 : CStringGetDatum(proname),
2035 : PointerGetDatum(proargtypes),
2036 : ObjectIdGetDatum(nspOid)))
2037 24 : ereport(ERROR,
2038 : (errcode(ERRCODE_DUPLICATE_FUNCTION),
2039 : errmsg("function %s already exists in schema \"%s\"",
2040 : funcname_signature_string(proname, pronargs,
2041 : NIL, proargtypes->values),
2042 : get_namespace_name(nspOid))));
2043 100 : }
2044 :
2045 : /*
2046 : * ExecuteDoStmt
2047 : * Execute inline procedural-language code
2048 : *
2049 : * See at ExecuteCallStmt() about the atomic argument.
2050 : */
2051 : void
2052 1084 : ExecuteDoStmt(ParseState *pstate, DoStmt *stmt, bool atomic)
2053 : {
2054 1084 : InlineCodeBlock *codeblock = makeNode(InlineCodeBlock);
2055 : ListCell *arg;
2056 1084 : DefElem *as_item = NULL;
2057 1084 : DefElem *language_item = NULL;
2058 : char *language;
2059 : Oid laninline;
2060 : HeapTuple languageTuple;
2061 : Form_pg_language languageStruct;
2062 :
2063 : /* Process options we got from gram.y */
2064 2364 : foreach(arg, stmt->args)
2065 : {
2066 1280 : DefElem *defel = (DefElem *) lfirst(arg);
2067 :
2068 1280 : if (strcmp(defel->defname, "as") == 0)
2069 : {
2070 1084 : if (as_item)
2071 0 : errorConflictingDefElem(defel, pstate);
2072 1084 : as_item = defel;
2073 : }
2074 196 : else if (strcmp(defel->defname, "language") == 0)
2075 : {
2076 196 : if (language_item)
2077 0 : errorConflictingDefElem(defel, pstate);
2078 196 : language_item = defel;
2079 : }
2080 : else
2081 0 : elog(ERROR, "option \"%s\" not recognized",
2082 : defel->defname);
2083 : }
2084 :
2085 1084 : if (as_item)
2086 1084 : codeblock->source_text = strVal(as_item->arg);
2087 : else
2088 0 : ereport(ERROR,
2089 : (errcode(ERRCODE_SYNTAX_ERROR),
2090 : errmsg("no inline code specified")));
2091 :
2092 : /* if LANGUAGE option wasn't specified, use the default */
2093 1084 : if (language_item)
2094 196 : language = strVal(language_item->arg);
2095 : else
2096 888 : language = "plpgsql";
2097 :
2098 : /* Look up the language and validate permissions */
2099 1084 : languageTuple = SearchSysCache1(LANGNAME, PointerGetDatum(language));
2100 1084 : if (!HeapTupleIsValid(languageTuple))
2101 0 : ereport(ERROR,
2102 : (errcode(ERRCODE_UNDEFINED_OBJECT),
2103 : errmsg("language \"%s\" does not exist", language),
2104 : (extension_file_exists(language) ?
2105 : errhint("Use CREATE EXTENSION to load the language into the database.") : 0)));
2106 :
2107 1084 : languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
2108 1084 : codeblock->langOid = languageStruct->oid;
2109 1084 : codeblock->langIsTrusted = languageStruct->lanpltrusted;
2110 1084 : codeblock->atomic = atomic;
2111 :
2112 1084 : if (languageStruct->lanpltrusted)
2113 : {
2114 : /* if trusted language, need USAGE privilege */
2115 : AclResult aclresult;
2116 :
2117 1040 : aclresult = object_aclcheck(LanguageRelationId, codeblock->langOid, GetUserId(),
2118 : ACL_USAGE);
2119 1040 : if (aclresult != ACLCHECK_OK)
2120 0 : aclcheck_error(aclresult, OBJECT_LANGUAGE,
2121 0 : NameStr(languageStruct->lanname));
2122 : }
2123 : else
2124 : {
2125 : /* if untrusted language, must be superuser */
2126 44 : if (!superuser())
2127 0 : aclcheck_error(ACLCHECK_NO_PRIV, OBJECT_LANGUAGE,
2128 0 : NameStr(languageStruct->lanname));
2129 : }
2130 :
2131 : /* get the handler function's OID */
2132 1084 : laninline = languageStruct->laninline;
2133 1084 : if (!OidIsValid(laninline))
2134 0 : ereport(ERROR,
2135 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2136 : errmsg("language \"%s\" does not support inline code execution",
2137 : NameStr(languageStruct->lanname))));
2138 :
2139 1084 : ReleaseSysCache(languageTuple);
2140 :
2141 : /* execute the inline handler */
2142 1084 : OidFunctionCall1(laninline, PointerGetDatum(codeblock));
2143 684 : }
2144 :
2145 : /*
2146 : * Execute CALL statement
2147 : *
2148 : * Inside a top-level CALL statement, transaction-terminating commands such as
2149 : * COMMIT or a PL-specific equivalent are allowed. The terminology in the SQL
2150 : * standard is that CALL establishes a non-atomic execution context. Most
2151 : * other commands establish an atomic execution context, in which transaction
2152 : * control actions are not allowed. If there are nested executions of CALL,
2153 : * we want to track the execution context recursively, so that the nested
2154 : * CALLs can also do transaction control. Note, however, that for example in
2155 : * CALL -> SELECT -> CALL, the second call cannot do transaction control,
2156 : * because the SELECT in between establishes an atomic execution context.
2157 : *
2158 : * So when ExecuteCallStmt() is called from the top level, we pass in atomic =
2159 : * false (recall that that means transactions = yes). We then create a
2160 : * CallContext node with content atomic = false, which is passed in the
2161 : * fcinfo->context field to the procedure invocation. The language
2162 : * implementation should then take appropriate measures to allow or prevent
2163 : * transaction commands based on that information, e.g., call
2164 : * SPI_connect_ext(SPI_OPT_NONATOMIC). The language should also pass on the
2165 : * atomic flag to any nested invocations to CALL.
2166 : *
2167 : * The expression data structures and execution context that we create
2168 : * within this function are children of the portalContext of the Portal
2169 : * that the CALL utility statement runs in. Therefore, any pass-by-ref
2170 : * values that we're passing to the procedure will survive transaction
2171 : * commits that might occur inside the procedure.
2172 : */
2173 : void
2174 410 : ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver *dest)
2175 : {
2176 410 : LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
2177 : ListCell *lc;
2178 : FuncExpr *fexpr;
2179 : int nargs;
2180 : int i;
2181 : AclResult aclresult;
2182 : FmgrInfo flinfo;
2183 : CallContext *callcontext;
2184 : EState *estate;
2185 : ExprContext *econtext;
2186 : HeapTuple tp;
2187 : PgStat_FunctionCallUsage fcusage;
2188 : Datum retval;
2189 :
2190 410 : fexpr = stmt->funcexpr;
2191 : Assert(fexpr);
2192 : Assert(IsA(fexpr, FuncExpr));
2193 :
2194 410 : aclresult = object_aclcheck(ProcedureRelationId, fexpr->funcid, GetUserId(), ACL_EXECUTE);
2195 410 : if (aclresult != ACLCHECK_OK)
2196 12 : aclcheck_error(aclresult, OBJECT_PROCEDURE, get_func_name(fexpr->funcid));
2197 :
2198 : /* Prep the context object we'll pass to the procedure */
2199 398 : callcontext = makeNode(CallContext);
2200 398 : callcontext->atomic = atomic;
2201 :
2202 398 : tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid));
2203 398 : if (!HeapTupleIsValid(tp))
2204 0 : elog(ERROR, "cache lookup failed for function %u", fexpr->funcid);
2205 :
2206 : /*
2207 : * If proconfig is set we can't allow transaction commands because of the
2208 : * way the GUC stacking works: The transaction boundary would have to pop
2209 : * the proconfig setting off the stack. That restriction could be lifted
2210 : * by redesigning the GUC nesting mechanism a bit.
2211 : */
2212 398 : if (!heap_attisnull(tp, Anum_pg_proc_proconfig, NULL))
2213 2 : callcontext->atomic = true;
2214 :
2215 : /*
2216 : * In security definer procedures, we can't allow transaction commands.
2217 : * StartTransaction() insists that the security context stack is empty,
2218 : * and AbortTransaction() resets the security context. This could be
2219 : * reorganized, but right now it doesn't work.
2220 : */
2221 398 : if (((Form_pg_proc) GETSTRUCT(tp))->prosecdef)
2222 2 : callcontext->atomic = true;
2223 :
2224 398 : ReleaseSysCache(tp);
2225 :
2226 : /* safety check; see ExecInitFunc() */
2227 398 : nargs = list_length(fexpr->args);
2228 398 : if (nargs > FUNC_MAX_ARGS)
2229 0 : ereport(ERROR,
2230 : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
2231 : errmsg_plural("cannot pass more than %d argument to a procedure",
2232 : "cannot pass more than %d arguments to a procedure",
2233 : FUNC_MAX_ARGS,
2234 : FUNC_MAX_ARGS)));
2235 :
2236 : /* Initialize function call structure */
2237 398 : InvokeFunctionExecuteHook(fexpr->funcid);
2238 398 : fmgr_info(fexpr->funcid, &flinfo);
2239 398 : fmgr_info_set_expr((Node *) fexpr, &flinfo);
2240 398 : InitFunctionCallInfoData(*fcinfo, &flinfo, nargs, fexpr->inputcollid,
2241 : (Node *) callcontext, NULL);
2242 :
2243 : /*
2244 : * Evaluate procedure arguments inside a suitable execution context. Note
2245 : * we can't free this context till the procedure returns.
2246 : */
2247 398 : estate = CreateExecutorState();
2248 398 : estate->es_param_list_info = params;
2249 398 : econtext = CreateExprContext(estate);
2250 :
2251 : /*
2252 : * If we're called in non-atomic context, we also have to ensure that the
2253 : * argument expressions run with an up-to-date snapshot. Our caller will
2254 : * have provided a current snapshot in atomic contexts, but not in
2255 : * non-atomic contexts, because the possibility of a COMMIT/ROLLBACK
2256 : * destroying the snapshot makes higher-level management too complicated.
2257 : */
2258 398 : if (!atomic)
2259 368 : PushActiveSnapshot(GetTransactionSnapshot());
2260 :
2261 398 : i = 0;
2262 962 : foreach(lc, fexpr->args)
2263 : {
2264 : ExprState *exprstate;
2265 : Datum val;
2266 : bool isnull;
2267 :
2268 564 : exprstate = ExecPrepareExpr(lfirst(lc), estate);
2269 :
2270 564 : val = ExecEvalExprSwitchContext(exprstate, econtext, &isnull);
2271 :
2272 564 : fcinfo->args[i].value = val;
2273 564 : fcinfo->args[i].isnull = isnull;
2274 :
2275 564 : i++;
2276 : }
2277 :
2278 : /* Get rid of temporary snapshot for arguments, if we made one */
2279 398 : if (!atomic)
2280 368 : PopActiveSnapshot();
2281 :
2282 : /* Here we actually call the procedure */
2283 398 : pgstat_init_function_usage(fcinfo, &fcusage);
2284 398 : retval = FunctionCallInvoke(fcinfo);
2285 368 : pgstat_end_function_usage(&fcusage, true);
2286 :
2287 : /* Handle the procedure's outputs */
2288 368 : if (fexpr->funcresulttype == VOIDOID)
2289 : {
2290 : /* do nothing */
2291 : }
2292 164 : else if (fexpr->funcresulttype == RECORDOID)
2293 : {
2294 : /* send tuple to client */
2295 : HeapTupleHeader td;
2296 : Oid tupType;
2297 : int32 tupTypmod;
2298 : TupleDesc retdesc;
2299 : HeapTupleData rettupdata;
2300 : TupOutputState *tstate;
2301 : TupleTableSlot *slot;
2302 :
2303 164 : if (fcinfo->isnull)
2304 0 : elog(ERROR, "procedure returned null record");
2305 :
2306 : /*
2307 : * Ensure there's an active snapshot whilst we execute whatever's
2308 : * involved here. Note that this is *not* sufficient to make the
2309 : * world safe for TOAST pointers to be included in the returned data:
2310 : * the referenced data could have gone away while we didn't hold a
2311 : * snapshot. Hence, it's incumbent on PLs that can do COMMIT/ROLLBACK
2312 : * to not return TOAST pointers, unless those pointers were fetched
2313 : * after the last COMMIT/ROLLBACK in the procedure.
2314 : *
2315 : * XXX that is a really nasty, hard-to-test requirement. Is there a
2316 : * way to remove it?
2317 : */
2318 164 : EnsurePortalSnapshotExists();
2319 :
2320 164 : td = DatumGetHeapTupleHeader(retval);
2321 164 : tupType = HeapTupleHeaderGetTypeId(td);
2322 164 : tupTypmod = HeapTupleHeaderGetTypMod(td);
2323 164 : retdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
2324 :
2325 164 : tstate = begin_tup_output_tupdesc(dest, retdesc,
2326 : &TTSOpsHeapTuple);
2327 :
2328 164 : rettupdata.t_len = HeapTupleHeaderGetDatumLength(td);
2329 164 : ItemPointerSetInvalid(&(rettupdata.t_self));
2330 164 : rettupdata.t_tableOid = InvalidOid;
2331 164 : rettupdata.t_data = td;
2332 :
2333 164 : slot = ExecStoreHeapTuple(&rettupdata, tstate->slot, false);
2334 164 : tstate->dest->receiveSlot(slot, tstate->dest);
2335 :
2336 164 : end_tup_output(tstate);
2337 :
2338 164 : ReleaseTupleDesc(retdesc);
2339 : }
2340 : else
2341 0 : elog(ERROR, "unexpected result type for procedure: %u",
2342 : fexpr->funcresulttype);
2343 :
2344 368 : FreeExecutorState(estate);
2345 368 : }
2346 :
2347 : /*
2348 : * Construct the tuple descriptor for a CALL statement return
2349 : */
2350 : TupleDesc
2351 156 : CallStmtResultDesc(CallStmt *stmt)
2352 : {
2353 : FuncExpr *fexpr;
2354 : HeapTuple tuple;
2355 : TupleDesc tupdesc;
2356 :
2357 156 : fexpr = stmt->funcexpr;
2358 :
2359 156 : tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid));
2360 156 : if (!HeapTupleIsValid(tuple))
2361 0 : elog(ERROR, "cache lookup failed for procedure %u", fexpr->funcid);
2362 :
2363 156 : tupdesc = build_function_result_tupdesc_t(tuple);
2364 :
2365 156 : ReleaseSysCache(tuple);
2366 :
2367 156 : return tupdesc;
2368 : }
|