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