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

时间:2017-05-15 11:52:52

标签: python sqlite twisted in-memory-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有关的问题,还是要扭曲。为此,我运行了以下脚本,其行为符合预期

#! /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) 的以下替换也会失败并出现相同的错误

  • yield cp.runInteraction(lambda c, s, p: c.execute(s), sql_insert, params)
  • yield cp.runQuery(sql_insert, params)

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

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

有什么想法吗?

1 个答案:

答案 0 :(得分:0)

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

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