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