diff options
Diffstat (limited to 'src/pl/plpython/plpython.c')
-rw-r--r-- | src/pl/plpython/plpython.c | 3868 |
1 files changed, 1989 insertions, 1879 deletions
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c index edb2d69a9fe..3483d21ae33 100644 --- a/src/pl/plpython/plpython.c +++ b/src/pl/plpython/plpython.c @@ -29,7 +29,7 @@ * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/plpython/plpython.c,v 1.9 2001/10/22 19:32:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpython/plpython.c,v 1.10 2001/10/25 05:50:20 momjian Exp $ * ********************************************************************* */ @@ -70,88 +70,100 @@ typedef PyObject *(*PLyDatumToObFunc) (const char *); -typedef struct PLyDatumToOb { - PLyDatumToObFunc func; - FmgrInfo typfunc; - Oid typelem; - bool typbyval; -} PLyDatumToOb; +typedef struct PLyDatumToOb +{ + PLyDatumToObFunc func; + FmgrInfo typfunc; + Oid typelem; + bool typbyval; +} PLyDatumToOb; -typedef struct PLyTupleToOb { - PLyDatumToOb *atts; - int natts; -} PLyTupleToOb; +typedef struct PLyTupleToOb +{ + PLyDatumToOb *atts; + int natts; +} PLyTupleToOb; -typedef union PLyTypeInput { - PLyDatumToOb d; - PLyTupleToOb r; -} PLyTypeInput; +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; +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; +typedef struct PLyTypeInfo +{ + PLyTypeInput in; + PLyTypeOutput out; + int is_rel; +} PLyTypeInfo; /* cached procedure data */ -typedef struct PLyProcedure { - char *proname; - 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 *interp; /* restricted interpreter instance */ - PyObject *reval; /* interpreter return */ - 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 PLyProcedure +{ + char *proname; + 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 *interp; /* restricted interpreter instance */ + PyObject *reval; /* interpreter return */ + 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; +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 @@ -160,7 +172,8 @@ typedef struct PLyResultObject { /* the only exported function, with the magic telling Postgresql * what function call interface it implements. */ -Datum plpython_call_handler(PG_FUNCTION_ARGS); +Datum plpython_call_handler(PG_FUNCTION_ARGS); + PG_FUNCTION_INFO_V1(plpython_call_handler); /* most of the remaining of the declarations, all static @@ -178,12 +191,13 @@ 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 *, ...); +static void PLy_elog(int, const char *,...); /* call PyErr_SetString with a vprint interface */ -static void PLy_exception_set(PyObject *, const char *, ...) - __attribute__ ((format (printf, 2, 3))); +static void +PLy_exception_set(PyObject *, const char *,...) +__attribute__((format(printf, 2, 3))); /* some utility functions */ @@ -198,9 +212,9 @@ 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 *); + HeapTuple *); static HeapTuple PLy_modify_tuple(PLyProcedure *, PyObject *, - TriggerData *, HeapTuple); + TriggerData *, HeapTuple); static PyObject *PLy_procedure_call(PLyProcedure *, char *, PyObject *); @@ -210,8 +224,8 @@ static PyObject *PLy_procedure_call(PLyProcedure *, char *, PyObject *); static PLyProcedure *PLy_procedure_get(FunctionCallInfo fcinfo, bool); static PLyProcedure *PLy_procedure_create(FunctionCallInfo fcinfo, - bool is_trigger, - HeapTuple procTup, char *key); + bool is_trigger, + HeapTuple procTup, char *key); static void PLy_procedure_compile(PLyProcedure *, const char *); static char *PLy_procedure_munge_source(const char *, const char *); @@ -238,7 +252,7 @@ static PyObject *PLyString_FromString(const char *); /* global data */ -static int PLy_first_call = 1; +static int PLy_first_call = 1; static volatile int PLy_call_level = 0; /* this gets modified in plpython_call_handler and PLy_elog. @@ -253,42 +267,42 @@ static PyObject *PLy_interp_safe_globals = NULL; static PyObject *PLy_importable_modules = NULL; static PyObject *PLy_procedure_cache = NULL; -char *PLy_importable_modules_list[] = { - "array", - "bisect", - "calendar", - "cmath", - "errno", - "marshal", - "math", - "md5", - "mpz", - "operator", - "pickle", - "random", - "re", - "sha", - "string", - "StringIO", - "time", - "whrandom", - "zlib" +char *PLy_importable_modules_list[] = { + "array", + "bisect", + "calendar", + "cmath", + "errno", + "marshal", + "math", + "md5", + "mpz", + "operator", + "pickle", + "random", + "re", + "sha", + "string", + "StringIO", + "time", + "whrandom", + "zlib" }; /* Python exceptions */ -PyObject *PLy_exc_error = NULL; -PyObject *PLy_exc_fatal = NULL; -PyObject *PLy_exc_spi_error = NULL; +PyObject *PLy_exc_error = NULL; +PyObject *PLy_exc_fatal = NULL; +PyObject *PLy_exc_spi_error = NULL; /* some globals for the python module */ static char PLy_plan_doc[] = { - "Store a PostgreSQL plan" + "Store a PostgreSQL plan" }; static char PLy_result_doc[] = { - "Results of a PostgreSQL query" + "Results of a PostgreSQL query" }; @@ -324,58 +338,62 @@ perm_fmgr_info(Oid functionId, FmgrInfo *finfo) Datum plpython_call_handler(PG_FUNCTION_ARGS) { - DECLARE_EXC(); - Datum retval; - bool is_trigger; - PLyProcedure *volatile proc = NULL; + DECLARE_EXC(); + Datum retval; + bool is_trigger; + PLyProcedure *volatile proc = NULL; - enter(); + enter(); - if (PLy_first_call) - PLy_init_all(); + if (PLy_first_call) + PLy_init_all(); - if (SPI_connect() != SPI_OK_CONNECT) - elog(ERROR, "plpython: Unable to connect to SPI manager"); + if (SPI_connect() != SPI_OK_CONNECT) + elog(ERROR, "plpython: Unable to connect to SPI manager"); - CALL_LEVEL_INC(); - is_trigger = CALLED_AS_TRIGGER(fcinfo); + CALL_LEVEL_INC(); + is_trigger = CALLED_AS_TRIGGER(fcinfo); - SAVE_EXC(); - if (TRAP_EXC()) - { - RESTORE_EXC(); - CALL_LEVEL_DEC(); - if (PLy_call_level == 0) + SAVE_EXC(); + if (TRAP_EXC()) { - PLy_restart_in_progress = 0; - PyErr_Clear(); + 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) + Py_DECREF(proc->me); + RERAISE_EXC(); } - else - PLy_restart_in_progress += 1; - if (proc) - { Py_DECREF(proc->me); } - RERAISE_EXC(); - } - /*elog(NOTICE, "PLy_restart_in_progress is %d", PLy_restart_in_progress);*/ + /* + * elog(NOTICE, "PLy_restart_in_progress is %d", + * PLy_restart_in_progress); + */ + + proc = PLy_procedure_get(fcinfo, is_trigger); - proc = PLy_procedure_get(fcinfo, is_trigger); + if (is_trigger) + { + HeapTuple trv = PLy_trigger_handler(fcinfo, proc); + + retval = PointerGetDatum(trv); + } + else + retval = PLy_function_handler(fcinfo, proc); - if (is_trigger) - { - HeapTuple trv = PLy_trigger_handler(fcinfo, proc); - retval = PointerGetDatum(trv); - } - else - retval = PLy_function_handler(fcinfo, proc); + CALL_LEVEL_DEC(); + RESTORE_EXC(); - CALL_LEVEL_DEC(); - RESTORE_EXC(); - - Py_DECREF(proc->me); - refc(proc->me); + Py_DECREF(proc->me); + refc(proc->me); - return retval; + return retval; } /* trigger and function sub handlers @@ -389,339 +407,355 @@ plpython_call_handler(PG_FUNCTION_ARGS) * to take no arguments and return an argument of type opaque. */ HeapTuple -PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure *proc) +PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure * proc) { - DECLARE_EXC(); - HeapTuple rv = NULL; - PyObject * volatile plargs = NULL; - PyObject * volatile plrv = NULL; + DECLARE_EXC(); + HeapTuple rv = NULL; + PyObject *volatile plargs = NULL; + PyObject *volatile plrv = NULL; - enter(); + enter(); - SAVE_EXC(); - if (TRAP_EXC()) - { - RESTORE_EXC(); + SAVE_EXC(); + if (TRAP_EXC()) + { + RESTORE_EXC(); - Py_XDECREF(plargs); - Py_XDECREF(plrv); + Py_XDECREF(plargs); + Py_XDECREF(plrv); - RERAISE_EXC(); - } + RERAISE_EXC(); + } - plargs = PLy_trigger_build_args(fcinfo, proc, &rv); - plrv = PLy_procedure_call(proc, "TD", plargs); + 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, "plpython: SPI_finish failed"); + /* + * Disconnect from SPI manager + */ + if (SPI_finish() != SPI_OK_FINISH) + elog(ERROR, "plpython: SPI_finish failed"); - if (plrv == NULL) - elog(FATAL, "Aiieee, PLy_procedure_call returned NULL"); + if (plrv == NULL) + elog(FATAL, "Aiieee, PLy_procedure_call returned NULL"); - if (PLy_restart_in_progress) - elog(FATAL, "Aiieee, restart in progress not expected"); + if (PLy_restart_in_progress) + elog(FATAL, "Aiieee, restart in progress not expected"); - /* return of None means we're happy with the tuple - */ - if (plrv != Py_None) - { - char *srv; + /* + * return of None means we're happy with the tuple + */ + if (plrv != Py_None) + { + char *srv; - if (!PyString_Check(plrv)) - elog(ERROR, "plpython: Expected trigger to return None or a String"); + if (!PyString_Check(plrv)) + elog(ERROR, "plpython: 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; + 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(NOTICE,"plpython: 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, "plpython: Expected return to be 'SKIP' or 'MODIFY'"); + 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(NOTICE, "plpython: 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, "plpython: Expected return to be 'SKIP' or 'MODIFY'"); + } } - } - Py_DECREF(plargs); - Py_DECREF(plrv); + Py_DECREF(plargs); + Py_DECREF(plrv); - RESTORE_EXC(); + RESTORE_EXC(); - return rv; + return rv; } HeapTuple -PLy_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, j, 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) +PLy_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, + j, + 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, "plpython: TD[\"new\"] deleted, unable to modify tuple"); + if (!PyDict_Check(plntup)) + elog(ERROR, "plpython: TD[\"new\"] is not a dictionary object"); + Py_INCREF(plntup); + + plkeys = PyDict_Keys(plntup); + natts = PyList_Size(plkeys); + + if (natts != proc->result.out.r.natts) + elog(ERROR, "plpython: TD[\"new\"] has an incorrect number of keys."); + + modattrs = palloc(natts * sizeof(int)); + modvalues = palloc(natts * sizeof(Datum)); + for (i = 0; i < natts; i++) + { + modattrs[i] = i + 1; + modvalues[i] = (Datum) NULL; + } + modnulls = palloc(natts + 1); + memset(modnulls, 'n', natts); + modnulls[natts] = '\0'; + + tupdesc = tdata->tg_relation->rd_att; + + for (j = 0; j < natts; j++) + { + char *src; + + platt = PyList_GetItem(plkeys, j); + if (!PyString_Check(platt)) + elog(ERROR, "plpython: attribute is not a string"); + attn = modattrs[j] = SPI_fnumber(tupdesc, PyString_AsString(platt)); + + if (attn == SPI_ERROR_NOATTRIBUTE) + elog(ERROR, "plpython: invalid attribute `%s' in tuple.", + PyString_AsString(platt)); + atti = attn - 1; + + plval = PyDict_GetItem(plntup, platt); + if (plval == NULL) + elog(FATAL, "plpython: interpreter is probably corrupted"); + + Py_INCREF(plval); + + if (plval != Py_None) + { + plstr = PyObject_Str(plval); + src = PyString_AsString(plstr); + + modvalues[j] = FunctionCall3(&proc->result.out.r.atts[atti].typfunc, + CStringGetDatum(src), + ObjectIdGetDatum(proc->result.out.r.atts[atti].typelem), + Int32GetDatum(tupdesc->attrs[j]->atttypmod)); + modnulls[j] = ' '; + + Py_DECREF(plstr); + plstr = NULL; + } + Py_DECREF(plval); + plval = NULL; + + } + rtup = SPI_modifytuple(tdata->tg_relation, otup, natts, modattrs, + modvalues, modnulls); + + /* + * FIXME -- these leak if not explicity pfree'd by other elog calls, + * no? + */ pfree(modattrs); + pfree(modvalues); + pfree(modnulls); + + if (rtup == NULL) + elog(ERROR, "plpython: SPI_modifytuple failed -- error %d", SPI_result); + + Py_DECREF(plntup); + Py_DECREF(plkeys); + + RESTORE_EXC(); - RERAISE_EXC(); - } - - if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL) - elog(ERROR, "plpython: TD[\"new\"] deleted, unable to modify tuple"); - if (!PyDict_Check(plntup)) - elog(ERROR, "plpython: TD[\"new\"] is not a dictionary object"); - Py_INCREF(plntup); - - plkeys = PyDict_Keys(plntup); - natts = PyList_Size(plkeys); - - if (natts != proc->result.out.r.natts) - elog(ERROR, "plpython: TD[\"new\"] has an incorrect number of keys."); - - modattrs = palloc(natts * sizeof(int)); - modvalues = palloc(natts * sizeof(Datum)); - for (i = 0; i < natts; i++) - { - modattrs[i] = i + 1; - modvalues[i] = (Datum) NULL; - } - modnulls = palloc(natts + 1); - memset(modnulls, 'n', natts); - modnulls[natts] = '\0'; - - tupdesc = tdata->tg_relation->rd_att; - - for (j = 0; j < natts; j++) - { - char *src; - - platt = PyList_GetItem(plkeys, j); - if (!PyString_Check(platt)) - elog(ERROR, "plpython: attribute is not a string"); - attn = modattrs[j] = SPI_fnumber(tupdesc, PyString_AsString(platt)); - - if (attn == SPI_ERROR_NOATTRIBUTE) - elog(ERROR, "plpython: invalid attribute `%s' in tuple.", - PyString_AsString(platt)); - atti = attn - 1; - - plval = PyDict_GetItem(plntup, platt); - if (plval == NULL) - elog(FATAL, "plpython: interpreter is probably corrupted"); - - Py_INCREF(plval); - - if (plval != Py_None) - { - plstr = PyObject_Str(plval); - src = PyString_AsString(plstr); - - modvalues[j] = FunctionCall3(&proc->result.out.r.atts[atti].typfunc, - CStringGetDatum(src), - ObjectIdGetDatum(proc->result.out.r.atts[atti].typelem), - Int32GetDatum(tupdesc->attrs[j]->atttypmod)); - modnulls[j] = ' '; - - Py_DECREF(plstr); - plstr = NULL; - } - Py_DECREF(plval); - plval = NULL; - - } - rtup = SPI_modifytuple(tdata->tg_relation, otup, natts, modattrs, - modvalues, modnulls); - - /* FIXME -- these leak if not explicity pfree'd by other elog calls, no? - */ - pfree(modattrs); - pfree(modvalues); - pfree(modnulls); - - if (rtup == NULL) - elog(ERROR, "plpython: SPI_modifytuple failed -- error %d", SPI_result); - - Py_DECREF(plntup); - Py_DECREF(plkeys); - - RESTORE_EXC(); - - return rtup; + return rtup; } PyObject * -PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *rv) -{ - DECLARE_EXC(); - TriggerData *tdata; - PyObject *pltname, *pltevent, *pltwhen, *pltlevel, *pltrelid; - PyObject *pltargs, *pytnew, *pytold; - PyObject * volatile pltdata = NULL; - char *stroid; - - enter(); - - SAVE_EXC(); - if (TRAP_EXC()) - { - RESTORE_EXC(); - - Py_XDECREF(pltdata); - - RERAISE_EXC(); - } - - tdata = (TriggerData *) fcinfo->context; - - pltdata = PyDict_New(); - if (!pltdata) - PLy_elog(ERROR, "Unable to build arguments for trigger procedure"); - - pltname = PyString_FromString(tdata->tg_trigger->tgname); - PyDict_SetItemString(pltdata, "name", pltname); - Py_DECREF(pltname); - - stroid = DatumGetCString(DirectFunctionCall1(oidout, - ObjectIdGetDatum(tdata->tg_relation->rd_id))); - pltrelid = PyString_FromString(stroid); - PyDict_SetItemString(pltdata, "relid", pltrelid); - Py_DECREF(pltrelid); - pfree(stroid); - - - - if (TRIGGER_FIRED_BEFORE(tdata->tg_event)) - pltwhen = PyString_FromString("BEFORE"); - else if (TRIGGER_FIRED_AFTER(tdata->tg_event)) - pltwhen = PyString_FromString("AFTER"); - else - pltwhen = PyString_FromString("UNKNOWN"); - PyDict_SetItemString(pltdata, "when", pltwhen); - Py_DECREF(pltwhen); - - if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event)) - pltlevel = PyString_FromString("ROW"); - else if (TRIGGER_FIRED_FOR_STATEMENT(tdata->tg_event)) - pltlevel = PyString_FromString("STATEMENT"); - else - pltlevel = PyString_FromString("UNKNOWN"); - PyDict_SetItemString(pltdata, "level", pltlevel); - Py_DECREF(pltlevel); - - if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event)) - { - pltevent = PyString_FromString("INSERT"); - PyDict_SetItemString(pltdata, "old", Py_None); - pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple, - tdata->tg_relation->rd_att); - PyDict_SetItemString(pltdata, "new", pytnew); - Py_DECREF(pytnew); - *rv = tdata->tg_trigtuple; - } - else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event)) - { - pltevent = PyString_FromString("DELETE"); - PyDict_SetItemString(pltdata, "new", Py_None); - pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple, - tdata->tg_relation->rd_att); - PyDict_SetItemString(pltdata, "old", pytold); - Py_DECREF(pytold); - *rv = tdata->tg_trigtuple; - } - else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event)) - { - pltevent = PyString_FromString("UPDATE"); - pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_newtuple, - tdata->tg_relation->rd_att); - PyDict_SetItemString(pltdata, "new", pytnew); - Py_DECREF(pytnew); - pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple, - tdata->tg_relation->rd_att); - PyDict_SetItemString(pltdata, "old", pytold); - Py_DECREF(pytold); - *rv = tdata->tg_newtuple; - } - else - { - pltevent = PyString_FromString("UNKNOWN"); - PyDict_SetItemString(pltdata, "old", Py_None); - PyDict_SetItemString(pltdata, "new", Py_None); - *rv = tdata->tg_trigtuple; - } - PyDict_SetItemString(pltdata, "event", pltevent); - Py_DECREF(pltevent); - - if (tdata->tg_trigger->tgnargs) - { - /* all strings... - */ - int i; - PyObject *pltarg; - - pltargs = PyList_New(tdata->tg_trigger->tgnargs); - for (i = 0; i < tdata->tg_trigger->tgnargs; i++) - { - pltarg = PyString_FromString(tdata->tg_trigger->tgargs[i]); - /* stolen, don't Py_DECREF - */ - PyList_SetItem(pltargs, i, pltarg); - } - } - else - { - Py_INCREF(Py_None); - pltargs = Py_None; - } - PyDict_SetItemString(pltdata, "args", pltargs); - Py_DECREF(pltargs); - - RESTORE_EXC(); - - return pltdata; +PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *rv) +{ + DECLARE_EXC(); + TriggerData *tdata; + PyObject *pltname, + *pltevent, + *pltwhen, + *pltlevel, + *pltrelid; + PyObject *pltargs, + *pytnew, + *pytold; + PyObject *volatile pltdata = NULL; + char *stroid; + + enter(); + + SAVE_EXC(); + if (TRAP_EXC()) + { + RESTORE_EXC(); + + Py_XDECREF(pltdata); + + RERAISE_EXC(); + } + + tdata = (TriggerData *) fcinfo->context; + + pltdata = PyDict_New(); + if (!pltdata) + PLy_elog(ERROR, "Unable to build arguments for trigger procedure"); + + pltname = PyString_FromString(tdata->tg_trigger->tgname); + PyDict_SetItemString(pltdata, "name", pltname); + Py_DECREF(pltname); + + stroid = DatumGetCString(DirectFunctionCall1(oidout, + ObjectIdGetDatum(tdata->tg_relation->rd_id))); + pltrelid = PyString_FromString(stroid); + PyDict_SetItemString(pltdata, "relid", pltrelid); + Py_DECREF(pltrelid); + pfree(stroid); + + + + if (TRIGGER_FIRED_BEFORE(tdata->tg_event)) + pltwhen = PyString_FromString("BEFORE"); + else if (TRIGGER_FIRED_AFTER(tdata->tg_event)) + pltwhen = PyString_FromString("AFTER"); + else + pltwhen = PyString_FromString("UNKNOWN"); + PyDict_SetItemString(pltdata, "when", pltwhen); + Py_DECREF(pltwhen); + + if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event)) + pltlevel = PyString_FromString("ROW"); + else if (TRIGGER_FIRED_FOR_STATEMENT(tdata->tg_event)) + pltlevel = PyString_FromString("STATEMENT"); + else + pltlevel = PyString_FromString("UNKNOWN"); + PyDict_SetItemString(pltdata, "level", pltlevel); + Py_DECREF(pltlevel); + + if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event)) + { + pltevent = PyString_FromString("INSERT"); + PyDict_SetItemString(pltdata, "old", Py_None); + pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple, + tdata->tg_relation->rd_att); + PyDict_SetItemString(pltdata, "new", pytnew); + Py_DECREF(pytnew); + *rv = tdata->tg_trigtuple; + } + else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event)) + { + pltevent = PyString_FromString("DELETE"); + PyDict_SetItemString(pltdata, "new", Py_None); + pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple, + tdata->tg_relation->rd_att); + PyDict_SetItemString(pltdata, "old", pytold); + Py_DECREF(pytold); + *rv = tdata->tg_trigtuple; + } + else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event)) + { + pltevent = PyString_FromString("UPDATE"); + pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_newtuple, + tdata->tg_relation->rd_att); + PyDict_SetItemString(pltdata, "new", pytnew); + Py_DECREF(pytnew); + pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple, + tdata->tg_relation->rd_att); + PyDict_SetItemString(pltdata, "old", pytold); + Py_DECREF(pytold); + *rv = tdata->tg_newtuple; + } + else + { + pltevent = PyString_FromString("UNKNOWN"); + PyDict_SetItemString(pltdata, "old", Py_None); + PyDict_SetItemString(pltdata, "new", Py_None); + *rv = tdata->tg_trigtuple; + } + PyDict_SetItemString(pltdata, "event", pltevent); + Py_DECREF(pltevent); + + if (tdata->tg_trigger->tgnargs) + { + /* + * all strings... + */ + int i; + PyObject *pltarg; + + pltargs = PyList_New(tdata->tg_trigger->tgnargs); + for (i = 0; i < tdata->tg_trigger->tgnargs; i++) + { + pltarg = PyString_FromString(tdata->tg_trigger->tgargs[i]); + + /* + * stolen, don't Py_DECREF + */ + PyList_SetItem(pltargs, i, pltarg); + } + } + else + { + Py_INCREF(Py_None); + pltargs = Py_None; + } + PyDict_SetItemString(pltdata, "args", pltargs); + Py_DECREF(pltargs); + + RESTORE_EXC(); + + return pltdata; } @@ -729,176 +763,182 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r /* function handler and friends */ Datum -PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *proc) -{ - DECLARE_EXC(); - Datum rv; - PyObject * volatile plargs = NULL; - PyObject * volatile plrv = NULL; - PyObject * volatile plrv_so = NULL; - char *plrv_sc; - - enter(); - - /* - * setup to catch elog in while building function arguments, - * and DECREF the plargs if the function call fails - */ - SAVE_EXC(); - if (TRAP_EXC()) - { - RESTORE_EXC(); - - Py_XDECREF(plargs); - Py_XDECREF(plrv); - Py_XDECREF(plrv_so); - - RERAISE_EXC(); - } - - plargs = PLy_function_build_args(fcinfo, proc); - plrv = PLy_procedure_call(proc, "args", plargs); - - /* Disconnect from SPI manager and then create the return - * values datum (if the input function does a palloc for it - * this must not be allocated in the SPI memory context - * because SPI_finish would free it). - */ - if (SPI_finish() != SPI_OK_FINISH) - elog(ERROR, "plpython: SPI_finish failed"); - - if (plrv == NULL) - { - elog(FATAL, "Aiieee, PLy_procedure_call returned NULL"); -#if 0 - if (!PLy_restart_in_progress) - PLy_elog(ERROR, "plpython: Function \"%s\" failed.", proc->proname); +PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc) +{ + DECLARE_EXC(); + Datum rv; + PyObject *volatile plargs = NULL; + PyObject *volatile plrv = NULL; + PyObject *volatile plrv_so = NULL; + char *plrv_sc; + + enter(); + + /* + * setup to catch elog in while building function arguments, and + * DECREF the plargs if the function call fails + */ + SAVE_EXC(); + if (TRAP_EXC()) + { + RESTORE_EXC(); + + Py_XDECREF(plargs); + Py_XDECREF(plrv); + Py_XDECREF(plrv_so); + + RERAISE_EXC(); + } + + plargs = PLy_function_build_args(fcinfo, proc); + plrv = PLy_procedure_call(proc, "args", plargs); + + /* + * Disconnect from SPI manager and then create the return values datum + * (if the input function does a palloc for it this must not be + * allocated in the SPI memory context because SPI_finish would free + * it). + */ + if (SPI_finish() != SPI_OK_FINISH) + elog(ERROR, "plpython: SPI_finish failed"); - /* FIXME is this dead code? i'm pretty sure it is for unnested - * calls, but not for nested calls - */ - RAISE_EXC(1); + if (plrv == NULL) + { + elog(FATAL, "Aiieee, PLy_procedure_call returned NULL"); +#if 0 + if (!PLy_restart_in_progress) + PLy_elog(ERROR, "plpython: Function \"%s\" failed.", proc->proname); + + /* + * FIXME is this dead code? i'm pretty sure it is for unnested + * calls, but not for nested calls + */ + RAISE_EXC(1); #endif - } - - /* convert the python PyObject to a postgresql Datum - * FIXME returning a NULL, ie PG_RETURN_NULL() blows the backend - * to small messy bits... it this a bug or expected? so just - * call with the string value of None for now - */ - - if (plrv == Py_None) - { - fcinfo->isnull = true; - rv = (Datum) NULL; - } - else - { - fcinfo->isnull = false; - plrv_so = PyObject_Str(plrv); - plrv_sc = PyString_AsString(plrv_so); - rv = FunctionCall3(&proc->result.out.d.typfunc, - PointerGetDatum(plrv_sc), - ObjectIdGetDatum(proc->result.out.d.typelem), - Int32GetDatum(-1)); - } - - RESTORE_EXC(); - - Py_XDECREF(plargs); - Py_DECREF(plrv); - Py_XDECREF(plrv_so); - - return rv; -} + } -PyObject * -PLy_procedure_call(PLyProcedure *proc, char *kargs, PyObject *vargs) -{ - PyObject *rv; - - enter(); + /* + * convert the python PyObject to a postgresql Datum FIXME returning a + * NULL, ie PG_RETURN_NULL() blows the backend to small messy bits... + * it this a bug or expected? so just call with the string value of + * None for now + */ + + if (plrv == Py_None) + { + fcinfo->isnull = true; + rv = (Datum) NULL; + } + else + { + fcinfo->isnull = false; + plrv_so = PyObject_Str(plrv); + plrv_sc = PyString_AsString(plrv_so); + rv = FunctionCall3(&proc->result.out.d.typfunc, + PointerGetDatum(plrv_sc), + ObjectIdGetDatum(proc->result.out.d.typelem), + Int32GetDatum(-1)); + } - PyDict_SetItemString(proc->globals, kargs, vargs); - rv = PyObject_CallFunction(proc->reval, "O", proc->code); + RESTORE_EXC(); - if ((rv == NULL) || (PyErr_Occurred())) - { - Py_XDECREF(rv); - if (!PLy_restart_in_progress) - PLy_elog(ERROR, "Call of function `%s' failed.", proc->proname); - RAISE_EXC(1); - } + Py_XDECREF(plargs); + Py_DECREF(plrv); + Py_XDECREF(plrv_so); - return rv; + return rv; } PyObject * -PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc) +PLy_procedure_call(PLyProcedure * proc, char *kargs, PyObject * vargs) { - DECLARE_EXC(); - PyObject * volatile arg = NULL; - PyObject * volatile args = NULL; - int i; + PyObject *rv; - enter(); + enter(); - /* FIXME -- if the setjmp setup is expensive, add the arg and - * args field to the procedure struct and cleanup at the - * start of the next call - */ - SAVE_EXC(); - if (TRAP_EXC()) - { - RESTORE_EXC(); - Py_XDECREF(arg); - Py_XDECREF(args); + PyDict_SetItemString(proc->globals, kargs, vargs); + rv = PyObject_CallFunction(proc->reval, "O", proc->code); - RERAISE_EXC(); - } - - args = PyList_New(proc->nargs); - for (i = 0; i < proc->nargs; i++) - { - if (proc->args[i].is_rel == 1) + if ((rv == NULL) || (PyErr_Occurred())) { - TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i]; - arg = PLyDict_FromTuple(&(proc->args[i]), slot->val, - slot->ttc_tupleDescriptor); + Py_XDECREF(rv); + if (!PLy_restart_in_progress) + PLy_elog(ERROR, "Call of function `%s' failed.", proc->proname); + RAISE_EXC(1); } - else + + return rv; +} + +PyObject * +PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc) +{ + DECLARE_EXC(); + PyObject *volatile arg = NULL; + PyObject *volatile args = NULL; + int i; + + enter(); + + /* + * FIXME -- if the setjmp setup is expensive, add the arg and args + * field to the procedure struct and cleanup at the start of the next + * call + */ + SAVE_EXC(); + if (TRAP_EXC()) { - if (!fcinfo->argnull[i]) - { - char *ct; - Datum dt; + RESTORE_EXC(); + Py_XDECREF(arg); + Py_XDECREF(args); - dt = FunctionCall3(&(proc->args[i].in.d.typfunc), - fcinfo->arg[i], - ObjectIdGetDatum(proc->args[i].in.d.typelem), - Int32GetDatum(-1)); - ct = DatumGetCString(dt); - arg = (proc->args[i].in.d.func)(ct); - pfree(ct); - } - else - arg = NULL; + RERAISE_EXC(); } - if (arg == NULL) + args = PyList_New(proc->nargs); + for (i = 0; i < proc->nargs; i++) { - Py_INCREF(Py_None); - arg = Py_None; - } + if (proc->args[i].is_rel == 1) + { + TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i]; - /* FIXME -- error check this - */ - PyList_SetItem(args, i, arg); - } + arg = PLyDict_FromTuple(&(proc->args[i]), slot->val, + slot->ttc_tupleDescriptor); + } + else + { + if (!fcinfo->argnull[i]) + { + char *ct; + Datum dt; + + dt = FunctionCall3(&(proc->args[i].in.d.typfunc), + fcinfo->arg[i], + ObjectIdGetDatum(proc->args[i].in.d.typelem), + Int32GetDatum(-1)); + ct = DatumGetCString(dt); + arg = (proc->args[i].in.d.func) (ct); + pfree(ct); + } + else + arg = NULL; + } - RESTORE_EXC(); + if (arg == NULL) + { + Py_INCREF(Py_None); + arg = Py_None; + } - return args; + /* + * FIXME -- error check this + */ + PyList_SetItem(args, i, arg); + } + + RESTORE_EXC(); + + return args; } @@ -907,532 +947,552 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc) static PLyProcedure * PLy_procedure_get(FunctionCallInfo fcinfo, bool is_trigger) { - Oid fn_oid; - HeapTuple procTup; - char key[128]; - PyObject *plproc; - PLyProcedure *proc = NULL; - int rv; - - enter(); - - fn_oid = fcinfo->flinfo->fn_oid; - procTup = SearchSysCache(PROCOID, - ObjectIdGetDatum(fn_oid), - 0, 0, 0); - if (!HeapTupleIsValid(procTup)) - elog(ERROR, "plpython: cache lookup for procedure %u failed", fn_oid); - - rv = snprintf(key, sizeof(key), "%u%s", - fn_oid, - is_trigger ? "_trigger" : ""); - if ((rv >= sizeof(key)) || (rv < 0)) - elog(FATAL, "plpython: Buffer overrun in %s:%d", __FILE__, __LINE__); - - plproc = PyDict_GetItemString(PLy_procedure_cache, key); - - if (plproc != NULL) - { - Py_INCREF(plproc); - if (!PyCObject_Check(plproc)) - elog(FATAL, "plpython: Expected a PyCObject, didn't get one"); - - mark(); - - proc = PyCObject_AsVoidPtr(plproc); - if (proc->me != plproc) - elog(FATAL, "plpython: Aiieee, proc->me != plproc"); - /* did we find an up-to-date cache entry? */ - if (proc->fn_xmin != procTup->t_data->t_xmin || - proc->fn_cmin != procTup->t_data->t_cmin) - { - Py_DECREF(plproc); - proc = NULL; - } - } - - if (proc == NULL) - proc = PLy_procedure_create(fcinfo, is_trigger, procTup, key); - - ReleaseSysCache(procTup); - - return proc; + Oid fn_oid; + HeapTuple procTup; + char key[128]; + PyObject *plproc; + PLyProcedure *proc = NULL; + int rv; + + enter(); + + fn_oid = fcinfo->flinfo->fn_oid; + procTup = SearchSysCache(PROCOID, + ObjectIdGetDatum(fn_oid), + 0, 0, 0); + if (!HeapTupleIsValid(procTup)) + elog(ERROR, "plpython: cache lookup for procedure %u failed", fn_oid); + + rv = snprintf(key, sizeof(key), "%u%s", + fn_oid, + is_trigger ? "_trigger" : ""); + if ((rv >= sizeof(key)) || (rv < 0)) + elog(FATAL, "plpython: Buffer overrun in %s:%d", __FILE__, __LINE__); + + plproc = PyDict_GetItemString(PLy_procedure_cache, key); + + if (plproc != NULL) + { + Py_INCREF(plproc); + if (!PyCObject_Check(plproc)) + elog(FATAL, "plpython: Expected a PyCObject, didn't get one"); + + mark(); + + proc = PyCObject_AsVoidPtr(plproc); + if (proc->me != plproc) + elog(FATAL, "plpython: Aiieee, proc->me != plproc"); + /* did we find an up-to-date cache entry? */ + if (proc->fn_xmin != procTup->t_data->t_xmin || + proc->fn_cmin != procTup->t_data->t_cmin) + { + Py_DECREF(plproc); + proc = NULL; + } + } + + if (proc == NULL) + proc = PLy_procedure_create(fcinfo, is_trigger, procTup, key); + + ReleaseSysCache(procTup); + + return proc; } static PLyProcedure * PLy_procedure_create(FunctionCallInfo fcinfo, bool is_trigger, HeapTuple procTup, char *key) { - char procName[256]; - DECLARE_EXC(); - Form_pg_proc procStruct; - PLyProcedure *volatile proc; - char *volatile procSource = NULL; - Datum procDatum; - int i, rv; - - enter(); - - procStruct = (Form_pg_proc) GETSTRUCT(procTup); - - rv = snprintf(procName, sizeof(procName), - "__plpython_procedure_%s_%u%s", - NameStr(procStruct->proname), - fcinfo->flinfo->fn_oid, - is_trigger ? "_trigger" : ""); - if ((rv >= sizeof(procName)) || (rv < 0)) - elog(FATAL, "plpython: Procedure name would overrun buffer"); - - proc = PLy_malloc(sizeof(PLyProcedure)); - proc->proname = PLy_malloc(strlen(procName) + 1); - strcpy(proc->proname, procName); - proc->fn_xmin = procTup->t_data->t_xmin; - proc->fn_cmin = procTup->t_data->t_cmin; - PLy_typeinfo_init(&proc->result); - for (i = 0; i < FUNC_MAX_ARGS; i++) - PLy_typeinfo_init(&proc->args[i]); - proc->nargs = 0; - proc->code = proc->interp = proc->reval = proc->statics = NULL; - proc->globals = proc->me = NULL; - - SAVE_EXC(); - if (TRAP_EXC()) - { - RESTORE_EXC(); - PLy_procedure_delete(proc); - if (procSource) + char procName[256]; + + DECLARE_EXC(); + Form_pg_proc procStruct; + PLyProcedure *volatile proc; + char *volatile procSource = NULL; + Datum procDatum; + int i, + rv; + + enter(); + + procStruct = (Form_pg_proc) GETSTRUCT(procTup); + + rv = snprintf(procName, sizeof(procName), + "__plpython_procedure_%s_%u%s", + NameStr(procStruct->proname), + fcinfo->flinfo->fn_oid, + is_trigger ? "_trigger" : ""); + if ((rv >= sizeof(procName)) || (rv < 0)) + elog(FATAL, "plpython: Procedure name would overrun buffer"); + + proc = PLy_malloc(sizeof(PLyProcedure)); + proc->proname = PLy_malloc(strlen(procName) + 1); + strcpy(proc->proname, procName); + proc->fn_xmin = procTup->t_data->t_xmin; + proc->fn_cmin = procTup->t_data->t_cmin; + PLy_typeinfo_init(&proc->result); + for (i = 0; i < FUNC_MAX_ARGS; i++) + PLy_typeinfo_init(&proc->args[i]); + proc->nargs = 0; + proc->code = proc->interp = proc->reval = proc->statics = NULL; + proc->globals = proc->me = NULL; + + SAVE_EXC(); + if (TRAP_EXC()) + { + RESTORE_EXC(); + PLy_procedure_delete(proc); + if (procSource) + pfree(procSource); + RERAISE_EXC(); + } + + /* + * get information required for output conversion of the return value, + * but only if this isn't a trigger. + */ + if (!is_trigger) + { + HeapTuple rvTypeTup; + Form_pg_type rvTypeStruct; + Datum rvDatum; + + rvDatum = ObjectIdGetDatum(procStruct->prorettype); + rvTypeTup = SearchSysCache(TYPEOID, rvDatum, 0, 0, 0); + if (!HeapTupleIsValid(rvTypeTup)) + elog(ERROR, "plpython: cache lookup for type \"%u\" failed", + procStruct->prorettype); + + rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup); + if (rvTypeStruct->typrelid == InvalidOid) + PLy_output_datum_func(&proc->result, rvTypeStruct); + else + elog(ERROR, "plpython: tuple return types not supported, yet"); + + ReleaseSysCache(rvTypeTup); + } + else + { + /* + * input/output conversion for trigger tuples. use the result + * TypeInfo variable to store the tuple conversion info. + */ + TriggerData *tdata = (TriggerData *) fcinfo->context; + + PLy_input_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att); + PLy_output_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att); + } + + /* + * now get information required for input conversion of the procedures + * arguments. + */ + proc->nargs = fcinfo->nargs; + for (i = 0; i < fcinfo->nargs; i++) + { + HeapTuple argTypeTup; + Form_pg_type argTypeStruct; + Datum argDatum; + + argDatum = ObjectIdGetDatum(procStruct->proargtypes[i]); + argTypeTup = SearchSysCache(TYPEOID, argDatum, 0, 0, 0); + if (!HeapTupleIsValid(argTypeTup)) + elog(ERROR, "plpython: cache lookup for type \"%u\" failed", + procStruct->proargtypes[i]); + argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup); + + if (argTypeStruct->typrelid == InvalidOid) + PLy_input_datum_func(&(proc->args[i]), argTypeStruct); + else + { + TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i]; + + PLy_input_tuple_funcs(&(proc->args[i]), + slot->ttc_tupleDescriptor); + } + + ReleaseSysCache(argTypeTup); + } + + + /* + * get the text of the function. + */ + procDatum = DirectFunctionCall1(textout, + PointerGetDatum(&procStruct->prosrc)); + procSource = DatumGetCString(procDatum); + + PLy_procedure_compile(proc, procSource); + pfree(procSource); - RERAISE_EXC(); - } - - /* get information required for output conversion of the return - * value, but only if this isn't a trigger. - */ - if (!is_trigger) - { - HeapTuple rvTypeTup; - Form_pg_type rvTypeStruct; - Datum rvDatum; - - rvDatum = ObjectIdGetDatum(procStruct->prorettype); - rvTypeTup = SearchSysCache(TYPEOID, rvDatum, 0, 0, 0); - if (!HeapTupleIsValid(rvTypeTup)) - elog(ERROR, "plpython: cache lookup for type \"%u\" failed", - procStruct->prorettype); - - rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup); - if (rvTypeStruct->typrelid == InvalidOid) - PLy_output_datum_func(&proc->result, rvTypeStruct); - else - elog(ERROR, "plpython: tuple return types not supported, yet"); - - ReleaseSysCache(rvTypeTup); - } - else - { - /* input/output conversion for trigger tuples. use the - * result TypeInfo variable to store the tuple conversion - * info. - */ - TriggerData *tdata = (TriggerData *) fcinfo->context; - PLy_input_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att); - PLy_output_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att); - } - - /* now get information required for input conversion of the - * procedures arguments. - */ - proc->nargs = fcinfo->nargs; - for (i = 0; i < fcinfo->nargs; i++) - { - HeapTuple argTypeTup; - Form_pg_type argTypeStruct; - Datum argDatum; - - argDatum = ObjectIdGetDatum(procStruct->proargtypes[i]); - argTypeTup = SearchSysCache(TYPEOID, argDatum, 0, 0, 0); - if (!HeapTupleIsValid(argTypeTup)) - elog(ERROR, "plpython: cache lookup for type \"%u\" failed", - procStruct->proargtypes[i]); - argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup); - - if (argTypeStruct->typrelid == InvalidOid) - PLy_input_datum_func(&(proc->args[i]), argTypeStruct); - else - { - TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i]; - PLy_input_tuple_funcs(&(proc->args[i]), - slot->ttc_tupleDescriptor); - } - - ReleaseSysCache(argTypeTup); - } - - - /* get the text of the function. - */ - procDatum = DirectFunctionCall1(textout, - PointerGetDatum(&procStruct->prosrc)); - procSource = DatumGetCString(procDatum); - - PLy_procedure_compile(proc, procSource); - - pfree(procSource); - - proc->me = PyCObject_FromVoidPtr(proc, NULL); - PyDict_SetItemString(PLy_procedure_cache, key, proc->me); - - RESTORE_EXC(); - - return proc; + + proc->me = PyCObject_FromVoidPtr(proc, NULL); + PyDict_SetItemString(PLy_procedure_cache, key, proc->me); + + RESTORE_EXC(); + + return proc; } void -PLy_procedure_compile(PLyProcedure *proc, const char *src) -{ - PyObject *module, *crv = NULL; - char *msrc; - - enter(); - - /* get an instance of rexec.RExec for the function - */ - proc->interp = PyObject_CallMethod(PLy_interp_safe, "RExec", NULL); - if ((proc->interp == NULL) || (PyErr_Occurred ())) - PLy_elog(ERROR, "Unable to create rexec.RExec instance"); - - /* tweak the list of permitted modules - */ - PyObject_SetAttrString(proc->interp, "ok_builtin_modules", - PLy_importable_modules); - - proc->reval = PyObject_GetAttrString(proc->interp, "r_eval"); - if ((proc->reval == NULL) || (PyErr_Occurred ())) - PLy_elog(ERROR, "Unable to get method `r_eval' from rexec.RExec"); - - /* add a __main__ module to the function's interpreter - */ - module = PyObject_CallMethod (proc->interp, "add_module", "s", "__main__"); - if ((module == NULL) || (PyErr_Occurred ())) - PLy_elog(ERROR, "Unable to get module `__main__' from rexec.RExec"); - - /* add plpy module to the interpreters main dictionary - */ - proc->globals = PyModule_GetDict (module); - if ((proc->globals == NULL) || (PyErr_Occurred ())) - PLy_elog(ERROR, "Unable to get `__main__.__dict__' from rexec.RExec"); - - /* why the hell won't r_import or r_exec('import plpy') work? - */ - module = PyDict_GetItemString(PLy_interp_globals, "plpy"); - if ((module == NULL) || (PyErr_Occurred())) - PLy_elog(ERROR, "Unable to get `plpy'"); - Py_INCREF(module); - PyDict_SetItemString(proc->globals, "plpy", module); - - /* SD is private preserved data between calls - * GD is global data shared by all functions - */ - proc->statics = PyDict_New(); - PyDict_SetItemString(proc->globals, "SD", proc->statics); - PyDict_SetItemString(proc->globals, "GD", PLy_interp_safe_globals); - - /* insert the function code into the interpreter - */ - msrc = PLy_procedure_munge_source(proc->proname, src); - crv = PyObject_CallMethod(proc->interp, "r_exec", "s", msrc); - free(msrc); - - if ((crv != NULL) && (!PyErr_Occurred ())) - { - int clen; - char call[256]; - - Py_DECREF(crv); - - /* compile a call to the function - */ - clen = snprintf(call, sizeof(call), "%s()", proc->proname); - if ((clen < 0) || (clen >= sizeof(call))) - elog(ERROR, "plpython: string would overflow buffer."); - proc->code = Py_CompileString(call, "<string>", Py_eval_input); - if ((proc->code != NULL) && (!PyErr_Occurred ())) - return; - } - else - Py_XDECREF(crv); - - PLy_elog(ERROR, "Unable to compile function %s", proc->proname); +PLy_procedure_compile(PLyProcedure * proc, const char *src) +{ + PyObject *module, + *crv = NULL; + char *msrc; + + enter(); + + /* + * get an instance of rexec.RExec for the function + */ + proc->interp = PyObject_CallMethod(PLy_interp_safe, "RExec", NULL); + if ((proc->interp == NULL) || (PyErr_Occurred())) + PLy_elog(ERROR, "Unable to create rexec.RExec instance"); + + /* + * tweak the list of permitted modules + */ + PyObject_SetAttrString(proc->interp, "ok_builtin_modules", + PLy_importable_modules); + + proc->reval = PyObject_GetAttrString(proc->interp, "r_eval"); + if ((proc->reval == NULL) || (PyErr_Occurred())) + PLy_elog(ERROR, "Unable to get method `r_eval' from rexec.RExec"); + + /* + * add a __main__ module to the function's interpreter + */ + module = PyObject_CallMethod(proc->interp, "add_module", "s", "__main__"); + if ((module == NULL) || (PyErr_Occurred())) + PLy_elog(ERROR, "Unable to get module `__main__' from rexec.RExec"); + + /* + * add plpy module to the interpreters main dictionary + */ + proc->globals = PyModule_GetDict(module); + if ((proc->globals == NULL) || (PyErr_Occurred())) + PLy_elog(ERROR, "Unable to get `__main__.__dict__' from rexec.RExec"); + + /* + * why the hell won't r_import or r_exec('import plpy') work? + */ + module = PyDict_GetItemString(PLy_interp_globals, "plpy"); + if ((module == NULL) || (PyErr_Occurred())) + PLy_elog(ERROR, "Unable to get `plpy'"); + Py_INCREF(module); + PyDict_SetItemString(proc->globals, "plpy", module); + + /* + * SD is private preserved data between calls GD is global data shared + * by all functions + */ + proc->statics = PyDict_New(); + PyDict_SetItemString(proc->globals, "SD", proc->statics); + PyDict_SetItemString(proc->globals, "GD", PLy_interp_safe_globals); + + /* + * insert the function code into the interpreter + */ + msrc = PLy_procedure_munge_source(proc->proname, src); + crv = PyObject_CallMethod(proc->interp, "r_exec", "s", msrc); + free(msrc); + + if ((crv != NULL) && (!PyErr_Occurred())) + { + int clen; + char call[256]; + + Py_DECREF(crv); + + /* + * compile a call to the function + */ + clen = snprintf(call, sizeof(call), "%s()", proc->proname); + if ((clen < 0) || (clen >= sizeof(call))) + elog(ERROR, "plpython: string would overflow buffer."); + proc->code = Py_CompileString(call, "<string>", Py_eval_input); + if ((proc->code != NULL) && (!PyErr_Occurred())) + return; + } + else + Py_XDECREF(crv); + + PLy_elog(ERROR, "Unable to compile function %s", proc->proname); } char * PLy_procedure_munge_source(const char *name, const char *src) { - char *mrc, *mp; - const char *sp; - size_t mlen, plen; + char *mrc, + *mp; + const char *sp; + size_t mlen, + plen; - enter(); + enter(); - /* room for function source and the def statement - */ - mlen = (strlen (src) * 2) + strlen(name) + 16; + /* + * room for function source and the def statement + */ + mlen = (strlen(src) * 2) + strlen(name) + 16; - mrc = PLy_malloc(mlen); - plen = snprintf(mrc, mlen, "def %s():\n\t", name); - if ((plen < 0) || (plen >= mlen)) - elog(FATAL, "Aiieee, impossible buffer overrun (or snprintf failure)"); + mrc = PLy_malloc(mlen); + plen = snprintf(mrc, mlen, "def %s():\n\t", name); + if ((plen < 0) || (plen >= mlen)) + elog(FATAL, "Aiieee, impossible buffer overrun (or snprintf failure)"); - sp = src; - mp = mrc + plen; + sp = src; + mp = mrc + plen; - while (*sp != '\0') - { - if (*sp == '\n') + while (*sp != '\0') { - *mp++ = *sp++; - *mp++ = '\t'; + if (*sp == '\n') + { + *mp++ = *sp++; + *mp++ = '\t'; + } + else + *mp++ = *sp++; } - else - *mp++ = *sp++; - } - *mp++ = '\n'; - *mp++ = '\n'; - *mp = '\0'; + *mp++ = '\n'; + *mp++ = '\n'; + *mp = '\0'; - if (mp > (mrc + mlen)) - elog(FATAL, "plpython: Buffer overrun in PLy_munge_source"); + if (mp > (mrc + mlen)) + elog(FATAL, "plpython: Buffer overrun in PLy_munge_source"); - return mrc; + return mrc; } void -PLy_procedure_delete(PLyProcedure *proc) -{ - int i; - - enter(); - - Py_XDECREF(proc->code); - Py_XDECREF(proc->interp); - Py_XDECREF(proc->reval); - Py_XDECREF(proc->statics); - Py_XDECREF(proc->globals); - Py_XDECREF(proc->me); - if (proc->proname) - PLy_free(proc->proname); - for (i = 0; i < proc->nargs; i++) - if (proc->args[i].is_rel == 1) - { - if (proc->args[i].in.r.atts) - PLy_free(proc->args[i].in.r.atts); - if (proc->args[i].out.r.atts) - PLy_free(proc->args[i].out.r.atts); - } +PLy_procedure_delete(PLyProcedure * proc) +{ + int i; + + enter(); + + Py_XDECREF(proc->code); + Py_XDECREF(proc->interp); + Py_XDECREF(proc->reval); + Py_XDECREF(proc->statics); + Py_XDECREF(proc->globals); + Py_XDECREF(proc->me); + if (proc->proname) + PLy_free(proc->proname); + for (i = 0; i < proc->nargs; i++) + if (proc->args[i].is_rel == 1) + { + if (proc->args[i].in.r.atts) + PLy_free(proc->args[i].in.r.atts); + if (proc->args[i].out.r.atts) + PLy_free(proc->args[i].out.r.atts); + } - leave(); + leave(); } /* conversion functions. remember output from python is * input to postgresql, and vis versa. */ void -PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc) +PLy_input_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc) { - int i; - Datum datum; - - enter (); + int i; + Datum datum; - if (arg->is_rel == 0) - elog(FATAL, "plpython: PLyTypeInfo struct is initialized for a Datum"); + enter(); - arg->is_rel = 1; - arg->in.r.natts = desc->natts; - arg->in.r.atts = malloc(desc->natts * sizeof(PLyDatumToOb)); + if (arg->is_rel == 0) + elog(FATAL, "plpython: PLyTypeInfo struct is initialized for a Datum"); - for (i = 0; i < desc->natts; i++) - { - HeapTuple typeTup; - Form_pg_type typeStruct; + arg->is_rel = 1; + arg->in.r.natts = desc->natts; + arg->in.r.atts = malloc(desc->natts * sizeof(PLyDatumToOb)); - datum = ObjectIdGetDatum(desc->attrs[i]->atttypid); - typeTup = SearchSysCache(TYPEOID, datum, 0, 0, 0); - if (!HeapTupleIsValid(typeTup)) + for (i = 0; i < desc->natts; i++) { - char *attname = NameStr(desc->attrs[i]->attname); - elog(ERROR, "plpython: Cache lookup for attribute `%s' type `%u' failed", - attname, desc->attrs[i]->atttypid); - } + HeapTuple typeTup; + Form_pg_type typeStruct; + + datum = ObjectIdGetDatum(desc->attrs[i]->atttypid); + typeTup = SearchSysCache(TYPEOID, datum, 0, 0, 0); + if (!HeapTupleIsValid(typeTup)) + { + char *attname = NameStr(desc->attrs[i]->attname); + + elog(ERROR, "plpython: Cache lookup for attribute `%s' type `%u' failed", + attname, desc->attrs[i]->atttypid); + } - typeStruct = (Form_pg_type) GETSTRUCT(typeTup); + typeStruct = (Form_pg_type) GETSTRUCT(typeTup); - PLy_input_datum_func2(&(arg->in.r.atts[i]), typeStruct); + PLy_input_datum_func2(&(arg->in.r.atts[i]), typeStruct); - ReleaseSysCache(typeTup); - } + ReleaseSysCache(typeTup); + } } void -PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc) +PLy_output_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc) { - int i; - Datum datum; - - enter (); + int i; + Datum datum; - if (arg->is_rel == 0) - elog(FATAL, "plpython: PLyTypeInfo struct is initialized for a Datum"); + enter(); - arg->is_rel = 1; - arg->out.r.natts = desc->natts; - arg->out.r.atts = malloc(desc->natts * sizeof(PLyDatumToOb)); + if (arg->is_rel == 0) + elog(FATAL, "plpython: PLyTypeInfo struct is initialized for a Datum"); - for (i = 0; i < desc->natts; i++) - { - HeapTuple typeTup; - Form_pg_type typeStruct; + arg->is_rel = 1; + arg->out.r.natts = desc->natts; + arg->out.r.atts = malloc(desc->natts * sizeof(PLyDatumToOb)); - datum = ObjectIdGetDatum(desc->attrs[i]->atttypid); - typeTup = SearchSysCache(TYPEOID, datum, 0, 0, 0); - if (!HeapTupleIsValid(typeTup)) + for (i = 0; i < desc->natts; i++) { - char *attname = NameStr(desc->attrs[i]->attname); - elog(ERROR, "plpython: Cache lookup for attribute `%s' type `%u' failed", - attname, desc->attrs[i]->atttypid); - } + HeapTuple typeTup; + Form_pg_type typeStruct; + + datum = ObjectIdGetDatum(desc->attrs[i]->atttypid); + typeTup = SearchSysCache(TYPEOID, datum, 0, 0, 0); + if (!HeapTupleIsValid(typeTup)) + { + char *attname = NameStr(desc->attrs[i]->attname); + + elog(ERROR, "plpython: Cache lookup for attribute `%s' type `%u' failed", + attname, desc->attrs[i]->atttypid); + } - typeStruct = (Form_pg_type) GETSTRUCT(typeTup); + typeStruct = (Form_pg_type) GETSTRUCT(typeTup); - PLy_output_datum_func2(&(arg->out.r.atts[i]), typeStruct); + PLy_output_datum_func2(&(arg->out.r.atts[i]), typeStruct); - ReleaseSysCache(typeTup); - } + ReleaseSysCache(typeTup); + } } void -PLy_output_datum_func(PLyTypeInfo *arg, Form_pg_type typeStruct) +PLy_output_datum_func(PLyTypeInfo * arg, Form_pg_type typeStruct) { - enter(); + enter(); - if (arg->is_rel == 1) - elog(FATAL, "plpython: PLyTypeInfo struct is initialized for a Tuple"); - arg->is_rel = 0; - PLy_output_datum_func2(&(arg->out.d), typeStruct); + if (arg->is_rel == 1) + elog(FATAL, "plpython: PLyTypeInfo struct is initialized for a Tuple"); + arg->is_rel = 0; + PLy_output_datum_func2(&(arg->out.d), typeStruct); } void -PLy_output_datum_func2(PLyObToDatum *arg, Form_pg_type typeStruct) +PLy_output_datum_func2(PLyObToDatum * arg, Form_pg_type typeStruct) { - enter(); + enter(); - perm_fmgr_info(typeStruct->typinput, &arg->typfunc); - arg->typelem = typeStruct->typelem; - arg->typbyval = typeStruct->typbyval; + perm_fmgr_info(typeStruct->typinput, &arg->typfunc); + arg->typelem = typeStruct->typelem; + arg->typbyval = typeStruct->typbyval; } void -PLy_input_datum_func(PLyTypeInfo *arg, Form_pg_type typeStruct) +PLy_input_datum_func(PLyTypeInfo * arg, Form_pg_type typeStruct) { - enter(); + enter(); - if (arg->is_rel == 1) - elog(FATAL, "plpython: PLyTypeInfo struct is initialized for Tuple"); - arg->is_rel = 0; - PLy_input_datum_func2(&(arg->in.d), typeStruct); + if (arg->is_rel == 1) + elog(FATAL, "plpython: PLyTypeInfo struct is initialized for Tuple"); + arg->is_rel = 0; + PLy_input_datum_func2(&(arg->in.d), typeStruct); } void -PLy_input_datum_func2(PLyDatumToOb *arg, Form_pg_type typeStruct) -{ - char *type; - - perm_fmgr_info(typeStruct->typoutput, &arg->typfunc); - arg->typelem = typeStruct->typelem; - arg->typbyval = typeStruct->typbyval; - - /* hmmm, wierd. means this arg will always be converted - * to a python None - */ - if (!OidIsValid(typeStruct->typoutput)) - { - elog(ERROR, "plpython: (FIXME) typeStruct->typoutput is invalid"); - - arg->func = NULL; - return; - } - - type = NameStr(typeStruct->typname); - switch (type[0]) - { - case 'b': - { - if (strcasecmp("bool", type)) - { - arg->func = PLyBool_FromString; - return; - } - break; - } - case 'f': - { - if ((strncasecmp("float", type, 5) == 0) && - ((type[5] == '8') || (type[5] == '4'))) - { - arg->func = PLyFloat_FromString; - return; - } - break; - } - case 'i': - { - if ((strncasecmp("int", type, 3) == 0) && - ((type[3] == '4') || (type[3] == '2')) && - (type[4] == '\0')) - { - arg->func = PLyInt_FromString; - return; - } - else if ( strcasecmp("int8", type) == 0 ) - { - arg->func = PLyLong_FromString; - } - break; - } - case 'n': - { - if (strcasecmp("numeric", type) == 0) - { - arg->func = PLyFloat_FromString; - return; - } - break; - } - default: - break; - } - arg->func = PLyString_FromString; +PLy_input_datum_func2(PLyDatumToOb * arg, Form_pg_type typeStruct) +{ + char *type; + + perm_fmgr_info(typeStruct->typoutput, &arg->typfunc); + arg->typelem = typeStruct->typelem; + arg->typbyval = typeStruct->typbyval; + + /* + * hmmm, wierd. means this arg will always be converted to a python + * None + */ + if (!OidIsValid(typeStruct->typoutput)) + { + elog(ERROR, "plpython: (FIXME) typeStruct->typoutput is invalid"); + + arg->func = NULL; + return; + } + + type = NameStr(typeStruct->typname); + switch (type[0]) + { + case 'b': + { + if (strcasecmp("bool", type)) + { + arg->func = PLyBool_FromString; + return; + } + break; + } + case 'f': + { + if ((strncasecmp("float", type, 5) == 0) && + ((type[5] == '8') || (type[5] == '4'))) + { + arg->func = PLyFloat_FromString; + return; + } + break; + } + case 'i': + { + if ((strncasecmp("int", type, 3) == 0) && + ((type[3] == '4') || (type[3] == '2')) && + (type[4] == '\0')) + { + arg->func = PLyInt_FromString; + return; + } + else if (strcasecmp("int8", type) == 0) + arg->func = PLyLong_FromString; + break; + } + case 'n': + { + if (strcasecmp("numeric", type) == 0) + { + arg->func = PLyFloat_FromString; + return; + } + break; + } + default: + break; + } + arg->func = PLyString_FromString; } void -PLy_typeinfo_init(PLyTypeInfo *arg) +PLy_typeinfo_init(PLyTypeInfo * arg) { - arg->is_rel = -1; - arg->in.r.natts = arg->out.r.natts = 0; - arg->in.r.atts = NULL; - arg->out.r.atts = NULL; + arg->is_rel = -1; + arg->in.r.natts = arg->out.r.natts = 0; + arg->in.r.atts = NULL; + arg->out.r.atts = NULL; } void -PLy_typeinfo_dealloc(PLyTypeInfo *arg) +PLy_typeinfo_dealloc(PLyTypeInfo * arg) { - if (arg->is_rel == 1) - { - if (arg->in.r.atts) - PLy_free(arg->in.r.atts); - if (arg->out.r.atts) - PLy_free(arg->out.r.atts); - } + if (arg->is_rel == 1) + { + if (arg->in.r.atts) + PLy_free(arg->in.r.atts); + if (arg->out.r.atts) + PLy_free(arg->out.r.atts); + } } /* assumes that a bool is always returned as a 't' or 'f' @@ -1440,112 +1500,115 @@ PLy_typeinfo_dealloc(PLyTypeInfo *arg) PyObject * PLyBool_FromString(const char *src) { - enter(); + enter(); - if (src[0] == 't') - return PyInt_FromLong(1); - return PyInt_FromLong(0); + if (src[0] == 't') + return PyInt_FromLong(1); + return PyInt_FromLong(0); } PyObject * PLyFloat_FromString(const char *src) { - double v; - char *eptr; + double v; + char *eptr; + + enter(); - enter(); - - errno = 0; - v = strtod(src, &eptr); - if ((*eptr != '\0') || (errno)) - return NULL; - return PyFloat_FromDouble(v); + errno = 0; + v = strtod(src, &eptr); + if ((*eptr != '\0') || (errno)) + return NULL; + return PyFloat_FromDouble(v); } PyObject * PLyInt_FromString(const char *src) { - long v; - char *eptr; + long v; + char *eptr; - enter(); + enter(); - errno = 0; - v = strtol(src, &eptr, 0); - if ((*eptr != '\0') || (errno)) - return NULL; - return PyInt_FromLong(v); + errno = 0; + v = strtol(src, &eptr, 0); + if ((*eptr != '\0') || (errno)) + return NULL; + return PyInt_FromLong(v); } PyObject * PLyLong_FromString(const char *src) { - return PyLong_FromString((char *)src,NULL,0); + return PyLong_FromString((char *) src, NULL, 0); } PyObject * PLyString_FromString(const char *src) { - return PyString_FromString(src); + return PyString_FromString(src); } PyObject * -PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc) +PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc) { - DECLARE_EXC(); - PyObject *volatile dict; - int i; + DECLARE_EXC(); + PyObject *volatile dict; + int i; - enter(); + enter(); - if (info->is_rel != 1) - elog(FATAL, "plpython: PLyTypeInfo structure describes a datum."); + if (info->is_rel != 1) + elog(FATAL, "plpython: PLyTypeInfo structure describes a datum."); - dict = PyDict_New(); - if (dict == NULL) - PLy_elog(ERROR, "Unable to create tuple dictionary."); + dict = PyDict_New(); + if (dict == NULL) + PLy_elog(ERROR, "Unable to create tuple dictionary."); - SAVE_EXC(); - if (TRAP_EXC()) - { - RESTORE_EXC(); - Py_DECREF(dict); - - RERAISE_EXC(); - } - - for (i = 0; i < info->in.r.natts; i++) - { - char *key, *vsrc; - Datum vattr, vdat; - bool is_null; - PyObject *value; + SAVE_EXC(); + if (TRAP_EXC()) + { + RESTORE_EXC(); + Py_DECREF(dict); - key = NameStr(desc->attrs[i]->attname); - vattr = heap_getattr(tuple, (i + 1), desc, &is_null); + RERAISE_EXC(); + } - if ((is_null) || (info->in.r.atts[i].func == NULL)) - PyDict_SetItemString(dict, key, Py_None); - else + for (i = 0; i < info->in.r.natts; i++) { - vdat = FunctionCall3(&info->in.r.atts[i].typfunc, - vattr, - ObjectIdGetDatum(info->in.r.atts[i].typelem), - Int32GetDatum(desc->attrs[i]->atttypmod)); - vsrc = DatumGetCString(vdat); - - /* no exceptions allowed - */ - value = info->in.r.atts[i].func (vsrc); - pfree(vsrc); - PyDict_SetItemString(dict, key, value); - Py_DECREF(value); + char *key, + *vsrc; + Datum vattr, + vdat; + bool is_null; + PyObject *value; + + key = NameStr(desc->attrs[i]->attname); + vattr = heap_getattr(tuple, (i + 1), desc, &is_null); + + if ((is_null) || (info->in.r.atts[i].func == NULL)) + PyDict_SetItemString(dict, key, Py_None); + else + { + vdat = FunctionCall3(&info->in.r.atts[i].typfunc, + vattr, + ObjectIdGetDatum(info->in.r.atts[i].typelem), + Int32GetDatum(desc->attrs[i]->atttypmod)); + vsrc = DatumGetCString(vdat); + + /* + * no exceptions allowed + */ + value = info->in.r.atts[i].func(vsrc); + pfree(vsrc); + PyDict_SetItemString(dict, key, value); + Py_DECREF(value); + } } - } - - RESTORE_EXC(); - return dict; + RESTORE_EXC(); + + return dict; } /* initialization, some python variables function declared here @@ -1572,11 +1635,11 @@ static PyObject *PLy_result_getattr(PyObject *, char *); static PyObject *PLy_result_fetch(PyObject *, PyObject *); static PyObject *PLy_result_nrows(PyObject *, PyObject *); static PyObject *PLy_result_status(PyObject *, PyObject *); -static int PLy_result_length(PyObject *); +static int PLy_result_length(PyObject *); static PyObject *PLy_result_item(PyObject *, int); static PyObject *PLy_result_slice(PyObject *, int, int); -static int PLy_result_ass_item(PyObject *, int, PyObject *); -static int PLy_result_ass_slice(PyObject *, int, int, PyObject *); +static int PLy_result_ass_item(PyObject *, int, PyObject *); +static int PLy_result_ass_slice(PyObject *, int, int, PyObject *); static PyObject *PLy_spi_prepare(PyObject *, PyObject *); @@ -1588,100 +1651,107 @@ static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *, int, int); PyTypeObject PLy_PlanType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "PLyPlan", /*tp_name*/ - sizeof(PLyPlanObject), /*tp_size*/ - 0, /*tp_itemsize*/ - /* methods - */ - (destructor) PLy_plan_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)PLy_plan_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - 0, /*tp_xxx4*/ - PLy_plan_doc, /*tp_doc*/ + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "PLyPlan", /* tp_name */ + sizeof(PLyPlanObject), /* tp_size */ + 0, /* tp_itemsize */ + + /* + * methods + */ + (destructor) PLy_plan_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc) PLy_plan_getattr, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + 0, /* tp_xxx4 */ + PLy_plan_doc, /* tp_doc */ }; PyMethodDef PLy_plan_methods[] = { - { "status", (PyCFunction) PLy_plan_status, METH_VARARGS, NULL }, - { NULL, NULL, 0, NULL } + {"status", (PyCFunction) PLy_plan_status, METH_VARARGS, NULL}, + {NULL, NULL, 0, NULL} }; PySequenceMethods PLy_result_as_sequence = { - (inquiry) PLy_result_length, /* sq_length */ - (binaryfunc) 0, /* sq_concat */ - (intargfunc) 0, /* sq_repeat */ - (intargfunc) PLy_result_item, /* sq_item */ - (intintargfunc) PLy_result_slice, /* sq_slice */ - (intobjargproc) PLy_result_ass_item, /* sq_ass_item */ - (intintobjargproc) PLy_result_ass_slice, /* sq_ass_slice */ + (inquiry) PLy_result_length,/* sq_length */ + (binaryfunc) 0, /* sq_concat */ + (intargfunc) 0, /* sq_repeat */ + (intargfunc) PLy_result_item, /* sq_item */ + (intintargfunc) PLy_result_slice, /* sq_slice */ + (intobjargproc) PLy_result_ass_item, /* sq_ass_item */ + (intintobjargproc) PLy_result_ass_slice, /* sq_ass_slice */ }; PyTypeObject PLy_ResultType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "PLyResult", /*tp_name*/ - sizeof(PLyResultObject), /*tp_size*/ - 0, /*tp_itemsize*/ - /* methods - */ - (destructor) PLy_result_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc) PLy_result_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - &PLy_result_as_sequence, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - 0, /*tp_xxx4*/ - PLy_result_doc, /*tp_doc*/ + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "PLyResult", /* tp_name */ + sizeof(PLyResultObject), /* tp_size */ + 0, /* tp_itemsize */ + + /* + * methods + */ + (destructor) PLy_result_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc) PLy_result_getattr, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &PLy_result_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + 0, /* tp_xxx4 */ + PLy_result_doc, /* tp_doc */ }; PyMethodDef PLy_result_methods[] = { - { "fetch", (PyCFunction) PLy_result_fetch, METH_VARARGS, NULL,}, - { "nrows", (PyCFunction) PLy_result_nrows, METH_VARARGS, NULL }, - { "status", (PyCFunction) PLy_result_status, METH_VARARGS, NULL }, - { NULL, NULL, 0, NULL } + {"fetch", (PyCFunction) PLy_result_fetch, METH_VARARGS, NULL,}, + {"nrows", (PyCFunction) PLy_result_nrows, METH_VARARGS, NULL}, + {"status", (PyCFunction) PLy_result_status, METH_VARARGS, NULL}, + {NULL, NULL, 0, NULL} }; static PyMethodDef PLy_methods[] = { - /* logging methods - */ - { "debug", PLy_debug, METH_VARARGS, NULL }, - { "error", PLy_error, METH_VARARGS, NULL }, - { "fatal", PLy_fatal, METH_VARARGS, NULL }, - { "notice", PLy_notice, METH_VARARGS, NULL }, - - /* create a stored plan - */ - { "prepare", PLy_spi_prepare, METH_VARARGS, NULL }, - - /* execute a plan or query - */ - { "execute", PLy_spi_execute, METH_VARARGS, NULL }, - - { NULL, NULL, 0, NULL } + /* + * logging methods + */ + {"debug", PLy_debug, METH_VARARGS, NULL}, + {"error", PLy_error, METH_VARARGS, NULL}, + {"fatal", PLy_fatal, METH_VARARGS, NULL}, + {"notice", PLy_notice, METH_VARARGS, NULL}, + + /* + * create a stored plan + */ + {"prepare", PLy_spi_prepare, METH_VARARGS, NULL}, + + /* + * execute a plan or query + */ + {"execute", PLy_spi_execute, METH_VARARGS, NULL}, + + {NULL, NULL, 0, NULL} }; @@ -1690,72 +1760,72 @@ static PyMethodDef PLy_methods[] = { PyObject * PLy_plan_new(void) { - PLyPlanObject *ob; - - enter(); + PLyPlanObject *ob; - if ((ob = PyObject_NEW(PLyPlanObject, &PLy_PlanType)) == NULL) - return NULL; + enter(); - ob->plan = NULL; - ob->nargs = 0; - ob->types = NULL; - ob->args = NULL; + if ((ob = PyObject_NEW(PLyPlanObject, &PLy_PlanType)) == NULL) + return NULL; - return (PyObject *) ob; + ob->plan = NULL; + ob->nargs = 0; + ob->types = NULL; + ob->args = NULL; + + return (PyObject *) ob; } void -PLy_plan_dealloc(PyObject *arg) +PLy_plan_dealloc(PyObject * arg) { - PLyPlanObject *ob = (PLyPlanObject *) arg; + PLyPlanObject *ob = (PLyPlanObject *) arg; - enter(); + enter(); - if (ob->plan) - { - /* free the plan... - * pfree(ob->plan); - * - * FIXME -- leaks saved plan on object destruction. can - * this be avoided? - */ - } - if (ob->types) - PLy_free(ob->types); - if (ob->args) - { - int i; + if (ob->plan) + { + /* + * free the plan... pfree(ob->plan); + * + * FIXME -- leaks saved plan on object destruction. can this be + * avoided? + */ + } + if (ob->types) + PLy_free(ob->types); + if (ob->args) + { + int i; - for (i = 0; i < ob->nargs; i++) - PLy_typeinfo_dealloc(&ob->args[i]); - PLy_free(ob->args); - } + for (i = 0; i < ob->nargs; i++) + PLy_typeinfo_dealloc(&ob->args[i]); + PLy_free(ob->args); + } - PyMem_DEL(arg); + PyMem_DEL(arg); - leave(); + leave(); } PyObject * -PLy_plan_getattr(PyObject *self, char *name) +PLy_plan_getattr(PyObject * self, char *name) { - return Py_FindMethod(PLy_plan_methods, self, name); + return Py_FindMethod(PLy_plan_methods, self, name); } PyObject * -PLy_plan_status(PyObject *self, PyObject *args) +PLy_plan_status(PyObject * self, PyObject * args) { - if (PyArg_ParseTuple(args, "")) - { - Py_INCREF(Py_True); - return Py_True; - /* return PyInt_FromLong(self->status); */ - } - PyErr_SetString(PLy_exc_error, "plan.status() takes no arguments"); - return NULL; + if (PyArg_ParseTuple(args, "")) + { + Py_INCREF(Py_True); + return Py_True; + /* return PyInt_FromLong(self->status); */ + } + PyErr_SetString(PLy_exc_error, "plan.status() takes no arguments"); + return NULL; } @@ -1766,626 +1836,649 @@ PLy_plan_status(PyObject *self, PyObject *args) static PyObject * PLy_result_new(void) { - PLyResultObject *ob; - - enter(); + PLyResultObject *ob; + + enter(); - if ((ob = PyObject_NEW(PLyResultObject, &PLy_ResultType)) == NULL) - return NULL; + if ((ob = PyObject_NEW(PLyResultObject, &PLy_ResultType)) == NULL) + return NULL; - /* ob->tuples = NULL; */ + /* ob->tuples = NULL; */ - Py_INCREF(Py_None); - ob->status = Py_None; - ob->nrows = PyInt_FromLong(-1); - ob->rows = PyList_New(0); + Py_INCREF(Py_None); + ob->status = Py_None; + ob->nrows = PyInt_FromLong(-1); + ob->rows = PyList_New(0); - return (PyObject *) ob; + return (PyObject *) ob; } static void -PLy_result_dealloc(PyObject *arg) +PLy_result_dealloc(PyObject * arg) { - PLyResultObject *ob = (PLyResultObject *) arg; + PLyResultObject *ob = (PLyResultObject *) arg; - enter(); + enter(); - Py_XDECREF(ob->nrows); - Py_XDECREF(ob->rows); - Py_XDECREF(ob->status); + Py_XDECREF(ob->nrows); + Py_XDECREF(ob->rows); + Py_XDECREF(ob->status); - PyMem_DEL(ob); + PyMem_DEL(ob); } static PyObject * -PLy_result_getattr(PyObject *self, char *attr) +PLy_result_getattr(PyObject * self, char *attr) { - return NULL; + return NULL; } static PyObject * -PLy_result_fetch(PyObject *self, PyObject *args) +PLy_result_fetch(PyObject * self, PyObject * args) { - return NULL; + return NULL; } static PyObject * -PLy_result_nrows(PyObject *self, PyObject *args) +PLy_result_nrows(PyObject * self, PyObject * args) { - PLyResultObject *ob = (PLyResultObject *) self; - Py_INCREF(ob->nrows); - return ob->nrows; + PLyResultObject *ob = (PLyResultObject *) self; + + Py_INCREF(ob->nrows); + return ob->nrows; } static PyObject * -PLy_result_status(PyObject *self, PyObject *args) +PLy_result_status(PyObject * self, PyObject * args) { - PLyResultObject *ob = (PLyResultObject *) self; - Py_INCREF(ob->status); - return ob->status; + PLyResultObject *ob = (PLyResultObject *) self; + + Py_INCREF(ob->status); + return ob->status; } int -PLy_result_length(PyObject *arg) +PLy_result_length(PyObject * arg) { - PLyResultObject *ob = (PLyResultObject *) arg; - return PyList_Size(ob->rows); + PLyResultObject *ob = (PLyResultObject *) arg; + + return PyList_Size(ob->rows); } PyObject * -PLy_result_item(PyObject *arg, int idx) +PLy_result_item(PyObject * arg, int idx) { - PyObject *rv; - PLyResultObject *ob = (PLyResultObject *) arg; + PyObject *rv; + PLyResultObject *ob = (PLyResultObject *) arg; - rv = PyList_GetItem(ob->rows, idx); - if (rv != NULL) - Py_INCREF(rv); - return rv; + rv = PyList_GetItem(ob->rows, idx); + if (rv != NULL) + Py_INCREF(rv); + return rv; } int -PLy_result_ass_item(PyObject *arg, int idx, PyObject *item) +PLy_result_ass_item(PyObject * arg, int idx, PyObject * item) { - int rv; - PLyResultObject *ob = (PLyResultObject *) arg; + int rv; + PLyResultObject *ob = (PLyResultObject *) arg; - Py_INCREF(item); - rv = PyList_SetItem(ob->rows, idx, item); - return rv; + Py_INCREF(item); + rv = PyList_SetItem(ob->rows, idx, item); + return rv; } PyObject * -PLy_result_slice(PyObject *arg, int lidx, int hidx) +PLy_result_slice(PyObject * arg, int lidx, int hidx) { - PyObject *rv; - PLyResultObject *ob = (PLyResultObject *) arg; - - rv = PyList_GetSlice(ob->rows, lidx, hidx); - if (rv == NULL) - return NULL; - Py_INCREF(rv); - return rv; + PyObject *rv; + PLyResultObject *ob = (PLyResultObject *) arg; + + rv = PyList_GetSlice(ob->rows, lidx, hidx); + if (rv == NULL) + return NULL; + Py_INCREF(rv); + return rv; } int -PLy_result_ass_slice(PyObject *arg, int lidx, int hidx, PyObject *slice) +PLy_result_ass_slice(PyObject * arg, int lidx, int hidx, PyObject * slice) { - int rv; - PLyResultObject *ob = (PLyResultObject *) arg; - - rv = PyList_SetSlice(ob->rows, lidx, hidx, slice); - return rv; + int rv; + PLyResultObject *ob = (PLyResultObject *) arg; + + rv = PyList_SetSlice(ob->rows, lidx, hidx, slice); + return rv; } /* SPI interface */ PyObject * -PLy_spi_prepare(PyObject *self, PyObject *args) -{ - DECLARE_EXC(); - PLyPlanObject *plan; - PyObject *list = NULL; - PyObject * volatile optr = NULL; - char *query; - - enter(); - - if (!PyArg_ParseTuple(args, "s|O", &query, &list)) - { - PyErr_SetString(PLy_exc_spi_error, - "Invalid arguments for plpy.prepare()"); - return NULL; - } - - if ((list) && (!PySequence_Check(list))) - { - PyErr_SetString(PLy_exc_spi_error, - "Second argument in plpy.prepare() must be a sequence"); - return NULL; - } - - - if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL) - return NULL; - - SAVE_EXC(); - if (TRAP_EXC()) - { - RESTORE_EXC(); - Py_DECREF(plan); - Py_XDECREF(optr); - if (!PyErr_Occurred ()) - PyErr_SetString(PLy_exc_spi_error, - "Unknown error in PLy_spi_prepare."); - return NULL; - } - - if (list != NULL) - { - int nargs, i; - - - nargs = PySequence_Length(list); - if (nargs > 0) - { - plan->nargs = nargs; - plan->types = PLy_malloc(sizeof(Oid) * nargs); - plan->values = PLy_malloc(sizeof(Datum) * nargs); - plan->args = PLy_malloc(sizeof(PLyTypeInfo) * nargs); - - /* the other loop might throw an exception, if PLyTypeInfo - * member isn't properly initialized the Py_DECREF(plan) - * will go boom - */ - for (i = 0; i < nargs; i++) - { - PLy_typeinfo_init(&plan->args[i]); - plan->values[i] = (Datum) NULL; - } - - for (i = 0; i < nargs; i++) - { - char *sptr; - HeapTuple typeTup; - Form_pg_type typeStruct; - - optr = PySequence_GetItem(list, i); - if (!PyString_Check(optr)) - { - PyErr_SetString(PLy_exc_spi_error, - "Type names must be strings."); - RAISE_EXC(1); - } - sptr = PyString_AsString(optr); - typeTup = SearchSysCache(TYPENAME, PointerGetDatum(sptr), - 0, 0, 0); - if (!HeapTupleIsValid(typeTup)) - { - PLy_exception_set(PLy_exc_spi_error, - "Cache lookup for type `%s' failed.", - sptr); - RAISE_EXC(1); - } +PLy_spi_prepare(PyObject * self, PyObject * args) +{ + DECLARE_EXC(); + PLyPlanObject *plan; + PyObject *list = NULL; + PyObject *volatile optr = NULL; + char *query; - Py_DECREF(optr); - optr = NULL; /* this is important */ + enter(); - plan->types[i] = typeTup->t_data->t_oid; - typeStruct = (Form_pg_type) GETSTRUCT(typeTup); - if (typeStruct->typrelid == InvalidOid) - PLy_output_datum_func(&plan->args[i], typeStruct); - else + if (!PyArg_ParseTuple(args, "s|O", &query, &list)) + { + PyErr_SetString(PLy_exc_spi_error, + "Invalid arguments for plpy.prepare()"); + return NULL; + } + + if ((list) && (!PySequence_Check(list))) + { + PyErr_SetString(PLy_exc_spi_error, + "Second argument in plpy.prepare() must be a sequence"); + return NULL; + } + + + if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL) + return NULL; + + SAVE_EXC(); + if (TRAP_EXC()) + { + RESTORE_EXC(); + Py_DECREF(plan); + Py_XDECREF(optr); + if (!PyErr_Occurred()) + PyErr_SetString(PLy_exc_spi_error, + "Unknown error in PLy_spi_prepare."); + return NULL; + } + + if (list != NULL) + { + int nargs, + i; + + + nargs = PySequence_Length(list); + if (nargs > 0) { - PyErr_SetString(PLy_exc_spi_error, - "tuples not handled in plpy.prepare, yet."); - RAISE_EXC(1); + plan->nargs = nargs; + plan->types = PLy_malloc(sizeof(Oid) * nargs); + plan->values = PLy_malloc(sizeof(Datum) * nargs); + plan->args = PLy_malloc(sizeof(PLyTypeInfo) * nargs); + + /* + * the other loop might throw an exception, if PLyTypeInfo + * member isn't properly initialized the Py_DECREF(plan) will + * go boom + */ + for (i = 0; i < nargs; i++) + { + PLy_typeinfo_init(&plan->args[i]); + plan->values[i] = (Datum) NULL; + } + + for (i = 0; i < nargs; i++) + { + char *sptr; + HeapTuple typeTup; + Form_pg_type typeStruct; + + optr = PySequence_GetItem(list, i); + if (!PyString_Check(optr)) + { + PyErr_SetString(PLy_exc_spi_error, + "Type names must be strings."); + RAISE_EXC(1); + } + sptr = PyString_AsString(optr); + typeTup = SearchSysCache(TYPENAME, PointerGetDatum(sptr), + 0, 0, 0); + if (!HeapTupleIsValid(typeTup)) + { + PLy_exception_set(PLy_exc_spi_error, + "Cache lookup for type `%s' failed.", + sptr); + RAISE_EXC(1); + } + + Py_DECREF(optr); + optr = NULL; /* this is important */ + + plan->types[i] = typeTup->t_data->t_oid; + typeStruct = (Form_pg_type) GETSTRUCT(typeTup); + if (typeStruct->typrelid == InvalidOid) + PLy_output_datum_func(&plan->args[i], typeStruct); + else + { + PyErr_SetString(PLy_exc_spi_error, + "tuples not handled in plpy.prepare, yet."); + RAISE_EXC(1); + } + ReleaseSysCache(typeTup); + } } - ReleaseSysCache(typeTup); - } } - } - plan->plan = SPI_prepare(query, plan->nargs, plan->types); - if (plan->plan == NULL) - { - PLy_exception_set(PLy_exc_spi_error, - "Unable to prepare plan. SPI_prepare failed -- %s.", - PLy_spi_error_string(SPI_result)); - RAISE_EXC(1); - } + plan->plan = SPI_prepare(query, plan->nargs, plan->types); + if (plan->plan == NULL) + { + PLy_exception_set(PLy_exc_spi_error, + "Unable to prepare plan. SPI_prepare failed -- %s.", + PLy_spi_error_string(SPI_result)); + RAISE_EXC(1); + } - plan->plan = SPI_saveplan(plan->plan); - if (plan->plan == NULL) - { - PLy_exception_set(PLy_exc_spi_error, - "Unable to save plan. SPI_saveplan failed -- %s.", - PLy_spi_error_string(SPI_result)); - RAISE_EXC(1); - } + plan->plan = SPI_saveplan(plan->plan); + if (plan->plan == NULL) + { + PLy_exception_set(PLy_exc_spi_error, + "Unable to save plan. SPI_saveplan failed -- %s.", + PLy_spi_error_string(SPI_result)); + RAISE_EXC(1); + } - RESTORE_EXC(); + RESTORE_EXC(); - return (PyObject *) plan; + return (PyObject *) plan; } /* execute(query="select * from foo", limit=5) * execute(plan=plan, values=(foo, bar), limit=5) */ PyObject * -PLy_spi_execute(PyObject *self, PyObject *args) +PLy_spi_execute(PyObject * self, PyObject * args) { - char *query; - PyObject *plan; - PyObject *list = NULL; - int limit = 0; + char *query; + PyObject *plan; + PyObject *list = NULL; + int limit = 0; - enter(); + enter(); #if 0 - /* there should - hahaha - be an python exception set so just - * return NULL. FIXME -- is this needed? - */ - if (PLy_restart_in_progress) - return NULL; + + /* + * there should - hahaha - be an python exception set so just return + * NULL. FIXME -- is this needed? + */ + if (PLy_restart_in_progress) + return NULL; #endif - if (PyArg_ParseTuple(args, "s|i", &query, &limit)) - return PLy_spi_execute_query(query, limit); + if (PyArg_ParseTuple(args, "s|i", &query, &limit)) + return PLy_spi_execute_query(query, limit); - PyErr_Clear(); + PyErr_Clear(); + + if ((PyArg_ParseTuple(args, "O|Oi", &plan, &list, &limit)) && + (is_PLyPlanObject(plan))) + { + PyObject *rv = PLy_spi_execute_plan(plan, list, limit); - if ((PyArg_ParseTuple(args, "O|Oi", &plan, &list, &limit)) && - (is_PLyPlanObject(plan))) - { - PyObject *rv = PLy_spi_execute_plan(plan, list, limit); - return rv; - } + return rv; + } - PyErr_SetString(PLy_exc_error, "Expected a query or plan."); - return NULL; + PyErr_SetString(PLy_exc_error, "Expected a query or plan."); + return NULL; } PyObject * -PLy_spi_execute_plan(PyObject *ob, PyObject *list, int limit) +PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit) { - DECLARE_EXC(); - volatile int nargs; - int i, rv; - PLyPlanObject *plan; + DECLARE_EXC(); + volatile int nargs; + int i, + rv; + PLyPlanObject *plan; - enter(); + enter(); - if (list != NULL) - { - if ((!PySequence_Check(list)) || (PyString_Check(list))) + if (list != NULL) { - char *msg = "plpy.execute() takes a sequence as its second argument"; - PyErr_SetString(PLy_exc_spi_error, msg); - return NULL; - } - nargs = PySequence_Length(list); - } - else - nargs = 0; + if ((!PySequence_Check(list)) || (PyString_Check(list))) + { + char *msg = "plpy.execute() takes a sequence as its second argument"; - plan = (PLyPlanObject *) ob; + PyErr_SetString(PLy_exc_spi_error, msg); + return NULL; + } + nargs = PySequence_Length(list); + } + else + nargs = 0; - if (nargs != plan->nargs) - { - char *sv; + plan = (PLyPlanObject *) ob; - PyObject *so = PyObject_Str(list); - sv = PyString_AsString(so); - PLy_exception_set(PLy_exc_spi_error, - "Expected sequence of %d arguments, got %d. %s", - plan->nargs, nargs, sv); - Py_DECREF(so); + if (nargs != plan->nargs) + { + char *sv; - return NULL; - } + PyObject *so = PyObject_Str(list); - SAVE_EXC(); - if (TRAP_EXC()) - { - RESTORE_EXC(); + sv = PyString_AsString(so); + PLy_exception_set(PLy_exc_spi_error, + "Expected sequence of %d arguments, got %d. %s", + plan->nargs, nargs, sv); + Py_DECREF(so); - /* cleanup plan->values array - */ - for (i = 0; i < nargs; i++) - { - if (!plan->args[i].out.d.typbyval && - (plan->values[i] != (Datum) NULL)) - { - pfree(DatumGetPointer(plan->values[i])); - plan->values[i] = (Datum) NULL; - } + return NULL; } - if (!PyErr_Occurred()) - PyErr_SetString(PLy_exc_error, - "Unknown error in PLy_spi_execute_plan"); - return NULL; - } - - if (nargs) - { - for (i = 0; i < nargs; i++) + SAVE_EXC(); + if (TRAP_EXC()) { - PyObject *elem, *so; - char *sv; + RESTORE_EXC(); - elem = PySequence_GetItem(list, i); - so = PyObject_Str(elem); - sv = PyString_AsString(so); + /* + * cleanup plan->values array + */ + for (i = 0; i < nargs; i++) + { + if (!plan->args[i].out.d.typbyval && + (plan->values[i] != (Datum) NULL)) + { + pfree(DatumGetPointer(plan->values[i])); + plan->values[i] = (Datum) NULL; + } + } - /* FIXME -- if this can elog, we have leak - */ - plan->values[i] = FunctionCall3(&(plan->args[i].out.d.typfunc), - CStringGetDatum(sv), - ObjectIdGetDatum(plan->args[i].out.d.typelem), - Int32GetDatum(-1)); + if (!PyErr_Occurred()) + PyErr_SetString(PLy_exc_error, + "Unknown error in PLy_spi_execute_plan"); + return NULL; + } - Py_DECREF(so); - Py_DECREF(elem); + if (nargs) + { + for (i = 0; i < nargs; i++) + { + PyObject *elem, + *so; + char *sv; + + elem = PySequence_GetItem(list, i); + so = PyObject_Str(elem); + sv = PyString_AsString(so); + + /* + * FIXME -- if this can elog, we have leak + */ + plan->values[i] = FunctionCall3(&(plan->args[i].out.d.typfunc), + CStringGetDatum(sv), + ObjectIdGetDatum(plan->args[i].out.d.typelem), + Int32GetDatum(-1)); + + Py_DECREF(so); + Py_DECREF(elem); + } } - } - rv = SPI_execp(plan->plan, plan->values, NULL, limit); - RESTORE_EXC(); + rv = SPI_execp(plan->plan, plan->values, NULL, limit); + RESTORE_EXC(); - for (i = 0; i < nargs; i++) - { - if (!plan->args[i].out.d.typbyval && - (plan->values[i] != (Datum) NULL)) + for (i = 0; i < nargs; i++) { - pfree(DatumGetPointer(plan->values[i])); - plan->values[i] = (Datum) NULL; + if (!plan->args[i].out.d.typbyval && + (plan->values[i] != (Datum) NULL)) + { + pfree(DatumGetPointer(plan->values[i])); + plan->values[i] = (Datum) NULL; + } } - } - if (rv < 0) - { - PLy_exception_set(PLy_exc_spi_error, - "Unable to execute plan. SPI_execp failed -- %s", - PLy_spi_error_string(rv)); - return NULL; - } + if (rv < 0) + { + PLy_exception_set(PLy_exc_spi_error, + "Unable to execute plan. SPI_execp failed -- %s", + PLy_spi_error_string(rv)); + return NULL; + } - return PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv); + return PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv); } PyObject * PLy_spi_execute_query(char *query, int limit) { - DECLARE_EXC(); - int rv; + DECLARE_EXC(); + int rv; + + SAVE_EXC(); + if (TRAP_EXC()) + { + RESTORE_EXC(); - SAVE_EXC(); - if (TRAP_EXC()) - { - RESTORE_EXC(); + if ((!PLy_restart_in_progress) && (!PyErr_Occurred())) + PyErr_SetString(PLy_exc_spi_error, + "Unknown error in PLy_spi_execute_query."); + return NULL; + } - if ((!PLy_restart_in_progress) && (!PyErr_Occurred())) - PyErr_SetString(PLy_exc_spi_error, - "Unknown error in PLy_spi_execute_query."); - return NULL; - } - - rv = SPI_exec(query, limit); - RESTORE_EXC(); + rv = SPI_exec(query, limit); + RESTORE_EXC(); - if (rv < 0) - { - PLy_exception_set(PLy_exc_spi_error, - "Unable to execute query. SPI_exec failed -- %s", - PLy_spi_error_string(rv)); - return NULL; - } + if (rv < 0) + { + PLy_exception_set(PLy_exc_spi_error, + "Unable to execute query. SPI_exec failed -- %s", + PLy_spi_error_string(rv)); + return NULL; + } - return PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv); + return PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv); } PyObject * PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status) { - PLyResultObject *result; - - enter(); - - result = (PLyResultObject *) PLy_result_new(); - Py_DECREF(result->status); - result->status = PyInt_FromLong(status); + PLyResultObject *result; - if (status == SPI_OK_UTILITY) - { - Py_DECREF(result->nrows); - result->nrows = PyInt_FromLong(0); - } - else if (status != SPI_OK_SELECT) - { - Py_DECREF(result->nrows); - result->nrows = PyInt_FromLong(rows); - } - else - { - DECLARE_EXC(); - PLyTypeInfo args; - int i; + enter(); - PLy_typeinfo_init(&args); - Py_DECREF(result->nrows); - result->nrows = PyInt_FromLong(rows); + result = (PLyResultObject *) PLy_result_new(); + Py_DECREF(result->status); + result->status = PyInt_FromLong(status); - SAVE_EXC(); - if (TRAP_EXC()) + if (status == SPI_OK_UTILITY) { - RESTORE_EXC(); - - if (!PyErr_Occurred()) - PyErr_SetString(PLy_exc_error, - "Unknown error in PLy_spi_execute_fetch_result"); - Py_DECREF(result); - PLy_typeinfo_dealloc(&args); - return NULL; + Py_DECREF(result->nrows); + result->nrows = PyInt_FromLong(0); + } + else if (status != SPI_OK_SELECT) + { + Py_DECREF(result->nrows); + result->nrows = PyInt_FromLong(rows); } - - if (rows) + else { - Py_DECREF(result->rows); - result->rows = PyList_New(rows); + DECLARE_EXC(); + PLyTypeInfo args; + int i; + + PLy_typeinfo_init(&args); + Py_DECREF(result->nrows); + result->nrows = PyInt_FromLong(rows); + + SAVE_EXC(); + if (TRAP_EXC()) + { + RESTORE_EXC(); + + if (!PyErr_Occurred()) + PyErr_SetString(PLy_exc_error, + "Unknown error in PLy_spi_execute_fetch_result"); + Py_DECREF(result); + PLy_typeinfo_dealloc(&args); + return NULL; + } - PLy_input_tuple_funcs(&args, tuptable->tupdesc); - for (i = 0; i < rows; i++) - { - PyObject *row = PLyDict_FromTuple(&args, tuptable->vals[i], - tuptable->tupdesc); - PyList_SetItem(result->rows, i, row); - } - PLy_typeinfo_dealloc(&args); + if (rows) + { + Py_DECREF(result->rows); + result->rows = PyList_New(rows); + + PLy_input_tuple_funcs(&args, tuptable->tupdesc); + for (i = 0; i < rows; i++) + { + PyObject *row = PLyDict_FromTuple(&args, tuptable->vals[i], + tuptable->tupdesc); + + PyList_SetItem(result->rows, i, row); + } + PLy_typeinfo_dealloc(&args); + } + RESTORE_EXC(); } - RESTORE_EXC(); - } - return (PyObject *) result; + return (PyObject *) result; } const char * PLy_spi_error_string(int code) { - switch (code) - { - case SPI_ERROR_TYPUNKNOWN: - return "SPI_ERROR_TYPUNKNOWN"; - case SPI_ERROR_NOOUTFUNC: - return "SPI_ERROR_NOOUTFUNC"; - case SPI_ERROR_NOATTRIBUTE: - return "SPI_ERROR_NOATTRIBUTE"; - case SPI_ERROR_TRANSACTION: - return "SPI_ERROR_TRANSACTION"; - case SPI_ERROR_PARAM: - return "SPI_ERROR_PARAM"; - case SPI_ERROR_ARGUMENT: - return "SPI_ERROR_ARGUMENT"; - case SPI_ERROR_CURSOR: - return "SPI_ERROR_CURSOR"; - case SPI_ERROR_UNCONNECTED: - return "SPI_ERROR_UNCONNECTED"; - case SPI_ERROR_OPUNKNOWN: - return "SPI_ERROR_OPUNKNOWN"; - case SPI_ERROR_COPY: - return "SPI_ERROR_COPY"; - case SPI_ERROR_CONNECT: - return "SPI_ERROR_CONNECT"; - } - return "Unknown or Invalid code"; + switch (code) + { + case SPI_ERROR_TYPUNKNOWN: + return "SPI_ERROR_TYPUNKNOWN"; + case SPI_ERROR_NOOUTFUNC: + return "SPI_ERROR_NOOUTFUNC"; + case SPI_ERROR_NOATTRIBUTE: + return "SPI_ERROR_NOATTRIBUTE"; + case SPI_ERROR_TRANSACTION: + return "SPI_ERROR_TRANSACTION"; + case SPI_ERROR_PARAM: + return "SPI_ERROR_PARAM"; + case SPI_ERROR_ARGUMENT: + return "SPI_ERROR_ARGUMENT"; + case SPI_ERROR_CURSOR: + return "SPI_ERROR_CURSOR"; + case SPI_ERROR_UNCONNECTED: + return "SPI_ERROR_UNCONNECTED"; + case SPI_ERROR_OPUNKNOWN: + return "SPI_ERROR_OPUNKNOWN"; + case SPI_ERROR_COPY: + return "SPI_ERROR_COPY"; + case SPI_ERROR_CONNECT: + return "SPI_ERROR_CONNECT"; + } + return "Unknown or Invalid code"; } /* language handler and interpreter initialization */ -void PLy_init_all(void) +void +PLy_init_all(void) { - static volatile int init_active = 0; + static volatile int init_active = 0; - enter(); + enter(); - if (init_active) - elog(FATAL, "plpython: Initialization of language module failed."); - init_active = 1; + if (init_active) + elog(FATAL, "plpython: Initialization of language module failed."); + init_active = 1; - Py_Initialize(); - PLy_init_interp(); - PLy_init_plpy(); - PLy_init_safe_interp(); - if (PyErr_Occurred()) - PLy_elog(FATAL, "Untrapped error in initialization."); - PLy_procedure_cache = PyDict_New(); - if (PLy_procedure_cache == NULL) - PLy_elog(ERROR, "Unable to create procedure cache."); + Py_Initialize(); + PLy_init_interp(); + PLy_init_plpy(); + PLy_init_safe_interp(); + if (PyErr_Occurred()) + PLy_elog(FATAL, "Untrapped error in initialization."); + PLy_procedure_cache = PyDict_New(); + if (PLy_procedure_cache == NULL) + PLy_elog(ERROR, "Unable to create procedure cache."); - PLy_first_call = 0; + PLy_first_call = 0; - leave(); + leave(); } void PLy_init_interp(void) { - PyObject *mainmod; + PyObject *mainmod; - enter(); + enter(); - mainmod = PyImport_AddModule("__main__"); - if ((mainmod == NULL) || (PyErr_Occurred())) - PLy_elog(ERROR, "Unable to import '__main__' module."); - Py_INCREF(mainmod); - PLy_interp_globals = PyModule_GetDict(mainmod); - Py_DECREF(mainmod); - if ((PLy_interp_globals == NULL) || (PyErr_Occurred())) - PLy_elog(ERROR, "Unable to initialize globals."); + mainmod = PyImport_AddModule("__main__"); + if ((mainmod == NULL) || (PyErr_Occurred())) + PLy_elog(ERROR, "Unable to import '__main__' module."); + Py_INCREF(mainmod); + PLy_interp_globals = PyModule_GetDict(mainmod); + Py_DECREF(mainmod); + if ((PLy_interp_globals == NULL) || (PyErr_Occurred())) + PLy_elog(ERROR, "Unable to initialize globals."); } void PLy_init_plpy(void) { - PyObject *main_mod, *main_dict, *plpy_mod; - PyObject *plpy, *plpy_dict; - - enter(); - - /* initialize plpy module - */ - PLy_PlanType.ob_type = PLy_ResultType.ob_type = &PyType_Type; - plpy = Py_InitModule("plpy", PLy_methods); - plpy_dict = PyModule_GetDict(plpy); - - /* PyDict_SetItemString(plpy, "PlanType", (PyObject *) &PLy_PlanType); */ - - PLy_exc_error = PyErr_NewException("plpy.Error", NULL, NULL); - PLy_exc_fatal = PyErr_NewException("plpy.Fatal", NULL, NULL); - PLy_exc_spi_error = PyErr_NewException("plpy.SPIError", NULL, NULL); - PyDict_SetItemString(plpy_dict, "Error", PLy_exc_error); - PyDict_SetItemString(plpy_dict, "Fatal", PLy_exc_fatal); - PyDict_SetItemString(plpy_dict, "SPIError", PLy_exc_spi_error); - - /* initialize main module, and add plpy - */ - main_mod = PyImport_AddModule("__main__"); - main_dict = PyModule_GetDict(main_mod); - plpy_mod = PyImport_AddModule("plpy"); - PyDict_SetItemString(main_dict, "plpy", plpy_mod); - if (PyErr_Occurred ()) - elog(ERROR, "Unable to init plpy."); + PyObject *main_mod, + *main_dict, + *plpy_mod; + PyObject *plpy, + *plpy_dict; + + enter(); + + /* + * initialize plpy module + */ + PLy_PlanType.ob_type = PLy_ResultType.ob_type = &PyType_Type; + plpy = Py_InitModule("plpy", PLy_methods); + plpy_dict = PyModule_GetDict(plpy); + + /* PyDict_SetItemString(plpy, "PlanType", (PyObject *) &PLy_PlanType); */ + + PLy_exc_error = PyErr_NewException("plpy.Error", NULL, NULL); + PLy_exc_fatal = PyErr_NewException("plpy.Fatal", NULL, NULL); + PLy_exc_spi_error = PyErr_NewException("plpy.SPIError", NULL, NULL); + PyDict_SetItemString(plpy_dict, "Error", PLy_exc_error); + PyDict_SetItemString(plpy_dict, "Fatal", PLy_exc_fatal); + PyDict_SetItemString(plpy_dict, "SPIError", PLy_exc_spi_error); + + /* + * initialize main module, and add plpy + */ + main_mod = PyImport_AddModule("__main__"); + main_dict = PyModule_GetDict(main_mod); + plpy_mod = PyImport_AddModule("plpy"); + PyDict_SetItemString(main_dict, "plpy", plpy_mod); + if (PyErr_Occurred()) + elog(ERROR, "Unable to init plpy."); } void PLy_init_safe_interp(void) { - PyObject *rmod; - char *rname = "rexec"; - int i, imax; + PyObject *rmod; + char *rname = "rexec"; + int i, + imax; - enter(); + enter(); - rmod = PyImport_ImportModuleEx(rname, PLy_interp_globals, - PLy_interp_globals, Py_None); - if ((rmod == NULL) || (PyErr_Occurred ())) - PLy_elog(ERROR, "Unable to import %s.", rname); - PyDict_SetItemString(PLy_interp_globals, rname, rmod); - PLy_interp_safe = rmod; + rmod = PyImport_ImportModuleEx(rname, PLy_interp_globals, + PLy_interp_globals, Py_None); + if ((rmod == NULL) || (PyErr_Occurred())) + PLy_elog(ERROR, "Unable to import %s.", rname); + PyDict_SetItemString(PLy_interp_globals, rname, rmod); + PLy_interp_safe = rmod; - imax = sizeof(PLy_importable_modules_list) / sizeof(char *); - PLy_importable_modules = PyTuple_New(imax); - for (i = 0; i < imax; i++) - { - PyObject *m = PyString_FromString(PLy_importable_modules_list[i]); - PyTuple_SetItem(PLy_importable_modules, i, m); - } + imax = sizeof(PLy_importable_modules_list) / sizeof(char *); + PLy_importable_modules = PyTuple_New(imax); + for (i = 0; i < imax; i++) + { + PyObject *m = PyString_FromString(PLy_importable_modules_list[i]); + + PyTuple_SetItem(PLy_importable_modules, i, m); + } - PLy_interp_safe_globals = PyDict_New(); - if (PLy_interp_safe_globals == NULL) - PLy_elog(ERROR, "Unable to create shared global dictionary."); + PLy_interp_safe_globals = PyDict_New(); + if (PLy_interp_safe_globals == NULL) + PLy_elog(ERROR, "Unable to create shared global dictionary."); } @@ -2396,95 +2489,98 @@ PLy_init_safe_interp(void) static PyObject *PLy_log(int, PyObject *, PyObject *); PyObject * -PLy_debug(PyObject *self, PyObject *args) +PLy_debug(PyObject * self, PyObject * args) { - return PLy_log(DEBUG, self, args); + return PLy_log(DEBUG, self, args); } PyObject * -PLy_error(PyObject *self, PyObject *args) +PLy_error(PyObject * self, PyObject * args) { - return PLy_log(ERROR, self, args); + return PLy_log(ERROR, self, args); } PyObject * -PLy_fatal(PyObject *self, PyObject *args) +PLy_fatal(PyObject * self, PyObject * args) { - return PLy_log(FATAL, self, args); + return PLy_log(FATAL, self, args); } PyObject * -PLy_notice(PyObject *self, PyObject *args) +PLy_notice(PyObject * self, PyObject * args) { - return PLy_log(NOTICE, self, args); + return PLy_log(NOTICE, self, args); } PyObject * -PLy_log(volatile int level, PyObject *self, PyObject *args) -{ - DECLARE_EXC(); - PyObject *so; - char * volatile sv; - - enter(); - - if (args == NULL) - elog(NOTICE, "plpython, args is NULL in %s", __FUNCTION__); - - so = PyObject_Str(args); - if ((so == NULL) || ((sv = PyString_AsString(so)) == NULL)) - { - level = ERROR; - sv = "Unable to parse error message in `plpy.elog'"; - } - - /* returning NULL here causes the python interpreter to bail. - * when control passes back into plpython_*_handler, we - * check for python exceptions and do the actual elog - * call. actually PLy_elog. - */ - if (level == ERROR) - { - PyErr_SetString(PLy_exc_error, sv); - return NULL; - } - else if (level >= FATAL) - { - PyErr_SetString(PLy_exc_fatal, sv); - return NULL; - } - - /* ok, this is a NOTICE, or DEBUG message - * - * but just in case DON'T long jump out of the interpreter! - */ - SAVE_EXC(); - if (TRAP_EXC()) - { - RESTORE_EXC(); - - Py_XDECREF(so); - - /* the real error message should already be written into - * the postgresql log, no? whatever, this shouldn't happen - * so die hideously. - */ - elog(FATAL, "plpython: Aiieee, elog threw an unknown exception!"); - return NULL; - } - - elog(level, sv); - - RESTORE_EXC(); - - Py_XDECREF(so); - Py_INCREF(Py_None); - - /* return a legal object so the interpreter will continue on its - * merry way - */ - return Py_None; +PLy_log(volatile int level, PyObject * self, PyObject * args) +{ + DECLARE_EXC(); + PyObject *so; + char *volatile sv; + + enter(); + + if (args == NULL) + elog(NOTICE, "plpython, args is NULL in %s", __FUNCTION__); + + so = PyObject_Str(args); + if ((so == NULL) || ((sv = PyString_AsString(so)) == NULL)) + { + level = ERROR; + sv = "Unable to parse error message in `plpy.elog'"; + } + + /* + * returning NULL here causes the python interpreter to bail. when + * control passes back into plpython_*_handler, we check for python + * exceptions and do the actual elog call. actually PLy_elog. + */ + if (level == ERROR) + { + PyErr_SetString(PLy_exc_error, sv); + return NULL; + } + else if (level >= FATAL) + { + PyErr_SetString(PLy_exc_fatal, sv); + return NULL; + } + + /* + * ok, this is a NOTICE, or DEBUG message + * + * but just in case DON'T long jump out of the interpreter! + */ + SAVE_EXC(); + if (TRAP_EXC()) + { + RESTORE_EXC(); + + Py_XDECREF(so); + + /* + * the real error message should already be written into the + * postgresql log, no? whatever, this shouldn't happen so die + * hideously. + */ + elog(FATAL, "plpython: Aiieee, elog threw an unknown exception!"); + return NULL; + } + + elog(level, sv); + + RESTORE_EXC(); + + Py_XDECREF(so); + Py_INCREF(Py_None); + + /* + * return a legal object so the interpreter will continue on its merry + * way + */ + return Py_None; } @@ -2494,152 +2590,164 @@ PLy_log(volatile int level, PyObject *self, PyObject *args) static char *PLy_traceback(int *); static char *PLy_vprintf(const char *fmt, va_list ap); -static char *PLy_printf(const char *fmt, ...); +static char *PLy_printf(const char *fmt,...); void -PLy_exception_set(PyObject *exc, const char *fmt, ...) +PLy_exception_set(PyObject * exc, const char *fmt,...) { - char buf[1024]; - va_list ap; + char buf[1024]; + va_list ap; - va_start(ap, fmt); - vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); - PyErr_SetString(exc, buf); + PyErr_SetString(exc, buf); } void PLy_elog(int elevel, const char *fmt,...) { - DECLARE_EXC(); - va_list ap; - char *xmsg, *emsg; - int xlevel; + DECLARE_EXC(); + va_list ap; + char *xmsg, + *emsg; + int xlevel; - enter(); + enter(); - xmsg = PLy_traceback(&xlevel); + xmsg = PLy_traceback(&xlevel); - va_start(ap, fmt); - emsg = PLy_vprintf(fmt, ap); - va_end(ap); + va_start(ap, fmt); + emsg = PLy_vprintf(fmt, ap); + va_end(ap); - SAVE_EXC(); - if (TRAP_EXC()) - { - RESTORE_EXC(); - mark(); - /* elog called siglongjmp. cleanup, restore and reraise - */ - PLy_restart_in_progress += 1; - PLy_free(emsg); - PLy_free(xmsg); - RERAISE_EXC(); - } + SAVE_EXC(); + if (TRAP_EXC()) + { + RESTORE_EXC(); + mark(); + + /* + * elog called siglongjmp. cleanup, restore and reraise + */ + PLy_restart_in_progress += 1; + PLy_free(emsg); + PLy_free(xmsg); + RERAISE_EXC(); + } - if (xmsg) - { - elog(elevel, "plpython: %s\n%s", emsg, xmsg); - PLy_free(xmsg); - } - else - elog(elevel, "plpython: %s", emsg); - PLy_free(emsg); + if (xmsg) + { + elog(elevel, "plpython: %s\n%s", emsg, xmsg); + PLy_free(xmsg); + } + else + elog(elevel, "plpython: %s", emsg); + PLy_free(emsg); - leave(); + leave(); - RESTORE_EXC(); + RESTORE_EXC(); } char * PLy_traceback(int *xlevel) { - PyObject *e, *v, *tb; - PyObject *eob, *vob = NULL; - char *vstr, *estr, *xstr = NULL; - - enter(); - - /* get the current exception - */ - PyErr_Fetch(&e, &v, &tb); - - /* oops, no exception, return - */ - if (e == NULL) - { - *xlevel = NOTICE; - return NULL; - } + PyObject *e, + *v, + *tb; + PyObject *eob, + *vob = NULL; + char *vstr, + *estr, + *xstr = NULL; + + enter(); + + /* + * get the current exception + */ + PyErr_Fetch(&e, &v, &tb); + + /* + * oops, no exception, return + */ + if (e == NULL) + { + *xlevel = NOTICE; + return NULL; + } - PyErr_NormalizeException(&e, &v, &tb); + PyErr_NormalizeException(&e, &v, &tb); - eob = PyObject_Str(e); - if ((v) && ((vob = PyObject_Str(v)) != NULL)) - vstr = PyString_AsString(vob); - else - vstr = "Unknown"; + eob = PyObject_Str(e); + if ((v) && ((vob = PyObject_Str(v)) != NULL)) + vstr = PyString_AsString(vob); + else + vstr = "Unknown"; - estr = PyString_AsString(eob); - xstr = PLy_printf("%s: %s", estr, vstr); + estr = PyString_AsString(eob); + xstr = PLy_printf("%s: %s", estr, vstr); - Py_DECREF(eob); - Py_XDECREF(vob); + Py_DECREF(eob); + Py_XDECREF(vob); - /* intuit an appropriate error level for based on the exception type - */ - if ((PLy_exc_error) && (PyErr_GivenExceptionMatches(e, PLy_exc_error))) - *xlevel = ERROR; - else if ((PLy_exc_fatal) && (PyErr_GivenExceptionMatches(e, PLy_exc_fatal))) - *xlevel = FATAL; - else - *xlevel = ERROR; + /* + * intuit an appropriate error level for based on the exception type + */ + if ((PLy_exc_error) && (PyErr_GivenExceptionMatches(e, PLy_exc_error))) + *xlevel = ERROR; + else if ((PLy_exc_fatal) && (PyErr_GivenExceptionMatches(e, PLy_exc_fatal))) + *xlevel = FATAL; + else + *xlevel = ERROR; - leave(); + leave(); - return xstr; + return xstr; } char * -PLy_printf(const char *fmt, ...) +PLy_printf(const char *fmt,...) { - va_list ap; - char *emsg; + va_list ap; + char *emsg; - va_start(ap, fmt); - emsg = PLy_vprintf(fmt, ap); - va_end(ap); - return emsg; + va_start(ap, fmt); + emsg = PLy_vprintf(fmt, ap); + va_end(ap); + return emsg; } char * PLy_vprintf(const char *fmt, va_list ap) { - size_t blen; - int bchar, tries = 2; - char *buf; - - blen = strlen(fmt) * 2; - if (blen < 256) - blen = 256; - buf = PLy_malloc(blen * sizeof(char)); - - while (1) - { - bchar = vsnprintf(buf, blen, fmt, ap); - if ((bchar > 0) && (bchar < blen)) - return buf; - if (tries-- <= 0) - break; - if (blen > 0) - blen = bchar + 1; - else - blen *= 2; - buf = PLy_realloc(buf, blen); - } - PLy_free(buf); - return NULL; + size_t blen; + int bchar, + tries = 2; + char *buf; + + blen = strlen(fmt) * 2; + if (blen < 256) + blen = 256; + buf = PLy_malloc(blen * sizeof(char)); + + while (1) + { + bchar = vsnprintf(buf, blen, fmt, ap); + if ((bchar > 0) && (bchar < blen)) + return buf; + if (tries-- <= 0) + break; + if (blen > 0) + blen = bchar + 1; + else + blen *= 2; + buf = PLy_realloc(buf, blen); + } + PLy_free(buf); + return NULL; } /* python module code @@ -2652,19 +2760,21 @@ PLy_vprintf(const char *fmt, va_list ap) void * PLy_malloc(size_t bytes) { - void *ptr = malloc(bytes); - if (ptr == NULL) - elog(FATAL, "plpython: Memory exhausted."); - return ptr; + void *ptr = malloc(bytes); + + if (ptr == NULL) + elog(FATAL, "plpython: Memory exhausted."); + return ptr; } void * PLy_realloc(void *optr, size_t bytes) { - void *nptr = realloc(optr, bytes); - if (nptr == NULL) - elog(FATAL, "plpython: Memory exhausted."); - return nptr; + void *nptr = realloc(optr, bytes); + + if (nptr == NULL) + elog(FATAL, "plpython: Memory exhausted."); + return nptr; } /* define this away @@ -2672,5 +2782,5 @@ PLy_realloc(void *optr, size_t bytes) void PLy_free(void *ptr) { - free(ptr); + free(ptr); } |