From a031a128140f3f4c61e6a5468c1a603f547a986b Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Mon, 2 Dec 2024 08:53:42 +0100 Subject: [PATCH 2/2] Use Python "Limited API" in PL/Python This allows building PL/Python against any Python 3.x version and using another Python 3.x version at run time. Implementation details: - Convert static types to heap types (https://docs.python.org/3/howto/isolating-extensions.html#heap-types). - Replace PyRun_String() with component functions. - Replace PyList_SET_ITEM() with PyList_SetItem(). --- src/pl/plpython/plpy_cursorobject.c | 71 +++++++++++++------- src/pl/plpython/plpy_planobject.c | 61 +++++++++++------ src/pl/plpython/plpy_procedure.c | 5 +- src/pl/plpython/plpy_resultobject.c | 98 +++++++++++++++++----------- src/pl/plpython/plpy_subxactobject.c | 41 +++++++----- src/pl/plpython/plpy_typeio.c | 6 +- src/pl/plpython/plpython.h | 2 + 7 files changed, 179 insertions(+), 105 deletions(-) diff --git a/src/pl/plpython/plpy_cursorobject.c b/src/pl/plpython/plpy_cursorobject.c index 6108384c9a5..39bcae3f1d9 100644 --- a/src/pl/plpython/plpy_cursorobject.c +++ b/src/pl/plpython/plpy_cursorobject.c @@ -20,7 +20,7 @@ #include "utils/memutils.h" static PyObject *PLy_cursor_query(const char *query); -static void PLy_cursor_dealloc(PyObject *arg); +static void PLy_cursor_dealloc(PLyCursorObject *self); static PyObject *PLy_cursor_iternext(PyObject *self); static PyObject *PLy_cursor_fetch(PyObject *self, PyObject *args); static PyObject *PLy_cursor_close(PyObject *self, PyObject *unused); @@ -33,22 +33,43 @@ static PyMethodDef PLy_cursor_methods[] = { {NULL, NULL, 0, NULL} }; -static PyTypeObject PLy_CursorType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "PLyCursor", - .tp_basicsize = sizeof(PLyCursorObject), - .tp_dealloc = PLy_cursor_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = PLy_cursor_doc, - .tp_iter = PyObject_SelfIter, - .tp_iternext = PLy_cursor_iternext, - .tp_methods = PLy_cursor_methods, +static PyType_Slot PLyCursor_slots[] = +{ + { + Py_tp_dealloc, PLy_cursor_dealloc + }, + { + Py_tp_doc, (char *) PLy_cursor_doc + }, + { + Py_tp_iter, PyObject_SelfIter + }, + { + Py_tp_iternext, PLy_cursor_iternext + }, + { + Py_tp_methods, PLy_cursor_methods + }, + { + 0, NULL + } }; +static PyType_Spec PLyCursor_spec = +{ + .name = "PLyCursor", + .basicsize = sizeof(PLyCursorObject), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .slots = PLyCursor_slots, +}; + +static PyTypeObject *PLy_CursorType; + void PLy_cursor_init_type(void) { - if (PyType_Ready(&PLy_CursorType) < 0) + PLy_CursorType = (PyTypeObject *) PyType_FromSpec(&PLyCursor_spec); + if (!PLy_CursorType) elog(ERROR, "could not initialize PLy_CursorType"); } @@ -80,7 +101,7 @@ PLy_cursor_query(const char *query) volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; - if ((cursor = PyObject_New(PLyCursorObject, &PLy_CursorType)) == NULL) + if ((cursor = PyObject_New(PLyCursorObject, PLy_CursorType)) == NULL) return NULL; cursor->portalname = NULL; cursor->closed = false; @@ -178,7 +199,7 @@ PLy_cursor_plan(PyObject *ob, PyObject *args) return NULL; } - if ((cursor = PyObject_New(PLyCursorObject, &PLy_CursorType)) == NULL) + if ((cursor = PyObject_New(PLyCursorObject, PLy_CursorType)) == NULL) return NULL; cursor->portalname = NULL; cursor->closed = false; @@ -276,30 +297,30 @@ PLy_cursor_plan(PyObject *ob, PyObject *args) } static void -PLy_cursor_dealloc(PyObject *arg) +PLy_cursor_dealloc(PLyCursorObject *self) { - PLyCursorObject *cursor; + PyTypeObject *tp = Py_TYPE(self); Portal portal; - cursor = (PLyCursorObject *) arg; - - if (!cursor->closed) + if (!self->closed) { - portal = GetPortalByName(cursor->portalname); + portal = GetPortalByName(self->portalname); if (PortalIsValid(portal)) { UnpinPortal(portal); SPI_cursor_close(portal); } - cursor->closed = true; + self->closed = true; } - if (cursor->mcxt) + if (self->mcxt) { - MemoryContextDelete(cursor->mcxt); - cursor->mcxt = NULL; + MemoryContextDelete(self->mcxt); + self->mcxt = NULL; } - arg->ob_type->tp_free(arg); + + PyObject_Free(self); + Py_DECREF(tp); } static PyObject * diff --git a/src/pl/plpython/plpy_planobject.c b/src/pl/plpython/plpy_planobject.c index bbef889329e..986b7179f14 100644 --- a/src/pl/plpython/plpy_planobject.c +++ b/src/pl/plpython/plpy_planobject.c @@ -12,7 +12,7 @@ #include "plpython.h" #include "utils/memutils.h" -static void PLy_plan_dealloc(PyObject *arg); +static void PLy_plan_dealloc(PLyPlanObject *self); static PyObject *PLy_plan_cursor(PyObject *self, PyObject *args); static PyObject *PLy_plan_execute(PyObject *self, PyObject *args); static PyObject *PLy_plan_status(PyObject *self, PyObject *args); @@ -26,20 +26,37 @@ static PyMethodDef PLy_plan_methods[] = { {NULL, NULL, 0, NULL} }; -static PyTypeObject PLy_PlanType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "PLyPlan", - .tp_basicsize = sizeof(PLyPlanObject), - .tp_dealloc = PLy_plan_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = PLy_plan_doc, - .tp_methods = PLy_plan_methods, +static PyType_Slot PLyPlan_slots[] = +{ + { + Py_tp_dealloc, PLy_plan_dealloc + }, + { + Py_tp_doc, (char *) PLy_plan_doc + }, + { + Py_tp_methods, PLy_plan_methods + }, + { + 0, NULL + } }; +static PyType_Spec PLyPlan_spec = +{ + .name = "PLyPlan", + .basicsize = sizeof(PLyPlanObject), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .slots = PLyPlan_slots, +}; + +static PyTypeObject *PLy_PlanType; + void PLy_plan_init_type(void) { - if (PyType_Ready(&PLy_PlanType) < 0) + PLy_PlanType = (PyTypeObject *) PyType_FromSpec(&PLyPlan_spec); + if (!PLy_PlanType) elog(ERROR, "could not initialize PLy_PlanType"); } @@ -48,7 +65,7 @@ PLy_plan_new(void) { PLyPlanObject *ob; - if ((ob = PyObject_New(PLyPlanObject, &PLy_PlanType)) == NULL) + if ((ob = PyObject_New(PLyPlanObject, PLy_PlanType)) == NULL) return NULL; ob->plan = NULL; @@ -64,25 +81,27 @@ PLy_plan_new(void) bool is_PLyPlanObject(PyObject *ob) { - return ob->ob_type == &PLy_PlanType; + return ob->ob_type == PLy_PlanType; } static void -PLy_plan_dealloc(PyObject *arg) +PLy_plan_dealloc(PLyPlanObject *self) { - PLyPlanObject *ob = (PLyPlanObject *) arg; + PyTypeObject *tp = Py_TYPE(self); - if (ob->plan) + if (self->plan) { - SPI_freeplan(ob->plan); - ob->plan = NULL; + SPI_freeplan(self->plan); + self->plan = NULL; } - if (ob->mcxt) + if (self->mcxt) { - MemoryContextDelete(ob->mcxt); - ob->mcxt = NULL; + MemoryContextDelete(self->mcxt); + self->mcxt = NULL; } - arg->ob_type->tp_free(arg); + + PyObject_Free(self); + Py_DECREF(tp); } diff --git a/src/pl/plpython/plpy_procedure.c b/src/pl/plpython/plpy_procedure.c index c35a3b801ab..b494eeb474f 100644 --- a/src/pl/plpython/plpy_procedure.c +++ b/src/pl/plpython/plpy_procedure.c @@ -350,6 +350,7 @@ PLy_procedure_compile(PLyProcedure *proc, const char *src) { PyObject *crv = NULL; char *msrc; + PyObject *code0; proc->globals = PyDict_Copy(PLy_interp_globals); @@ -368,7 +369,9 @@ PLy_procedure_compile(PLyProcedure *proc, const char *src) msrc = PLy_procedure_munge_source(proc->pyname, src); /* Save the mangled source for later inclusion in tracebacks */ proc->src = MemoryContextStrdup(proc->mcxt, msrc); - crv = PyRun_String(msrc, Py_file_input, proc->globals, NULL); + code0 = Py_CompileString(msrc, "", Py_file_input); + if (code0) + crv = PyEval_EvalCode(code0, proc->globals, NULL); pfree(msrc); if (crv != NULL) diff --git a/src/pl/plpython/plpy_resultobject.c b/src/pl/plpython/plpy_resultobject.c index 95acce65493..f2628205669 100644 --- a/src/pl/plpython/plpy_resultobject.c +++ b/src/pl/plpython/plpy_resultobject.c @@ -10,7 +10,7 @@ #include "plpy_resultobject.h" #include "plpython.h" -static void PLy_result_dealloc(PyObject *arg); +static void PLy_result_dealloc(PLyResultObject *self); static PyObject *PLy_result_colnames(PyObject *self, PyObject *unused); static PyObject *PLy_result_coltypes(PyObject *self, PyObject *unused); static PyObject *PLy_result_coltypmods(PyObject *self, PyObject *unused); @@ -24,17 +24,6 @@ static int PLy_result_ass_subscript(PyObject *arg, PyObject *item, PyObject *val static char PLy_result_doc[] = "Results of a PostgreSQL query"; -static PySequenceMethods PLy_result_as_sequence = { - .sq_length = PLy_result_length, - .sq_item = PLy_result_item, -}; - -static PyMappingMethods PLy_result_as_mapping = { - .mp_length = PLy_result_length, - .mp_subscript = PLy_result_subscript, - .mp_ass_subscript = PLy_result_ass_subscript, -}; - static PyMethodDef PLy_result_methods[] = { {"colnames", PLy_result_colnames, METH_NOARGS, NULL}, {"coltypes", PLy_result_coltypes, METH_NOARGS, NULL}, @@ -44,23 +33,55 @@ static PyMethodDef PLy_result_methods[] = { {NULL, NULL, 0, NULL} }; -static PyTypeObject PLy_ResultType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "PLyResult", - .tp_basicsize = sizeof(PLyResultObject), - .tp_dealloc = PLy_result_dealloc, - .tp_as_sequence = &PLy_result_as_sequence, - .tp_as_mapping = &PLy_result_as_mapping, - .tp_str = &PLy_result_str, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = PLy_result_doc, - .tp_methods = PLy_result_methods, +static PyType_Slot PLyResult_slots[] = +{ + { + Py_tp_dealloc, PLy_result_dealloc + }, + { + Py_sq_length, PLy_result_length + }, + { + Py_sq_item, PLy_result_item + }, + { + Py_mp_length, PLy_result_length + }, + { + Py_mp_subscript, PLy_result_subscript + }, + { + Py_mp_ass_subscript, PLy_result_ass_subscript + }, + { + Py_tp_str, PLy_result_str + }, + { + Py_tp_doc, (char *) PLy_result_doc + }, + { + Py_tp_methods, PLy_result_methods + }, + { + 0, NULL + } }; +static PyType_Spec PLyResult_spec = +{ + .name = "PLyResult", + .basicsize = sizeof(PLyResultObject), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .slots = PLyResult_slots, +}; + +static PyTypeObject *PLy_ResultType; + void PLy_result_init_type(void) { - if (PyType_Ready(&PLy_ResultType) < 0) + PLy_ResultType = (PyTypeObject *) PyType_FromSpec(&PLyResult_spec); + if (!PLy_ResultType) elog(ERROR, "could not initialize PLy_ResultType"); } @@ -69,7 +90,7 @@ PLy_result_new(void) { PLyResultObject *ob; - if ((ob = PyObject_New(PLyResultObject, &PLy_ResultType)) == NULL) + if ((ob = PyObject_New(PLyResultObject, PLy_ResultType)) == NULL) return NULL; /* ob->tuples = NULL; */ @@ -89,20 +110,21 @@ PLy_result_new(void) } static void -PLy_result_dealloc(PyObject *arg) +PLy_result_dealloc(PLyResultObject *self) { - PLyResultObject *ob = (PLyResultObject *) arg; + PyTypeObject *tp = Py_TYPE(self); - Py_XDECREF(ob->nrows); - Py_XDECREF(ob->rows); - Py_XDECREF(ob->status); - if (ob->tupdesc) + Py_XDECREF(self->nrows); + Py_XDECREF(self->rows); + Py_XDECREF(self->status); + if (self->tupdesc) { - FreeTupleDesc(ob->tupdesc); - ob->tupdesc = NULL; + FreeTupleDesc(self->tupdesc); + self->tupdesc = NULL; } - arg->ob_type->tp_free(arg); + PyObject_Free(self); + Py_DECREF(tp); } static PyObject * @@ -125,7 +147,7 @@ PLy_result_colnames(PyObject *self, PyObject *unused) { Form_pg_attribute attr = TupleDescAttr(ob->tupdesc, i); - PyList_SET_ITEM(list, i, PLyUnicode_FromString(NameStr(attr->attname))); + PyList_SetItem(list, i, PLyUnicode_FromString(NameStr(attr->attname))); } return list; @@ -151,7 +173,7 @@ PLy_result_coltypes(PyObject *self, PyObject *unused) { Form_pg_attribute attr = TupleDescAttr(ob->tupdesc, i); - PyList_SET_ITEM(list, i, PyLong_FromLong(attr->atttypid)); + PyList_SetItem(list, i, PyLong_FromLong(attr->atttypid)); } return list; @@ -177,7 +199,7 @@ PLy_result_coltypmods(PyObject *self, PyObject *unused) { Form_pg_attribute attr = TupleDescAttr(ob->tupdesc, i); - PyList_SET_ITEM(list, i, PyLong_FromLong(attr->atttypmod)); + PyList_SetItem(list, i, PyLong_FromLong(attr->atttypmod)); } return list; @@ -227,7 +249,7 @@ PLy_result_str(PyObject *arg) PLyResultObject *ob = (PLyResultObject *) arg; return PyUnicode_FromFormat("<%s status=%S nrows=%S rows=%S>", - Py_TYPE(ob)->tp_name, + "PLyResult", ob->status, ob->nrows, ob->rows); diff --git a/src/pl/plpython/plpy_subxactobject.c b/src/pl/plpython/plpy_subxactobject.c index 5c92a0e089a..cc7ff3f9df7 100644 --- a/src/pl/plpython/plpy_subxactobject.c +++ b/src/pl/plpython/plpy_subxactobject.c @@ -15,7 +15,6 @@ List *explicit_subtransactions = NIL; -static void PLy_subtransaction_dealloc(PyObject *subxact); static PyObject *PLy_subtransaction_enter(PyObject *self, PyObject *unused); static PyObject *PLy_subtransaction_exit(PyObject *self, PyObject *args); @@ -31,21 +30,35 @@ static PyMethodDef PLy_subtransaction_methods[] = { {NULL, NULL, 0, NULL} }; -static PyTypeObject PLy_SubtransactionType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "PLySubtransaction", - .tp_basicsize = sizeof(PLySubtransactionObject), - .tp_dealloc = PLy_subtransaction_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = PLy_subtransaction_doc, - .tp_methods = PLy_subtransaction_methods, +static PyType_Slot PLySubtransaction_slots[] = +{ + { + Py_tp_doc, (char *) PLy_subtransaction_doc + }, + { + Py_tp_methods, PLy_subtransaction_methods + }, + { + 0, NULL + } +}; + +static PyType_Spec PLySubtransaction_spec = +{ + .name = "PLySubtransaction", + .basicsize = sizeof(PLySubtransactionObject), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .slots = PLySubtransaction_slots, }; +static PyTypeObject *PLy_SubtransactionType; + void PLy_subtransaction_init_type(void) { - if (PyType_Ready(&PLy_SubtransactionType) < 0) + PLy_SubtransactionType = (PyTypeObject *) PyType_FromSpec(&PLySubtransaction_spec); + if (!PLy_SubtransactionType) elog(ERROR, "could not initialize PLy_SubtransactionType"); } @@ -55,7 +68,7 @@ PLy_subtransaction_new(PyObject *self, PyObject *unused) { PLySubtransactionObject *ob; - ob = PyObject_New(PLySubtransactionObject, &PLy_SubtransactionType); + ob = PyObject_New(PLySubtransactionObject, PLy_SubtransactionType); if (ob == NULL) return NULL; @@ -66,12 +79,6 @@ PLy_subtransaction_new(PyObject *self, PyObject *unused) return (PyObject *) ob; } -/* Python requires a dealloc function to be defined */ -static void -PLy_subtransaction_dealloc(PyObject *subxact) -{ -} - /* * subxact.__enter__() or subxact.enter() * diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c index db14c5f8dae..27d9b2f1af6 100644 --- a/src/pl/plpython/plpy_typeio.c +++ b/src/pl/plpython/plpy_typeio.c @@ -723,7 +723,7 @@ PLyList_FromArray_recurse(PLyDatumToOb *elm, int *dims, int ndim, int dim, sublist = PLyList_FromArray_recurse(elm, dims, ndim, dim + 1, dataptr_p, bitmap_p, bitmask_p); - PyList_SET_ITEM(list, i, sublist); + PyList_SetItem(list, i, sublist); } } else @@ -742,14 +742,14 @@ PLyList_FromArray_recurse(PLyDatumToOb *elm, int *dims, int ndim, int dim, if (bitmap && (*bitmap & bitmask) == 0) { Py_INCREF(Py_None); - PyList_SET_ITEM(list, i, Py_None); + PyList_SetItem(list, i, Py_None); } else { Datum itemvalue; itemvalue = fetch_att(dataptr, elm->typbyval, elm->typlen); - PyList_SET_ITEM(list, i, elm->func(elm, itemvalue)); + PyList_SetItem(list, i, elm->func(elm, itemvalue)); dataptr = att_addlength_pointer(dataptr, elm->typlen, dataptr); dataptr = (char *) att_align_nominal(dataptr, elm->typalign); } diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h index d929afefc33..fb95b23730f 100644 --- a/src/pl/plpython/plpython.h +++ b/src/pl/plpython/plpython.h @@ -19,6 +19,8 @@ #error Python.h must be included via plpython.h #endif +#define Py_LIMITED_API 0x03020000 + /* * Pull in Python headers via a wrapper header, to control the scope of * the system_header pragma therein. -- 2.47.1