]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Added Transformer.dump_sequence()
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Fri, 8 Jan 2021 00:55:26 +0000 (01:55 +0100)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Fri, 8 Jan 2021 01:32:29 +0000 (02:32 +0100)
psycopg3/psycopg3/_queries.py
psycopg3/psycopg3/_transform.py
psycopg3/psycopg3/proto.py
psycopg3_c/psycopg3_c/_psycopg3.pyi
psycopg3_c/psycopg3_c/_psycopg3/transform.pyx

index 55d234fac889bfb0cc73af57a0457d67394cdc08..76ccb1ac10c01bed39c1212bbfda1df6edfffc7b 100644 (file)
@@ -12,7 +12,6 @@ from functools import lru_cache
 from . import errors as e
 from .pq import Format
 from .sql import Composable
-from .oids import TEXT_OID, INVALID_OID
 from .proto import Query, Params
 
 if TYPE_CHECKING:
@@ -43,7 +42,6 @@ class PostgresQuery:
         self.types: Tuple[int, ...] = ()
         self.formats: Optional[List[Format]] = None
 
-        self._unknown_oid = INVALID_OID
         self._parts: List[QueryPart]
         self.query = b""
         self._encoding = "utf-8"
@@ -52,8 +50,6 @@ class PostgresQuery:
         conn = transformer.connection
         if conn:
             self._encoding = conn.client_encoding
-            if conn.pgconn.server_version < 100000:
-                self._unknown_oid = TEXT_OID
 
     def convert(self, query: Query, vars: Optional[Params]) -> None:
         """
@@ -88,16 +84,9 @@ class PostgresQuery:
                 self._parts, vars, self._order
             )
             assert self.formats is not None
-            ps: List[Optional[bytes]] = [None] * len(params)
-            ts = [self._unknown_oid] * len(params)
-            for i in range(len(params)):
-                param = params[i]
-                if param is not None:
-                    dumper = self._tx.get_dumper(param, self.formats[i])
-                    ps[i] = dumper.dump(param)
-                    ts[i] = dumper.oid
-            self.params = ps
-            self.types = tuple(ts)
+            self.params, self.types = self._tx.dump_sequence(
+                params, self.formats
+            )
         else:
             self.params = None
             self.types = ()
index 4926a055881aec4f5ca0bed472d7ad4c08c32a86..1cdea90d06a30f12521150d231883abe5a3477b0 100644 (file)
@@ -9,7 +9,7 @@ from typing import TYPE_CHECKING
 
 from . import errors as e
 from .pq import Format
-from .oids import INVALID_OID
+from .oids import INVALID_OID, TEXT_OID
 from .proto import LoadFunc, AdaptContext
 
 if TYPE_CHECKING:
@@ -32,11 +32,16 @@ class Transformer(AdaptContext):
     _pgresult: Optional["PGresult"] = None
 
     def __init__(self, context: Optional[AdaptContext] = None):
+        self._unknown_oid = INVALID_OID
+
         # WARNING: don't store context, or you'll create a loop with the Cursor
         if context:
             self._adapters = context.adapters
-            self._connection = context.connection
+            conn = self._connection = context.connection
 
+            # PG 9.6 gives an error if an unknown oid is emitted as column
+            if conn and conn.pgconn.server_version < 100000:
+                self._unknown_oid = TEXT_OID
         else:
             from .adapt import global_adapters
 
@@ -95,6 +100,20 @@ class Transformer(AdaptContext):
 
         self._row_loaders = rc
 
+    def dump_sequence(
+        self, params: Sequence[Any], formats: Sequence[Format]
+    ) -> Tuple[List[Any], Tuple[int, ...]]:
+        ps: List[Optional[bytes]] = [None] * len(params)
+        ts = [self._unknown_oid] * len(params)
+        for i in range(len(params)):
+            param = params[i]
+            if param is not None:
+                dumper = self.get_dumper(param, formats[i])
+                ps[i] = dumper.dump(param)
+                ts[i] = dumper.oid
+
+        return ps, tuple(ts)
+
     def get_dumper(self, obj: Any, format: Format) -> "Dumper":
         # Fast path: return a Dumper class already instantiated from the same type
         cls = type(obj)
index f7a717297b7008bcf52656499d81553658fae9a7..f0a5a4c3e1dd59e8511e440b99f16145a9b3e412 100644 (file)
@@ -5,7 +5,7 @@ Protocol objects representing different implementations of the same classes.
 # Copyright (C) 2020 The Psycopg Team
 
 from typing import Any, Callable, Generator, Mapping
-from typing import Optional, Sequence, Tuple, TypeVar, Union
+from typing import List, Optional, Sequence, Tuple, TypeVar, Union
 from typing import TYPE_CHECKING
 from typing_extensions import Protocol
 
@@ -91,6 +91,11 @@ class Transformer(Protocol):
     ) -> None:
         ...
 
+    def dump_sequence(
+        self, params: Sequence[Any], formats: Sequence[Format]
+    ) -> Tuple[List[Any], Tuple[int, ...]]:
+        ...
+
     def get_dumper(self, obj: Any, format: Format) -> "Dumper":
         ...
 
index 167d2560f1b9d49e0cde7643e0793150ebfcceb0..9e24e0b831c55afd279d2aee24682be7ba9c3d4f 100644 (file)
@@ -28,6 +28,9 @@ class Transformer(proto.AdaptContext):
     def set_row_types(
         self, types: Sequence[int], formats: Sequence[Format]
     ) -> None: ...
+    def dump_sequence(
+        self, params: Sequence[Any], formats: Sequence[Format]
+    ) -> Tuple[List[Any], Tuple[int, ...]]: ...
     def get_dumper(self, obj: Any, format: Format) -> Dumper: ...
     def load_rows(self, row0: int, row1: int) -> Sequence[Tuple[Any, ...]]: ...
     def load_row(self, row: int) -> Optional[Tuple[Any, ...]]: ...
index 57e8dc4a6a60108ee207af716a3543884edbbf8d..4b9c62e439599c5085972149aba611dcb4d744a2 100644 (file)
@@ -58,11 +58,17 @@ cdef class Transformer:
     cdef pq.PGresult _pgresult
     cdef int _nfields, _ntuples
     cdef list _row_loaders
+    cdef int _unknown_oid
 
     def __cinit__(self, context: Optional["AdaptContext"] = None):
+        self._unknown_oid = oids.INVALID_OID
         if context is not None:
             self.adapters = context.adapters
             self.connection = context.connection
+
+            # PG 9.6 gives an error if an unknown oid is emitted as column
+            if self.connection and self.connection.pgconn.server_version < 100000:
+                self._unknown_oid = oids.TEXT_OID
         else:
             from psycopg3.adapt import global_adapters
             self.adapters = global_adapters
@@ -183,6 +189,39 @@ cdef class Transformer:
             f"cannot adapt type {cls.__name__} to format {Format(format).name}"
         )
 
+    cpdef dump_sequence(self, object params, object formats):
+        # Verify that they are not none and that PyList_GET_ITEM won't blow up
+        cdef int nparams = len(params)
+        cdef list ps = PyList_New(nparams)
+        cdef tuple ts = PyTuple_New(nparams)
+        cdef object dumped, oid
+        cdef Py_ssize_t size
+
+        cdef int i
+        for i in range(nparams):
+            param = params[i]
+            if param is not None:
+                format = formats[i]
+                dumper = self.get_dumper(param, format)
+                if isinstance(dumper, CDumper):
+                    dumped = PyByteArray_FromStringAndSize("", 0)
+                    size = (<CDumper>dumper).cdump(param, <bytearray>dumped, 0)
+                    PyByteArray_Resize(dumped, size)
+                    oid = (<CDumper>dumper).oid
+                else:
+                    dumped = dumper.dump(param)
+                    oid = dumper.oid
+            else:
+                dumped = None
+                oid = self._unknown_oid
+
+            Py_INCREF(dumped)
+            PyList_SET_ITEM(ps, i, dumped)
+            Py_INCREF(oid)
+            PyTuple_SET_ITEM(ts, i, oid)
+
+        return ps, ts
+
     def load_rows(self, int row0, int row1) -> Sequence[Tuple[Any, ...]]:
         if self._pgresult is None:
             raise e.InterfaceError("result not set")