diff --git a/README.md b/README.md index b39aa2ba..d85dced0 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ You'll need to build [RocksDB](https://github.com/facebook/rocksdb) v5.16+ on yo After that, you can install gorocksdb using the following command: - CGO_CFLAGS="-I/path/to/rocksdb/include" \ + CGO_CFLAGS="-I/path/to/rocksdb/include -DUSE_USER_DEFINED_TIMESTAMP" \ CGO_LDFLAGS="-L/path/to/rocksdb -lrocksdb -lstdc++ -lm -lz -lbz2 -lsnappy -llz4 -lzstd" \ go get github.com/tecbot/gorocksdb diff --git a/column_family_ts.go b/column_family_ts.go new file mode 100644 index 00000000..202cee00 --- /dev/null +++ b/column_family_ts.go @@ -0,0 +1,27 @@ +//go:build rocksdb_user_timestamp +// +build rocksdb_user_timestamp + +package gorocksdb + +/* +#cgo CFLAGS: -DUSE_USER_DEFINED_TIMESTAMP +#include "rocksdb/c.h" +*/ +import "C" + +// EnableUserDefinedTimestamp turns the feature on/off for this column‑family +// *before* the CF is created. It maps straight to +// rocksdb_column_family_options_enable_user_defined_timestamp(). +func (opts *ColumnFamilyOptions) EnableUserDefinedTimestamp(enable bool) { + var flag C.uchar + if enable { + flag = 1 + } + C.rocksdb_column_family_options_enable_user_defined_timestamp(opts.c, flag) +} + +// SetTimestampSize sets the fixed length (in bytes) of the timestamp slice +// RocksDB will expect. Most users pick 8 for uint64 epoch microseconds. +func (opts *ColumnFamilyOptions) SetTimestampSize(size int) { + C.rocksdb_column_family_options_set_timestamp_size(opts.c, C.size_t(size)) +} diff --git a/db_ts.go b/db_ts.go new file mode 100644 index 00000000..099b3f54 --- /dev/null +++ b/db_ts.go @@ -0,0 +1,85 @@ +//go:build rocksdb_user_timestamp +// +build rocksdb_user_timestamp + +package gorocksdb + +/* +#cgo CFLAGS: -DUSE_USER_DEFINED_TIMESTAMP +#include "rocksdb/c.h" +#include "gorocksdb.h" +*/ +import "C" +import ( + "errors" + "unsafe" +) + +// PutCFWithTS stores the given key/value pair in the specified column family +// while attaching an explicit timestamp (ts). +func (db *DB) PutCFWithTS( + wo *WriteOptions, + cf *ColumnFamilyHandle, + key, ts, value []byte, +) error { + cErr := C.gorocksdb_put_cf_with_ts( + db.c, wo.c, cf.c, + byteSliceToChar(key), C.size_t(len(key)), + byteSliceToChar(ts), C.size_t(len(ts)), + byteSliceToChar(value), C.size_t(len(value))) + return convertErr(cErr) +} + +// PutWithTS is the default‑CF convenience wrapper. +func (db *DB) PutWithTS(wo *WriteOptions, key, ts, value []byte) error { + return db.PutCFWithTS(wo, db.defaultCF, key, ts, value) +} + +// DeleteCFWithTS marks the given key as deleted with the supplied timestamp. +// The semantics mirror PutCFWithTS but create a tombstone instead of a value. +func (db *DB) DeleteCFWithTS( + wo *WriteOptions, + cf *ColumnFamilyHandle, + key, ts []byte, +) error { + cErr := C.gorocksdb_delete_cf_with_ts( + db.c, wo.c, cf.c, + byteSliceToChar(key), C.size_t(len(key)), + byteSliceToChar(ts), C.size_t(len(ts))) + return convertErr(cErr) +} + +// DeleteWithTS is the default‑CF convenience wrapper. +func (db *DB) DeleteWithTS(wo *WriteOptions, key, ts []byte) error { + return db.DeleteCFWithTS(wo, db.defaultCF, key, ts) +} + +// IncreaseFullHistoryTsLow raises RocksDB’s low‑water mark for full‑history +// retention for the specified column family. +func (db *DB) IncreaseFullHistoryTsLow(cf *ColumnFamilyHandle, ts []byte) error { + cErr := C.gorocksdb_increase_full_history_ts_low( + db.c, cf.c, + byteSliceToChar(ts), C.size_t(len(ts))) + return convertErr(cErr) +} + +// byteSliceToChar safely converts a nil‑able Go []byte to *C.char. +func byteSliceToChar(b []byte) *C.char { + if len(b) == 0 { + return nil + } + return (*C.char)(unsafe.Pointer(&b[0])) +} + +// convertErr frees a rocksdb_status_t* and converts it to Go error +func convertErr(st *C.rocksdb_status_t) error { + if st == nil { + return nil + } + defer C.rocksdb_status_destroy(st) + + if C.rocksdb_status_code(st) == C.rocksdb_status_code_t(0) { + return nil + } + msg := C.GoString(C.rocksdb_status_to_string(st)) + return errors.New(msg) +} diff --git a/gorocksdb.c b/gorocksdb.c index efebbe51..0b416e22 100644 --- a/gorocksdb.c +++ b/gorocksdb.c @@ -68,3 +68,30 @@ rocksdb_slicetransform_t* gorocksdb_slicetransform_create(uintptr_t idx) { (unsigned char (*)(void*, const char*, size_t))(gorocksdb_slicetransform_in_range), (const char* (*)(void*))(gorocksdb_slicetransform_name)); } + +/* gorocksdb.c */ +rocksdb_status_t* gorocksdb_put_cf_with_ts( + rocksdb_t* db, + const rocksdb_writeoptions_t* options, + rocksdb_column_family_handle_t* cf, + const char* key, size_t keylen, + const char* ts, size_t tslen, + const char* val, size_t vallen) { + return rocksdb_put_cf_with_ts(db, options, cf, key, keylen, ts, tslen, val, vallen); +} + +rocksdb_status_t* gorocksdb_increase_full_history_ts_low( + rocksdb_t* db, + rocksdb_column_family_handle_t* cf, + const char* ts, size_t tslen) { + return rocksdb_increase_full_history_ts_low(db, cf, ts, tslen); +} + +rocksdb_status_t* gorocksdb_delete_cf_with_ts( + rocksdb_t* db, + const rocksdb_writeoptions_t* options, + rocksdb_column_family_handle_t* cf, + const char* key, size_t keylen, + const char* ts, size_t tslen) { + return rocksdb_delete_cf_with_ts(db, options, cf, key, keylen, ts, tslen); +} diff --git a/gorocksdb.h b/gorocksdb.h index 4a9968f0..4cf8ac93 100644 --- a/gorocksdb.h +++ b/gorocksdb.h @@ -28,3 +28,24 @@ extern void gorocksdb_mergeoperator_delete_value(void* state, const char* v, siz /* Slice Transform */ extern rocksdb_slicetransform_t* gorocksdb_slicetransform_create(uintptr_t idx); + +/* gorocksdb.h */ +extern rocksdb_status_t* gorocksdb_put_cf_with_ts( + rocksdb_t* db, + const rocksdb_writeoptions_t* options, + rocksdb_column_family_handle_t* cf, + const char* key, size_t keylen, + const char* ts, size_t tslen, + const char* val, size_t vallen); + +extern rocksdb_status_t* gorocksdb_increase_full_history_ts_low( + rocksdb_t* db, + rocksdb_column_family_handle_t* cf, + const char* ts, size_t tslen); + +extern rocksdb_status_t* gorocksdb_delete_cf_with_ts( + rocksdb_t*, const rocksdb_writeoptions_t*, + rocksdb_column_family_handle_t*, + const char* key, size_t keylen, + const char* ts, size_t tslen); +