]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_proc.c
Fix bug noted by Bruce: FETCH in an already-aborted transaction block
[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-2000, PostgreSQL, Inc
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.41 2000/04/04 21:44:37 tgl 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_proc.h"
21 #include "catalog/pg_type.h"
22 #include "miscadmin.h"
23 #include "optimizer/planner.h"
24 #include "parser/parse_type.h"
25 #include "tcop/tcopprot.h"
26 #include "utils/builtins.h"
27 #include "utils/fmgrtab.h"
28 #include "utils/lsyscache.h"
29 #include "utils/sets.h"
30 #include "utils/syscache.h"
31
32
33 /* ----------------------------------------------------------------
34  *              ProcedureCreate
35  * ----------------------------------------------------------------
36  */
37 Oid
38 ProcedureCreate(char *procedureName,
39                                 bool returnsSet,
40                                 char *returnTypeName,
41                                 char *languageName,
42                                 char *prosrc,
43                                 char *probin,
44                                 bool canCache,
45                                 bool trusted,
46                                 int32 byte_pct,
47                                 int32 perbyte_cpu,
48                                 int32 percall_cpu,
49                                 int32 outin_ratio,
50                                 List *argList,
51                                 CommandDest dest)
52 {
53         int                     i;
54         Relation        rel;
55         HeapTuple       tup;
56         bool            defined;
57         uint16          parameterCount;
58         char            nulls[Natts_pg_proc];
59         Datum           values[Natts_pg_proc];
60         Oid                     languageObjectId;
61         Oid                     typeObjectId;
62         List       *x;
63         List       *querytree_list;
64         Oid                     typev[FUNC_MAX_ARGS];
65         Oid                     relid;
66         Oid                     toid;
67         NameData        procname;
68         TupleDesc       tupDesc;
69
70         /* ----------------
71          *      sanity checks
72          * ----------------
73          */
74         Assert(PointerIsValid(prosrc));
75         Assert(PointerIsValid(probin));
76
77         parameterCount = 0;
78         MemSet(typev, 0, FUNC_MAX_ARGS * sizeof(Oid));
79         foreach(x, argList)
80         {
81                 Value      *t = lfirst(x);
82
83                 if (parameterCount >= FUNC_MAX_ARGS)
84                         elog(ERROR, "Procedures cannot take more than %d arguments",
85                                  FUNC_MAX_ARGS);
86
87                 if (strcmp(strVal(t), "opaque") == 0)
88                 {
89                         if (strcmp(languageName, "sql") == 0)
90                                 elog(ERROR, "ProcedureCreate: sql functions cannot take type \"opaque\"");
91                         toid = 0;
92                 }
93                 else
94                 {
95                         toid = TypeGet(strVal(t), &defined);
96
97                         if (!OidIsValid(toid))
98                         {
99                                 elog(ERROR, "ProcedureCreate: arg type '%s' is not defined",
100                                          strVal(t));
101                         }
102
103                         if (!defined)
104                         {
105                                 elog(NOTICE, "ProcedureCreate: arg type '%s' is only a shell",
106                                          strVal(t));
107                         }
108                 }
109
110                 typev[parameterCount++] = toid;
111         }
112
113         tup = SearchSysCacheTuple(PROCNAME,
114                                                           PointerGetDatum(procedureName),
115                                                           UInt16GetDatum(parameterCount),
116                                                           PointerGetDatum(typev),
117                                                           0);
118
119         if (HeapTupleIsValid(tup))
120                 elog(ERROR, "ProcedureCreate: procedure %s already exists with same arguments",
121                          procedureName);
122
123         if (!strcmp(languageName, "sql"))
124         {
125
126                 /*
127                  * If this call is defining a set, check if the set is already
128                  * defined by looking to see whether this call's function text
129                  * matches a function already in pg_proc.  If so just return the
130                  * OID of the existing set.
131                  */
132                 if (!strcmp(procedureName, GENERICSETNAME))
133                 {
134 #ifdef SETS_FIXED
135                         /* ----------
136                          * The code below doesn't work any more because the
137                          * PROSRC system cache and the pg_proc_prosrc_index
138                          * have been removed. Instead a sequential heap scan
139                          * or something better must get implemented. The reason
140                          * for removing is that nbtree index crashes if sources
141                          * exceed 2K what's likely for procedural languages.
142                          *
143                          * 1999/09/30 Jan
144                          * ----------
145                          */
146                         text       *prosrctext;
147
148                         prosrctext = textin(prosrc);
149                         tup = SearchSysCacheTuple(PROSRC,
150                                                                           PointerGetDatum(prosrctext),
151                                                                           0, 0, 0);
152                         pfree(prosrctext);
153                         if (HeapTupleIsValid(tup))
154                                 return tup->t_data->t_oid;
155 #else
156                         elog(ERROR, "lookup for procedure by source needs fix (Jan)");
157 #endif /* SETS_FIXED */
158                 }
159         }
160
161         tup = SearchSysCacheTuple(LANGNAME,
162                                                           PointerGetDatum(languageName),
163                                                           0, 0, 0);
164
165         if (!HeapTupleIsValid(tup))
166                 elog(ERROR, "ProcedureCreate: no such language %s", languageName);
167
168         languageObjectId = tup->t_data->t_oid;
169
170         if (strcmp(returnTypeName, "opaque") == 0)
171         {
172                 if (strcmp(languageName, "sql") == 0)
173                         elog(ERROR, "ProcedureCreate: sql functions cannot return type \"opaque\"");
174                 typeObjectId = 0;
175         }
176         else
177         {
178                 typeObjectId = TypeGet(returnTypeName, &defined);
179
180                 if (!OidIsValid(typeObjectId))
181                 {
182                         elog(NOTICE, "ProcedureCreate: type '%s' is not yet defined",
183                                  returnTypeName);
184 #ifdef NOT_USED
185                         elog(NOTICE, "ProcedureCreate: creating a shell for type '%s'",
186                                  returnTypeName);
187 #endif
188                         typeObjectId = TypeShellMake(returnTypeName);
189                         if (!OidIsValid(typeObjectId))
190                         {
191                                 elog(ERROR, "ProcedureCreate: could not create type '%s'",
192                                          returnTypeName);
193                         }
194                 }
195                 else if (!defined)
196                 {
197                         elog(NOTICE, "ProcedureCreate: return type '%s' is only a shell",
198                                  returnTypeName);
199                 }
200         }
201
202         /*
203          * don't allow functions of complex types that have the same name as
204          * existing attributes of the type
205          */
206         if (parameterCount == 1 &&
207                 (toid = TypeGet(strVal(lfirst(argList)), &defined)) &&
208                 defined &&
209                 (relid = typeidTypeRelid(toid)) != 0 &&
210                 get_attnum(relid, procedureName) != InvalidAttrNumber)
211                 elog(ERROR, "method %s already an attribute of type %s",
212                          procedureName, strVal(lfirst(argList)));
213
214
215         /*
216          * If this is a postquel procedure, we parse it here in order to be
217          * sure that it contains no syntax errors.      We should store the plan
218          * in an Inversion file for use later, but for now, we just store the
219          * procedure's text in the prosrc attribute.
220          */
221
222         if (strcmp(languageName, "sql") == 0)
223         {
224                 querytree_list = pg_parse_and_rewrite(prosrc, typev, parameterCount,
225                                                                                           FALSE);
226                 /* typecheck return value */
227                 pg_checkretval(typeObjectId, querytree_list);
228         }
229
230         /*
231          * If this is an internal procedure, check that the given internal
232          * function name (the 'prosrc' value) is a known builtin function.
233          *
234          * NOTE: in Postgres versions before 6.5, the SQL name of the created
235          * function could not be different from the internal name, and
236          * 'prosrc' wasn't used.  So there is code out there that does CREATE
237          * FUNCTION xyz AS '' LANGUAGE 'internal'.      To preserve some modicum
238          * of backwards compatibility, accept an empty 'prosrc' value as
239          * meaning the supplied SQL function name.
240          */
241
242         if (strcmp(languageName, "internal") == 0)
243         {
244                 if (strlen(prosrc) == 0)
245                         prosrc = procedureName;
246                 if (fmgr_lookupByName(prosrc) == (func_ptr) NULL)
247                         elog(ERROR,
248                                  "ProcedureCreate: there is no builtin function named \"%s\"",
249                                  prosrc);
250         }
251
252         /*
253          * All seems OK; prepare the tuple to be inserted into pg_proc.
254          */
255
256         for (i = 0; i < Natts_pg_proc; ++i)
257         {
258                 nulls[i] = ' ';
259                 values[i] = (Datum) NULL;
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++] = Int8GetDatum((bool) 0);
269         values[i++] = Int8GetDatum(trusted);
270         values[i++] = Int8GetDatum(canCache);
271         values[i++] = UInt16GetDatum(parameterCount);
272         values[i++] = Int8GetDatum(returnsSet);
273         values[i++] = ObjectIdGetDatum(typeObjectId);
274         values[i++] = (Datum) typev;
275         values[i++] = Int32GetDatum(byte_pct);          /* probyte_pct */
276         values[i++] = Int32GetDatum(perbyte_cpu);       /* properbyte_cpu */
277         values[i++] = Int32GetDatum(percall_cpu);       /* propercall_cpu */
278         values[i++] = Int32GetDatum(outin_ratio);       /* prooutin_ratio */
279         values[i++] = (Datum) fmgr(F_TEXTIN, prosrc);           /* prosrc */
280         values[i++] = (Datum) fmgr(F_TEXTIN, probin);           /* probin */
281
282         rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
283
284         tupDesc = rel->rd_att;
285         tup = heap_formtuple(tupDesc,
286                                                  values,
287                                                  nulls);
288
289         heap_insert(rel, tup);
290
291         if (RelationGetForm(rel)->relhasindex)
292         {
293                 Relation        idescs[Num_pg_proc_indices];
294
295                 CatalogOpenIndices(Num_pg_proc_indices, Name_pg_proc_indices, idescs);
296                 CatalogIndexInsert(idescs, Num_pg_proc_indices, rel, tup);
297                 CatalogCloseIndices(Num_pg_proc_indices, idescs);
298         }
299         heap_close(rel, RowExclusiveLock);
300         return tup->t_data->t_oid;
301 }