📄 plpython.c
字号:
/********************************************************************** * plpython.c - python as a procedural language for PostgreSQL * * This software is copyright by Andrew Bosma * but is really shameless cribbed from pltcl.c by Jan Weick, 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 * $Header: /cvsroot/pgsql/src/pl/plpython/plpython.c,v 1.41.2.1 2004/01/04 00:14:55 momjian Exp $ * ********************************************************************* */#include "postgres.h"/* system stuff */#include <dlfcn.h>#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <unistd.h>#include <fcntl.h>#include <string.h>#include <setjmp.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/syscache.h"#include <Python.h>#include <compile.h>#include <eval.h>#include "plpython.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 typelem; 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 typelem; 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_rel;} 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; 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);/* error handler. collects the current Python exception, if any, * and appends it to the error and sends it to elog */static void PLy_elog(int, const char *,...);/* 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_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 *, Form_pg_type);static void PLy_output_datum_func2(PLyObToDatum *, Form_pg_type);static void PLy_input_datum_func(PLyTypeInfo *, Oid, Form_pg_type);static void PLy_input_datum_func2(PLyDatumToOb *, Oid, Form_pg_type);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;static volatile int PLy_call_level = 0;/* * Last function called by postgres backend */static PLyProcedure *PLy_last_procedure = NULL;/* this gets modified in plpython_call_handler and PLy_elog. * test it any old where, but do NOT modify it anywhere except * those two functions */static volatile int PLy_restart_in_progress = 0;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"};#if DEBUG_EXCvolatile int exc_save_calls = 0;volatile int exc_restore_calls = 0;volatile int func_enter_calls = 0;volatile int func_leave_calls = 0;#endif/* * 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){ DECLARE_EXC(); Datum retval; PLyProcedure *volatile proc = NULL; enter(); PLy_init_all(); if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "could not connect to SPI manager"); CALL_LEVEL_INC(); SAVE_EXC(); if (TRAP_EXC()) { RESTORE_EXC(); CALL_LEVEL_DEC(); if (PLy_call_level == 0) { PLy_restart_in_progress = 0; PyErr_Clear(); } else PLy_restart_in_progress += 1; if (proc) { /* note: Py_DECREF needs braces around it, as of 2003/08 */ Py_DECREF(proc->me); } RERAISE_EXC(); } /* * elog(DEBUG3, "PLy_restart_in_progress is %d", * PLy_restart_in_progress); */ if (CALLED_AS_TRIGGER(fcinfo)) { TriggerData *tdata = (TriggerData *) fcinfo->context; HeapTuple trv; proc = PLy_procedure_get(fcinfo, RelationGetRelid(tdata->tg_relation)); trv = PLy_trigger_handler(fcinfo, proc); retval = PointerGetDatum(trv); } else { proc = PLy_procedure_get(fcinfo, InvalidOid); retval = PLy_function_handler(fcinfo, proc); } CALL_LEVEL_DEC(); RESTORE_EXC(); Py_DECREF(proc->me); refc(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. */HeapTuplePLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure * proc){ DECLARE_EXC(); HeapTuple rv = NULL; PyObject *volatile plargs = NULL; PyObject *volatile plrv = NULL; enter(); SAVE_EXC(); if (TRAP_EXC()) { RESTORE_EXC(); Py_XDECREF(plargs); Py_XDECREF(plrv); RERAISE_EXC(); } plargs = PLy_trigger_build_args(fcinfo, proc, &rv); plrv = PLy_procedure_call(proc, "TD", plargs); /* * Disconnect from SPI manager */ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed"); if (plrv == NULL) elog(FATAL, "PLy_procedure_call returned NULL"); if (PLy_restart_in_progress) elog(FATAL, "restart in progress not expected"); /* * 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 (strcasecmp(srv, "SKIP") == 0) rv = NULL; else if (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 (strcasecmp(srv, "OK")) { /* * 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\""); } } Py_DECREF(plargs); Py_DECREF(plrv); RESTORE_EXC(); return rv;}HeapTuplePLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata, HeapTuple otup){ DECLARE_EXC(); 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; enter(); SAVE_EXC(); if (TRAP_EXC()) { RESTORE_EXC(); Py_XDECREF(plntup); Py_XDECREF(plkeys); Py_XDECREF(platt); Py_XDECREF(plval); Py_XDECREF(plstr); if (modnulls) pfree(modnulls); if (modvalues) pfree(modvalues); if (modattrs) pfree(modattrs); RERAISE_EXC(); } 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); /* +1 to avoid palloc(0) on empty tuple */ modattrs = palloc(natts * sizeof(int) + 1); modvalues = palloc(natts * sizeof(Datum) + 1); modnulls = palloc(natts + 1); 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -