sqlite3.ProgrammingError という名前が付いていますが、この例外を投げているのは SQLite ではありません。
sqlite3.ProgrammingError: Binding 1 (':value') is a named parameter, but you supplied a sequence which requires nameless (qmark) placeholders.
該当するのは、名前つきの placeholder に tuple や list を渡しているコードです。
connection.execute("SELECT :value", (42,)) # 3.14 で ProgrammingError
3.13 では同じ行が動きます。Python 3.14 は、名前つき placeholder に名前を持たない引数(tuple・list)を渡す組み合わせを、警告からエラーに変えました。 直すには、placeholder の名前をキーにした dict を渡します。
connection.execute("SELECT :value", {"value": 42}) # 3.11〜3.14 すべてで通る
名前つき placeholder は :name だけではありません。 SQLite は :name / @name / $name の 3 つを名前つきとして扱い、3 つとも同じように落ちます(実測)。: だけを検索して直すと、@ と $ が残ります。
3.12・3.13 は、落ちる版を名指しして予告していた
この組み合わせは 3.12 から DeprecationWarning になっていて、その警告文は落ちる版まで名指ししていました。
DeprecationWarning: Binding 1 (':value') is a named parameter, but you supplied a sequence
which requires nameless (qmark) placeholders.
Starting with Python 3.14 an sqlite3.ProgrammingError will be raised.
3.14 のエラー文は、この警告文から最後の一文(予告)を取り除いたものです。
| Python | execute("SELECT :value", (42,)) |
|---|---|
| 3.11 | 通る(警告なし) |
| 3.12 | DeprecationWarning(上の全文)を出して通る(終了コード 0) |
| 3.13 | 同上(終了コード 0) |
| 3.14 | sqlite3.ProgrammingError(終了コード 1) |
この予告が届いたかどうかは、その execute() がどのモジュールにあったかで決まります。CPython の既定の警告フィルタは、DeprecationWarning を発生元が __main__ のときだけ表示し、それ以外のモジュールでは抑制するからです(既定の警告フィルタ)。app.py に直接書いた execute() なら 3.13 は -W なしでも警告を出していましたが(実測)、SQL はたいていリポジトリ層やモデルのモジュールにあるので、そこで出る分は既定では 1 行も表示されません(実測)。
3.13 のうちに洗い出すなら、抑制を外して実行します。
python -W always::DeprecationWarning app.py
python -W always::DeprecationWarning -m pytest
警告は execute() を呼ぶたびに評価されるので、テストが通る経路の分はここで出そろいます(asyncio の同種の警告と違い、1 回目で状態が変わって黙るということはありません)。ただし、テストが踏まない SQL の行は出てきません。:・@・$ の検索と併用してください。
直し方:placeholder の名前をキーにした dict を渡す
# 落ちる
connection.execute("INSERT INTO t VALUES (:a, :b)", [1, 2])
# 通る
connection.execute("INSERT INTO t VALUES (:a, :b)", {"a": 1, "b": 2})
この修正は前方にも後方にも安全です。dict を渡す形は 3.11 から 3.14 まで、どの版でも同じように通ります(実測)。3.13 のまま先に直しておいて構いません。
executemany() も同じ検査を通ります。 行のリストを tuple で渡していると、同じ ProgrammingError になります。
# 落ちる
connection.executemany("INSERT INTO t VALUES (:a, :b)", [(1, 2), (3, 4)])
# 通る
connection.executemany("INSERT INTO t VALUES (:a, :b)", [{"a": 1, "b": 2}, {"a": 3, "b": 4}])
dict に余分なキーが混ざっているのは 3.14 でも許されます(不足しているときだけ、You did not supply a value for binding parameter :value. という別のエラーになります。これは以前からの動作です)。つまり、行を表す dict をそのまま渡し、SQL 側で使う名前だけを拾わせる書き方は、これまでどおり使えます。
落ちる組み合わせ・落ちない組み合わせ
CPython の検査は、こう書かれています——placeholder に ? 以外で始まる名前が付いていて、引数が dict でなければ拒否する。SQLite が名前を付けるのは :name / @name / $name の 3 つで、?1 にも "?1" という名前が付きますが、CPython は ? で始まる名前を検査から外しています。だから ? と ?1 は tuple のままで構いません。
| SQL の placeholder | 渡す引数 | 3.13 | 3.14 |
|---|---|---|---|
:name | tuple / list | 通る(警告) | ProgrammingError |
@name | tuple / list | 通る(警告) | ProgrammingError |
$name | tuple / list | 通る(警告) | ProgrammingError |
:name / @name / $name | dict | 通る | 通る |
? | tuple / list | 通る | 通る |
?1(番号つき) | tuple / list | 通る | 通る |
? | dict | ProgrammingError | ProgrammingError |
名前つきは 3 形あります。 :name だけを検索して置き換えると、@name と $name を使っている行が残ります(エラー文の Binding 1 (':value') の部分が、それぞれ '@value'・'$value' になります)。@ 記法は SQL Server 系から移してきたコードでよく残っています。
「tuple や list」も、正確には「dict でない sequence」です。 namedtuple を渡していても落ちます(実測)。逆に、dict のサブクラスは dict として扱われて通ります(実測)。
表の最下段にあるとおり、逆向きの組み合わせ(? に dict)は 3.11 でもエラーでした。「placeholder と引数の型が合わなければ拒否する」という規則自体は、以前からありました。片方向だけが見逃されていて、3.14 でそれが閉じた、という形です。
名前つきと ? が混ざった SQL は、引数を変えても直りません
1 つの SQL に名前つきと ? が両方あると、どちらの引数型でも拒否されます(実測)。
connection.execute("SELECT :a, ?", (1, 2)) # 3.14: Binding 1 (':a') is a named parameter ...
connection.execute("SELECT :a, ?", {"a": 1}) # 3.13 も 3.14 も: Binding 2 has no name, but you supplied a dictionary
sequence は :a の検査で弾かれ、dict は ? に名前が無いので弾かれます。引数の型を変えて直す道はありません。 SQL 側を、名前つきだけか ? だけかに統一してください。この形は 3.13 では tuple で通っていたので、3.14 で初めて表に出てきます。
SQLite を上げ下げしても直りません
エラーの名前が sqlite3.ProgrammingError なので、SQLite 側の変更に見えます。実際は違います。
公式 python イメージ | sqlite3.sqlite_version |
|---|---|
| 3.11 | 3.46.1 |
| 3.12 | 3.46.1 |
| 3.13 | 3.46.1 |
| 3.14 | 3.46.1 |
4 つとも同じ SQLite です。 挙動を変えたのは CPython の sqlite3 モジュールで、SQL を SQLite に渡す前の引数検査です。SQLite エンジンはこの引数を一度も見ていません。したがって、libsqlite3 を入れ替えても、sqlite3.sqlite_version を確認しても、この件は動きません。境界は CPython の版です。
(この表は公式 python イメージでの実測です。Linux の CPython は SQLite を同梱せず、その環境の libsqlite3 にリンクします——4 つで版が一致したのは、4 つのイメージが同じ Debian ベースだからです。Python の版が SQLite の版を決めているわけではありません(macOS・Windows のインストーラは同梱するので、版ごとに違います)。手元で sqlite3.sqlite_version が版ごとに違っても、上の結論は変わりません——CPython が SQLite に渡す前に弾いているからです。)
切り分け(うまくいかないとき)
- エラー文の
Binding 1の番号だけ変わって出続ける:SQL 内の別の placeholder が残っています。1 つのexecute()に複数の名前つき placeholder があると、最初に見つかった 1 つが報告されます。その SQL の引数を丸ごと dict に変えてください。 - dict に変えたら
Binding 2 has no name, but you supplied a dictionaryになった:その SQL に?が混ざっています。上の節のとおり、引数の型では直りません。SQL 側を統一してください。 - ORM やクエリビルダの中で落ちる:SQL と引数を組み立てているのはそのライブラリです。ライブラリが名前つき placeholder を生成しながら引数を tuple で渡していると、この検査に当たります。ライブラリを 3.14 対応版に上げる以外の回避策は、利用者側にはありません(SQL も引数もライブラリが作っているため)。上げられないなら 3.13 に留まることになります。自分のコードで
execute()を直接呼んでいる箇所を先に潰すと、残りがライブラリ由来だと切り分けられます。 You did not supply a value for binding parameter :value.に変わった:dict へ移行できていますが、キーが足りません。キーには記号を付けません(:value/@value/$valueのどれでも、キーは"value"です)。このエラーは 3.13 でも同じ文言で出るので、3.14 特有ではありません。Binding 1 has no name, but you supplied a dictionary:SQL 側が?なのに dict を渡しています。3.14 特有ではなく、3.11 でも同じエラーになります。- 3.13 では警告も出ない:呼んでいるモジュールが
__main__でないなら、既定では抑制されます。-W always::DeprecationWarningを付けてください。pytestならfilterwarningsの設定が警告を握りつぶしていることもあります。 - 3.14 の他の変更も先に知っておきたい:同じ 3.14 で、
multiprocessingの既定 start method とasyncio.get_event_loop()の暗黙生成 も、3.13 まで通っていた形が落ちるようになりました。
検証環境
python@sha256:b877e50b...(python:3.14-slim/ Python 3.14.6・Linux)、ネットワーク不要- 再現:
connection.execute("SELECT :value", (42,))がsqlite3.ProgrammingError: Binding 1 (':value') is a named parameterで終了コード 1 - 修正:
connection.execute("SELECT :value", {"value": 42})にすると終了コード 0
再現から修正までは errfix の検証ハーネスが機械的に確認しています。本文の版ごとの表(3.11 は無言で通す/3.12・3.13 は版を名指しした DeprecationWarning つきで通す/3.14 は ProgrammingError)、落ちる組み合わせの表(@name と $name も対象・?1 は対象外・namedtuple も sequence として落ちる・dict のサブクラスは通る・executemany も同じ検査・dict の余分なキーは許される)、名前つきと ? が混ざった SQL は tuple でも dict でも拒否されること、3.13 では -W なしでも __main__ からの呼び出しなら警告が出て、パッケージ側のモジュールからの呼び出しでは出ないこと、4 つのイメージとも sqlite3.sqlite_version が 3.46.1 であることは、いずれも公式 python イメージの 3.11・3.12・3.13・3.14 で実測しています。CPython の判定条件(? で始まる名前を検査から除外する)は CPython のソースに拠ります。