1
/*-------------------------------------------------------------------------
4
* routines to support manipulation of the pg_proc relation
6
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7
* Portions Copyright (c) 1994, Regents of the University of California
11
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.122.4.1 2005-05-03 16:51:22 tgl Exp $
13
*-------------------------------------------------------------------------
17
#include "access/heapam.h"
18
#include "catalog/catname.h"
19
#include "catalog/dependency.h"
20
#include "catalog/indexing.h"
21
#include "catalog/pg_proc.h"
22
#include "catalog/pg_type.h"
23
#include "executor/functions.h"
24
#include "miscadmin.h"
25
#include "mb/pg_wchar.h"
26
#include "parser/parse_type.h"
27
#include "tcop/pquery.h"
28
#include "tcop/tcopprot.h"
29
#include "utils/acl.h"
30
#include "utils/builtins.h"
31
#include "utils/lsyscache.h"
32
#include "utils/syscache.h"
36
bool check_function_bodies = true;
39
Datum fmgr_internal_validator(PG_FUNCTION_ARGS);
40
Datum fmgr_c_validator(PG_FUNCTION_ARGS);
41
Datum fmgr_sql_validator(PG_FUNCTION_ARGS);
43
static Datum create_parameternames_array(int parameterCount,
44
const char *parameterNames[]);
45
static void sql_function_parse_error_callback(void *arg);
46
static int match_prosrc_to_query(const char *prosrc, const char *queryText,
48
static bool match_prosrc_to_literal(const char *prosrc, const char *literal,
49
int cursorpos, int *newcursorpos);
52
/* ----------------------------------------------------------------
54
* ----------------------------------------------------------------
57
ProcedureCreate(const char *procedureName,
63
Oid languageValidator,
67
bool security_definer,
71
const Oid *parameterTypes,
72
const char *parameterNames[])
75
bool genericParam = false;
76
bool internalParam = false;
80
char nulls[Natts_pg_proc];
81
Datum values[Natts_pg_proc];
82
char replaces[Natts_pg_proc];
83
Oid typev[FUNC_MAX_ARGS];
96
Assert(PointerIsValid(prosrc));
97
Assert(PointerIsValid(probin));
99
if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS)
101
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
102
errmsg("functions cannot have more than %d arguments",
106
* Do not allow return type ANYARRAY or ANYELEMENT unless at least one
107
* input argument is ANYARRAY or ANYELEMENT. Also, do not allow
108
* return type INTERNAL unless at least one input argument is INTERNAL.
110
for (i = 0; i < parameterCount; i++)
112
switch (parameterTypes[i])
119
internalParam = true;
124
if ((returnType == ANYARRAYOID || returnType == ANYELEMENTOID)
127
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
128
errmsg("cannot determine result data type"),
129
errdetail("A function returning \"anyarray\" or \"anyelement\" must have at least one argument of either type.")));
131
if (returnType == INTERNALOID && !internalParam)
133
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
134
errmsg("unsafe use of INTERNAL pseudo-type"),
135
errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));
137
/* Make sure we have a zero-padded param type array */
138
MemSet(typev, 0, FUNC_MAX_ARGS * sizeof(Oid));
139
if (parameterCount > 0)
140
memcpy(typev, parameterTypes, parameterCount * sizeof(Oid));
142
/* Process param names, if given */
143
namesarray = create_parameternames_array(parameterCount, parameterNames);
146
* don't allow functions of complex types that have the same name as
147
* existing attributes of the type
149
if (parameterCount == 1 && OidIsValid(typev[0]) &&
150
(relid = typeidTypeRelid(typev[0])) != InvalidOid &&
151
get_attnum(relid, procedureName) != InvalidAttrNumber)
153
(errcode(ERRCODE_DUPLICATE_COLUMN),
154
errmsg("\"%s\" is already an attribute of type %s",
155
procedureName, format_type_be(typev[0]))));
158
* All seems OK; prepare the data to be inserted into pg_proc.
161
for (i = 0; i < Natts_pg_proc; ++i)
164
values[i] = (Datum) NULL;
169
namestrcpy(&procname, procedureName);
170
values[i++] = NameGetDatum(&procname); /* proname */
171
values[i++] = ObjectIdGetDatum(procNamespace); /* pronamespace */
172
values[i++] = Int32GetDatum(GetUserId()); /* proowner */
173
values[i++] = ObjectIdGetDatum(languageObjectId); /* prolang */
174
values[i++] = BoolGetDatum(isAgg); /* proisagg */
175
values[i++] = BoolGetDatum(security_definer); /* prosecdef */
176
values[i++] = BoolGetDatum(isStrict); /* proisstrict */
177
values[i++] = BoolGetDatum(returnsSet); /* proretset */
178
values[i++] = CharGetDatum(volatility); /* provolatile */
179
values[i++] = UInt16GetDatum(parameterCount); /* pronargs */
180
values[i++] = ObjectIdGetDatum(returnType); /* prorettype */
181
values[i++] = PointerGetDatum(typev); /* proargtypes */
182
values[i++] = namesarray; /* proargnames */
183
if (namesarray == PointerGetDatum(NULL))
184
nulls[Anum_pg_proc_proargnames - 1] = 'n';
185
values[i++] = DirectFunctionCall1(textin, /* prosrc */
186
CStringGetDatum(prosrc));
187
values[i++] = DirectFunctionCall1(textin, /* probin */
188
CStringGetDatum(probin));
189
/* proacl will be handled below */
191
rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
192
tupDesc = rel->rd_att;
194
/* Check for pre-existing definition */
195
oldtup = SearchSysCache(PROCNAMENSP,
196
PointerGetDatum(procedureName),
197
UInt16GetDatum(parameterCount),
198
PointerGetDatum(typev),
199
ObjectIdGetDatum(procNamespace));
201
if (HeapTupleIsValid(oldtup))
203
/* There is one; okay to replace it? */
204
Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
208
(errcode(ERRCODE_DUPLICATE_FUNCTION),
209
errmsg("function \"%s\" already exists with same argument types",
211
if (GetUserId() != oldproc->proowner && !superuser())
212
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
216
* Not okay to change the return type of the existing proc, since
217
* existing rules, views, etc may depend on the return type.
219
if (returnType != oldproc->prorettype ||
220
returnsSet != oldproc->proretset)
222
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
223
errmsg("cannot change return type of existing function"),
224
errhint("Use DROP FUNCTION first.")));
226
/* Can't change aggregate status, either */
227
if (oldproc->proisagg != isAgg)
229
if (oldproc->proisagg)
231
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
232
errmsg("function \"%s\" is an aggregate",
236
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
237
errmsg("function \"%s\" is not an aggregate",
241
/* do not change existing ownership or permissions, either */
242
replaces[Anum_pg_proc_proowner - 1] = ' ';
243
replaces[Anum_pg_proc_proacl - 1] = ' ';
246
tup = heap_modifytuple(oldtup, rel, values, nulls, replaces);
247
simple_heap_update(rel, &tup->t_self, tup);
249
ReleaseSysCache(oldtup);
254
/* Creating a new procedure */
256
/* start out with empty permissions */
257
nulls[Anum_pg_proc_proacl - 1] = 'n';
259
tup = heap_formtuple(tupDesc, values, nulls);
260
simple_heap_insert(rel, tup);
264
/* Need to update indexes for either the insert or update case */
265
CatalogUpdateIndexes(rel, tup);
267
retval = HeapTupleGetOid(tup);
270
* Create dependencies for the new function. If we are updating an
271
* existing function, first delete any existing pg_depend entries.
274
deleteDependencyRecordsFor(RelOid_pg_proc, retval);
276
myself.classId = RelOid_pg_proc;
277
myself.objectId = retval;
278
myself.objectSubId = 0;
280
/* dependency on namespace */
281
referenced.classId = get_system_catalog_relid(NamespaceRelationName);
282
referenced.objectId = procNamespace;
283
referenced.objectSubId = 0;
284
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
286
/* dependency on implementation language */
287
referenced.classId = get_system_catalog_relid(LanguageRelationName);
288
referenced.objectId = languageObjectId;
289
referenced.objectSubId = 0;
290
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
292
/* dependency on return type */
293
referenced.classId = RelOid_pg_type;
294
referenced.objectId = returnType;
295
referenced.objectSubId = 0;
296
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
298
/* dependency on input types */
299
for (i = 0; i < parameterCount; i++)
301
referenced.classId = RelOid_pg_type;
302
referenced.objectId = typev[i];
303
referenced.objectSubId = 0;
304
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
309
heap_close(rel, RowExclusiveLock);
311
/* Verify function body */
312
if (OidIsValid(languageValidator))
314
/* Advance command counter so new tuple can be seen by validator */
315
CommandCounterIncrement();
316
OidFunctionCall1(languageValidator, ObjectIdGetDatum(retval));
324
* create_parameternames_array - build proargnames value from an array
325
* of C strings. Returns a NULL pointer if no names provided.
328
create_parameternames_array(int parameterCount, const char *parameterNames[])
330
Datum elems[FUNC_MAX_ARGS];
336
return PointerGetDatum(NULL);
338
for (i = 0; i < parameterCount; i++)
340
const char *s = parameterNames[i];
347
elems[i] = DirectFunctionCall1(textin, CStringGetDatum(s));
351
return PointerGetDatum(NULL);
353
names = construct_array(elems, parameterCount, TEXTOID, -1, false, 'i');
355
return PointerGetDatum(names);
361
* Validator for internal functions
363
* Check that the given internal function name (the "prosrc" value) is
364
* a known builtin function.
367
fmgr_internal_validator(PG_FUNCTION_ARGS)
369
Oid funcoid = PG_GETARG_OID(0);
377
* We do not honor check_function_bodies since it's unlikely the
378
* function name will be found later if it isn't there now.
381
tuple = SearchSysCache(PROCOID,
382
ObjectIdGetDatum(funcoid),
384
if (!HeapTupleIsValid(tuple))
385
elog(ERROR, "cache lookup failed for function %u", funcoid);
386
proc = (Form_pg_proc) GETSTRUCT(tuple);
388
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
390
elog(ERROR, "null prosrc");
391
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
393
if (fmgr_internal_function(prosrc) == InvalidOid)
395
(errcode(ERRCODE_UNDEFINED_FUNCTION),
396
errmsg("there is no built-in function named \"%s\"",
399
ReleaseSysCache(tuple);
407
* Validator for C language functions
409
* Make sure that the library file exists, is loadable, and contains
410
* the specified link symbol. Also check for a valid function
411
* information record.
414
fmgr_c_validator(PG_FUNCTION_ARGS)
416
Oid funcoid = PG_GETARG_OID(0);
426
* It'd be most consistent to skip the check if
427
* !check_function_bodies, but the purpose of that switch is to be
428
* helpful for pg_dump loading, and for pg_dump loading it's much
429
* better if we *do* check.
432
tuple = SearchSysCache(PROCOID,
433
ObjectIdGetDatum(funcoid),
435
if (!HeapTupleIsValid(tuple))
436
elog(ERROR, "cache lookup failed for function %u", funcoid);
437
proc = (Form_pg_proc) GETSTRUCT(tuple);
439
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
441
elog(ERROR, "null prosrc");
442
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
444
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_probin, &isnull);
446
elog(ERROR, "null probin");
447
probin = DatumGetCString(DirectFunctionCall1(textout, tmp));
449
(void) load_external_function(probin, prosrc, true, &libraryhandle);
450
(void) fetch_finfo_record(libraryhandle, prosrc);
452
ReleaseSysCache(tuple);
459
* Validator for SQL language functions
461
* Parse it here in order to be sure that it contains no syntax errors.
464
fmgr_sql_validator(PG_FUNCTION_ARGS)
466
Oid funcoid = PG_GETARG_OID(0);
469
List *querytree_list;
473
ErrorContextCallback sqlerrcontext;
478
tuple = SearchSysCache(PROCOID,
479
ObjectIdGetDatum(funcoid),
481
if (!HeapTupleIsValid(tuple))
482
elog(ERROR, "cache lookup failed for function %u", funcoid);
483
proc = (Form_pg_proc) GETSTRUCT(tuple);
485
functyptype = get_typtype(proc->prorettype);
487
/* Disallow pseudotype result */
488
/* except for RECORD, VOID, ANYARRAY, or ANYELEMENT */
489
if (functyptype == 'p' &&
490
proc->prorettype != RECORDOID &&
491
proc->prorettype != VOIDOID &&
492
proc->prorettype != ANYARRAYOID &&
493
proc->prorettype != ANYELEMENTOID)
495
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
496
errmsg("SQL functions cannot return type %s",
497
format_type_be(proc->prorettype))));
499
/* Disallow pseudotypes in arguments */
500
/* except for ANYARRAY or ANYELEMENT */
502
for (i = 0; i < proc->pronargs; i++)
504
if (get_typtype(proc->proargtypes[i]) == 'p')
506
if (proc->proargtypes[i] == ANYARRAYOID ||
507
proc->proargtypes[i] == ANYELEMENTOID)
511
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
512
errmsg("SQL functions cannot have arguments of type %s",
513
format_type_be(proc->proargtypes[i]))));
517
/* Postpone body checks if !check_function_bodies */
518
if (check_function_bodies)
520
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
522
elog(ERROR, "null prosrc");
524
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
527
* Setup error traceback support for ereport().
529
sqlerrcontext.callback = sql_function_parse_error_callback;
530
sqlerrcontext.arg = tuple;
531
sqlerrcontext.previous = error_context_stack;
532
error_context_stack = &sqlerrcontext;
535
* We can't do full prechecking of the function definition if
536
* there are any polymorphic input types, because actual datatypes
537
* of expression results will be unresolvable. The check will be
538
* done at runtime instead.
540
* We can run the text through the raw parser though; this will at
541
* least catch silly syntactic errors.
545
querytree_list = pg_parse_and_rewrite(prosrc,
548
(void) check_sql_fn_retval(proc->prorettype, functyptype,
549
querytree_list, NULL);
552
querytree_list = pg_parse_query(prosrc);
554
error_context_stack = sqlerrcontext.previous;
557
ReleaseSysCache(tuple);
563
* Error context callback for handling errors in SQL function definitions
566
sql_function_parse_error_callback(void *arg)
568
HeapTuple tuple = (HeapTuple) arg;
569
Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(tuple);
574
/* See if it's a syntax error; if so, transpose to CREATE FUNCTION */
575
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
577
elog(ERROR, "null prosrc");
578
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
580
if (!function_parse_error_transpose(prosrc))
582
/* If it's not a syntax error, push info onto context stack */
583
errcontext("SQL function \"%s\"", NameStr(proc->proname));
590
* Adjust a syntax error occurring inside the function body of a CREATE
591
* FUNCTION command. This can be used by any function validator, not only
592
* for SQL-language functions. It is assumed that the syntax error position
593
* is initially relative to the function body string (as passed in). If
594
* possible, we adjust the position to reference the original CREATE command;
595
* if we can't manage that, we set up an "internal query" syntax error instead.
597
* Returns true if a syntax error was processed, false if not.
600
function_parse_error_transpose(const char *prosrc)
604
const char *queryText;
607
* Nothing to do unless we are dealing with a syntax error that has a
610
* Some PLs may prefer to report the error position as an internal error
611
* to begin with, so check that too.
613
origerrposition = geterrposition();
614
if (origerrposition <= 0)
616
origerrposition = getinternalerrposition();
617
if (origerrposition <= 0)
621
/* We can get the original query text from the active portal (hack...) */
622
Assert(ActivePortal && ActivePortal->status == PORTAL_ACTIVE);
623
queryText = ActivePortal->sourceText;
625
/* Try to locate the prosrc in the original text */
626
newerrposition = match_prosrc_to_query(prosrc, queryText, origerrposition);
628
if (newerrposition > 0)
630
/* Successful, so fix error position to reference original query */
631
errposition(newerrposition);
632
/* Get rid of any report of the error as an "internal query" */
633
internalerrposition(0);
634
internalerrquery(NULL);
639
* If unsuccessful, convert the position to an internal position
640
* marker and give the function text as the internal query.
643
internalerrposition(origerrposition);
644
internalerrquery(prosrc);
651
* Try to locate the string literal containing the function body in the
652
* given text of the CREATE FUNCTION command. If successful, return the
653
* character (not byte) index within the command corresponding to the
654
* given character index within the literal. If not successful, return 0.
657
match_prosrc_to_query(const char *prosrc, const char *queryText,
661
* Rather than fully parsing the CREATE FUNCTION command, we just scan
662
* the command looking for $prosrc$ or 'prosrc'. This could be fooled
663
* (though not in any very probable scenarios), so fail if we find
664
* more than one match.
666
int prosrclen = strlen(prosrc);
667
int querylen = strlen(queryText);
672
for (curpos = 0; curpos < querylen - prosrclen; curpos++)
674
if (queryText[curpos] == '$' &&
675
strncmp(prosrc, &queryText[curpos + 1], prosrclen) == 0 &&
676
queryText[curpos + 1 + prosrclen] == '$')
679
* Found a $foo$ match. Since there are no embedded quoting
680
* characters in a dollar-quoted literal, we don't have to do
681
* any fancy arithmetic; just offset by the starting position.
684
return 0; /* multiple matches, fail */
685
matchpos = pg_mbstrlen_with_len(queryText, curpos + 1)
688
else if (queryText[curpos] == '\'' &&
689
match_prosrc_to_literal(prosrc, &queryText[curpos + 1],
690
cursorpos, &newcursorpos))
693
* Found a 'foo' match. match_prosrc_to_literal() has
694
* adjusted for any quotes or backslashes embedded in the
698
return 0; /* multiple matches, fail */
699
matchpos = pg_mbstrlen_with_len(queryText, curpos + 1)
708
* Try to match the given source text to a single-quoted literal.
709
* If successful, adjust newcursorpos to correspond to the character
710
* (not byte) index corresponding to cursorpos in the source text.
712
* At entry, literal points just past a ' character. We must check for the
716
match_prosrc_to_literal(const char *prosrc, const char *literal,
717
int cursorpos, int *newcursorpos)
719
int newcp = cursorpos;
723
* This implementation handles backslashes and doubled quotes in the
724
* string literal. It does not handle the SQL syntax for literals
725
* continued across line boundaries.
727
* We do the comparison a character at a time, not a byte at a time, so
728
* that we can do the correct cursorpos math.
732
cursorpos--; /* characters left before cursor */
735
* Check for backslashes and doubled quotes in the literal; adjust
736
* newcp when one is found before the cursor.
738
if (*literal == '\\')
744
else if (*literal == '\'')
746
if (literal[1] != '\'')
752
chlen = pg_mblen(prosrc);
753
if (strncmp(prosrc, literal, chlen) != 0)
759
*newcursorpos = newcp;
761
if (*literal == '\'' && literal[1] != '\'')