rocksiterator.nim
1 # Nim-RocksDB 2 # Copyright 2024-2025 Status Research & Development GmbH 3 # Licensed under either of 4 # 5 # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 6 # * GPL license, version 2.0, ([LICENSE-GPLv2](LICENSE-GPLv2) or https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) 7 # 8 # at your option. This file may not be copied, modified, or distributed except according to those terms. 9 10 ## A `RocksIteratorRef` is a reference to a RocksDB iterator which supports 11 ## iterating over the key value pairs in a column family. 12 13 {.push raises: [].} 14 15 import ./lib/librocksdb, ./internal/utils, ./options/readopts, ./rocksresult, rocksslice 16 17 export rocksresult, rocksslice 18 19 type 20 RocksIteratorPtr* = ptr rocksdb_iterator_t 21 22 RocksIteratorRef* = ref object 23 cPtr: RocksIteratorPtr 24 readOpts: ReadOptionsRef 25 26 PinnableSlicePtr = ptr rocksdb_pinnableslice_t 27 28 MultiGetIteratorRef* = ref seq[PinnableSlicePtr] 29 30 func init*(T: type MultiGetIteratorRef, len: int): T = 31 doAssert len > 0 32 let iter = new T 33 iter[] = 34 when NimMajor >= 2 and NimMinor >= 2: 35 newSeqUninit[PinnableSlicePtr](len) 36 else: 37 newSeq[PinnableSlicePtr](len) 38 iter 39 40 func isClosed*(iter: MultiGetIteratorRef): bool = 41 iter[].len() == 0 42 43 func close*(iter: MultiGetIteratorRef) = 44 for pSlice in iter[]: 45 rocksdb_pinnableslice_destroy(pSlice) 46 iter[].setLen(0) 47 48 iterator items*( 49 iter: MultiGetIteratorRef, autoClose: static bool = true 50 ): Opt[RocksDbSlice] = 51 ## Iterates over the values in the iterator returning an optional slice 52 ## for each value. The iterator is automatically closed after the iteration 53 ## unless autoClose is set to false. 54 doAssert not iter.isClosed() 55 56 when autoClose: 57 defer: 58 iter.close() 59 60 for pSlice in iter[]: 61 if pSlice.isNil(): 62 yield Opt.none(RocksDbSlice) 63 continue 64 65 var len: csize_t = 0 66 let data = rocksdb_pinnableslice_value(pSlice, len.addr) 67 yield Opt.some(RocksDbSlice.init(data, len)) 68 69 proc newRocksIterator*( 70 cPtr: RocksIteratorPtr, readOpts: ReadOptionsRef 71 ): RocksIteratorRef = 72 doAssert not cPtr.isNil() 73 RocksIteratorRef(cPtr: cPtr, readOpts: readOpts) 74 75 template isClosed*(iter: RocksIteratorRef): bool = 76 ## Returns `true` if the iterator is closed and `false` otherwise. 77 iter.cPtr.isNil() 78 79 proc seekToKey*(iter: RocksIteratorRef, key: openArray[byte]) = 80 ## Seeks to the `key` argument in the column family. If the return code is 81 ## `false`, the iterator has become invalid and should be closed. 82 ## 83 ## It is not clear what happens when the `key` does not exist in the column 84 ## family. The guess is that the interation will proceed at the next key 85 ## position. This is suggested by a comment from the GO port at 86 ## 87 ## //github.com/DanielMorsing/rocksdb/blob/master/iterator.go: 88 ## 89 ## Seek moves the iterator the position of the key given or, if the key 90 ## doesn't exist, the next key that does exist in the database. If the 91 ## key doesn't exist, and there is no next key, the Iterator becomes 92 ## invalid. 93 ## 94 doAssert not iter.isClosed() 95 rocksdb_iter_seek(iter.cPtr, cast[cstring](key.unsafeAddrOrNil()), csize_t(key.len)) 96 97 proc seekToFirst*(iter: RocksIteratorRef) = 98 ## Seeks to the first entry in the column family. 99 doAssert not iter.isClosed() 100 rocksdb_iter_seek_to_first(iter.cPtr) 101 102 proc seekToLast*(iter: RocksIteratorRef) = 103 ## Seeks to the last entry in the column family. 104 doAssert not iter.isClosed() 105 rocksdb_iter_seek_to_last(iter.cPtr) 106 107 proc isValid*(iter: RocksIteratorRef): bool = 108 ## Returns `true` if the iterator is valid and `false` otherwise. 109 rocksdb_iter_valid(iter.cPtr).bool 110 111 proc next*(iter: RocksIteratorRef) = 112 ## Seeks to the next entry in the column family. 113 rocksdb_iter_next(iter.cPtr) 114 115 proc prev*(iter: RocksIteratorRef) = 116 ## Seeks to the previous entry in the column family. 117 rocksdb_iter_prev(iter.cPtr) 118 119 template keyOpenArray(iter: RocksIteratorRef): openArray[byte] = 120 let kSlice = rocksdb_iter_key_slice(iter.cPtr) 121 toOpenArray(kSlice.data, kSlice.size) 122 123 proc key*(iter: RocksIteratorRef, onData: DataProc) = 124 ## Returns the current key using the provided `onData` callback. 125 onData(iter.keyOpenArray()) 126 127 template key*(iter: RocksIteratorRef, asOpenArray: static bool = false): auto = 128 ## Returns the current key. 129 when asOpenArray: 130 iter.keyOpenArray() 131 else: 132 @(iter.keyOpenArray()) 133 134 template valueOpenArray(iter: RocksIteratorRef): openArray[byte] = 135 let vSlice = rocksdb_iter_value_slice(iter.cPtr) 136 toOpenArray(vSlice.data, vSlice.size) 137 138 proc value*(iter: RocksIteratorRef, onData: DataProc) = 139 ## Returns the current value using the provided `onData` callback. 140 onData(iter.valueOpenArray()) 141 142 template value*(iter: RocksIteratorRef, asOpenArray: static bool = false): auto = 143 ## Returns the current value. 144 when asOpenArray: 145 iter.valueOpenArray() 146 else: 147 @(iter.valueOpenArray()) 148 149 proc status*(iter: RocksIteratorRef): RocksDBResult[void] = 150 ## Returns the status of the iterator. 151 doAssert not iter.isClosed() 152 153 var errors: cstring 154 rocksdb_iter_get_error(iter.cPtr, cast[cstringArray](errors.addr)) 155 bailOnErrors(errors) 156 157 ok() 158 159 proc close*(iter: RocksIteratorRef) = 160 ## Closes the `RocksIteratorRef`. 161 if not iter.isClosed(): 162 rocksdb_iter_destroy(iter.cPtr) 163 iter.cPtr = nil 164 165 autoCloseNonNil(iter.readOpts) 166 167 iterator pairs*( 168 iter: RocksIteratorRef, autoClose: static bool = true 169 ): tuple[key: seq[byte], value: seq[byte]] = 170 ## Iterates over the key value pairs in the column family yielding them in 171 ## the form of a tuple of seq[byte]. The iterator is automatically closed 172 ## after the iteration unless autoClose is set to false. 173 doAssert not iter.isClosed() 174 when autoClose: 175 defer: 176 iter.close() 177 178 iter.seekToFirst() 179 180 while iter.isValid(): 181 yield (iter.key(), iter.value()) 182 iter.next() 183 184 func keySlice(iter: RocksIteratorRef): RocksDbSlice = 185 ## Returns the current key as a slice. 186 let kSlice = rocksdb_iter_key_slice(iter.cPtr) 187 RocksDbSlice.init(kSlice.data, kSlice.size) 188 189 func valueSlice(iter: RocksIteratorRef): RocksDbSlice = 190 ## Returns the current value as a slice. 191 let vSlice = rocksdb_iter_value_slice(iter.cPtr) 192 RocksDbSlice.init(vSlice.data, vSlice.size) 193 194 iterator slicePairs*( 195 iter: RocksIteratorRef, autoClose: static bool = true 196 ): tuple[key: RocksDbSlice, value: RocksDbSlice] = 197 ## Iterates over the key value pairs in the column family yielding them in 198 ## the form of a tuple of slices. The iterator is automatically closed 199 ## after the iteration unless autoClose is set to false. 200 doAssert not iter.isClosed() 201 when autoClose: 202 defer: 203 iter.close() 204 205 iter.seekToFirst() 206 207 while iter.isValid(): 208 yield (iter.keySlice(), iter.valueSlice()) 209 iter.next()