From 5d8e064651cb52df54b5fb1b23359dbadcc29630 Mon Sep 17 00:00:00 2001 From: Jonathan Stacks Date: Fri, 22 Nov 2024 13:11:48 -0600 Subject: [PATCH 1/4] Add ability to set an int64 file control --- sqlite3.go | 28 ++++++++++++++++++++++++++++ sqlite3_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/sqlite3.go b/sqlite3.go index ce985ec8..4a7eb4e6 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -1893,6 +1893,34 @@ func (c *SQLiteConn) SetFileControlInt(dbName string, op int, arg int) error { return nil } +// SetFileControlInt 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. +// +// Prefer this method over SetFileControlInt when the argument to the underlying +// SQLite function is an 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 63c939d3..e3356c09 100644 --- a/sqlite3_test.go +++ b/sqlite3_test.go @@ -1864,6 +1864,35 @@ 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) + } + if err != nil { + t.Fatal("Failed to open", 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 { From fe25834d6b54c8e0da705e96f0674f3251e0b134 Mon Sep 17 00:00:00 2001 From: Jonathan Stacks Date: Fri, 22 Nov 2024 15:39:01 -0600 Subject: [PATCH 2/4] Update documentation --- sqlite3.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sqlite3.go b/sqlite3.go index 4a7eb4e6..27561649 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 == "" { @@ -1901,8 +1904,8 @@ func (c *SQLiteConn) SetFileControlInt(dbName string, op int, arg int) error { // This method is not thread-safe as the returned error code can be changed by // another call if invoked concurrently. // -// Prefer this method over SetFileControlInt when the argument to the underlying -// SQLite function is an int64. +// 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 { From d74f08189f2056eb2cc31c1e4257c0db9c279990 Mon Sep 17 00:00:00 2001 From: Jonathan Stacks Date: Fri, 22 Nov 2024 15:41:24 -0600 Subject: [PATCH 3/4] Remove duplicate err check in test --- sqlite3_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/sqlite3_test.go b/sqlite3_test.go index e3356c09..522f49c5 100644 --- a/sqlite3_test.go +++ b/sqlite3_test.go @@ -1882,9 +1882,6 @@ func TestSetFileControlInt64(t *testing.T) { if err != nil { t.Fatal("Failed to open database:", err) } - if err != nil { - t.Fatal("Failed to open", err) - } err = db.Ping() if err != nil { t.Fatal("Failed to ping", err) From 3e990904b326294c2846aebc03ef4ac8b7c98b2f Mon Sep 17 00:00:00 2001 From: Jonathan Stacks Date: Mon, 25 Nov 2024 10:50:39 -0600 Subject: [PATCH 4/4] Update sqlite3.go Co-authored-by: rittneje --- sqlite3.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlite3.go b/sqlite3.go index 27561649..811587ed 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -1896,7 +1896,7 @@ func (c *SQLiteConn) SetFileControlInt(dbName string, op int, arg int) error { return nil } -// SetFileControlInt invokes the xFileControl method on a given database. The +// 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.