fix🐛: only treat exact in-memory forms as in-memory sqlite sources

A file whose name merely contained :memory: or mode=memory was classified as an
in-memory database and skipped the locking defaults. Parse the query and match
the exact filename forms or a mode=memory parameter.
This commit is contained in:
zhangwenjian
2026-10-07 17:40:49 +08:00
parent b082ea5d85
commit 8a1218c404
2 changed files with 24 additions and 3 deletions
+16 -3
View File
@@ -30,10 +30,9 @@ var sqlitePragmas = [][2]string{
}
// sqliteDSN returns source with the defaults in sqlitePragmas added.
// In-memory databases are returned untouched: WAL has no meaning there, and
// every connection to one is its own database anyway.
// In-memory databases are returned untouched: WAL has no meaning there.
func sqliteDSN(source string) string {
if source == "" || strings.Contains(source, ":memory:") || strings.Contains(source, "mode=memory") {
if source == "" {
return source
}
@@ -46,6 +45,9 @@ func sqliteDSN(source string) string {
// Not something to rewrite blind; the driver will report it.
return source
}
if isMemoryDB(path, q) {
return source
}
for _, p := range sqlitePragmas {
if _, set := q[p[0]]; !set {
q.Set(p[0], p[1])
@@ -54,6 +56,17 @@ func sqliteDSN(source string) string {
return path + "?" + q.Encode()
}
// isMemoryDB reports whether the source names an in-memory database: the exact
// filename forms sqlite recognises, or a mode=memory parameter. A file that
// merely has ":memory:" or "mode=memory" somewhere in its name is a file.
func isMemoryDB(path string, q url.Values) bool {
switch path {
case ":memory:", "file::memory:", "file:":
return true
}
return q.Get("mode") == "memory"
}
// withSqliteDefaults wraps open so every connection string handed to a sqlite3
// dialector - the primary and any replicas - carries the defaults above.
func withSqliteDefaults(driver string, open func(string) gorm.Dialector) func(string) gorm.Dialector {
+8
View File
@@ -36,6 +36,14 @@ func TestSqliteDSN(t *testing.T) {
}
})
t.Run("files that only look like memory databases get the defaults", func(t *testing.T) {
for _, s := range []string{"mode=memory.db", "/tmp/:memory:.db", "data/mode=memory/app.db"} {
if got := sqliteDSN(s); got == s {
t.Errorf("sqliteDSN(%q) was left untouched", s)
}
}
})
t.Run("in-memory and empty untouched", func(t *testing.T) {
for _, s := range []string{"", ":memory:", "file::memory:?cache=shared", "file:x?mode=memory"} {
if got := sqliteDSN(s); got != s {