PostgreSQL Source Code  git master
plpy_main.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "miscadmin.h"
#include "plpy_elog.h"
#include "plpy_exec.h"
#include "plpy_main.h"
#include "plpy_plpymodule.h"
#include "plpy_procedure.h"
#include "plpy_subxactobject.h"
#include "plpython.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/syscache.h"
Include dependency graph for plpy_main.c:

Go to the source code of this file.

Functions

 PG_FUNCTION_INFO_V1 (plpython3_validator)
 
 PG_FUNCTION_INFO_V1 (plpython3_call_handler)
 
 PG_FUNCTION_INFO_V1 (plpython3_inline_handler)
 
static bool PLy_procedure_is_trigger (Form_pg_proc procStruct)
 
static void plpython_error_callback (void *arg)
 
static void plpython_inline_error_callback (void *arg)
 
static void PLy_init_interp (void)
 
static PLyExecutionContextPLy_push_execution_context (bool atomic_context)
 
static void PLy_pop_execution_context (void)
 
void _PG_init (void)
 
static void PLy_initialize (void)
 
Datum plpython3_validator (PG_FUNCTION_ARGS)
 
Datum plpython3_call_handler (PG_FUNCTION_ARGS)
 
Datum plpython3_inline_handler (PG_FUNCTION_ARGS)
 
PLyExecutionContextPLy_current_execution_context (void)
 
MemoryContext PLy_get_scratch_context (PLyExecutionContext *context)
 

Variables

 PG_MODULE_MAGIC
 
static int * plpython_version_bitmask_ptr = NULL
 
static int plpython_version_bitmask = 0
 
PyObject * PLy_interp_globals = NULL
 
static PLyExecutionContextPLy_execution_contexts = NULL
 

Function Documentation

◆ _PG_init()

void _PG_init ( void  )

Definition at line 58 of file plpy_main.c.

59 {
60  int **bitmask_ptr;
61 
62  /*
63  * Set up a shared bitmask variable telling which Python version(s) are
64  * loaded into this process's address space. If there's more than one, we
65  * cannot call into libpython for fear of causing crashes. But postpone
66  * the actual failure for later, so that operations like pg_restore can
67  * load more than one plpython library so long as they don't try to do
68  * anything much with the language.
69  *
70  * While we only support Python 3 these days, somebody might create an
71  * out-of-tree version adding back support for Python 2. Conflicts with
72  * such an extension should be detected.
73  */
74  bitmask_ptr = (int **) find_rendezvous_variable("plpython_version_bitmask");
75  if (!(*bitmask_ptr)) /* am I the first? */
76  *bitmask_ptr = &plpython_version_bitmask;
77  /* Retain pointer to the agreed-on shared variable ... */
78  plpython_version_bitmask_ptr = *bitmask_ptr;
79  /* ... and announce my presence */
80  *plpython_version_bitmask_ptr |= (1 << PY_MAJOR_VERSION);
81 
82  /*
83  * This should be safe even in the presence of conflicting plpythons, and
84  * it's necessary to do it before possibly throwing a conflict error, or
85  * the error message won't get localized.
86  */
88 }
void ** find_rendezvous_variable(const char *varName)
Definition: dfmgr.c:599
#define TEXTDOMAIN
Definition: elog.h:152
void pg_bindtextdomain(const char *domain)
Definition: miscinit.c:1880
static int plpython_version_bitmask
Definition: plpy_main.c:48
static int * plpython_version_bitmask_ptr
Definition: plpy_main.c:47

References find_rendezvous_variable(), pg_bindtextdomain(), plpython_version_bitmask, plpython_version_bitmask_ptr, and TEXTDOMAIN.

◆ PG_FUNCTION_INFO_V1() [1/3]

PG_FUNCTION_INFO_V1 ( plpython3_call_handler  )

◆ PG_FUNCTION_INFO_V1() [2/3]

PG_FUNCTION_INFO_V1 ( plpython3_inline_handler  )

◆ PG_FUNCTION_INFO_V1() [3/3]

PG_FUNCTION_INFO_V1 ( plpython3_validator  )

◆ plpython3_call_handler()

Datum plpython3_call_handler ( PG_FUNCTION_ARGS  )

Definition at line 191 of file plpy_main.c.

192 {
193  bool nonatomic;
194  Datum retval;
195  PLyExecutionContext *exec_ctx;
196  ErrorContextCallback plerrcontext;
197 
198  PLy_initialize();
199 
200  nonatomic = fcinfo->context &&
201  IsA(fcinfo->context, CallContext) &&
202  !castNode(CallContext, fcinfo->context)->atomic;
203 
204  /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
205  if (SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0) != SPI_OK_CONNECT)
206  elog(ERROR, "SPI_connect failed");
207 
208  /*
209  * Push execution context onto stack. It is important that this get
210  * popped again, so avoid putting anything that could throw error between
211  * here and the PG_TRY.
212  */
213  exec_ctx = PLy_push_execution_context(!nonatomic);
214 
215  PG_TRY();
216  {
217  Oid funcoid = fcinfo->flinfo->fn_oid;
218  PLyProcedure *proc;
219 
220  /*
221  * Setup error traceback support for ereport(). Note that the PG_TRY
222  * structure pops this for us again at exit, so we needn't do that
223  * explicitly, nor do we risk the callback getting called after we've
224  * destroyed the exec_ctx.
225  */
226  plerrcontext.callback = plpython_error_callback;
227  plerrcontext.arg = exec_ctx;
228  plerrcontext.previous = error_context_stack;
229  error_context_stack = &plerrcontext;
230 
231  if (CALLED_AS_TRIGGER(fcinfo))
232  {
233  Relation tgrel = ((TriggerData *) fcinfo->context)->tg_relation;
234  HeapTuple trv;
235 
236  proc = PLy_procedure_get(funcoid, RelationGetRelid(tgrel), true);
237  exec_ctx->curr_proc = proc;
238  trv = PLy_exec_trigger(fcinfo, proc);
239  retval = PointerGetDatum(trv);
240  }
241  else
242  {
243  proc = PLy_procedure_get(funcoid, InvalidOid, false);
244  exec_ctx->curr_proc = proc;
245  retval = PLy_exec_function(fcinfo, proc);
246  }
247  }
248  PG_CATCH();
249  {
251  PyErr_Clear();
252  PG_RE_THROW();
253  }
254  PG_END_TRY();
255 
256  /* Destroy the execution context */
258 
259  return retval;
260 }
ErrorContextCallback * error_context_stack
Definition: elog.c:94
#define PG_RE_THROW()
Definition: elog.h:411
#define PG_TRY(...)
Definition: elog.h:370
#define PG_END_TRY(...)
Definition: elog.h:395
#define ERROR
Definition: elog.h:39
#define PG_CATCH(...)
Definition: elog.h:380
#define elog(elevel,...)
Definition: elog.h:224
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
Datum PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc)
Definition: plpy_exec.c:55
HeapTuple PLy_exec_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc)
Definition: plpy_exec.c:305
static void PLy_initialize(void)
Definition: plpy_main.c:95
static void plpython_error_callback(void *arg)
Definition: plpy_main.c:345
static PLyExecutionContext * PLy_push_execution_context(bool atomic_context)
Definition: plpy_main.c:391
static void PLy_pop_execution_context(void)
Definition: plpy_main.c:407
PLyProcedure * PLy_procedure_get(Oid fn_oid, Oid fn_rel, bool is_trigger)
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetRelid(relation)
Definition: rel.h:505
int SPI_connect_ext(int options)
Definition: spi.c:100
#define SPI_OPT_NONATOMIC
Definition: spi.h:102
#define SPI_OK_CONNECT
Definition: spi.h:82
struct ErrorContextCallback * previous
Definition: elog.h:295
void(* callback)(void *arg)
Definition: elog.h:296
PLyProcedure * curr_proc
Definition: plpy_main.h:20
#define CALLED_AS_TRIGGER(fcinfo)
Definition: trigger.h:26

References ErrorContextCallback::arg, ErrorContextCallback::callback, CALLED_AS_TRIGGER, castNode, PLyExecutionContext::curr_proc, elog, ERROR, error_context_stack, InvalidOid, IsA, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, plpython_error_callback(), PLy_exec_function(), PLy_exec_trigger(), PLy_initialize(), PLy_pop_execution_context(), PLy_procedure_get(), PLy_push_execution_context(), PointerGetDatum(), ErrorContextCallback::previous, RelationGetRelid, SPI_connect_ext(), SPI_OK_CONNECT, and SPI_OPT_NONATOMIC.

◆ plpython3_inline_handler()

Datum plpython3_inline_handler ( PG_FUNCTION_ARGS  )

Definition at line 263 of file plpy_main.c.

264 {
265  LOCAL_FCINFO(fake_fcinfo, 0);
267  FmgrInfo flinfo;
268  PLyProcedure proc;
269  PLyExecutionContext *exec_ctx;
270  ErrorContextCallback plerrcontext;
271 
272  PLy_initialize();
273 
274  /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
275  if (SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC) != SPI_OK_CONNECT)
276  elog(ERROR, "SPI_connect failed");
277 
278  MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
279  MemSet(&flinfo, 0, sizeof(flinfo));
280  fake_fcinfo->flinfo = &flinfo;
281  flinfo.fn_oid = InvalidOid;
282  flinfo.fn_mcxt = CurrentMemoryContext;
283 
284  MemSet(&proc, 0, sizeof(PLyProcedure));
286  "__plpython_inline_block",
288  proc.pyname = MemoryContextStrdup(proc.mcxt, "__plpython_inline_block");
289  proc.langid = codeblock->langOid;
290 
291  /*
292  * This is currently sufficient to get PLy_exec_function to work, but
293  * someday we might need to be honest and use PLy_output_setup_func.
294  */
295  proc.result.typoid = VOIDOID;
296 
297  /*
298  * Push execution context onto stack. It is important that this get
299  * popped again, so avoid putting anything that could throw error between
300  * here and the PG_TRY.
301  */
302  exec_ctx = PLy_push_execution_context(codeblock->atomic);
303 
304  PG_TRY();
305  {
306  /*
307  * Setup error traceback support for ereport().
308  * plpython_inline_error_callback doesn't currently need exec_ctx, but
309  * for consistency with plpython3_call_handler we do it the same way.
310  */
312  plerrcontext.arg = exec_ctx;
313  plerrcontext.previous = error_context_stack;
314  error_context_stack = &plerrcontext;
315 
316  PLy_procedure_compile(&proc, codeblock->source_text);
317  exec_ctx->curr_proc = &proc;
318  PLy_exec_function(fake_fcinfo, &proc);
319  }
320  PG_CATCH();
321  {
323  PLy_procedure_delete(&proc);
324  PyErr_Clear();
325  PG_RE_THROW();
326  }
327  PG_END_TRY();
328 
329  /* Destroy the execution context */
331 
332  /* Now clean up the transient procedure we made */
333  PLy_procedure_delete(&proc);
334 
335  PG_RETURN_VOID();
336 }
#define MemSet(start, val, len)
Definition: c.h:1020
#define PG_RETURN_VOID()
Definition: fmgr.h:349
#define SizeForFunctionCallInfo(nargs)
Definition: fmgr.h:102
#define PG_GETARG_DATUM(n)
Definition: fmgr.h:268
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
MemoryContext TopMemoryContext
Definition: mcxt.c:149
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
char * MemoryContextStrdup(MemoryContext context, const char *string)
Definition: mcxt.c:1682
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
static void plpython_inline_error_callback(void *arg)
Definition: plpy_main.c:361
void PLy_procedure_compile(PLyProcedure *proc, const char *src)
void PLy_procedure_delete(PLyProcedure *proc)
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
Definition: fmgr.h:57
MemoryContext fn_mcxt
Definition: fmgr.h:65
Oid fn_oid
Definition: fmgr.h:59
char * source_text
Definition: parsenodes.h:3497
PLyObToDatum result
MemoryContext mcxt

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, ErrorContextCallback::arg, InlineCodeBlock::atomic, ErrorContextCallback::callback, PLyExecutionContext::curr_proc, CurrentMemoryContext, DatumGetPointer(), elog, ERROR, error_context_stack, FmgrInfo::fn_mcxt, FmgrInfo::fn_oid, InvalidOid, PLyProcedure::langid, InlineCodeBlock::langOid, LOCAL_FCINFO, PLyProcedure::mcxt, MemoryContextStrdup(), MemSet, PG_CATCH, PG_END_TRY, PG_GETARG_DATUM, PG_RE_THROW, PG_RETURN_VOID, PG_TRY, plpython_inline_error_callback(), PLy_exec_function(), PLy_initialize(), PLy_pop_execution_context(), PLy_procedure_compile(), PLy_procedure_delete(), PLy_push_execution_context(), ErrorContextCallback::previous, PLyProcedure::pyname, PLyProcedure::result, SizeForFunctionCallInfo, InlineCodeBlock::source_text, SPI_connect_ext(), SPI_OK_CONNECT, SPI_OPT_NONATOMIC, TopMemoryContext, and PLyObToDatum::typoid.

◆ plpython3_validator()

Datum plpython3_validator ( PG_FUNCTION_ARGS  )

Definition at line 158 of file plpy_main.c.

159 {
160  Oid funcoid = PG_GETARG_OID(0);
161  HeapTuple tuple;
162  Form_pg_proc procStruct;
163  bool is_trigger;
164 
165  if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
166  PG_RETURN_VOID();
167 
169  PG_RETURN_VOID();
170 
171  /* Do this only after making sure we need to do something */
172  PLy_initialize();
173 
174  /* Get the new function's pg_proc entry */
175  tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
176  if (!HeapTupleIsValid(tuple))
177  elog(ERROR, "cache lookup failed for function %u", funcoid);
178  procStruct = (Form_pg_proc) GETSTRUCT(tuple);
179 
180  is_trigger = PLy_procedure_is_trigger(procStruct);
181 
182  ReleaseSysCache(tuple);
183 
184  /* We can't validate triggers against any particular table ... */
185  PLy_procedure_get(funcoid, InvalidOid, is_trigger);
186 
187  PG_RETURN_VOID();
188 }
bool CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid)
Definition: fmgr.c:2145
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
bool check_function_bodies
Definition: guc_tables.c:514
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:136
static bool PLy_procedure_is_trigger(Form_pg_proc procStruct)
Definition: plpy_main.c:339
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:266
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:218

References check_function_bodies, CheckFunctionValidatorAccess(), elog, ERROR, GETSTRUCT, HeapTupleIsValid, InvalidOid, ObjectIdGetDatum(), PG_GETARG_OID, PG_RETURN_VOID, PLy_initialize(), PLy_procedure_get(), PLy_procedure_is_trigger(), ReleaseSysCache(), and SearchSysCache1().

◆ plpython_error_callback()

static void plpython_error_callback ( void *  arg)
static

Definition at line 345 of file plpy_main.c.

346 {
348 
349  if (exec_ctx->curr_proc)
350  {
351  if (exec_ctx->curr_proc->is_procedure)
352  errcontext("PL/Python procedure \"%s\"",
353  PLy_procedure_name(exec_ctx->curr_proc));
354  else
355  errcontext("PL/Python function \"%s\"",
356  PLy_procedure_name(exec_ctx->curr_proc));
357  }
358 }
#define errcontext
Definition: elog.h:196
void * arg
char * PLy_procedure_name(PLyProcedure *proc)

References arg, PLyExecutionContext::curr_proc, errcontext, PLyProcedure::is_procedure, and PLy_procedure_name().

Referenced by plpython3_call_handler().

◆ plpython_inline_error_callback()

static void plpython_inline_error_callback ( void *  arg)
static

Definition at line 361 of file plpy_main.c.

362 {
363  errcontext("PL/Python anonymous code block");
364 }

References errcontext.

Referenced by plpython3_inline_handler().

◆ PLy_current_execution_context()

◆ PLy_get_scratch_context()

MemoryContext PLy_get_scratch_context ( PLyExecutionContext context)

Definition at line 376 of file plpy_main.c.

377 {
378  /*
379  * A scratch context might never be needed in a given plpython procedure,
380  * so allocate it on first request.
381  */
382  if (context->scratch_ctx == NULL)
383  context->scratch_ctx =
385  "PL/Python scratch context",
387  return context->scratch_ctx;
388 }
MemoryContext TopTransactionContext
Definition: mcxt.c:154
tree context
Definition: radixtree.h:1833

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, context, and TopTransactionContext.

Referenced by PLy_input_convert(), and PLy_input_from_tuple().

◆ PLy_init_interp()

static void PLy_init_interp ( void  )
static

Definition at line 138 of file plpy_main.c.

139 {
140  static PyObject *PLy_interp_safe_globals = NULL;
141  PyObject *mainmod;
142 
143  mainmod = PyImport_AddModule("__main__");
144  if (mainmod == NULL || PyErr_Occurred())
145  PLy_elog(ERROR, "could not import \"__main__\" module");
146  Py_INCREF(mainmod);
147  PLy_interp_globals = PyModule_GetDict(mainmod);
148  PLy_interp_safe_globals = PyDict_New();
149  if (PLy_interp_safe_globals == NULL)
150  PLy_elog(ERROR, NULL);
151  PyDict_SetItemString(PLy_interp_globals, "GD", PLy_interp_safe_globals);
152  Py_DECREF(mainmod);
153  if (PLy_interp_globals == NULL || PyErr_Occurred())
154  PLy_elog(ERROR, "could not initialize globals");
155 }
#define PLy_elog
PyObject * PLy_interp_globals
Definition: plpy_main.c:51

References ERROR, PLy_elog, and PLy_interp_globals.

Referenced by PLy_initialize().

◆ PLy_initialize()

static void PLy_initialize ( void  )
static

Definition at line 95 of file plpy_main.c.

96 {
97  static bool inited = false;
98 
99  /*
100  * Check for multiple Python libraries before actively doing anything with
101  * libpython. This must be repeated on each entry to PL/Python, in case a
102  * conflicting library got loaded since we last looked.
103  *
104  * It is attractive to weaken this error from FATAL to ERROR, but there
105  * would be corner cases, so it seems best to be conservative.
106  */
107  if (*plpython_version_bitmask_ptr != (1 << PY_MAJOR_VERSION))
108  ereport(FATAL,
109  (errmsg("multiple Python libraries are present in session"),
110  errdetail("Only one Python major version can be used in one session.")));
111 
112  /* The rest should only be done once per session */
113  if (inited)
114  return;
115 
116  PyImport_AppendInittab("plpy", PyInit_plpy);
117  Py_Initialize();
118  PyImport_ImportModule("plpy");
119  PLy_init_interp();
120  PLy_init_plpy();
121  if (PyErr_Occurred())
122  PLy_elog(FATAL, "untrapped error in initialization");
123 
125 
127 
128  PLy_execution_contexts = NULL;
129 
130  inited = true;
131 }
int errdetail(const char *fmt,...)
Definition: elog.c:1205
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define FATAL
Definition: elog.h:41
#define ereport(elevel,...)
Definition: elog.h:149
#define NIL
Definition: pg_list.h:68
static void PLy_init_interp(void)
Definition: plpy_main.c:138
PyMODINIT_FUNC PyInit_plpy(void)
void PLy_init_plpy(void)
void init_procedure_caches(void)
List * explicit_subtransactions

References ereport, errdetail(), errmsg(), explicit_subtransactions, FATAL, init_procedure_caches(), NIL, plpython_version_bitmask_ptr, PLy_elog, PLy_execution_contexts, PLy_init_interp(), PLy_init_plpy(), and PyInit_plpy().

Referenced by plpython3_call_handler(), plpython3_inline_handler(), and plpython3_validator().

◆ PLy_pop_execution_context()

static void PLy_pop_execution_context ( void  )
static

Definition at line 407 of file plpy_main.c.

408 {
410 
411  if (context == NULL)
412  elog(ERROR, "no Python function is currently executing");
413 
415 
416  if (context->scratch_ctx)
417  MemoryContextDelete(context->scratch_ctx);
418  pfree(context);
419 }
void pfree(void *pointer)
Definition: mcxt.c:1520
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454

References context, elog, ERROR, MemoryContextDelete(), pfree(), and PLy_execution_contexts.

Referenced by plpython3_call_handler(), and plpython3_inline_handler().

◆ PLy_procedure_is_trigger()

static bool PLy_procedure_is_trigger ( Form_pg_proc  procStruct)
static

Definition at line 339 of file plpy_main.c.

340 {
341  return (procStruct->prorettype == TRIGGEROID);
342 }

Referenced by plpython3_validator().

◆ PLy_push_execution_context()

static PLyExecutionContext * PLy_push_execution_context ( bool  atomic_context)
static

Definition at line 391 of file plpy_main.c.

392 {
394 
395  /* Pick a memory context similar to what SPI uses. */
398  sizeof(PLyExecutionContext));
399  context->curr_proc = NULL;
400  context->scratch_ctx = NULL;
403  return context;
404 }
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1180
MemoryContext PortalContext
Definition: mcxt.c:158

References context, MemoryContextAlloc(), PLy_execution_contexts, PortalContext, and TopTransactionContext.

Referenced by plpython3_call_handler(), and plpython3_inline_handler().

Variable Documentation

◆ PG_MODULE_MAGIC

PG_MODULE_MAGIC

Definition at line 31 of file plpy_main.c.

◆ plpython_version_bitmask

int plpython_version_bitmask = 0
static

Definition at line 48 of file plpy_main.c.

Referenced by _PG_init().

◆ plpython_version_bitmask_ptr

int* plpython_version_bitmask_ptr = NULL
static

Definition at line 47 of file plpy_main.c.

Referenced by _PG_init(), and PLy_initialize().

◆ PLy_execution_contexts

PLyExecutionContext* PLy_execution_contexts = NULL
static

◆ PLy_interp_globals

PyObject* PLy_interp_globals = NULL

Definition at line 51 of file plpy_main.c.

Referenced by PLy_init_interp(), and PLy_procedure_compile().