OSDN Git Service

Error message editing in backend/catalog.
[pg-rex/syncrep.git] / 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-2002, 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.101 2003/07/21 01:59:11 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include "access/heapam.h"
18 #include "catalog/catname.h"
19 #include "catalog/dependency.h"
20 #include "catalog/indexing.h"
21 #include "catalog/pg_language.h"
22 #include "catalog/pg_proc.h"
23 #include "executor/executor.h"
24 #include "fmgr.h"
25 #include "miscadmin.h"
26 #include "parser/parse_coerce.h"
27 #include "parser/parse_expr.h"
28 #include "parser/parse_type.h"
29 #include "tcop/tcopprot.h"
30 #include "utils/builtins.h"
31 #include "utils/lsyscache.h"
32 #include "utils/sets.h"
33 #include "utils/syscache.h"
34
35
36 Datum           fmgr_internal_validator(PG_FUNCTION_ARGS);
37 Datum           fmgr_c_validator(PG_FUNCTION_ARGS);
38 Datum           fmgr_sql_validator(PG_FUNCTION_ARGS);
39
40
41 /* ----------------------------------------------------------------
42  *              ProcedureCreate
43  * ----------------------------------------------------------------
44  */
45 Oid
46 ProcedureCreate(const char *procedureName,
47                                 Oid procNamespace,
48                                 bool replace,
49                                 bool returnsSet,
50                                 Oid returnType,
51                                 Oid languageObjectId,
52                                 Oid languageValidator,
53                                 const char *prosrc,
54                                 const char *probin,
55                                 bool isAgg,
56                                 bool security_definer,
57                                 bool isStrict,
58                                 char volatility,
59                                 int parameterCount,
60                                 const Oid *parameterTypes)
61 {
62         int                     i;
63         Relation        rel;
64         HeapTuple       tup;
65         HeapTuple       oldtup;
66         char            nulls[Natts_pg_proc];
67         Datum           values[Natts_pg_proc];
68         char            replaces[Natts_pg_proc];
69         Oid                     typev[FUNC_MAX_ARGS];
70         Oid                     relid;
71         NameData        procname;
72         TupleDesc       tupDesc;
73         Oid                     retval;
74         bool            is_update;
75         ObjectAddress myself,
76                                 referenced;
77
78         /*
79          * sanity checks
80          */
81         Assert(PointerIsValid(prosrc));
82         Assert(PointerIsValid(probin));
83
84         if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS)
85                 ereport(ERROR,
86                                 (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
87                                  errmsg("functions cannot have more than %d arguments",
88                                                 FUNC_MAX_ARGS)));
89
90         /*
91          * Do not allow return type ANYARRAY or ANYELEMENT unless at least one
92          * argument is also ANYARRAY or ANYELEMENT
93          */
94         if (returnType == ANYARRAYOID || returnType == ANYELEMENTOID)
95         {
96                 bool    genericParam = false;
97
98                 for (i = 0; i < parameterCount; i++)
99                 {
100                         if (parameterTypes[i] == ANYARRAYOID ||
101                                 parameterTypes[i] == ANYELEMENTOID)
102                         {
103                                 genericParam = true;
104                                 break;
105                         }
106                 }
107
108                 if (!genericParam)
109                         ereport(ERROR,
110                                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
111                                          errmsg("cannot determine result datatype"),
112                                          errdetail("A function returning ANYARRAY or ANYELEMENT must have at least one argument of either type.")));
113         }
114
115         /* Make sure we have a zero-padded param type array */
116         MemSet(typev, 0, FUNC_MAX_ARGS * sizeof(Oid));
117         if (parameterCount > 0)
118                 memcpy(typev, parameterTypes, parameterCount * sizeof(Oid));
119
120         if (languageObjectId == SQLlanguageId)
121         {
122                 /*
123                  * If this call is defining a set, check if the set is already
124                  * defined by looking to see whether this call's function text
125                  * matches a function already in pg_proc.  If so just return the
126                  * OID of the existing set.
127                  */
128                 if (strcmp(procedureName, GENERICSETNAME) == 0)
129                 {
130 #ifdef SETS_FIXED
131
132                         /*
133                          * The code below doesn't work any more because the PROSRC
134                          * system cache and the pg_proc_prosrc_index have been
135                          * removed. Instead a sequential heap scan or something better
136                          * must get implemented. The reason for removing is that
137                          * nbtree index crashes if sources exceed 2K --- what's likely
138                          * for procedural languages.
139                          *
140                          * 1999/09/30 Jan
141                          */
142                         text       *prosrctext;
143
144                         prosrctext = DatumGetTextP(DirectFunctionCall1(textin,
145                                                                                            CStringGetDatum(prosrc)));
146                         retval = GetSysCacheOid(PROSRC,
147                                                                         PointerGetDatum(prosrctext),
148                                                                         0, 0, 0);
149                         pfree(prosrctext);
150                         if (OidIsValid(retval))
151                                 return retval;
152 #else
153                         elog(ERROR, "lookup for procedure by source needs fix (Jan)");
154 #endif   /* SETS_FIXED */
155                 }
156         }
157
158         /*
159          * don't allow functions of complex types that have the same name as
160          * existing attributes of the type
161          */
162         if (parameterCount == 1 && OidIsValid(typev[0]) &&
163                 (relid = typeidTypeRelid(typev[0])) != InvalidOid &&
164                 get_attnum(relid, (char *) procedureName) != InvalidAttrNumber)
165                 ereport(ERROR,
166                                 (errcode(ERRCODE_DUPLICATE_COLUMN),
167                                  errmsg("\"%s\" is already an attribute of type %s",
168                                                 procedureName, format_type_be(typev[0]))));
169
170         /*
171          * All seems OK; prepare the data to be inserted into pg_proc.
172          */
173
174         for (i = 0; i < Natts_pg_proc; ++i)
175         {
176                 nulls[i] = ' ';
177                 values[i] = (Datum) NULL;
178                 replaces[i] = 'r';
179         }
180
181         i = 0;
182         namestrcpy(&procname, procedureName);
183         values[i++] = NameGetDatum(&procname);          /* proname */
184         values[i++] = ObjectIdGetDatum(procNamespace);          /* pronamespace */
185         values[i++] = Int32GetDatum(GetUserId());       /* proowner */
186         values[i++] = ObjectIdGetDatum(languageObjectId);       /* prolang */
187         values[i++] = BoolGetDatum(isAgg);      /* proisagg */
188         values[i++] = BoolGetDatum(security_definer);           /* prosecdef */
189         values[i++] = BoolGetDatum(isStrict);           /* proisstrict */
190         values[i++] = BoolGetDatum(returnsSet);         /* proretset */
191         values[i++] = CharGetDatum(volatility);         /* provolatile */
192         values[i++] = UInt16GetDatum(parameterCount);           /* pronargs */
193         values[i++] = ObjectIdGetDatum(returnType); /* prorettype */
194         values[i++] = PointerGetDatum(typev);           /* proargtypes */
195         values[i++] = DirectFunctionCall1(textin,       /* prosrc */
196                                                                           CStringGetDatum(prosrc));
197         values[i++] = DirectFunctionCall1(textin,       /* probin */
198                                                                           CStringGetDatum(probin));
199         /* proacl will be handled below */
200
201         rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
202         tupDesc = rel->rd_att;
203
204         /* Check for pre-existing definition */
205         oldtup = SearchSysCache(PROCNAMENSP,
206                                                         PointerGetDatum(procedureName),
207                                                         UInt16GetDatum(parameterCount),
208                                                         PointerGetDatum(typev),
209                                                         ObjectIdGetDatum(procNamespace));
210
211         if (HeapTupleIsValid(oldtup))
212         {
213                 /* There is one; okay to replace it? */
214                 Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
215
216                 if (!replace)
217                         ereport(ERROR,
218                                         (errcode(ERRCODE_DUPLICATE_FUNCTION),
219                                          errmsg("function \"%s\" already exists with same argument types",
220                                                         procedureName)));
221                 if (GetUserId() != oldproc->proowner && !superuser())
222                         ereport(ERROR,
223                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
224                                          errmsg("you do not have permission to replace function \"%s\"",
225                                                         procedureName)));
226
227                 /*
228                  * Not okay to change the return type of the existing proc, since
229                  * existing rules, views, etc may depend on the return type.
230                  */
231                 if (returnType != oldproc->prorettype ||
232                         returnsSet != oldproc->proretset)
233                         ereport(ERROR,
234                                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
235                                          errmsg("cannot change return type of existing function"),
236                                          errhint("Use DROP FUNCTION first.")));
237
238                 /* Can't change aggregate status, either */
239                 if (oldproc->proisagg != isAgg)
240                 {
241                         if (oldproc->proisagg)
242                                 ereport(ERROR,
243                                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
244                                                  errmsg("function \"%s\" is an aggregate",
245                                                                 procedureName)));
246                         else
247                                 ereport(ERROR,
248                                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
249                                                  errmsg("function \"%s\" is not an aggregate",
250                                                                 procedureName)));
251                 }
252
253                 /* do not change existing ownership or permissions, either */
254                 replaces[Anum_pg_proc_proowner - 1] = ' ';
255                 replaces[Anum_pg_proc_proacl - 1] = ' ';
256
257                 /* Okay, do it... */
258                 tup = heap_modifytuple(oldtup, rel, values, nulls, replaces);
259                 simple_heap_update(rel, &tup->t_self, tup);
260
261                 ReleaseSysCache(oldtup);
262                 is_update = true;
263         }
264         else
265         {
266                 /* Creating a new procedure */
267
268                 /* start out with empty permissions */
269                 nulls[Anum_pg_proc_proacl - 1] = 'n';
270
271                 tup = heap_formtuple(tupDesc, values, nulls);
272                 simple_heap_insert(rel, tup);
273                 is_update = false;
274         }
275
276         /* Need to update indexes for either the insert or update case */
277         CatalogUpdateIndexes(rel, tup);
278
279         retval = HeapTupleGetOid(tup);
280
281         /*
282          * Create dependencies for the new function.  If we are updating an
283          * existing function, first delete any existing pg_depend entries.
284          */
285         if (is_update)
286                 deleteDependencyRecordsFor(RelOid_pg_proc, retval);
287
288         myself.classId = RelOid_pg_proc;
289         myself.objectId = retval;
290         myself.objectSubId = 0;
291
292         /* dependency on namespace */
293         referenced.classId = get_system_catalog_relid(NamespaceRelationName);
294         referenced.objectId = procNamespace;
295         referenced.objectSubId = 0;
296         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
297
298         /* dependency on implementation language */
299         referenced.classId = get_system_catalog_relid(LanguageRelationName);
300         referenced.objectId = languageObjectId;
301         referenced.objectSubId = 0;
302         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
303
304         /* dependency on return type */
305         referenced.classId = RelOid_pg_type;
306         referenced.objectId = returnType;
307         referenced.objectSubId = 0;
308         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
309
310         /* dependency on input types */
311         for (i = 0; i < parameterCount; i++)
312         {
313                 referenced.classId = RelOid_pg_type;
314                 referenced.objectId = typev[i];
315                 referenced.objectSubId = 0;
316                 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
317         }
318
319         heap_freetuple(tup);
320
321         heap_close(rel, RowExclusiveLock);
322
323         /* Verify function body */
324         if (OidIsValid(languageValidator))
325         {
326                 /* Advance command counter so new tuple can be seen by validator */
327                 CommandCounterIncrement();
328                 OidFunctionCall1(languageValidator, ObjectIdGetDatum(retval));
329         }
330
331         return retval;
332 }
333
334 /*
335  * check_sql_fn_retval() -- check return value of a list of sql parse trees.
336  *
337  * The return value of a sql function is the value returned by
338  * the final query in the function.  We do some ad-hoc type checking here
339  * to be sure that the user is returning the type he claims.
340  *
341  * This is normally applied during function definition, but in the case
342  * of a function with polymorphic arguments, we instead apply it during
343  * function execution startup.  The rettype is then the actual resolved
344  * output type of the function, rather than the declared type.  (Therefore,
345  * we should never see ANYARRAY or ANYELEMENT as rettype.)
346  */
347 void
348 check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
349 {
350         Query      *parse;
351         int                     cmd;
352         List       *tlist;
353         List       *tlistitem;
354         int                     tlistlen;
355         Oid                     typerelid;
356         Oid                     restype;
357         Relation        reln;
358         int                     relnatts;               /* physical number of columns in rel */
359         int                     rellogcols;             /* # of nondeleted columns in rel */
360         int                     colindex;               /* physical column index */
361
362         /* guard against empty function body; OK only if void return type */
363         if (queryTreeList == NIL)
364         {
365                 if (rettype != VOIDOID)
366                         ereport(ERROR,
367                                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
368                                          errmsg("return type mismatch in function declared to return %s",
369                                                         format_type_be(rettype)),
370                                          errdetail("Function's final statement must be a SELECT.")));
371                 return;
372         }
373
374         /* find the final query */
375         parse = (Query *) nth(length(queryTreeList) - 1, queryTreeList);
376
377         cmd = parse->commandType;
378         tlist = parse->targetList;
379
380         /*
381          * The last query must be a SELECT if and only if return type isn't
382          * VOID.
383          */
384         if (rettype == VOIDOID)
385         {
386                 if (cmd == CMD_SELECT)
387                         ereport(ERROR,
388                                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
389                                          errmsg("return type mismatch in function declared to return %s",
390                                                         format_type_be(rettype)),
391                                          errdetail("Function's final statement must not be a SELECT.")));
392                 return;
393         }
394
395         /* by here, the function is declared to return some type */
396         if (cmd != CMD_SELECT)
397                 ereport(ERROR,
398                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
399                                  errmsg("return type mismatch in function declared to return %s",
400                                                 format_type_be(rettype)),
401                                  errdetail("Function's final statement must be a SELECT.")));
402
403         /*
404          * Count the non-junk entries in the result targetlist.
405          */
406         tlistlen = ExecCleanTargetListLength(tlist);
407
408         typerelid = typeidTypeRelid(rettype);
409
410         if (fn_typtype == 'b' || fn_typtype == 'd')
411         {
412                 /* Shouldn't have a typerelid */
413                 Assert(typerelid == InvalidOid);
414
415                 /*
416                  * For base-type returns, the target list should have exactly one
417                  * entry, and its type should agree with what the user declared.
418                  * (As of Postgres 7.2, we accept binary-compatible types too.)
419                  */
420                 if (tlistlen != 1)
421                         ereport(ERROR,
422                                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
423                                          errmsg("return type mismatch in function declared to return %s",
424                                                         format_type_be(rettype)),
425                                          errdetail("Final SELECT must return exactly one column.")));
426
427                 restype = ((TargetEntry *) lfirst(tlist))->resdom->restype;
428                 if (!IsBinaryCoercible(restype, rettype))
429                         ereport(ERROR,
430                                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
431                                          errmsg("return type mismatch in function declared to return %s",
432                                                         format_type_be(rettype)),
433                                          errdetail("Actual return type is %s.",
434                                                            format_type_be(restype))));
435         }
436         else if (fn_typtype == 'c')
437         {
438                 /* Must have a typerelid */
439                 Assert(typerelid != InvalidOid);
440
441                 /*
442                  * If the target list is of length 1, and the type of the varnode
443                  * in the target list matches the declared return type, this is
444                  * okay. This can happen, for example, where the body of the
445                  * function is 'SELECT func2()', where func2 has the same return
446                  * type as the function that's calling it.
447                  */
448                 if (tlistlen == 1)
449                 {
450                         restype = ((TargetEntry *) lfirst(tlist))->resdom->restype;
451                         if (IsBinaryCoercible(restype, rettype))
452                                 return;
453                 }
454
455                 /*
456                  * Otherwise verify that the targetlist matches the return tuple
457                  * type. This part of the typechecking is a hack. We look up the
458                  * relation that is the declared return type, and scan the
459                  * non-deleted attributes to ensure that they match the datatypes
460                  * of the non-resjunk columns.
461                  */
462                 reln = relation_open(typerelid, AccessShareLock);
463                 relnatts = reln->rd_rel->relnatts;
464                 rellogcols = 0;                 /* we'll count nondeleted cols as we go */
465                 colindex = 0;
466
467                 foreach(tlistitem, tlist)
468                 {
469                         TargetEntry *tle = (TargetEntry *) lfirst(tlistitem);
470                         Form_pg_attribute attr;
471                         Oid                     tletype;
472                         Oid                     atttype;
473
474                         if (tle->resdom->resjunk)
475                                 continue;
476
477                         do
478                         {
479                                 colindex++;
480                                 if (colindex > relnatts)
481                                         ereport(ERROR,
482                                                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
483                                                          errmsg("return type mismatch in function declared to return %s",
484                                                                         format_type_be(rettype)),
485                                                          errdetail("Final SELECT returns too many columns.")));
486                                 attr = reln->rd_att->attrs[colindex - 1];
487                         } while (attr->attisdropped);
488                         rellogcols++;
489
490                         tletype = exprType((Node *) tle->expr);
491                         atttype = attr->atttypid;
492                         if (!IsBinaryCoercible(tletype, atttype))
493                                 ereport(ERROR,
494                                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
495                                                  errmsg("return type mismatch in function declared to return %s",
496                                                                 format_type_be(rettype)),
497                                                  errdetail("Final SELECT returns %s instead of %s at column %d.",
498                                                                    format_type_be(tletype),
499                                                                    format_type_be(atttype),
500                                                                    rellogcols)));
501                 }
502
503                 for (;;)
504                 {
505                         colindex++;
506                         if (colindex > relnatts)
507                                 break;
508                         if (!reln->rd_att->attrs[colindex - 1]->attisdropped)
509                                 rellogcols++;
510                 }
511
512                 if (tlistlen != rellogcols)
513                         ereport(ERROR,
514                                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
515                                          errmsg("return type mismatch in function declared to return %s",
516                                                         format_type_be(rettype)),
517                                          errdetail("Final SELECT returns too few columns.")));
518
519                 relation_close(reln, AccessShareLock);
520         }
521         else if (rettype == RECORDOID)
522         {
523                 /* Shouldn't have a typerelid */
524                 Assert(typerelid == InvalidOid);
525
526                 /*
527                  * For RECORD return type, defer this check until we get the first
528                  * tuple.
529                  */
530         }
531         else if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
532         {
533                 /* This should already have been caught ... */
534                 ereport(ERROR,
535                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
536                                  errmsg("cannot determine result datatype"),
537                                  errdetail("A function returning ANYARRAY or ANYELEMENT must have at least one argument of either type.")));
538         }
539         else
540                 ereport(ERROR,
541                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
542                                  errmsg("return type %s is not supported for SQL functions",
543                                                 format_type_be(rettype))));
544 }
545
546
547
548 /*
549  * Validator for internal functions
550  *
551  * Check that the given internal function name (the "prosrc" value) is
552  * a known builtin function.
553  */
554 Datum
555 fmgr_internal_validator(PG_FUNCTION_ARGS)
556 {
557         Oid                     funcoid = PG_GETARG_OID(0);
558         HeapTuple       tuple;
559         Form_pg_proc proc;
560         bool            isnull;
561         Datum           tmp;
562         char       *prosrc;
563
564         tuple = SearchSysCache(PROCOID,
565                                                    ObjectIdGetDatum(funcoid),
566                                                    0, 0, 0);
567         if (!HeapTupleIsValid(tuple))
568                 elog(ERROR, "cache lookup failed for function %u", funcoid);
569         proc = (Form_pg_proc) GETSTRUCT(tuple);
570
571         tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
572         if (isnull)
573                 elog(ERROR, "null prosrc");
574         prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
575
576         if (fmgr_internal_function(prosrc) == InvalidOid)
577                 ereport(ERROR,
578                                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
579                                  errmsg("there is no built-in function named \"%s\"",
580                                                 prosrc)));
581
582         ReleaseSysCache(tuple);
583
584         PG_RETURN_VOID();
585 }
586
587
588
589 /*
590  * Validator for C language functions
591  *
592  * Make sure that the library file exists, is loadable, and contains
593  * the specified link symbol. Also check for a valid function
594  * information record.
595  */
596 Datum
597 fmgr_c_validator(PG_FUNCTION_ARGS)
598 {
599         Oid                     funcoid = PG_GETARG_OID(0);
600         void       *libraryhandle;
601         HeapTuple       tuple;
602         Form_pg_proc proc;
603         bool            isnull;
604         Datum           tmp;
605         char       *prosrc;
606         char       *probin;
607
608         tuple = SearchSysCache(PROCOID,
609                                                    ObjectIdGetDatum(funcoid),
610                                                    0, 0, 0);
611         if (!HeapTupleIsValid(tuple))
612                 elog(ERROR, "cache lookup failed for function %u", funcoid);
613         proc = (Form_pg_proc) GETSTRUCT(tuple);
614
615         tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
616         if (isnull)
617                 elog(ERROR, "null prosrc");
618         prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
619
620         tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_probin, &isnull);
621         if (isnull)
622                 elog(ERROR, "null probin");
623         probin = DatumGetCString(DirectFunctionCall1(textout, tmp));
624
625         (void) load_external_function(probin, prosrc, true, &libraryhandle);
626         (void) fetch_finfo_record(libraryhandle, prosrc);
627
628         ReleaseSysCache(tuple);
629
630         PG_RETURN_VOID();
631 }
632
633
634 /*
635  * Validator for SQL language functions
636  *
637  * Parse it here in order to be sure that it contains no syntax
638  * errors.
639  */
640 Datum
641 fmgr_sql_validator(PG_FUNCTION_ARGS)
642 {
643         Oid                     funcoid = PG_GETARG_OID(0);
644         HeapTuple       tuple;
645         Form_pg_proc proc;
646         List       *querytree_list;
647         bool            isnull;
648         Datum           tmp;
649         char       *prosrc;
650         char            functyptype;
651         bool            haspolyarg;
652         int                     i;
653
654         tuple = SearchSysCache(PROCOID,
655                                                    ObjectIdGetDatum(funcoid),
656                                                    0, 0, 0);
657         if (!HeapTupleIsValid(tuple))
658                 elog(ERROR, "cache lookup failed for function %u", funcoid);
659         proc = (Form_pg_proc) GETSTRUCT(tuple);
660
661         functyptype = get_typtype(proc->prorettype);
662
663         /* Disallow pseudotype result */
664         /* except for RECORD, VOID, ANYARRAY, or ANYELEMENT */
665         if (functyptype == 'p' &&
666                 proc->prorettype != RECORDOID &&
667                 proc->prorettype != VOIDOID &&
668                 proc->prorettype != ANYARRAYOID &&
669                 proc->prorettype != ANYELEMENTOID)
670                 ereport(ERROR,
671                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
672                                  errmsg("SQL functions cannot return type %s",
673                                                 format_type_be(proc->prorettype))));
674
675         /* Disallow pseudotypes in arguments */
676         /* except for ANYARRAY or ANYELEMENT */
677         haspolyarg = false;
678         for (i = 0; i < proc->pronargs; i++)
679         {
680                 if (get_typtype(proc->proargtypes[i]) == 'p')
681                 {
682                         if (proc->proargtypes[i] == ANYARRAYOID ||
683                                 proc->proargtypes[i] == ANYELEMENTOID)
684                                 haspolyarg = true;
685                         else
686                                 ereport(ERROR,
687                                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
688                                                  errmsg("SQL functions cannot have arguments of type %s",
689                                                                 format_type_be(proc->proargtypes[i]))));
690                 }
691         }
692
693         tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
694         if (isnull)
695                 elog(ERROR, "null prosrc");
696
697         prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
698
699         /*
700          * We can't do full prechecking of the function definition if there are
701          * any polymorphic input types, because actual datatypes of expression
702          * results will be unresolvable.  The check will be done at runtime
703          * instead.
704          *
705          * We can run the text through the raw parser though; this will at
706          * least catch silly syntactic errors.
707          */
708         if (!haspolyarg)
709         {
710                 querytree_list = pg_parse_and_rewrite(prosrc,
711                                                                                           proc->proargtypes,
712                                                                                           proc->pronargs);
713                 check_sql_fn_retval(proc->prorettype, functyptype, querytree_list);
714         }
715         else
716         {
717                 querytree_list = pg_parse_query(prosrc);
718         }
719
720         ReleaseSysCache(tuple);
721
722         PG_RETURN_VOID();
723 }