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