gologiusの巣

プログラミングなどの技術メモです。誰かの役に立てるとうれしいです。

【SQLite+Python】 on conflict で syntax error(near "on") になる場合は、SQLiteのバージョンを疑う

備忘録なんで、厳密性は求めないでほしい。

問題

  • SQLitePython経由で使う
  • on conflict 構文を使う 場合に
near "on": syntax error

などと出て動作しない

原因

  • エラーに書いてある通り、構文が間違っている
  • SQLiteの実行バージョンが古く、on conflict の構文に対応していない

のどちらかかと思われる。

私が解決した方法

前者は頑張ってバグ取りしましょう。

後者が若干面倒な模様。

DBファイル側にはバージョンという概念がないらしく、 バージョンアップはsqliteを叩く側のライブラリのバージョンを上げる必要がある。

私の場合はPythonの標準モジュールsqlite3から叩いていたが、 標準モジュールなので、pip install sqlite3 などでのバージョンアップができない模様。

方法としては、

の二択になる。

面倒なので、私の場合はPythonのバージョンを上げることで対応した (厳密にいうと、Anaconda環境でバージョンアップしようとすると、 諸々の操作が原因で環境崩壊したので、Anaconda環境の入れ直しをしている)

SQLiteのバージョンについて

DBファイル側にも一応バージョンを保持する項目がある。 96byte目。

https://www.sqlite.org/fileformat.html

ただ、私がバイナリエディタで覗いたファイルは、値はセットされていなかった。 恐らくアプリ側がファイル生成時にセットするかどうかに依存しているのだと思う。 ちなみに除いたファイルは DB Browser for SQLiteで作ったファイル。

そもそも、SQLite自体が2050年までの長期サポートを目標にしてるそうなので、 ファイルにバージョン(依存性)を持たせることはしてないのだと思われる。

Long Term Support

余談

いわゆるUPSERTをしたかったのでon conflictを使った。

insert or replace into でも代用できるが、「UPDATEの場合には更新しないでほしい項目がある」 場合などには、全部上書きされてしまうので使えない。

もっと具体例をいうと、レコードに「レコード作成日」「レコード更新日」を持たせる場合に、 insert or replace into だと「レコード作成日」が上書きで消えてしまう。

トリガーで実装する方法もあるが、私の環境・プログラムだと重すぎて使いものにならなかったので、 on conflictを今回は採用した。

おしまい。