From 65302b070828694de19af82d6bf91bed343d29bb Mon Sep 17 00:00:00 2001 From: Jonathan Stacks Date: Sat, 5 Apr 2025 20:42:20 +0100 Subject: [PATCH] Add ability to set an int64 file control (#1298) * Add ability to set an int64 file control * Update documentation * Remove duplicate err check in test * Update sqlite3.go Co-authored-by: rittneje --------- Co-authored-by: rittneje --- sqlite3.go | 31 +++++++++++++++++++++++++++++++ sqlite3_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/sqlite3.go b/sqlite3.go index 3025a50..a967cab 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -1876,6 +1876,9 @@ func (c *SQLiteConn) SetLimit(id int, newVal int) int { // This method is not thread-safe as the returned error code can be changed by // another call if invoked concurrently. // +// Use SetFileControlInt64 instead if the argument for the opcode is documented +// as a pointer to a sqlite3_int64. +// // See: sqlite3_file_control, https://www.sqlite.org/c3ref/file_control.html func (c *SQLiteConn) SetFileControlInt(dbName string, op int, arg int) error { if dbName == "" { @@ -1893,6 +1896,34 @@ func (c *SQLiteConn) SetFileControlInt(dbName string, op int, arg int) error { return nil } +// SetFileControlInt64 invokes the xFileControl method on a given database. The +// dbName is the name of the database. It will default to "main" if left blank. +// The op is one of the opcodes prefixed by "SQLITE_FCNTL_". The arg argument +// and return code are both opcode-specific. Please see the SQLite documentation. +// +// This method is not thread-safe as the returned error code can be changed by +// another call if invoked concurrently. +// +// Only use this method if the argument for the opcode is documented as a pointer +// to a sqlite3_int64. +// +// See: sqlite3_file_control, https://www.sqlite.org/c3ref/file_control.html +func (c *SQLiteConn) SetFileControlInt64(dbName string, op int, arg int64) error { + if dbName == "" { + dbName = "main" + } + + cDBName := C.CString(dbName) + defer C.free(unsafe.Pointer(cDBName)) + + cArg := C.sqlite3_int64(arg) + rv := C.sqlite3_file_control(c.db, cDBName, C.int(op), unsafe.Pointer(&cArg)) + if rv != C.SQLITE_OK { + return c.lastError() + } + return nil +} + // Close the statement. func (s *SQLiteStmt) Close() error { s.mu.Lock() diff --git a/sqlite3_test.go b/sqlite3_test.go index 94de738..ed8fa64 100644 --- a/sqlite3_test.go +++ b/sqlite3_test.go @@ -1864,6 +1864,32 @@ func TestSetFileControlInt(t *testing.T) { }) } +func TestSetFileControlInt64(t *testing.T) { + const GiB = 1024 * 1024 * 1024 + + t.Run("", func(t *testing.T) { + + sql.Register("sqlite3_FCNTL_SIZE_LIMIT", &SQLiteDriver{ + ConnectHook: func(conn *SQLiteConn) error { + if err := conn.SetFileControlInt64("", SQLITE_FCNTL_SIZE_LIMIT, 4*GiB); err != nil { + return fmt.Errorf("Unexpected error from SetFileControlInt64(): %w", err) + } + return nil + }, + }) + + db, err := sql.Open("sqlite3", "file:/dbname?vfs=memdb") + if err != nil { + t.Fatal("Failed to open database:", err) + } + err = db.Ping() + if err != nil { + t.Fatal("Failed to ping", err) + } + db.Close() + }) +} + func TestNonColumnString(t *testing.T) { db, err := sql.Open("sqlite3", ":memory:") if err != nil {