我们通过 github.com/mattn/go-sqlite3 v1.14.16 在 Go 中使用 SQLite 3.39.4。连接字符串为
":memory:?cache=shared&mode=rwc&_mutex=no&_journal=WAL&_sync=NORMAL"
我们偶尔会在测试中看到错误“没有这样的表:配置”。令人困惑的是,有问题的查询已经在测试用例中成功使用,并且该表随后显示在 sqlite_master 的查询中,无论是在数据库连接还是引发错误的事务对象中。但是,一旦发生错误,查询就不会再成功。
我意识到这是一个相当模糊的问题,但有人至少可以建议去哪里看吗?数据库连接始终具有相同的指针值。
更新:
第二次尝试时,我几乎可以在这个 SSCCE 中重现问题:
package main
import (
"database/sql"
"fmt"
_ "github.com/mattn/go-sqlite3"
"os"
)
func main() {
os.Remove("example.db")
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
panic(err)
}
for _, s := range []string{
"CREATE TABLE if not exists Configuration (" +
"`id` varchar(1024) NOT NULL," +
"`body` varchar(10240) DEFAULT NULL, " +
"PRIMARY KEY (id) " +
")",
"INSERT INTO Configuration (id, body) VALUES ('some-unique-value', 'another-unique-value')",
} {
_, err = db.Exec(s)
if err != nil {
panic(err)
}
}
for i := 0; i < 10; i++ {
tx, err := db.Begin()
if err != nil {
panic(err)
}
q, err := tx.Prepare("select Configuration.id, Configuration.body \n\t\t\tfrom Configuration\n\t\t\tWHERE Configuration.id = ? ")
fmt.Println(i, err)
if q != nil {
_ = q.Close()
}
fmt.Println("tx:")
showTables(tx)
fmt.Println("db:")
showTables(db)
tx.Commit()
}
}
func showTables(db interface {
Query(query string, args ...interface{}) (*sql.Rows, error)
}) {
r, err := db.Query("SELECT name FROM sqlite_master")
if err != nil {
panic(err)
}
for r.Next() {
var name string
_ = r.Scan(&name)
fmt.Println(name)
}
}
但与实际问题不同,showTables 不显示 SSCCE 中的表,而是在实际测试中显示。此示例显示了使用
:memory:
而不是
example.db
的问题,但前提是交易未关闭。这是内存数据库已知的或预期的行为吗?database/sql
隐式使用连接池,但
:memory:
数据库默认对于打开它们的连接来说是私有。 由于在幕后创建/关闭多个 SQLite 连接,因此您并不真正知道哪个连接正在运行每个语句以及针对哪个数据库,因此很自然,某些连接可以看到某些数据,而其他连接则看不到。
共享连接的一种方法是使用
cache=shared
,正如您所尝试的那样。但 SQLite 要求将其指定为 URI:
file::memory:?cache=shared
(file:
很重要)。打开共享内存数据库的更可靠方法是:
file:memdb1?mode=memory&cache=shared
memdb1
)file:/memdb1?vfs=memdb
/memdb1
,并使用memdb
VFS)