Skip to content

Commit c5cbda1

Browse files
author
James William Pye
committed
Improve ARRAY representation.
Give types.Array the appropriate lowerbounds and upperbounds fields for properly representing a PostgreSQL array.
1 parent 5a63357 commit c5cbda1

5 files changed

Lines changed: 326 additions & 139 deletions

File tree

postgresql/documentation/changes.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,6 @@ Changes
2323
an interface intended for speed, types.Row() impedes its performance.
2424
* Fix handling of infinity values with timestamptz, timestamp, and date.
2525
Bug reported by Axel Rau.
26+
* Correct representation of PostgreSQL ARRAYs by properly recording
27+
lowerbounds and upperbounds. Internally, sub-ARRAYs have their own
28+
element lists.

postgresql/test/test_types.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from .. import types as pg_types
77
from ..types.io import lib as typlib
88
from ..types.io import builtins
9+
from ..types import Array
910

1011
# this must pack to that, and
1112
# that must unpack to this
@@ -362,6 +363,129 @@ def testConsistency(self):
362363
)
363364
)
364365

366+
# Make some slices
367+
slice_samples = [
368+
slice(0, None, x+1) for x in range(10)
369+
] + [
370+
slice(x, None, 1) for x in range(10)
371+
] + [
372+
slice(None, x, 1) for x in range(10)
373+
] + [
374+
slice(None, -x, 70) for x in range(10)
375+
]
376+
377+
class test_Array(unittest.TestCase):
378+
def emptyArray(self, a):
379+
self.failUnlessEqual(len(a), 0)
380+
self.failUnlessEqual(list(a.elements()), [])
381+
self.failUnlessEqual(a.dimensions, ())
382+
self.failUnlessEqual(a.lowerbounds, ())
383+
self.failUnlessEqual(a.upperbounds, ())
384+
self.failUnlessRaises(IndexError, a.__getitem__, 0)
385+
386+
def testArrayInstantiation(self):
387+
a = Array([])
388+
self.emptyArray(a)
389+
# exercise default upper/lower
390+
a = Array((1,2,3,))
391+
self.failUnlessEqual((a[0],a[1],a[2]), (1,2,3,))
392+
# Python interface, Python semantics.
393+
self.failUnlessRaises(IndexError, a.__getitem__, 3)
394+
self.failUnlessEqual(a.dimensions, (3,))
395+
self.failUnlessEqual(a.lowerbounds, (1,))
396+
self.failUnlessEqual(a.upperbounds, (3,))
397+
398+
def testNestedArrayInstantiation(self):
399+
a = Array(([1,2],[3,4]))
400+
# Python interface, Python semantics.
401+
self.failUnlessRaises(IndexError, a.__getitem__, 3)
402+
self.failUnlessEqual(a.dimensions, (2,2,))
403+
self.failUnlessEqual(a.lowerbounds, (1,1))
404+
self.failUnlessEqual(a.upperbounds, (2,2))
405+
self.failUnlessEqual(list(a.elements()), [1,2,3,4])
406+
self.failUnlessEqual(list(a),
407+
[
408+
Array([1, 2]),
409+
Array([3, 4]),
410+
]
411+
)
412+
413+
a = Array(([[1],[2]],[[3],[4]]))
414+
self.failUnlessRaises(IndexError, a.__getitem__, 3)
415+
self.failUnlessEqual(a.dimensions, (2,2,1))
416+
self.failUnlessEqual(a.lowerbounds, (1,1,1))
417+
self.failUnlessEqual(a.upperbounds, (2,2,1))
418+
self.failUnlessEqual(list(a),
419+
[
420+
Array([[1], [2]]),
421+
Array([[3], [4]]),
422+
]
423+
)
424+
425+
def testSlicing(self):
426+
elements = [1,2,3,4,5,6,7,8]
427+
d1 = Array([1,2,3,4,5,6,7,8])
428+
for x in slice_samples:
429+
self.failUnlessEqual(
430+
d1[x], Array(elements[x])
431+
)
432+
elements = [[1,2],[3,4],[5,6],[7,8]]
433+
d2 = Array(elements)
434+
for x in slice_samples:
435+
self.failUnlessEqual(
436+
d2[x], Array(elements[x])
437+
)
438+
439+
def testFromElements(self):
440+
a = Array.from_elements(())
441+
self.emptyArray(a)
442+
443+
# exercise default upper/lower
444+
a = Array.from_elements((1,2,3,))
445+
self.failUnlessEqual((a[0],a[1],a[2]), (1,2,3,))
446+
# Python interface, Python semantics.
447+
self.failUnlessRaises(IndexError, a.__getitem__, 3)
448+
self.failUnlessEqual(a.dimensions, (3,))
449+
self.failUnlessEqual(a.lowerbounds, (1,))
450+
self.failUnlessEqual(a.upperbounds, (3,))
451+
452+
# exercise default upper/lower
453+
a = Array.from_elements([3,2,1], lowerbounds = (2,), upperbounds = (4,))
454+
self.failUnlessEqual(a.dimensions, (3,))
455+
self.failUnlessEqual(a.lowerbounds, (2,))
456+
self.failUnlessEqual(a.upperbounds, (4,))
457+
458+
def testEmptyDimension(self):
459+
self.failUnlessRaises(ValueError,
460+
Array, [[]]
461+
)
462+
self.failUnlessRaises(ValueError,
463+
Array, [[2],[]]
464+
)
465+
self.failUnlessRaises(ValueError,
466+
Array, [[],[],[]]
467+
)
468+
self.failUnlessRaises(ValueError,
469+
Array, [[2],[3],[]]
470+
)
471+
472+
def testExcessive(self):
473+
# lowerbounds too high for upperbounds
474+
self.failUnlessRaises(ValueError,
475+
Array.from_elements, [1], lowerbounds = (2,), upperbounds = (1,)
476+
)
477+
478+
def testNegatives(self):
479+
a = Array.from_elements([0], lowerbounds = (-1,), upperbounds = (-1,))
480+
self.failUnlessEqual(a[0], 0)
481+
self.failUnlessEqual(a[-1], 0)
482+
# upperbounds at zero
483+
a = Array.from_elements([1,2], lowerbounds = (-1,), upperbounds = (0,))
484+
self.failUnlessEqual(a[0], 1)
485+
self.failUnlessEqual(a[1], 2)
486+
self.failUnlessEqual(a[-2], 1)
487+
self.failUnlessEqual(a[-1], 2)
488+
365489
if __name__ == '__main__':
366490
from types import ModuleType
367491
this = ModuleType("this")

0 commit comments

Comments
 (0)