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