SQLite3
前回、plotlyでrangeselectorでワンクリックで表示範囲を変更する方法を紹介しました。
今回はちょっと苦しめられたSQLite3でのエスケープ処理を解説していきます。
PythonでSQLite3を使うと文字列としてコマンドを入力するため、ダブルクォーテーションやシングルクォーテーションで囲みます。
そうするとPythonの文字列としてのダブルクォーテーションやシングルクォーテーションとSQLite3の文字列としてのダブルクォーテーションやシングルクォーテーションと干渉してしまい、エラーとなってしまうことが多々ありました。
ということでどういう場合に正常に処理でき、どういう場合にエラーとなるのか、またダブルクォーテーションやシングルクォーテーションをSQLite3の文字列として使いたい場合のエスケープ処理に関して色々と試してみたというのが今回のお話です。
それでは始めていきましょう。
準備と基本のプログラム
まずは準備と基本のプログラムからです。
準備としてデータベースとテーブルを作成し、文字列を1つ保存してみます。
テーブルの作成やデータの保存は何度もやるとエラーとなるので、別のセルで一度だけ実行するようにします。
import sqlite3
with sqlite3.connect('text1.db') as conn:
cur = conn.cursor()
cur.execute('CREATE TABLE test(id INTEGER PRIMARY KEY, text TEXT)')
conn.commit()
with sqlite3.connect('text1.db') as conn:
cur = conn.cursor()
cur.execute('INSERT INTO test(id, text) values(0, "abcde")')
conn.commit()
with sqlite3.connect('text1.db') as conn:
cur = conn.cursor()
data = cur.execute('SELECT * FROM test').fetchall()
print(data)
実行結果
[(0, 'abcde')]
上記の例はPythonの文字列にはシングルクォーテーション、SQLite3の文字列にはダブルクォーテーションを使った場合で問題なく処理できています。
エラーとなる例とその対処
まずはSQLite3の文字列中に文字列としてダブルクォーテーションやシングルクォーテーションを入れようとしてエラーとなる例です。
with sqlite3.connect('text1.db') as conn:
cur = conn.cursor()
cur.execute('INSERT INTO test(id, text) values(1, "abc'de")')
conn.commit()
実行結果
Input In [8]
cur.execute('INSERT INTO test(id, text) values(1, "abc'de")')
^
SyntaxError: unterminated string literal (detected at line 3)
with sqlite3.connect('text1.db') as conn:
cur = conn.cursor()
cur.execute('INSERT INTO test(id, text) values(1, "abc"de")')
conn.commit()
実行結果
---------------------------------------------------------------------------
OperationalError Traceback (most recent call last)
Input In [9], in <cell line: 1>()
1 with sqlite3.connect('text1.db') as conn:
2 cur = conn.cursor()
----> 3 cur.execute('INSERT INTO test(id, text) values(1, "abc"de")')
4 conn.commit()
OperationalError: near "de": syntax error
どちらもSyntax errorが出ていますが、エラーの出どころが違うようです。
最初の例ではPythonの文字列として終端していない(閉じられていない)ため、実行すらされていません。
2番目の例では実行はされていますが、SQLite3として「”abc”de”」だと「”abc”」までが文字列として認識され、「de”」が何か分からない状態となってしまいエラーとなってしまったようです。
文字列中にダブルクォーテーションやシングルクォーテーションを入れたい場合、エスケープ処理が必要になります。
SQLite3のダブルクォーテーションやシングルクォーテーションのエスケープ処理は、ダブルクォーテーションやシングルクォーテーションを重ねることでエスケープ処理となります。
with sqlite3.connect('text1.db') as conn:
cur = conn.cursor()
cur.execute('INSERT INTO test(id, text) values(1, "abc""de")')
conn.commit()
with sqlite3.connect('text1.db') as conn:
cur = conn.cursor()
cur.execute("INSERT INTO test(id, text) values(2, 'abc''de')")
conn.commit()
with sqlite3.connect('text1.db') as conn:
cur = conn.cursor()
data = cur.execute('SELECT * FROM test').fetchall()
print(data)
実行結果
[(0, 'abcde'), (1, 'abc"de'), (2, "abc'de")]
注意点1:Pythonとは違うものを使う
ただしここで注意です。
上記2例のようにPythonの文字列に使用したダブルクォーテーションやシングルクォーテーションとは違うものを用いてください。
つまりPythonの文字列でシングルクォーテーションを使った場合は、SQLite3の文字列ではダブルクォーテーションがエスケープでき、逆にPythonの文字列でダブルクォーテーションを使った場合は、SQLite3の文字列ではシングルクォーテーションがエスケープできます。
Pythonの文字列と同じものをエスケープしようとすると単純にPythonの文字列が二つ並んだだけとなり、SQLite3のエスケープ処理としては働きません。
with sqlite3.connect('text1.db') as conn:
cur = conn.cursor()
cur.execute('INSERT INTO test(id, text) values(3, "abc''de")')
conn.commit()
with sqlite3.connect('text1.db') as conn:
cur = conn.cursor()
cur.execute("INSERT INTO test(id, text) values(4, 'abc""de')")
conn.commit()
with sqlite3.connect('text1.db') as conn:
cur = conn.cursor()
data = cur.execute('SELECT * FROM test').fetchall()
print(data)
実行結果
[(0, 'abcde'), (1, 'abc"de'), (2, "abc'de"), (3, 'abcde'), (4, 'abcde')]
注意点2:ダブルクォーテーション、シングルクォーテーション自体がエスケープ文字ではない
もう一つの注意点としてはダブルクォーテーション、シングルクォーテーション自体がエスケープ文字ではないということです。
あくまでもダブルクォーテーションやシングルクォーテーションを2つ並べることで一つがエスケープ文字として、もう一つが文字列として認識されるということです。
つまりダブルクォーテーション+シングルクォーテーションの並びではエスケープできませんし、その逆でもエスケープできません。
with sqlite3.connect('text1.db') as conn:
cur = conn.cursor()
cur.execute('INSERT INTO test(id, text) values(5, "abc'"de")')
conn.commit()
実行結果
Input In [22]
cur.execute('INSERT INTO test(id, text) values(5, "abc'"de")')
^
SyntaxError: unterminated string literal (detected at line 3)
with sqlite3.connect('text1.db') as conn:
cur = conn.cursor()
cur.execute('INSERT INTO test(id, text) values(5, "abc"'de")')
conn.commit()
実行結果
Input In [24]
cur.execute('INSERT INTO test(id, text) values(5, "abc"'de")')
^
SyntaxError: unterminated string literal (detected at line 3)
Python上でSQLite3を使うとダブルクォーテーションとシングルクォーテーションの使い分けが重要になってくるようです。
もしかしたらもっと良い処理方法があるかもしれないので、見つけたら記事を書きたいと思います。
次回はPandasのデータフレームをそのままHTMLに変換する方法を紹介します。
ではでは今回はこんな感じで。
コメント