]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_proc.c
32d9293f566c4bd319305cabc6355be22cd07a7b
[postgresql] / src / backend / catalog / pg_proc.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_proc.c
4  *        routines to support manipulation of the pg_proc relation
5  *
6  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.63 2001/11/05 17:46:24 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include "access/heapam.h"
18 #include "catalog/catname.h"
19 #include "catalog/indexing.h"
20 #include "catalog/pg_language.h"
21 #include "catalog/pg_proc.h"
22 #include "catalog/pg_type.h"
23 #include "executor/executor.h"
24 #include "miscadmin.h"
25 #include "parser/parse_coerce.h"
26 #include "parser/parse_expr.h"
27 #include "parser/parse_type.h"
28 #include "tcop/tcopprot.h"
29 #include "utils/builtins.h"
30 #include "utils/lsyscache.h"
31 #include "utils/sets.h"
32 #include "utils/syscache.h"
33
34
35 static void checkretval(Oid rettype, List *queryTreeList);
36
37
38 /* ----------------------------------------------------------------
39  *              ProcedureCreate
40  * ----------------------------------------------------------------
41  */
42 Oid
43 ProcedureCreate(char *procedureName,
44                                 bool replace,
45                                 bool returnsSet,
46                                 char *returnTypeName,
47                                 char *languageName,
48                                 char *prosrc,
49                                 char *probin,
50                                 bool trusted,
51                                 bool canCache,
52                                 bool isStrict,
53                                 int32 byte_pct,
54                                 int32 perbyte_cpu,
55                                 int32 percall_cpu,
56                                 int32 outin_ratio,
57                                 List *argList)
58 {
59         int                     i;
60         Relation        rel;
61         HeapTuple       tup;
62         HeapTuple       oldtup;
63         bool            defined;
64         uint16          parameterCount;
65         char            nulls[Natts_pg_proc];
66         Datum           values[Natts_pg_proc];
67         char            replaces[Natts_pg_proc];
68         Oid                     languageObjectId;
69         Oid                     typeObjectId;
70         List       *x;
71         List       *querytree_list;
72         Oid                     typev[FUNC_MAX_ARGS];
73         Oid                     relid;
74         Oid                     toid;
75         NameData        procname;
76         TupleDesc       tupDesc;
77         Oid                     retval;
78
79         /*
80          * sanity checks
81          */
82         Assert(PointerIsValid(prosrc));
83         Assert(PointerIsValid(probin));
84
85         languageObjectId = GetSysCacheOid(LANGNAME,
86                                                                           PointerGetDatum(languageName),
87                                                                           0, 0, 0);
88         if (!OidIsValid(languageObjectId))
89                 elog(ERROR, "language '%s' does not exist", languageName);
90
91         parameterCount = 0;
92         MemSet(typev, 0, FUNC_MAX_ARGS * sizeof(Oid));
93         foreach(x, argList)
94         {
95                 TypeName   *t = (TypeName *) lfirst(x);
96                 char       *typnam = TypeNameToInternalName(t);
97
98                 if (parameterCount >= FUNC_MAX_ARGS)
99                         elog(ERROR, "functions cannot have more than %d arguments",
100                                  FUNC_MAX_ARGS);
101
102                 if (strcmp(typnam, "opaque") == 0)
103                 {
104                         if (languageObjectId == SQLlanguageId)
105                                 elog(ERROR, "SQL functions cannot have arguments of type \"opaque\"");
106                         toid = InvalidOid;
107                 }
108                 else
109                 {
110                         toid = TypeGet(typnam, &defined);
111
112                         if (!OidIsValid(toid))
113                                 elog(ERROR, "argument type %s does not exist",
114                                          typnam);
115                         if (!defined)
116                                 elog(NOTICE, "argument type %s is only a shell",
117                                          typnam);
118                 }
119
120                 if (t->setof)
121                         elog(ERROR, "functions cannot accept set arguments");
122
123                 typev[parameterCount++] = toid;
124         }
125
126         if (languageObjectId == SQLlanguageId)
127         {
128                 /*
129                  * If this call is defining a set, check if the set is already
130                  * defined by looking to see whether this call's function text
131                  * matches a function already in pg_proc.  If so just return the
132                  * OID of the existing set.
133                  */
134                 if (strcmp(procedureName, GENERICSETNAME) == 0)
135                 {
136 #ifdef SETS_FIXED
137
138                         /*
139                          * The code below doesn't work any more because the PROSRC
140                          * system cache and the pg_proc_prosrc_index have been
141                          * removed. Instead a sequential heap scan or something better
142                          * must get implemented. The reason for removing is that
143                          * nbtree index crashes if sources exceed 2K --- what's likely
144                          * for procedural languages.
145                          *
146                          * 1999/09/30 Jan
147                          */
148                         text       *prosrctext;
149
150                         prosrctext = DatumGetTextP(DirectFunctionCall1(textin,
151                                                                                            CStringGetDatum(prosrc)));
152                         retval = GetSysCacheOid(PROSRC,
153                                                                         PointerGetDatum(prosrctext),
154                                                                         0, 0, 0);
155                         pfree(prosrctext);
156                         if (OidIsValid(retval))
157                                 return retval;
158 #else
159                         elog(ERROR, "lookup for procedure by source needs fix (Jan)");
160 #endif   /* SETS_FIXED */
161                 }
162         }
163
164         if (strcmp(returnTypeName, "opaque") == 0)
165         {
166                 if (languageObjectId == SQLlanguageId)
167                         elog(ERROR, "SQL functions cannot return type \"opaque\"");
168                 typeObjectId = InvalidOid;
169         }
170         else
171         {
172                 typeObjectId = TypeGet(returnTypeName, &defined);
173
174                 if (!OidIsValid(typeObjectId))
175                 {
176                         elog(NOTICE, "ProcedureCreate: type %s is not yet defined",
177                                  returnTypeName);
178                         typeObjectId = TypeShellMake(returnTypeName);
179                         if (!OidIsValid(typeObjectId))
180                                 elog(ERROR, "could not create type %s",
181                                          returnTypeName);
182                 }
183                 else if (!defined)
184                         elog(NOTICE, "return type %s is only a shell",
185                                  returnTypeName);
186         }
187
188         /*
189          * don't allow functions of complex types that have the same name as
190          * existing attributes of the type
191          */
192         if (parameterCount == 1 &&
193                 (toid = TypeGet(strVal(lfirst(argList)), &defined)) &&
194                 defined &&
195                 (relid = typeidTypeRelid(toid)) != 0 &&
196                 get_attnum(relid, procedureName) != InvalidAttrNumber)
197                 elog(ERROR, "method %s already an attribute of type %s",
198                          procedureName, strVal(lfirst(argList)));
199
200         /*
201          * If this is a postquel procedure, we parse it here in order to be
202          * sure that it contains no syntax errors.      We should store the plan
203          * in an Inversion file for use later, but for now, we just store the
204          * procedure's text in the prosrc attribute.
205          */
206
207         if (languageObjectId == SQLlanguageId)
208         {
209                 querytree_list = pg_parse_and_rewrite(prosrc, typev, parameterCount);
210                 /* typecheck return value */
211                 checkretval(typeObjectId, querytree_list);
212         }
213
214         /*
215          * If this is an internal procedure, check that the given internal
216          * function name (the 'prosrc' value) is a known builtin function.
217          *
218          * NOTE: in Postgres versions before 6.5, the SQL name of the created
219          * function could not be different from the internal name, and
220          * 'prosrc' wasn't used.  So there is code out there that does CREATE
221          * FUNCTION xyz AS '' LANGUAGE 'internal'.      To preserve some modicum
222          * of backwards compatibility, accept an empty 'prosrc' value as
223          * meaning the supplied SQL function name.
224          */
225
226         if (languageObjectId == INTERNALlanguageId)
227         {
228                 if (strlen(prosrc) == 0)
229                         prosrc = procedureName;
230                 if (fmgr_internal_function(prosrc) == InvalidOid)
231                         elog(ERROR,
232                                  "there is no built-in function named \"%s\"",
233                                  prosrc);
234         }
235
236         /*
237          * If this is a dynamically loadable procedure, make sure that the
238          * library file exists, is loadable, and contains the specified link
239          * symbol.      Also check for a valid function information record.
240          *
241          * We used to perform these checks only when the function was first
242          * called, but it seems friendlier to verify the library's validity at
243          * CREATE FUNCTION time.
244          */
245
246         if (languageObjectId == ClanguageId)
247         {
248                 void       *libraryhandle;
249
250                 /* If link symbol is specified as "-", substitute procedure name */
251                 if (strcmp(prosrc, "-") == 0)
252                         prosrc = procedureName;
253                 (void) load_external_function(probin, prosrc, true,
254                                                                           &libraryhandle);
255                 (void) fetch_finfo_record(libraryhandle, prosrc);
256         }
257
258         /*
259          * All seems OK; prepare the data to be inserted into pg_proc.
260          */
261
262         for (i = 0; i < Natts_pg_proc; ++i)
263         {
264                 nulls[i] = ' ';
265                 values[i] = (Datum) NULL;
266                 replaces[i] = 'r';
267         }
268
269         i = 0;
270         namestrcpy(&procname, procedureName);
271         values[i++] = NameGetDatum(&procname);
272         values[i++] = Int32GetDatum(GetUserId());
273         values[i++] = ObjectIdGetDatum(languageObjectId);
274         /* XXX isinherited is always false for now */
275         values[i++] = BoolGetDatum(false);
276         values[i++] = BoolGetDatum(trusted);
277         values[i++] = BoolGetDatum(canCache);
278         values[i++] = BoolGetDatum(isStrict);
279         values[i++] = UInt16GetDatum(parameterCount);
280         values[i++] = BoolGetDatum(returnsSet);
281         values[i++] = ObjectIdGetDatum(typeObjectId);
282         values[i++] = PointerGetDatum(typev);
283         values[i++] = Int32GetDatum(byte_pct);          /* probyte_pct */
284         values[i++] = Int32GetDatum(perbyte_cpu);       /* properbyte_cpu */
285         values[i++] = Int32GetDatum(percall_cpu);       /* propercall_cpu */
286         values[i++] = Int32GetDatum(outin_ratio);       /* prooutin_ratio */
287         values[i++] = DirectFunctionCall1(textin,       /* prosrc */
288                                                                           CStringGetDatum(prosrc));
289         values[i++] = DirectFunctionCall1(textin,       /* probin */
290                                                                           CStringGetDatum(probin));
291
292         rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
293         tupDesc = rel->rd_att;
294
295         /* Check for pre-existing definition */
296         oldtup = SearchSysCache(PROCNAME,
297                                                         PointerGetDatum(procedureName),
298                                                         UInt16GetDatum(parameterCount),
299                                                         PointerGetDatum(typev),
300                                                         0);
301
302         if (HeapTupleIsValid(oldtup))
303         {
304                 /* There is one; okay to replace it? */
305                 Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
306
307                 if (!replace)
308                         elog(ERROR, "function %s already exists with same argument types",
309                                  procedureName);
310                 if (GetUserId() != oldproc->proowner && !superuser())
311                         elog(ERROR, "ProcedureCreate: you do not have permission to replace function %s",
312                                  procedureName);
313
314                 /*
315                  * Not okay to change the return type of the existing proc, since
316                  * existing rules, views, etc may depend on the return type.
317                  */
318                 if (typeObjectId != oldproc->prorettype ||
319                         returnsSet != oldproc->proretset)
320                         elog(ERROR, "ProcedureCreate: cannot change return type of existing function."
321                                  "\n\tUse DROP FUNCTION first.");
322
323                 /* Okay, do it... */
324                 tup = heap_modifytuple(oldtup, rel, values, nulls, replaces);
325                 simple_heap_update(rel, &tup->t_self, tup);
326
327                 ReleaseSysCache(oldtup);
328         }
329         else
330         {
331                 /* Creating a new procedure */
332                 tup = heap_formtuple(tupDesc, values, nulls);
333                 heap_insert(rel, tup);
334         }
335
336         /* Need to update indices for either the insert or update case */
337         if (RelationGetForm(rel)->relhasindex)
338         {
339                 Relation        idescs[Num_pg_proc_indices];
340
341                 CatalogOpenIndices(Num_pg_proc_indices, Name_pg_proc_indices, idescs);
342                 CatalogIndexInsert(idescs, Num_pg_proc_indices, rel, tup);
343                 CatalogCloseIndices(Num_pg_proc_indices, idescs);
344         }
345
346         retval = tup->t_data->t_oid;
347         heap_freetuple(tup);
348
349         heap_close(rel, RowExclusiveLock);
350
351         return retval;
352 }
353
354 /*
355  * checkretval() -- check return value of a list of sql parse trees.
356  *
357  * The return value of a sql function is the value returned by
358  * the final query in the function.  We do some ad-hoc define-time
359  * type checking here to be sure that the user is returning the
360  * type he claims.
361  */
362 static void
363 checkretval(Oid rettype, List *queryTreeList)
364 {
365         Query      *parse;
366         int                     cmd;
367         List       *tlist;
368         List       *tlistitem;
369         int                     tlistlen;
370         Oid                     typerelid;
371         Oid                     restype;
372         Relation        reln;
373         Oid                     relid;
374         int                     relnatts;
375         int                     i;
376
377         /* guard against empty function body; OK only if no return type */
378         if (queryTreeList == NIL)
379         {
380                 if (rettype != InvalidOid)
381                         elog(ERROR, "function declared to return %s, but no SELECT provided",
382                                  format_type_be(rettype));
383                 return;
384         }
385
386         /* find the final query */
387         parse = (Query *) nth(length(queryTreeList) - 1, queryTreeList);
388
389         cmd = parse->commandType;
390         tlist = parse->targetList;
391
392         /*
393          * The last query must be a SELECT if and only if there is a return
394          * type.
395          */
396         if (rettype == InvalidOid)
397         {
398                 if (cmd == CMD_SELECT)
399                         elog(ERROR, "function declared with no return type, but final statement is a SELECT");
400                 return;
401         }
402
403         /* by here, the function is declared to return some type */
404         if (cmd != CMD_SELECT)
405                 elog(ERROR, "function declared to return %s, but final statement is not a SELECT",
406                          format_type_be(rettype));
407
408         /*
409          * Count the non-junk entries in the result targetlist.
410          */
411         tlistlen = ExecCleanTargetListLength(tlist);
412
413         /*
414          * For base-type returns, the target list should have exactly one
415          * entry, and its type should agree with what the user declared. (As
416          * of Postgres 7.2, we accept binary-compatible types too.)
417          */
418         typerelid = typeidTypeRelid(rettype);
419         if (typerelid == InvalidOid)
420         {
421                 if (tlistlen != 1)
422                         elog(ERROR, "function declared to return %s returns multiple columns in final SELECT",
423                                  format_type_be(rettype));
424
425                 restype = ((TargetEntry *) lfirst(tlist))->resdom->restype;
426                 if (restype != rettype && !IS_BINARY_COMPATIBLE(restype, rettype))
427                         elog(ERROR, "return type mismatch in function: declared to return %s, returns %s",
428                                  format_type_be(rettype), format_type_be(restype));
429
430                 return;
431         }
432
433         /*
434          * If the target list is of length 1, and the type of the varnode in
435          * the target list matches the declared return type, this is okay.
436          * This can happen, for example, where the body of the function is
437          * 'SELECT func2()', where func2 has the same return type as the
438          * function that's calling it.
439          */
440         if (tlistlen == 1)
441         {
442                 restype = ((TargetEntry *) lfirst(tlist))->resdom->restype;
443                 if (restype == rettype || IS_BINARY_COMPATIBLE(restype, rettype))
444                         return;
445         }
446
447         /*
448          * By here, the procedure returns a tuple or set of tuples.  This part
449          * of the typechecking is a hack. We look up the relation that is the
450          * declared return type, and be sure that attributes 1 .. n in the
451          * target list match the declared types.
452          */
453         reln = heap_open(typerelid, AccessShareLock);
454         relid = reln->rd_id;
455         relnatts = reln->rd_rel->relnatts;
456
457         if (tlistlen != relnatts)
458                 elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)",
459                          format_type_be(rettype), relnatts);
460
461         /* expect attributes 1 .. n in order */
462         i = 0;
463         foreach(tlistitem, tlist)
464         {
465                 TargetEntry *tle = (TargetEntry *) lfirst(tlistitem);
466                 Oid                     tletype;
467                 Oid                     atttype;
468
469                 if (tle->resdom->resjunk)
470                         continue;
471                 tletype = exprType(tle->expr);
472                 atttype = reln->rd_att->attrs[i]->atttypid;
473                 if (tletype != atttype && !IS_BINARY_COMPATIBLE(tletype, atttype))
474                         elog(ERROR, "function declared to return %s returns %s instead of %s at column %d",
475                                  format_type_be(rettype),
476                                  format_type_be(tletype),
477                                  format_type_be(atttype),
478                                  i + 1);
479                 i++;
480         }
481
482         /* this shouldn't happen, but let's just check... */
483         if (i != relnatts)
484                 elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)",
485                          format_type_be(rettype), relnatts);
486
487         heap_close(reln, AccessShareLock);
488 }