From: Daniele Varrazzo Date: Thu, 14 Jan 2021 19:45:00 +0000 (+0100) Subject: Dropped intermediate dumpers cache in C transformer too X-Git-Tag: 3.0.dev0~160 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=325d8c928ace4473b7360b48713bde5cd1003506;p=thirdparty%2Fpsycopg.git Dropped intermediate dumpers cache in C transformer too --- diff --git a/psycopg3_c/psycopg3_c/_psycopg3/transform.pyx b/psycopg3_c/psycopg3_c/_psycopg3/transform.pyx index e2140a95d..741c2c2c9 100644 --- a/psycopg3_c/psycopg3_c/_psycopg3/transform.pyx +++ b/psycopg3_c/psycopg3_c/_psycopg3/transform.pyx @@ -50,10 +50,11 @@ cdef class RowLoader: @cython.freelist(16) cdef class RowDumper: + cdef CDumper cdumper + cdef object pydumper cdef object dumpfunc cdef object oid cdef object format - cdef CDumper cdumper cdef class Transformer: @@ -135,8 +136,6 @@ cdef class Transformer: # these are used more as Python object than C cdef PyObject *oid cdef PyObject *fmt - cdef PyObject *ptr - cdef PyObject *cache cdef PyObject *row_loader for i in range(ntypes): oid = PyList_GET_ITEM(types, i) @@ -147,19 +146,27 @@ cdef class Transformer: self._row_loaders = loaders - cpdef object get_dumper(self, object obj, object format): + def get_dumper(self, obj, format) -> "Dumper": + cdef PyObject *row_dumper = self._c_get_dumper( + obj, format) + return (row_dumper).pydumper + + cdef PyObject *_c_get_dumper(self, PyObject *obj, PyObject *fmt) except NULL: + """ + Return a borrowed reference to the RowDumper for the given obj/fmt. + """ # Fast path: return a Dumper class already instantiated from the same type cdef PyObject *cache cdef PyObject *ptr - cls = type(obj) + cls = type(obj) if cls is not list: key = cls else: - subobj = self._find_list_element(obj, set()) + subobj = _find_list_element(obj, set()) key = (cls, type(subobj)) - bfmt = PyUnicode_AsUTF8String(format) + bfmt = PyUnicode_AsUTF8String(fmt) cdef char cfmt = PyBytes_AS_STRING(bfmt)[0] if cfmt == b's': if self._auto_dumpers is None: @@ -175,43 +182,53 @@ cdef class Transformer: cache = self._text_dumpers else: raise ValueError( - f"format should be a psycopg3.adapt.Format, not {format}") + f"format should be a psycopg3.adapt.Format, not {fmt}") ptr = PyDict_GetItem(cache, key) if ptr != NULL: - return ptr + return ptr # When dumping a string with %s we may refer to any type actually, # but the user surely passed a text format if cls is str and cfmt == b's': - format = PG_TEXT + fmt = PG_TEXT - sub_dumper = None + cdef PyObject *sub_dumper = NULL if cls is list: # It's not possible to declare an empty unknown array, so force text if subobj is None: - format = PG_TEXT + fmt = PG_TEXT # If we are dumping a list it's the sub-object which should dictate # what format to use. else: - sub_dumper = self.get_dumper(subobj, format) - format = Pg3Format.from_pq(sub_dumper.format) + sub_dumper = self._c_get_dumper(subobj, fmt) + tmp = Pg3Format.from_pq((sub_dumper).format) + fmt = tmp dcls = PyObject_CallFunctionObjArgs( - self.adapters.get_dumper, cls, format, NULL) + self.adapters.get_dumper, cls, fmt, NULL) if dcls is None: raise e.ProgrammingError( f"cannot adapt type {cls.__name__}" - f" to format {Pg3Format(format).name}") + f" to format {Pg3Format(fmt).name}") - d = PyObject_CallFunctionObjArgs( + dumper = PyObject_CallFunctionObjArgs( dcls, cls, self, NULL) - if sub_dumper is not None: - d.set_sub_dumper(sub_dumper) + if sub_dumper != NULL: + dumper.set_sub_dumper((sub_dumper).pydumper) + + cdef RowDumper row_dumper = RowDumper() - PyDict_SetItem(cache, key, d) - return d + row_dumper.pydumper = dumper + row_dumper.dumpfunc = dumper.dump + row_dumper.oid = dumper.oid + row_dumper.format = dumper.format + if isinstance(dumper, CDumper): + row_dumper.cdumper = dumper + + PyDict_SetItem(cache, key, row_dumper) + return row_dumper cpdef dump_sequence(self, object params, object formats): # Verify that they are not none and that PyList_GET_ITEM won't blow up @@ -235,10 +252,10 @@ cdef class Transformer: dumper_ptr = PyList_GET_ITEM(dumpers, i) if dumper_ptr == NULL: format = formats[i] - tmp_dumper = self._get_row_dumper(param, format) - Py_INCREF(tmp_dumper) - PyList_SET_ITEM(dumpers, i, tmp_dumper) - dumper_ptr = tmp_dumper + dumper_ptr = self._c_get_dumper( + param, format) + Py_INCREF(dumper_ptr) + PyList_SET_ITEM(dumpers, i, dumper_ptr) oid = (dumper_ptr).oid dfmt = (dumper_ptr).format @@ -265,21 +282,6 @@ cdef class Transformer: return ps, ts, fs - cdef RowDumper _get_row_dumper(self, object param, object fmt): - cdef RowDumper row_dumper = RowDumper() - - dumper = self.get_dumper(param, fmt) - row_dumper.dumpfunc = dumper.dump - row_dumper.oid = dumper.oid - row_dumper.format = dumper.format - - if isinstance(dumper, CDumper): - row_dumper.cdumper = dumper - else: - row_dumper.cdumper = None - - return row_dumper - def load_rows(self, int row0, int row1) -> List[Tuple[Any, ...]]: if self._pgresult is None: raise e.InterfaceError("result not set") @@ -416,10 +418,7 @@ cdef class Transformer: def get_loader(self, oid: int, format: pq.Format) -> "Loader": cdef PyObject *row_loader = self._c_get_loader( oid, format) - if (row_loader).cloader is not None: - return (row_loader).cloader - else: - return (row_loader).pyloader + return (row_loader).pyloader cdef PyObject *_c_get_loader(self, PyObject *oid, PyObject *fmt) except NULL: @@ -455,34 +454,34 @@ cdef class Transformer: loader_cls, oid, self, NULL) cdef RowLoader row_loader = RowLoader() + row_loader.pyloader = loader row_loader.loadfunc = loader.load if isinstance(loader, CLoader): - row_loader.cloader = loader - else: - row_loader.pyloader = loader + row_loader.cloader = loader PyDict_SetItem(cache, oid, row_loader) return row_loader - cdef object _find_list_element(self, object L, set seen): - """ - Find the first non-null element of an eventually nested list - """ - cdef object list_id = L - if PySet_Contains(seen, list_id): - raise e.DataError("cannot dump a recursive list") - - PySet_Add(seen, list_id) - cdef int i - cdef PyObject *it - for i in range(PyList_GET_SIZE(L)): - it = PyList_GET_ITEM(L, i) - if PyList_CheckExact(it): - subit = self._find_list_element(it, seen) - if subit is not None: - return subit - elif it is not None: - return it - - return None +cdef object _find_list_element(PyObject *L, object seen): + """ + Find the first non-null element of an eventually nested list + """ + cdef object list_id = L + if PySet_Contains(seen, list_id): + raise e.DataError("cannot dump a recursive list") + + PySet_Add(seen, list_id) + + cdef int i + cdef PyObject *it + for i in range(PyList_GET_SIZE(L)): + it = PyList_GET_ITEM(L, i) + if PyList_CheckExact(it): + subit = _find_list_element(it, seen) + if subit is not None: + return subit + elif it is not None: + return it + + return None