Skip to content

Commit

Permalink
Support disabling checkpoint-on-close
Browse files Browse the repository at this point in the history
Because Go doesn't support calling varadic C functions, this required a
wrapper.
  • Loading branch information
otoolep committed Jun 12, 2024
1 parent 0c35ea2 commit 1fd986c
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 0 deletions.
16 changes: 16 additions & 0 deletions sqlite3.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ _sqlite3_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
}
static int
_sqlite3_db_config_no_ckpt_on_close(sqlite3 *db) {
int v;
return sqlite3_db_config(db, SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, 1, &v);
}
#include <stdio.h>
#include <stdint.h>
Expand Down Expand Up @@ -1898,6 +1904,16 @@ func (c *SQLiteConn) SetFileControlInt(dbName string, op int, arg int) error {
return nil
}

// DBConfigNoCkptOnClose disables checkpointing on database close.
// See http://sqlite.org/c3ref/db_config.html
func (c *SQLiteConn) DBConfigNoCkptOnClose() error {
rv := C._sqlite3_db_config_no_ckpt_on_close(c.db)
if rv != C.SQLITE_OK {
return c.lastError()
}
return nil
}

// Close the statement.
func (s *SQLiteStmt) Close() error {
s.mu.Lock()
Expand Down
87 changes: 87 additions & 0 deletions sqlite3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package sqlite3

import (
"bytes"
"context"
"database/sql"
"database/sql/driver"
"errors"
Expand Down Expand Up @@ -1864,6 +1865,92 @@ func TestSetFileControlInt(t *testing.T) {
})
}

func TestDBConfigNoCkptOnClose(t *testing.T) {
fname := TempFilename(t)
defer os.Remove(fname)
db, err := sql.Open("sqlite3", fname)
if err != nil {
t.Fatal(err)
}
defer db.Close()

// Enable WAL mode.
if _, err := db.Exec(`PRAGMA journal_mode = wal`); err != nil {
t.Fatal(err)
}

// Write some data.
_, err = db.Exec("create table foo (department integer, profits integer)")
if err != nil {
t.Fatal("Failed to create table:", err)
}

// Confirm WAL file exists.
if _, err := os.Stat(fname + "-wal"); err != nil {
t.Fatal("Expected WAL file to exist", err)
}

// Close the database, and confirm WAL file is removed.
if err := db.Close(); err != nil {
t.Fatal("Failed to close database", err)
}
if _, err := os.Stat(fname + "-wal"); err == nil {
t.Fatal("Expected WAL file to be removed after close")
}

// Now do it again, but with the DBConfig option set.
db, err = sql.Open("sqlite3", fname)
if err != nil {
t.Fatal(err)
}
defer db.Close()

// Insert a record, confirm a WAL file appears.
if _, err := db.Exec(`insert into foo values (1, 2)`); err != nil {
t.Fatal(err)
}
if _, err := os.Stat(fname + "-wal"); err != nil {
t.Fatal("Expected WAL file to exist", err)
}

// Disable checkpoint-on-close.
f := func(driverConn interface{}) error {
c := driverConn.(*SQLiteConn)
return c.DBConfigNoCkptOnClose()
}
conn, err := db.Conn(context.Background())
if err != nil {
t.Fatal(err)
}
if err := conn.Raw(f); err != nil {
t.Fatal(err)
}

// Read the SQLite file into a byte slice for comparison later.
bufPre, err := os.ReadFile(fname)
if err != nil {
t.Fatal(err)
}

// Close the database, and confirm WAL file is still present.
if err := db.Close(); err != nil {
t.Fatal("Failed to close database", err)
}
if _, err := os.Stat(fname + "-wal"); err != nil {
t.Fatal("Expected WAL file to be present after close", err)
}

// Confirm the SQLite file is the same as before since no checkpoint
// was performed.
bufPost, err := os.ReadFile(fname)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(bufPre, bufPost) {
t.Fatal("Expected SQLite file to be unchanged after close")
}
}

func TestNonColumnString(t *testing.T) {
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
Expand Down

0 comments on commit 1fd986c

Please sign in to comment.