diff --git a/sqlite3.go b/sqlite3.go index 7506522..f731d20 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -990,6 +990,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { authPass := "" authCrypt := "" authSalt := "" + mutex := C.int(C.SQLITE_OPEN_FULLMUTEX) txlock := "BEGIN" // PRAGMA's @@ -1006,7 +1007,6 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { secureDelete := "DEFAULT" synchronousMode := "NORMAL" writableSchema := -1 - flags := C.SQLITE_OPEN_READWRITE | C.SQLITE_OPEN_CREATE | C.SQLITE_OPEN_FULLMUTEX pos := strings.IndexRune(dsn, '?') if pos >= 1 { @@ -1015,17 +1015,6 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { return nil, err } - if val := params.Get("mode"); val != "" { - switch val { - case "ro": - flags |= C.SQLITE_OPEN_READONLY - case "rw": - flags ^= C.SQLITE_OPEN_CREATE - case "rwc": - flags |= C.SQLITE_OPEN_CREATE - } - } - // Authentication if _, ok := params["_auth"]; ok { authCreate = true @@ -1060,9 +1049,9 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { if val := params.Get("_mutex"); val != "" { switch strings.ToLower(val) { case "no": - flags |= C.SQLITE_OPEN_NOMUTEX + mutex = C.SQLITE_OPEN_NOMUTEX case "full": - flags |= C.SQLITE_OPEN_FULLMUTEX + mutex = C.SQLITE_OPEN_FULLMUTEX default: return nil, fmt.Errorf("Invalid _mutex: %v", val) } @@ -1349,8 +1338,9 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { var db *C.sqlite3 name := C.CString(dsn) defer C.free(unsafe.Pointer(name)) - - rv := C._sqlite3_open_v2(name, &db, C.int(flags), nil) + rv := C._sqlite3_open_v2(name, &db, + mutex|C.SQLITE_OPEN_READWRITE|C.SQLITE_OPEN_CREATE, + nil) if rv != 0 { if db != nil { C.sqlite3_close_v2(db) diff --git a/sqlite3_test.go b/sqlite3_test.go index 0667893..16bbb10 100644 --- a/sqlite3_test.go +++ b/sqlite3_test.go @@ -87,6 +87,64 @@ func TestOpen(t *testing.T) { } } +func TestOpenNoCreate(t *testing.T) { + filename := t.Name() + ".sqlite" + + if err := os.Remove(filename); err != nil && !os.IsNotExist(err) { + t.Fatal(err) + } + defer os.Remove(filename) + + // https://golang.org/pkg/database/sql/#Open + // "Open may just validate its arguments without creating a connection + // to the database. To verify that the data source name is valid, call Ping." + db, err := sql.Open("sqlite3", fmt.Sprintf("file:%s?mode=rw", filename)) + if err == nil { + defer db.Close() + + err = db.Ping() + if err == nil { + t.Fatal("expected error from Open or Ping") + } + } + + sqlErr, ok := err.(Error) + if !ok { + t.Fatalf("expected sqlite3.Error, but got %T", err) + } + + if sqlErr.Code != ErrCantOpen { + t.Fatalf("expected SQLITE_CANTOPEN, but got %v", sqlErr) + } + + // make sure database file truly was not created + if _, err := os.Stat(filename); !os.IsNotExist(err) { + if err != nil { + t.Fatal(err) + } + t.Fatal("expected database file to not exist") + } + + // verify that it works if the mode is "rwc" instead + db, err = sql.Open("sqlite3", fmt.Sprintf("file:%s?mode=rwc", filename)) + if err != nil { + t.Fatal(err) + } + defer db.Close() + + if err := db.Ping(); err != nil { + t.Fatal(err) + } + + // make sure database file truly was created + if _, err := os.Stat(filename); err != nil { + if !os.IsNotExist(err) { + t.Fatal(err) + } + t.Fatal("expected database file to exist") + } +} + func TestReadonly(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename)