From: Daniele Varrazzo Date: Sun, 5 Apr 2020 04:44:31 +0000 (+1200) Subject: Cleanup of the array adapter algorithm X-Git-Tag: 3.0.dev0~605 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=66ff409117c156194efe0f1c4f5cdd697bf3dfcf;p=thirdparty%2Fpsycopg.git Cleanup of the array adapter algorithm --- diff --git a/psycopg3/types/array.py b/psycopg3/types/array.py index 6d0d937a0..6bfe9f07f 100644 --- a/psycopg3/types/array.py +++ b/psycopg3/types/array.py @@ -130,10 +130,10 @@ class BinaryListAdapter(BaseListAdapter): if not obj: return _struct_head.pack(0, 0, TEXT_OID), TEXT_ARRAY_OID - data: List[bytes] = [] - head = [0, 0, 0] # to fill: ndims, hasnull, base_oid - dims = [] - data = [] + data: List[bytes] = [b"", b""] # placeholders to avoid a resize + dims: List[int] = [] + hasnull = 0 + oid = 0 def calc_dims(L: List[Any]) -> None: if isinstance(L, self.src): @@ -145,6 +145,7 @@ class BinaryListAdapter(BaseListAdapter): calc_dims(obj) def adapt_list(L: List[Any], dim: int) -> None: + nonlocal oid, hasnull if len(L) != dims[dim]: raise e.DataError("nested lists have inconsistent lengths") @@ -152,17 +153,17 @@ class BinaryListAdapter(BaseListAdapter): for item in L: ad = self.tx.adapt(item, Format.BINARY) if isinstance(ad, tuple): - if head[2] == 0: - head[2] = ad[1] + if oid == 0: + oid = ad[1] got_type = type(item) - elif head[2] != ad[1]: + elif oid != ad[1]: raise e.DataError( f"array contains different types," f" at least {got_type} and {type(item)}" ) ad = ad[0] if ad is None: - head[1] = 1 + hasnull = 1 data.append(b"\xff\xff\xff\xff") else: data.append(_struct_len.pack(len(ad))) @@ -177,16 +178,12 @@ class BinaryListAdapter(BaseListAdapter): adapt_list(obj, 0) - head[0] = len(dims) - if head[2] == 0: - head[2] = TEXT_OID - - oid = self._array_oid(head[2]) + if oid == 0: + oid = TEXT_OID - bhead = _struct_head.pack(*head) + b"".join( - _struct_dim.pack(dim, 1) for dim in dims - ) - return bhead + b"".join(data), oid + data[0] = _struct_head.pack(len(dims), hasnull, oid or TEXT_OID) + data[1] = b"".join(_struct_dim.pack(dim, 1) for dim in dims) + return b"".join(data), self._array_oid(oid) class ArrayCasterBase(TypeCaster): diff --git a/tests/types/test_array.py b/tests/types/test_array.py index 26721bf4b..136c9b306 100644 --- a/tests/types/test_array.py +++ b/tests/types/test_array.py @@ -7,6 +7,8 @@ from psycopg3.types.array import UnknownArrayCaster, ArrayCaster tests_str = [ ([], "{}"), + ([[[[[["a"]]]]]], "{{{{{{a}}}}}}"), + ([[[[[[None]]]]]], "{{{{{{NULL}}}}}}"), (["foo", "bar", "baz"], "{foo,bar,baz}"), (["foo", None, "baz"], "{foo,null,baz}"), (["foo", "null", "", "baz"], '{foo,"null","",baz}'), @@ -75,7 +77,14 @@ def test_adapt_list_int(conn, obj, want): @pytest.mark.parametrize( "input", - [[["a"], ["b", "c"]], [["a"], []], [[]], [[["a"]], ["b"]], [True, b"a"]], + [ + [["a"], ["b", "c"]], + [["a"], []], + [[]], + [[["a"]], ["b"]], + # [["a"], [["b"]]], # todo, but expensive (an isinstance per item) + [True, b"a"], + ], ) def test_bad_binary_array(input): tx = Transformer()