1
0
Fork 0

rqlite doesn't (yet) support WAL mode SQLite data

master
Philip O'Toole 1 year ago
parent d7c47d8080
commit 4a7ac2850f

@ -105,6 +105,29 @@ func IsValidSQLiteData(b []byte) bool {
return len(b) > 13 && string(b[0:13]) == "SQLite format"
}
// IsWALModeEnabledSQLiteFile checks that the supplied path looks like a SQLite
// with WAL mode enabled.
func IsWALModeEnabledSQLiteFile(path string) bool {
f, err := os.Open(path)
if err != nil {
return false
}
defer f.Close()
b := make([]byte, 20)
if _, err := f.Read(b); err != nil {
return false
}
return IsWALModeEnabled(b)
}
// IsWALModeEnabled checks that the supplied data looks like a SQLite data
// with WAL mode enabled.
func IsWALModeEnabled(b []byte) bool {
return len(b) >= 20 && b[18] == 2 && b[19] == 2
}
// Open opens a file-based database, creating it if it does not exist. After this
// function returns, an actual SQLite file will always exist.
func Open(dbPath string, fkEnabled bool) (*DB, error) {

@ -38,6 +38,68 @@ func Test_IsValidSQLiteOnDisk(t *testing.T) {
}
}
func Test_IsWALModeEnablednDiskDELETE(t *testing.T) {
path := mustTempFile()
defer os.Remove(path)
dsn := fmt.Sprintf("file:%s", path)
db, err := sql.Open("sqlite3", dsn)
if err != nil {
t.Fatalf("failed to create SQLite database: %s", err.Error())
}
_, err = db.Exec("CREATE TABLE foo (name TEXT)")
if err != nil {
t.Fatalf("failed to create table: %s", err.Error())
}
if err := db.Close(); err != nil {
t.Fatalf("failed to close database: %s", err.Error())
}
if IsWALModeEnabledSQLiteFile(path) {
t.Fatalf("non WAL file marked as WAL")
}
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("failed to read SQLite file: %s", err.Error())
}
if IsWALModeEnabled(data) {
t.Fatalf("non WAL data marked as WAL")
}
}
func Test_IsWALModeEnablednDiskWAL(t *testing.T) {
path := mustTempFile()
defer os.Remove(path)
dsn := fmt.Sprintf("file:%s", path)
db, err := sql.Open("sqlite3", dsn)
if err != nil {
t.Fatalf("failed to create SQLite database: %s", err.Error())
}
_, err = db.Exec("CREATE TABLE foo (name TEXT)")
if err != nil {
t.Fatalf("failed to create table: %s", err.Error())
}
_, err = db.Exec("PRAGMA journal_mode=WAL")
if err != nil {
t.Fatalf("failed to enable WAL mode: %s", err.Error())
}
if err := db.Close(); err != nil {
t.Fatalf("failed to close database: %s", err.Error())
}
if !IsWALModeEnabledSQLiteFile(path) {
t.Fatalf("WAL file marked as non-WAL")
}
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("failed to read SQLite file: %s", err.Error())
}
if !IsWALModeEnabled(data) {
t.Fatalf("WAL data marked as non-WAL")
}
}
func Test_FileCreationOnDisk(t *testing.T) {
dir := t.TempDir()
dbPath := path.Join(dir, "test_db")

@ -858,6 +858,14 @@ func (s *Service) handleLoad(w http.ResponseWriter, r *http.Request) {
lr := &command.LoadRequest{
Data: b,
}
if db.IsWALModeEnabled(b) {
s.logger.Printf("SQLite database file is in WAL mode - rejecting load request")
http.Error(w, `SQLite database file is in WAL mode - convert it to DELETE mode via 'PRAGMA journal_mode=DELETE'`,
http.StatusBadRequest)
return
}
err := s.store.Load(lr)
if err != nil && err != store.ErrNotLeader {
http.Error(w, err.Error(), http.StatusInternalServerError)

@ -310,6 +310,10 @@ func (s *Store) SetRestorePath(path string) error {
if !sql.IsValidSQLiteFile(path) {
return fmt.Errorf("file %s is not a valid SQLite file", path)
}
if sql.IsWALModeEnabledSQLiteFile(path) {
return fmt.Errorf("file %s is in WAL mode - convert to DELETE mode", path)
}
s.RegisterReadyChannel(s.restoreDoneCh)
s.restorePath = path
return nil

Loading…
Cancel
Save