Age Owner Branch data TLA Line data Source code
1 : : /*
2 : : * Python procedure manipulation for plpython
3 : : *
4 : : * src/pl/plpython/plpy_procedure.c
5 : : */
6 : :
7 : : #include "postgres.h"
8 : :
9 : : #include "access/htup_details.h"
10 : : #include "catalog/pg_proc.h"
11 : : #include "catalog/pg_type.h"
12 : : #include "funcapi.h"
13 : : #include "plpy_elog.h"
14 : : #include "plpy_main.h"
15 : : #include "plpy_procedure.h"
16 : : #include "plpy_util.h"
17 : : #include "utils/builtins.h"
18 : : #include "utils/hsearch.h"
19 : : #include "utils/memutils.h"
20 : : #include "utils/syscache.h"
21 : :
22 : : static HTAB *PLy_procedure_cache = NULL;
23 : :
24 : : static PLyProcedure *PLy_procedure_create(HeapTuple procTup, Oid fn_oid, PLyTrigType is_trigger);
25 : : static bool PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup);
26 : : static char *PLy_procedure_munge_source(const char *name, const char *src);
27 : :
28 : :
29 : : void
5011 peter_e@gmx.net 30 :CBC 23 : init_procedure_caches(void)
31 : : {
32 : : HASHCTL hash_ctl;
33 : :
4607 tgl@sss.pgh.pa.us 34 : 23 : hash_ctl.keysize = sizeof(PLyProcedureKey);
5011 peter_e@gmx.net 35 : 23 : hash_ctl.entrysize = sizeof(PLyProcedureEntry);
36 : 23 : PLy_procedure_cache = hash_create("PL/Python procedures", 32, &hash_ctl,
37 : : HASH_ELEM | HASH_BLOBS);
38 : 23 : }
39 : :
40 : : /*
41 : : * PLy_procedure_name: get the name of the specified procedure.
42 : : *
43 : : * NB: this returns the SQL name, not the internal Python procedure name
44 : : */
45 : : char *
46 : 531 : PLy_procedure_name(PLyProcedure *proc)
47 : : {
48 [ - + ]: 531 : if (proc == NULL)
5011 peter_e@gmx.net 49 :UBC 0 : return "<unknown procedure>";
5011 peter_e@gmx.net 50 :CBC 531 : return proc->proname;
51 : : }
52 : :
53 : : /*
54 : : * PLy_procedure_get: returns a cached PLyProcedure, or creates, stores and
55 : : * returns a new PLyProcedure.
56 : : *
57 : : * fn_oid is the OID of the function requested
58 : : * fn_rel is InvalidOid or the relation this function triggers on
59 : : * is_trigger denotes whether the function is a trigger function
60 : : *
61 : : * The reason that both fn_rel and is_trigger need to be passed is that when
62 : : * trigger functions get validated we don't know which relation(s) they'll
63 : : * be used with, so no sensible fn_rel can be passed.
64 : : */
65 : : PLyProcedure *
16 peter@eisentraut.org 66 :GNC 951 : PLy_procedure_get(Oid fn_oid, Oid fn_rel, PLyTrigType is_trigger)
67 : : {
68 : : bool use_cache;
69 : : HeapTuple procTup;
70 : : PLyProcedureKey key;
4607 tgl@sss.pgh.pa.us 71 :CBC 951 : PLyProcedureEntry *volatile entry = NULL;
72 : 951 : PLyProcedure *volatile proc = NULL;
73 : 951 : bool found = false;
74 : :
16 peter@eisentraut.org 75 [ + + + + ]:GNC 951 : if (is_trigger == PLPY_TRIGGER && fn_rel == InvalidOid)
76 : 24 : use_cache = false;
77 : : else
78 : 927 : use_cache = true;
79 : :
5011 peter_e@gmx.net 80 :CBC 951 : procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
81 [ - + ]: 951 : if (!HeapTupleIsValid(procTup))
5011 peter_e@gmx.net 82 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for function %u", fn_oid);
83 : :
84 : : /*
85 : : * Look for the function in the cache, unless we don't have the necessary
86 : : * information (e.g. during validation). In that case we just don't cache
87 : : * anything.
88 : : */
4607 tgl@sss.pgh.pa.us 89 [ + + ]:CBC 951 : if (use_cache)
90 : : {
91 : 927 : key.fn_oid = fn_oid;
92 : 927 : key.fn_rel = fn_rel;
93 : 927 : entry = hash_search(PLy_procedure_cache, &key, HASH_ENTER, &found);
94 : 927 : proc = entry->proc;
95 : : }
96 : :
5011 peter_e@gmx.net 97 [ + + ]: 951 : PG_TRY();
98 : : {
99 [ + + ]: 951 : if (!found)
100 : : {
101 : : /* Haven't found it, create a new procedure */
4607 tgl@sss.pgh.pa.us 102 : 274 : proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
103 [ + + ]: 270 : if (use_cache)
104 : 246 : entry->proc = proc;
105 : : }
106 [ + + ]: 677 : else if (!PLy_procedure_valid(proc, procTup))
107 : : {
108 : : /* Found it, but it's invalid, free and reuse the cache entry */
3593 109 : 5 : entry->proc = NULL;
110 [ + - ]: 5 : if (proc)
111 : 5 : PLy_procedure_delete(proc);
4607 112 : 5 : proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
113 : 5 : entry->proc = proc;
114 : : }
115 : : /* Found it and it's valid, it's fine to use it */
116 : : }
5011 peter_e@gmx.net 117 : 4 : PG_CATCH();
118 : : {
119 : : /* Do not leave an uninitialized entry in the cache */
4607 tgl@sss.pgh.pa.us 120 [ + - ]: 4 : if (use_cache)
121 : 4 : hash_search(PLy_procedure_cache, &key, HASH_REMOVE, NULL);
5011 peter_e@gmx.net 122 : 4 : PG_RE_THROW();
123 : : }
124 [ - + ]: 947 : PG_END_TRY();
125 : :
126 : 947 : ReleaseSysCache(procTup);
127 : :
4607 tgl@sss.pgh.pa.us 128 : 947 : return proc;
129 : : }
130 : :
131 : : /*
132 : : * Create a new PLyProcedure structure
133 : : */
134 : : static PLyProcedure *
16 peter@eisentraut.org 135 :GNC 279 : PLy_procedure_create(HeapTuple procTup, Oid fn_oid, PLyTrigType is_trigger)
136 : : {
137 : : char procName[NAMEDATALEN + 256];
138 : : Form_pg_proc procStruct;
139 : : PLyProcedure *volatile proc;
140 : : MemoryContext cxt;
141 : : MemoryContext oldcxt;
142 : : int rv;
143 : : char *ptr;
144 : :
5011 peter_e@gmx.net 145 :CBC 279 : procStruct = (Form_pg_proc) GETSTRUCT(procTup);
146 : 558 : rv = snprintf(procName, sizeof(procName),
147 : : "__plpython_procedure_%s_%u",
148 : 279 : NameStr(procStruct->proname),
149 : : fn_oid);
150 [ + - - + ]: 279 : if (rv >= sizeof(procName) || rv < 0)
5011 peter_e@gmx.net 151 [ # # ]:UBC 0 : elog(ERROR, "procedure name would overrun buffer");
152 : :
153 : : /* Replace any not-legal-in-Python-names characters with '_' */
3490 tgl@sss.pgh.pa.us 154 [ + + ]:CBC 13067 : for (ptr = procName; *ptr; ptr++)
155 : : {
156 [ + + + + ]: 12788 : if (!((*ptr >= 'A' && *ptr <= 'Z') ||
157 [ + + - + ]: 12787 : (*ptr >= 'a' && *ptr <= 'z') ||
158 [ + + + + ]: 3427 : (*ptr >= '0' && *ptr <= '9')))
159 : 1930 : *ptr = '_';
160 : : }
161 : :
162 : : /* Create long-lived context that all procedure info will live in */
2720 163 : 279 : cxt = AllocSetContextCreate(TopMemoryContext,
164 : : "PL/Python function",
165 : : ALLOCSET_DEFAULT_SIZES);
166 : :
3593 167 : 279 : oldcxt = MemoryContextSwitchTo(cxt);
168 : :
169 : 279 : proc = (PLyProcedure *) palloc0(sizeof(PLyProcedure));
170 : 279 : proc->mcxt = cxt;
171 : :
5011 peter_e@gmx.net 172 [ + + ]: 279 : PG_TRY();
173 : : {
174 : : Datum protrftypes_datum;
175 : : Datum prosrcdatum;
176 : : bool isnull;
177 : : char *procSource;
178 : : int i;
179 : :
3593 tgl@sss.pgh.pa.us 180 : 279 : proc->proname = pstrdup(NameStr(procStruct->proname));
2720 181 : 279 : MemoryContextSetIdentifier(cxt, proc->proname);
3593 182 : 279 : proc->pyname = pstrdup(procName);
183 : 279 : proc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
184 : 279 : proc->fn_tid = procTup->t_self;
3441 185 : 279 : proc->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE);
186 : 279 : proc->is_setof = procStruct->proretset;
2745 peter_e@gmx.net 187 : 279 : proc->is_procedure = (procStruct->prokind == PROKIND_PROCEDURE);
487 tgl@sss.pgh.pa.us 188 : 279 : proc->is_trigger = is_trigger;
3441 189 : 279 : proc->src = NULL;
190 : 279 : proc->argnames = NULL;
2851 191 : 279 : proc->args = NULL;
3593 192 : 279 : proc->nargs = 0;
193 : 279 : proc->langid = procStruct->prolang;
194 : 279 : protrftypes_datum = SysCacheGetAttr(PROCOID, procTup,
195 : : Anum_pg_proc_protrftypes,
196 : : &isnull);
197 [ + + ]: 279 : proc->trftypes = isnull ? NIL : oid_array_to_list(protrftypes_datum);
3441 198 : 279 : proc->code = NULL;
199 : 279 : proc->statics = NULL;
3593 200 : 279 : proc->globals = NULL;
3441 201 : 279 : proc->calldepth = 0;
202 : 279 : proc->argstack = NULL;
203 : :
204 : : /*
205 : : * get information required for output conversion of the return value,
206 : : * but only if this isn't a trigger.
207 : : */
16 peter@eisentraut.org 208 [ + + ]:GNC 279 : if (is_trigger == PLPY_NOT_TRIGGER)
209 : : {
2851 tgl@sss.pgh.pa.us 210 :CBC 228 : Oid rettype = procStruct->prorettype;
211 : : HeapTuple rvTypeTup;
212 : : Form_pg_type rvTypeStruct;
213 : :
214 : 228 : rvTypeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(rettype));
5011 peter_e@gmx.net 215 [ - + ]: 228 : if (!HeapTupleIsValid(rvTypeTup))
2851 tgl@sss.pgh.pa.us 216 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", rettype);
5011 peter_e@gmx.net 217 :CBC 228 : rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);
218 : :
219 : : /* Disallow pseudotype result, except for void or record */
220 [ + + ]: 228 : if (rvTypeStruct->typtype == TYPTYPE_PSEUDO)
221 : : {
2851 tgl@sss.pgh.pa.us 222 [ + + + + ]: 52 : if (rettype == VOIDOID ||
223 : : rettype == RECORDOID)
224 : : /* okay */ ;
1773 225 [ - + - - ]: 1 : else if (rettype == TRIGGEROID || rettype == EVENT_TRIGGEROID)
5011 peter_e@gmx.net 226 [ + - ]: 1 : ereport(ERROR,
227 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
228 : : errmsg("trigger functions can only be called as triggers")));
229 : : else
5011 peter_e@gmx.net 230 [ # # ]:UBC 0 : ereport(ERROR,
231 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
232 : : errmsg("PL/Python functions cannot return type %s",
233 : : format_type_be(rettype))));
234 : : }
235 : :
236 : : /* set up output function for procedure result */
2851 tgl@sss.pgh.pa.us 237 :CBC 227 : PLy_output_setup_func(&proc->result, proc->mcxt,
238 : : rettype, -1, proc);
239 : :
5011 peter_e@gmx.net 240 : 227 : ReleaseSysCache(rvTypeTup);
241 : : }
242 : : else
243 : : {
244 : : /*
245 : : * In a trigger function, we use proc->result and proc->result_in
246 : : * for converting tuples, but we don't yet have enough info to set
247 : : * them up. PLy_exec_trigger will deal with it.
248 : : */
2851 tgl@sss.pgh.pa.us 249 : 51 : proc->result.typoid = InvalidOid;
250 : 51 : proc->result_in.typoid = InvalidOid;
251 : : }
252 : :
253 : : /*
254 : : * Now get information required for input conversion of the
255 : : * procedure's arguments. Note that we ignore output arguments here.
256 : : * If the function returns record, those I/O functions will be set up
257 : : * when the function is first called.
258 : : */
5011 peter_e@gmx.net 259 [ + + ]: 278 : if (procStruct->pronargs)
260 : : {
261 : : Oid *types;
262 : : char **names,
263 : : *modes;
264 : : int pos,
265 : : total;
266 : :
267 : : /* extract argument type info from the pg_proc tuple */
268 : 107 : total = get_func_arg_info(procTup, &types, &names, &modes);
269 : :
270 : : /* count number of in+inout args into proc->nargs */
271 [ + + ]: 107 : if (modes == NULL)
272 : 96 : proc->nargs = total;
273 : : else
274 : : {
275 : : /* proc->nargs was initialized to 0 above */
276 [ + + ]: 41 : for (i = 0; i < total; i++)
277 : : {
1549 tgl@sss.pgh.pa.us 278 [ + + ]: 30 : if (modes[i] != PROARGMODE_OUT &&
5011 peter_e@gmx.net 279 [ + - ]: 18 : modes[i] != PROARGMODE_TABLE)
280 : 18 : (proc->nargs)++;
281 : : }
282 : : }
283 : :
284 : : /* Allocate arrays for per-input-argument data */
3593 tgl@sss.pgh.pa.us 285 : 107 : proc->argnames = (char **) palloc0(sizeof(char *) * proc->nargs);
2851 286 : 107 : proc->args = (PLyDatumToOb *) palloc0(sizeof(PLyDatumToOb) * proc->nargs);
287 : :
5011 peter_e@gmx.net 288 [ + + ]: 277 : for (i = pos = 0; i < total; i++)
289 : : {
290 : : HeapTuple argTypeTup;
291 : : Form_pg_type argTypeStruct;
292 : :
293 [ + + ]: 170 : if (modes &&
1549 tgl@sss.pgh.pa.us 294 [ + + ]: 30 : (modes[i] == PROARGMODE_OUT ||
5011 peter_e@gmx.net 295 [ - + ]: 18 : modes[i] == PROARGMODE_TABLE))
296 : 12 : continue; /* skip OUT arguments */
297 : :
298 [ - + ]: 158 : Assert(types[i] == procStruct->proargtypes.values[pos]);
299 : :
300 : 158 : argTypeTup = SearchSysCache1(TYPEOID,
301 : 158 : ObjectIdGetDatum(types[i]));
302 [ - + ]: 158 : if (!HeapTupleIsValid(argTypeTup))
5011 peter_e@gmx.net 303 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", types[i]);
5011 peter_e@gmx.net 304 :CBC 158 : argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);
305 : :
306 : : /* disallow pseudotype arguments */
2851 tgl@sss.pgh.pa.us 307 [ - + ]: 158 : if (argTypeStruct->typtype == TYPTYPE_PSEUDO)
2851 tgl@sss.pgh.pa.us 308 [ # # ]:UBC 0 : ereport(ERROR,
309 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
310 : : errmsg("PL/Python functions cannot accept type %s",
311 : : format_type_be(types[i]))));
312 : :
313 : : /* set up I/O function info */
2851 tgl@sss.pgh.pa.us 314 :CBC 158 : PLy_input_setup_func(&proc->args[pos], proc->mcxt,
315 : 158 : types[i], -1, /* typmod not known */
316 : : proc);
317 : :
318 : : /* get argument name */
3593 319 [ + + ]: 158 : proc->argnames[pos] = names ? pstrdup(names[i]) : NULL;
320 : :
5011 peter_e@gmx.net 321 : 158 : ReleaseSysCache(argTypeTup);
322 : :
323 : 158 : pos++;
324 : : }
325 : : }
326 : :
327 : : /*
328 : : * get the text of the function.
329 : : */
896 dgustafsson@postgres 330 : 278 : prosrcdatum = SysCacheGetAttrNotNull(PROCOID, procTup,
331 : : Anum_pg_proc_prosrc);
5011 peter_e@gmx.net 332 : 278 : procSource = TextDatumGetCString(prosrcdatum);
333 : :
334 : 278 : PLy_procedure_compile(proc, procSource);
335 : :
336 : 275 : pfree(procSource);
337 : : }
338 : 4 : PG_CATCH();
339 : : {
3593 tgl@sss.pgh.pa.us 340 : 4 : MemoryContextSwitchTo(oldcxt);
5011 peter_e@gmx.net 341 : 4 : PLy_procedure_delete(proc);
342 : 4 : PG_RE_THROW();
343 : : }
344 [ - + ]: 275 : PG_END_TRY();
345 : :
3593 tgl@sss.pgh.pa.us 346 : 275 : MemoryContextSwitchTo(oldcxt);
5011 peter_e@gmx.net 347 : 275 : return proc;
348 : : }
349 : :
350 : : /*
351 : : * Insert the procedure into the Python interpreter
352 : : */
353 : : void
354 : 299 : PLy_procedure_compile(PLyProcedure *proc, const char *src)
355 : : {
356 : 299 : PyObject *crv = NULL;
357 : : char *msrc;
358 : : PyObject *code0;
359 : :
360 : 299 : proc->globals = PyDict_Copy(PLy_interp_globals);
361 : :
362 : : /*
363 : : * SD is private preserved data between calls. GD is global data shared by
364 : : * all functions
365 : : */
366 : 299 : proc->statics = PyDict_New();
2867 367 [ - + ]: 299 : if (!proc->statics)
2867 peter_e@gmx.net 368 :UBC 0 : PLy_elog(ERROR, NULL);
5011 peter_e@gmx.net 369 :CBC 299 : PyDict_SetItemString(proc->globals, "SD", proc->statics);
370 : :
371 : : /*
372 : : * insert the function code into the interpreter
373 : : */
374 : 299 : msrc = PLy_procedure_munge_source(proc->pyname, src);
375 : : /* Save the mangled source for later inclusion in tracebacks */
3593 tgl@sss.pgh.pa.us 376 : 299 : proc->src = MemoryContextStrdup(proc->mcxt, msrc);
178 peter@eisentraut.org 377 : 299 : code0 = Py_CompileString(msrc, "<string>", Py_file_input);
378 [ + + ]: 299 : if (code0)
379 : 296 : crv = PyEval_EvalCode(code0, proc->globals, NULL);
5011 peter_e@gmx.net 380 : 299 : pfree(msrc);
381 : :
382 [ + + ]: 299 : if (crv != NULL)
383 : : {
384 : : int clen;
385 : : char call[NAMEDATALEN + 256];
386 : :
387 : : Py_DECREF(crv);
388 : :
389 : : /*
390 : : * compile a call to the function
391 : : */
392 : 296 : clen = snprintf(call, sizeof(call), "%s()", proc->pyname);
393 [ + - - + ]: 296 : if (clen < 0 || clen >= sizeof(call))
5011 peter_e@gmx.net 394 [ # # ]:UBC 0 : elog(ERROR, "string would overflow buffer");
5011 peter_e@gmx.net 395 :CBC 296 : proc->code = Py_CompileString(call, "<string>", Py_eval_input);
396 [ + - ]: 296 : if (proc->code != NULL)
397 : 296 : return;
398 : : }
399 : :
400 [ + - ]: 3 : if (proc->proname)
401 : 3 : PLy_elog(ERROR, "could not compile PL/Python function \"%s\"",
402 : : proc->proname);
403 : : else
5011 peter_e@gmx.net 404 :UBC 0 : PLy_elog(ERROR, "could not compile anonymous PL/Python code block");
405 : : }
406 : :
407 : : void
5011 peter_e@gmx.net 408 :CBC 30 : PLy_procedure_delete(PLyProcedure *proc)
409 : : {
410 : 30 : Py_XDECREF(proc->code);
411 : 30 : Py_XDECREF(proc->statics);
412 : 30 : Py_XDECREF(proc->globals);
3593 tgl@sss.pgh.pa.us 413 : 30 : MemoryContextDelete(proc->mcxt);
5011 peter_e@gmx.net 414 : 30 : }
415 : :
416 : : /*
417 : : * Decide whether a cached PLyProcedure struct is still valid
418 : : */
419 : : static bool
420 : 677 : PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
421 : : {
3593 tgl@sss.pgh.pa.us 422 [ - + ]: 677 : if (proc == NULL)
3593 tgl@sss.pgh.pa.us 423 :UBC 0 : return false;
424 : :
425 : : /* If the pg_proc tuple has changed, it's not valid */
4276 rhaas@postgresql.org 426 [ + + - + ]:CBC 1349 : if (!(proc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
5011 peter_e@gmx.net 427 : 672 : ItemPointerEquals(&proc->fn_tid, &procTup->t_self)))
428 : 5 : return false;
429 : :
2851 tgl@sss.pgh.pa.us 430 : 672 : return true;
431 : : }
432 : :
433 : : static char *
5011 peter_e@gmx.net 434 : 299 : PLy_procedure_munge_source(const char *name, const char *src)
435 : : {
436 : : char *mrc,
437 : : *mp;
438 : : const char *sp;
439 : : size_t mlen;
440 : : int plen;
441 : :
442 : : /*
443 : : * room for function source and the def statement
444 : : */
445 : 299 : mlen = (strlen(src) * 2) + strlen(name) + 16;
446 : :
447 : 299 : mrc = palloc(mlen);
448 : 299 : plen = snprintf(mrc, mlen, "def %s():\n\t", name);
449 [ + - - + ]: 299 : Assert(plen >= 0 && plen < mlen);
450 : :
451 : 299 : sp = src;
452 : 299 : mp = mrc + plen;
453 : :
454 [ + + ]: 32181 : while (*sp != '\0')
455 : : {
456 [ + + + + ]: 31882 : if (*sp == '\r' && *(sp + 1) == '\n')
457 : 3 : sp++;
458 : :
459 [ + + + + ]: 31882 : if (*sp == '\n' || *sp == '\r')
460 : : {
461 : 1346 : *mp++ = '\n';
462 : 1346 : *mp++ = '\t';
463 : 1346 : sp++;
464 : : }
465 : : else
466 : 30536 : *mp++ = *sp++;
467 : : }
468 : 299 : *mp++ = '\n';
469 : 299 : *mp++ = '\n';
470 : 299 : *mp = '\0';
471 : :
472 [ - + ]: 299 : if (mp > (mrc + mlen))
2224 michael@paquier.xyz 473 [ # # ]:UBC 0 : elog(FATAL, "buffer overrun in PLy_procedure_munge_source");
474 : :
5011 peter_e@gmx.net 475 :CBC 299 : return mrc;
476 : : }
|