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