mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 05:59:14 +00:00
Integrity.
This commit is contained in:
@@ -34,7 +34,7 @@ Go, wazero and [`x/sys`](https://pkg.go.dev/golang.org/x/sys) are the _only_ run
|
||||
- [`github.com/ncruces/go-sqlite3/ext/blobio`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/blobio)
|
||||
simplifies [incremental BLOB I/O](https://sqlite.org/c3ref/blob_open.html).
|
||||
- [`github.com/ncruces/go-sqlite3/ext/bloom`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/bloom)
|
||||
provides the [`bloom_filter`](https://github.com/nalgeon/sqlean/issues/27#issuecomment-1002267134) virtual table.
|
||||
provides a [Bloom filter](https://github.com/nalgeon/sqlean/issues/27#issuecomment-1002267134) virtual table.
|
||||
- [`github.com/ncruces/go-sqlite3/ext/csv`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/csv)
|
||||
reads [comma-separated values](https://sqlite.org/csv.html).
|
||||
- [`github.com/ncruces/go-sqlite3/ext/fileio`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/fileio)
|
||||
|
||||
@@ -229,6 +229,7 @@ func (c *conn) Raw() *sqlite3.Conn {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
// Deprecated: use BeginTx instead.
|
||||
func (c *conn) Begin() (driver.Tx, error) {
|
||||
return c.BeginTx(context.Background(), driver.TxOptions{})
|
||||
}
|
||||
@@ -559,19 +560,20 @@ func (r *rows) Next(dest []driver.Value) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *rows) decodeTime(i int, v any) (_ time.Time, _ bool) {
|
||||
func (r *rows) decodeTime(i int, v any) (_ time.Time, ok bool) {
|
||||
if r.tmRead == sqlite3.TimeFormatDefault {
|
||||
return
|
||||
}
|
||||
switch r.declType(i) {
|
||||
case "DATE", "TIME", "DATETIME", "TIMESTAMP":
|
||||
// maybe
|
||||
default:
|
||||
// handled by maybeTime
|
||||
return
|
||||
}
|
||||
switch v.(type) {
|
||||
case int64, float64, string:
|
||||
// maybe
|
||||
// could be a time value
|
||||
default:
|
||||
return
|
||||
}
|
||||
switch r.declType(i) {
|
||||
case "DATE", "TIME", "DATETIME", "TIMESTAMP":
|
||||
// could be a time value
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -15,7 +15,6 @@ import (
|
||||
|
||||
"github.com/dchest/siphash"
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
"github.com/ncruces/go-sqlite3/internal/util"
|
||||
)
|
||||
|
||||
// Register registers the bloom_filter virtual table:
|
||||
@@ -161,6 +160,41 @@ func (b *bloom) Rename(new string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *bloom) ShadowTables() {}
|
||||
|
||||
func (t *bloom) Integrity(schema, table string, flags int) error {
|
||||
load, _, err := t.db.Prepare(fmt.Sprintf(
|
||||
`SELECT typeof(data), length(data), p, n, m, k FROM %s.%s WHERE rowid = 1`,
|
||||
sqlite3.QuoteIdentifier(t.schema), sqlite3.QuoteIdentifier(t.storage)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("bloom: %v", err) // can't wrap!
|
||||
}
|
||||
defer load.Close()
|
||||
|
||||
err = errors.New("bloom: invalid parameters")
|
||||
if !load.Step() {
|
||||
return err
|
||||
}
|
||||
if t := load.ColumnText(0); t != "blob" {
|
||||
return err
|
||||
}
|
||||
if m := load.ColumnInt64(4); m <= 0 || m%8 != 0 {
|
||||
return err
|
||||
} else if load.ColumnInt64(1) != m/8 {
|
||||
return err
|
||||
}
|
||||
if p := load.ColumnFloat(2); p <= 0 || p >= 1 {
|
||||
return err
|
||||
}
|
||||
if n := load.ColumnInt64(3); n <= 0 {
|
||||
return err
|
||||
}
|
||||
if k := load.ColumnInt(5); k <= 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *bloom) BestIndex(idx *sqlite3.IndexInfo) error {
|
||||
for n, cst := range idx.Constraint {
|
||||
if cst.Usable && cst.Column == 1 &&
|
||||
@@ -274,8 +308,6 @@ func (c *cursor) Column(ctx *sqlite3.Context, n int) error {
|
||||
ctx.ResultBool(true)
|
||||
case 1:
|
||||
ctx.ResultValue(*c.arg)
|
||||
default:
|
||||
panic(util.AssertErr())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -127,4 +127,14 @@ func Test_compatible(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = db.Exec(`PRAGMA integrity_check`)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = db.Exec(`PRAGMA quick_check`)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "include.h"
|
||||
#include "sqlite3.h"
|
||||
|
||||
#define SQLITE_VTAB_CREATOR_GO /******/ 0x01
|
||||
#define SQLITE_VTAB_DESTROYER_GO /****/ 0x02
|
||||
#define SQLITE_VTAB_UPDATER_GO /******/ 0x04
|
||||
#define SQLITE_VTAB_RENAMER_GO /******/ 0x08
|
||||
#define SQLITE_VTAB_OVERLOADER_GO /***/ 0x10
|
||||
#define SQLITE_VTAB_CHECKER_GO /******/ 0x20
|
||||
#define SQLITE_VTAB_TXN_GO /**********/ 0x40
|
||||
#define SQLITE_VTAB_SAVEPOINTER_GO /**/ 0x80
|
||||
#define SQLITE_VTAB_CREATOR_GO /******/ 0x001
|
||||
#define SQLITE_VTAB_DESTROYER_GO /****/ 0x002
|
||||
#define SQLITE_VTAB_UPDATER_GO /******/ 0x004
|
||||
#define SQLITE_VTAB_RENAMER_GO /******/ 0x008
|
||||
#define SQLITE_VTAB_OVERLOADER_GO /***/ 0x010
|
||||
#define SQLITE_VTAB_CHECKER_GO /******/ 0x020
|
||||
#define SQLITE_VTAB_TXN_GO /**********/ 0x040
|
||||
#define SQLITE_VTAB_SAVEPOINTER_GO /**/ 0x080
|
||||
#define SQLITE_VTAB_SHADOWTABS_GO /***/ 0x100
|
||||
|
||||
int go_vtab_create(sqlite3_module *, int argc, const char *const *argv,
|
||||
sqlite3_vtab **, char **pzErr);
|
||||
@@ -157,6 +159,8 @@ static int go_vtab_integrity_wrapper(sqlite3_vtab *pVTab, const char *zSchema,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int go_vtab_shadown_name_wrapper(const char *zName) { return 1; }
|
||||
|
||||
int sqlite3_create_module_go(sqlite3 *db, const char *zName, int flags,
|
||||
go_handle handle) {
|
||||
struct go_module *mod = malloc(sizeof(struct go_module));
|
||||
@@ -208,6 +212,9 @@ int sqlite3_create_module_go(sqlite3 *db, const char *zName, int flags,
|
||||
mod->base.xRelease = go_vtab_release;
|
||||
mod->base.xRollbackTo = go_vtab_rollback_to;
|
||||
}
|
||||
if (flags & SQLITE_VTAB_SHADOWTABS_GO) {
|
||||
mod->base.xShadowName = go_vtab_shadown_name_wrapper;
|
||||
}
|
||||
if (mod->base.xCreate && !mod->base.xDestroy) {
|
||||
mod->base.xDestroy = mod->base.xDisconnect;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
# Go `"adiantum"` SQLite VFS
|
||||
# Go `adiantum` SQLite VFS
|
||||
|
||||
This package wraps an SQLite VFS to offer encryption at rest.
|
||||
|
||||
> [!WARNING]
|
||||
> This work was not certified by a cryptographer.
|
||||
> If you need vetted encryption, you should purchase the
|
||||
> [SQLite Encryption Extension](https://sqlite.org/see),
|
||||
> and either wrap it, or seek assistance wrapping it.
|
||||
|
||||
The `"adiantum"` VFS wraps the default SQLite VFS using the
|
||||
[Adiantum](https://github.com/lukechampine/adiantum)
|
||||
tweakable and length-preserving encryption.\
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Go `"memdb"` SQLite VFS
|
||||
# Go `memdb` SQLite VFS
|
||||
|
||||
This package implements the [`"memdb"`](https://sqlite.org/src/doc/tip/src/memdb.c)
|
||||
SQLite VFS in pure Go.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Go `"reader"` SQLite VFS
|
||||
# Go `reader` SQLite VFS
|
||||
|
||||
This package implements a `"reader"` SQLite VFS
|
||||
that allows accessing any [`io.ReaderAt`](https://pkg.go.dev/io#ReaderAt)
|
||||
|
||||
31
vtab.go
31
vtab.go
@@ -16,14 +16,15 @@ func CreateModule[T VTab](db *Conn, name string, create, connect VTabConstructor
|
||||
var flags int
|
||||
|
||||
const (
|
||||
VTAB_CREATOR = 0x01
|
||||
VTAB_DESTROYER = 0x02
|
||||
VTAB_UPDATER = 0x04
|
||||
VTAB_RENAMER = 0x08
|
||||
VTAB_OVERLOADER = 0x10
|
||||
VTAB_CHECKER = 0x20
|
||||
VTAB_TXN = 0x40
|
||||
VTAB_SAVEPOINTER = 0x80
|
||||
VTAB_CREATOR = 0x001
|
||||
VTAB_DESTROYER = 0x002
|
||||
VTAB_UPDATER = 0x004
|
||||
VTAB_RENAMER = 0x008
|
||||
VTAB_OVERLOADER = 0x010
|
||||
VTAB_CHECKER = 0x020
|
||||
VTAB_TXN = 0x040
|
||||
VTAB_SAVEPOINTER = 0x080
|
||||
VTAB_SHADOWTABS = 0x100
|
||||
)
|
||||
|
||||
if create != nil {
|
||||
@@ -52,6 +53,9 @@ func CreateModule[T VTab](db *Conn, name string, create, connect VTabConstructor
|
||||
if implements[VTabSavepointer](vtab) {
|
||||
flags |= VTAB_SAVEPOINTER
|
||||
}
|
||||
if implements[VTabShadowTabler](vtab) {
|
||||
flags |= VTAB_SHADOWTABS
|
||||
}
|
||||
|
||||
defer db.arena.mark()()
|
||||
namePtr := db.arena.string(name)
|
||||
@@ -174,6 +178,17 @@ type VTabOverloader interface {
|
||||
FindFunction(arg int, name string) (ScalarFunction, IndexConstraintOp)
|
||||
}
|
||||
|
||||
// A VTabShadowTabler allows a virtual table to protect the content
|
||||
// of shadow tables from being corrupted by hostile SQL.
|
||||
//
|
||||
// Implementing this interface signals that a virtual table named
|
||||
// "mumble" reserves all table names starting with "mumble_".
|
||||
type VTabShadowTabler interface {
|
||||
VTab
|
||||
// https://sqlite.org/vtab.html#the_xshadowname_method
|
||||
ShadowTables()
|
||||
}
|
||||
|
||||
// A VTabChecker allows a virtual table to report errors
|
||||
// to the PRAGMA integrity_check and PRAGMA quick_check commands.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user