在SQLite查询中使用变量值的标准方法是“问号样式”,如下所示:
import sqlite3 with sqlite3.connect(":memory:") as connection: connection.execute("CREATE TABLE foo(bar)") connection.execute("INSERT INTO foo(bar) VALUES (?)", ("cow",)) print(list(connection.execute("SELECT * from foo"))) # prints [(u'cow',)]
但是,这仅适用于将值替换为查询。当用于表名或列名时失败:
import sqlite3 with sqlite3.connect(":memory:") as connection: connection.execute("CREATE TABLE foo(?)", ("bar",)) # raises sqlite3.OperationalError: near "?": syntax error
sqlite3模块和PEP 249均未提及用于转义名称或值的功能。大概是为了阻止用户使用字符串来组合他们的查询,但这使我无所适从。
sqlite3
什么功能或技术最适合在SQLite中为列或表使用变量名?我强烈希望能够在没有任何其他依赖项的情况下做到这一点,因为我将在自己的包装器中使用它。
我寻找了但找不到关于SQLite语法相关部分的清晰完整的描述,以用于编写自己的函数。我想确保这对于SQLite允许的任何标识符都适用,所以对我来说,试错法还是不确定的。
SQLite用来"引用标识符,但是我不确定仅转义标识符就足够了。PHPsqlite_escape_string函数的文档表明,某些二进制数据可能也需要转义,但这可能是PHP库的一个怪癖。
"
sqlite_escape_string
要将任何字符串转换为SQLite标识符:
""
import codecs def quote_identifier(s, errors="strict"): encodable = s.encode("utf-8", errors).decode("utf-8") nul_index = encodable.find("\x00") if nul_index >= 0: error = UnicodeEncodeError("NUL-terminated utf-8", encodable, nul_index, nul_index + 1, "NUL not allowed") error_handler = codecs.lookup_error(errors) replacement, _ = error_handler(error) encodable = encodable.replace("\x00", replacement) return "\"" + encodable.replace("\"", "\"\"") + "\""
给定一个字符串单个参数,它将转义并正确引用它或引发异常。第二个参数可以被用来指定在注册的任何错误处理程序的codecs模块。内置的是:
codecs
'strict':在编码错误的情况下引发异常 'replace':用适当的替换标记替换格式错误的数据,例如'?'或'\ufffd' 'ignore':忽略格式错误的数据,并继续进行,恕不另行通知 'xmlcharrefreplace':替换为适当的XML字符参考(仅用于编码) 'backslashreplace':替换为反斜杠转义序列(仅用于编码)
'strict'
'replace'
'?'
'\ufffd'
'ignore'
'xmlcharrefreplace'
'backslashreplace'
这不会检查保留的标识符,因此,如果您尝试创建一个新SQLITE_MASTER表,它不会阻止您。
SQLITE_MASTER
import sqlite3 def test_identifier(identifier): "Tests an identifier to ensure it's handled properly." with sqlite3.connect(":memory:") as c: c.execute("CREATE TABLE " + quote_identifier(identifier) + " (foo)") assert identifier == c.execute("SELECT name FROM SQLITE_MASTER").fetchone()[0] test_identifier("'Héllo?'\\\n\r\t\"Hello!\" -☃") # works test_identifier("北方话") # works test_identifier(chr(0x20000)) # works print(quote_identifier("Fo\x00o!", "replace")) # prints "Fo?o!" print(quote_identifier("Fo\x00o!", "ignore")) # prints "Foo!" print(quote_identifier("Fo\x00o!")) # raises UnicodeEncodeError print(quote_identifier(chr(0xD800))) # raises UnicodeEncodeError
SQLite标识符是TEXT,不是二进制。
TEXT
str
bytes
SQLite标识符使用双引号引起来。
SQLite标识符中的双引号以两个双引号转义。
SQLite标识符保留大小写,但对ASCII字母不区分大小写。可以启用可识别unicode的大小写。
SQLite不支持字符串或标识符中的NUL字符。
sqlite3只要可以将其正确编码为UTF-8,就可以处理任何其他unicode字符串。无效的字符串可能会导致Python 3.0和Python 3.1.2或其附近崩溃。Python 2接受了这些无效的字符串,但这被认为是一个错误。