OSDN Git Service

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