diff --git a/go.mod b/go.mod index 3dfcc2d..fcb56d4 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/mjl-/adns v0.0.0-20231109160910-82839fe3e6ae github.com/mjl-/autocert v0.0.0-20231013072455-c361ae2e20a6 - github.com/mjl-/bstore v0.0.2 + github.com/mjl-/bstore v0.0.3 github.com/mjl-/sconf v0.0.5 github.com/mjl-/sherpa v0.6.6 github.com/mjl-/sherpadoc v0.0.12 diff --git a/go.sum b/go.sum index 67f04a6..6b19f50 100644 --- a/go.sum +++ b/go.sum @@ -29,8 +29,8 @@ github.com/mjl-/adns v0.0.0-20231109160910-82839fe3e6ae h1:P/kTaQbDFSbmDK+RVjwu7 github.com/mjl-/adns v0.0.0-20231109160910-82839fe3e6ae/go.mod h1:v47qUMJnipnmDTRGaHwpCwzE6oypa5K33mUvBfzZBn8= github.com/mjl-/autocert v0.0.0-20231013072455-c361ae2e20a6 h1:TEXyTghAN9pmV2ffzdnhmzkML08e1Z/oGywJ9eunbRI= github.com/mjl-/autocert v0.0.0-20231013072455-c361ae2e20a6/go.mod h1:taMFU86abMxKLPV4Bynhv8enbYmS67b8LG80qZv2Qus= -github.com/mjl-/bstore v0.0.2 h1:4fdpIOY/+Dv1dBHyzdqa4PD90p8Mz86FeyRpI4qcehw= -github.com/mjl-/bstore v0.0.2/go.mod h1:/cD25FNBaDfvL/plFRxI3Ba3E+wcB0XVOS8nJDqndg0= +github.com/mjl-/bstore v0.0.3 h1:7lpZfzADXYbodan3s8Tb5aXXK90oKB9vdDvz0MC5jrw= +github.com/mjl-/bstore v0.0.3/go.mod h1:/cD25FNBaDfvL/plFRxI3Ba3E+wcB0XVOS8nJDqndg0= github.com/mjl-/sconf v0.0.5 h1:4CMUTENpSnaeP2g6RKtrs8udTxnJgjX2MCCovxGId6s= github.com/mjl-/sconf v0.0.5/go.mod h1:uF8OdWtLT8La3i4ln176i1pB0ps9pXGCaABEU55ZkE0= github.com/mjl-/sherpa v0.6.6 h1:4Xc4/s12W2I/C1genIL8l4ZCLMsTo8498cPSjQcIHGc= diff --git a/vendor/github.com/mjl-/bstore/parse.go b/vendor/github.com/mjl-/bstore/parse.go index 00e0c69..0416a34 100644 --- a/vendor/github.com/mjl-/bstore/parse.go +++ b/vendor/github.com/mjl-/bstore/parse.go @@ -26,11 +26,11 @@ func (p *parser) checkInt(un uint64) int { } // Fieldmap starts a new fieldmap for n fields. -func (p *parser) Fieldmap(n int) *fieldmap { +func (p *parser) Fieldmap(n int) fieldmap { // log.Printf("parse fieldmap %d bits", n) nb := (n + 7) / 8 buf := p.Take(nb) - return &fieldmap{n, buf, 0, 0, p.Errorf} + return fieldmap{n, buf, 0, 0, p.Errorf} } // Take reads nb bytes. diff --git a/vendor/github.com/mjl-/bstore/query.go b/vendor/github.com/mjl-/bstore/query.go index c2bbaa2..2b7efa1 100644 --- a/vendor/github.com/mjl-/bstore/query.go +++ b/vendor/github.com/mjl-/bstore/query.go @@ -924,15 +924,28 @@ func (q *Query[T]) Delete() (deleted int, rerr error) { return 0, q.err } - n := 0 + // We collect the records to delete first, then delete them. + type work struct { + bk []byte + rov reflect.Value + } + var deletes []work err := q.foreachKey(true, true, func(bk []byte, ov T) error { - n++ rov := reflect.ValueOf(ov) q.gather(ov, rov) - q.stats.Delete++ - return q.xtx.delete(q.exec.rb, q.st, bk, rov) + deletes = append(deletes, work{bk, rov}) + return nil }) - return n, err + if err != nil { + return 0, err + } + for _, w := range deletes { + q.stats.Delete++ + if err := q.xtx.delete(q.exec.rb, q.st, w.bk, w.rov); err != nil { + return 0, err + } + } + return len(deletes), nil } // Get returns the single selected record. @@ -1081,21 +1094,37 @@ next: } func (q *Query[T]) update(fields []reflect.StructField, values []reflect.Value) (int, error) { - n := 0 - ov := reflect.New(q.st.Type).Elem() + // todo: we could check if the updated fields are not relevant for the cursor (not in filter/sort query) and update inside foreach. + // We first gather all records to be updated (using the query), then update the + // records. + type work struct { + bk []byte + rv reflect.Value + ov reflect.Value + } + var updates []work err := q.foreachKey(true, true, func(bk []byte, v T) error { - n++ rv := reflect.ValueOf(&v).Elem() + ov := reflect.New(q.st.Type).Elem() ov.Set(rv) for i, sf := range fields { frv := rv.FieldByIndex(sf.Index) frv.Set(values[i]) } q.gather(v, rv) - q.stats.Update++ - return q.xtx.update(q.exec.rb, q.st, rv, ov, bk) + updates = append(updates, work{bk, rv, ov}) + return nil }) - return n, err + if err != nil { + return 0, err + } + for _, w := range updates { + q.stats.Update++ + if err := q.xtx.update(q.exec.rb, q.st, w.rv, w.ov, w.bk); err != nil { + return 0, err + } + } + return len(updates), nil } // IDs sets idsptr to the primary keys of selected records. Idptrs must be a @@ -1201,6 +1230,9 @@ var StopForEach error = errors.New("stop foreach") // ForEach calls fn on each selected record. // If fn returns StopForEach, ForEach stops iterating, so no longer calls fn, // and returns nil. +// Fn must not update values, the internal cursor is not repositioned between +// invocations of fn, which would cause undefined behaviour (in practice, +// matching values could be skipped). func (q *Query[T]) ForEach(fn func(value T) error) (rerr error) { defer q.finish(&rerr) q.checkNotNext() diff --git a/vendor/modules.txt b/vendor/modules.txt index 62d0a72..745840e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -19,7 +19,7 @@ github.com/mjl-/adns/internal/singleflight # github.com/mjl-/autocert v0.0.0-20231013072455-c361ae2e20a6 ## explicit; go 1.20 github.com/mjl-/autocert -# github.com/mjl-/bstore v0.0.2 +# github.com/mjl-/bstore v0.0.3 ## explicit; go 1.19 github.com/mjl-/bstore # github.com/mjl-/sconf v0.0.5