Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * pg_proc.c
4 : : * routines to support manipulation of the pg_proc relation
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/catalog/pg_proc.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include "access/htup_details.h"
18 : : #include "access/table.h"
19 : : #include "access/xact.h"
20 : : #include "catalog/catalog.h"
21 : : #include "catalog/dependency.h"
22 : : #include "catalog/indexing.h"
23 : : #include "catalog/namespace.h"
24 : : #include "catalog/objectaccess.h"
25 : : #include "catalog/pg_language.h"
26 : : #include "catalog/pg_namespace.h"
27 : : #include "catalog/pg_proc.h"
28 : : #include "catalog/pg_transform.h"
29 : : #include "catalog/pg_type.h"
30 : : #include "executor/functions.h"
31 : : #include "funcapi.h"
32 : : #include "mb/pg_wchar.h"
33 : : #include "miscadmin.h"
34 : : #include "nodes/nodeFuncs.h"
35 : : #include "parser/parse_coerce.h"
36 : : #include "pgstat.h"
37 : : #include "rewrite/rewriteHandler.h"
38 : : #include "storage/lmgr.h"
39 : : #include "tcop/pquery.h"
40 : : #include "tcop/tcopprot.h"
41 : : #include "utils/acl.h"
42 : : #include "utils/builtins.h"
43 : : #include "utils/lsyscache.h"
44 : : #include "utils/regproc.h"
45 : : #include "utils/rel.h"
46 : : #include "utils/syscache.h"
47 : :
48 : :
49 : : typedef struct
50 : : {
51 : : char *proname;
52 : : char *prosrc;
53 : : } parse_error_callback_arg;
54 : :
55 : : static void sql_function_parse_error_callback(void *arg);
56 : : static int match_prosrc_to_query(const char *prosrc, const char *queryText,
57 : : int cursorpos);
58 : : static bool match_prosrc_to_literal(const char *prosrc, const char *literal,
59 : : int cursorpos, int *newcursorpos);
60 : :
61 : :
62 : : /* ----------------------------------------------------------------
63 : : * ProcedureCreate
64 : : *
65 : : * procedureName: string name of routine (proname)
66 : : * procNamespace: OID of namespace (pronamespace)
67 : : * replace: true to allow replacement of an existing pg_proc entry
68 : : * returnsSet: returns set? (proretset)
69 : : * returnType: OID of result type (prorettype)
70 : : * proowner: OID of owner role (proowner)
71 : : * languageObjectId: OID of function language (prolang)
72 : : * languageValidator: OID of validator function to apply, if any
73 : : * prosrc: string form of function definition (prosrc)
74 : : * probin: string form of binary reference, or NULL (probin)
75 : : * prosqlbody: Node tree of pre-parsed SQL body, or NULL (prosqlbody)
76 : : * prokind: function/aggregate/procedure/etc code (prokind)
77 : : * security_definer: security definer? (prosecdef)
78 : : * isLeakProof: leak proof? (proleakproof)
79 : : * isStrict: strict? (proisstrict)
80 : : * volatility: volatility code (provolatile)
81 : : * parallel: parallel safety code (proparallel)
82 : : * parameterTypes: input parameter types, as an oidvector (proargtypes)
83 : : * allParameterTypes: all parameter types, as an OID array (proallargtypes)
84 : : * parameterModes: parameter modes, as a "char" array (proargmodes)
85 : : * parameterNames: parameter names, as a text array (proargnames)
86 : : * parameterDefaults: defaults, as a List of Node trees (proargdefaults)
87 : : * trftypes: transformable type OIDs, as an OID array (protrftypes)
88 : : * trfoids: List of transform OIDs that routine should depend on
89 : : * proconfig: GUC set clauses, as a text array (proconfig)
90 : : * prosupport: OID of support function, if any (prosupport)
91 : : * procost: cost factor (procost)
92 : : * prorows: estimated output rows for a SRF (prorows)
93 : : *
94 : : * Note: allParameterTypes, parameterModes, parameterNames, trftypes, and proconfig
95 : : * are either arrays of the proper types or NULL. We declare them Datum,
96 : : * not "ArrayType *", to avoid importing array.h into pg_proc.h.
97 : : * ----------------------------------------------------------------
98 : : */
99 : : ObjectAddress
100 : 15119 : ProcedureCreate(const char *procedureName,
101 : : Oid procNamespace,
102 : : bool replace,
103 : : bool returnsSet,
104 : : Oid returnType,
105 : : Oid proowner,
106 : : Oid languageObjectId,
107 : : Oid languageValidator,
108 : : const char *prosrc,
109 : : const char *probin,
110 : : Node *prosqlbody,
111 : : char prokind,
112 : : bool security_definer,
113 : : bool isLeakProof,
114 : : bool isStrict,
115 : : char volatility,
116 : : char parallel,
117 : : oidvector *parameterTypes,
118 : : Datum allParameterTypes,
119 : : Datum parameterModes,
120 : : Datum parameterNames,
121 : : List *parameterDefaults,
122 : : Datum trftypes,
123 : : List *trfoids,
124 : : Datum proconfig,
125 : : Oid prosupport,
126 : : float4 procost,
127 : : float4 prorows)
128 : : {
129 : : Oid retval;
130 : : int parameterCount;
131 : : int allParamCount;
132 : : Oid *allParams;
133 : 15119 : char *paramModes = NULL;
134 : 15119 : Oid variadicType = InvalidOid;
135 : 15119 : Acl *proacl = NULL;
136 : : Relation rel;
137 : : HeapTuple tup;
138 : : HeapTuple oldtup;
139 : : bool nulls[Natts_pg_proc];
140 : : Datum values[Natts_pg_proc];
141 : : bool replaces[Natts_pg_proc];
142 : : NameData procname;
143 : : TupleDesc tupDesc;
144 : : bool is_update;
145 : : ObjectAddress myself,
146 : : referenced,
147 : : temp_object;
148 : : char *detailmsg;
149 : : int i;
150 : : ObjectAddresses *addrs;
151 : :
152 : : /*
153 : : * sanity checks
154 : : */
155 : : Assert(prosrc);
156 : :
157 : 15119 : parameterCount = parameterTypes->dim1;
158 [ + - - + ]: 15119 : if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS)
159 [ # # ]: 0 : ereport(ERROR,
160 : : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
161 : : errmsg_plural("functions cannot have more than %d argument",
162 : : "functions cannot have more than %d arguments",
163 : : FUNC_MAX_ARGS,
164 : : FUNC_MAX_ARGS)));
165 : : /* note: the above is correct, we do NOT count output arguments */
166 : :
167 : : /* Deconstruct array inputs */
168 [ + + ]: 15119 : if (allParameterTypes != PointerGetDatum(NULL))
169 : : {
170 : : /*
171 : : * We expect the array to be a 1-D OID array; verify that. We don't
172 : : * need to use deconstruct_array() since the array data is just going
173 : : * to look like a C array of OID values.
174 : : */
175 : 1457 : ArrayType *allParamArray = (ArrayType *) DatumGetPointer(allParameterTypes);
176 : :
177 : 1457 : allParamCount = ARR_DIMS(allParamArray)[0];
178 [ + - + - ]: 1457 : if (ARR_NDIM(allParamArray) != 1 ||
179 : 1457 : allParamCount <= 0 ||
180 [ + - ]: 1457 : ARR_HASNULL(allParamArray) ||
181 [ - + ]: 1457 : ARR_ELEMTYPE(allParamArray) != OIDOID)
182 [ # # ]: 0 : elog(ERROR, "allParameterTypes is not a 1-D Oid array");
183 [ - + ]: 1457 : allParams = (Oid *) ARR_DATA_PTR(allParamArray);
184 : : Assert(allParamCount >= parameterCount);
185 : : /* we assume caller got the contents right */
186 : : }
187 : : else
188 : : {
189 : 13662 : allParamCount = parameterCount;
190 : 13662 : allParams = parameterTypes->values;
191 : : }
192 : :
193 [ + + ]: 15119 : if (parameterModes != PointerGetDatum(NULL))
194 : : {
195 : : /*
196 : : * We expect the array to be a 1-D CHAR array; verify that. We don't
197 : : * need to use deconstruct_array() since the array data is just going
198 : : * to look like a C array of char values.
199 : : */
200 : 1457 : ArrayType *modesArray = (ArrayType *) DatumGetPointer(parameterModes);
201 : :
202 [ + - ]: 1457 : if (ARR_NDIM(modesArray) != 1 ||
203 [ + - ]: 1457 : ARR_DIMS(modesArray)[0] != allParamCount ||
204 [ + - ]: 1457 : ARR_HASNULL(modesArray) ||
205 [ - + ]: 1457 : ARR_ELEMTYPE(modesArray) != CHAROID)
206 [ # # ]: 0 : elog(ERROR, "parameterModes is not a 1-D char array");
207 [ - + ]: 1457 : paramModes = (char *) ARR_DATA_PTR(modesArray);
208 : : }
209 : :
210 : : /*
211 : : * Do not allow polymorphic return type unless there is a polymorphic
212 : : * input argument that we can use to deduce the actual return type.
213 : : */
214 : 15119 : detailmsg = check_valid_polymorphic_signature(returnType,
215 : 15119 : parameterTypes->values,
216 : : parameterCount);
217 [ + + ]: 15119 : if (detailmsg)
218 [ + - ]: 52 : ereport(ERROR,
219 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
220 : : errmsg("cannot determine result data type"),
221 : : errdetail_internal("%s", detailmsg)));
222 : :
223 : : /*
224 : : * Also, do not allow return type INTERNAL unless at least one input
225 : : * argument is INTERNAL.
226 : : */
227 : 15067 : detailmsg = check_valid_internal_signature(returnType,
228 : 15067 : parameterTypes->values,
229 : : parameterCount);
230 [ - + ]: 15067 : if (detailmsg)
231 [ # # ]: 0 : ereport(ERROR,
232 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
233 : : errmsg("unsafe use of pseudo-type \"internal\""),
234 : : errdetail_internal("%s", detailmsg)));
235 : :
236 : : /*
237 : : * Apply the same tests to any OUT arguments.
238 : : */
239 [ + + ]: 15067 : if (allParameterTypes != PointerGetDatum(NULL))
240 : : {
241 [ + + ]: 10029 : for (i = 0; i < allParamCount; i++)
242 : : {
243 [ + - ]: 8604 : if (paramModes == NULL ||
244 [ + + ]: 8604 : paramModes[i] == PROARGMODE_IN ||
245 [ + + ]: 7093 : paramModes[i] == PROARGMODE_VARIADIC)
246 : 1721 : continue; /* ignore input-only params */
247 : :
248 : 6883 : detailmsg = check_valid_polymorphic_signature(allParams[i],
249 : 6883 : parameterTypes->values,
250 : : parameterCount);
251 [ + + ]: 6883 : if (detailmsg)
252 [ + - ]: 32 : ereport(ERROR,
253 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
254 : : errmsg("cannot determine result data type"),
255 : : errdetail_internal("%s", detailmsg)));
256 : 6851 : detailmsg = check_valid_internal_signature(allParams[i],
257 : 6851 : parameterTypes->values,
258 : : parameterCount);
259 [ - + ]: 6851 : if (detailmsg)
260 [ # # ]: 0 : ereport(ERROR,
261 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
262 : : errmsg("unsafe use of pseudo-type \"internal\""),
263 : : errdetail_internal("%s", detailmsg)));
264 : : }
265 : : }
266 : :
267 : : /* Identify variadic argument type, if any */
268 [ + + ]: 15035 : if (paramModes != NULL)
269 : : {
270 : : /*
271 : : * Only the last input parameter can be variadic; if it is, save its
272 : : * element type. Errors here are just elog since caller should have
273 : : * checked this already.
274 : : */
275 [ + + ]: 9957 : for (i = 0; i < allParamCount; i++)
276 : : {
277 [ + + + + : 8532 : switch (paramModes[i])
- ]
278 : : {
279 : 1585 : case PROARGMODE_IN:
280 : : case PROARGMODE_INOUT:
281 [ - + ]: 1585 : if (OidIsValid(variadicType))
282 [ # # ]: 0 : elog(ERROR, "variadic parameter must be last");
283 : 1585 : break;
284 : 6404 : case PROARGMODE_OUT:
285 [ - + - - ]: 6404 : if (OidIsValid(variadicType) && prokind == PROKIND_PROCEDURE)
286 [ # # ]: 0 : elog(ERROR, "variadic parameter must be last");
287 : 6404 : break;
288 : 333 : case PROARGMODE_TABLE:
289 : : /* okay */
290 : 333 : break;
291 : 210 : case PROARGMODE_VARIADIC:
292 [ - + ]: 210 : if (OidIsValid(variadicType))
293 [ # # ]: 0 : elog(ERROR, "variadic parameter must be last");
294 [ + + + + ]: 210 : switch (allParams[i])
295 : : {
296 : 5 : case ANYOID:
297 : 5 : variadicType = ANYOID;
298 : 5 : break;
299 : 25 : case ANYARRAYOID:
300 : 25 : variadicType = ANYELEMENTOID;
301 : 25 : break;
302 : 14 : case ANYCOMPATIBLEARRAYOID:
303 : 14 : variadicType = ANYCOMPATIBLEOID;
304 : 14 : break;
305 : 166 : default:
306 : 166 : variadicType = get_element_type(allParams[i]);
307 [ - + ]: 166 : if (!OidIsValid(variadicType))
308 [ # # ]: 0 : elog(ERROR, "variadic parameter is not an array");
309 : 166 : break;
310 : : }
311 : 210 : break;
312 : 0 : default:
313 [ # # ]: 0 : elog(ERROR, "invalid parameter mode '%c'", paramModes[i]);
314 : : break;
315 : : }
316 : : }
317 : : }
318 : :
319 : : /*
320 : : * All seems OK; prepare the data to be inserted into pg_proc.
321 : : */
322 : :
323 [ + + ]: 466085 : for (i = 0; i < Natts_pg_proc; ++i)
324 : : {
325 : 451050 : nulls[i] = false;
326 : 451050 : values[i] = (Datum) 0;
327 : 451050 : replaces[i] = true;
328 : : }
329 : :
330 : 15035 : namestrcpy(&procname, procedureName);
331 : 15035 : values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname);
332 : 15035 : values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace);
333 : 15035 : values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(proowner);
334 : 15035 : values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId);
335 : 15035 : values[Anum_pg_proc_procost - 1] = Float4GetDatum(procost);
336 : 15035 : values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows);
337 : 15035 : values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType);
338 : 15035 : values[Anum_pg_proc_prosupport - 1] = ObjectIdGetDatum(prosupport);
339 : 15035 : values[Anum_pg_proc_prokind - 1] = CharGetDatum(prokind);
340 : 15035 : values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);
341 : 15035 : values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof);
342 : 15035 : values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict);
343 : 15035 : values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet);
344 : 15035 : values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility);
345 : 15035 : values[Anum_pg_proc_proparallel - 1] = CharGetDatum(parallel);
346 : 15035 : values[Anum_pg_proc_pronargs - 1] = Int16GetDatum(parameterCount);
347 : 15035 : values[Anum_pg_proc_pronargdefaults - 1] = Int16GetDatum(list_length(parameterDefaults));
348 : 15035 : values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType);
349 : 15035 : values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes);
350 [ + + ]: 15035 : if (allParameterTypes != PointerGetDatum(NULL))
351 : 1425 : values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes;
352 : : else
353 : 13610 : nulls[Anum_pg_proc_proallargtypes - 1] = true;
354 [ + + ]: 15035 : if (parameterModes != PointerGetDatum(NULL))
355 : 1425 : values[Anum_pg_proc_proargmodes - 1] = parameterModes;
356 : : else
357 : 13610 : nulls[Anum_pg_proc_proargmodes - 1] = true;
358 [ + + ]: 15035 : if (parameterNames != PointerGetDatum(NULL))
359 : 3716 : values[Anum_pg_proc_proargnames - 1] = parameterNames;
360 : : else
361 : 11319 : nulls[Anum_pg_proc_proargnames - 1] = true;
362 [ + + ]: 15035 : if (parameterDefaults != NIL)
363 : 469 : values[Anum_pg_proc_proargdefaults - 1] = CStringGetTextDatum(nodeToString(parameterDefaults));
364 : : else
365 : 14566 : nulls[Anum_pg_proc_proargdefaults - 1] = true;
366 [ + + ]: 15035 : if (trftypes != PointerGetDatum(NULL))
367 : 76 : values[Anum_pg_proc_protrftypes - 1] = trftypes;
368 : : else
369 : 14959 : nulls[Anum_pg_proc_protrftypes - 1] = true;
370 : 15035 : values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum(prosrc);
371 [ + + ]: 15035 : if (probin)
372 : 4414 : values[Anum_pg_proc_probin - 1] = CStringGetTextDatum(probin);
373 : : else
374 : 10621 : nulls[Anum_pg_proc_probin - 1] = true;
375 [ + + ]: 15035 : if (prosqlbody)
376 : 3436 : values[Anum_pg_proc_prosqlbody - 1] = CStringGetTextDatum(nodeToString(prosqlbody));
377 : : else
378 : 11599 : nulls[Anum_pg_proc_prosqlbody - 1] = true;
379 [ + + ]: 15035 : if (proconfig != PointerGetDatum(NULL))
380 : 71 : values[Anum_pg_proc_proconfig - 1] = proconfig;
381 : : else
382 : 14964 : nulls[Anum_pg_proc_proconfig - 1] = true;
383 : : /* proacl will be determined later */
384 : :
385 : 15035 : rel = table_open(ProcedureRelationId, RowExclusiveLock);
386 : 15035 : tupDesc = RelationGetDescr(rel);
387 : :
388 : : /* Check for pre-existing definition */
389 : 15035 : oldtup = SearchSysCache3(PROCNAMEARGSNSP,
390 : : PointerGetDatum(procedureName),
391 : : PointerGetDatum(parameterTypes),
392 : : ObjectIdGetDatum(procNamespace));
393 : :
394 [ + + ]: 15035 : if (HeapTupleIsValid(oldtup))
395 : : {
396 : : /* There is one; okay to replace it? */
397 : 3275 : Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
398 : : Datum proargnames;
399 : : bool isnull;
400 : : const char *dropcmd;
401 : :
402 [ - + ]: 3275 : if (!replace)
403 [ # # ]: 0 : ereport(ERROR,
404 : : (errcode(ERRCODE_DUPLICATE_FUNCTION),
405 : : errmsg("function \"%s\" already exists with same argument types",
406 : : procedureName)));
407 : :
408 : : /*
409 : : * The row-locking on the pg_proc row is generally enough protection
410 : : * from concurrently DROPping the function, but acquire a heavy-weight
411 : : * lock on the function anyway, so that other DDL commands that depend
412 : : * on the details of the function can prevent the function from being
413 : : * concurrently modified. And you get nicer error message on
414 : : * conflicting ALTER FUNCTION commands than "row concurrently
415 : : * updated".
416 : : */
417 : 3275 : LockDatabaseObject(ProcedureRelationId, oldproc->oid, 0, AccessExclusiveLock);
418 : :
419 [ - + ]: 3275 : if (!object_ownercheck(ProcedureRelationId, oldproc->oid, proowner))
420 : 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
421 : : procedureName);
422 : :
423 : : /* Not okay to change routine kind */
424 [ + + ]: 3275 : if (oldproc->prokind != prokind)
425 [ + - - + : 12 : ereport(ERROR,
+ - - - -
- ]
426 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
427 : : errmsg("cannot change routine kind"),
428 : : (oldproc->prokind == PROKIND_AGGREGATE ?
429 : : errdetail("\"%s\" is an aggregate function.", procedureName) :
430 : : oldproc->prokind == PROKIND_FUNCTION ?
431 : : errdetail("\"%s\" is a function.", procedureName) :
432 : : oldproc->prokind == PROKIND_PROCEDURE ?
433 : : errdetail("\"%s\" is a procedure.", procedureName) :
434 : : oldproc->prokind == PROKIND_WINDOW ?
435 : : errdetail("\"%s\" is a window function.", procedureName) :
436 : : 0)));
437 : :
438 [ + + + + ]: 3263 : dropcmd = (prokind == PROKIND_PROCEDURE ? "DROP PROCEDURE" :
439 : : prokind == PROKIND_AGGREGATE ? "DROP AGGREGATE" :
440 : : "DROP FUNCTION");
441 : :
442 : : /*
443 : : * Not okay to change the return type of the existing proc, since
444 : : * existing rules, views, etc may depend on the return type.
445 : : *
446 : : * In case of a procedure, a changing return type means that whether
447 : : * the procedure has output parameters was changed. Since there is no
448 : : * user visible return type, we produce a more specific error message.
449 : : */
450 [ + + ]: 3263 : if (returnType != oldproc->prorettype ||
451 [ - + ]: 3255 : returnsSet != oldproc->proretset)
452 [ + - - + ]: 8 : ereport(ERROR,
453 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
454 : : prokind == PROKIND_PROCEDURE
455 : : ? errmsg("cannot change whether a procedure has output parameters")
456 : : : errmsg("cannot change return type of existing function"),
457 : :
458 : : /*
459 : : * translator: first %s is DROP FUNCTION, DROP PROCEDURE, or DROP
460 : : * AGGREGATE
461 : : */
462 : : errhint("Use %s %s first.",
463 : : dropcmd,
464 : : format_procedure(oldproc->oid))));
465 : :
466 : : /*
467 : : * If it returns RECORD, check for possible change of record type
468 : : * implied by OUT parameters
469 : : */
470 [ + + ]: 3255 : if (returnType == RECORDOID)
471 : : {
472 : : TupleDesc olddesc;
473 : : TupleDesc newdesc;
474 : :
475 : 175 : olddesc = build_function_result_tupdesc_t(oldtup);
476 : 175 : newdesc = build_function_result_tupdesc_d(prokind,
477 : : allParameterTypes,
478 : : parameterModes,
479 : : parameterNames);
480 [ + + + - ]: 175 : if (olddesc == NULL && newdesc == NULL)
481 : : /* ok, both are runtime-defined RECORDs */ ;
482 [ + - + - ]: 161 : else if (olddesc == NULL || newdesc == NULL ||
483 [ - + ]: 161 : !equalRowTypes(olddesc, newdesc))
484 [ # # ]: 0 : ereport(ERROR,
485 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
486 : : errmsg("cannot change return type of existing function"),
487 : : errdetail("Row type defined by OUT parameters is different."),
488 : : /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */
489 : : errhint("Use %s %s first.",
490 : : dropcmd,
491 : : format_procedure(oldproc->oid))));
492 : : }
493 : :
494 : : /*
495 : : * If there were any named input parameters, check to make sure the
496 : : * names have not been changed, as this could break existing calls. We
497 : : * allow adding names to formerly unnamed parameters, though.
498 : : */
499 : 3255 : proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
500 : : Anum_pg_proc_proargnames,
501 : : &isnull);
502 [ + + ]: 3255 : if (!isnull)
503 : : {
504 : : Datum proargmodes;
505 : : char **old_arg_names;
506 : : char **new_arg_names;
507 : : int n_old_arg_names;
508 : : int n_new_arg_names;
509 : : int j;
510 : :
511 : 213 : proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
512 : : Anum_pg_proc_proargmodes,
513 : : &isnull);
514 [ + + ]: 213 : if (isnull)
515 : 40 : proargmodes = PointerGetDatum(NULL); /* just to be sure */
516 : :
517 : 213 : n_old_arg_names = get_func_input_arg_names(proargnames,
518 : : proargmodes,
519 : : &old_arg_names);
520 : 213 : n_new_arg_names = get_func_input_arg_names(parameterNames,
521 : : parameterModes,
522 : : &new_arg_names);
523 [ + + ]: 539 : for (j = 0; j < n_old_arg_names; j++)
524 : : {
525 [ + + ]: 338 : if (old_arg_names[j] == NULL)
526 : 4 : continue;
527 [ + - + + ]: 334 : if (j >= n_new_arg_names || new_arg_names[j] == NULL ||
528 [ + + ]: 330 : strcmp(old_arg_names[j], new_arg_names[j]) != 0)
529 [ + - ]: 12 : ereport(ERROR,
530 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
531 : : errmsg("cannot change name of input parameter \"%s\"",
532 : : old_arg_names[j]),
533 : : /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */
534 : : errhint("Use %s %s first.",
535 : : dropcmd,
536 : : format_procedure(oldproc->oid))));
537 : : }
538 : : }
539 : :
540 : : /*
541 : : * If there are existing defaults, check compatibility: redefinition
542 : : * must not remove any defaults nor change their types. (Removing a
543 : : * default might cause a function to fail to satisfy an existing call.
544 : : * Changing type would only be possible if the associated parameter is
545 : : * polymorphic, and in such cases a change of default type might alter
546 : : * the resolved output type of existing calls.)
547 : : */
548 [ + + ]: 3243 : if (oldproc->pronargdefaults != 0)
549 : : {
550 : : Datum proargdefaults;
551 : : List *oldDefaults;
552 : : ListCell *oldlc;
553 : : ListCell *newlc;
554 : :
555 [ + - ]: 4 : if (list_length(parameterDefaults) < oldproc->pronargdefaults)
556 [ + - ]: 4 : ereport(ERROR,
557 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
558 : : errmsg("cannot remove parameter defaults from existing function"),
559 : : /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */
560 : : errhint("Use %s %s first.",
561 : : dropcmd,
562 : : format_procedure(oldproc->oid))));
563 : :
564 : 0 : proargdefaults = SysCacheGetAttrNotNull(PROCNAMEARGSNSP, oldtup,
565 : : Anum_pg_proc_proargdefaults);
566 : 0 : oldDefaults = castNode(List, stringToNode(TextDatumGetCString(proargdefaults)));
567 : : Assert(list_length(oldDefaults) == oldproc->pronargdefaults);
568 : :
569 : : /* new list can have more defaults than old, advance over 'em */
570 : 0 : newlc = list_nth_cell(parameterDefaults,
571 : 0 : list_length(parameterDefaults) -
572 : 0 : oldproc->pronargdefaults);
573 : :
574 [ # # # # : 0 : foreach(oldlc, oldDefaults)
# # ]
575 : : {
576 : 0 : Node *oldDef = (Node *) lfirst(oldlc);
577 : 0 : Node *newDef = (Node *) lfirst(newlc);
578 : :
579 [ # # ]: 0 : if (exprType(oldDef) != exprType(newDef))
580 [ # # ]: 0 : ereport(ERROR,
581 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
582 : : errmsg("cannot change data type of existing parameter default value"),
583 : : /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */
584 : : errhint("Use %s %s first.",
585 : : dropcmd,
586 : : format_procedure(oldproc->oid))));
587 : 0 : newlc = lnext(parameterDefaults, newlc);
588 : : }
589 : : }
590 : :
591 : : /*
592 : : * Do not change existing oid, ownership or permissions, either. Note
593 : : * dependency-update code below has to agree with this decision.
594 : : */
595 : 3239 : replaces[Anum_pg_proc_oid - 1] = false;
596 : 3239 : replaces[Anum_pg_proc_proowner - 1] = false;
597 : 3239 : replaces[Anum_pg_proc_proacl - 1] = false;
598 : :
599 : : /* Okay, do it... */
600 : 3239 : tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
601 : 3239 : CatalogTupleUpdate(rel, &tup->t_self, tup);
602 : :
603 : 3239 : ReleaseSysCache(oldtup);
604 : 3239 : is_update = true;
605 : : }
606 : : else
607 : : {
608 : : /* Creating a new procedure */
609 : : Oid newOid;
610 : :
611 : : /* First, get default permissions and set up proacl */
612 : 11760 : proacl = get_user_default_acl(OBJECT_FUNCTION, proowner,
613 : : procNamespace);
614 [ + + ]: 11760 : if (proacl != NULL)
615 : 12 : values[Anum_pg_proc_proacl - 1] = PointerGetDatum(proacl);
616 : : else
617 : 11748 : nulls[Anum_pg_proc_proacl - 1] = true;
618 : :
619 : 11760 : newOid = GetNewOidWithIndex(rel, ProcedureOidIndexId,
620 : : Anum_pg_proc_oid);
621 : 11760 : values[Anum_pg_proc_oid - 1] = ObjectIdGetDatum(newOid);
622 : 11760 : tup = heap_form_tuple(tupDesc, values, nulls);
623 : 11760 : CatalogTupleInsert(rel, tup);
624 : 11760 : is_update = false;
625 : : }
626 : :
627 : :
628 : 14999 : retval = ((Form_pg_proc) GETSTRUCT(tup))->oid;
629 : :
630 : : /*
631 : : * Create dependencies for the new function. If we are updating an
632 : : * existing function, first delete any existing pg_depend entries.
633 : : * (However, since we are not changing ownership or permissions, the
634 : : * shared dependencies do *not* need to change, and we leave them alone.)
635 : : */
636 [ + + ]: 14999 : if (is_update)
637 : 3239 : deleteDependencyRecordsFor(ProcedureRelationId, retval, true);
638 : :
639 : 14999 : addrs = new_object_addresses();
640 : :
641 : 14999 : ObjectAddressSet(myself, ProcedureRelationId, retval);
642 : :
643 : : /* dependency on namespace */
644 : 14999 : ObjectAddressSet(referenced, NamespaceRelationId, procNamespace);
645 : 14999 : add_exact_object_address(&referenced, addrs);
646 : :
647 : : /* dependency on implementation language */
648 : 14999 : ObjectAddressSet(referenced, LanguageRelationId, languageObjectId);
649 : 14999 : add_exact_object_address(&referenced, addrs);
650 : :
651 : : /* dependency on return type */
652 : 14999 : ObjectAddressSet(referenced, TypeRelationId, returnType);
653 : 14999 : add_exact_object_address(&referenced, addrs);
654 : :
655 : : /* dependency on parameter types */
656 [ + + ]: 43279 : for (i = 0; i < allParamCount; i++)
657 : : {
658 : 28280 : ObjectAddressSet(referenced, TypeRelationId, allParams[i]);
659 : 28280 : add_exact_object_address(&referenced, addrs);
660 : : }
661 : :
662 : : /* dependency on transforms, if any */
663 [ + + + + : 30076 : foreach_oid(transformid, trfoids)
+ + ]
664 : : {
665 : 78 : ObjectAddressSet(referenced, TransformRelationId, transformid);
666 : 78 : add_exact_object_address(&referenced, addrs);
667 : : }
668 : :
669 : : /* dependency on support function, if any */
670 [ + + ]: 14999 : if (OidIsValid(prosupport))
671 : : {
672 : 70 : ObjectAddressSet(referenced, ProcedureRelationId, prosupport);
673 : 70 : add_exact_object_address(&referenced, addrs);
674 : : }
675 : :
676 : : /* dependencies appearing in new-style SQL routine body */
677 [ + + + + ]: 14999 : if (languageObjectId == SQLlanguageId && prosqlbody)
678 : : {
679 : 3436 : CheckUsageOnTypesInExpr(prosqlbody, NIL, GetUserId());
680 : 3436 : collectDependenciesOfExpr(addrs, prosqlbody, NIL);
681 : : }
682 : :
683 : : /* dependency on parameter default expressions */
684 [ + + ]: 14999 : if (parameterDefaults)
685 : : {
686 : 461 : CheckUsageOnTypesInExpr((Node *) parameterDefaults, NIL, GetUserId());
687 : 457 : collectDependenciesOfExpr(addrs, (Node *) parameterDefaults, NIL);
688 : : }
689 : :
690 : : /*
691 : : * Now that we have all the normal dependencies, thumb through them and
692 : : * warn if any are to temporary objects. This informs the user if their
693 : : * supposedly non-temp function will silently go away at session exit, due
694 : : * to a dependency on a temp object. However, do not complain when a
695 : : * function created in our own pg_temp namespace refers to other objects
696 : : * in that namespace, since then they'll have similar lifespans anyway.
697 : : */
698 [ + + ]: 14995 : if (find_temp_object(addrs, isTempNamespace(procNamespace), &temp_object))
699 [ + - ]: 43 : ereport(NOTICE,
700 : : (errmsg("function \"%s\" will be effectively temporary",
701 : : procedureName),
702 : : errdetail("It depends on temporary %s.",
703 : : getObjectDescription(&temp_object, false))));
704 : :
705 : : /*
706 : : * Now record all normal dependencies at once. This will also remove any
707 : : * duplicates in the list. (Role and extension dependencies are handled
708 : : * separately below. Role dependencies would have to be separate anyway
709 : : * since they are shared dependencies. An extension dependency could be
710 : : * folded into the addrs list, but pg_depend.c doesn't make that easy, and
711 : : * it won't duplicate anything we've collected so far anyway.)
712 : : */
713 : 14995 : record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
714 : :
715 : 14991 : free_object_addresses(addrs);
716 : :
717 : : /* dependency on owner */
718 [ + + ]: 14991 : if (!is_update)
719 : 11752 : recordDependencyOnOwner(ProcedureRelationId, retval, proowner);
720 : :
721 : : /* dependency on any roles mentioned in ACL */
722 [ + + ]: 14991 : if (!is_update)
723 : 11752 : recordDependencyOnNewAcl(ProcedureRelationId, retval, 0,
724 : : proowner, proacl);
725 : :
726 : : /* dependency on extension */
727 : 14991 : recordDependencyOnCurrentExtension(&myself, is_update);
728 : :
729 : 14990 : heap_freetuple(tup);
730 : :
731 : : /* Post creation hook for new function */
732 [ + + ]: 14990 : InvokeObjectPostCreateHook(ProcedureRelationId, retval, 0);
733 : :
734 : 14990 : table_close(rel, RowExclusiveLock);
735 : :
736 : : /* Verify function body */
737 [ + + ]: 14990 : if (OidIsValid(languageValidator))
738 : : {
739 : 14556 : ArrayType *set_items = NULL;
740 : 14556 : int save_nestlevel = 0;
741 : :
742 : : /* Advance command counter so new tuple can be seen by validator */
743 : 14556 : CommandCounterIncrement();
744 : :
745 : : /*
746 : : * Set per-function configuration parameters so that the validation is
747 : : * done with the environment the function expects. However, if
748 : : * check_function_bodies is off, we don't do this, because that would
749 : : * create dump ordering hazards that pg_dump doesn't know how to deal
750 : : * with. (For example, a SET clause might refer to a not-yet-created
751 : : * text search configuration.) This means that the validator
752 : : * shouldn't complain about anything that might depend on a GUC
753 : : * parameter when check_function_bodies is off.
754 : : */
755 [ + + ]: 14556 : if (check_function_bodies)
756 : : {
757 : 9714 : set_items = (ArrayType *) DatumGetPointer(proconfig);
758 [ + + ]: 9714 : if (set_items) /* Need a new GUC nesting level */
759 : : {
760 : 64 : save_nestlevel = NewGUCNestLevel();
761 [ + - ]: 64 : ProcessGUCArray(set_items,
762 : 64 : (superuser() ? PGC_SUSET : PGC_USERSET),
763 : : PGC_S_SESSION,
764 : : GUC_ACTION_SAVE);
765 : : }
766 : : }
767 : :
768 : 14548 : OidFunctionCall1(languageValidator, ObjectIdGetDatum(retval));
769 : :
770 [ + + ]: 14408 : if (set_items)
771 : 56 : AtEOXact_GUC(true, save_nestlevel);
772 : : }
773 : :
774 : : /* ensure that stats are dropped if transaction aborts */
775 [ + + ]: 14842 : if (!is_update)
776 : 11604 : pgstat_create_function(retval);
777 : :
778 : 14842 : return myself;
779 : : }
780 : :
781 : :
782 : :
783 : : /*
784 : : * Validator for internal functions
785 : : *
786 : : * Check that the given internal function name (the "prosrc" value) is
787 : : * a known builtin function.
788 : : */
789 : : Datum
790 : 1177 : fmgr_internal_validator(PG_FUNCTION_ARGS)
791 : : {
792 : 1177 : Oid funcoid = PG_GETARG_OID(0);
793 : : HeapTuple tuple;
794 : : Datum tmp;
795 : : char *prosrc;
796 : :
797 [ - + ]: 1177 : if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
798 : 0 : PG_RETURN_VOID();
799 : :
800 : : /*
801 : : * We do not honor check_function_bodies since it's unlikely the function
802 : : * name will be found later if it isn't there now.
803 : : */
804 : :
805 : 1177 : tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
806 [ - + ]: 1177 : if (!HeapTupleIsValid(tuple))
807 [ # # ]: 0 : elog(ERROR, "cache lookup failed for function %u", funcoid);
808 : :
809 : 1177 : tmp = SysCacheGetAttrNotNull(PROCOID, tuple, Anum_pg_proc_prosrc);
810 : 1177 : prosrc = TextDatumGetCString(tmp);
811 : :
812 [ + + ]: 1177 : if (fmgr_internal_function(prosrc) == InvalidOid)
813 [ + - ]: 4 : ereport(ERROR,
814 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
815 : : errmsg("there is no built-in function named \"%s\"",
816 : : prosrc)));
817 : :
818 : 1173 : ReleaseSysCache(tuple);
819 : :
820 : 1173 : PG_RETURN_VOID();
821 : : }
822 : :
823 : :
824 : :
825 : : /*
826 : : * Validator for C language functions
827 : : *
828 : : * Make sure that the library file exists, is loadable, and contains
829 : : * the specified link symbol. Also check for a valid function
830 : : * information record.
831 : : */
832 : : Datum
833 : 4414 : fmgr_c_validator(PG_FUNCTION_ARGS)
834 : : {
835 : 4414 : Oid funcoid = PG_GETARG_OID(0);
836 : : void *libraryhandle;
837 : : HeapTuple tuple;
838 : : Datum tmp;
839 : : char *prosrc;
840 : : char *probin;
841 : :
842 [ - + ]: 4414 : if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
843 : 0 : PG_RETURN_VOID();
844 : :
845 : : /*
846 : : * It'd be most consistent to skip the check if !check_function_bodies,
847 : : * but the purpose of that switch is to be helpful for pg_dump loading,
848 : : * and for pg_dump loading it's much better if we *do* check.
849 : : */
850 : :
851 : 4414 : tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
852 [ - + ]: 4414 : if (!HeapTupleIsValid(tuple))
853 [ # # ]: 0 : elog(ERROR, "cache lookup failed for function %u", funcoid);
854 : :
855 : 4414 : tmp = SysCacheGetAttrNotNull(PROCOID, tuple, Anum_pg_proc_prosrc);
856 : 4414 : prosrc = TextDatumGetCString(tmp);
857 : :
858 : 4414 : tmp = SysCacheGetAttrNotNull(PROCOID, tuple, Anum_pg_proc_probin);
859 : 4414 : probin = TextDatumGetCString(tmp);
860 : :
861 : 4414 : (void) load_external_function(probin, prosrc, true, &libraryhandle);
862 : 4406 : (void) fetch_finfo_record(libraryhandle, prosrc);
863 : :
864 : 4406 : ReleaseSysCache(tuple);
865 : :
866 : 4406 : PG_RETURN_VOID();
867 : : }
868 : :
869 : :
870 : : /*
871 : : * Validator for SQL language functions
872 : : *
873 : : * Parse it here in order to be sure that it contains no syntax errors.
874 : : */
875 : : Datum
876 : 5373 : fmgr_sql_validator(PG_FUNCTION_ARGS)
877 : : {
878 : 5373 : Oid funcoid = PG_GETARG_OID(0);
879 : : HeapTuple tuple;
880 : : Form_pg_proc proc;
881 : : List *raw_parsetree_list;
882 : : List *querytree_list;
883 : : ListCell *lc;
884 : : bool isnull;
885 : : Datum tmp;
886 : : char *prosrc;
887 : : parse_error_callback_arg callback_arg;
888 : : ErrorContextCallback sqlerrcontext;
889 : : bool haspolyarg;
890 : : Oid *argtypes;
891 : : char **argnames;
892 : : char *argmodes;
893 : : int numargs;
894 : : int i;
895 : :
896 [ - + ]: 5373 : if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
897 : 0 : PG_RETURN_VOID();
898 : :
899 : 5373 : tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
900 [ - + ]: 5373 : if (!HeapTupleIsValid(tuple))
901 [ # # ]: 0 : elog(ERROR, "cache lookup failed for function %u", funcoid);
902 : 5373 : proc = (Form_pg_proc) GETSTRUCT(tuple);
903 : :
904 : : /* Disallow pseudotype result */
905 : : /* except for RECORD, VOID, or polymorphic */
906 [ + + ]: 5373 : if (get_typtype(proc->prorettype) == TYPTYPE_PSEUDO &&
907 [ + + ]: 996 : proc->prorettype != RECORDOID &&
908 [ + + ]: 528 : proc->prorettype != VOIDOID &&
909 [ + + + + : 245 : !IsPolymorphicType(proc->prorettype))
+ - + - +
+ + + + +
+ + + - +
+ + + ]
910 [ + - ]: 4 : ereport(ERROR,
911 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
912 : : errmsg("SQL functions cannot return type %s",
913 : : format_type_be(proc->prorettype))));
914 : :
915 : : /* Disallow pseudotypes in arguments (either IN or OUT) */
916 : : /* except for polymorphic, or RECORD/RECORD[] as an output column type */
917 : 5369 : haspolyarg = false;
918 : 5369 : numargs = get_func_arg_info(tuple, &argtypes, &argnames, &argmodes);
919 [ + + ]: 16324 : for (i = 0; i < numargs; i++)
920 : : {
921 [ + + ]: 10975 : char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
922 : :
923 [ + + ]: 10975 : if (get_typtype(argtypes[i]) != TYPTYPE_PSEUDO)
924 : 10108 : continue;
925 : :
926 [ + + + + : 867 : if (IsPolymorphicType(argtypes[i]))
+ - + - +
+ + + + +
+ + + + +
+ + + ]
927 : : {
928 : 839 : haspolyarg = true;
929 : 839 : continue;
930 : : }
931 : :
932 [ + + + + : 28 : if ((argtypes[i] == RECORDOID || argtypes[i] == RECORDARRAYOID) &&
+ + ]
933 [ - + ]: 4 : (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE))
934 : 8 : continue;
935 : :
936 [ + - ]: 20 : ereport(ERROR,
937 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
938 : : errmsg("SQL functions cannot have arguments of type %s",
939 : : format_type_be(argtypes[i]))));
940 : : }
941 : :
942 : : /* Postpone body checks if !check_function_bodies */
943 [ + + ]: 5349 : if (check_function_bodies)
944 : : {
945 : 5084 : tmp = SysCacheGetAttrNotNull(PROCOID, tuple, Anum_pg_proc_prosrc);
946 : 5084 : prosrc = TextDatumGetCString(tmp);
947 : :
948 : : /*
949 : : * Setup error traceback support for ereport().
950 : : */
951 : 5084 : callback_arg.proname = NameStr(proc->proname);
952 : 5084 : callback_arg.prosrc = prosrc;
953 : :
954 : 5084 : sqlerrcontext.callback = sql_function_parse_error_callback;
955 : 5084 : sqlerrcontext.arg = &callback_arg;
956 : 5084 : sqlerrcontext.previous = error_context_stack;
957 : 5084 : error_context_stack = &sqlerrcontext;
958 : :
959 : : /* If we have prosqlbody, pay attention to that not prosrc */
960 : 5084 : tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosqlbody, &isnull);
961 [ + + ]: 5084 : if (!isnull)
962 : : {
963 : : Node *n;
964 : : List *stored_query_list;
965 : :
966 : 3374 : n = stringToNode(TextDatumGetCString(tmp));
967 [ + + ]: 3374 : if (IsA(n, List))
968 : 506 : stored_query_list = linitial(castNode(List, n));
969 : : else
970 : 2868 : stored_query_list = list_make1(n);
971 : :
972 : 3374 : querytree_list = NIL;
973 [ + + + + : 6748 : foreach(lc, stored_query_list)
+ + ]
974 : : {
975 : 3374 : Query *parsetree = lfirst_node(Query, lc);
976 : : List *querytree_sublist;
977 : :
978 : : /*
979 : : * Typically, we'd have acquired locks already while parsing
980 : : * the body of the CREATE FUNCTION command. However, a
981 : : * validator function cannot assume that it's only called in
982 : : * that context.
983 : : */
984 : 3374 : AcquireRewriteLocks(parsetree, true, false);
985 : 3374 : querytree_sublist = pg_rewrite_query(parsetree);
986 : 3374 : querytree_list = lappend(querytree_list, querytree_sublist);
987 : : }
988 : : }
989 : : else
990 : : {
991 : : /*
992 : : * We can't do full prechecking of the function definition if
993 : : * there are any polymorphic input types, because actual datatypes
994 : : * of expression results will be unresolvable. The check will be
995 : : * done at runtime instead.
996 : : *
997 : : * We can run the text through the raw parser though; this will at
998 : : * least catch silly syntactic errors.
999 : : */
1000 : 1710 : raw_parsetree_list = pg_parse_query(prosrc);
1001 : 1705 : querytree_list = NIL;
1002 : :
1003 [ + + ]: 1705 : if (!haspolyarg)
1004 : : {
1005 : : /*
1006 : : * OK to do full precheck: analyze and rewrite the queries,
1007 : : * then verify the result type.
1008 : : */
1009 : : SQLFunctionParseInfoPtr pinfo;
1010 : :
1011 : : /* But first, set up parameter information */
1012 : 1289 : pinfo = prepare_sql_fn_parse_info(tuple, NULL, InvalidOid);
1013 : :
1014 [ + + + + : 2653 : foreach(lc, raw_parsetree_list)
+ + ]
1015 : : {
1016 : 1372 : RawStmt *parsetree = lfirst_node(RawStmt, lc);
1017 : : List *querytree_sublist;
1018 : :
1019 : 1372 : querytree_sublist = pg_analyze_and_rewrite_withcb(parsetree,
1020 : : prosrc,
1021 : : (ParserSetupHook) sql_fn_parser_setup,
1022 : : pinfo,
1023 : : NULL);
1024 : 1364 : querytree_list = lappend(querytree_list,
1025 : : querytree_sublist);
1026 : : }
1027 : : }
1028 : : }
1029 : :
1030 [ + + ]: 5071 : if (!haspolyarg)
1031 : : {
1032 : : Oid rettype;
1033 : : TupleDesc rettupdesc;
1034 : :
1035 : 4655 : check_sql_fn_statements(querytree_list);
1036 : :
1037 : 4651 : (void) get_func_result_type(funcoid, &rettype, &rettupdesc);
1038 : :
1039 : 4651 : (void) check_sql_fn_retval(querytree_list,
1040 : : rettype, rettupdesc,
1041 : 4651 : proc->prokind,
1042 : : false);
1043 : : }
1044 : :
1045 : 5055 : error_context_stack = sqlerrcontext.previous;
1046 : : }
1047 : :
1048 : 5320 : ReleaseSysCache(tuple);
1049 : :
1050 : 5320 : PG_RETURN_VOID();
1051 : : }
1052 : :
1053 : : /*
1054 : : * Error context callback for handling errors in SQL function definitions
1055 : : */
1056 : : static void
1057 : 29 : sql_function_parse_error_callback(void *arg)
1058 : : {
1059 : 29 : parse_error_callback_arg *callback_arg = (parse_error_callback_arg *) arg;
1060 : :
1061 : : /* See if it's a syntax error; if so, transpose to CREATE FUNCTION */
1062 [ + + ]: 29 : if (!function_parse_error_transpose(callback_arg->prosrc))
1063 : : {
1064 : : /* If it's not a syntax error, push info onto context stack */
1065 : 16 : errcontext("SQL function \"%s\"", callback_arg->proname);
1066 : : }
1067 : 29 : }
1068 : :
1069 : : /*
1070 : : * Adjust a syntax error occurring inside the function body of a CREATE
1071 : : * FUNCTION or DO command. This can be used by any function validator or
1072 : : * anonymous-block handler, not only for SQL-language functions.
1073 : : * It is assumed that the syntax error position is initially relative to the
1074 : : * function body string (as passed in). If possible, we adjust the position
1075 : : * to reference the original command text; if we can't manage that, we set
1076 : : * up an "internal query" syntax error instead.
1077 : : *
1078 : : * Returns true if a syntax error was processed, false if not.
1079 : : */
1080 : : bool
1081 : 161 : function_parse_error_transpose(const char *prosrc)
1082 : : {
1083 : : int origerrposition;
1084 : : int newerrposition;
1085 : :
1086 : : /*
1087 : : * Nothing to do unless we are dealing with a syntax error that has a
1088 : : * cursor position.
1089 : : *
1090 : : * Some PLs may prefer to report the error position as an internal error
1091 : : * to begin with, so check that too.
1092 : : */
1093 : 161 : origerrposition = geterrposition();
1094 [ + + ]: 161 : if (origerrposition <= 0)
1095 : : {
1096 : 148 : origerrposition = getinternalerrposition();
1097 [ + + ]: 148 : if (origerrposition <= 0)
1098 : 42 : return false;
1099 : : }
1100 : :
1101 : : /* We can get the original query text from the active portal (hack...) */
1102 [ + - + - ]: 119 : if (ActivePortal && ActivePortal->status == PORTAL_ACTIVE)
1103 : 119 : {
1104 : 119 : const char *queryText = ActivePortal->sourceText;
1105 : :
1106 : : /* Try to locate the prosrc in the original text */
1107 : 119 : newerrposition = match_prosrc_to_query(prosrc, queryText,
1108 : : origerrposition);
1109 : : }
1110 : : else
1111 : : {
1112 : : /*
1113 : : * Quietly give up if no ActivePortal. This is an unusual situation
1114 : : * but it can happen in, e.g., logical replication workers.
1115 : : */
1116 : 0 : newerrposition = -1;
1117 : : }
1118 : :
1119 [ + + ]: 119 : if (newerrposition > 0)
1120 : : {
1121 : : /* Successful, so fix error position to reference original query */
1122 : 118 : errposition(newerrposition);
1123 : : /* Get rid of any report of the error as an "internal query" */
1124 : 118 : internalerrposition(0);
1125 : 118 : internalerrquery(NULL);
1126 : : }
1127 : : else
1128 : : {
1129 : : /*
1130 : : * If unsuccessful, convert the position to an internal position
1131 : : * marker and give the function text as the internal query.
1132 : : */
1133 : 1 : errposition(0);
1134 : 1 : internalerrposition(origerrposition);
1135 : 1 : internalerrquery(prosrc);
1136 : : }
1137 : :
1138 : 119 : return true;
1139 : : }
1140 : :
1141 : : /*
1142 : : * Try to locate the string literal containing the function body in the
1143 : : * given text of the CREATE FUNCTION or DO command. If successful, return
1144 : : * the character (not byte) index within the command corresponding to the
1145 : : * given character index within the literal. If not successful, return 0.
1146 : : */
1147 : : static int
1148 : 119 : match_prosrc_to_query(const char *prosrc, const char *queryText,
1149 : : int cursorpos)
1150 : : {
1151 : : /*
1152 : : * Rather than fully parsing the original command, we just scan the
1153 : : * command looking for $prosrc$ or 'prosrc'. This could be fooled (though
1154 : : * not in any very probable scenarios), so fail if we find more than one
1155 : : * match.
1156 : : */
1157 : 119 : int prosrclen = strlen(prosrc);
1158 : 119 : int querylen = strlen(queryText);
1159 : 119 : int matchpos = 0;
1160 : : int curpos;
1161 : : int newcursorpos;
1162 : :
1163 [ + + ]: 8207 : for (curpos = 0; curpos < querylen - prosrclen; curpos++)
1164 : : {
1165 [ + + ]: 8088 : if (queryText[curpos] == '$' &&
1166 [ + + ]: 220 : strncmp(prosrc, &queryText[curpos + 1], prosrclen) == 0 &&
1167 [ + - ]: 110 : queryText[curpos + 1 + prosrclen] == '$')
1168 : : {
1169 : : /*
1170 : : * Found a $foo$ match. Since there are no embedded quoting
1171 : : * characters in a dollar-quoted literal, we don't have to do any
1172 : : * fancy arithmetic; just offset by the starting position.
1173 : : */
1174 [ - + ]: 110 : if (matchpos)
1175 : 0 : return 0; /* multiple matches, fail */
1176 : 110 : matchpos = pg_mbstrlen_with_len(queryText, curpos + 1)
1177 : : + cursorpos;
1178 : : }
1179 [ + + + - ]: 7986 : else if (queryText[curpos] == '\'' &&
1180 : 8 : match_prosrc_to_literal(prosrc, &queryText[curpos + 1],
1181 : : cursorpos, &newcursorpos))
1182 : : {
1183 : : /*
1184 : : * Found a 'foo' match. match_prosrc_to_literal() has adjusted
1185 : : * for any quotes or backslashes embedded in the literal.
1186 : : */
1187 [ - + ]: 8 : if (matchpos)
1188 : 0 : return 0; /* multiple matches, fail */
1189 : 8 : matchpos = pg_mbstrlen_with_len(queryText, curpos + 1)
1190 : 8 : + newcursorpos;
1191 : : }
1192 : : }
1193 : :
1194 : 119 : return matchpos;
1195 : : }
1196 : :
1197 : : /*
1198 : : * Try to match the given source text to a single-quoted literal.
1199 : : * If successful, adjust newcursorpos to correspond to the character
1200 : : * (not byte) index corresponding to cursorpos in the source text.
1201 : : *
1202 : : * At entry, literal points just past a ' character. We must check for the
1203 : : * trailing quote.
1204 : : */
1205 : : static bool
1206 : 8 : match_prosrc_to_literal(const char *prosrc, const char *literal,
1207 : : int cursorpos, int *newcursorpos)
1208 : : {
1209 : 8 : int newcp = cursorpos;
1210 : : int chlen;
1211 : :
1212 : : /*
1213 : : * This implementation handles backslashes and doubled quotes in the
1214 : : * string literal. It does not handle the SQL syntax for literals
1215 : : * continued across line boundaries.
1216 : : *
1217 : : * We do the comparison a character at a time, not a byte at a time, so
1218 : : * that we can do the correct cursorpos math.
1219 : : */
1220 [ + + ]: 96 : while (*prosrc)
1221 : : {
1222 : 88 : cursorpos--; /* characters left before cursor */
1223 : :
1224 : : /*
1225 : : * Check for backslashes and doubled quotes in the literal; adjust
1226 : : * newcp when one is found before the cursor.
1227 : : */
1228 [ - + ]: 88 : if (*literal == '\\')
1229 : : {
1230 : 0 : literal++;
1231 [ # # ]: 0 : if (cursorpos > 0)
1232 : 0 : newcp++;
1233 : : }
1234 [ - + ]: 88 : else if (*literal == '\'')
1235 : : {
1236 [ # # ]: 0 : if (literal[1] != '\'')
1237 : 0 : goto fail;
1238 : 0 : literal++;
1239 [ # # ]: 0 : if (cursorpos > 0)
1240 : 0 : newcp++;
1241 : : }
1242 : 88 : chlen = pg_mblen_cstr(prosrc);
1243 [ - + ]: 88 : if (strncmp(prosrc, literal, chlen) != 0)
1244 : 0 : goto fail;
1245 : 88 : prosrc += chlen;
1246 : 88 : literal += chlen;
1247 : : }
1248 : :
1249 [ + - - + ]: 8 : if (*literal == '\'' && literal[1] != '\'')
1250 : : {
1251 : : /* success */
1252 : 8 : *newcursorpos = newcp;
1253 : 8 : return true;
1254 : : }
1255 : :
1256 : 0 : fail:
1257 : : /* Must set *newcursorpos to suppress compiler warning */
1258 : 0 : *newcursorpos = newcp;
1259 : 0 : return false;
1260 : : }
1261 : :
1262 : : List *
1263 : 74 : oid_array_to_list(Datum datum)
1264 : : {
1265 : 74 : ArrayType *array = DatumGetArrayTypeP(datum);
1266 : : Datum *values;
1267 : : int nelems;
1268 : : int i;
1269 : 74 : List *result = NIL;
1270 : :
1271 : 74 : deconstruct_array_builtin(array, OIDOID, &values, NULL, &nelems);
1272 [ + + ]: 150 : for (i = 0; i < nelems; i++)
1273 : 76 : result = lappend_oid(result, DatumGetObjectId(values[i]));
1274 : 74 : return result;
1275 : : }
|