1 /*-------------------------------------------------------------------------
4 * routines to support manipulation of the pg_proc relation
6 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.41 2000/04/04 21:44:37 tgl Exp $
13 *-------------------------------------------------------------------------
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"
33 /* ----------------------------------------------------------------
35 * ----------------------------------------------------------------
38 ProcedureCreate(char *procedureName,
57 uint16 parameterCount;
58 char nulls[Natts_pg_proc];
59 Datum values[Natts_pg_proc];
64 Oid typev[FUNC_MAX_ARGS];
74 Assert(PointerIsValid(prosrc));
75 Assert(PointerIsValid(probin));
78 MemSet(typev, 0, FUNC_MAX_ARGS * sizeof(Oid));
83 if (parameterCount >= FUNC_MAX_ARGS)
84 elog(ERROR, "Procedures cannot take more than %d arguments",
87 if (strcmp(strVal(t), "opaque") == 0)
89 if (strcmp(languageName, "sql") == 0)
90 elog(ERROR, "ProcedureCreate: sql functions cannot take type \"opaque\"");
95 toid = TypeGet(strVal(t), &defined);
97 if (!OidIsValid(toid))
99 elog(ERROR, "ProcedureCreate: arg type '%s' is not defined",
105 elog(NOTICE, "ProcedureCreate: arg type '%s' is only a shell",
110 typev[parameterCount++] = toid;
113 tup = SearchSysCacheTuple(PROCNAME,
114 PointerGetDatum(procedureName),
115 UInt16GetDatum(parameterCount),
116 PointerGetDatum(typev),
119 if (HeapTupleIsValid(tup))
120 elog(ERROR, "ProcedureCreate: procedure %s already exists with same arguments",
123 if (!strcmp(languageName, "sql"))
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.
132 if (!strcmp(procedureName, GENERICSETNAME))
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.
148 prosrctext = textin(prosrc);
149 tup = SearchSysCacheTuple(PROSRC,
150 PointerGetDatum(prosrctext),
153 if (HeapTupleIsValid(tup))
154 return tup->t_data->t_oid;
156 elog(ERROR, "lookup for procedure by source needs fix (Jan)");
157 #endif /* SETS_FIXED */
161 tup = SearchSysCacheTuple(LANGNAME,
162 PointerGetDatum(languageName),
165 if (!HeapTupleIsValid(tup))
166 elog(ERROR, "ProcedureCreate: no such language %s", languageName);
168 languageObjectId = tup->t_data->t_oid;
170 if (strcmp(returnTypeName, "opaque") == 0)
172 if (strcmp(languageName, "sql") == 0)
173 elog(ERROR, "ProcedureCreate: sql functions cannot return type \"opaque\"");
178 typeObjectId = TypeGet(returnTypeName, &defined);
180 if (!OidIsValid(typeObjectId))
182 elog(NOTICE, "ProcedureCreate: type '%s' is not yet defined",
185 elog(NOTICE, "ProcedureCreate: creating a shell for type '%s'",
188 typeObjectId = TypeShellMake(returnTypeName);
189 if (!OidIsValid(typeObjectId))
191 elog(ERROR, "ProcedureCreate: could not create type '%s'",
197 elog(NOTICE, "ProcedureCreate: return type '%s' is only a shell",
203 * don't allow functions of complex types that have the same name as
204 * existing attributes of the type
206 if (parameterCount == 1 &&
207 (toid = TypeGet(strVal(lfirst(argList)), &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)));
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.
222 if (strcmp(languageName, "sql") == 0)
224 querytree_list = pg_parse_and_rewrite(prosrc, typev, parameterCount,
226 /* typecheck return value */
227 pg_checkretval(typeObjectId, querytree_list);
231 * If this is an internal procedure, check that the given internal
232 * function name (the 'prosrc' value) is a known builtin function.
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.
242 if (strcmp(languageName, "internal") == 0)
244 if (strlen(prosrc) == 0)
245 prosrc = procedureName;
246 if (fmgr_lookupByName(prosrc) == (func_ptr) NULL)
248 "ProcedureCreate: there is no builtin function named \"%s\"",
253 * All seems OK; prepare the tuple to be inserted into pg_proc.
256 for (i = 0; i < Natts_pg_proc; ++i)
259 values[i] = (Datum) NULL;
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 */
282 rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
284 tupDesc = rel->rd_att;
285 tup = heap_formtuple(tupDesc,
289 heap_insert(rel, tup);
291 if (RelationGetForm(rel)->relhasindex)
293 Relation idescs[Num_pg_proc_indices];
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);
299 heap_close(rel, RowExclusiveLock);
300 return tup->t_data->t_oid;