diff --git a/rocksdb.nimble b/rocksdb.nimble index a5f109e..07946f1 100644 --- a/rocksdb.nimble +++ b/rocksdb.nimble @@ -1,5 +1,5 @@ packageName = "rocksdb" -version = "10.4.2.0" +version = "10.4.2.1" author = "Status Research & Development GmbH" description = "A wrapper for Facebook's RocksDB, an embeddable, persistent key-value store for fast storage" diff --git a/rocksdb/backup.nim b/rocksdb/backup.nim index 2c251d9..3a0f959 100644 --- a/rocksdb/backup.nim +++ b/rocksdb/backup.nim @@ -50,7 +50,7 @@ proc openBackupEngine*( BackupEngineRef(cPtr: backupEnginePtr, path: path, backupOpts: backupOpts) ok(engine) -proc isClosed*(backupEngine: BackupEngineRef): bool {.inline.} = +template isClosed*(backupEngine: BackupEngineRef): bool = ## Returns `true` if the `BackupEngineRef` has been closed. backupEngine.cPtr.isNil() diff --git a/rocksdb/columnfamily.nim b/rocksdb/columnfamily.nim index 7164cb8..a20634a 100644 --- a/rocksdb/columnfamily.nim +++ b/rocksdb/columnfamily.nim @@ -53,70 +53,91 @@ proc getColFamily*( ok(ColFamilyReadWrite(db: db, name: name, handle: ?db.getColFamilyHandle(name))) -proc db*(cf: ColFamilyReadOnly | ColFamilyReadWrite): auto {.inline.} = +template db*(cf: ColFamilyReadOnly | ColFamilyReadWrite): auto = ## Returns the underlying `RocksDbReadOnlyRef` or `RocksDbReadWriteRef`. cf.db -proc name*(cf: ColFamilyReadOnly | ColFamilyReadWrite): string {.inline.} = +template name*(cf: ColFamilyReadOnly | ColFamilyReadWrite): string = ## Returns the name of the column family. cf.name -proc handle*( - cf: ColFamilyReadOnly | ColFamilyReadWrite -): ColFamilyHandleRef {.inline.} = +template handle*(cf: ColFamilyReadOnly | ColFamilyReadWrite): ColFamilyHandleRef = ## Returns the name of the column family. cf.handle -proc get*( +template get*( cf: ColFamilyReadOnly | ColFamilyReadWrite, key: openArray[byte], onData: DataProc -): RocksDBResult[bool] {.inline.} = +): RocksDBResult[bool] = ## Gets the value of the given key from the column family using the `onData` ## callback. cf.db.get(key, onData, cf.handle) -proc get*( +template get*( cf: ColFamilyReadOnly | ColFamilyReadWrite, key: openArray[byte] -): RocksDBResult[seq[byte]] {.inline.} = +): RocksDBResult[seq[byte]] = ## Gets the value of the given key from the column family. cf.db.get(key, cf.handle) -proc put*( - cf: ColFamilyReadWrite, key, val: openArray[byte] -): RocksDBResult[void] {.inline.} = +template multiGet*( + cf: ColFamilyReadOnly | ColFamilyReadWrite, + keys: openArray[seq[byte]], + sortedInput = false, +): RocksDBResult[seq[seq[byte]]] = + ## Get a batch of values for the given set of keys. + cf.db.multiGet(keys, sortedInput, cf.handle) + +template put*(cf: ColFamilyReadWrite, key, val: openArray[byte]): RocksDBResult[void] = ## Puts a value for the given key into the column family. cf.db.put(key, val, cf.handle) -proc keyExists*( +template keyExists*( cf: ColFamilyReadOnly | ColFamilyReadWrite, key: openArray[byte] -): RocksDBResult[bool] {.inline.} = +): RocksDBResult[bool] = ## Checks if the given key exists in the column family. cf.db.keyExists(key, cf.handle) -proc delete*( - cf: ColFamilyReadWrite, key: openArray[byte] -): RocksDBResult[void] {.inline.} = +template delete*(cf: ColFamilyReadWrite, key: openArray[byte]): RocksDBResult[void] = ## Deletes the given key from the column family. cf.db.delete(key, cf.handle) -proc openIterator*( +template deleteRange*( + cf: ColFamilyReadWrite, startKey, endKey: openArray[byte] +): RocksDBResult[void] = + ## Deletes the given key range from the column family including startKey and + ## excluding endKey. + cf.db.deleteRange(startKey, endKey, cf.handle) + +template compactRange*( + cf: ColFamilyReadWrite, startKey, endKey: openArray[byte] +): RocksDBResult[void] = + ## Trigger range compaction for the given key range. + cf.db.compactRange(startKey, endKey, cf.handle) + +template suggestCompactRange*( + cf: ColFamilyReadWrite, startKey, endKey: openArray[byte] +): RocksDBResult[void] = + ## Suggest the range to compact. + cf.db.suggestCompactRange(startKey, endKey, cf.handle) + +template openIterator*( cf: ColFamilyReadOnly | ColFamilyReadWrite, readOpts = defaultReadOptions(autoClose = true), -): RocksDBResult[RocksIteratorRef] {.inline.} = +): RocksDBResult[RocksIteratorRef] = ## Opens an `RocksIteratorRef` for the given column family. cf.db.openIterator(readOpts, cf.handle) -proc openWriteBatch*(cf: ColFamilyReadWrite): WriteBatchRef {.inline.} = +template openWriteBatch*(cf: ColFamilyReadWrite): WriteBatchRef = ## Opens a `WriteBatchRef` for the given column family. cf.db.openWriteBatch(cf.handle) -proc openWriteBatchWithIndex*( +template openWriteBatchWithIndex*( cf: ColFamilyReadWrite, reservedBytes = 0, overwriteKey = false -): WriteBatchWIRef {.inline.} = +): WriteBatchWIRef = ## Opens a `WriteBatchRef` for the given column family. cf.db.openWriteBatchWithIndex(reservedBytes, overwriteKey, cf.handle) -proc write*( +template write*( cf: ColFamilyReadWrite, updates: WriteBatchRef | WriteBatchWIRef -): RocksDBResult[void] {.inline.} = +): RocksDBResult[void] = ## Writes the updates in the `WriteBatchRef` to the column family. cf.db.write(updates) diff --git a/rocksdb/columnfamily/cfdescriptor.nim b/rocksdb/columnfamily/cfdescriptor.nim index c02eb7e..2bf77eb 100644 --- a/rocksdb/columnfamily/cfdescriptor.nim +++ b/rocksdb/columnfamily/cfdescriptor.nim @@ -24,25 +24,25 @@ proc initColFamilyDescriptor*( ): ColFamilyDescriptor = ColFamilyDescriptor(name: name, options: options) -proc name*(descriptor: ColFamilyDescriptor): string {.inline.} = +template name*(descriptor: ColFamilyDescriptor): string = descriptor.name -proc options*(descriptor: ColFamilyDescriptor): ColFamilyOptionsRef {.inline.} = +template options*(descriptor: ColFamilyDescriptor): ColFamilyOptionsRef = descriptor.options -proc autoClose*(descriptor: ColFamilyDescriptor): bool {.inline.} = +template autoClose*(descriptor: ColFamilyDescriptor): bool = descriptor.options.autoClose -proc isDefault*(descriptor: ColFamilyDescriptor): bool {.inline.} = +template isDefault*(descriptor: ColFamilyDescriptor): bool = descriptor.name == DEFAULT_COLUMN_FAMILY_NAME -proc defaultColFamilyDescriptor*(autoClose = false): ColFamilyDescriptor {.inline.} = +proc defaultColFamilyDescriptor*(autoClose = false): ColFamilyDescriptor = initColFamilyDescriptor( DEFAULT_COLUMN_FAMILY_NAME, defaultColFamilyOptions(autoClose = autoClose) ) -proc isClosed*(descriptor: ColFamilyDescriptor): bool {.inline.} = +template isClosed*(descriptor: ColFamilyDescriptor): bool = descriptor.options.isClosed() -proc close*(descriptor: ColFamilyDescriptor) {.inline.} = +template close*(descriptor: ColFamilyDescriptor) = descriptor.options.close() diff --git a/rocksdb/columnfamily/cfhandle.nim b/rocksdb/columnfamily/cfhandle.nim index abb6d01..dc9a560 100644 --- a/rocksdb/columnfamily/cfhandle.nim +++ b/rocksdb/columnfamily/cfhandle.nim @@ -20,7 +20,7 @@ type proc newColFamilyHandle*(cPtr: ColFamilyHandlePtr): ColFamilyHandleRef = ColFamilyHandleRef(cPtr: cPtr) -proc isClosed*(handle: ColFamilyHandleRef): bool {.inline.} = +template isClosed*(handle: ColFamilyHandleRef): bool = handle.cPtr.isNil() proc cPtr*(handle: ColFamilyHandleRef): ColFamilyHandlePtr = diff --git a/rocksdb/columnfamily/cfopts.nim b/rocksdb/columnfamily/cfopts.nim index f3820ba..5c2e852 100644 --- a/rocksdb/columnfamily/cfopts.nim +++ b/rocksdb/columnfamily/cfopts.nim @@ -43,7 +43,7 @@ type proc createFixedPrefix*(value: int): SlicetransformRef = SlicetransformRef(cPtr: rocksdb_slicetransform_create_fixed_prefix(value.csize_t)) -proc isClosed*(s: SlicetransformRef): bool {.inline.} = +template isClosed*(s: SlicetransformRef): bool = s.cPtr.isNil() proc cPtr*(s: SlicetransformRef): SlicetransformPtr = @@ -58,7 +58,7 @@ proc close*(s: SlicetransformRef) = proc createColFamilyOptions*(autoClose = false): ColFamilyOptionsRef = ColFamilyOptionsRef(cPtr: rocksdb_options_create(), autoClose: autoClose) -proc isClosed*(cfOpts: ColFamilyOptionsRef): bool {.inline.} = +template isClosed*(cfOpts: ColFamilyOptionsRef): bool = cfOpts.cPtr.isNil() proc cPtr*(cfOpts: ColFamilyOptionsRef): ColFamilyOptionsPtr = diff --git a/rocksdb/internal/cftable.nim b/rocksdb/internal/cftable.nim index 32797d4..9054bb1 100644 --- a/rocksdb/internal/cftable.nim +++ b/rocksdb/internal/cftable.nim @@ -27,10 +27,10 @@ proc newColFamilyTable*( ColFamilyTableRef(columnFamilies: cfTable) -proc isClosed*(table: ColFamilyTableRef): bool {.inline.} = +template isClosed*(table: ColFamilyTableRef): bool = table.columnFamilies.isNil() -proc get*(table: ColFamilyTableRef, name: string): ColFamilyHandleRef {.inline.} = +template get*(table: ColFamilyTableRef, name: string): ColFamilyHandleRef = table.columnFamilies.getOrDefault(name) proc close*(table: ColFamilyTableRef) = diff --git a/rocksdb/optimistictxdb.nim b/rocksdb/optimistictxdb.nim index e54ff46..632baf2 100644 --- a/rocksdb/optimistictxdb.nim +++ b/rocksdb/optimistictxdb.nim @@ -94,7 +94,7 @@ proc getColFamilyHandle*( else: ok(cfHandle) -proc isClosed*(db: OptimisticTxDbRef): bool {.inline.} = +template isClosed*(db: OptimisticTxDbRef): bool = ## Returns `true` if the `OptimisticTxDbRef` has been closed. db.cPtr.isNil() diff --git a/rocksdb/options/backupopts.nim b/rocksdb/options/backupopts.nim index 18c5659..cfae4ba 100644 --- a/rocksdb/options/backupopts.nim +++ b/rocksdb/options/backupopts.nim @@ -25,7 +25,7 @@ proc createBackupEngineOptions*( cPtr: rocksdb_backup_engine_options_create(backupDir.cstring), autoClose: autoClose ) -proc isClosed*(backupOpts: BackupEngineOptionsRef): bool {.inline.} = +template isClosed*(backupOpts: BackupEngineOptionsRef): bool = backupOpts.cPtr.isNil() proc cPtr*(backupOpts: BackupEngineOptionsRef): BackupEngineOptionsPtr = @@ -53,7 +53,7 @@ opt callbackTriggerIntervalSize, int, uint64 proc defaultBackupEngineOptions*( backupDir: string, autoClose = false -): BackupEngineOptionsRef {.inline.} = +): BackupEngineOptionsRef = let backupOpts = createBackupEngineOptions(backupDir, autoClose) # TODO: set defaults here diff --git a/rocksdb/options/dbopts.nim b/rocksdb/options/dbopts.nim index 76110f9..d709fa5 100644 --- a/rocksdb/options/dbopts.nim +++ b/rocksdb/options/dbopts.nim @@ -24,7 +24,7 @@ type proc createDbOptions*(autoClose = false): DbOptionsRef = DbOptionsRef(cPtr: rocksdb_options_create(), autoClose: autoClose) -proc isClosed*(dbOpts: DbOptionsRef): bool {.inline.} = +template isClosed*(dbOpts: DbOptionsRef): bool = dbOpts.cPtr.isNil() proc cPtr*(dbOpts: DbOptionsRef): DbOptionsPtr = diff --git a/rocksdb/options/readopts.nim b/rocksdb/options/readopts.nim index 5cebe49..2dfd862 100644 --- a/rocksdb/options/readopts.nim +++ b/rocksdb/options/readopts.nim @@ -23,7 +23,7 @@ type proc createReadOptions*(autoClose = false): ReadOptionsRef = ReadOptionsRef(cPtr: rocksdb_readoptions_create(), autoClose: autoClose) -proc isClosed*(readOpts: ReadOptionsRef): bool {.inline.} = +template isClosed*(readOpts: ReadOptionsRef): bool = readOpts.cPtr.isNil() proc cPtr*(readOpts: ReadOptionsRef): ReadOptionsPtr = @@ -52,12 +52,13 @@ opt maxSkippableInternalKeys, int, csize_t opt ignoreRangeDeletions, bool, uint8 opt deadline, int, uint64 opt ioTimeout, int, uint64 +opt asyncIo, bool, uint8 proc setSnapshot*(readOpts: ReadOptionsRef, snapshot: SnapshotRef) = doAssert not readOpts.isClosed() rocksdb_readoptions_set_snapshot(readOpts.cPtr, snapshot.cPtr) -proc defaultReadOptions*(autoClose = false): ReadOptionsRef {.inline.} = +proc defaultReadOptions*(autoClose = false): ReadOptionsRef = let readOpts = createReadOptions(autoClose) # TODO: set prefered defaults diff --git a/rocksdb/options/writeopts.nim b/rocksdb/options/writeopts.nim index 62c3f48..b3e8354 100644 --- a/rocksdb/options/writeopts.nim +++ b/rocksdb/options/writeopts.nim @@ -21,7 +21,7 @@ type proc createWriteOptions*(autoClose = false): WriteOptionsRef = WriteOptionsRef(cPtr: rocksdb_writeoptions_create(), autoClose: autoClose) -proc isClosed*(writeOpts: WriteOptionsRef): bool {.inline.} = +template isClosed*(writeOpts: WriteOptionsRef): bool = writeOpts.cPtr.isNil() proc cPtr*(writeOpts: WriteOptionsRef): WriteOptionsPtr = @@ -51,7 +51,7 @@ proc disableWAL*(writeOpts: WriteOptionsRef): bool = doAssert not writeOpts.isClosed() rocksdb_writeoptions_get_disable_WAL(writeOpts.cPtr).bool -proc defaultWriteOptions*(autoClose = false): WriteOptionsRef {.inline.} = +proc defaultWriteOptions*(autoClose = false): WriteOptionsRef = let writeOpts = createWriteOptions(autoClose) # TODO: set prefered defaults diff --git a/rocksdb/rocksdb.nim b/rocksdb/rocksdb.nim index 77a3d41..b1a50bd 100644 --- a/rocksdb/rocksdb.nim +++ b/rocksdb/rocksdb.nim @@ -221,11 +221,11 @@ proc getColFamilyHandle*( else: ok(cfHandle) -proc isClosed*(db: RocksDbRef): bool {.inline.} = +template isClosed*(db: RocksDbRef): bool = ## Returns `true` if the database has been closed and `false` otherwise. db.cPtr.isNil() -proc cPtr*(db: RocksDbRef): RocksDbPtr {.inline.} = +proc cPtr*(db: RocksDbRef): RocksDbPtr = ## Get the underlying database pointer. doAssert not db.isClosed() db.cPtr @@ -282,6 +282,74 @@ proc get*( dataRes.err(res.error()) +proc multiGet*( + db: RocksDbRef, + keys: openArray[seq[byte]], + sortedInput = false, + cfHandle = db.defaultCfHandle, +): RocksDBResult[seq[seq[byte]]] = + ## Get a batch of values for the given set of keys. + ## + ## The multiGet API improves performance by batching operations + ## in the read path for greater efficiency. Currently, only the block based + ## table format with full filters are supported. Other table formats such + ## as plain table, block based table with block based filters and + ## partitioned indexes will still work, but will not get any performance + ## benefits. + ## + ## sortedInput - If true, it means the input keys are already sorted by key + ## order, so the MultiGet() API doesn't have to sort them again. If false, + ## the keys will be copied and sorted internally by the API - the input + ## array will not be modified. + assert keys.len() > 0 + + var + keysList = keys.mapIt(cast[cstring](it[0].addr)) + keysListSizes = keys.mapIt(csize_t(it.len)) + errors = newSeq[cstring](keys.len()) + + var values = + when NimMajor >= 2 and NimMinor >= 2: + newSeqUninit[ptr rocksdb_pinnableslice_t](keys.len) + else: + newSeq[ptr rocksdb_pinnableslice_t](keys.len) + + rocksdb_batched_multi_get_cf( + db.cPtr, + db.readOpts.cPtr, + cfHandle.cPtr, + csize_t(keys.len), + cast[cstringArray](keysList[0].addr), + keysListSizes[0].addr, + values[0].addr, + cast[cstringArray](errors[0].addr), + sortedInput, + ) + + for e in errors: + if not e.isNil: + let res = err($(e)) + rocksdb_free(e) + return res + + var data = newSeq[seq[byte]](keys.len()) + for i, v in values: + var vLen: csize_t + let src = rocksdb_pinnableslice_value(v, vLen.addr) + + if vLen > 0: + var dest = + when NimMajor >= 2 and NimMinor >= 2: + newSeqUninit[byte](vLen.int) + else: + newSeq[byte](vLen.int) + copyMem(dest[0].addr, src, vLen) + data[i] = dest + + rocksdb_pinnableslice_destroy(v) + + ok(data) + proc put*( db: RocksDbReadWriteRef, key, val: openArray[byte], cfHandle = db.defaultCfHandle ): RocksDBResult[void] = @@ -360,6 +428,69 @@ proc delete*( ok() +proc deleteRange*( + db: RocksDbReadWriteRef, + startKey, endKey: openArray[byte], + cfHandle = db.defaultCfHandle, +): RocksDBResult[void] = + ## Removes the database entries in the range [startKey, endKey), i.e. including + ## startKey and excluding endKey. It is not an error if no keys exist in the + ## range ["beginKey", "endKey"). + + var errors: cstring + rocksdb_delete_range_cf( + db.cPtr, + db.writeOpts.cPtr, + cfHandle.cPtr, + cast[cstring](startKey.unsafeAddrOrNil()), + csize_t(startKey.len), + cast[cstring](endKey.unsafeAddrOrNil()), + csize_t(endKey.len), + cast[cstringArray](errors.addr), + ) + bailOnErrors(errors) + + ok() + +proc compactRange*( + db: RocksDbReadWriteRef, + startKey, endKey: openArray[byte], + cfHandle = db.defaultCfHandle, +): RocksDBResult[void] = + ## Trigger range compaction for the given key range. + + rocksdb_compact_range_cf( + db.cPtr, + cfHandle.cPtr, + cast[cstring](startKey.unsafeAddrOrNil()), + csize_t(startKey.len), + cast[cstring](endKey.unsafeAddrOrNil()), + csize_t(endKey.len), + ) + + ok() + +proc suggestCompactRange*( + db: RocksDbReadWriteRef, + startKey, endKey: openArray[byte], + cfHandle = db.defaultCfHandle, +): RocksDBResult[void] = + ## Suggest the range to compact. + + var errors: cstring + rocksdb_suggest_compact_range_cf( + db.cPtr, + cfHandle.cPtr, + cast[cstring](startKey.unsafeAddrOrNil()), + csize_t(startKey.len), + cast[cstring](endKey.unsafeAddrOrNil()), + csize_t(endKey.len), + cast[cstringArray](errors.addr), + ) + bailOnErrors(errors) + + ok() + proc openIterator*( db: RocksDbRef, readOpts = defaultReadOptions(autoClose = true), diff --git a/rocksdb/rocksiterator.nim b/rocksdb/rocksiterator.nim index 68468aa..e6b9add 100644 --- a/rocksdb/rocksiterator.nim +++ b/rocksdb/rocksiterator.nim @@ -29,7 +29,7 @@ proc newRocksIterator*( doAssert not cPtr.isNil() RocksIteratorRef(cPtr: cPtr, readOpts: readOpts) -proc isClosed*(iter: RocksIteratorRef): bool {.inline.} = +template isClosed*(iter: RocksIteratorRef): bool = ## Returns `true` if the iterator is closed and `false` otherwise. iter.cPtr.isNil() diff --git a/rocksdb/snapshot.nim b/rocksdb/snapshot.nim index 117e181..d42e642 100644 --- a/rocksdb/snapshot.nim +++ b/rocksdb/snapshot.nim @@ -28,7 +28,7 @@ proc newSnapshot*(cPtr: SnapshotPtr, kind: SnapshotType): SnapshotRef = doAssert not cPtr.isNil() SnapshotRef(cPtr: cPtr, kind: kind) -proc isClosed*(snapshot: SnapshotRef): bool {.inline.} = +template isClosed*(snapshot: SnapshotRef): bool = ## Returns `true` if the `SnapshotRef` has been closed and `false` otherwise. snapshot.cPtr.isNil() diff --git a/rocksdb/sstfilewriter.nim b/rocksdb/sstfilewriter.nim index 7d6c6c5..c84d58f 100644 --- a/rocksdb/sstfilewriter.nim +++ b/rocksdb/sstfilewriter.nim @@ -49,7 +49,7 @@ proc openSstFileWriter*( ok(writer) -proc isClosed*(writer: SstFileWriterRef): bool {.inline.} = +template isClosed*(writer: SstFileWriterRef): bool = ## Returns `true` if the `SstFileWriterRef` is closed and `false` otherwise. writer.cPtr.isNil() diff --git a/rocksdb/transactiondb.nim b/rocksdb/transactiondb.nim index 781a044..cd8e333 100644 --- a/rocksdb/transactiondb.nim +++ b/rocksdb/transactiondb.nim @@ -102,7 +102,7 @@ proc getColFamilyHandle*( else: ok(cfHandle) -proc isClosed*(db: TransactionDbRef): bool {.inline.} = +template isClosed*(db: TransactionDbRef): bool = ## Returns `true` if the `TransactionDbRef` has been closed. db.cPtr.isNil() diff --git a/rocksdb/transactions/otxopts.nim b/rocksdb/transactions/otxopts.nim index 89a60c3..b1bacdc 100644 --- a/rocksdb/transactions/otxopts.nim +++ b/rocksdb/transactions/otxopts.nim @@ -23,7 +23,7 @@ proc createOptimisticTxOptions*(autoClose = false): OptimisticTxOptionsRef = cPtr: rocksdb_optimistictransaction_options_create(), autoClose: autoClose ) -proc isClosed*(txOpts: OptimisticTxOptionsRef): bool {.inline.} = +template isClosed*(txOpts: OptimisticTxOptionsRef): bool = txOpts.cPtr.isNil() proc cPtr*(txOpts: OptimisticTxOptionsRef): OptimisticTxOptionsPtr = @@ -37,7 +37,7 @@ template setOpt(nname, ntyp, ctyp: untyped) = setOpt setSnapshot, bool, uint8 -proc defaultOptimisticTxOptions*(autoClose = false): OptimisticTxOptionsRef {.inline.} = +proc defaultOptimisticTxOptions*(autoClose = false): OptimisticTxOptionsRef = let txOpts = createOptimisticTxOptions(autoClose) # TODO: set prefered defaults diff --git a/rocksdb/transactions/transaction.nim b/rocksdb/transactions/transaction.nim index 8c27bcd..ca2dc6f 100644 --- a/rocksdb/transactions/transaction.nim +++ b/rocksdb/transactions/transaction.nim @@ -56,7 +56,7 @@ proc newTransaction*( defaultCfHandle: defaultCfHandle, ) -proc isClosed*(tx: TransactionRef): bool {.inline.} = +template isClosed*(tx: TransactionRef): bool = ## Returns `true` if the `TransactionRef` has been closed. tx.cPtr.isNil() diff --git a/rocksdb/transactions/txdbopts.nim b/rocksdb/transactions/txdbopts.nim index aa6b8c5..b5a44ba 100644 --- a/rocksdb/transactions/txdbopts.nim +++ b/rocksdb/transactions/txdbopts.nim @@ -23,7 +23,7 @@ proc createTransactionDbOptions*(autoClose = false): TransactionDbOptionsRef = cPtr: rocksdb_transactiondb_options_create(), autoClose: autoClose ) -proc isClosed*(txDbOpts: TransactionDbOptionsRef): bool {.inline.} = +template isClosed*(txDbOpts: TransactionDbOptionsRef): bool = txDbOpts.cPtr.isNil() proc cPtr*(txDbOpts: TransactionDbOptionsRef): TransactionDbOptionsPtr = @@ -40,9 +40,7 @@ setOpt numStripes, int, csize_t setOpt transactionLockTimeout, int, int64 setOpt defaultLockTimeout, int, int64 -proc defaultTransactionDbOptions*( - autoClose = false -): TransactionDbOptionsRef {.inline.} = +proc defaultTransactionDbOptions*(autoClose = false): TransactionDbOptionsRef = let txDbOpts = createTransactionDbOptions(autoClose) # TODO: set prefered defaults diff --git a/rocksdb/transactions/txopts.nim b/rocksdb/transactions/txopts.nim index a312f86..b5c7558 100644 --- a/rocksdb/transactions/txopts.nim +++ b/rocksdb/transactions/txopts.nim @@ -23,7 +23,7 @@ proc createTransactionOptions*(autoClose = false): TransactionOptionsRef = cPtr: rocksdb_transaction_options_create(), autoClose: autoClose ) -proc isClosed*(txOpts: TransactionOptionsRef): bool {.inline.} = +template isClosed*(txOpts: TransactionOptionsRef): bool = txOpts.cPtr.isNil() proc cPtr*(txOpts: TransactionOptionsRef): TransactionOptionsPtr = @@ -42,7 +42,7 @@ setOpt deadlockDetectDepth, int, int64 setOpt maxWriteBatchSize, int, csize_t setOpt skipPrepare, bool, uint8 -proc defaultTransactionOptions*(autoClose = false): TransactionOptionsRef {.inline.} = +proc defaultTransactionOptions*(autoClose = false): TransactionOptionsRef = let txOpts = createTransactionOptions(autoClose) # TODO: set prefered defaults diff --git a/rocksdb/writebatch.nim b/rocksdb/writebatch.nim index c302ebd..96e6b53 100644 --- a/rocksdb/writebatch.nim +++ b/rocksdb/writebatch.nim @@ -27,7 +27,7 @@ type proc createWriteBatch*(defaultCfHandle: ColFamilyHandleRef): WriteBatchRef = WriteBatchRef(cPtr: rocksdb_writebatch_create(), defaultCfHandle: defaultCfHandle) -proc isClosed*(batch: WriteBatchRef): bool {.inline.} = +template isClosed*(batch: WriteBatchRef): bool = ## Returns `true` if the `WriteBatchRef` has been closed and `false` otherwise. batch.cPtr.isNil() @@ -73,6 +73,24 @@ proc delete*( ok() +proc deleteRange*( + batch: WriteBatchRef, + startKey, endKey: openArray[byte], + cfHandle = batch.defaultCfHandle, +): RocksDBResult[void] = + ## Add a delete range operation to the write batch. + + rocksdb_writebatch_delete_range_cf( + batch.cPtr, + cfHandle.cPtr, + cast[cstring](startKey.unsafeAddrOrNil()), + csize_t(startKey.len), + cast[cstring](endKey.unsafeAddrOrNil()), + csize_t(endKey.len), + ) + + ok() + proc close*(batch: WriteBatchRef) = ## Close the `WriteBatchRef`. if not batch.isClosed(): diff --git a/rocksdb/writebatchwi.nim b/rocksdb/writebatchwi.nim index 59456c0..1b60fa6 100644 --- a/rocksdb/writebatchwi.nim +++ b/rocksdb/writebatchwi.nim @@ -41,7 +41,7 @@ proc createWriteBatch*( defaultCfHandle: defaultCfHandle, ) -proc isClosed*(batch: WriteBatchWIRef): bool {.inline.} = +template isClosed*(batch: WriteBatchWIRef): bool = ## Returns `true` if the `WriteBatchWIRef` has been closed and `false` otherwise. batch.cPtr.isNil() diff --git a/tests/test_columnfamily.nim b/tests/test_columnfamily.nim index 671908d..5af2e7d 100644 --- a/tests/test_columnfamily.nim +++ b/tests/test_columnfamily.nim @@ -101,3 +101,49 @@ suite "ColFamily Tests": iter.value() == val iter.seekToKey(otherKey) check iter.isValid() == false + + test "Test deleteRange": + let cf = db.getColFamily(CF_OTHER).get() + + let + keyValue1 = @[1.byte] + keyValue2 = @[2.byte] + keyValue3 = @[3.byte] + + check: + cf.put(keyValue1, keyValue1).isOk() + cf.put(keyValue2, keyValue2).isOk() + cf.put(keyValue3, keyValue3).isOk() + cf.keyExists(keyValue1).get() == true + cf.keyExists(keyValue2).get() == true + cf.keyExists(keyValue3).get() == true + + cf.suggestCompactRange(keyValue1, keyValue3).isOk() + cf.deleteRange(keyValue1, keyValue3).isOk() + cf.compactRange(keyValue1, keyValue3).isOk() + + cf.keyExists(keyValue1).get() == false + cf.keyExists(keyValue2).get() == false + cf.keyExists(keyValue3).get() == true + + test "Test multiget": + let cf = db.getColFamily(CF_OTHER).get() + + let + keyValue1 = @[100.byte] + keyValue2 = @[300.byte] + keyValue3 = @[200.byte] + + check: + cf.put(keyValue1, keyValue1).isOk() + cf.put(keyValue2, keyValue2).isOk() + cf.keyExists(keyValue1).get() == true + cf.keyExists(keyValue2).get() == true + cf.keyExists(keyValue3).get() == false + + let dataRes = cf.multiGet(@[keyValue1, keyValue2, keyValue3]).expect("ok") + check: + dataRes.len() == 3 + dataRes[0] == keyValue1 + dataRes[1] == keyValue2 + dataRes[2] == default(seq[byte]) diff --git a/tests/test_rocksdb.nim b/tests/test_rocksdb.nim index f89d3e4..ec75b72 100644 --- a/tests/test_rocksdb.nim +++ b/tests/test_rocksdb.nim @@ -514,3 +514,117 @@ suite "RocksDbRef Tests": db.put(otherKey, val, defaultCfHandle).isOk() db.put(key, val, otherCfHandle).isOk() db.flush(cfHandles).isOk() + + test "Test deleteRange": + let + keyValue1 = @[1.byte] + keyValue2 = @[2.byte] + keyValue3 = @[3.byte] + + check: + db.put(keyValue1, keyValue1).isOk() + db.put(keyValue2, keyValue2).isOk() + db.put(keyValue3, keyValue3).isOk() + db.keyExists(keyValue1).get() == true + db.keyExists(keyValue2).get() == true + db.keyExists(keyValue3).get() == true + + db.deleteRange(keyValue1, keyValue3).isOk() + db.compactRange(keyValue1, keyValue3).isOk() + + db.keyExists(keyValue1).get() == false + db.keyExists(keyValue2).get() == false + db.keyExists(keyValue3).get() == true + + check: + db.put(keyValue1, keyValue1, otherCfHandle).isOk() + db.put(keyValue2, keyValue2, otherCfHandle).isOk() + db.keyExists(keyValue1, otherCfHandle).get() == true + db.keyExists(keyValue2, otherCfHandle).get() == true + db.keyExists(keyValue3, otherCfHandle).get() == false + + db.deleteRange(keyValue1, keyValue2, otherCfHandle).isOk() + db.suggestCompactRange(keyValue1, keyValue3, otherCfHandle).isOk() + + db.keyExists(keyValue1, otherCfHandle).get() == false + db.keyExists(keyValue2, otherCfHandle).get() == true + db.keyExists(keyValue3, otherCfHandle).get() == false + + test "Test multiget": + let + keyValue1 = @[1.byte] + keyValue2 = @[2.byte] + keyValue3 = @[3.byte] + keyValue4 = @[4.byte] + keyValue5 = @[5.byte] + keyValue6 = @[6.byte] + keyValue7 = @[7.byte] + keyValue8 = @[8.byte] + keyValue9 = @[9.byte] + + check: + db.put(keyValue1, keyValue1).isOk() + db.put(keyValue2, keyValue2).isOk() + db.put(keyValue5, keyValue5).isOk() + db.put(keyValue7, keyValue7).isOk() + db.put(keyValue9, keyValue9).isOk() + db.keyExists(keyValue1).get() == true + db.keyExists(keyValue2).get() == true + db.keyExists(keyValue3).get() == false + + block: + let dataRes = db.multiGet(@[keyValue1]).expect("ok") + check: + dataRes.len() == 1 + dataRes[0] == keyValue1 + + block: + let dataRes = db.multiGet(@[keyValue1, keyValue2]).expect("ok") + check: + dataRes.len() == 2 + dataRes[0] == keyValue1 + dataRes[1] == keyValue2 + + block: + let dataRes = db.multiGet(@[keyValue2, keyValue3]).expect("ok") + check: + dataRes.len() == 2 + dataRes[0] == keyValue2 + dataRes[1] == default(seq[byte]) + + block: + let dataRes = db.multiGet(@[keyValue1, keyValue2, keyValue3]).expect("ok") + check: + dataRes.len() == 3 + dataRes[0] == keyValue1 + dataRes[1] == keyValue2 + dataRes[2] == default(seq[byte]) + + block: + let dataRes = + db.multiGet(@[keyValue1, keyValue2, keyValue3], sortedInput = true).expect("ok") + check: + dataRes.len() == 3 + dataRes[0] == keyValue1 + dataRes[1] == keyValue2 + dataRes[2] == default(seq[byte]) + + block: + let + keys = + @[ + keyValue1, keyValue2, keyValue3, keyValue4, keyValue5, keyValue6, keyValue7, + keyValue8, keyValue9, + ] + dataRes = db.multiGet(keys).expect("ok") + check: + dataRes.len() == 9 + dataRes[0] == keyValue1 + dataRes[1] == keyValue2 + dataRes[2] == default(seq[byte]) + dataRes[3] == default(seq[byte]) + dataRes[4] == keyValue5 + dataRes[5] == default(seq[byte]) + dataRes[6] == keyValue7 + dataRes[7] == default(seq[byte]) + dataRes[8] == keyValue9 diff --git a/tests/test_writebatch.nim b/tests/test_writebatch.nim index fc55962..b67155d 100644 --- a/tests/test_writebatch.nim +++ b/tests/test_writebatch.nim @@ -195,3 +195,27 @@ suite "WriteBatchRef Tests": check batch.isClosed() batch.close() check batch.isClosed() + + test "Test deleteRange": + let batch = db.openWriteBatch() + defer: + batch.close() + + let + keyValue1 = @[1.byte] + keyValue2 = @[2.byte] + keyValue3 = @[3.byte] + + check: + batch.put(keyValue1, keyValue1).isOk() + batch.put(keyValue2, keyValue2).isOk() + batch.put(keyValue3, keyValue3).isOk() + batch.deleteRange(keyValue1, keyValue3).isOk() + batch.count() == 4 + + let res1 = db.write(batch) + check: + res1.isOk() + db.keyExists(keyValue1).get() == false + db.keyExists(keyValue2).get() == false + db.keyExists(keyValue3).get() == true