]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_proc.c
Commit to match discussed elog() changes. Only update is that LOG is
[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.64 2002/02/18 23:11:08 petere 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                                 Oid languageObjectId,
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                     typeObjectId;
69         List       *x;
70         List       *querytree_list;
71         Oid                     typev[FUNC_MAX_ARGS];
72         Oid                     relid;
73         Oid                     toid;
74         NameData        procname;
75         TupleDesc       tupDesc;
76         Oid                     retval;
77
78         /*
79          * sanity checks
80          */
81         Assert(PointerIsValid(prosrc));
82         Assert(PointerIsValid(probin));
83
84         parameterCount = 0;
85         MemSet(typev, 0, FUNC_MAX_ARGS * sizeof(Oid));
86         foreach(x, argList)
87         {
88                 TypeName   *t = (TypeName *) lfirst(x);
89                 char       *typnam = TypeNameToInternalName(t);
90
91                 if (parameterCount >= FUNC_MAX_ARGS)
92                         elog(ERROR, "functions cannot have more than %d arguments",
93                                  FUNC_MAX_ARGS);
94
95                 if (strcmp(typnam, "opaque") == 0)
96                 {
97                         if (languageObjectId == SQLlanguageId)
98                                 elog(ERROR, "SQL functions cannot have arguments of type \"opaque\"");
99                         toid = InvalidOid;
100                 }
101                 else
102                 {
103                         toid = TypeGet(typnam, &defined);
104
105                         if (!OidIsValid(toid))
106                                 elog(ERROR, "argument type %s does not exist",
107                                          typnam);
108                         if (!defined)
109                                 elog(NOTICE, "argument type %s is only a shell",
110                                          typnam);
111                 }
112
113                 if (t->setof)
114                         elog(ERROR, "functions cannot accept set arguments");
115
116                 typev[parameterCount++] = toid;
117         }
118
119         if (languageObjectId == SQLlanguageId)
120         {
121                 /*
122                  * If this call is defining a set, check if the set is already
123                  * defined by looking to see whether this call's function text
124                  * matches a function already in pg_proc.  If so just return the
125                  * OID of the existing set.
126                  */
127                 if (strcmp(procedureName, GENERICSETNAME) == 0)
128                 {
129 #ifdef SETS_FIXED
130
131                         /*
132                          * The code below doesn't work any more because the PROSRC
133                          * system cache and the pg_proc_prosrc_index have been
134                          * removed. Instead a sequential heap scan or something better
135                          * must get implemented. The reason for removing is that
136                          * nbtree index crashes if sources exceed 2K --- what's likely
137                          * for procedural languages.
138                          *
139                          * 1999/09/30 Jan
140                          */
141                         text       *prosrctext;
142
143                         prosrctext = DatumGetTextP(DirectFunctionCall1(textin,
144                                                                                            CStringGetDatum(prosrc)));
145                         retval = GetSysCacheOid(PROSRC,
146                                                                         PointerGetDatum(prosrctext),
147                                                                         0, 0, 0);
148                         pfree(prosrctext);
149                         if (OidIsValid(retval))
150                                 return retval;
151 #else
152                         elog(ERROR, "lookup for procedure by source needs fix (Jan)");
153 #endif   /* SETS_FIXED */
154                 }
155         }
156
157         if (strcmp(returnTypeName, "opaque") == 0)
158         {
159                 if (languageObjectId == SQLlanguageId)
160                         elog(ERROR, "SQL functions cannot return type \"opaque\"");
161                 typeObjectId = InvalidOid;
162         }
163         else
164         {
165                 typeObjectId = TypeGet(returnTypeName, &defined);
166
167                 if (!OidIsValid(typeObjectId))
168                 {
169                         elog(NOTICE, "ProcedureCreate: type %s is not yet defined",
170                                  returnTypeName);
171                         typeObjectId = TypeShellMake(returnTypeName);
172                         if (!OidIsValid(typeObjectId))
173                                 elog(ERROR, "could not create type %s",
174                                          returnTypeName);
175                 }
176                 else if (!defined)
177                         elog(NOTICE, "return type %s is only a shell",
178                                  returnTypeName);
179         }
180
181         /*
182          * don't allow functions of complex types that have the same name as
183          * existing attributes of the type
184          */
185         if (parameterCount == 1 &&
186                 (toid = TypeGet(strVal(lfirst(argList)), &defined)) &&
187                 defined &&
188                 (relid = typeidTypeRelid(toid)) != 0 &&
189                 get_attnum(relid, procedureName) != InvalidAttrNumber)
190                 elog(ERROR, "method %s already an attribute of type %s",
191                          procedureName, strVal(lfirst(argList)));
192
193         /*
194          * If this is a postquel procedure, we parse it here in order to be
195          * sure that it contains no syntax errors.      We should store the plan
196          * in an Inversion file for use later, but for now, we just store the
197          * procedure's text in the prosrc attribute.
198          */
199
200         if (languageObjectId == SQLlanguageId)
201         {
202                 querytree_list = pg_parse_and_rewrite(prosrc, typev, parameterCount);
203                 /* typecheck return value */
204                 checkretval(typeObjectId, querytree_list);
205         }
206
207         /*
208          * If this is an internal procedure, check that the given internal
209          * function name (the 'prosrc' value) is a known builtin function.
210          *
211          * NOTE: in Postgres versions before 6.5, the SQL name of the created
212          * function could not be different from the internal name, and
213          * 'prosrc' wasn't used.  So there is code out there that does CREATE
214          * FUNCTION xyz AS '' LANGUAGE 'internal'.      To preserve some modicum
215          * of backwards compatibility, accept an empty 'prosrc' value as
216          * meaning the supplied SQL function name.
217          */
218
219         if (languageObjectId == INTERNALlanguageId)
220         {
221                 if (strlen(prosrc) == 0)
222                         prosrc = procedureName;
223                 if (fmgr_internal_function(prosrc) == InvalidOid)
224                         elog(ERROR,
225                                  "there is no built-in function named \"%s\"",
226                                  prosrc);
227         }
228
229         /*
230          * If this is a dynamically loadable procedure, make sure that the
231          * library file exists, is loadable, and contains the specified link
232          * symbol.      Also check for a valid function information record.
233          *
234          * We used to perform these checks only when the function was first
235          * called, but it seems friendlier to verify the library's validity at
236          * CREATE FUNCTION time.
237          */
238
239         if (languageObjectId == ClanguageId)
240         {
241                 void       *libraryhandle;
242
243                 /* If link symbol is specified as "-", substitute procedure name */
244                 if (strcmp(prosrc, "-") == 0)
245                         prosrc = procedureName;
246                 (void) load_external_function(probin, prosrc, true,
247                                                                           &libraryhandle);
248                 (void) fetch_finfo_record(libraryhandle, prosrc);
249         }
250
251         /*
252          * All seems OK; prepare the data to be inserted into pg_proc.
253          */
254
255         for (i = 0; i < Natts_pg_proc; ++i)
256         {
257                 nulls[i] = ' ';
258                 values[i] = (Datum) NULL;
259                 replaces[i] = 'r';
260         }
261
262         i = 0;
263         namestrcpy(&procname, procedureName);
264         values[i++] = NameGetDatum(&procname);
265         values[i++] = Int32GetDatum(GetUserId());
266         values[i++] = ObjectIdGetDatum(languageObjectId);
267         /* XXX isinherited is always false for now */
268         values[i++] = BoolGetDatum(false);
269         values[i++] = BoolGetDatum(trusted);
270         values[i++] = BoolGetDatum(canCache);
271         values[i++] = BoolGetDatum(isStrict);
272         values[i++] = UInt16GetDatum(parameterCount);
273         values[i++] = BoolGetDatum(returnsSet);
274         values[i++] = ObjectIdGetDatum(typeObjectId);
275         values[i++] = PointerGetDatum(typev);
276         values[i++] = Int32GetDatum(byte_pct);          /* probyte_pct */
277         values[i++] = Int32GetDatum(perbyte_cpu);       /* properbyte_cpu */
278         values[i++] = Int32GetDatum(percall_cpu);       /* propercall_cpu */
279         values[i++] = Int32GetDatum(outin_ratio);       /* prooutin_ratio */
280         values[i++] = DirectFunctionCall1(textin,       /* prosrc */
281                                                                           CStringGetDatum(prosrc));
282         values[i++] = DirectFunctionCall1(textin,       /* probin */
283                                                                           CStringGetDatum(probin));
284
285         rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
286         tupDesc = rel->rd_att;
287
288         /* Check for pre-existing definition */
289         oldtup = SearchSysCache(PROCNAME,
290                                                         PointerGetDatum(procedureName),
291                                                         UInt16GetDatum(parameterCount),
292                                                         PointerGetDatum(typev),
293                                                         0);
294
295         if (HeapTupleIsValid(oldtup))
296         {
297                 /* There is one; okay to replace it? */
298                 Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
299
300                 if (!replace)
301                         elog(ERROR, "function %s already exists with same argument types",
302                                  procedureName);
303                 if (GetUserId() != oldproc->proowner && !superuser())
304                         elog(ERROR, "ProcedureCreate: you do not have permission to replace function %s",
305                                  procedureName);
306
307                 /*
308                  * Not okay to change the return type of the existing proc, since
309                  * existing rules, views, etc may depend on the return type.
310                  */
311                 if (typeObjectId != oldproc->prorettype ||
312                         returnsSet != oldproc->proretset)
313                         elog(ERROR, "ProcedureCreate: cannot change return type of existing function."
314                                  "\n\tUse DROP FUNCTION first.");
315
316                 /* do not change existing permissions */
317                 replaces[Anum_pg_proc_proacl-1] = ' ';
318
319                 /* Okay, do it... */
320                 tup = heap_modifytuple(oldtup, rel, values, nulls, replaces);
321                 simple_heap_update(rel, &tup->t_self, tup);
322
323                 ReleaseSysCache(oldtup);
324         }
325         else
326         {
327                 /* Creating a new procedure */
328
329                 /* start out with empty permissions */
330                 nulls[Anum_pg_proc_proacl-1] = 'n';
331
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 }