From 052e7d9d99097ac226871c52616f640d753472c9 Mon Sep 17 00:00:00 2001 From: Paul Komkoff Date: Thu, 23 Apr 2026 23:39:57 +0200 Subject: [PATCH] sqlite: add Conn.Serialize wrapping sqlite3_serialize Returns the complete contents of a named database as a Go-owned []byte, freeing the SQLite-allocated buffer before returning. Co-Authored-By: Claude Sonnet 4.6 --- backup.go | 21 +++++++++++++++++++++ backup_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/backup.go b/backup.go index e7dfe7f..0d1473b 100644 --- a/backup.go +++ b/backup.go @@ -18,6 +18,7 @@ package sqlite // #include import "C" import ( + "fmt" "runtime" "unsafe" ) @@ -131,3 +132,23 @@ func (b *Backup) Remaining() int { func (b *Backup) PageCount() int { return int(C.sqlite3_backup_pagecount(b.ptr)) } + +// Serialize returns a byte slice containing the complete contents of the +// named database (use "" or "main" for the main database). The returned +// slice is a Go-owned copy; the caller may use it freely. +// +// https://www.sqlite.org/c3ref/serialize.html +func (c *Conn) Serialize(dbName string) ([]byte, error) { + cname := cmain + if dbName != "" && dbName != "main" { + cname = C.CString(dbName) + defer C.free(unsafe.Pointer(cname)) + } + var size C.sqlite3_int64 + p := C.sqlite3_serialize(c.conn, cname, &size, 0) + if p == nil { + return nil, fmt.Errorf("sqlite.Conn.Serialize: out of memory or unsupported") + } + defer C.sqlite3_free(unsafe.Pointer(p)) + return C.GoBytes(unsafe.Pointer(p), C.int(size)), nil +} diff --git a/backup_test.go b/backup_test.go index 1d0a43f..f11866c 100644 --- a/backup_test.go +++ b/backup_test.go @@ -15,6 +15,7 @@ package sqlite_test import ( + "os" "testing" "github.com/go-llsqlite/crawshaw" @@ -35,6 +36,41 @@ func initSrc(t *testing.T) *sqlite.Conn { return conn } +func TestSerialize(t *testing.T) { + conn := initSrc(t) + defer conn.Close() + + data, err := conn.Serialize("") + if err != nil { + t.Fatal(err) + } + + f, err := os.CreateTemp("", "serialize-test-*.sqlite3") + if err != nil { + t.Fatal(err) + } + defer os.Remove(f.Name()) + if _, err := f.Write(data); err != nil { + f.Close() + t.Fatal(err) + } + f.Close() + + conn2, err := sqlite.OpenConn(f.Name(), 0) + if err != nil { + t.Fatal(err) + } + defer conn2.Close() + + count, err := sqlitex.ResultInt(conn2.Prep(`SELECT count(*) FROM t;`)) + if err != nil { + t.Fatal(err) + } + if count != 2 { + t.Fatalf("expected 2 rows in serialized DB, got %d", count) + } +} + func TestBackup(t *testing.T) { conn := initSrc(t) defer conn.Close()