Skip to content

Commit 21fe68a

Browse files
author
James William Pye
committed
Implement a sized split generator in python.structlib.
This will be used to implement hstore.
1 parent 7b478c6 commit 21fe68a

3 files changed

Lines changed: 54 additions & 5 deletions

File tree

postgresql/python/structlib.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
##
2-
# copyright 2009, James William Pye
3-
# http://python.projects.postgresql.org
2+
# .python.structlib - module for extracting serialized data
43
##
54
import struct
65
from .functools import Composition as compose
@@ -76,3 +75,30 @@ def mk_pack(x):
7675
ushort_pack, ushort_unpack = mk_pack("H")
7776
long_pack, long_unpack = mk_pack("l")
7877
ulong_pack, ulong_unpack = mk_pack("L")
78+
79+
def split_sized_data(
80+
data,
81+
ulong_unpack = ulong_unpack,
82+
null_field = 0xFFFFFFFF,
83+
len = len,
84+
errmsg = "insufficient data in field {0}, required {1} bytes, {2} remaining".format
85+
):
86+
"""
87+
Given serialized record data, return a tuple of tuples of type Oids and
88+
attributes.
89+
"""
90+
v = memoryview(data)
91+
f = 1
92+
while v:
93+
l = ulong_unpack(v)
94+
if l == null_field:
95+
v = v[4:]
96+
yield None
97+
continue
98+
l += 4
99+
d = v[4:l].tobytes()
100+
if len(d) < l-4:
101+
raise ValueError(errmsg(f, l - 4, len(d)))
102+
v = v[l:]
103+
f += 1
104+
yield d

postgresql/test/test_driver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,7 @@ def testLoadRows(self):
850850
list((x[0] for x in gs.rows())),
851851
list(range(1, 10001))
852852
)
853-
# exercise ``for x in chunks: dst.load(x)``
853+
# exercise ``for x in chunks: dst.load_rows(x)``
854854
with self.db.connector() as db2:
855855
db2.execute(
856856
"""

postgresql/test/test_python.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
##
2-
# copyright 2009, James William Pye
3-
# http://python.projects.postgresql.org
2+
# .test.test_python
43
##
54
import unittest
65
import socket
76
import errno
7+
import struct
88
from itertools import chain
99
from operator import methodcaller
1010
from contextlib import contextmanager
1111

12+
from ..python.itertools import interlace
13+
from ..python.structlib import split_sized_data
1214
from ..python.contextlib import *
1315
from ..python import functools
1416
from ..python import itertools
@@ -161,6 +163,27 @@ def testNoCM(self):
161163
pass
162164
self.failUnlessEqual(foo, None)
163165

166+
def join_sized_data(*data,
167+
packL = struct.Struct("!L").pack,
168+
getlen = lambda x: len(x) if x is not None else 0xFFFFFFFF
169+
):
170+
return b''.join(interlace(map(packL, map(getlen, data)), (x if x is not None else b'' for x in data)))
171+
172+
class test_structlib(unittest.TestCase):
173+
def testSizedSplit(self):
174+
sample = [
175+
(b'foo', b'bar'),
176+
(b'foo', None, b'bar'),
177+
(b'foo', None, b'bar'),
178+
(b'foo', b'bar'),
179+
(),
180+
(None,None,None),
181+
(b'x', None,None,None, b'yz'),
182+
]
183+
packed_sample = [join_sized_data(*x) for x in sample]
184+
self.failUnlessRaises(ValueError, split_sized_data(b'\xFF\xFF\xFF\x01foo').__next__)
185+
self.failUnlessEqual(sample, [tuple(split_sized_data(x)) for x in packed_sample])
186+
164187
if __name__ == '__main__':
165188
from types import ModuleType
166189
this = ModuleType("this")

0 commit comments

Comments
 (0)