Header And Logo

PostgreSQL
| The world's most advanced open source database.

plpy_main.c

Go to the documentation of this file.
00001 /*
00002  * PL/Python main entry points
00003  *
00004  * src/pl/plpython/plpy_main.c
00005  */
00006 
00007 #include "postgres.h"
00008 
00009 #include "access/htup_details.h"
00010 #include "catalog/pg_proc.h"
00011 #include "catalog/pg_type.h"
00012 #include "commands/trigger.h"
00013 #include "executor/spi.h"
00014 #include "miscadmin.h"
00015 #include "utils/guc.h"
00016 #include "utils/memutils.h"
00017 #include "utils/rel.h"
00018 #include "utils/syscache.h"
00019 
00020 #include "plpython.h"
00021 
00022 #include "plpy_main.h"
00023 
00024 #include "plpy_elog.h"
00025 #include "plpy_exec.h"
00026 #include "plpy_plpymodule.h"
00027 #include "plpy_procedure.h"
00028 #include "plpy_subxactobject.h"
00029 
00030 
00031 /*
00032  * exported functions
00033  */
00034 
00035 #if PY_MAJOR_VERSION >= 3
00036 /* Use separate names to avoid clash in pg_pltemplate */
00037 #define plpython_validator plpython3_validator
00038 #define plpython_call_handler plpython3_call_handler
00039 #define plpython_inline_handler plpython3_inline_handler
00040 #endif
00041 
00042 extern void _PG_init(void);
00043 extern Datum plpython_validator(PG_FUNCTION_ARGS);
00044 extern Datum plpython_call_handler(PG_FUNCTION_ARGS);
00045 extern Datum plpython_inline_handler(PG_FUNCTION_ARGS);
00046 
00047 #if PY_MAJOR_VERSION < 3
00048 /* Define aliases plpython2_call_handler etc */
00049 extern Datum plpython2_validator(PG_FUNCTION_ARGS);
00050 extern Datum plpython2_call_handler(PG_FUNCTION_ARGS);
00051 extern Datum plpython2_inline_handler(PG_FUNCTION_ARGS);
00052 #endif
00053 
00054 PG_MODULE_MAGIC;
00055 
00056 PG_FUNCTION_INFO_V1(plpython_validator);
00057 PG_FUNCTION_INFO_V1(plpython_call_handler);
00058 PG_FUNCTION_INFO_V1(plpython_inline_handler);
00059 
00060 #if PY_MAJOR_VERSION < 3
00061 PG_FUNCTION_INFO_V1(plpython2_validator);
00062 PG_FUNCTION_INFO_V1(plpython2_call_handler);
00063 PG_FUNCTION_INFO_V1(plpython2_inline_handler);
00064 #endif
00065 
00066 
00067 static bool PLy_procedure_is_trigger(Form_pg_proc procStruct);
00068 static void plpython_error_callback(void *arg);
00069 static void plpython_inline_error_callback(void *arg);
00070 static void PLy_init_interp(void);
00071 
00072 static PLyExecutionContext *PLy_push_execution_context(void);
00073 static void PLy_pop_execution_context(void);
00074 
00075 static const int plpython_python_version = PY_MAJOR_VERSION;
00076 
00077 /* initialize global variables */
00078 PyObject   *PLy_interp_globals = NULL;
00079 
00080 /* this doesn't need to be global; use PLy_current_execution_context() */
00081 static PLyExecutionContext *PLy_execution_contexts = NULL;
00082 
00083 
00084 void
00085 _PG_init(void)
00086 {
00087     /* Be sure we do initialization only once (should be redundant now) */
00088     static bool inited = false;
00089     const int **version_ptr;
00090 
00091     if (inited)
00092         return;
00093 
00094     /* Be sure we don't run Python 2 and 3 in the same session (might crash) */
00095     version_ptr = (const int **) find_rendezvous_variable("plpython_python_version");
00096     if (!(*version_ptr))
00097         *version_ptr = &plpython_python_version;
00098     else
00099     {
00100         if (**version_ptr != plpython_python_version)
00101             ereport(FATAL,
00102                     (errmsg("Python major version mismatch in session"),
00103                      errdetail("This session has previously used Python major version %d, and it is now attempting to use Python major version %d.",
00104                                **version_ptr, plpython_python_version),
00105                      errhint("Start a new session to use a different Python major version.")));
00106     }
00107 
00108     pg_bindtextdomain(TEXTDOMAIN);
00109 
00110 #if PY_MAJOR_VERSION >= 3
00111     PyImport_AppendInittab("plpy", PyInit_plpy);
00112 #endif
00113     Py_Initialize();
00114 #if PY_MAJOR_VERSION >= 3
00115     PyImport_ImportModule("plpy");
00116 #endif
00117     PLy_init_interp();
00118     PLy_init_plpy();
00119     if (PyErr_Occurred())
00120         PLy_elog(FATAL, "untrapped error in initialization");
00121 
00122     init_procedure_caches();
00123 
00124     explicit_subtransactions = NIL;
00125 
00126     PLy_execution_contexts = NULL;
00127 
00128     inited = true;
00129 }
00130 
00131 /*
00132  * This should only be called once from _PG_init. Initialize the Python
00133  * interpreter and global data.
00134  */
00135 void
00136 PLy_init_interp(void)
00137 {
00138     static PyObject *PLy_interp_safe_globals = NULL;
00139     PyObject   *mainmod;
00140 
00141     mainmod = PyImport_AddModule("__main__");
00142     if (mainmod == NULL || PyErr_Occurred())
00143         PLy_elog(ERROR, "could not import \"__main__\" module");
00144     Py_INCREF(mainmod);
00145     PLy_interp_globals = PyModule_GetDict(mainmod);
00146     PLy_interp_safe_globals = PyDict_New();
00147     if (PLy_interp_safe_globals == NULL)
00148         PLy_elog(ERROR, "could not create globals");
00149     PyDict_SetItemString(PLy_interp_globals, "GD", PLy_interp_safe_globals);
00150     Py_DECREF(mainmod);
00151     if (PLy_interp_globals == NULL || PyErr_Occurred())
00152         PLy_elog(ERROR, "could not initialize globals");
00153 }
00154 
00155 Datum
00156 plpython_validator(PG_FUNCTION_ARGS)
00157 {
00158     Oid         funcoid = PG_GETARG_OID(0);
00159     HeapTuple   tuple;
00160     Form_pg_proc procStruct;
00161     bool        is_trigger;
00162 
00163     if (!check_function_bodies)
00164     {
00165         PG_RETURN_VOID();
00166     }
00167 
00168     /* Get the new function's pg_proc entry */
00169     tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
00170     if (!HeapTupleIsValid(tuple))
00171         elog(ERROR, "cache lookup failed for function %u", funcoid);
00172     procStruct = (Form_pg_proc) GETSTRUCT(tuple);
00173 
00174     is_trigger = PLy_procedure_is_trigger(procStruct);
00175 
00176     ReleaseSysCache(tuple);
00177 
00178     /* We can't validate triggers against any particular table ... */
00179     PLy_procedure_get(funcoid, InvalidOid, is_trigger);
00180 
00181     PG_RETURN_VOID();
00182 }
00183 
00184 #if PY_MAJOR_VERSION < 3
00185 Datum
00186 plpython2_validator(PG_FUNCTION_ARGS)
00187 {
00188     return plpython_validator(fcinfo);
00189 }
00190 #endif   /* PY_MAJOR_VERSION < 3 */
00191 
00192 Datum
00193 plpython_call_handler(PG_FUNCTION_ARGS)
00194 {
00195     Datum       retval;
00196     PLyExecutionContext *exec_ctx;
00197     ErrorContextCallback plerrcontext;
00198 
00199     /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
00200     if (SPI_connect() != SPI_OK_CONNECT)
00201         elog(ERROR, "SPI_connect failed");
00202 
00203     /*
00204      * Push execution context onto stack.  It is important that this get
00205      * popped again, so avoid putting anything that could throw error between
00206      * here and the PG_TRY.  (plpython_error_callback expects the stack entry
00207      * to be there, so we have to make the context first.)
00208      */
00209     exec_ctx = PLy_push_execution_context();
00210 
00211     /*
00212      * Setup error traceback support for ereport()
00213      */
00214     plerrcontext.callback = plpython_error_callback;
00215     plerrcontext.previous = error_context_stack;
00216     error_context_stack = &plerrcontext;
00217 
00218     PG_TRY();
00219     {
00220         Oid         funcoid = fcinfo->flinfo->fn_oid;
00221         PLyProcedure *proc;
00222 
00223         if (CALLED_AS_TRIGGER(fcinfo))
00224         {
00225             Relation    tgrel = ((TriggerData *) fcinfo->context)->tg_relation;
00226             HeapTuple   trv;
00227 
00228             proc = PLy_procedure_get(funcoid, RelationGetRelid(tgrel), true);
00229             exec_ctx->curr_proc = proc;
00230             trv = PLy_exec_trigger(fcinfo, proc);
00231             retval = PointerGetDatum(trv);
00232         }
00233         else
00234         {
00235             proc = PLy_procedure_get(funcoid, InvalidOid, false);
00236             exec_ctx->curr_proc = proc;
00237             retval = PLy_exec_function(fcinfo, proc);
00238         }
00239     }
00240     PG_CATCH();
00241     {
00242         PLy_pop_execution_context();
00243         PyErr_Clear();
00244         PG_RE_THROW();
00245     }
00246     PG_END_TRY();
00247 
00248     /* Pop the error context stack */
00249     error_context_stack = plerrcontext.previous;
00250     /* ... and then the execution context */
00251     PLy_pop_execution_context();
00252 
00253     return retval;
00254 }
00255 
00256 #if PY_MAJOR_VERSION < 3
00257 Datum
00258 plpython2_call_handler(PG_FUNCTION_ARGS)
00259 {
00260     return plpython_call_handler(fcinfo);
00261 }
00262 #endif   /* PY_MAJOR_VERSION < 3 */
00263 
00264 Datum
00265 plpython_inline_handler(PG_FUNCTION_ARGS)
00266 {
00267     InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
00268     FunctionCallInfoData fake_fcinfo;
00269     FmgrInfo    flinfo;
00270     PLyProcedure proc;
00271     PLyExecutionContext *exec_ctx;
00272     ErrorContextCallback plerrcontext;
00273 
00274     /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
00275     if (SPI_connect() != SPI_OK_CONNECT)
00276         elog(ERROR, "SPI_connect failed");
00277 
00278     MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
00279     MemSet(&flinfo, 0, sizeof(flinfo));
00280     fake_fcinfo.flinfo = &flinfo;
00281     flinfo.fn_oid = InvalidOid;
00282     flinfo.fn_mcxt = CurrentMemoryContext;
00283 
00284     MemSet(&proc, 0, sizeof(PLyProcedure));
00285     proc.pyname = PLy_strdup("__plpython_inline_block");
00286     proc.result.out.d.typoid = VOIDOID;
00287 
00288     /*
00289      * Push execution context onto stack.  It is important that this get
00290      * popped again, so avoid putting anything that could throw error between
00291      * here and the PG_TRY.  (plpython_inline_error_callback doesn't currently
00292      * need the stack entry, but for consistency with plpython_call_handler we
00293      * do it in this order.)
00294      */
00295     exec_ctx = PLy_push_execution_context();
00296 
00297     /*
00298      * Setup error traceback support for ereport()
00299      */
00300     plerrcontext.callback = plpython_inline_error_callback;
00301     plerrcontext.previous = error_context_stack;
00302     error_context_stack = &plerrcontext;
00303 
00304     PG_TRY();
00305     {
00306         PLy_procedure_compile(&proc, codeblock->source_text);
00307         exec_ctx->curr_proc = &proc;
00308         PLy_exec_function(&fake_fcinfo, &proc);
00309     }
00310     PG_CATCH();
00311     {
00312         PLy_pop_execution_context();
00313         PLy_procedure_delete(&proc);
00314         PyErr_Clear();
00315         PG_RE_THROW();
00316     }
00317     PG_END_TRY();
00318 
00319     /* Pop the error context stack */
00320     error_context_stack = plerrcontext.previous;
00321     /* ... and then the execution context */
00322     PLy_pop_execution_context();
00323 
00324     /* Now clean up the transient procedure we made */
00325     PLy_procedure_delete(&proc);
00326 
00327     PG_RETURN_VOID();
00328 }
00329 
00330 #if PY_MAJOR_VERSION < 3
00331 Datum
00332 plpython2_inline_handler(PG_FUNCTION_ARGS)
00333 {
00334     return plpython_inline_handler(fcinfo);
00335 }
00336 #endif   /* PY_MAJOR_VERSION < 3 */
00337 
00338 static bool
00339 PLy_procedure_is_trigger(Form_pg_proc procStruct)
00340 {
00341     return (procStruct->prorettype == TRIGGEROID ||
00342             (procStruct->prorettype == OPAQUEOID &&
00343              procStruct->pronargs == 0));
00344 }
00345 
00346 static void
00347 plpython_error_callback(void *arg)
00348 {
00349     PLyExecutionContext *exec_ctx = PLy_current_execution_context();
00350 
00351     if (exec_ctx->curr_proc)
00352         errcontext("PL/Python function \"%s\"",
00353                    PLy_procedure_name(exec_ctx->curr_proc));
00354 }
00355 
00356 static void
00357 plpython_inline_error_callback(void *arg)
00358 {
00359     errcontext("PL/Python anonymous code block");
00360 }
00361 
00362 PLyExecutionContext *
00363 PLy_current_execution_context(void)
00364 {
00365     if (PLy_execution_contexts == NULL)
00366         elog(ERROR, "no Python function is currently executing");
00367 
00368     return PLy_execution_contexts;
00369 }
00370 
00371 static PLyExecutionContext *
00372 PLy_push_execution_context(void)
00373 {
00374     PLyExecutionContext *context = PLy_malloc(sizeof(PLyExecutionContext));
00375 
00376     context->curr_proc = NULL;
00377     context->scratch_ctx = AllocSetContextCreate(TopTransactionContext,
00378                                                  "PL/Python scratch context",
00379                                                  ALLOCSET_DEFAULT_MINSIZE,
00380                                                  ALLOCSET_DEFAULT_INITSIZE,
00381                                                  ALLOCSET_DEFAULT_MAXSIZE);
00382     context->next = PLy_execution_contexts;
00383     PLy_execution_contexts = context;
00384     return context;
00385 }
00386 
00387 static void
00388 PLy_pop_execution_context(void)
00389 {
00390     PLyExecutionContext *context = PLy_execution_contexts;
00391 
00392     if (context == NULL)
00393         elog(ERROR, "no Python function is currently executing");
00394 
00395     PLy_execution_contexts = context->next;
00396 
00397     MemoryContextDelete(context->scratch_ctx);
00398     PLy_free(context);
00399 }