Skip to content

Commit 8709585

Browse files
author
James William Pye
committed
Implement binary transfer of inet & cidr using ipaddr.
closes 1010579
1 parent 516e54b commit 8709585

5 files changed

Lines changed: 88 additions & 13 deletions

File tree

postgresql/documentation/driver.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,6 +1525,9 @@
15251525
`postgresql.types.NUMERICOID` `decimal.Decimal` numeric
15261526
`postgresql.types.BYTEAOID` `bytes` bytea
15271527
`postgresql.types.TEXTOID` `str` text
1528+
1529+
`postgresql.types.CIDROID` `ipaddr.IPv4` or `ipaddr.IPv6` cidr
1530+
`postgresql.types.INETOID` `ipaddr.IPv4` or `ipaddr.IPv6` inet
15281531
================================= ================================== ===========
15291532
15301533
The mapping in the above table *normally* goes both ways. So when a parameter

postgresql/driver/pq3.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1921,7 +1921,15 @@ def connect(self):
19211921

19221922
r = self.sys.activity_for(self.backend_id)
19231923
if r is not None:
1924-
self.client_address = r.get('client_addr')
1924+
# conditional initialization of client_address.
1925+
# pythons without ipaddr will likely give strings.
1926+
ca = r.get('client_addr')
1927+
if ca is not None:
1928+
if not isinstance(ca, str):
1929+
# it better be a ipaddr.BaseIP..
1930+
self.client_address = ca.ip_ext
1931+
else:
1932+
self.client_address = ca
19251933
self.client_port = r.get('client_port')
19261934
self.backend_start = r.get('backend_start')
19271935
try:

postgresql/protocol/typio.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,39 @@ def numeric_unpack(x):
421421
pg_types.CIRCLEOID : (circle_pack, circle_unpack),
422422
}
423423

424+
# Conditionally use ipaddr.
425+
# Condition goes away when 3.0 is deprecated.
426+
try:
427+
import ipaddr
428+
def net_pack(ip):
429+
family = None
430+
converted = False
431+
while family is None:
432+
if isinstance(ip, ipaddr.IPv4):
433+
family = 2
434+
elif isinstance(ip, ipaddr.IPv6):
435+
family = 3
436+
else:
437+
if converted is True:
438+
raise ValueError("unknown IP type: " + repr(ip))
439+
ip = ipaddr.IP(ip)
440+
converted = True
441+
return ts.net_pack((family, ip.prefixlen, ip.packed))
442+
443+
def net_unpack(data):
444+
family, mask, data = ts.net_unpack(data)
445+
if family in (2, 3):
446+
v = ipaddr.IP(data)
447+
else:
448+
raise ValueError("unknown net family: " + repr(family))
449+
v.prefixlen = mask
450+
return v
451+
oid_to_io[pg_types.CIDROID] = (net_pack, net_unpack)
452+
oid_to_io[pg_types.INETOID] = (net_pack, net_unpack)
453+
except ImportError:
454+
oid_to_io[pg_types.CIDROID] = (None, None)
455+
oid_to_io[pg_types.INETOID] = (None, None)
456+
424457
def process_tuple(procs, tup, exception_handler):
425458
"""
426459
Call each item in `procs` with the corresponding
@@ -716,8 +749,6 @@ def __init__(self):
716749
pg_types.VARCHAROID : (None, None),
717750
pg_types.CSTRINGOID : (None, None),
718751
pg_types.TEXTOID : (None, None),
719-
pg_types.CIDROID : (None, None),
720-
pg_types.INETOID : (None, None),
721752
pg_types.REGPROCEDUREOID : (None, None),
722753
pg_types.REGTYPEOID : (None, None),
723754
pg_types.REGPROCOID : (None, None),

postgresql/protocol/typstruct.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -313,11 +313,13 @@ def varbit_unpack(data):
313313
"""
314314
return long_unpack(data[0:4]), data[4:]
315315

316-
def cidr_pack(family_mask_data):
316+
def net_pack(family_mask_data):
317317
"""
318318
Given a triple, yield the serialized form for transport.
319319
320320
Prepends the ``family``, ``mask`` and implicit ``is_cidr`` fields.
321+
322+
Supports cidr and inet types.
321323
"""
322324
(family, mask, data) = family_mask_data
323325

@@ -328,9 +330,8 @@ def cidr_pack(family_mask_data):
328330
byte_pack(len(data)),
329331
data
330332
))
331-
inet_pack = cidr_pack
332333

333-
def cidr_unpack(data):
334+
def net_unpack(data):
334335
"""
335336
Given serialized cidr data, return a tuple:
336337
@@ -343,7 +344,6 @@ def cidr_unpack(data):
343344
raise ValueError("invalid size parameter")
344345

345346
return (family, mask, rd)
346-
inet_unpack = cidr_unpack
347347

348348
def record_unpack(data):
349349
"""
@@ -471,9 +471,9 @@ def return_arg(arg):
471471
pg_types.BYTEAOID : (bytes, bytes),
472472
pg_types.CHAROID : literal,
473473

474-
# pg_type.MACADDROID : literal,
475-
# pg_type.INETOID : (cidr_pack, cidr_unpack),
476-
# pg_type.CIDROID : (cidr_pack, cidr_unpack),
474+
# pg_types.MACADDROID : literal,
475+
pg_types.INETOID : (net_pack, net_unpack),
476+
pg_types.CIDROID : (net_pack, net_unpack),
477477

478478
pg_types.DATEOID : (date_pack, date_unpack),
479479
pg_types.ABSTIMEOID : (long_pack, long_unpack),

postgresql/test/test_driver.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from .. import unittest as pg_unittest
2020
from .. import lib as pg_lib
2121

22-
type_samples = (
22+
type_samples = [
2323
('smallint', (
2424
((1 << 16) // 2) - 1, - ((1 << 16) // 2),
2525
-1, 0, 1,
@@ -268,7 +268,40 @@
268268
pg_types.varbit('010111101111'),
269269
],
270270
),
271-
)
271+
]
272+
273+
try:
274+
import ipaddr
275+
type_samples.append((
276+
'inet', [
277+
ipaddr.IPv4('255.255.255.255'),
278+
ipaddr.IPv4('127.0.0.1'),
279+
ipaddr.IPv4('10.0.0.1'),
280+
ipaddr.IPv4('0.0.0.0'),
281+
ipaddr.IPv6('::1'),
282+
ipaddr.IPv6('ffff' + ':ffff'*7),
283+
ipaddr.IPv6('fe80::1'),
284+
ipaddr.IPv6('fe80::1'),
285+
ipaddr.IPv6('0::0'),
286+
],
287+
))
288+
type_samples.append((
289+
'cidr', [
290+
ipaddr.IPv4('255.255.255.255/32'),
291+
ipaddr.IPv4('127.0.0.0/8'),
292+
ipaddr.IPv4('127.1.0.0/16'),
293+
ipaddr.IPv4('10.0.0.0/32'),
294+
ipaddr.IPv4('0.0.0.0/0'),
295+
ipaddr.IPv6('ffff' + ':ffff'*7 + '/128'),
296+
ipaddr.IPv6('::1/128'),
297+
ipaddr.IPv6('fe80::1/128'),
298+
ipaddr.IPv6('fe80::0/64'),
299+
ipaddr.IPv6('fe80::0/16'),
300+
ipaddr.IPv6('0::0/0'),
301+
],
302+
))
303+
except ImportError:
304+
pass
272305

273306
class test_driver(pg_unittest.TestCaseWithCluster):
274307
"""
@@ -873,7 +906,7 @@ def testTypes(self):
873906
"SELECT $1::" + typname
874907
)
875908
for sample in sample_data:
876-
rsample = pb.first(sample)
909+
rsample = list(pb.rows(sample))[0][0]
877910
if isinstance(rsample, pg_types.Array):
878911
rsample = rsample.nest()
879912
self.failUnless(

0 commit comments

Comments
 (0)