Skip to content

Commit bf10537

Browse files
committed
kvdb/sqlbase: optimize Delete for the rw cursor
Similar to the write bucket, we can optimize for the case where the key already exists, allowing us to skip the extra `SELECT` statement.
1 parent a271eaf commit bf10537

File tree

1 file changed

+61
-0
lines changed

1 file changed

+61
-0
lines changed

kvdb/sqlbase/readwrite_cursor.go

+61
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ package sqlbase
44

55
import (
66
"database/sql"
7+
"errors"
8+
"fmt"
79

810
"github.com/btcsuite/btcwallet/walletdb"
911
)
@@ -230,3 +232,62 @@ func (c *readWriteCursor) Delete() error {
230232

231233
return err
232234
}
235+
236+
// Delete removes the current key/value pair the cursor is at without
237+
// invalidating the cursor. Returns ErrIncompatibleValue if attempted when the
238+
// cursor points to a nested bucket.
239+
func (c *readWriteCursor) Delete() error {
240+
if c.currKey == nil {
241+
// Cursor might not be positioned on a valid key.
242+
return errors.New("cursor not positioned on a key")
243+
}
244+
245+
// Attempt to delete the key the cursor is pointing at, but only if it's
246+
// a value.
247+
result, err := c.bucket.tx.Exec(
248+
"DELETE FROM "+c.bucket.table+" WHERE "+
249+
parentSelector(c.bucket.id)+
250+
" AND key=$1 AND value IS NOT NULL",
251+
c.currKey,
252+
)
253+
if err != nil {
254+
panic(err)
255+
}
256+
257+
rows, err := result.RowsAffected()
258+
if err != nil {
259+
return fmt.Errorf("error getting rows affected "+
260+
"for cursor key %x: %w", c.currKey, err)
261+
}
262+
263+
// If deletion succeeded, we are done.
264+
if rows == 1 {
265+
return nil
266+
}
267+
268+
// If rows == 0, the key either didn't exist anymore (concurrent
269+
// delete?) or it was a bucket. Check if it's a bucket.
270+
var existsAsBucket int
271+
rowCheck, cancelCheck := c.bucket.tx.QueryRow(
272+
"SELECT 1 FROM "+c.bucket.table+" WHERE "+
273+
parentSelector(c.bucket.id)+
274+
" AND key=$1 AND value IS NULL", c.currKey,
275+
)
276+
defer cancelCheck()
277+
278+
errCheck := rowCheck.Scan(&existsAsBucket)
279+
if errCheck == nil {
280+
// Found it, and value IS NULL -> It's a bucket.
281+
return walletdb.ErrIncompatibleValue
282+
}
283+
284+
if errCheck == sql.ErrNoRows {
285+
return nil
286+
}
287+
288+
// Some other error during the check.
289+
//
290+
// TODO(roasbeef): panic here like above/
291+
return fmt.Errorf("error checking if cursor key %x is "+
292+
"bucket: %w", c.currKey, errCheck)
293+
}

0 commit comments

Comments
 (0)