summaryrefslogtreecommitdiff
path: root/vendor
diff options
context:
space:
mode:
Diffstat (limited to 'vendor')
-rw-r--r--vendor/codeberg.org/gruf/go-mempool/README.md2
-rw-r--r--vendor/codeberg.org/gruf/go-mempool/pool.go146
-rw-r--r--vendor/codeberg.org/gruf/go-mempool/simple.go111
-rw-r--r--vendor/codeberg.org/gruf/go-mutexes/map.go23
-rw-r--r--vendor/codeberg.org/gruf/go-structr/index.go17
-rw-r--r--vendor/codeberg.org/gruf/go-structr/item.go18
-rw-r--r--vendor/codeberg.org/gruf/go-structr/list.go30
-rw-r--r--vendor/codeberg.org/gruf/go-structr/runtime.go10
-rw-r--r--vendor/codeberg.org/gruf/go-structr/timeline.go19
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/conn.go27
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/context.go16
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/driver/driver.go107
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/json.go2
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/json_v2.go52
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/json.go83
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/json_v2.go113
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/sqlite.go8
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/stmt.go40
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/time.go2
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/value.go23
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/const.go4
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/file.go68
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/lock.go2
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/memdb/README.md5
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/memdb/api.go19
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go22
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go2
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sync.go2
-rw-r--r--vendor/modules.txt12
29 files changed, 687 insertions, 298 deletions
diff --git a/vendor/codeberg.org/gruf/go-mempool/README.md b/vendor/codeberg.org/gruf/go-mempool/README.md
index af4cb6770..1acc23d26 100644
--- a/vendor/codeberg.org/gruf/go-mempool/README.md
+++ b/vendor/codeberg.org/gruf/go-mempool/README.md
@@ -1,3 +1,3 @@
# go-mempool
-very simple memory pool implementation \ No newline at end of file
+very simple memory pool implementation
diff --git a/vendor/codeberg.org/gruf/go-mempool/pool.go b/vendor/codeberg.org/gruf/go-mempool/pool.go
index e5ff6ba3d..5bb80234c 100644
--- a/vendor/codeberg.org/gruf/go-mempool/pool.go
+++ b/vendor/codeberg.org/gruf/go-mempool/pool.go
@@ -1,17 +1,17 @@
package mempool
import (
+ "sync"
+ "sync/atomic"
"unsafe"
-)
-const DefaultDirtyFactor = 128
+ "golang.org/x/sys/cpu"
+)
-// Pool provides a type-safe form
-// of UnsafePool using generics.
-//
-// Note it is NOT safe for concurrent
-// use, you must protect it yourself!
+// Pool provides a form of SimplePool
+// with the addition of concurrency safety.
type Pool[T any] struct {
+ UnsafePool
// New is an optionally provided
// allocator used when no value
@@ -21,79 +21,119 @@ type Pool[T any] struct {
// Reset is an optionally provided
// value resetting function called
// on passed value to Put().
- Reset func(T)
+ Reset func(T) bool
+}
- UnsafePool
+func NewPool[T any](new func() T, reset func(T) bool, check func(current, victim int) bool) Pool[T] {
+ return Pool[T]{
+ New: new,
+ Reset: reset,
+ UnsafePool: NewUnsafePool(check),
+ }
}
func (p *Pool[T]) Get() T {
if ptr := p.UnsafePool.Get(); ptr != nil {
return *(*T)(ptr)
- } else if p.New != nil {
- return p.New()
}
- var z T
- return z
+ var t T
+ if p.New != nil {
+ t = p.New()
+ }
+ return t
}
func (p *Pool[T]) Put(t T) {
- if p.Reset != nil {
- p.Reset(t)
+ if p.Reset != nil && !p.Reset(t) {
+ return
}
ptr := unsafe.Pointer(&t)
p.UnsafePool.Put(ptr)
}
-// UnsafePool provides an incredibly
-// simple memory pool implementation
-// that stores ptrs to memory values,
-// and regularly flushes internal pool
-// structures according to DirtyFactor.
-//
-// Note it is NOT safe for concurrent
-// use, you must protect it yourself!
+// UnsafePool provides a form of UnsafeSimplePool
+// with the addition of concurrency safety.
type UnsafePool struct {
+ internal
+ _ [cache_line_size - unsafe.Sizeof(internal{})%cache_line_size]byte
+}
+
+func NewUnsafePool(check func(current, victim int) bool) UnsafePool {
+ return UnsafePool{internal: internal{
+ pool: UnsafeSimplePool{Check: check},
+ }}
+}
- // DirtyFactor determines the max
- // number of $dirty count before
- // pool is garbage collected. Where:
- // $dirty = len(current) - len(victim)
- DirtyFactor int
+const (
+ // current platform integer size.
+ int_size = 32 << (^uint(0) >> 63)
- current []unsafe.Pointer
- victim []unsafe.Pointer
+ // platform CPU cache line size to avoid false sharing.
+ cache_line_size = unsafe.Sizeof(cpu.CacheLinePad{})
+)
+
+type internal struct {
+ // fast-access ring-buffer of
+ // pointers accessible by index.
+ //
+ // if Go ever exposes goroutine IDs
+ // to us we can make this a lot faster.
+ ring [int_size / 4]unsafe.Pointer
+ index atomic.Uint64
+
+ // underlying pool and
+ // slow mutex protection.
+ pool UnsafeSimplePool
+ mutex sync.Mutex
}
-func (p *UnsafePool) Get() unsafe.Pointer {
- // First try current list.
- if len(p.current) > 0 {
- ptr := p.current[len(p.current)-1]
- p.current = p.current[:len(p.current)-1]
- return ptr
+func (p *internal) Check(fn func(current, victim int) bool) func(current, victim int) bool {
+ p.mutex.Lock()
+ if fn == nil {
+ if p.pool.Check == nil {
+ fn = defaultCheck
+ } else {
+ fn = p.pool.Check
+ }
+ } else {
+ p.pool.Check = fn
}
+ p.mutex.Unlock()
+ return fn
+}
- // Fallback to victim.
- if len(p.victim) > 0 {
- ptr := p.victim[len(p.victim)-1]
- p.victim = p.victim[:len(p.victim)-1]
+func (p *internal) Get() unsafe.Pointer {
+ if ptr := atomic.SwapPointer(&p.ring[p.index.Load()%uint64(cap(p.ring))], nil); ptr != nil {
+ p.index.Add(^uint64(0)) // i.e. -1
return ptr
}
-
- return nil
+ p.mutex.Lock()
+ ptr := p.pool.Get()
+ p.mutex.Unlock()
+ return ptr
}
-func (p *UnsafePool) Put(ptr unsafe.Pointer) {
- p.current = append(p.current, ptr)
-
- // Get dirty factor.
- df := p.DirtyFactor
- if df == 0 {
- df = DefaultDirtyFactor
+func (p *internal) Put(ptr unsafe.Pointer) {
+ if atomic.CompareAndSwapPointer(&p.ring[p.index.Add(1)%uint64(cap(p.ring))], nil, ptr) {
+ return
}
+ p.mutex.Lock()
+ p.pool.Put(ptr)
+ p.mutex.Unlock()
+}
- if len(p.current)-len(p.victim) > df {
- // Garbage collection!
- p.victim = p.current
- p.current = nil
+func (p *internal) GC() {
+ for i := range p.ring {
+ atomic.StorePointer(&p.ring[i], nil)
}
+ p.mutex.Lock()
+ p.pool.GC()
+ p.mutex.Unlock()
+}
+
+func (p *internal) Size() int {
+ p.mutex.Lock()
+ sz := p.pool.Size()
+ p.mutex.Unlock()
+ return sz
}
diff --git a/vendor/codeberg.org/gruf/go-mempool/simple.go b/vendor/codeberg.org/gruf/go-mempool/simple.go
new file mode 100644
index 000000000..c9f459890
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-mempool/simple.go
@@ -0,0 +1,111 @@
+package mempool
+
+import (
+ "unsafe"
+)
+
+// SimplePool provides a type-safe form
+// of UnsafePool using generics.
+//
+// Note it is NOT safe for concurrent
+// use, you must protect it yourself!
+type SimplePool[T any] struct {
+ UnsafeSimplePool
+
+ // New is an optionally provided
+ // allocator used when no value
+ // is available for use in pool.
+ New func() T
+
+ // Reset is an optionally provided
+ // value resetting function called
+ // on passed value to Put().
+ Reset func(T) bool
+}
+
+func (p *SimplePool[T]) Get() T {
+ if ptr := p.UnsafeSimplePool.Get(); ptr != nil {
+ return *(*T)(ptr)
+ }
+ var t T
+ if p.New != nil {
+ t = p.New()
+ }
+ return t
+}
+
+func (p *SimplePool[T]) Put(t T) {
+ if p.Reset != nil && !p.Reset(t) {
+ return
+ }
+ ptr := unsafe.Pointer(&t)
+ p.UnsafeSimplePool.Put(ptr)
+}
+
+// UnsafeSimplePool provides an incredibly
+// simple memory pool implementation
+// that stores ptrs to memory values,
+// and regularly flushes internal pool
+// structures according to CheckGC().
+//
+// Note it is NOT safe for concurrent
+// use, you must protect it yourself!
+type UnsafeSimplePool struct {
+
+ // Check determines how often to flush
+ // internal pools based on underlying
+ // current and victim pool sizes. It gets
+ // called on every pool Put() operation.
+ //
+ // A flush will start a new current
+ // pool, make victim the old current,
+ // and drop the existing victim pool.
+ Check func(current, victim int) bool
+
+ current []unsafe.Pointer
+ victim []unsafe.Pointer
+}
+
+func (p *UnsafeSimplePool) Get() unsafe.Pointer {
+ // First try current list.
+ if len(p.current) > 0 {
+ ptr := p.current[len(p.current)-1]
+ p.current = p.current[:len(p.current)-1]
+ return ptr
+ }
+
+ // Fallback to victim.
+ if len(p.victim) > 0 {
+ ptr := p.victim[len(p.victim)-1]
+ p.victim = p.victim[:len(p.victim)-1]
+ return ptr
+ }
+
+ return nil
+}
+
+func (p *UnsafeSimplePool) Put(ptr unsafe.Pointer) {
+ p.current = append(p.current, ptr)
+
+ // Get GC check func.
+ if p.Check == nil {
+ p.Check = defaultCheck
+ }
+
+ if p.Check(len(p.current), len(p.victim)) {
+ p.GC() // garbage collection time!
+ }
+}
+
+func (p *UnsafeSimplePool) GC() {
+ p.victim = p.current
+ p.current = nil
+}
+
+func (p *UnsafeSimplePool) Size() int {
+ return len(p.current) + len(p.victim)
+}
+
+func defaultCheck(current, victim int) bool {
+ return current-victim > 128 || victim > 256
+}
diff --git a/vendor/codeberg.org/gruf/go-mutexes/map.go b/vendor/codeberg.org/gruf/go-mutexes/map.go
index 2f21ae0bb..e8c4d0058 100644
--- a/vendor/codeberg.org/gruf/go-mutexes/map.go
+++ b/vendor/codeberg.org/gruf/go-mutexes/map.go
@@ -26,14 +26,13 @@ const (
type MutexMap struct {
mapmu sync.Mutex
mumap hashmap
- mupool mempool.UnsafePool
+ mupool mempool.UnsafeSimplePool
}
// checkInit ensures MutexMap is initialized (UNSAFE).
func (mm *MutexMap) checkInit() {
if mm.mumap.m == nil {
mm.mumap.init(0)
- mm.mupool.DirtyFactor = 256
}
}
@@ -175,13 +174,9 @@ func (mu *rwmutex) Lock(lt uint8) bool {
// sleeping goroutines waiting on this mutex.
func (mu *rwmutex) Unlock() bool {
switch mu.l--; {
- case mu.l > 0 && mu.t == lockTypeWrite:
- panic("BUG: multiple writer locks")
- case mu.l < 0:
- panic("BUG: negative lock count")
-
case mu.l == 0:
- // Fully unlocked.
+ // Fully
+ // unlock.
mu.t = 0
// Awake all blocked goroutines and check
@@ -197,11 +192,15 @@ func (mu *rwmutex) Unlock() bool {
// (before == after) => (waiters = 0)
return (before == after)
- default:
- // i.e. mutex still
- // locked by others.
- return false
+ case mu.l < 0:
+ panic("BUG: negative lock count")
+ case mu.t == lockTypeWrite:
+ panic("BUG: multiple write locks")
}
+
+ // i.e. mutex still
+ // locked by others.
+ return false
}
// WaitRelock expects a mutex to be passed in, already in the
diff --git a/vendor/codeberg.org/gruf/go-structr/index.go b/vendor/codeberg.org/gruf/go-structr/index.go
index d5bd5562e..d8469577d 100644
--- a/vendor/codeberg.org/gruf/go-structr/index.go
+++ b/vendor/codeberg.org/gruf/go-structr/index.go
@@ -4,10 +4,10 @@ import (
"os"
"reflect"
"strings"
- "sync"
"unsafe"
"codeberg.org/gruf/go-byteutil"
+ "codeberg.org/gruf/go-mempool"
"codeberg.org/gruf/go-xunsafe"
)
@@ -371,17 +371,15 @@ type index_entry struct {
key string
}
-var index_entry_pool sync.Pool
+var index_entry_pool mempool.UnsafePool
// new_index_entry returns a new prepared index_entry.
func new_index_entry() *index_entry {
- v := index_entry_pool.Get()
- if v == nil {
- e := new(index_entry)
- e.elem.data = unsafe.Pointer(e)
- v = e
+ if ptr := index_entry_pool.Get(); ptr != nil {
+ return (*index_entry)(ptr)
}
- entry := v.(*index_entry)
+ entry := new(index_entry)
+ entry.elem.data = unsafe.Pointer(entry)
return entry
}
@@ -396,7 +394,8 @@ func free_index_entry(entry *index_entry) {
entry.key = ""
entry.index = nil
entry.item = nil
- index_entry_pool.Put(entry)
+ ptr := unsafe.Pointer(entry)
+ index_entry_pool.Put(ptr)
}
func is_unique(f uint8) bool {
diff --git a/vendor/codeberg.org/gruf/go-structr/item.go b/vendor/codeberg.org/gruf/go-structr/item.go
index 08f054907..4c63b97c6 100644
--- a/vendor/codeberg.org/gruf/go-structr/item.go
+++ b/vendor/codeberg.org/gruf/go-structr/item.go
@@ -2,8 +2,9 @@ package structr
import (
"os"
- "sync"
"unsafe"
+
+ "codeberg.org/gruf/go-mempool"
)
type indexed_item struct {
@@ -19,17 +20,15 @@ type indexed_item struct {
indexed []*index_entry
}
-var indexed_item_pool sync.Pool
+var indexed_item_pool mempool.UnsafePool
// new_indexed_item returns a new prepared indexed_item.
func new_indexed_item() *indexed_item {
- v := indexed_item_pool.Get()
- if v == nil {
- i := new(indexed_item)
- i.elem.data = unsafe.Pointer(i)
- v = i
+ if ptr := indexed_item_pool.Get(); ptr != nil {
+ return (*indexed_item)(ptr)
}
- item := v.(*indexed_item)
+ item := new(indexed_item)
+ item.elem.data = unsafe.Pointer(item)
return item
}
@@ -43,7 +42,8 @@ func free_indexed_item(item *indexed_item) {
return
}
item.data = nil
- indexed_item_pool.Put(item)
+ ptr := unsafe.Pointer(item)
+ indexed_item_pool.Put(ptr)
}
// drop_index will drop the given index entry from item's indexed.
diff --git a/vendor/codeberg.org/gruf/go-structr/list.go b/vendor/codeberg.org/gruf/go-structr/list.go
index a2cb5b658..1c931fd51 100644
--- a/vendor/codeberg.org/gruf/go-structr/list.go
+++ b/vendor/codeberg.org/gruf/go-structr/list.go
@@ -2,8 +2,9 @@ package structr
import (
"os"
- "sync"
"unsafe"
+
+ "codeberg.org/gruf/go-mempool"
)
// elem represents an elem
@@ -27,16 +28,14 @@ type list struct {
len int
}
-var list_pool sync.Pool
+var list_pool mempool.UnsafePool
// new_list returns a new prepared list.
func new_list() *list {
- v := list_pool.Get()
- if v == nil {
- v = new(list)
+ if ptr := list_pool.Get(); ptr != nil {
+ return (*list)(ptr)
}
- list := v.(*list)
- return list
+ return new(list)
}
// free_list releases the list.
@@ -48,11 +47,13 @@ func free_list(list *list) {
os.Stderr.WriteString(msg + "\n")
return
}
- list_pool.Put(list)
+ ptr := unsafe.Pointer(list)
+ list_pool.Put(ptr)
}
// push_front will push the given elem to front (head) of list.
func (l *list) push_front(elem *list_elem) {
+
// Set new head.
oldHead := l.head
l.head = elem
@@ -66,12 +67,14 @@ func (l *list) push_front(elem *list_elem) {
l.tail = elem
}
- // Incr count
+ // Incr
+ // count
l.len++
}
// push_back will push the given elem to back (tail) of list.
func (l *list) push_back(elem *list_elem) {
+
// Set new tail.
oldTail := l.tail
l.tail = elem
@@ -85,7 +88,8 @@ func (l *list) push_back(elem *list_elem) {
l.head = elem
}
- // Incr count
+ // Incr
+ // count
l.len++
}
@@ -131,7 +135,8 @@ func (l *list) insert(elem *list_elem, at *list_elem) {
elem.next = oldNext
}
- // Incr count
+ // Incr
+ // count
l.len++
}
@@ -174,6 +179,7 @@ func (l *list) remove(elem *list_elem) {
prev.next = next
}
- // Decr count
+ // Decr
+ // count
l.len--
}
diff --git a/vendor/codeberg.org/gruf/go-structr/runtime.go b/vendor/codeberg.org/gruf/go-structr/runtime.go
index 8a8d53ede..508cd6e4c 100644
--- a/vendor/codeberg.org/gruf/go-structr/runtime.go
+++ b/vendor/codeberg.org/gruf/go-structr/runtime.go
@@ -146,7 +146,7 @@ func find_field(t xunsafe.TypeIter, names []string) (sfield struct_field, ftype
sfield.mangle = mangler.Get(t)
// Calculate zero value string.
- zptr := zero_value_field(o, sfield.offsets)
+ zptr := zero_value_ptr(o, sfield.offsets)
zstr := string(sfield.mangle(nil, zptr))
sfield.zerostr = zstr
sfield.zero = zptr
@@ -154,7 +154,9 @@ func find_field(t xunsafe.TypeIter, names []string) (sfield struct_field, ftype
return
}
-// zero_value ...
+// zero_value iterates the type contained in TypeIter{} along the given
+// next_offset{} values, creating new ptrs where necessary, returning the
+// zero reflect.Value{} after fully iterating the next_offset{} slice.
func zero_value(t xunsafe.TypeIter, offsets []next_offset) reflect.Value {
v := reflect.New(t.Type).Elem()
for _, offset := range offsets {
@@ -175,8 +177,8 @@ func zero_value(t xunsafe.TypeIter, offsets []next_offset) reflect.Value {
return v
}
-// zero_value_field ...
-func zero_value_field(t xunsafe.TypeIter, offsets []next_offset) unsafe.Pointer {
+// zero_value_ptr returns the unsafe pointer address of the result of zero_value().
+func zero_value_ptr(t xunsafe.TypeIter, offsets []next_offset) unsafe.Pointer {
return zero_value(t, offsets).Addr().UnsafePointer()
}
diff --git a/vendor/codeberg.org/gruf/go-structr/timeline.go b/vendor/codeberg.org/gruf/go-structr/timeline.go
index 1e9703fca..749ec862a 100644
--- a/vendor/codeberg.org/gruf/go-structr/timeline.go
+++ b/vendor/codeberg.org/gruf/go-structr/timeline.go
@@ -8,6 +8,8 @@ import (
"strings"
"sync"
"unsafe"
+
+ "codeberg.org/gruf/go-mempool"
)
// Direction defines a direction
@@ -1133,18 +1135,16 @@ func to_timeline_item(item *indexed_item) *timeline_item {
return to
}
-var timeline_item_pool sync.Pool
+var timeline_item_pool mempool.UnsafePool
// new_timeline_item returns a new prepared timeline_item.
func new_timeline_item() *timeline_item {
- v := timeline_item_pool.Get()
- if v == nil {
- i := new(timeline_item)
- i.elem.data = unsafe.Pointer(i)
- i.ck = ^uint(0)
- v = i
+ if ptr := timeline_item_pool.Get(); ptr != nil {
+ return (*timeline_item)(ptr)
}
- item := v.(*timeline_item)
+ item := new(timeline_item)
+ item.elem.data = unsafe.Pointer(item)
+ item.ck = ^uint(0)
return item
}
@@ -1159,5 +1159,6 @@ func free_timeline_item(item *timeline_item) {
}
item.data = nil
item.pk = nil
- timeline_item_pool.Put(item)
+ ptr := unsafe.Pointer(item)
+ timeline_item_pool.Put(ptr)
}
diff --git a/vendor/github.com/ncruces/go-sqlite3/conn.go b/vendor/github.com/ncruces/go-sqlite3/conn.go
index 7e88d8c85..a7eca1652 100644
--- a/vendor/github.com/ncruces/go-sqlite3/conn.go
+++ b/vendor/github.com/ncruces/go-sqlite3/conn.go
@@ -444,20 +444,27 @@ func (c *Conn) Status(op DBStatus, reset bool) (current, highwater int, err erro
// https://sqlite.org/c3ref/table_column_metadata.html
func (c *Conn) TableColumnMetadata(schema, table, column string) (declType, collSeq string, notNull, primaryKey, autoInc bool, err error) {
defer c.arena.mark()()
-
- var schemaPtr, columnPtr ptr_t
- declTypePtr := c.arena.new(ptrlen)
- collSeqPtr := c.arena.new(ptrlen)
- notNullPtr := c.arena.new(ptrlen)
- autoIncPtr := c.arena.new(ptrlen)
- primaryKeyPtr := c.arena.new(ptrlen)
+ var (
+ declTypePtr ptr_t
+ collSeqPtr ptr_t
+ notNullPtr ptr_t
+ primaryKeyPtr ptr_t
+ autoIncPtr ptr_t
+ columnPtr ptr_t
+ schemaPtr ptr_t
+ )
+ if column != "" {
+ declTypePtr = c.arena.new(ptrlen)
+ collSeqPtr = c.arena.new(ptrlen)
+ notNullPtr = c.arena.new(ptrlen)
+ primaryKeyPtr = c.arena.new(ptrlen)
+ autoIncPtr = c.arena.new(ptrlen)
+ columnPtr = c.arena.string(column)
+ }
if schema != "" {
schemaPtr = c.arena.string(schema)
}
tablePtr := c.arena.string(table)
- if column != "" {
- columnPtr = c.arena.string(column)
- }
rc := res_t(c.call("sqlite3_table_column_metadata", stk_t(c.handle),
stk_t(schemaPtr), stk_t(tablePtr), stk_t(columnPtr),
diff --git a/vendor/github.com/ncruces/go-sqlite3/context.go b/vendor/github.com/ncruces/go-sqlite3/context.go
index 154c228cf..269bf52f9 100644
--- a/vendor/github.com/ncruces/go-sqlite3/context.go
+++ b/vendor/github.com/ncruces/go-sqlite3/context.go
@@ -1,7 +1,6 @@
package sqlite3
import (
- "encoding/json"
"errors"
"math"
"time"
@@ -173,21 +172,6 @@ func (ctx Context) ResultPointer(ptr any) {
stk_t(ctx.handle), stk_t(valPtr))
}
-// ResultJSON sets the result of the function to the JSON encoding of value.
-//
-// https://sqlite.org/c3ref/result_blob.html
-func (ctx Context) ResultJSON(value any) {
- err := json.NewEncoder(callbackWriter(func(p []byte) (int, error) {
- ctx.ResultRawText(p[:len(p)-1]) // remove the newline
- return 0, nil
- })).Encode(value)
-
- if err != nil {
- ctx.ResultError(err)
- return // notest
- }
-}
-
// ResultValue sets the result of the function to a copy of [Value].
//
// https://sqlite.org/c3ref/result_blob.html
diff --git a/vendor/github.com/ncruces/go-sqlite3/driver/driver.go b/vendor/github.com/ncruces/go-sqlite3/driver/driver.go
index 27496f6cb..5d2847369 100644
--- a/vendor/github.com/ncruces/go-sqlite3/driver/driver.go
+++ b/vendor/github.com/ncruces/go-sqlite3/driver/driver.go
@@ -607,14 +607,24 @@ func (r resultRowsAffected) RowsAffected() (int64, error) {
type scantype byte
const (
- _ANY scantype = iota
- _INT scantype = scantype(sqlite3.INTEGER)
- _REAL scantype = scantype(sqlite3.FLOAT)
- _TEXT scantype = scantype(sqlite3.TEXT)
- _BLOB scantype = scantype(sqlite3.BLOB)
- _NULL scantype = scantype(sqlite3.NULL)
- _BOOL scantype = iota
+ _ANY scantype = iota
+ _INT
+ _REAL
+ _TEXT
+ _BLOB
+ _NULL
+ _BOOL
_TIME
+ _NOT_NULL
+)
+
+var (
+ _ [0]struct{} = [scantype(sqlite3.INTEGER) - _INT]struct{}{}
+ _ [0]struct{} = [scantype(sqlite3.FLOAT) - _REAL]struct{}{}
+ _ [0]struct{} = [scantype(sqlite3.TEXT) - _TEXT]struct{}{}
+ _ [0]struct{} = [scantype(sqlite3.BLOB) - _BLOB]struct{}{}
+ _ [0]struct{} = [scantype(sqlite3.NULL) - _NULL]struct{}{}
+ _ [0]struct{} = [_NOT_NULL & (_NOT_NULL - 1)]struct{}{}
)
func scanFromDecl(decl string) scantype {
@@ -644,8 +654,8 @@ type rows struct {
*stmt
names []string
types []string
- nulls []bool
scans []scantype
+ dest []driver.Value
}
var (
@@ -675,34 +685,36 @@ func (r *rows) Columns() []string {
func (r *rows) scanType(index int) scantype {
if r.scans == nil {
- count := r.Stmt.ColumnCount()
+ count := len(r.names)
scans := make([]scantype, count)
for i := range scans {
scans[i] = scanFromDecl(strings.ToUpper(r.Stmt.ColumnDeclType(i)))
}
r.scans = scans
}
- return r.scans[index]
+ return r.scans[index] &^ _NOT_NULL
}
func (r *rows) loadColumnMetadata() {
- if r.nulls == nil {
+ if r.types == nil {
c := r.Stmt.Conn()
- count := r.Stmt.ColumnCount()
- nulls := make([]bool, count)
+ count := len(r.names)
types := make([]string, count)
scans := make([]scantype, count)
- for i := range nulls {
+ for i := range types {
+ var notnull bool
if col := r.Stmt.ColumnOriginName(i); col != "" {
- types[i], _, nulls[i], _, _, _ = c.TableColumnMetadata(
+ types[i], _, notnull, _, _, _ = c.TableColumnMetadata(
r.Stmt.ColumnDatabaseName(i),
r.Stmt.ColumnTableName(i),
col)
types[i] = strings.ToUpper(types[i])
scans[i] = scanFromDecl(types[i])
+ if notnull {
+ scans[i] |= _NOT_NULL
+ }
}
}
- r.nulls = nulls
r.types = types
r.scans = scans
}
@@ -721,15 +733,13 @@ func (r *rows) ColumnTypeDatabaseTypeName(index int) string {
func (r *rows) ColumnTypeNullable(index int) (nullable, ok bool) {
r.loadColumnMetadata()
- if r.nulls[index] {
- return false, true
- }
- return true, false
+ nullable = r.scans[index]&^_NOT_NULL == 0
+ return nullable, !nullable
}
func (r *rows) ColumnTypeScanType(index int) (typ reflect.Type) {
r.loadColumnMetadata()
- scan := r.scans[index]
+ scan := r.scans[index] &^ _NOT_NULL
if r.Stmt.Busy() {
// SQLite is dynamically typed and we now have a row.
@@ -772,6 +782,7 @@ func (r *rows) ColumnTypeScanType(index int) (typ reflect.Type) {
}
func (r *rows) Next(dest []driver.Value) error {
+ r.dest = nil
c := r.Stmt.Conn()
if old := c.SetInterrupt(r.ctx); old != r.ctx {
defer c.SetInterrupt(old)
@@ -790,18 +801,7 @@ func (r *rows) Next(dest []driver.Value) error {
}
for i := range dest {
scan := r.scanType(i)
- switch v := dest[i].(type) {
- case int64:
- if scan == _BOOL {
- switch v {
- case 1:
- dest[i] = true
- case 0:
- dest[i] = false
- }
- continue
- }
- case []byte:
+ if v, ok := dest[i].([]byte); ok {
if len(v) == cap(v) { // a BLOB
continue
}
@@ -816,38 +816,49 @@ func (r *rows) Next(dest []driver.Value) error {
}
}
dest[i] = string(v)
- case float64:
- break
- default:
- continue
}
- if scan == _TIME {
+ switch scan {
+ case _TIME:
t, err := r.tmRead.Decode(dest[i])
if err == nil {
dest[i] = t
- continue
+ }
+ case _BOOL:
+ switch dest[i] {
+ case int64(0):
+ dest[i] = false
+ case int64(1):
+ dest[i] = true
}
}
}
+ r.dest = dest
return nil
}
-func (r *rows) ScanColumn(dest any, index int) error {
+func (r *rows) ScanColumn(dest any, index int) (err error) {
// notest // Go 1.26
- var ptr *time.Time
+ var tm *time.Time
+ var ok *bool
switch d := dest.(type) {
case *time.Time:
- ptr = d
+ tm = d
case *sql.NullTime:
- ptr = &d.Time
+ tm = &d.Time
+ ok = &d.Valid
case *sql.Null[time.Time]:
- ptr = &d.V
+ tm = &d.V
+ ok = &d.Valid
default:
return driver.ErrSkip
}
- if t := r.Stmt.ColumnTime(index, r.tmRead); !t.IsZero() {
- *ptr = t
- return nil
+ value := r.dest[index]
+ *tm, err = r.tmRead.Decode(value)
+ if ok != nil {
+ *ok = err == nil
+ if value == nil {
+ return nil
+ }
}
- return driver.ErrSkip
+ return err
}
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/json.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/json.go
index 846237405..f582734cf 100644
--- a/vendor/github.com/ncruces/go-sqlite3/internal/util/json.go
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/json.go
@@ -1,3 +1,5 @@
+//go:build !goexperiment.jsonv2
+
package util
import (
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/json_v2.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/json_v2.go
new file mode 100644
index 000000000..2fb052233
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/json_v2.go
@@ -0,0 +1,52 @@
+//go:build goexperiment.jsonv2
+
+package util
+
+import (
+ "encoding/json/v2"
+ "math"
+ "strconv"
+ "time"
+ "unsafe"
+)
+
+type JSON struct{ Value any }
+
+func (j JSON) Scan(value any) error {
+ var buf []byte
+
+ switch v := value.(type) {
+ case []byte:
+ buf = v
+ case string:
+ buf = unsafe.Slice(unsafe.StringData(v), len(v))
+ case int64:
+ buf = strconv.AppendInt(nil, v, 10)
+ case float64:
+ buf = AppendNumber(nil, v)
+ case time.Time:
+ buf = append(buf, '"')
+ buf = v.AppendFormat(buf, time.RFC3339Nano)
+ buf = append(buf, '"')
+ case nil:
+ buf = []byte("null")
+ default:
+ panic(AssertErr())
+ }
+
+ return json.Unmarshal(buf, j.Value)
+}
+
+func AppendNumber(dst []byte, f float64) []byte {
+ switch {
+ case math.IsNaN(f):
+ dst = append(dst, "null"...)
+ case math.IsInf(f, 1):
+ dst = append(dst, "9.0e999"...)
+ case math.IsInf(f, -1):
+ dst = append(dst, "-9.0e999"...)
+ default:
+ return strconv.AppendFloat(dst, f, 'g', -1, 64)
+ }
+ return dst
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/json.go b/vendor/github.com/ncruces/go-sqlite3/json.go
index 2b762c092..78195f2e3 100644
--- a/vendor/github.com/ncruces/go-sqlite3/json.go
+++ b/vendor/github.com/ncruces/go-sqlite3/json.go
@@ -1,6 +1,13 @@
+//go:build !goexperiment.jsonv2
+
package sqlite3
-import "github.com/ncruces/go-sqlite3/internal/util"
+import (
+ "encoding/json"
+ "strconv"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+)
// JSON returns a value that can be used as an argument to
// [database/sql.DB.Exec], [database/sql.Row.Scan] and similar methods to
@@ -10,3 +17,77 @@ import "github.com/ncruces/go-sqlite3/internal/util"
func JSON(value any) any {
return util.JSON{Value: value}
}
+
+// ResultJSON sets the result of the function to the JSON encoding of value.
+//
+// https://sqlite.org/c3ref/result_blob.html
+func (ctx Context) ResultJSON(value any) {
+ err := json.NewEncoder(callbackWriter(func(p []byte) (int, error) {
+ ctx.ResultRawText(p[:len(p)-1]) // remove the newline
+ return 0, nil
+ })).Encode(value)
+
+ if err != nil {
+ ctx.ResultError(err)
+ return // notest
+ }
+}
+
+// BindJSON binds the JSON encoding of value to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindJSON(param int, value any) error {
+ return json.NewEncoder(callbackWriter(func(p []byte) (int, error) {
+ return 0, s.BindRawText(param, p[:len(p)-1]) // remove the newline
+ })).Encode(value)
+}
+
+// ColumnJSON parses the JSON-encoded value of the result column
+// and stores it in the value pointed to by ptr.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnJSON(col int, ptr any) error {
+ var data []byte
+ switch s.ColumnType(col) {
+ case NULL:
+ data = []byte("null")
+ case TEXT:
+ data = s.ColumnRawText(col)
+ case BLOB:
+ data = s.ColumnRawBlob(col)
+ case INTEGER:
+ data = strconv.AppendInt(nil, s.ColumnInt64(col), 10)
+ case FLOAT:
+ data = util.AppendNumber(nil, s.ColumnFloat(col))
+ default:
+ panic(util.AssertErr())
+ }
+ return json.Unmarshal(data, ptr)
+}
+
+// JSON parses a JSON-encoded value
+// and stores the result in the value pointed to by ptr.
+func (v Value) JSON(ptr any) error {
+ var data []byte
+ switch v.Type() {
+ case NULL:
+ data = []byte("null")
+ case TEXT:
+ data = v.RawText()
+ case BLOB:
+ data = v.RawBlob()
+ case INTEGER:
+ data = strconv.AppendInt(nil, v.Int64(), 10)
+ case FLOAT:
+ data = util.AppendNumber(nil, v.Float())
+ default:
+ panic(util.AssertErr())
+ }
+ return json.Unmarshal(data, ptr)
+}
+
+type callbackWriter func(p []byte) (int, error)
+
+func (fn callbackWriter) Write(p []byte) (int, error) { return fn(p) }
diff --git a/vendor/github.com/ncruces/go-sqlite3/json_v2.go b/vendor/github.com/ncruces/go-sqlite3/json_v2.go
new file mode 100644
index 000000000..4b74bc7a4
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/json_v2.go
@@ -0,0 +1,113 @@
+//go:build goexperiment.jsonv2
+
+package sqlite3
+
+import (
+ "encoding/json/v2"
+ "strconv"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+)
+
+// JSON returns a value that can be used as an argument to
+// [database/sql.DB.Exec], [database/sql.Row.Scan] and similar methods to
+// store value as JSON, or decode JSON into value.
+// JSON should NOT be used with [Stmt.BindJSON], [Stmt.ColumnJSON],
+// [Value.JSON], or [Context.ResultJSON].
+func JSON(value any) any {
+ return util.JSON{Value: value}
+}
+
+// ResultJSON sets the result of the function to the JSON encoding of value.
+//
+// https://sqlite.org/c3ref/result_blob.html
+func (ctx Context) ResultJSON(value any) {
+ w := bytesWriter{sqlite: ctx.c.sqlite}
+ if err := json.MarshalWrite(&w, value); err != nil {
+ ctx.c.free(w.ptr)
+ ctx.ResultError(err)
+ return // notest
+ }
+ ctx.c.call("sqlite3_result_text_go",
+ stk_t(ctx.handle), stk_t(w.ptr), stk_t(len(w.buf)))
+}
+
+// BindJSON binds the JSON encoding of value to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindJSON(param int, value any) error {
+ w := bytesWriter{sqlite: s.c.sqlite}
+ if err := json.MarshalWrite(&w, value); err != nil {
+ s.c.free(w.ptr)
+ return err
+ }
+ rc := res_t(s.c.call("sqlite3_bind_text_go",
+ stk_t(s.handle), stk_t(param),
+ stk_t(w.ptr), stk_t(len(w.buf))))
+ return s.c.error(rc)
+}
+
+// ColumnJSON parses the JSON-encoded value of the result column
+// and stores it in the value pointed to by ptr.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnJSON(col int, ptr any) error {
+ var data []byte
+ switch s.ColumnType(col) {
+ case NULL:
+ data = []byte("null")
+ case TEXT:
+ data = s.ColumnRawText(col)
+ case BLOB:
+ data = s.ColumnRawBlob(col)
+ case INTEGER:
+ data = strconv.AppendInt(nil, s.ColumnInt64(col), 10)
+ case FLOAT:
+ data = util.AppendNumber(nil, s.ColumnFloat(col))
+ default:
+ panic(util.AssertErr())
+ }
+ return json.Unmarshal(data, ptr)
+}
+
+// JSON parses a JSON-encoded value
+// and stores the result in the value pointed to by ptr.
+func (v Value) JSON(ptr any) error {
+ var data []byte
+ switch v.Type() {
+ case NULL:
+ data = []byte("null")
+ case TEXT:
+ data = v.RawText()
+ case BLOB:
+ data = v.RawBlob()
+ case INTEGER:
+ data = strconv.AppendInt(nil, v.Int64(), 10)
+ case FLOAT:
+ data = util.AppendNumber(nil, v.Float())
+ default:
+ panic(util.AssertErr())
+ }
+ return json.Unmarshal(data, ptr)
+}
+
+type bytesWriter struct {
+ *sqlite
+ buf []byte
+ ptr ptr_t
+}
+
+func (b *bytesWriter) Write(p []byte) (n int, err error) {
+ if len(p) > cap(b.buf)-len(b.buf) {
+ want := int64(len(b.buf)) + int64(len(p))
+ grow := int64(cap(b.buf))
+ grow += grow >> 1
+ want = max(want, grow)
+ b.ptr = b.realloc(b.ptr, want)
+ b.buf = util.View(b.mod, b.ptr, want)[:len(b.buf)]
+ }
+ b.buf = append(b.buf, p...)
+ return len(p), nil
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/sqlite.go b/vendor/github.com/ncruces/go-sqlite3/sqlite.go
index c05a86fde..fb64ac5c0 100644
--- a/vendor/github.com/ncruces/go-sqlite3/sqlite.go
+++ b/vendor/github.com/ncruces/go-sqlite3/sqlite.go
@@ -5,6 +5,7 @@ import (
"context"
"math/bits"
"os"
+ "strings"
"sync"
"unsafe"
@@ -128,11 +129,10 @@ func (sqlt *sqlite) error(rc res_t, handle ptr_t, sql ...string) error {
var msg, query string
if ptr := ptr_t(sqlt.call("sqlite3_errmsg", stk_t(handle))); ptr != 0 {
msg = util.ReadString(sqlt.mod, ptr, _MAX_LENGTH)
- switch {
- case msg == "not an error":
- msg = ""
- case msg == util.ErrorCodeString(uint32(rc))[len("sqlite3: "):]:
+ if msg == "not an error" {
msg = ""
+ } else {
+ msg = strings.TrimPrefix(msg, util.ErrorCodeString(uint32(rc))[len("sqlite3: "):])
}
}
diff --git a/vendor/github.com/ncruces/go-sqlite3/stmt.go b/vendor/github.com/ncruces/go-sqlite3/stmt.go
index 706182f9f..e2523b6cb 100644
--- a/vendor/github.com/ncruces/go-sqlite3/stmt.go
+++ b/vendor/github.com/ncruces/go-sqlite3/stmt.go
@@ -1,9 +1,7 @@
package sqlite3
import (
- "encoding/json"
"math"
- "strconv"
"time"
"github.com/ncruces/go-sqlite3/internal/util"
@@ -362,16 +360,6 @@ func (s *Stmt) BindPointer(param int, ptr any) error {
return s.c.error(rc)
}
-// BindJSON binds the JSON encoding of value to the prepared statement.
-// The leftmost SQL parameter has an index of 1.
-//
-// https://sqlite.org/c3ref/bind_blob.html
-func (s *Stmt) BindJSON(param int, value any) error {
- return json.NewEncoder(callbackWriter(func(p []byte) (int, error) {
- return 0, s.BindRawText(param, p[:len(p)-1]) // remove the newline
- })).Encode(value)
-}
-
// BindValue binds a copy of value to the prepared statement.
// The leftmost SQL parameter has an index of 1.
//
@@ -598,30 +586,6 @@ func (s *Stmt) columnRawBytes(col int, ptr ptr_t, nul int32) []byte {
return util.View(s.c.mod, ptr, int64(n+nul))[:n]
}
-// ColumnJSON parses the JSON-encoded value of the result column
-// and stores it in the value pointed to by ptr.
-// The leftmost column of the result set has the index 0.
-//
-// https://sqlite.org/c3ref/column_blob.html
-func (s *Stmt) ColumnJSON(col int, ptr any) error {
- var data []byte
- switch s.ColumnType(col) {
- case NULL:
- data = []byte("null")
- case TEXT:
- data = s.ColumnRawText(col)
- case BLOB:
- data = s.ColumnRawBlob(col)
- case INTEGER:
- data = strconv.AppendInt(nil, s.ColumnInt64(col), 10)
- case FLOAT:
- data = util.AppendNumber(nil, s.ColumnFloat(col))
- default:
- panic(util.AssertErr())
- }
- return json.Unmarshal(data, ptr)
-}
-
// ColumnValue returns the unprotected value of the result column.
// The leftmost column of the result set has the index 0.
//
@@ -748,7 +712,3 @@ func (s *Stmt) columns(count int64) ([]byte, ptr_t, error) {
return util.View(s.c.mod, typePtr, count), dataPtr, nil
}
-
-type callbackWriter func(p []byte) (int, error)
-
-func (fn callbackWriter) Write(p []byte) (int, error) { return fn(p) }
diff --git a/vendor/github.com/ncruces/go-sqlite3/time.go b/vendor/github.com/ncruces/go-sqlite3/time.go
index d9c516c81..19bcd2b0b 100644
--- a/vendor/github.com/ncruces/go-sqlite3/time.go
+++ b/vendor/github.com/ncruces/go-sqlite3/time.go
@@ -94,7 +94,7 @@ func (f TimeFormat) Encode(t time.Time) any {
case TimeFormatUnix:
return t.Unix()
case TimeFormatUnixFrac:
- return float64(t.Unix()) + float64(t.Nanosecond())*1e-9
+ return math.FMA(1e-9, float64(t.Nanosecond()), float64(t.Unix()))
case TimeFormatUnixMilli:
return t.UnixMilli()
case TimeFormatUnixMicro:
diff --git a/vendor/github.com/ncruces/go-sqlite3/value.go b/vendor/github.com/ncruces/go-sqlite3/value.go
index 6806e9a79..994743f82 100644
--- a/vendor/github.com/ncruces/go-sqlite3/value.go
+++ b/vendor/github.com/ncruces/go-sqlite3/value.go
@@ -1,9 +1,7 @@
package sqlite3
import (
- "encoding/json"
"math"
- "strconv"
"time"
"github.com/ncruces/go-sqlite3/internal/util"
@@ -162,27 +160,6 @@ func (v Value) Pointer() any {
return util.GetHandle(v.c.ctx, ptr)
}
-// JSON parses a JSON-encoded value
-// and stores the result in the value pointed to by ptr.
-func (v Value) JSON(ptr any) error {
- var data []byte
- switch v.Type() {
- case NULL:
- data = []byte("null")
- case TEXT:
- data = v.RawText()
- case BLOB:
- data = v.RawBlob()
- case INTEGER:
- data = strconv.AppendInt(nil, v.Int64(), 10)
- case FLOAT:
- data = util.AppendNumber(nil, v.Float())
- default:
- panic(util.AssertErr())
- }
- return json.Unmarshal(data, ptr)
-}
-
// NoChange returns true if and only if the value is unchanged
// in a virtual table update operatiom.
//
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/const.go b/vendor/github.com/ncruces/go-sqlite3/vfs/const.go
index 11afb1254..9ed67e385 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/const.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/const.go
@@ -94,6 +94,10 @@ const (
OPEN_PRIVATECACHE OpenFlag = 0x00040000 /* Ok for sqlite3_open_v2() */
OPEN_WAL OpenFlag = 0x00080000 /* VFS only */
OPEN_NOFOLLOW OpenFlag = 0x01000000 /* Ok for sqlite3_open_v2() */
+ _FLAG_ATOMIC OpenFlag = 0x10000000
+ _FLAG_KEEP_WAL OpenFlag = 0x20000000
+ _FLAG_PSOW OpenFlag = 0x40000000
+ _FLAG_SYNC_DIR OpenFlag = 0x80000000
)
// AccessFlag is a flag for the [VFS] Access method.
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/file.go b/vendor/github.com/ncruces/go-sqlite3/vfs/file.go
index 06906c961..bdebdf6aa 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/file.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/file.go
@@ -51,7 +51,7 @@ func (vfsOS) Delete(path string, syncDir bool) error {
return _OK
}
defer f.Close()
- err = osSync(f, false, false)
+ err = osSync(f, 0, SYNC_FULL)
if err != nil {
return _IOERR_DIR_FSYNC
}
@@ -131,27 +131,24 @@ func (vfsOS) OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error
}
file := vfsFile{
- File: f,
- psow: true,
- atomic: osBatchAtomic(f),
- readOnly: flags&OPEN_READONLY != 0,
- syncDir: isUnix && isCreate && isJournl,
- delete: !isUnix && flags&OPEN_DELETEONCLOSE != 0,
- shm: NewSharedMemory(name.String()+"-shm", flags),
+ File: f,
+ flags: flags | _FLAG_PSOW,
+ shm: NewSharedMemory(name.String()+"-shm", flags),
+ }
+ if osBatchAtomic(f) {
+ file.flags |= _FLAG_ATOMIC
+ }
+ if isUnix && isCreate && isJournl {
+ file.flags |= _FLAG_SYNC_DIR
}
return &file, flags, nil
}
type vfsFile struct {
*os.File
- shm SharedMemory
- lock LockLevel
- readOnly bool
- keepWAL bool
- syncDir bool
- atomic bool
- delete bool
- psow bool
+ shm SharedMemory
+ lock LockLevel
+ flags OpenFlag
}
var (
@@ -164,7 +161,7 @@ var (
)
func (f *vfsFile) Close() error {
- if f.delete {
+ if !isUnix && f.flags&OPEN_DELETEONCLOSE != 0 {
defer os.Remove(f.Name())
}
if f.shm != nil {
@@ -183,21 +180,18 @@ func (f *vfsFile) WriteAt(p []byte, off int64) (n int, err error) {
}
func (f *vfsFile) Sync(flags SyncFlag) error {
- dataonly := (flags & SYNC_DATAONLY) != 0
- fullsync := (flags & 0x0f) == SYNC_FULL
-
- err := osSync(f.File, fullsync, dataonly)
+ err := osSync(f.File, f.flags, flags)
if err != nil {
return err
}
- if isUnix && f.syncDir {
- f.syncDir = false
+ if isUnix && f.flags&_FLAG_SYNC_DIR != 0 {
+ f.flags ^= _FLAG_SYNC_DIR
d, err := os.Open(filepath.Dir(f.File.Name()))
if err != nil {
return nil
}
defer d.Close()
- err = osSync(d, false, false)
+ err = osSync(f.File, f.flags, flags)
if err != nil {
return _IOERR_DIR_FSYNC
}
@@ -215,10 +209,10 @@ func (f *vfsFile) SectorSize() int {
func (f *vfsFile) DeviceCharacteristics() DeviceCharacteristic {
ret := IOCAP_SUBPAGE_READ
- if f.atomic {
+ if f.flags&_FLAG_ATOMIC != 0 {
ret |= IOCAP_BATCH_ATOMIC
}
- if f.psow {
+ if f.flags&_FLAG_PSOW != 0 {
ret |= IOCAP_POWERSAFE_OVERWRITE
}
if runtime.GOOS == "windows" {
@@ -249,8 +243,20 @@ func (f *vfsFile) HasMoved() (bool, error) {
return !os.SameFile(fi, pi), nil
}
-func (f *vfsFile) LockState() LockLevel { return f.lock }
-func (f *vfsFile) PowersafeOverwrite() bool { return f.psow }
-func (f *vfsFile) PersistWAL() bool { return f.keepWAL }
-func (f *vfsFile) SetPowersafeOverwrite(psow bool) { f.psow = psow }
-func (f *vfsFile) SetPersistWAL(keepWAL bool) { f.keepWAL = keepWAL }
+func (f *vfsFile) LockState() LockLevel { return f.lock }
+func (f *vfsFile) PowersafeOverwrite() bool { return f.flags&_FLAG_PSOW != 0 }
+func (f *vfsFile) PersistWAL() bool { return f.flags&_FLAG_KEEP_WAL != 0 }
+
+func (f *vfsFile) SetPowersafeOverwrite(psow bool) {
+ f.flags &^= _FLAG_PSOW
+ if psow {
+ f.flags |= _FLAG_PSOW
+ }
+}
+
+func (f *vfsFile) SetPersistWAL(keepWAL bool) {
+ f.flags &^= _FLAG_KEEP_WAL
+ if keepWAL {
+ f.flags |= _FLAG_KEEP_WAL
+ }
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/lock.go b/vendor/github.com/ncruces/go-sqlite3/vfs/lock.go
index b28d83230..253057aea 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/lock.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/lock.go
@@ -41,7 +41,7 @@ func (f *vfsFile) Lock(lock LockLevel) error {
}
// Do not allow any kind of write-lock on a read-only database.
- if f.readOnly && lock >= LOCK_RESERVED {
+ if lock >= LOCK_RESERVED && f.flags&OPEN_READONLY != 0 {
return _IOERR_LOCK
}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/README.md b/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/README.md
index 2e2611bf8..e37db1be6 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/README.md
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/README.md
@@ -6,4 +6,7 @@ SQLite VFS in pure Go.
It has some benefits over the C version:
- the memory backing the database needs not be contiguous,
- the database can grow/shrink incrementally without copying,
-- reader-writer concurrency is slightly improved. \ No newline at end of file
+- reader-writer concurrency is slightly improved.
+
+[`memdb.TestDB`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/memdb#TestDB)
+is the preferred way to setup an in-memory database for testing. \ No newline at end of file
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/api.go b/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/api.go
index eb12eba09..a12819855 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/api.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/api.go
@@ -10,6 +10,7 @@
package memdb
import (
+ "crypto/rand"
"fmt"
"net/url"
"sync"
@@ -74,11 +75,27 @@ func Delete(name string) {
// TestDB creates an empty shared memory database for the test to use.
// The database is automatically deleted when the test and all its subtests complete.
+// Returns a URI filename appropriate to call Open with.
// Each subsequent call to TestDB returns a unique database.
+//
+// func Test_something(t *testing.T) {
+// t.Parallel()
+// dsn := memdb.TestDB(t, url.Values{
+// "_pragma": {"busy_timeout(1000)"},
+// })
+//
+// db, err := sql.Open("sqlite3", dsn)
+// if err != nil {
+// t.Fatal(err)
+// }
+// defer db.Close()
+//
+// // ...
+// }
func TestDB(tb testing.TB, params ...url.Values) string {
tb.Helper()
- name := fmt.Sprintf("%s_%p", tb.Name(), tb)
+ name := fmt.Sprintf("%s_%s", tb.Name(), rand.Text())
tb.Cleanup(func() { Delete(name) })
Create(name, nil)
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go
index ee08e9a7b..9bb8b559c 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go
@@ -23,12 +23,26 @@ type flocktimeout_t struct {
timeout unix.Timespec
}
-func osSync(file *os.File, fullsync, _ /*dataonly*/ bool) error {
- if fullsync {
- return file.Sync()
+func osSync(file *os.File, open OpenFlag, sync SyncFlag) error {
+ var cmd int
+ if sync&SYNC_FULL == SYNC_FULL {
+ // For rollback journals all we really need is a barrier.
+ if open&OPEN_MAIN_JOURNAL != 0 {
+ cmd = unix.F_BARRIERFSYNC
+ } else {
+ cmd = unix.F_FULLFSYNC
+ }
}
+
+ fd := file.Fd()
for {
- err := unix.Fsync(int(file.Fd()))
+ err := error(unix.ENOTSUP)
+ if cmd != 0 {
+ _, err = unix.FcntlInt(fd, cmd, 0)
+ }
+ if err == unix.ENOTSUP {
+ err = unix.Fsync(int(fd))
+ }
if err != unix.EINTR {
return err
}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go
index d112c5a99..893f1512c 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go
@@ -10,7 +10,7 @@ import (
"golang.org/x/sys/unix"
)
-func osSync(file *os.File, _ /*fullsync*/, _ /*dataonly*/ bool) error {
+func osSync(file *os.File, _ OpenFlag, _ SyncFlag) error {
// SQLite trusts Linux's fdatasync for all fsync's.
for {
err := unix.Fdatasync(int(file.Fd()))
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sync.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sync.go
index b32e83e08..87427d9ed 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sync.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sync.go
@@ -4,6 +4,6 @@ package vfs
import "os"
-func osSync(file *os.File, _ /*fullsync*/, _ /*dataonly*/ bool) error {
+func osSync(file *os.File, _ OpenFlag, _ SyncFlag) error {
return file.Sync()
}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 7d2b53ad4..10c10e1c2 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -271,11 +271,11 @@ codeberg.org/gruf/go-mangler/v2
# codeberg.org/gruf/go-maps v1.0.4
## explicit; go 1.20
codeberg.org/gruf/go-maps
-# codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760
-## explicit; go 1.22.2
+# codeberg.org/gruf/go-mempool v0.0.0-20251003110531-b54adae66253
+## explicit; go 1.24.0
codeberg.org/gruf/go-mempool
-# codeberg.org/gruf/go-mutexes v1.5.3
-## explicit; go 1.22.2
+# codeberg.org/gruf/go-mutexes v1.5.8
+## explicit; go 1.24.0
codeberg.org/gruf/go-mutexes
# codeberg.org/gruf/go-runners v1.6.3
## explicit; go 1.19
@@ -293,7 +293,7 @@ codeberg.org/gruf/go-storage/disk
codeberg.org/gruf/go-storage/internal
codeberg.org/gruf/go-storage/memory
codeberg.org/gruf/go-storage/s3
-# codeberg.org/gruf/go-structr v0.9.9
+# codeberg.org/gruf/go-structr v0.9.12
## explicit; go 1.24.5
codeberg.org/gruf/go-structr
# codeberg.org/gruf/go-xunsafe v0.0.0-20250809104800-512a9df57d73
@@ -727,7 +727,7 @@ github.com/modern-go/reflect2
# github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
## explicit
github.com/munnerz/goautoneg
-# github.com/ncruces/go-sqlite3 v0.29.0
+# github.com/ncruces/go-sqlite3 v0.29.1
## explicit; go 1.24.0
github.com/ncruces/go-sqlite3
github.com/ncruces/go-sqlite3/driver