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