📄 plpython.c
字号:
/********************************************************************** * plpython.c - python as a procedural language for PostgreSQL * * This software is copyright by Andrew Bosma * but is really shamelessly cribbed from pltcl.c by Jan Wieck, and * plperl.c by Mark Hollomon. * * The author hereby grants permission to use, copy, modify, * distribute, and license this software and its documentation for any * purpose, provided that existing copyright notices are retained in * all copies and that this notice is included verbatim in any * distributions. No written agreement, license, or royalty fee is * required for any of the authorized uses. Modifications to this * software may be copyrighted by their author and need not follow the * licensing terms described here, provided that the new terms are * clearly indicated on the first page of each file where they apply. * * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY PARTY * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY * DERIVATIVES THEREOF, EVEN IF THE AUTHOR HAVE BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, * AND THE AUTHOR AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * IDENTIFICATION * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.66.2.4 2006/02/20 20:10:41 neilc Exp $ * ********************************************************************* */#include <Python.h>#include "postgres.h"/* system stuff */#include <unistd.h>#include <fcntl.h>/* postgreSQL stuff */#include "access/heapam.h"#include "catalog/pg_proc.h"#include "catalog/pg_type.h"#include "commands/trigger.h"#include "executor/spi.h"#include "fmgr.h"#include "nodes/makefuncs.h"#include "parser/parse_type.h"#include "tcop/tcopprot.h"#include "utils/lsyscache.h"#include "utils/memutils.h"#include "utils/syscache.h"#include "utils/typcache.h"#include <compile.h>#include <eval.h>/* convert Postgresql Datum or tuple into a PyObject. * input to Python. Tuples are converted to dictionary * objects. */typedef PyObject *(*PLyDatumToObFunc) (const char *);typedef struct PLyDatumToOb{ PLyDatumToObFunc func; FmgrInfo typfunc; Oid typioparam; bool typbyval;} PLyDatumToOb;typedef struct PLyTupleToOb{ PLyDatumToOb *atts; int natts;} PLyTupleToOb;typedef union PLyTypeInput{ PLyDatumToOb d; PLyTupleToOb r;} PLyTypeInput;/* convert PyObject to a Postgresql Datum or tuple. * output from Python */typedef struct PLyObToDatum{ FmgrInfo typfunc; Oid typioparam; bool typbyval;} PLyObToDatum;typedef struct PLyObToTuple{ PLyObToDatum *atts; int natts;} PLyObToTuple;typedef union PLyTypeOutput{ PLyObToDatum d; PLyObToTuple r;} PLyTypeOutput;/* all we need to move Postgresql data to Python objects, * and vis versa */typedef struct PLyTypeInfo{ PLyTypeInput in; PLyTypeOutput out; int is_rowtype; /* * is_rowtype can be: -1 not known yet (initial state) 0 scalar datatype * 1 rowtype 2 rowtype, but I/O functions not set up yet */} PLyTypeInfo;/* cached procedure data */typedef struct PLyProcedure{ char *proname; /* SQL name of procedure */ char *pyname; /* Python name of procedure */ TransactionId fn_xmin; CommandId fn_cmin; bool fn_readonly; PLyTypeInfo result; /* also used to store info for trigger tuple * type */ PLyTypeInfo args[FUNC_MAX_ARGS]; int nargs; PyObject *code; /* compiled procedure code */ PyObject *statics; /* data saved across calls, local scope */ PyObject *globals; /* data saved across calls, global score */ PyObject *me; /* PyCObject containing pointer to this * PLyProcedure */} PLyProcedure;/* Python objects. */typedef struct PLyPlanObject{ PyObject_HEAD void *plan; /* return of an SPI_saveplan */ int nargs; Oid *types; Datum *values; PLyTypeInfo *args;} PLyPlanObject;typedef struct PLyResultObject{ PyObject_HEAD /* HeapTuple *tuples; */ PyObject * nrows; /* number of rows returned by query */ PyObject *rows; /* data rows, or None if no data returned */ PyObject *status; /* query status, SPI_OK_*, or SPI_ERR_* */} PLyResultObject;/* function declarations *//* Two exported functions: first is the magic telling Postgresql * what function call interface it implements. Second allows * preinitialization of the interpreter during postmaster startup. */Datum plpython_call_handler(PG_FUNCTION_ARGS);void plpython_init(void);PG_FUNCTION_INFO_V1(plpython_call_handler);/* most of the remaining of the declarations, all static *//* these should only be called once at the first call * of plpython_call_handler. initialize the python interpreter * and global data. */static void PLy_init_all(void);static void PLy_init_interp(void);static void PLy_init_plpy(void);/* call PyErr_SetString with a vprint interface */static voidPLy_exception_set(PyObject *, const char *,...)__attribute__((format(printf, 2, 3)));/* Get the innermost python procedure called from the backend. */static char *PLy_procedure_name(PLyProcedure *);/* some utility functions */static void PLy_elog(int, const char *,...);static char *PLy_traceback(int *);static char *PLy_vprintf(const char *fmt, va_list ap);static char *PLy_printf(const char *fmt,...);static void *PLy_malloc(size_t);static void *PLy_realloc(void *, size_t);static void PLy_free(void *);/* sub handlers for functions and triggers */static Datum PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *);static HeapTuple PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure *);static PyObject *PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *);static PyObject *PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *, HeapTuple *);static HeapTuple PLy_modify_tuple(PLyProcedure *, PyObject *, TriggerData *, HeapTuple);static PyObject *PLy_procedure_call(PLyProcedure *, char *, PyObject *);static PLyProcedure *PLy_procedure_get(FunctionCallInfo fcinfo, Oid tgreloid);static PLyProcedure *PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid, HeapTuple procTup, char *key);static void PLy_procedure_compile(PLyProcedure *, const char *);static char *PLy_procedure_munge_source(const char *, const char *);static void PLy_procedure_delete(PLyProcedure *);static void PLy_typeinfo_init(PLyTypeInfo *);static void PLy_typeinfo_dealloc(PLyTypeInfo *);static void PLy_output_datum_func(PLyTypeInfo *, HeapTuple);static void PLy_output_datum_func2(PLyObToDatum *, HeapTuple);static void PLy_input_datum_func(PLyTypeInfo *, Oid, HeapTuple);static void PLy_input_datum_func2(PLyDatumToOb *, Oid, HeapTuple);static void PLy_output_tuple_funcs(PLyTypeInfo *, TupleDesc);static void PLy_input_tuple_funcs(PLyTypeInfo *, TupleDesc);/* conversion functions */static PyObject *PLyDict_FromTuple(PLyTypeInfo *, HeapTuple, TupleDesc);static PyObject *PLyBool_FromString(const char *);static PyObject *PLyFloat_FromString(const char *);static PyObject *PLyInt_FromString(const char *);static PyObject *PLyLong_FromString(const char *);static PyObject *PLyString_FromString(const char *);/* global data */static int PLy_first_call = 1;/* * Currently active plpython function */static PLyProcedure *PLy_curr_procedure = NULL;/* * When a callback from Python into PG incurs an error, we temporarily store * the error information here, and return NULL to the Python interpreter. * Any further callback attempts immediately fail, and when the Python * interpreter returns to the calling function, we re-throw the error (even if * Python thinks it trapped the error and doesn't return NULL). Eventually * this ought to be improved to let Python code really truly trap the error, * but that's more of a change from the pre-8.0 semantics than I have time for * now --- it will only be possible if the callback query is executed inside a * subtransaction. */static ErrorData *PLy_error_in_progress = NULL;static PyObject *PLy_interp_globals = NULL;static PyObject *PLy_interp_safe_globals = NULL;static PyObject *PLy_procedure_cache = NULL;/* Python exceptions */static PyObject *PLy_exc_error = NULL;static PyObject *PLy_exc_fatal = NULL;static PyObject *PLy_exc_spi_error = NULL;/* some globals for the python module */static char PLy_plan_doc[] = { "Store a PostgreSQL plan"};static char PLy_result_doc[] = { "Results of a PostgreSQL query"};/* * the function definitions *//* * This routine is a crock, and so is everyplace that calls it. The problem * is that the cached form of plpython functions/queries is allocated permanently * (mostly via malloc()) and never released until backend exit. Subsidiary * data structures such as fmgr info records therefore must live forever * as well. A better implementation would store all this stuff in a per- * function memory context that could be reclaimed at need. In the meantime, * fmgr_info_cxt must be called specifying TopMemoryContext so that whatever * it might allocate, and whatever the eventual function might allocate using * fn_mcxt, will live forever too. */static voidperm_fmgr_info(Oid functionId, FmgrInfo *finfo){ fmgr_info_cxt(functionId, finfo, TopMemoryContext);}Datumplpython_call_handler(PG_FUNCTION_ARGS){ Datum retval; PLyProcedure *save_curr_proc; PLyProcedure *volatile proc = NULL; PLy_init_all(); if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "could not connect to SPI manager"); save_curr_proc = PLy_curr_procedure; PG_TRY(); { if (CALLED_AS_TRIGGER(fcinfo)) { TriggerData *tdata = (TriggerData *) fcinfo->context; HeapTuple trv; proc = PLy_procedure_get(fcinfo, RelationGetRelid(tdata->tg_relation)); PLy_curr_procedure = proc; trv = PLy_trigger_handler(fcinfo, proc); retval = PointerGetDatum(trv); } else { proc = PLy_procedure_get(fcinfo, InvalidOid); PLy_curr_procedure = proc; retval = PLy_function_handler(fcinfo, proc); } } PG_CATCH(); { PLy_curr_procedure = save_curr_proc; if (proc) { /* note: Py_DECREF needs braces around it, as of 2003/08 */ Py_DECREF(proc->me); } PyErr_Clear(); PG_RE_THROW(); } PG_END_TRY(); PLy_curr_procedure = save_curr_proc; Py_DECREF(proc->me); return retval;}/* trigger and function sub handlers * * the python function is expected to return Py_None if the tuple is * acceptable and unmodified. Otherwise it should return a PyString * object who's value is SKIP, or MODIFY. SKIP means don't perform * this action. MODIFY means the tuple has been modified, so update * tuple and perform action. SKIP and MODIFY assume the trigger fires * BEFORE the event and is ROW level. postgres expects the function * to take no arguments and return an argument of type trigger. */static HeapTuplePLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure * proc){ HeapTuple rv = NULL; PyObject *volatile plargs = NULL; PyObject *volatile plrv = NULL; PG_TRY(); { plargs = PLy_trigger_build_args(fcinfo, proc, &rv); plrv = PLy_procedure_call(proc, "TD", plargs); Assert(plrv != NULL); Assert(!PLy_error_in_progress); /* * Disconnect from SPI manager */ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed"); /* * return of None means we're happy with the tuple */ if (plrv != Py_None) { char *srv; if (!PyString_Check(plrv)) elog(ERROR, "expected trigger to return None or a String"); srv = PyString_AsString(plrv); if (pg_strcasecmp(srv, "SKIP") == 0) rv = NULL; else if (pg_strcasecmp(srv, "MODIFY") == 0) { TriggerData *tdata = (TriggerData *) fcinfo->context; if ((TRIGGER_FIRED_BY_INSERT(tdata->tg_event)) || (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))) rv = PLy_modify_tuple(proc, plargs, tdata, rv); else elog(WARNING, "ignoring modified tuple in DELETE trigger"); } else if (pg_strcasecmp(srv, "OK") != 0) { /* * hmmm, perhaps they only read the pltcl page, not a * surprising thing since i've written no documentation, so * accept a belated OK */ elog(ERROR, "expected return to be \"SKIP\" or \"MODIFY\""); } } } PG_CATCH(); { Py_XDECREF(plargs); Py_XDECREF(plrv); PG_RE_THROW(); } PG_END_TRY(); Py_DECREF(plargs); Py_DECREF(plrv); return rv;}static HeapTuplePLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata, HeapTuple otup){ PyObject *volatile plntup; PyObject *volatile plkeys; PyObject *volatile platt; PyObject *volatile plval; PyObject *volatile plstr; HeapTuple rtup; int natts, i, attn, atti; int *volatile modattrs; Datum *volatile modvalues; char *volatile modnulls; TupleDesc tupdesc; plntup = plkeys = platt = plval = plstr = NULL; modattrs = NULL; modvalues = NULL; modnulls = NULL; PG_TRY(); { if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL) elog(ERROR, "TD[\"new\"] deleted, unable to modify tuple"); if (!PyDict_Check(plntup)) elog(ERROR, "TD[\"new\"] is not a dictionary object"); Py_INCREF(plntup); plkeys = PyDict_Keys(plntup); natts = PyList_Size(plkeys); modattrs = (int *) palloc(natts * sizeof(int)); modvalues = (Datum *) palloc(natts * sizeof(Datum)); modnulls = (char *) palloc(natts * sizeof(char)); tupdesc = tdata->tg_relation->rd_att; for (i = 0; i < natts; i++) { char *src; platt = PyList_GetItem(plkeys, i); if (!PyString_Check(platt)) elog(ERROR, "attribute name is not a string"); attn = SPI_fnumber(tupdesc, PyString_AsString(platt)); if (attn == SPI_ERROR_NOATTRIBUTE) elog(ERROR, "invalid attribute \"%s\" in tuple", PyString_AsString(platt)); atti = attn - 1; plval = PyDict_GetItem(plntup, platt); if (plval == NULL) elog(FATAL, "python interpreter is probably corrupted"); Py_INCREF(plval); modattrs[i] = attn; if (plval != Py_None && !tupdesc->attrs[atti]->attisdropped) { plstr = PyObject_Str(plval); if (!plstr) PLy_elog(ERROR, "function \"%s\" could not modify tuple", proc->proname); src = PyString_AsString(plstr); modvalues[i] = FunctionCall3(&proc->result.out.r.atts[atti].typfunc, CStringGetDatum(src), ObjectIdGetDatum(proc->result.out.r.atts[atti].typioparam), Int32GetDatum(tupdesc->attrs[atti]->atttypmod)); modnulls[i] = ' '; Py_DECREF(plstr); plstr = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -