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

📄 plpython.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 5 页
字号:
/********************************************************************** * 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 + -