⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 plpython.c

📁 PostgreSQL7.4.6 for Linux
💻 C
📖 第 1 页 / 共 5 页
字号:
/********************************************************************** * 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 + -