为什么内存中的 sqlite 数据库 table 创建失败?

Why is table creation failing for an in-memory sqlite database?

我正在尝试使用 twisted.enterprise.adbapi.ConnectionPool 创建内存中的 sqlite 数据库。

问题描述:

以下代码按预期工作:

#! /usr/bin/env python
from twisted.internet.task import react
from twisted.internet import defer
from twisted.enterprise.adbapi import ConnectionPool

sql_init = """
    CREATE TABLE ajxp_changes ( seq INTEGER PRIMARY KEY AUTOINCREMENT, node_id NUMERIC, type TEXT, source TEXT, target TEXT, deleted_md5 TEXT );
    CREATE TABLE ajxp_index ( node_id INTEGER PRIMARY KEY AUTOINCREMENT, node_path TEXT, bytesize NUMERIC, md5 TEXT, mtime NUMERIC, stat_result BLOB);

    CREATE TRIGGER LOG_INSERT AFTER INSERT ON ajxp_index BEGIN INSERT INTO ajxp_changes (node_id,source,target,type) VALUES (new.node_id, "NULL", new.node_path, "create"); END;
    """

sql_insert = "INSERT INTO ajxp_index (node_path,bytesize,md5,mtime,stat_result) VALUES (?,?,?,?,?);"

sql_file_path = "/tmp/test.sqlite"

@react
@defer.inlineCallbacks
def main(reactor):
    cp = ConnectionPool("sqlite3", sql_file_path, check_same_thread=False)
    yield cp.runInteraction(lambda c, s: c.executescript(s), sql_init)

    params = (
        "/tmp/test.txt",
        "64",
        "5d41402abc4b2a76b9719d911017c592",
        2832345,
        "xxxxxx"
    )
    yield cp.runOperation(sql_insert, params)

但是,将 sql_file_path="/tmp/test.sqlite 替换为 sql_file_path=":memory:" 突然导致脚本失败,回溯如下:

$ python test.py 
main function encountered error
Traceback (most recent call last):
  File "/Users/lthibault/.pyenv/versions/3.5.3/lib/python3.5/site-packages/twisted/internet/defer.py", line 500, in errback
    self._startRunCallbacks(fail)
  File "/Users/lthibault/.pyenv/versions/3.5.3/lib/python3.5/site-packages/twisted/internet/defer.py", line 567, in _startRunCallbacks
    self._runCallbacks()
  File "/Users/lthibault/.pyenv/versions/3.5.3/lib/python3.5/site-packages/twisted/internet/defer.py", line 653, in _runCallbacks
    current.result = callback(current.result, *args, **kw)
  File "/Users/lthibault/.pyenv/versions/3.5.3/lib/python3.5/site-packages/twisted/internet/defer.py", line 1357, in gotResult
    _inlineCallbacks(r, g, deferred)
--- <exception caught here> ---
  File "/Users/lthibault/.pyenv/versions/3.5.3/lib/python3.5/site-packages/twisted/internet/defer.py", line 1299, in _inlineCallbacks
    result = result.throwExceptionIntoGenerator(g)
  File "/Users/lthibault/.pyenv/versions/3.5.3/lib/python3.5/site-packages/twisted/python/failure.py", line 393, in throwExceptionIntoGenerator
    return g.throw(self.type, self.value, self.tb)
  File "test.py", line 35, in main
    yield cp.runOperation(sql_insert, params)
  File "/Users/lthibault/.pyenv/versions/3.5.3/lib/python3.5/site-packages/twisted/python/threadpool.py", line 250, in inContext
    result = inContext.theWork()
  File "/Users/lthibault/.pyenv/versions/3.5.3/lib/python3.5/site-packages/twisted/python/threadpool.py", line 266, in <lambda>
    inContext.theWork = lambda: context.call(ctx, func, *args, **kw)
  File "/Users/lthibault/.pyenv/versions/3.5.3/lib/python3.5/site-packages/twisted/python/context.py", line 122, in callWithContext
    return self.currentContext().callWithContext(ctx, func, *args, **kw)
  File "/Users/lthibault/.pyenv/versions/3.5.3/lib/python3.5/site-packages/twisted/python/context.py", line 85, in callWithContext
    return func(*args,**kw)
  File "/Users/lthibault/.pyenv/versions/3.5.3/lib/python3.5/site-packages/twisted/enterprise/adbapi.py", line 477, in _runInteraction
    compat.reraise(excValue, excTraceback)
  File "/Users/lthibault/.pyenv/versions/3.5.3/lib/python3.5/site-packages/twisted/python/compat.py", line 467, in reraise
    raise exception.with_traceback(traceback)
  File "/Users/lthibault/.pyenv/versions/3.5.3/lib/python3.5/site-packages/twisted/enterprise/adbapi.py", line 467, in _runInteraction
    result = interaction(trans, *args, **kw)
  File "/Users/lthibault/.pyenv/versions/3.5.3/lib/python3.5/site-packages/twisted/enterprise/adbapi.py", line 486, in _runOperation
    trans.execute(*args, **kw)
sqlite3.OperationalError: no such table: ajxp_index

我试过的

1。在标准库中复制

我首先试图确定问题是否与sqlite有关,或者是否与twisted有关。为此,我 运行 以下脚本, 其行为符合预期

#! /usr/bin/env python
import sqlite3

conn = sqlite3.connect(":memory:")
conn.executescript(sql_init)
conn.execute(
    sql_insert,
    ("/tmp/test.txt", "64", "5d41402abc4b2a76b9719d911017c592", 2832345, "xxxxxx"),
)

结论:问题源于twisted.enterprise.adbapi.ConnectionPool

2。尝试使用不同的 ConnectionPool 方法来 运行 INSERT 语句。

诚然,此时我正在抓住救命稻草,但我认为问题可能源于我对 runOperation 的使用。我决定使用 runInteractionrunQuery.

复制原始示例

yield cp.runOperation(sql_insert, params) 的以下替换也失败并出现相同的错误

重要的是,将 sqlite 数据库路径从 :memory: 更改为持久存储上的某个路径,runInteractionrunQuery 都按预期工作。

结论:问题与在 Twisted 中使用内存中的 sqlite 数据库有关。

有什么想法吗?

好的,事实证明,在幕后,ConnectionPool 每次调用查询方法时都试图连接到 :memory:,因此每次都重新创建数据库。

解决方案似乎是编写一个 DB-API v.20 模块来包装 sqlite3 并在调用其连接函数时始终返回相同的 :memory: 连接。