Python
PostgreSQL
Python3
psycopg2

psycopg2 のインストールとテストエラー

More than 1 year has passed since last update.

きっかけ

CodeIQ 「進捗まあまあマーク」問題 闇落ち解答版(現在未公開) 作成中に、
sqlite3 の parametarized query でパラメータの渡し方に不審なところがあった。

で、他ではどうだ? と考え、とりあえず PostgreSQL を試してみることにした。

Python から PostgreSQL にどうやって繋げるの?

ググって PythonからPostgreSQLに接続する方法 を見て「psycopg2」を入れることにした。

Psycopg2 のインストール

Psycopg2 のドキュメントをみて --no-binary オプションを付けてインストールすることに。

sudo python3 -m pip install --no-binary --allow-external psycopg2

python が python2 へのシンボリックリンクになっているので python3 コマンドで実行。--alow-external を付けないと入らなかったので付けた。

テストの実行

その後、うまくインストールされているか確認するために
Running the test suite
見て既存のローカルの PostgreSQL に接続してテストしようとしたがどうもうまくいかない。
既存DBを汚されるのも嫌なので psycopg2_test という DB を作成し、この DB には local から trust接続できるよう pg_hba.conf を設定し、Postgresql を restart。
環境変数 PSYCOPG2_TESTDB と PSYCOPG2_TESTDB_USER も設定したらテストが動いた。

python3 -c "from psycopg2 import tests; tests.unittest.main(defaultTest='tests.test_suite')" --verbose

テストで失敗

でテスト結果は・・・ Fail

======================================================================
FAIL: test_date_value_error_sec_59_99 (psycopg2.tests.test_dates.FromTicksTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib64/python3.6/site-packages/psycopg2/tests/test_dates.py", line 641, in test_date_value_error_sec_59_99
    self.assertEqual(s.adapted, date(2010, 5, 6))
AssertionError: datetime.date(2010, 5, 7) != datetime.date(2010, 5, 6)

----------------------------------------------------------------------

なんでやねん・・・って思ったがまぁロケールの問題やろと考えた。
とりあえず、対応するテストコードを確認

test_dates.py
    def test_date_value_error_sec_59_99(self):
        from datetime import date
        s = psycopg2.DateFromTicks(1273173119.99992)
        self.assertEqual(s.adapted, date(2010, 5, 6))

    def test_time_value_error_sec_59_99(self):
        from datetime import time
        s = psycopg2.TimeFromTicks(1273173119.99992)
        self.assertEqual(s.adapted.replace(hour=0),
            time(0, 11, 59, 999920))

この psycopg2.DateFromTicks はローカルの時間を返すので
日本(UTC +9)では psycopg2.DateFromTicks(1273173119.99992) は 2010年5月7日になる。

>>> import psycopg2
>>> psycopg2.DateFromTicks(1273173119.99992).adapted
datetime.date(2010, 5, 7)

なお、psycopg2.TimeFromTicks のテストの方も日本では pass するがインド(UTC +5:30)では Fail する。

とりあえず、issue に書き込み

まぁ、テストでどうでもいいことだろうけど一応 pyscopg の issue に登録した。

Test for "DateFromTicks" and "TimeFromTicks" are failing in some region.

psycopg のパラメータ渡しのテスト。。。

で、肝心のパラメータ渡しのテスト。

>>> import psycopg2
>>> conn = psycopg2.connect("dbname=postgres user=naruto password=naruoka01a!")
>>> cur = conn.cursor()
>>> strSQL = "SELECT :1, :2;"
>>> cur.execute(strSQL,(1,2));
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
psycopg2.ProgrammingError: syntax error at or near ":"
LINE 1: SELECT :1, :2;
               ^

psycopg2 はテストしたかったパラメータのプレースフォルダの形式(:1)に対応していませんでしたっ orz

psycopg2 のSQL のパラメータのプレースフォルダは %s で名前付きの場合は %(name)s です。

>>> conn.rollback()
>>> cur.close()
>>> cur = conn.cursor()
>>> strSQL = "SELECT %s, %s;"
>>> cur.execute(strSQL,(1,2));
>>> cur.fetchall();
[(1, 2)]
>>> strSQL = "SELECT %(a)s, %(b)s;"
>>> cur.execute(strSQL,{'a':1, 'b':2})
>>> cur.fetchall()
[(1, 2)]
>>> strSQL = "SELECT %(b)s, %(a)s;"
>>> cur.execute(strSQL,{'a':1, 'b':2})
>>> cur.fetchall()
[(2, 1)]
>>> cur.close()
>>> conn.close()