]> granicus.if.org Git - postgresql/blob - src/pl/plpgsql/src/pl_handler.c
Magic blocks don't do us any good unless we use 'em ... so install one
[postgresql] / src / pl / plpgsql / src / pl_handler.c
1 /*-------------------------------------------------------------------------
2  *
3  * pl_handler.c         - Handler for the PL/pgSQL
4  *                        procedural language
5  *
6  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.29 2006/05/30 22:12:16 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include "plpgsql.h"
17 #include "pl.tab.h"
18
19 #include "access/heapam.h"
20 #include "catalog/pg_proc.h"
21 #include "catalog/pg_type.h"
22 #include "funcapi.h"
23 #include "utils/builtins.h"
24 #include "utils/lsyscache.h"
25 #include "utils/syscache.h"
26
27 extern DLLIMPORT bool check_function_bodies;
28
29 PG_MODULE_MAGIC;
30
31 static bool plpgsql_firstcall = true;
32
33 static void plpgsql_init_all(void);
34
35
36 /*
37  * plpgsql_init()                       - postmaster-startup safe initialization
38  *
39  * DO NOT make this static --- it has to be callable by preload
40  */
41 void
42 plpgsql_init(void)
43 {
44         /* Do initialization only once */
45         if (!plpgsql_firstcall)
46                 return;
47
48         plpgsql_HashTableInit();
49         RegisterXactCallback(plpgsql_xact_cb, NULL);
50         plpgsql_firstcall = false;
51 }
52
53 /*
54  * plpgsql_init_all()           - Initialize all
55  */
56 static void
57 plpgsql_init_all(void)
58 {
59         /* Execute any postmaster-startup safe initialization */
60         plpgsql_init();
61
62         /*
63          * Any other initialization that must be done each time a new backend
64          * starts -- currently none
65          */
66 }
67
68 /* ----------
69  * plpgsql_call_handler
70  *
71  * The PostgreSQL function manager and trigger manager
72  * call this function for execution of PL/pgSQL procedures.
73  * ----------
74  */
75 PG_FUNCTION_INFO_V1(plpgsql_call_handler);
76
77 Datum
78 plpgsql_call_handler(PG_FUNCTION_ARGS)
79 {
80         PLpgSQL_function *func;
81         Datum           retval;
82         int                     rc;
83
84         /* perform initialization */
85         plpgsql_init_all();
86
87         /*
88          * Connect to SPI manager
89          */
90         if ((rc = SPI_connect()) != SPI_OK_CONNECT)
91                 elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
92
93         /* Find or compile the function */
94         func = plpgsql_compile(fcinfo, false);
95
96         /*
97          * Determine if called as function or trigger and call appropriate
98          * subhandler
99          */
100         if (CALLED_AS_TRIGGER(fcinfo))
101                 retval = PointerGetDatum(plpgsql_exec_trigger(func,
102                                                                                    (TriggerData *) fcinfo->context));
103         else
104                 retval = plpgsql_exec_function(func, fcinfo);
105
106         /*
107          * Disconnect from SPI manager
108          */
109         if ((rc = SPI_finish()) != SPI_OK_FINISH)
110                 elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
111
112         return retval;
113 }
114
115 /* ----------
116  * plpgsql_validator
117  *
118  * This function attempts to validate a PL/pgSQL function at
119  * CREATE FUNCTION time.
120  * ----------
121  */
122 PG_FUNCTION_INFO_V1(plpgsql_validator);
123
124 Datum
125 plpgsql_validator(PG_FUNCTION_ARGS)
126 {
127         Oid                     funcoid = PG_GETARG_OID(0);
128         HeapTuple       tuple;
129         Form_pg_proc proc;
130         char            functyptype;
131         int                     numargs;
132         Oid                *argtypes;
133         char      **argnames;
134         char       *argmodes;
135         bool            istrigger = false;
136         int                     i;
137
138         /* perform initialization */
139         plpgsql_init_all();
140
141         /* Get the new function's pg_proc entry */
142         tuple = SearchSysCache(PROCOID,
143                                                    ObjectIdGetDatum(funcoid),
144                                                    0, 0, 0);
145         if (!HeapTupleIsValid(tuple))
146                 elog(ERROR, "cache lookup failed for function %u", funcoid);
147         proc = (Form_pg_proc) GETSTRUCT(tuple);
148
149         functyptype = get_typtype(proc->prorettype);
150
151         /* Disallow pseudotype result */
152         /* except for TRIGGER, RECORD, VOID, ANYARRAY, or ANYELEMENT */
153         if (functyptype == 'p')
154         {
155                 /* we assume OPAQUE with no arguments means a trigger */
156                 if (proc->prorettype == TRIGGEROID ||
157                         (proc->prorettype == OPAQUEOID && proc->pronargs == 0))
158                         istrigger = true;
159                 else if (proc->prorettype != RECORDOID &&
160                                  proc->prorettype != VOIDOID &&
161                                  proc->prorettype != ANYARRAYOID &&
162                                  proc->prorettype != ANYELEMENTOID)
163                         ereport(ERROR,
164                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
165                                          errmsg("plpgsql functions cannot return type %s",
166                                                         format_type_be(proc->prorettype))));
167         }
168
169         /* Disallow pseudotypes in arguments (either IN or OUT) */
170         /* except for ANYARRAY or ANYELEMENT */
171         numargs = get_func_arg_info(tuple,
172                                                                 &argtypes, &argnames, &argmodes);
173         for (i = 0; i < numargs; i++)
174         {
175                 if (get_typtype(argtypes[i]) == 'p')
176                 {
177                         if (argtypes[i] != ANYARRAYOID &&
178                                 argtypes[i] != ANYELEMENTOID)
179                                 ereport(ERROR,
180                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
181                                                  errmsg("plpgsql functions cannot take type %s",
182                                                           format_type_be(argtypes[i]))));
183                 }
184         }
185
186         /* Postpone body checks if !check_function_bodies */
187         if (check_function_bodies)
188         {
189                 FunctionCallInfoData fake_fcinfo;
190                 FmgrInfo        flinfo;
191                 TriggerData trigdata;
192                 int                     rc;
193
194                 /*
195                  * Connect to SPI manager (is this needed for compilation?)
196                  */
197                 if ((rc = SPI_connect()) != SPI_OK_CONNECT)
198                         elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
199
200                 /*
201                  * Set up a fake fcinfo with just enough info to satisfy
202                  * plpgsql_compile().
203                  */
204                 MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
205                 MemSet(&flinfo, 0, sizeof(flinfo));
206                 fake_fcinfo.flinfo = &flinfo;
207                 flinfo.fn_oid = funcoid;
208                 flinfo.fn_mcxt = CurrentMemoryContext;
209                 if (istrigger)
210                 {
211                         MemSet(&trigdata, 0, sizeof(trigdata));
212                         trigdata.type = T_TriggerData;
213                         fake_fcinfo.context = (Node *) &trigdata;
214                 }
215
216                 /* Test-compile the function */
217                 plpgsql_compile(&fake_fcinfo, true);
218
219                 /*
220                  * Disconnect from SPI manager
221                  */
222                 if ((rc = SPI_finish()) != SPI_OK_FINISH)
223                         elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
224         }
225
226         ReleaseSysCache(tuple);
227
228         PG_RETURN_VOID();
229 }