from cpython.object cimport PyObject
ctypedef object (*cloader_func)(const char *data, size_t length, void *context)
-# ctypedef void * (*get_context_func)(PGconn_ptr conn)
-
-cdef void register_c_loader(object pyloader, cloader_func cloader)
+ctypedef void * (*get_context_func)(object conn)
cdef struct RowLoader:
@Loader.binary(INVALID_OID)
-def load_unknown(data: bytes) -> bytes:
+def load_unknown_binary(data: bytes) -> bytes:
return data
-from psycopg3.adapt cimport cloader_func
+from psycopg3.adapt cimport cloader_func, get_context_func, RowLoader
-from psycopg3.types cimport numeric
+from psycopg3.types cimport numeric, text
import logging
cdef class CLoader:
cdef object pyloader
cdef cloader_func cloader
+ cdef get_context_func get_context
cloaders = {}
-cdef void register_c_loader(object pyloader, cloader_func cloader):
+cdef void register_c_loader(
+ object pyloader,
+ cloader_func cloader,
+ get_context_func get_context = NULL
+):
"""
Map a python loader to an optimised C version.
cdef CLoader cl = CLoader()
cl.pyloader = pyloader
cl.cloader = cloader
+ cl.get_context = get_context
cloaders[pyloader] = cl
+cdef void fill_row_loader(RowLoader *loader, object pyloader):
+ loader.pyloader = <PyObject *>pyloader
+
+ print(pyloader)
+ cdef CLoader cloader
+ cloader = cloaders.get(pyloader)
+ if cloader is not None:
+ loader.cloader = cloader.cloader
+ else:
+ cloader = cloaders.get(getattr(pyloader, '__func__', None))
+ if cloader is not None and cloader.get_context is not NULL:
+ loader.cloader = cloader.cloader
+ loader.context = cloader.get_context(pyloader.__self__)
+
+
def register_builtin_c_loaders():
if cloaders:
logger.warning("c loaders already registered")
logger.debug("registering optimised c loaders")
register_numeric_c_loaders()
+ register_text_c_loaders()
cdef void register_numeric_c_loaders():
register_c_loader(numeric.load_int4_binary, load_int4_binary)
register_c_loader(numeric.load_int8_binary, load_int8_binary)
register_c_loader(numeric.load_oid_binary, load_oid_binary)
+ register_c_loader(numeric.load_bool_binary, load_bool_binary)
+
+
+cdef void register_text_c_loaders():
+ logger.debug("registering optimised text c loaders")
+ from psycopg3 import adapt
+ from psycopg3.types import text
+ register_c_loader(text.StringLoader.load, load_text, get_context_text)
+ register_c_loader(adapt.UnknownLoader.load, load_unknown_text)
+ register_c_loader(adapt.load_unknown_binary, load_unknown_binary)
cdef void _set_loader(self, int col, libpq.Oid oid, int fmt):
cdef RowLoader *loader = self._row_loaders + col
- pyloader = self.get_load_function(oid, fmt)
- self._pyloaders[col] = pyloader
- loader.pyloader = <PyObject *>pyloader
-
- # STUB: special-case a few loaders
- from psycopg3.types import builtins
-
- if oid == builtins['int2'].oid and fmt == 0:
- loader.cloader = load_int_text
- elif oid == builtins['int2'].oid and fmt == 1:
- loader.cloader = load_int2_binary
- elif oid == builtins['int4'].oid and fmt == 1:
- loader.cloader = load_int4_binary
- elif oid == builtins['int8'].oid and fmt == 1:
- loader.cloader = load_int8_binary
- elif oid == builtins['oid'].oid and fmt == 1:
- loader.cloader = load_oid_binary
- elif oid == builtins['bool'].oid and fmt == 1:
- loader.cloader = load_bool_binary
- elif oid == builtins['text'].oid:
- loader.cloader = load_text_binary
- elif oid == builtins['"char"'].oid:
- loader.cloader = load_text_binary
- elif oid == builtins['name'].oid:
- loader.cloader = load_text_binary
- elif oid == builtins['unknown'].oid:
- loader.cloader = load_unknown_binary
+ pyloader = self._pyloaders[col] = self.get_load_function(oid, fmt)
+ fill_row_loader(loader, pyloader)
def dump_sequence(
self, objs: Iterable[Any], formats: Iterable[Format]
def _config_types(self, data: bytes) -> None:
self._tx.set_row_types(
- (oid, Format.BINARY) for oid, _, _ in self._walk_record(data)
+ [(oid, Format.BINARY) for oid, _, _ in self._walk_record(data)]
)
--- /dev/null
+cdef object load_text(const char *data, size_t length, void *context)
+cdef void *get_context_text(object loader)
+cdef object load_unknown_text(const char *data, size_t length, void *context)
+cdef object load_unknown_binary(const char *data, size_t length, void *context)
+from cpython.bytes cimport PyBytes_FromStringAndSize
from cpython.unicode cimport PyUnicode_DecodeUTF8
-cdef object load_text_binary(const char *data, size_t length, void *context):
+
+cdef object load_text(const char *data, size_t length, void *context):
+ # TODO: optimize
+ b = PyBytes_FromStringAndSize(data, length)
+ if context is not NULL:
+ codec = <object>context
+ # TODO: check if the refcount is right (but a DECREF here segfaults)
+ return codec(b)[0]
+ else:
+ return b
+
+
+cdef void *get_context_text(object loader):
+ if loader.decode is not None:
+ return <void *>loader.decode
+ else:
+ return NULL
+
+
+cdef object load_unknown_text(const char *data, size_t length, void *context):
# TODO: codec
return PyUnicode_DecodeUTF8(data, length, NULL)
cdef object load_unknown_binary(const char *data, size_t length, void *context):
- # TODO: codec
- return PyUnicode_DecodeUTF8(data, length, NULL)
+ return PyBytes_FromStringAndSize(data, length)