@@ -99,8 +99,15 @@ func (db *SQLite) GetMeta(key string) (*record.Meta, error) {
99
99
100
100
// Put stores a record in the database.
101
101
func (db * SQLite ) Put (r record.Record ) (record.Record , error ) {
102
- r .Lock ()
103
- defer r .Unlock ()
102
+ return db .putRecord (r , nil )
103
+ }
104
+
105
+ func (db * SQLite ) putRecord (r record.Record , tx * bob.Tx ) (record.Record , error ) {
106
+ // Lock record if in a transaction.
107
+ if tx != nil {
108
+ r .Lock ()
109
+ defer r .Unlock ()
110
+ }
104
111
105
112
// Serialize to JSON.
106
113
data , err := r .MarshalDataOnly (r , dsd .JSON )
@@ -127,12 +134,19 @@ func (db *SQLite) Put(r record.Record) (record.Record, error) {
127
134
defer db .lock .Unlock ()
128
135
129
136
// Simulate upsert with custom selection on conflict.
130
- _ , err = models .Records .Insert (
137
+ dbQuery : = models .Records .Insert (
131
138
& setter ,
132
139
im .OnConflict ("key" ).DoUpdate (
133
140
im .SetExcluded ("format" , "value" , "created" , "modified" , "expires" , "deleted" , "secret" , "crownjewel" ),
134
141
),
135
- ).Exec (db .ctx , db .bob )
142
+ )
143
+
144
+ // Execute in transaction or directly.
145
+ if tx != nil {
146
+ _ , err = dbQuery .Exec (db .ctx , tx )
147
+ } else {
148
+ _ , err = dbQuery .Exec (db .ctx , db .bob )
149
+ }
136
150
if err != nil {
137
151
return nil , err
138
152
}
@@ -150,16 +164,39 @@ func (db *SQLite) PutMany(shadowDelete bool) (chan<- record.Record, <-chan error
150
164
batch := make (chan record.Record , 100 )
151
165
errs := make (chan error , 1 )
152
166
167
+ tx , err := db .bob .BeginTx (db .ctx , nil )
168
+ if err != nil {
169
+ errs <- err
170
+ return batch , errs
171
+ }
172
+
153
173
// start handler
154
174
go func () {
155
- for r := range batch {
156
- _ , err := db .Put (r )
157
- if err != nil {
158
- errs <- err
159
- return
175
+ // Read all put records.
176
+ writeBatch:
177
+ for {
178
+ select {
179
+ case r := <- batch :
180
+ if r != nil {
181
+ // Write record.
182
+ _ , err := db .putRecord (r , & tx )
183
+ if err != nil {
184
+ errs <- err
185
+ break writeBatch
186
+ }
187
+ } else {
188
+ // Finalize transcation.
189
+ errs <- tx .Commit ()
190
+ return
191
+ }
192
+
193
+ case <- db .ctx .Done ():
194
+ break writeBatch
160
195
}
161
196
}
162
- errs <- nil
197
+
198
+ // Rollback transaction.
199
+ errs <- tx .Rollback ()
163
200
}()
164
201
165
202
return batch , errs
@@ -199,20 +236,25 @@ func (db *SQLite) queryExecutor(queryIter *iterator.Iterator, q *query.Query, lo
199
236
recordQuery = models .Records .View .Query ()
200
237
}
201
238
202
- // Get all records from query.
203
- // TODO: This will load all records into memory. While this is efficient and
204
- // will not block others from using the datbase, this might be quite a strain
205
- // on the system memory. Monitor and see if this is an issue.
239
+ // Get cursor to go over all records in the query.
206
240
db .lock .RLock ()
207
- records , err := models .RecordsQuery .All (recordQuery , db .ctx , db .bob )
241
+ cursor , err := models .RecordsQuery .Cursor (recordQuery , db .ctx , db .bob )
208
242
db .lock .RUnlock ()
209
243
if err != nil {
210
244
queryIter .Finish (err )
211
245
return
212
246
}
247
+ defer cursor .Close ()
213
248
214
249
recordsLoop:
215
- for _ , r := range records {
250
+ for cursor .Next () {
251
+ // Get next record
252
+ r , cErr := cursor .Get ()
253
+ if cErr != nil {
254
+ err = fmt .Errorf ("cursor error: %w" , cErr )
255
+ break recordsLoop
256
+ }
257
+
216
258
// Check if key matches.
217
259
if ! q .MatchesKey (r .Key ) {
218
260
continue recordsLoop
0 commit comments