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