github.com/decred/dcrlnd@v0.7.6/kvdb/postgres/readwrite_cursor.go (about) 1 //go:build kvdb_postgres 2 // +build kvdb_postgres 3 4 package postgres 5 6 import ( 7 "database/sql" 8 9 "github.com/btcsuite/btcwallet/walletdb" 10 ) 11 12 // readWriteCursor holds a reference to the cursors bucket, the value 13 // prefix and the current key used while iterating. 14 type readWriteCursor struct { 15 bucket *readWriteBucket 16 17 // currKey holds the current key of the cursor. 18 currKey []byte 19 } 20 21 func newReadWriteCursor(b *readWriteBucket) *readWriteCursor { 22 return &readWriteCursor{ 23 bucket: b, 24 } 25 } 26 27 // First positions the cursor at the first key/value pair and returns 28 // the pair. 29 func (c *readWriteCursor) First() ([]byte, []byte) { 30 var ( 31 key []byte 32 value []byte 33 ) 34 row, cancel := c.bucket.tx.QueryRow( 35 "SELECT key, value FROM " + c.bucket.table + " WHERE " + 36 parentSelector(c.bucket.id) + 37 " ORDER BY key LIMIT 1", 38 ) 39 defer cancel() 40 err := row.Scan(&key, &value) 41 42 switch { 43 case err == sql.ErrNoRows: 44 return nil, nil 45 46 case err != nil: 47 panic(err) 48 } 49 50 // Copy current key to prevent modification by the caller. 51 c.currKey = make([]byte, len(key)) 52 copy(c.currKey, key) 53 54 return key, value 55 } 56 57 // Last positions the cursor at the last key/value pair and returns the 58 // pair. 59 func (c *readWriteCursor) Last() ([]byte, []byte) { 60 var ( 61 key []byte 62 value []byte 63 ) 64 row, cancel := c.bucket.tx.QueryRow( 65 "SELECT key, value FROM " + c.bucket.table + " WHERE " + 66 parentSelector(c.bucket.id) + 67 " ORDER BY key DESC LIMIT 1", 68 ) 69 defer cancel() 70 err := row.Scan(&key, &value) 71 72 switch { 73 case err == sql.ErrNoRows: 74 return nil, nil 75 76 case err != nil: 77 panic(err) 78 } 79 80 // Copy current key to prevent modification by the caller. 81 c.currKey = make([]byte, len(key)) 82 copy(c.currKey, key) 83 84 return key, value 85 } 86 87 // Next moves the cursor one key/value pair forward and returns the new 88 // pair. 89 func (c *readWriteCursor) Next() ([]byte, []byte) { 90 var ( 91 key []byte 92 value []byte 93 ) 94 row, cancel := c.bucket.tx.QueryRow( 95 "SELECT key, value FROM "+c.bucket.table+" WHERE "+ 96 parentSelector(c.bucket.id)+ 97 " AND key>$1 ORDER BY key LIMIT 1", 98 c.currKey, 99 ) 100 defer cancel() 101 err := row.Scan(&key, &value) 102 103 switch { 104 case err == sql.ErrNoRows: 105 return nil, nil 106 107 case err != nil: 108 panic(err) 109 } 110 111 // Copy current key to prevent modification by the caller. 112 c.currKey = make([]byte, len(key)) 113 copy(c.currKey, key) 114 115 return key, value 116 } 117 118 // Prev moves the cursor one key/value pair backward and returns the new 119 // pair. 120 func (c *readWriteCursor) Prev() ([]byte, []byte) { 121 var ( 122 key []byte 123 value []byte 124 ) 125 row, cancel := c.bucket.tx.QueryRow( 126 "SELECT key, value FROM "+c.bucket.table+" WHERE "+ 127 parentSelector(c.bucket.id)+ 128 " AND key<$1 ORDER BY key DESC LIMIT 1", 129 c.currKey, 130 ) 131 defer cancel() 132 err := row.Scan(&key, &value) 133 134 switch { 135 case err == sql.ErrNoRows: 136 return nil, nil 137 138 case err != nil: 139 panic(err) 140 } 141 142 // Copy current key to prevent modification by the caller. 143 c.currKey = make([]byte, len(key)) 144 copy(c.currKey, key) 145 146 return key, value 147 } 148 149 // Seek positions the cursor at the passed seek key. If the key does 150 // not exist, the cursor is moved to the next key after seek. Returns 151 // the new pair. 152 func (c *readWriteCursor) Seek(seek []byte) ([]byte, []byte) { 153 // Convert nil to empty slice, otherwise sql mapping won't be correct 154 // and no keys are found. 155 if seek == nil { 156 seek = []byte{} 157 } 158 159 var ( 160 key []byte 161 value []byte 162 ) 163 row, cancel := c.bucket.tx.QueryRow( 164 "SELECT key, value FROM "+c.bucket.table+" WHERE "+ 165 parentSelector(c.bucket.id)+ 166 " AND key>=$1 ORDER BY key LIMIT 1", 167 seek, 168 ) 169 defer cancel() 170 err := row.Scan(&key, &value) 171 172 switch { 173 case err == sql.ErrNoRows: 174 return nil, nil 175 176 case err != nil: 177 panic(err) 178 } 179 180 // Copy current key to prevent modification by the caller. 181 c.currKey = make([]byte, len(key)) 182 copy(c.currKey, key) 183 184 return key, value 185 } 186 187 // Delete removes the current key/value pair the cursor is at without 188 // invalidating the cursor. Returns ErrIncompatibleValue if attempted 189 // when the cursor points to a nested bucket. 190 func (c *readWriteCursor) Delete() error { 191 // Get first record at or after cursor. 192 var key []byte 193 row, cancel := c.bucket.tx.QueryRow( 194 "SELECT key FROM "+c.bucket.table+" WHERE "+ 195 parentSelector(c.bucket.id)+ 196 " AND key>=$1 ORDER BY key LIMIT 1", 197 c.currKey, 198 ) 199 defer cancel() 200 err := row.Scan(&key) 201 202 switch { 203 case err == sql.ErrNoRows: 204 return nil 205 206 case err != nil: 207 panic(err) 208 } 209 210 // Delete record. 211 result, err := c.bucket.tx.Exec( 212 "DELETE FROM "+c.bucket.table+" WHERE "+ 213 parentSelector(c.bucket.id)+ 214 " AND key=$1 AND value IS NOT NULL", 215 key, 216 ) 217 if err != nil { 218 panic(err) 219 } 220 221 rows, err := result.RowsAffected() 222 if err != nil { 223 return err 224 } 225 226 // The key exists but nothing has been deleted. This means that the key 227 // must have been a bucket key. 228 if rows != 1 { 229 return walletdb.ErrIncompatibleValue 230 } 231 232 return err 233 }