RHEL6 付属の psycopg2 で、
# rpm -qa | grep psycopg
python-psycopg2-2.0.13-2.el6_1.1.x86_64
tuple 中にある None を mogrify するとエラーになる。
test1.py:
#!/usr/bin/python
import psycopg2.extensions
def main():
conn = psycopg2.connect('dbname=testdb user=testuser')
cur = conn.cursor()
for param in [0, [0], (0,), '', [''], ('',), None, [None], (None,)]:
print '%s\t->' % repr(param), cur.mogrify('%s', [param])
if __name__ == '__main__':
main()
$ python test1.py
0 -> 0
[0] -> ARRAY[0]
(0,) -> (0)
'' -> E''
[''] -> ARRAY[E'']
('',) -> (E'')
None -> NULL
[None] -> ARRAY[NULL]
(None,) ->
Traceback (most recent call last):
File "/root/tmp/test1.py", line 12, in <module>
main()
File "/root/tmp/test1.py", line 9, in main
print '%s\t->' % repr(param), cur.mogrify('%s', [param])
File "/usr/lib64/python2.6/site-packages/psycopg2/extensions.py", line 100, in getquoted
qobjs = [str(o.getquoted()) for o in pobjs]
AttributeError: 'str' object has no attribute 'getquoted'
落ちている場所は、
/usr/lib64/python2.6/site-packages/psycopg2/extensions.py:
# The SQL_IN class is the official adapter for tuples starting from 2.0.6.
class SQL_IN(object):
"""Adapt any iterable to an SQL quotable object."""
def __init__(self, seq):
self._seq = seq
def prepare(self, conn):
self._conn = conn
def getquoted(self):
# this is the important line: note how every object in the
# list is adapted and then how getquoted() is called on it
pobjs = [adapt(o) for o in self._seq]
for obj in pobjs:
if hasattr(obj, 'prepare'):
obj.prepare(self._conn)
qobjs = [str(o.getquoted()) for o in pobjs]
return '(' + ', '.join(qobjs) + ')'
__str__ = getquoted
register_adapter(tuple, SQL_IN)
print デバッグすると直ぐに分かるが、このとき o には 'NULL' という文字列(str 型)が入っている。当然、単なる文字列に getquoted なんてメソッドがあるはずがない。そりゃ落ちるわ。
何がどうあるべきなのかは分からないが、「動かないシステムに価値はない」の持論に従い、ともかく動くようにする。
test2.py:
#!/usr/bin/python
import psycopg2.extensions
# Fix a bug that can't mogrify `None' in a tuple to `NULL'
class FIXED_SQL_IN(object):
def __init__(self, seq):
self._seq = seq
def prepare(self, conn):
self._conn = conn
def getquoted(self):
adapt = psycopg2.extensions.adapt
pobjs = [adapt(o) for o in self._seq]
for obj in pobjs:
if hasattr(obj, 'prepare'):
obj.prepare(self._conn)
qobjs = [getattr(o, 'getquoted', o.__str__)() for o in pobjs]
return '(' + ', '.join(qobjs) + ')'
__str__ = getquoted
psycopg2.extensions.register_adapter(tuple, FIXED_SQL_IN)
def main():
conn = psycopg2.connect('dbname=testdb user=testuser')
cur = conn.cursor()
for param in [0, [0], (0,), '', [''], ('',), None, [None], (None,)]:
print '%s\t->' % repr(param), cur.mogrify('%s', [param])
if __name__ == '__main__':
main()
$ python test2.py
0 -> 0
[0] -> ARRAY[0]
(0,) -> (0)
'' -> E''
[''] -> ARRAY[E'']
('',) -> (E'')
None -> NULL
[None] -> ARRAY[NULL]
(None,) -> (NULL)
ということを、実は 2 年くらい前に RHEL5 + psycopg2-2.0.12 (from rpmforge)という組み合わせでやっていたのだが、最近になって検索すると次が出てきた。
- psycopg.lighthouseapp.com: #26 Failed adaptation for None in composite types
- psycopg2.git: Fixed adaptation of None in composite types (ticket #26).
本件は psycopg2-2.4 で直っている模様。しかし私が試した限り、この変更部分を 2.0 系に持ってきても動かない。None に対してアダプターを登録しても、全く動く気配がない。None に対するアダプターが動くようになる変更が 2.4 までの何処かに入ったのかも知れないが、私的にはもう上述の方法で解決しているのでこれ以上は調べる気なし。
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。