static PyObject * PLy_cursor_iternext(PyObject *self) { PLyCursorObject *cursor; PyObject *ret; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; Portal portal; cursor = (PLyCursorObject *) self; if (cursor->closed) { PLy_exception_set(PyExc_ValueError, "iterating a closed cursor"); return NULL; } portal = GetPortalByName(cursor->portalname); if (!PortalIsValid(portal)) { PLy_exception_set(PyExc_ValueError, "iterating a cursor in an aborted subtransaction"); return NULL; } oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { SPI_cursor_fetch(portal, true, 1); if (SPI_processed == 0) { PyErr_SetNone(PyExc_StopIteration); ret = NULL; } else { if (cursor->result.is_rowtype != 1) PLy_input_tuple_funcs(&cursor->result, SPI_tuptable->tupdesc); ret = PLyDict_FromTuple(&cursor->result, SPI_tuptable->vals[0], SPI_tuptable->tupdesc); } SPI_freetuptable(SPI_tuptable); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); return ret; }
/* * subxact.__exit__(exc_type, exc, tb) or subxact.exit(exc_type, exc, tb) * * Exit an explicit subtransaction. exc_type is an exception type, exc * is the exception object, tb is the traceback. If exc_type is None, * commit the subtransactiony, if not abort it. * * The method signature is chosen to allow subtransaction objects to * be used as context managers as described in * <http://www.python.org/dev/peps/pep-0343/>. */ static PyObject * PLy_subtransaction_exit(PyObject *self, PyObject *args) { PyObject *type; PyObject *value; PyObject *traceback; PLySubtransactionData *subxactdata; PLySubtransactionObject *subxact = (PLySubtransactionObject *) self; if (!PyArg_ParseTuple(args, "OOO", &type, &value, &traceback)) return NULL; if (!subxact->started) { PLy_exception_set(PyExc_ValueError, "this subtransaction has not been entered"); return NULL; } if (subxact->exited) { PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited"); return NULL; } if (explicit_subtransactions == NIL) { PLy_exception_set(PyExc_ValueError, "there is no subtransaction to exit from"); return NULL; } subxact->exited = true; if (type != Py_None) { /* Abort the inner transaction */ RollbackAndReleaseCurrentSubTransaction(); } else { ReleaseCurrentSubTransaction(); } subxactdata = (PLySubtransactionData *) linitial(explicit_subtransactions); explicit_subtransactions = list_delete_first(explicit_subtransactions); MemoryContextSwitchTo(subxactdata->oldcontext); CurrentResourceOwner = subxactdata->oldowner; pfree(subxactdata); /* * AtEOSubXact_SPI() should not have popped any SPI context, but just in * case it did, make sure we remain connected. */ SPI_restore_connection(); Py_INCREF(Py_None); return Py_None; }
/* * subxact.__enter__() or subxact.enter() * * Start an explicit subtransaction. SPI calls within an explicit * subtransaction will not start another one, so you can atomically * execute many SPI calls and still get a controllable exception if * one of them fails. */ static PyObject * PLy_subtransaction_enter(PyObject *self, PyObject *unused) { PLySubtransactionData *subxactdata; MemoryContext oldcontext; PLySubtransactionObject *subxact = (PLySubtransactionObject *) self; if (subxact->started) { PLy_exception_set(PyExc_ValueError, "this subtransaction has already been entered"); return NULL; } if (subxact->exited) { PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited"); return NULL; } subxact->started = true; oldcontext = CurrentMemoryContext; subxactdata = (PLySubtransactionData *) MemoryContextAlloc(TopTransactionContext, sizeof(PLySubtransactionData)); subxactdata->oldcontext = oldcontext; subxactdata->oldowner = CurrentResourceOwner; BeginInternalSubTransaction(NULL); /* Be sure that cells of explicit_subtransactions list are long-lived */ MemoryContextSwitchTo(TopTransactionContext); explicit_subtransactions = lcons(subxactdata, explicit_subtransactions); /* Caller wants to stay in original memory context */ MemoryContextSwitchTo(oldcontext); Py_INCREF(self); return self; }
PyObject * PLy_cursor(PyObject *self, PyObject *args) { char *query; PyObject *plan; PyObject *planargs = NULL; if (PyArg_ParseTuple(args, "s", &query)) return PLy_cursor_query(query); PyErr_Clear(); if (PyArg_ParseTuple(args, "O|O", &plan, &planargs)) return PLy_cursor_plan(plan, planargs); PLy_exception_set(PLy_exc_error, "plpy.cursor expected a query or a plan"); return NULL; }
static PyObject * PLy_result_coltypmods(PyObject *self, PyObject *unused) { PLyResultObject *ob = (PLyResultObject *) self; PyObject *list; int i; if (!ob->tupdesc) { PLy_exception_set(PLy_exc_error, "command did not produce a result set"); return NULL; } list = PyList_New(ob->tupdesc->natts); for (i = 0; i < ob->tupdesc->natts; i++) PyList_SET_ITEM(list, i, PyInt_FromLong(ob->tupdesc->attrs[i]->atttypmod)); return list; }
/* execute(query="select * from foo", limit=5) * execute(plan=plan, values=(foo, bar), limit=5) */ PyObject * PLy_spi_execute(PyObject *self, PyObject *args) { char *query; PyObject *plan; PyObject *list = NULL; long limit = 0; if (PyArg_ParseTuple(args, "s|l", &query, &limit)) return PLy_spi_execute_query(query, limit); PyErr_Clear(); if (PyArg_ParseTuple(args, "O|Ol", &plan, &list, &limit) && is_PLyPlanObject(plan)) return PLy_spi_execute_plan(plan, list, limit); PLy_exception_set(PLy_exc_error, "plpy.execute expected a query or a plan"); return NULL; }
static PyObject * PLy_spi_execute_query(char *query, long limit) { int rv; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; PyObject *ret = NULL; oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { PLyExecutionContext *exec_ctx = PLy_current_execution_context(); pg_verifymbstr(query, strlen(query), false); rv = SPI_execute(query, exec_ctx->curr_proc->fn_readonly, limit); ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); if (rv < 0) { Py_XDECREF(ret); PLy_exception_set(PLy_exc_spi_error, "SPI_execute failed: %s", SPI_result_code_string(rv)); return NULL; } return ret; }
static PyObject * PLy_cursor_close(PyObject *self, PyObject *unused) { PLyCursorObject *cursor = (PLyCursorObject *) self; if (!cursor->closed) { Portal portal = GetPortalByName(cursor->portalname); if (!PortalIsValid(portal)) { PLy_exception_set(PyExc_ValueError, "closing a cursor in an aborted subtransaction"); return NULL; } SPI_cursor_close(portal); cursor->closed = true; } Py_INCREF(Py_None); return Py_None; }
static PyObject * PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status) { PLyResultObject *result; volatile MemoryContext oldcontext; result = (PLyResultObject *) PLy_result_new(); Py_DECREF(result->status); result->status = PyInt_FromLong(status); if (status > 0 && tuptable == NULL) { Py_DECREF(result->nrows); result->nrows = PyInt_FromLong(rows); } else if (status > 0 && tuptable != NULL) { PLyTypeInfo args; int i; Py_DECREF(result->nrows); result->nrows = PyInt_FromLong(rows); PLy_typeinfo_init(&args); oldcontext = CurrentMemoryContext; PG_TRY(); { 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); } } } PG_CATCH(); { MemoryContextSwitchTo(oldcontext); if (!PyErr_Occurred()) PLy_exception_set(PLy_exc_error, "unrecognized error in PLy_spi_execute_fetch_result"); PLy_typeinfo_dealloc(&args); SPI_freetuptable(tuptable); Py_DECREF(result); return NULL; } PG_END_TRY(); PLy_typeinfo_dealloc(&args); SPI_freetuptable(tuptable); } return (PyObject *) result; }
/* prepare(query="select * from foo") * prepare(query="select * from foo where bar = $1", params=["text"]) * prepare(query="select * from foo where bar = $1", params=["text"], limit=5) */ PyObject * PLy_spi_prepare(PyObject *self, PyObject *args) { PLyPlanObject *plan; PyObject *list = NULL; PyObject *volatile optr = NULL; char *query; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; volatile int nargs; if (!PyArg_ParseTuple(args, "s|O", &query, &list)) return NULL; if (list && (!PySequence_Check(list))) { PLy_exception_set(PyExc_TypeError, "second argument of plpy.prepare must be a sequence"); return NULL; } if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL) return NULL; nargs = list ? PySequence_Length(list) : 0; plan->nargs = nargs; plan->types = nargs ? PLy_malloc(sizeof(Oid) * nargs) : NULL; plan->values = nargs ? PLy_malloc(sizeof(Datum) * nargs) : NULL; plan->args = nargs ? PLy_malloc(sizeof(PLyTypeInfo) * nargs) : NULL; oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { int i; /* * 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] = PointerGetDatum(NULL); } for (i = 0; i < nargs; i++) { char *sptr; HeapTuple typeTup; Oid typeId; int32 typmod; Form_pg_type typeStruct; optr = PySequence_GetItem(list, i); if (PyString_Check(optr)) sptr = PyString_AsString(optr); else if (PyUnicode_Check(optr)) sptr = PLyUnicode_AsString(optr); else { ereport(ERROR, (errmsg("plpy.prepare: type name at ordinal position %d is not a string", i))); sptr = NULL; /* keep compiler quiet */ } /******************************************************** * Resolve argument type names and then look them up by * oid in the system cache, and remember the required *information for input conversion. ********************************************************/ parseTypeString(sptr, &typeId, &typmod); typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeId)); if (!HeapTupleIsValid(typeTup)) elog(ERROR, "cache lookup failed for type %u", typeId); Py_DECREF(optr); /* * set optr to NULL, so we won't try to unref it again in case of * an error */ optr = NULL; plan->types[i] = typeId; typeStruct = (Form_pg_type) GETSTRUCT(typeTup); if (typeStruct->typtype != TYPTYPE_COMPOSITE) PLy_output_datum_func(&plan->args[i], typeTup); else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("plpy.prepare does not support composite types"))); ReleaseSysCache(typeTup); } pg_verifymbstr(query, strlen(query), false); plan->plan = SPI_prepare(query, plan->nargs, plan->types); if (plan->plan == NULL) elog(ERROR, "SPI_prepare failed: %s", SPI_result_code_string(SPI_result)); /* transfer plan from procCxt to topCxt */ if (SPI_keepplan(plan->plan)) elog(ERROR, "SPI_keepplan failed"); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { Py_DECREF(plan); Py_XDECREF(optr); PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); Assert(plan->plan != NULL); return (PyObject *) plan; }
static PyObject * PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit) { volatile int nargs; int i, rv; PLyPlanObject *plan; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; PyObject *ret; if (list != NULL) { if (!PySequence_Check(list) || PyString_Check(list) || PyUnicode_Check(list)) { PLy_exception_set(PyExc_TypeError, "plpy.execute takes a sequence as its second argument"); return NULL; } nargs = PySequence_Length(list); } else nargs = 0; plan = (PLyPlanObject *) ob; if (nargs != plan->nargs) { char *sv; PyObject *so = PyObject_Str(list); if (!so) PLy_elog(ERROR, "could not execute plan"); sv = PyString_AsString(so); PLy_exception_set_plural(PyExc_TypeError, "Expected sequence of %d argument, got %d: %s", "Expected sequence of %d arguments, got %d: %s", plan->nargs, plan->nargs, nargs, sv); Py_DECREF(so); return NULL; } oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { char *volatile nulls; volatile int j; if (nargs > 0) nulls = palloc(nargs * sizeof(char)); else nulls = NULL; for (j = 0; j < nargs; j++) { PyObject *elem; elem = PySequence_GetItem(list, j); if (elem != Py_None) { PG_TRY(); { plan->values[j] = plan->args[j].out.d.func(&(plan->args[j].out.d), -1, elem); } PG_CATCH(); { Py_DECREF(elem); PG_RE_THROW(); } PG_END_TRY(); Py_DECREF(elem); nulls[j] = ' '; } else { Py_DECREF(elem); plan->values[j] = InputFunctionCall(&(plan->args[j].out.d.typfunc), NULL, plan->args[j].out.d.typioparam, -1); nulls[j] = 'n'; } } rv = SPI_execute_plan(plan->plan, plan->values, nulls, PLy_curr_procedure->fn_readonly, limit); ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv); if (nargs > 0) pfree(nulls); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { int k; /* * cleanup plan->values array */ for (k = 0; k < nargs; k++) { if (!plan->args[k].out.d.typbyval && (plan->values[k] != PointerGetDatum(NULL))) { pfree(DatumGetPointer(plan->values[k])); plan->values[k] = PointerGetDatum(NULL); } } PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); for (i = 0; i < nargs; i++) { if (!plan->args[i].out.d.typbyval && (plan->values[i] != PointerGetDatum(NULL))) { pfree(DatumGetPointer(plan->values[i])); plan->values[i] = PointerGetDatum(NULL); } } if (rv < 0) { PLy_exception_set(PLy_exc_spi_error, "SPI_execute_plan failed: %s", SPI_result_code_string(rv)); return NULL; } return ret; }
static PyObject * PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status) { PLyResultObject *result; volatile MemoryContext oldcontext; result = (PLyResultObject *) PLy_result_new(); Py_DECREF(result->status); result->status = PyInt_FromLong(status); if (status > 0 && tuptable == NULL) { Py_DECREF(result->nrows); result->nrows = PyInt_FromLong(rows); } else if (status > 0 && tuptable != NULL) { PLyTypeInfo args; int i; Py_DECREF(result->nrows); result->nrows = PyInt_FromLong(rows); PLy_typeinfo_init(&args); oldcontext = CurrentMemoryContext; PG_TRY(); { MemoryContext oldcontext2; /* * Save tuple descriptor for later use by result set metadata * functions. Save it in TopMemoryContext so that it survives * outside of an SPI context. We trust that PLy_result_dealloc() * will clean it up when the time is right. */ oldcontext2 = MemoryContextSwitchTo(TopMemoryContext); result->tupdesc = CreateTupleDescCopy(tuptable->tupdesc); MemoryContextSwitchTo(oldcontext2); 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); } } } PG_CATCH(); { MemoryContextSwitchTo(oldcontext); if (!PyErr_Occurred()) PLy_exception_set(PLy_exc_error, "unrecognized error in PLy_spi_execute_fetch_result"); PLy_typeinfo_dealloc(&args); SPI_freetuptable(tuptable); Py_DECREF(result); return NULL; } PG_END_TRY(); PLy_typeinfo_dealloc(&args); SPI_freetuptable(tuptable); } return (PyObject *) result; }
static PyObject * PLy_output(volatile int level, PyObject *self, PyObject *args, PyObject *kw) { int sqlstate = 0; char *volatile sqlstatestr = NULL; char *volatile message = NULL; char *volatile detail = NULL; char *volatile hint = NULL; char *volatile column_name = NULL; char *volatile constraint_name = NULL; char *volatile datatype_name = NULL; char *volatile table_name = NULL; char *volatile schema_name = NULL; volatile MemoryContext oldcontext; PyObject *key, *value; PyObject *volatile so; Py_ssize_t pos = 0; if (PyTuple_Size(args) == 1) { /* * Treat single argument specially to avoid undesirable ('tuple',) * decoration. */ PyObject *o; if (!PyArg_UnpackTuple(args, "plpy.elog", 1, 1, &o)) PLy_elog(ERROR, "could not unpack arguments in plpy.elog"); so = PyObject_Str(o); } else so = PyObject_Str(args); if (so == NULL || ((message = PyString_AsString(so)) == NULL)) { level = ERROR; message = dgettext(TEXTDOMAIN, "could not parse error message in plpy.elog"); } message = pstrdup(message); Py_XDECREF(so); if (kw != NULL) { while (PyDict_Next(kw, &pos, &key, &value)) { char *keyword = PyString_AsString(key); if (strcmp(keyword, "message") == 0) { /* the message should not be overwriten */ if (PyTuple_Size(args) != 0) { PLy_exception_set(PyExc_TypeError, "Argument 'message' given by name and position"); return NULL; } if (message) pfree(message); message = object_to_string(value); } else if (strcmp(keyword, "detail") == 0) detail = object_to_string(value); else if (strcmp(keyword, "hint") == 0) hint = object_to_string(value); else if (strcmp(keyword, "sqlstate") == 0) sqlstatestr = object_to_string(value); else if (strcmp(keyword, "schema_name") == 0) schema_name = object_to_string(value); else if (strcmp(keyword, "table_name") == 0) table_name = object_to_string(value); else if (strcmp(keyword, "column_name") == 0) column_name = object_to_string(value); else if (strcmp(keyword, "datatype_name") == 0) datatype_name = object_to_string(value); else if (strcmp(keyword, "constraint_name") == 0) constraint_name = object_to_string(value); else { PLy_exception_set(PyExc_TypeError, "'%s' is an invalid keyword argument for this function", keyword); return NULL; } } } if (sqlstatestr != NULL) { if (strlen(sqlstatestr) != 5) { PLy_exception_set(PyExc_ValueError, "invalid SQLSTATE code"); return NULL; } if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5) { PLy_exception_set(PyExc_ValueError, "invalid SQLSTATE code"); return NULL; } sqlstate = MAKE_SQLSTATE(sqlstatestr[0], sqlstatestr[1], sqlstatestr[2], sqlstatestr[3], sqlstatestr[4]); } oldcontext = CurrentMemoryContext; PG_TRY(); { if (message != NULL) pg_verifymbstr(message, strlen(message), false); if (detail != NULL) pg_verifymbstr(detail, strlen(detail), false); if (hint != NULL) pg_verifymbstr(hint, strlen(hint), false); if (schema_name != NULL) pg_verifymbstr(schema_name, strlen(schema_name), false); if (table_name != NULL) pg_verifymbstr(table_name, strlen(table_name), false); if (column_name != NULL) pg_verifymbstr(column_name, strlen(column_name), false); if (datatype_name != NULL) pg_verifymbstr(datatype_name, strlen(datatype_name), false); if (constraint_name != NULL) pg_verifymbstr(constraint_name, strlen(constraint_name), false); ereport(level, ((sqlstate != 0) ? errcode(sqlstate) : 0, (message != NULL) ? errmsg_internal("%s", message) : 0, (detail != NULL) ? errdetail_internal("%s", detail) : 0, (hint != NULL) ? errhint("%s", hint) : 0, (column_name != NULL) ? err_generic_string(PG_DIAG_COLUMN_NAME, column_name) : 0, (constraint_name != NULL) ? err_generic_string(PG_DIAG_CONSTRAINT_NAME, constraint_name) : 0, (datatype_name != NULL) ? err_generic_string(PG_DIAG_DATATYPE_NAME, datatype_name) : 0, (table_name != NULL) ? err_generic_string(PG_DIAG_TABLE_NAME, table_name) : 0, (schema_name != NULL) ? err_generic_string(PG_DIAG_SCHEMA_NAME, schema_name) : 0)); } PG_CATCH(); { ErrorData *edata; MemoryContextSwitchTo(oldcontext); edata = CopyErrorData(); FlushErrorState(); PLy_exception_set_with_details(PLy_exc_error, edata); FreeErrorData(edata); return NULL; } PG_END_TRY(); /* * return a legal object so the interpreter will continue on its merry way */ Py_INCREF(Py_None); return Py_None; }
static PyObject * PLy_cursor_fetch(PyObject *self, PyObject *args) { PLyCursorObject *cursor; int count; PLyResultObject *ret; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; Portal portal; if (!PyArg_ParseTuple(args, "i", &count)) return NULL; cursor = (PLyCursorObject *) self; if (cursor->closed) { PLy_exception_set(PyExc_ValueError, "fetch from a closed cursor"); return NULL; } portal = GetPortalByName(cursor->portalname); if (!PortalIsValid(portal)) { PLy_exception_set(PyExc_ValueError, "iterating a cursor in an aborted subtransaction"); return NULL; } ret = (PLyResultObject *) PLy_result_new(); if (ret == NULL) return NULL; oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { SPI_cursor_fetch(portal, true, count); if (cursor->result.is_rowtype != 1) PLy_input_tuple_funcs(&cursor->result, SPI_tuptable->tupdesc); Py_DECREF(ret->status); ret->status = PyInt_FromLong(SPI_OK_FETCH); Py_DECREF(ret->nrows); ret->nrows = PyInt_FromLong(SPI_processed); if (SPI_processed != 0) { int i; Py_DECREF(ret->rows); ret->rows = PyList_New(SPI_processed); for (i = 0; i < SPI_processed; i++) { PyObject *row = PLyDict_FromTuple(&cursor->result, SPI_tuptable->vals[i], SPI_tuptable->tupdesc); PyList_SetItem(ret->rows, i, row); } } SPI_freetuptable(SPI_tuptable); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); return (PyObject *) ret; }
static PyObject * PLy_cursor_plan(PyObject *ob, PyObject *args) { PLyCursorObject *cursor; volatile int nargs; int i; PLyPlanObject *plan; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; if (args) { if (!PySequence_Check(args) || PyString_Check(args) || PyUnicode_Check(args)) { PLy_exception_set(PyExc_TypeError, "plpy.cursor takes a sequence as its second argument"); return NULL; } nargs = PySequence_Length(args); } else nargs = 0; plan = (PLyPlanObject *) ob; if (nargs != plan->nargs) { char *sv; PyObject *so = PyObject_Str(args); if (!so) PLy_elog(ERROR, "could not execute plan"); sv = PyString_AsString(so); PLy_exception_set_plural(PyExc_TypeError, "Expected sequence of %d argument, got %d: %s", "Expected sequence of %d arguments, got %d: %s", plan->nargs, plan->nargs, nargs, sv); Py_DECREF(so); return NULL; } if ((cursor = PyObject_New(PLyCursorObject, &PLy_CursorType)) == NULL) return NULL; cursor->portalname = NULL; cursor->closed = false; cursor->mcxt = AllocSetContextCreate(TopMemoryContext, "PL/Python cursor context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); PLy_typeinfo_init(&cursor->result, cursor->mcxt); oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { PLyExecutionContext *exec_ctx = PLy_current_execution_context(); Portal portal; char *volatile nulls; volatile int j; if (nargs > 0) nulls = palloc(nargs * sizeof(char)); else nulls = NULL; for (j = 0; j < nargs; j++) { PyObject *elem; elem = PySequence_GetItem(args, j); if (elem != Py_None) { PG_TRY(); { plan->values[j] = plan->args[j].out.d.func(&(plan->args[j].out.d), -1, elem); } PG_CATCH(); { Py_DECREF(elem); PG_RE_THROW(); } PG_END_TRY(); Py_DECREF(elem); nulls[j] = ' '; } else { Py_DECREF(elem); plan->values[j] = InputFunctionCall(&(plan->args[j].out.d.typfunc), NULL, plan->args[j].out.d.typioparam, -1); nulls[j] = 'n'; } } portal = SPI_cursor_open(NULL, plan->plan, plan->values, nulls, exec_ctx->curr_proc->fn_readonly); if (portal == NULL) elog(ERROR, "SPI_cursor_open() failed: %s", SPI_result_code_string(SPI_result)); cursor->portalname = MemoryContextStrdup(cursor->mcxt, portal->name); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { int k; /* cleanup plan->values array */ for (k = 0; k < nargs; k++) { if (!plan->args[k].out.d.typbyval && (plan->values[k] != PointerGetDatum(NULL))) { pfree(DatumGetPointer(plan->values[k])); plan->values[k] = PointerGetDatum(NULL); } } Py_DECREF(cursor); PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); for (i = 0; i < nargs; i++) { if (!plan->args[i].out.d.typbyval && (plan->values[i] != PointerGetDatum(NULL))) { pfree(DatumGetPointer(plan->values[i])); plan->values[i] = PointerGetDatum(NULL); } } Assert(cursor->portalname != NULL); return (PyObject *) cursor; }