diff --git a/.github/workflows/go.yaml b/.github/workflows/go.yaml index c96bf31a..0cb89569 100644 --- a/.github/workflows/go.yaml +++ b/.github/workflows/go.yaml @@ -44,7 +44,7 @@ jobs: run: go-acc . -- -race -v -tags "libsqlite3" - name: 'Tags: full' - run: go-acc . -- -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_column_metadata sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_math_functions sqlite_os_trace sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_unlock_notify sqlite_userauth sqlite_vacuum_incr sqlite_vtable" + run: go-acc . -- -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_column_metadata sqlite_dbpage sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_math_functions sqlite_os_trace sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_unlock_notify sqlite_userauth sqlite_vacuum_incr sqlite_vtable" - name: 'Tags: vacuum' run: go-acc . -- -race -v -tags "sqlite_vacuum_full" @@ -99,7 +99,7 @@ jobs: - name: 'Tags: full' run: | echo 'skip this test' - echo go build -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_column_metadata sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_math_functions sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_unlock_notify sqlite_userauth sqlite_vacuum_incr sqlite_vtable" + echo go build -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_column_metadata sqlite_dbpage sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_math_functions sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_unlock_notify sqlite_userauth sqlite_vacuum_incr sqlite_vtable" shell: msys2 {0} - name: 'Tags: vacuum' diff --git a/README.md b/README.md index 3b43b033..837fa786 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,7 @@ go build -tags "icu json1 fts5 secure_delete" | Tracing / Debug | sqlite_trace | Activate trace functions | | User Authentication | sqlite_userauth | SQLite User Authentication see [User Authentication](#user-authentication) for more information. | | Virtual Tables | sqlite_vtable | SQLite Virtual Tables see [SQLite Official VTABLE Documentation](https://www.sqlite.org/vtab.html) for more information, and a [full example here](https://github.com/mattn/go-sqlite3/tree/master/_example/vtable) | +| SQLITE_DBPAGE Virtual Table | sqlite_dbpage | SQLITE_DBPAGE Virtual Table see [SQLite Official SQLITE_DBPAGE Documentation](https://www.sqlite.org/dbpage.html) for more information. | # Compilation diff --git a/sqlite3_opt_dbpage.go b/sqlite3_opt_dbpage.go new file mode 100644 index 00000000..25e8e61d --- /dev/null +++ b/sqlite3_opt_dbpage.go @@ -0,0 +1,15 @@ +// Copyright (C) 2019 Yasuhiro Matsumoto . +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +//go:build sqlite_dbpage +// +build sqlite_dbpage + +package sqlite3 + +/* +#cgo CFLAGS: -DSQLITE_ENABLE_DBPAGE_VTAB +#cgo LDFLAGS: -lm +*/ +import "C" diff --git a/sqlite3_opt_dbpage_test.go b/sqlite3_opt_dbpage_test.go new file mode 100644 index 00000000..6b6735b3 --- /dev/null +++ b/sqlite3_opt_dbpage_test.go @@ -0,0 +1,89 @@ +// Copyright (C) 2019 Yasuhiro Matsumoto . +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +//go:build cgo +// +build cgo + +package sqlite3 + +import ( + "database/sql" + "os" + "testing" +) + +func TestDbpage(t *testing.T) { + sourceFilename := TempFilename(t) + defer os.Remove(sourceFilename) + + destFilename := TempFilename(t) + defer os.Remove(destFilename) + + db, err := sql.Open("sqlite3", sourceFilename) + if err != nil { + t.Fatal("Failed to open database:", err) + } + defer db.Close() + + if _, err = db.Exec("PRAGMA journal_mode=WAL"); err != nil { + t.Fatal("Failed to Exec PRAGMA journal_mode:", err) + } else if _, err := db.Exec("CREATE TABLE foo(data TEXT)"); err != nil { + t.Fatal("Failed to create table:", err) + } else if _, err := db.Exec("INSERT INTO foo(data) VALUES(?)", "hello sqlite_dbpage"); err != nil { + t.Fatal("Failed to create table:", err) + } + + rows, err := db.Query("SELECT data FROM sqlite_dbpage") + if err != nil && err.Error() == "no such table: sqlite_dbpage" { + t.Skip("sqlite_dbpage not supported") + } else if err != nil { + t.Fatal("Unable to query sqlite_dbpage table:", err) + } + defer rows.Close() + + destFile, err := os.OpenFile(destFilename, os.O_CREATE|os.O_WRONLY, 0700) + if err != nil { + t.Fatal("Unable to open file for writing:", err) + } + defer destFile.Close() + + for rows.Next() { + var page []byte + if err := rows.Scan(&page); err != nil { + t.Fatal("Unable to scan results:", err) + } + + if _, err := destFile.Write(page); err != nil { + t.Fatal("Unable to write page to file:", err) + } + } + if err := rows.Close(); err != nil { + t.Fatal("Unable to close rows:", err) + } else if err := db.Close(); err != nil { + t.Fatal("Unable to close database:", err) + } else if err := destFile.Close(); err != nil { + t.Fatal("Unable to close file:", err) + } + + db, err = sql.Open("sqlite3", destFilename) + if err != nil { + t.Fatal("Failed to open database:", err) + } + defer db.Close() + + var result string + if err = db.QueryRow("PRAGMA integrity_check").Scan(&result); err != nil { + t.Fatal("Failed to query PRAGMA integrity_check:", err) + } else if result != "ok" { + t.Fatal("Copied database integrity check failed:", result) + } + + var hello string + if err = db.QueryRow("SELECT data FROM foo").Scan(&hello); err != nil { + t.Fatal("Failed to query data:", err) + } else if hello != "hello sqlite_dbpage" { + t.Fatal("Unable to find expected data:", hello) + } +}