rdbpython 0.2.1__cp313-cp313-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. rdbpy/.dylibs/libbz2.1.0.8.dylib +0 -0
  2. rdbpy/.dylibs/liblz4.1.9.4.dylib +0 -0
  3. rdbpy/.dylibs/libsnappy.1.2.1.dylib +0 -0
  4. rdbpy/.dylibs/libz.1.3.1.dylib +0 -0
  5. rdbpy/.dylibs/libzstd.1.5.6.dylib +0 -0
  6. rdbpy/__init__.py +1 -0
  7. rdbpy/backup.pxd +35 -0
  8. rdbpy/cache.pxd +8 -0
  9. rdbpy/comparator.pxd +23 -0
  10. rdbpy/db.pxd +230 -0
  11. rdbpy/env.pxd +5 -0
  12. rdbpy/errors.py +26 -0
  13. rdbpy/filter_policy.pxd +39 -0
  14. rdbpy/include/rdb_include/comparator_wrapper.hpp +63 -0
  15. rdbpy/include/rdb_include/filter_policy_wrapper.hpp +89 -0
  16. rdbpy/include/rdb_include/memtable_factories.hpp +15 -0
  17. rdbpy/include/rdb_include/merge_operator_wrapper.hpp +132 -0
  18. rdbpy/include/rdb_include/slice_transform_wrapper.hpp +109 -0
  19. rdbpy/include/rdb_include/utils.hpp +8 -0
  20. rdbpy/include/rdb_include/write_batch_iter_helper.hpp +65 -0
  21. rdbpy/interfaces.py +77 -0
  22. rdbpy/iterator.pxd +16 -0
  23. rdbpy/lib_rocksdb.cpython-313-darwin.so +0 -0
  24. rdbpy/lib_rocksdb.pxd +768 -0
  25. rdbpy/lib_rocksdb.pyi +1465 -0
  26. rdbpy/lib_rocksdb.pyx +3221 -0
  27. rdbpy/lib_utils.py +52 -0
  28. rdbpy/logger.pxd +5 -0
  29. rdbpy/memtablerep.pxd +12 -0
  30. rdbpy/merge_operator.pxd +46 -0
  31. rdbpy/merge_operators.py +25 -0
  32. rdbpy/options.pxd +179 -0
  33. rdbpy/slice_.pxd +20 -0
  34. rdbpy/slice_transform.pxd +37 -0
  35. rdbpy/snapshot.pxd +3 -0
  36. rdbpy/status.pxd +15 -0
  37. rdbpy/std_memory.pxd +7 -0
  38. rdbpy/table_factory.pxd +50 -0
  39. rdbpy/transaction.pxd +154 -0
  40. rdbpy/universal_compaction.pxd +15 -0
  41. rdbpython-0.2.1.dist-info/METADATA +104 -0
  42. rdbpython-0.2.1.dist-info/RECORD +45 -0
  43. rdbpython-0.2.1.dist-info/WHEEL +5 -0
  44. rdbpython-0.2.1.dist-info/licenses/LICENSE.md +21 -0
  45. rdbpython-0.2.1.dist-info/top_level.txt +1 -0
rdbpy/lib_rocksdb.pyx ADDED
@@ -0,0 +1,3221 @@
1
+ # distutils: language = c++
2
+ # distutils: language_level = 3
3
+ import cython
4
+ from libcpp.string cimport string
5
+ from libcpp.deque cimport deque
6
+ from libcpp.vector cimport vector
7
+ from cpython cimport bool as py_bool
8
+ from libcpp cimport bool as cpp_bool
9
+ from libc.stddef cimport size_t
10
+ from libc.stdint cimport uint32_t
11
+ from cython.operator cimport dereference as deref
12
+ from cpython.bytes cimport PyBytes_AsString
13
+ from cpython.bytes cimport PyBytes_Size
14
+ from cpython.bytes cimport PyBytes_FromString
15
+ from cpython.bytes cimport PyBytes_FromStringAndSize
16
+ from cpython.unicode cimport PyUnicode_Decode
17
+
18
+ from rdbpy.std_memory cimport shared_ptr
19
+ cimport rdbpy.options as options
20
+ cimport rdbpy.merge_operator as merge_operator
21
+ cimport rdbpy.filter_policy as filter_policy
22
+ cimport rdbpy.comparator as comparator
23
+ cimport rdbpy.slice_transform as slice_transform
24
+ cimport rdbpy.cache as cache
25
+ cimport rdbpy.logger as logger
26
+ cimport rdbpy.snapshot as snapshot
27
+ cimport rdbpy.db as db
28
+ cimport rdbpy.iterator as iterator
29
+ cimport rdbpy.backup as backup
30
+ cimport rdbpy.env as env
31
+ cimport rdbpy.transaction as transaction
32
+ cimport rdbpy.table_factory as table_factory
33
+ cimport rdbpy.memtablerep as memtablerep
34
+ cimport rdbpy.universal_compaction as universal_compaction
35
+
36
+ # Enums are the only exception for direct imports
37
+ # Their name als already unique enough
38
+ from rdbpy.universal_compaction cimport kCompactionStopStyleSimilarSize
39
+ from rdbpy.universal_compaction cimport kCompactionStopStyleTotalSize
40
+
41
+ from rdbpy.options cimport FlushOptions
42
+ from rdbpy.options cimport kCompactionStyleLevel
43
+ from rdbpy.options cimport kCompactionStyleUniversal
44
+ from rdbpy.options cimport kCompactionStyleFIFO
45
+ from rdbpy.options cimport kCompactionStyleNone
46
+
47
+ from rdbpy.slice_ cimport Slice
48
+ from rdbpy.status cimport Status
49
+
50
+ import sys
51
+ from rdbpy.interfaces import MergeOperator as IMergeOperator
52
+ from rdbpy.interfaces import AssociativeMergeOperator as IAssociativeMergeOperator
53
+ from rdbpy.interfaces import FilterPolicy as IFilterPolicy
54
+ from rdbpy.interfaces import Comparator as IComparator
55
+ from rdbpy.interfaces import SliceTransform as ISliceTransform
56
+ import traceback
57
+ import rdbpy.errors as errors
58
+ import weakref
59
+
60
+ # pxd defines:
61
+ # ctypedef const filter_policy.FilterPolicy ConstFilterPolicy
62
+
63
+ cdef extern from "rdb_include/utils.hpp" namespace "py_rocks":
64
+ cdef const Slice* vector_data(vector[Slice]&)
65
+
66
+ # Prepare python for threaded usage.
67
+ # Python callbacks (merge, comparator)
68
+ # could be executed in a rocksdb background thread (eg. compaction).
69
+ cdef extern from "Python.h":
70
+ void PyEval_InitThreads()
71
+ PyEval_InitThreads()
72
+
73
+ ## Here comes the stuff to wrap the status to exception
74
+ cdef check_status(const Status& st):
75
+ if st.ok():
76
+ return
77
+
78
+ if st.IsNotFound():
79
+ raise errors.NotFound(st.ToString())
80
+
81
+ if st.IsCorruption():
82
+ raise errors.Corruption(st.ToString())
83
+
84
+ if st.IsNotSupported():
85
+ raise errors.NotSupported(st.ToString())
86
+
87
+ if st.IsInvalidArgument():
88
+ raise errors.InvalidArgument(st.ToString())
89
+
90
+ if st.IsIOError():
91
+ raise errors.RocksIOError(st.ToString())
92
+
93
+ if st.IsMergeInProgress():
94
+ raise errors.MergeInProgress(st.ToString())
95
+
96
+ if st.IsIncomplete():
97
+ raise errors.Incomplete(st.ToString())
98
+
99
+ raise Exception("Unknown error: %s" % st.ToString())
100
+ ######################################################
101
+
102
+
103
+ cdef string bytes_to_string(path) except *:
104
+ return string(PyBytes_AsString(path), PyBytes_Size(path))
105
+
106
+ cdef string_to_bytes(string ob):
107
+ return PyBytes_FromStringAndSize(ob.c_str(), ob.size())
108
+
109
+ cdef Slice bytes_to_slice(ob) except *:
110
+ return Slice(PyBytes_AsString(ob), PyBytes_Size(ob))
111
+
112
+ cdef slice_to_bytes(Slice sl):
113
+ return PyBytes_FromStringAndSize(sl.data(), sl.size())
114
+
115
+ ## only for filsystem paths
116
+ cdef string path_to_string(object path) except *:
117
+ if isinstance(path, bytes):
118
+ return bytes_to_string(path)
119
+ if isinstance(path, unicode):
120
+ path = path.encode(sys.getfilesystemencoding())
121
+ return bytes_to_string(path)
122
+ else:
123
+ raise TypeError("Wrong type for path: %s" % path)
124
+
125
+ cdef object string_to_path(string path):
126
+ fs_encoding = sys.getfilesystemencoding().encode('ascii')
127
+ return PyUnicode_Decode(path.c_str(), path.size(), fs_encoding, "replace")
128
+
129
+ ## Here comes the stuff for the comparator
130
+ cdef class PyComparator(object):
131
+ cdef object get_ob(self):
132
+ return None
133
+
134
+ cdef const comparator.Comparator* get_comparator(self):
135
+ return NULL
136
+
137
+ cdef set_info_log(self, shared_ptr[logger.Logger] info_log):
138
+ pass
139
+
140
+
141
+ cdef class PyGenericComparator(PyComparator):
142
+ # pxd defines:
143
+ # cdef comparator.ComparatorWrapper* comparator_ptr
144
+ # cdef object ob
145
+
146
+ def __cinit__(self, object ob):
147
+ self.comparator_ptr = NULL
148
+ if not isinstance(ob, IComparator):
149
+ raise TypeError("%s is not of type %s" % (ob, IComparator))
150
+
151
+ self.ob = ob
152
+ self.comparator_ptr = new comparator.ComparatorWrapper(
153
+ bytes_to_string(ob.name()),
154
+ <void*>ob,
155
+ compare_callback)
156
+
157
+ def __dealloc__(self):
158
+ if not self.comparator_ptr == NULL:
159
+ del self.comparator_ptr
160
+
161
+ cdef object get_ob(self):
162
+ return self.ob
163
+
164
+ cdef const comparator.Comparator* get_comparator(self):
165
+ return <comparator.Comparator*> self.comparator_ptr
166
+
167
+ cdef set_info_log(self, shared_ptr[logger.Logger] info_log):
168
+ self.comparator_ptr.set_info_log(info_log)
169
+
170
+
171
+ cdef class PyBytewiseComparator(PyComparator):
172
+ # pxd defines:
173
+ # cdef const comparator.Comparator* comparator_ptr
174
+
175
+ def __cinit__(self):
176
+ self.comparator_ptr = comparator.BytewiseComparator()
177
+
178
+ cpdef name(self):
179
+ return PyBytes_FromString(self.comparator_ptr.Name())
180
+
181
+ cpdef compare(self, a, b):
182
+ return self.comparator_ptr.Compare(
183
+ bytes_to_slice(a),
184
+ bytes_to_slice(b))
185
+
186
+ cdef object get_ob(self):
187
+ return self
188
+
189
+ cdef const comparator.Comparator* get_comparator(self):
190
+ return self.comparator_ptr
191
+
192
+
193
+
194
+ cdef int compare_callback(
195
+ void* ctx,
196
+ logger.Logger* log,
197
+ string& error_msg,
198
+ const Slice& a,
199
+ const Slice& b) with gil:
200
+
201
+ try:
202
+ return (<object>ctx).compare(slice_to_bytes(a), slice_to_bytes(b))
203
+ except BaseException as error:
204
+ tb = traceback.format_exc()
205
+ logger.Log(log, "Error in compare callback: %s", <bytes>tb)
206
+ error_msg.assign(<bytes>str(error))
207
+
208
+ BytewiseComparator = PyBytewiseComparator
209
+ #########################################
210
+
211
+
212
+
213
+ ## Here comes the stuff for the filter policy
214
+ cdef class PyFilterPolicy(object):
215
+ cdef object get_ob(self):
216
+ return None
217
+
218
+ cdef shared_ptr[ConstFilterPolicy] get_policy(self):
219
+ return shared_ptr[ConstFilterPolicy]()
220
+
221
+ cdef set_info_log(self, shared_ptr[logger.Logger] info_log):
222
+ pass
223
+
224
+
225
+ cdef class PyGenericFilterPolicy(PyFilterPolicy):
226
+ # pxd defines:
227
+ # cdef shared_ptr[filter_policy.FilterPolicyWrapper] policy
228
+ # cdef object ob
229
+
230
+ def __cinit__(self, object ob):
231
+ if not isinstance(ob, IFilterPolicy):
232
+ raise TypeError("%s is not of type %s" % (ob, IFilterPolicy))
233
+
234
+ self.ob = ob
235
+ self.policy.reset(new filter_policy.FilterPolicyWrapper(
236
+ bytes_to_string(ob.name()),
237
+ <void*>ob,
238
+ create_filter_callback,
239
+ key_may_match_callback))
240
+
241
+ cdef object get_ob(self):
242
+ return self.ob
243
+
244
+ cdef shared_ptr[ConstFilterPolicy] get_policy(self):
245
+ return <shared_ptr[ConstFilterPolicy]>(self.policy)
246
+
247
+ cdef set_info_log(self, shared_ptr[logger.Logger] info_log):
248
+ self.policy.get().set_info_log(info_log)
249
+
250
+
251
+ cdef void create_filter_callback(
252
+ void* ctx,
253
+ logger.Logger* log,
254
+ string& error_msg,
255
+ const Slice* keys,
256
+ int n,
257
+ string* dst) with gil:
258
+
259
+ try:
260
+ ret = (<object>ctx).create_filter(
261
+ [slice_to_bytes(keys[i]) for i in range(n)])
262
+ dst.append(bytes_to_string(ret))
263
+ except BaseException as error:
264
+ tb = traceback.format_exc()
265
+ logger.Log(log, "Error in create filter callback: %s", <bytes>tb)
266
+ error_msg.assign(<bytes>str(error))
267
+
268
+ cdef cpp_bool key_may_match_callback(
269
+ void* ctx,
270
+ logger.Logger* log,
271
+ string& error_msg,
272
+ const Slice& key,
273
+ const Slice& filt) with gil:
274
+
275
+ try:
276
+ return (<object>ctx).key_may_match(
277
+ slice_to_bytes(key),
278
+ slice_to_bytes(filt))
279
+ except BaseException as error:
280
+ tb = traceback.format_exc()
281
+ logger.Log(log, "Error in key_mach_match callback: %s", <bytes>tb)
282
+ error_msg.assign(<bytes>str(error))
283
+
284
+
285
+ cdef class PyBloomFilterPolicy(PyFilterPolicy):
286
+ # pxd defines:
287
+ # cdef shared_ptr[ConstFilterPolicy] policy
288
+
289
+ def __cinit__(self, int bits_per_key):
290
+ self.policy.reset(filter_policy.NewBloomFilterPolicy(bits_per_key))
291
+
292
+ cpdef name(self):
293
+ return PyBytes_FromString(self.policy.get().Name())
294
+
295
+ cpdef create_filter(self, keys):
296
+ cdef string dst
297
+ cdef vector[Slice] c_keys
298
+
299
+ for key in keys:
300
+ c_keys.push_back(bytes_to_slice(key))
301
+
302
+ self.policy.get().CreateFilter(
303
+ vector_data(c_keys),
304
+ <int>c_keys.size(),
305
+ cython.address(dst))
306
+
307
+ return string_to_bytes(dst)
308
+
309
+ cpdef key_may_match(self, key, filter_):
310
+ return self.policy.get().KeyMayMatch(
311
+ bytes_to_slice(key),
312
+ bytes_to_slice(filter_))
313
+
314
+ cdef object get_ob(self):
315
+ return self
316
+
317
+ cdef shared_ptr[ConstFilterPolicy] get_policy(self):
318
+ return self.policy
319
+
320
+ BloomFilterPolicy = PyBloomFilterPolicy
321
+ #############################################
322
+
323
+
324
+
325
+ ## Here comes the stuff for the merge operator
326
+ cdef class PyMergeOperator(object):
327
+ # pxd defines:
328
+ # cdef shared_ptr[merge_operator.MergeOperator] merge_op
329
+ # cdef object ob
330
+
331
+ def __cinit__(self, object ob):
332
+ self.ob = ob
333
+ if isinstance(ob, IAssociativeMergeOperator):
334
+ self.merge_op.reset(
335
+ <merge_operator.MergeOperator*>
336
+ new merge_operator.AssociativeMergeOperatorWrapper(
337
+ bytes_to_string(ob.name()),
338
+ <void*>(ob),
339
+ merge_callback))
340
+
341
+ elif isinstance(ob, IMergeOperator):
342
+ self.merge_op.reset(
343
+ <merge_operator.MergeOperator*>
344
+ new merge_operator.MergeOperatorWrapper(
345
+ bytes_to_string(ob.name()),
346
+ <void*>ob,
347
+ <void*>ob,
348
+ full_merge_callback,
349
+ partial_merge_callback))
350
+ # elif isinstance(ob, str):
351
+ # if ob == "put":
352
+ # self.merge_op = merge_operator.MergeOperators.CreatePutOperator()
353
+ # elif ob == "put_v1":
354
+ # self.merge_op = merge_operator.MergeOperators.CreateDeprecatedPutOperator()
355
+ # elif ob == "uint64add":
356
+ # self.merge_op = merge_operator.MergeOperators.CreateUInt64AddOperator()
357
+ # elif ob == "stringappend":
358
+ # self.merge_op = merge_operator.MergeOperators.CreateStringAppendOperator()
359
+ # #TODO: necessary?
360
+ # # elif ob == "stringappendtest":
361
+ # # self.merge_op = merge_operator.MergeOperators.CreateStringAppendTESTOperator()
362
+ # elif ob == "max":
363
+ # self.merge_op = merge_operator.MergeOperators.CreateMaxOperator()
364
+ # else:
365
+ # msg = "{0} is not the default type".format(ob)
366
+ # raise TypeError(msg)
367
+ else:
368
+ msg = "%s is not of this types %s"
369
+ msg %= (ob, (IAssociativeMergeOperator, IMergeOperator))
370
+ raise TypeError(msg)
371
+
372
+
373
+ cdef object get_ob(self):
374
+ return self.ob
375
+
376
+ cdef shared_ptr[merge_operator.MergeOperator] get_operator(self):
377
+ return self.merge_op
378
+
379
+ cdef cpp_bool merge_callback(
380
+ void* ctx,
381
+ const Slice& key,
382
+ const Slice* existing_value,
383
+ const Slice& value,
384
+ string* new_value,
385
+ logger.Logger* log) with gil:
386
+
387
+ if existing_value == NULL:
388
+ py_existing_value = None
389
+ else:
390
+ py_existing_value = slice_to_bytes(deref(existing_value))
391
+
392
+ try:
393
+ ret = (<object>ctx).merge(
394
+ slice_to_bytes(key),
395
+ py_existing_value,
396
+ slice_to_bytes(value))
397
+
398
+ if ret[0]:
399
+ new_value.assign(bytes_to_string(ret[1]))
400
+ return True
401
+ return False
402
+
403
+ except:
404
+ tb = traceback.format_exc()
405
+ logger.Log(log, "Error in merge_callback: %s", <bytes>tb)
406
+ return False
407
+
408
+ cdef cpp_bool full_merge_callback(
409
+ void* ctx,
410
+ const Slice& key,
411
+ const Slice* existing_value,
412
+ const deque[string]& op_list,
413
+ string* new_value,
414
+ logger.Logger* log) with gil:
415
+
416
+ if existing_value == NULL:
417
+ py_existing_value = None
418
+ else:
419
+ py_existing_value = slice_to_bytes(deref(existing_value))
420
+
421
+ try:
422
+ ret = (<object>ctx).full_merge(
423
+ slice_to_bytes(key),
424
+ py_existing_value,
425
+ [string_to_bytes(op_list[i]) for i in range(op_list.size())])
426
+
427
+ if ret[0]:
428
+ new_value.assign(bytes_to_string(ret[1]))
429
+ return True
430
+ return False
431
+
432
+ except:
433
+ tb = traceback.format_exc()
434
+ logger.Log(log, "Error in full_merge_callback: %s", <bytes>tb)
435
+ return False
436
+
437
+ cdef cpp_bool partial_merge_callback(
438
+ void* ctx,
439
+ const Slice& key,
440
+ const Slice& left_op,
441
+ const Slice& right_op,
442
+ string* new_value,
443
+ logger.Logger* log) with gil:
444
+
445
+ try:
446
+ ret = (<object>ctx).partial_merge(
447
+ slice_to_bytes(key),
448
+ slice_to_bytes(left_op),
449
+ slice_to_bytes(right_op))
450
+
451
+ if ret[0]:
452
+ new_value.assign(bytes_to_string(ret[1]))
453
+ return True
454
+ return False
455
+
456
+ except:
457
+ tb = traceback.format_exc()
458
+ logger.Log(log, "Error in partial_merge_callback: %s", <bytes>tb)
459
+ return False
460
+ ##############################################
461
+
462
+ #### Here comes the Cache stuff
463
+ cdef class PyCache(object):
464
+ cdef shared_ptr[cache.Cache] get_cache(self):
465
+ return shared_ptr[cache.Cache]()
466
+
467
+
468
+ cdef class PyLRUCache(PyCache):
469
+ # pxd defines:
470
+ # cdef shared_ptr[cache.Cache] cache_ob
471
+
472
+ def __cinit__(self, capacity, shard_bits=None):
473
+ if shard_bits is not None:
474
+ self.cache_ob = cache.NewLRUCache(capacity, shard_bits)
475
+ else:
476
+ self.cache_ob = cache.NewLRUCache(capacity)
477
+
478
+ cdef shared_ptr[cache.Cache] get_cache(self):
479
+ return self.cache_ob
480
+
481
+ LRUCache = PyLRUCache
482
+ ###############################
483
+
484
+ ### Here comes the stuff for SliceTransform
485
+ cdef class PySliceTransform(object):
486
+ # pxd defines:
487
+ # cdef shared_ptr[slice_transform.SliceTransform] transfomer
488
+ # cdef object ob
489
+
490
+ def __cinit__(self, object ob):
491
+ if not isinstance(ob, ISliceTransform):
492
+ raise TypeError("%s is not of type %s" % (ob, ISliceTransform))
493
+
494
+ self.ob = ob
495
+ self.transfomer.reset(
496
+ <slice_transform.SliceTransform*>
497
+ new slice_transform.SliceTransformWrapper(
498
+ bytes_to_string(ob.name()),
499
+ <void*>ob,
500
+ slice_transform_callback,
501
+ slice_in_domain_callback,
502
+ slice_in_range_callback))
503
+
504
+ cdef object get_ob(self):
505
+ return self.ob
506
+
507
+ cdef shared_ptr[slice_transform.SliceTransform] get_transformer(self):
508
+ return self.transfomer
509
+
510
+ cdef set_info_log(self, shared_ptr[logger.Logger] info_log):
511
+ cdef slice_transform.SliceTransformWrapper* ptr
512
+ ptr = <slice_transform.SliceTransformWrapper*> self.transfomer.get()
513
+ ptr.set_info_log(info_log)
514
+
515
+
516
+ cdef Slice slice_transform_callback(
517
+ void* ctx,
518
+ logger.Logger* log,
519
+ string& error_msg,
520
+ const Slice& src) with gil:
521
+
522
+ cdef size_t offset
523
+ cdef size_t size
524
+
525
+ try:
526
+ ret = (<object>ctx).transform(slice_to_bytes(src))
527
+ offset = ret[0]
528
+ size = ret[1]
529
+ if (offset + size) > src.size():
530
+ msg = "offset(%i) + size(%i) is bigger than slice(%i)"
531
+ raise Exception(msg % (offset, size, src.size()))
532
+
533
+ return Slice(src.data() + offset, size)
534
+ except BaseException as error:
535
+ tb = traceback.format_exc()
536
+ logger.Log(log, "Error in slice transfrom callback: %s", <bytes>tb)
537
+ error_msg.assign(<bytes>str(error))
538
+
539
+ cdef cpp_bool slice_in_domain_callback(
540
+ void* ctx,
541
+ logger.Logger* log,
542
+ string& error_msg,
543
+ const Slice& src) with gil:
544
+
545
+ try:
546
+ return (<object>ctx).in_domain(slice_to_bytes(src))
547
+ except BaseException as error:
548
+ tb = traceback.format_exc()
549
+ logger.Log(log, "Error in slice transfrom callback: %s", <bytes>tb)
550
+ error_msg.assign(<bytes>str(error))
551
+
552
+ cdef cpp_bool slice_in_range_callback(
553
+ void* ctx,
554
+ logger.Logger* log,
555
+ string& error_msg,
556
+ const Slice& src) with gil:
557
+
558
+ try:
559
+ return (<object>ctx).in_range(slice_to_bytes(src))
560
+ except BaseException as error:
561
+ tb = traceback.format_exc()
562
+ logger.Log(log, "Error in slice transfrom callback: %s", <bytes>tb)
563
+ error_msg.assign(<bytes>str(error))
564
+ ###########################################
565
+
566
+
567
+ ## Here are the TableFactories
568
+ cdef class PyTableFactory(object):
569
+ # pxd defines:
570
+ # cdef shared_ptr[table_factory.TableFactory] factory
571
+
572
+ cdef shared_ptr[table_factory.TableFactory] get_table_factory(self):
573
+ return self.factory
574
+
575
+ cdef set_info_log(self, shared_ptr[logger.Logger] info_log):
576
+ pass
577
+
578
+
579
+ cdef class BlockBasedTableFactory(PyTableFactory):
580
+ # pxd defines:
581
+ # cdef PyFilterPolicy py_filter_policy
582
+
583
+ def __init__(self,
584
+ index_type='binary_search',
585
+ py_bool hash_index_allow_collision=True,
586
+ checksum='crc32',
587
+ PyCache block_cache=None,
588
+ PyCache block_cache_compressed=None,
589
+ filter_policy=None,
590
+ no_block_cache=False,
591
+ block_size=None,
592
+ block_size_deviation=None,
593
+ block_restart_interval=None,
594
+ whole_key_filtering=None):
595
+
596
+ cdef table_factory.BlockBasedTableOptions table_options
597
+
598
+ if index_type == 'binary_search':
599
+ table_options.index_type = table_factory.kBinarySearch
600
+ elif index_type == 'hash_search':
601
+ table_options.index_type = table_factory.kHashSearch
602
+ else:
603
+ raise ValueError("Unknown index_type: %s" % index_type)
604
+
605
+ if hash_index_allow_collision:
606
+ table_options.hash_index_allow_collision = True
607
+ else:
608
+ table_options.hash_index_allow_collision = False
609
+
610
+ if checksum == 'crc32':
611
+ table_options.checksum = table_factory.kCRC32c
612
+ elif checksum == 'xxhash':
613
+ table_options.checksum = table_factory.kxxHash
614
+ else:
615
+ raise ValueError("Unknown checksum: %s" % checksum)
616
+
617
+ if no_block_cache:
618
+ table_options.no_block_cache = True
619
+ else:
620
+ table_options.no_block_cache = False
621
+
622
+ # If the following options are None use the rocksdb default.
623
+ if block_size is not None:
624
+ table_options.block_size = block_size
625
+
626
+ if block_size_deviation is not None:
627
+ table_options.block_size_deviation = block_size_deviation
628
+
629
+ if block_restart_interval is not None:
630
+ table_options.block_restart_interval = block_restart_interval
631
+
632
+ if whole_key_filtering is not None:
633
+ if whole_key_filtering:
634
+ table_options.whole_key_filtering = True
635
+ else:
636
+ table_options.whole_key_filtering = False
637
+
638
+ if block_cache is not None:
639
+ table_options.block_cache = block_cache.get_cache()
640
+
641
+ if block_cache_compressed is not None:
642
+ table_options.block_cache_compressed = block_cache_compressed.get_cache()
643
+
644
+ # Set the filter_policy
645
+ self.py_filter_policy = None
646
+ if filter_policy is not None:
647
+ if isinstance(filter_policy, PyFilterPolicy):
648
+ if (<PyFilterPolicy?>filter_policy).get_policy().get() == NULL:
649
+ raise Exception("Cannot set filter policy: %s" % filter_policy)
650
+ self.py_filter_policy = filter_policy
651
+ else:
652
+ self.py_filter_policy = PyGenericFilterPolicy(filter_policy)
653
+
654
+ table_options.filter_policy = self.py_filter_policy.get_policy()
655
+
656
+ self.factory.reset(table_factory.NewBlockBasedTableFactory(table_options))
657
+
658
+ cdef set_info_log(self, shared_ptr[logger.Logger] info_log):
659
+ if self.py_filter_policy is not None:
660
+ self.py_filter_policy.set_info_log(info_log)
661
+
662
+
663
+ cdef class PlainTableFactory(PyTableFactory):
664
+ def __init__(
665
+ self,
666
+ user_key_len=0,
667
+ bloom_bits_per_key=10,
668
+ hash_table_ratio=0.75,
669
+ index_sparseness=10,
670
+ huge_page_tlb_size=0,
671
+ encoding_type='plain',
672
+ py_bool full_scan_mode=False):
673
+
674
+ cdef table_factory.PlainTableOptions table_options
675
+
676
+ table_options.user_key_len = user_key_len
677
+ table_options.bloom_bits_per_key = bloom_bits_per_key
678
+ table_options.hash_table_ratio = hash_table_ratio
679
+ table_options.index_sparseness = index_sparseness
680
+ table_options.huge_page_tlb_size = huge_page_tlb_size
681
+
682
+ if encoding_type == 'plain':
683
+ table_options.encoding_type = table_factory.kPlain
684
+ elif encoding_type == 'prefix':
685
+ table_options.encoding_type = table_factory.kPrefix
686
+ else:
687
+ raise ValueError("Unknown encoding_type: %s" % encoding_type)
688
+
689
+ table_options.full_scan_mode = full_scan_mode
690
+
691
+ self.factory.reset( table_factory.NewPlainTableFactory(table_options))
692
+ #############################################
693
+
694
+
695
+ ### Here are the MemtableFactories
696
+ cdef class PyMemtableFactory(object):
697
+ # pxd defines:
698
+ # cdef shared_ptr[memtablerep.MemTableRepFactory] factory
699
+
700
+ cdef shared_ptr[memtablerep.MemTableRepFactory] get_memtable_factory(self):
701
+ return self.factory
702
+
703
+
704
+ cdef class SkipListMemtableFactory(PyMemtableFactory):
705
+ def __init__(self):
706
+ self.factory.reset(memtablerep.NewSkipListFactory())
707
+
708
+
709
+ cdef class VectorMemtableFactory(PyMemtableFactory):
710
+ def __init__(self, count=0):
711
+ self.factory.reset(memtablerep.NewVectorRepFactory(count))
712
+
713
+
714
+ cdef class HashSkipListMemtableFactory(PyMemtableFactory):
715
+ def __init__(
716
+ self,
717
+ bucket_count=1000000,
718
+ skiplist_height=4,
719
+ skiplist_branching_factor=4):
720
+
721
+ self.factory.reset(
722
+ memtablerep.NewHashSkipListRepFactory(
723
+ bucket_count,
724
+ skiplist_height,
725
+ skiplist_branching_factor))
726
+
727
+
728
+ cdef class HashLinkListMemtableFactory(PyMemtableFactory):
729
+ def __init__(self, bucket_count=50000):
730
+ self.factory.reset(memtablerep.NewHashLinkListRepFactory(bucket_count))
731
+ ##################################
732
+
733
+
734
+ cdef class CompressionType(object):
735
+ no_compression = u'no_compression'
736
+ snappy_compression = u'snappy_compression'
737
+ zlib_compression = u'zlib_compression'
738
+ bzip2_compression = u'bzip2_compression'
739
+ lz4_compression = u'lz4_compression'
740
+ lz4hc_compression = u'lz4hc_compression'
741
+ xpress_compression = u'xpress_compression'
742
+ zstd_compression = u'zstd_compression'
743
+ zstdnotfinal_compression = u'zstdnotfinal_compression'
744
+ disable_compression = u'disable_compression'
745
+
746
+ cdef class CompactionPri(object):
747
+ by_compensated_size = u'by_compensated_size'
748
+ oldest_largest_seq_first = u'oldest_largest_seq_first'
749
+ oldest_smallest_seq_first = u'oldest_smallest_seq_first'
750
+ min_overlapping_ratio = u'min_overlapping_ratio'
751
+
752
+
753
+ cdef class TxnDBWritePolicy(object):
754
+ write_committed = u'write_committed'
755
+ write_prepared = u'write_prepared'
756
+ write_unprepared = u'write_unprepared'
757
+
758
+
759
+ cdef class _ColumnFamilyHandle:
760
+ """ This is an internal class that we will weakref for safety """
761
+ # pxd defines:
762
+ # cdef db.ColumnFamilyHandle* handle
763
+ # cdef object __weakref__
764
+ # cdef object weak_handle
765
+
766
+ def __cinit__(self):
767
+ self.handle = NULL
768
+
769
+ def __dealloc__(self):
770
+ if not self.handle == NULL:
771
+ del self.handle
772
+
773
+ @staticmethod
774
+ cdef from_handle_ptr(db.ColumnFamilyHandle* handle):
775
+ inst = <_ColumnFamilyHandle>_ColumnFamilyHandle.__new__(_ColumnFamilyHandle)
776
+ inst.handle = handle
777
+ return inst
778
+
779
+ @property
780
+ def name(self):
781
+ return self.handle.GetName()
782
+
783
+ @property
784
+ def id(self):
785
+ return self.handle.GetID()
786
+
787
+ @property
788
+ def weakref(self):
789
+ if self.weak_handle is None:
790
+ self.weak_handle = ColumnFamilyHandle.from_wrapper(self)
791
+ return self.weak_handle
792
+
793
+ cdef class ColumnFamilyHandle:
794
+ """ This represents a ColumnFamilyHandle """
795
+ # cdef object _ref
796
+ # cdef readonly bytes name
797
+ # cdef readonly int id
798
+
799
+ def __cinit__(self, weakhandle):
800
+ self._ref = weakhandle
801
+ self.name = self._ref().name
802
+ self.id = self._ref().id
803
+
804
+ def __init__(self, *):
805
+ raise TypeError("These can not be constructed from Python")
806
+
807
+ @staticmethod
808
+ cdef object from_wrapper(_ColumnFamilyHandle real_handle):
809
+ return ColumnFamilyHandle.__new__(ColumnFamilyHandle, weakref.ref(real_handle))
810
+
811
+ @property
812
+ def is_valid(self):
813
+ return self._ref() is not None
814
+
815
+ def __repr__(self):
816
+ valid = "valid" if self.is_valid else "invalid"
817
+ return f"<ColumnFamilyHandle name: {self.name}, id: {self.id}, state: {valid}>"
818
+
819
+ cdef db.ColumnFamilyHandle* get_handle(self) except NULL:
820
+ cdef _ColumnFamilyHandle real_handle = self._ref()
821
+ if real_handle is None:
822
+ raise ValueError(f"{self} is no longer a valid ColumnFamilyHandle!")
823
+ return real_handle.handle
824
+
825
+ def __eq__(self, other):
826
+ cdef ColumnFamilyHandle fast_other
827
+ if isinstance(other, ColumnFamilyHandle):
828
+ fast_other = other
829
+ return (
830
+ self.name == fast_other.name
831
+ and self.id == fast_other.id
832
+ and self._ref == fast_other._ref
833
+ )
834
+ return False
835
+
836
+ def __lt__(self, other):
837
+ cdef ColumnFamilyHandle fast_other
838
+ if isinstance(other, ColumnFamilyHandle):
839
+ return self.id < other.id
840
+ return NotImplemented
841
+
842
+ # Since @total_ordering isn't a thing for cython
843
+ def __ne__(self, other):
844
+ return not self == other
845
+
846
+ def __gt__(self, other):
847
+ return other < self
848
+
849
+ def __le__(self, other):
850
+ return not other < self
851
+
852
+ def __ge__(self, other):
853
+ return not self < other
854
+
855
+ def __hash__(self):
856
+ # hash of a weakref matches that of its original ref'ed object
857
+ # so we use the id of our weakref object here to prevent
858
+ # a situation where we are invalid, but match a valid handle's hash
859
+ return hash((self.id, self.name, id(self._ref)))
860
+
861
+
862
+ cdef class ColumnFamilyOptions(object):
863
+ # pxd defines:
864
+ # cdef options.ColumnFamilyOptions* copts
865
+ # cdef PyComparator py_comparator
866
+ # cdef PyMergeOperator py_merge_operator
867
+ # cdef PySliceTransform py_prefix_extractor
868
+ # cdef PyTableFactory py_table_factory
869
+ # cdef PyMemtableFactory py_memtable_factory
870
+ # cdef cpp_bool in_use
871
+
872
+ def __cinit__(self):
873
+ self.copts = NULL
874
+ self.copts = new options.ColumnFamilyOptions()
875
+ self.in_use = False
876
+
877
+ def __dealloc__(self):
878
+ if not self.copts == NULL:
879
+ del self.copts
880
+
881
+ def __init__(self, **kwargs):
882
+ self.py_comparator = BytewiseComparator()
883
+ self.py_merge_operator = None
884
+ self.py_prefix_extractor = None
885
+ self.py_table_factory = None
886
+ self.py_memtable_factory = None
887
+
888
+ for key, value in kwargs.items():
889
+ setattr(self, key, value)
890
+
891
+ property write_buffer_size:
892
+ def __get__(self):
893
+ return self.copts.write_buffer_size
894
+ def __set__(self, value):
895
+ self.copts.write_buffer_size = value
896
+
897
+ property max_write_buffer_number:
898
+ def __get__(self):
899
+ return self.copts.max_write_buffer_number
900
+ def __set__(self, value):
901
+ self.copts.max_write_buffer_number = value
902
+
903
+ property min_write_buffer_number_to_merge:
904
+ def __get__(self):
905
+ return self.copts.min_write_buffer_number_to_merge
906
+ def __set__(self, value):
907
+ self.copts.min_write_buffer_number_to_merge = value
908
+
909
+ property compression_opts:
910
+ def __get__(self):
911
+ cdef dict ret_ob = {}
912
+
913
+ ret_ob['window_bits'] = self.copts.compression_opts.window_bits
914
+ ret_ob['level'] = self.copts.compression_opts.level
915
+ ret_ob['strategy'] = self.copts.compression_opts.strategy
916
+ ret_ob['max_dict_bytes'] = self.copts.compression_opts.max_dict_bytes
917
+
918
+ return ret_ob
919
+
920
+ def __set__(self, dict value):
921
+ cdef options.CompressionOptions* copts
922
+ copts = cython.address(self.copts.compression_opts)
923
+ # CompressionOptions(int wbits, int _lev, int _strategy, int _max_dict_bytes)
924
+ if 'window_bits' in value:
925
+ copts.window_bits = value['window_bits']
926
+ if 'level' in value:
927
+ copts.level = value['level']
928
+ if 'strategy' in value:
929
+ copts.strategy = value['strategy']
930
+ if 'max_dict_bytes' in value:
931
+ copts.max_dict_bytes = value['max_dict_bytes']
932
+
933
+ property compaction_pri:
934
+ def __get__(self):
935
+ if self.copts.compaction_pri == options.kByCompensatedSize:
936
+ return CompactionPri.by_compensated_size
937
+ if self.copts.compaction_pri == options.kOldestLargestSeqFirst:
938
+ return CompactionPri.oldest_largest_seq_first
939
+ if self.copts.compaction_pri == options.kOldestSmallestSeqFirst:
940
+ return CompactionPri.oldest_smallest_seq_first
941
+ if self.copts.compaction_pri == options.kMinOverlappingRatio:
942
+ return CompactionPri.min_overlapping_ratio
943
+ def __set__(self, value):
944
+ if value == CompactionPri.by_compensated_size:
945
+ self.copts.compaction_pri = options.kByCompensatedSize
946
+ elif value == CompactionPri.oldest_largest_seq_first:
947
+ self.copts.compaction_pri = options.kOldestLargestSeqFirst
948
+ elif value == CompactionPri.oldest_smallest_seq_first:
949
+ self.copts.compaction_pri = options.kOldestSmallestSeqFirst
950
+ elif value == CompactionPri.min_overlapping_ratio:
951
+ self.copts.compaction_pri = options.kMinOverlappingRatio
952
+ else:
953
+ raise TypeError("Unknown compaction pri: %s" % value)
954
+
955
+ property compression:
956
+ def __get__(self):
957
+ if self.copts.compression == options.kNoCompression:
958
+ return CompressionType.no_compression
959
+ elif self.copts.compression == options.kSnappyCompression:
960
+ return CompressionType.snappy_compression
961
+ elif self.copts.compression == options.kZlibCompression:
962
+ return CompressionType.zlib_compression
963
+ elif self.copts.compression == options.kBZip2Compression:
964
+ return CompressionType.bzip2_compression
965
+ elif self.copts.compression == options.kLZ4Compression:
966
+ return CompressionType.lz4_compression
967
+ elif self.copts.compression == options.kLZ4HCCompression:
968
+ return CompressionType.lz4hc_compression
969
+ elif self.copts.compression == options.kXpressCompression:
970
+ return CompressionType.xpress_compression
971
+ elif self.copts.compression == options.kZSTD:
972
+ return CompressionType.zstd_compression
973
+ elif self.copts.compression == options.kZSTDNotFinalCompression:
974
+ return CompressionType.zstdnotfinal_compression
975
+ elif self.copts.compression == options.kDisableCompressionOption:
976
+ return CompressionType.disable_compression
977
+ else:
978
+ raise Exception("Unknonw type: %s" % self.opts.compression)
979
+
980
+ def __set__(self, value):
981
+ if value == CompressionType.no_compression:
982
+ self.copts.compression = options.kNoCompression
983
+ elif value == CompressionType.snappy_compression:
984
+ self.copts.compression = options.kSnappyCompression
985
+ elif value == CompressionType.zlib_compression:
986
+ self.copts.compression = options.kZlibCompression
987
+ elif value == CompressionType.bzip2_compression:
988
+ self.copts.compression = options.kBZip2Compression
989
+ elif value == CompressionType.lz4_compression:
990
+ self.copts.compression = options.kLZ4Compression
991
+ elif value == CompressionType.lz4hc_compression:
992
+ self.copts.compression = options.kLZ4HCCompression
993
+ elif value == CompressionType.zstd_compression:
994
+ self.copts.compression = options.kZSTD
995
+ elif value == CompressionType.zstdnotfinal_compression:
996
+ self.copts.compression = options.kZSTDNotFinalCompression
997
+ elif value == CompressionType.disable_compression:
998
+ self.copts.compression = options.kDisableCompressionOption
999
+ else:
1000
+ raise TypeError("Unknown compression: %s" % value)
1001
+
1002
+ property max_compaction_bytes:
1003
+ def __get__(self):
1004
+ return self.copts.max_compaction_bytes
1005
+ def __set__(self, value):
1006
+ self.copts.max_compaction_bytes = value
1007
+
1008
+ property num_levels:
1009
+ def __get__(self):
1010
+ return self.copts.num_levels
1011
+ def __set__(self, value):
1012
+ self.copts.num_levels = value
1013
+
1014
+ property level0_file_num_compaction_trigger:
1015
+ def __get__(self):
1016
+ return self.copts.level0_file_num_compaction_trigger
1017
+ def __set__(self, value):
1018
+ self.copts.level0_file_num_compaction_trigger = value
1019
+
1020
+ property level0_slowdown_writes_trigger:
1021
+ def __get__(self):
1022
+ return self.copts.level0_slowdown_writes_trigger
1023
+ def __set__(self, value):
1024
+ self.copts.level0_slowdown_writes_trigger = value
1025
+
1026
+ property level0_stop_writes_trigger:
1027
+ def __get__(self):
1028
+ return self.copts.level0_stop_writes_trigger
1029
+ def __set__(self, value):
1030
+ self.copts.level0_stop_writes_trigger = value
1031
+
1032
+ property max_mem_compaction_level:
1033
+ def __get__(self):
1034
+ return self.copts.max_mem_compaction_level
1035
+ def __set__(self, value):
1036
+ self.copts.max_mem_compaction_level = value
1037
+
1038
+ property target_file_size_base:
1039
+ def __get__(self):
1040
+ return self.copts.target_file_size_base
1041
+ def __set__(self, value):
1042
+ self.copts.target_file_size_base = value
1043
+
1044
+ property target_file_size_multiplier:
1045
+ def __get__(self):
1046
+ return self.copts.target_file_size_multiplier
1047
+ def __set__(self, value):
1048
+ self.copts.target_file_size_multiplier = value
1049
+
1050
+ property max_bytes_for_level_base:
1051
+ def __get__(self):
1052
+ return self.copts.max_bytes_for_level_base
1053
+ def __set__(self, value):
1054
+ self.copts.max_bytes_for_level_base = value
1055
+
1056
+ property max_bytes_for_level_multiplier:
1057
+ def __get__(self):
1058
+ return self.copts.max_bytes_for_level_multiplier
1059
+ def __set__(self, value):
1060
+ self.copts.max_bytes_for_level_multiplier = value
1061
+
1062
+ property max_bytes_for_level_multiplier_additional:
1063
+ def __get__(self):
1064
+ return self.copts.max_bytes_for_level_multiplier_additional
1065
+ def __set__(self, value):
1066
+ self.copts.max_bytes_for_level_multiplier_additional = value
1067
+
1068
+ property arena_block_size:
1069
+ def __get__(self):
1070
+ return self.copts.arena_block_size
1071
+ def __set__(self, value):
1072
+ self.copts.arena_block_size = value
1073
+
1074
+ property disable_auto_compactions:
1075
+ def __get__(self):
1076
+ return self.copts.disable_auto_compactions
1077
+ def __set__(self, value):
1078
+ self.copts.disable_auto_compactions = value
1079
+
1080
+ # FIXME: remove to util/options_helper.h
1081
+ # property allow_os_buffer:
1082
+ # def __get__(self):
1083
+ # return self.copts.allow_os_buffer
1084
+ # def __set__(self, value):
1085
+ # self.copts.allow_os_buffer = value
1086
+
1087
+ property compaction_style:
1088
+ def __get__(self):
1089
+ if self.copts.compaction_style == kCompactionStyleLevel:
1090
+ return 'level'
1091
+ if self.copts.compaction_style == kCompactionStyleUniversal:
1092
+ return 'universal'
1093
+ if self.copts.compaction_style == kCompactionStyleFIFO:
1094
+ return 'fifo'
1095
+ if self.copts.compaction_style == kCompactionStyleNone:
1096
+ return 'none'
1097
+ raise Exception("Unknown compaction_style")
1098
+
1099
+ def __set__(self, str value):
1100
+ if value == 'level':
1101
+ self.copts.compaction_style = kCompactionStyleLevel
1102
+ elif value == 'universal':
1103
+ self.copts.compaction_style = kCompactionStyleUniversal
1104
+ elif value == 'fifo':
1105
+ self.copts.compaction_style = kCompactionStyleFIFO
1106
+ elif value == 'none':
1107
+ self.copts.compaction_style = kCompactionStyleNone
1108
+ else:
1109
+ raise Exception("Unknown compaction style")
1110
+
1111
+ property compaction_options_universal:
1112
+ def __get__(self):
1113
+ cdef universal_compaction.CompactionOptionsUniversal uopts
1114
+ cdef dict ret_ob = {}
1115
+
1116
+ uopts = self.copts.compaction_options_universal
1117
+
1118
+ ret_ob['size_ratio'] = uopts.size_ratio
1119
+ ret_ob['min_merge_width'] = uopts.min_merge_width
1120
+ ret_ob['max_merge_width'] = uopts.max_merge_width
1121
+ ret_ob['max_size_amplification_percent'] = uopts.max_size_amplification_percent
1122
+ ret_ob['compression_size_percent'] = uopts.compression_size_percent
1123
+
1124
+ if uopts.stop_style == kCompactionStopStyleSimilarSize:
1125
+ ret_ob['stop_style'] = 'similar_size'
1126
+ elif uopts.stop_style == kCompactionStopStyleTotalSize:
1127
+ ret_ob['stop_style'] = 'total_size'
1128
+ else:
1129
+ raise Exception("Unknown compaction style")
1130
+
1131
+ return ret_ob
1132
+
1133
+ def __set__(self, dict value):
1134
+ cdef universal_compaction.CompactionOptionsUniversal* uopts
1135
+ uopts = cython.address(self.copts.compaction_options_universal)
1136
+
1137
+ if 'size_ratio' in value:
1138
+ uopts.size_ratio = value['size_ratio']
1139
+
1140
+ if 'min_merge_width' in value:
1141
+ uopts.min_merge_width = value['min_merge_width']
1142
+
1143
+ if 'max_merge_width' in value:
1144
+ uopts.max_merge_width = value['max_merge_width']
1145
+
1146
+ if 'max_size_amplification_percent' in value:
1147
+ uopts.max_size_amplification_percent = value['max_size_amplification_percent']
1148
+
1149
+ if 'compression_size_percent' in value:
1150
+ uopts.compression_size_percent = value['compression_size_percent']
1151
+
1152
+ if 'stop_style' in value:
1153
+ if value['stop_style'] == 'similar_size':
1154
+ uopts.stop_style = kCompactionStopStyleSimilarSize
1155
+ elif value['stop_style'] == 'total_size':
1156
+ uopts.stop_style = kCompactionStopStyleTotalSize
1157
+ else:
1158
+ raise Exception("Unknown compaction style")
1159
+
1160
+ # Deprecate
1161
+ # property filter_deletes:
1162
+ # def __get__(self):
1163
+ # return self.copts.filter_deletes
1164
+ # def __set__(self, value):
1165
+ # self.copts.filter_deletes = value
1166
+
1167
+ property max_sequential_skip_in_iterations:
1168
+ def __get__(self):
1169
+ return self.copts.max_sequential_skip_in_iterations
1170
+ def __set__(self, value):
1171
+ self.copts.max_sequential_skip_in_iterations = value
1172
+
1173
+ property inplace_update_support:
1174
+ def __get__(self):
1175
+ return self.copts.inplace_update_support
1176
+ def __set__(self, value):
1177
+ self.copts.inplace_update_support = value
1178
+
1179
+ property table_factory:
1180
+ def __get__(self):
1181
+ return self.py_table_factory
1182
+
1183
+ def __set__(self, PyTableFactory value):
1184
+ self.py_table_factory = value
1185
+ self.copts.table_factory = value.get_table_factory()
1186
+
1187
+ property memtable_factory:
1188
+ def __get__(self):
1189
+ return self.py_memtable_factory
1190
+
1191
+ def __set__(self, PyMemtableFactory value):
1192
+ self.py_memtable_factory = value
1193
+ self.copts.memtable_factory = value.get_memtable_factory()
1194
+
1195
+ property inplace_update_num_locks:
1196
+ def __get__(self):
1197
+ return self.copts.inplace_update_num_locks
1198
+ def __set__(self, value):
1199
+ self.copts.inplace_update_num_locks = value
1200
+
1201
+ property comparator:
1202
+ def __get__(self):
1203
+ return self.py_comparator.get_ob()
1204
+
1205
+ def __set__(self, value):
1206
+ if isinstance(value, PyComparator):
1207
+ if (<PyComparator?>value).get_comparator() == NULL:
1208
+ raise Exception("Cannot set %s as comparator" % value)
1209
+ else:
1210
+ self.py_comparator = value
1211
+ else:
1212
+ self.py_comparator = PyGenericComparator(value)
1213
+
1214
+ self.copts.comparator = self.py_comparator.get_comparator()
1215
+
1216
+ property merge_operator:
1217
+ def __get__(self):
1218
+ if self.py_merge_operator is None:
1219
+ return None
1220
+ return self.py_merge_operator.get_ob()
1221
+
1222
+ def __set__(self, value):
1223
+ self.py_merge_operator = PyMergeOperator(value)
1224
+ self.copts.merge_operator = self.py_merge_operator.get_operator()
1225
+
1226
+ property prefix_extractor:
1227
+ def __get__(self):
1228
+ if self.py_prefix_extractor is None:
1229
+ return None
1230
+ return self.py_prefix_extractor.get_ob()
1231
+
1232
+ def __set__(self, value):
1233
+ self.py_prefix_extractor = PySliceTransform(value)
1234
+ self.copts.prefix_extractor = self.py_prefix_extractor.get_transformer()
1235
+
1236
+
1237
+ cdef class Options(ColumnFamilyOptions):
1238
+ # pxd defines:
1239
+ # cdef options.Options* opts
1240
+ # cdef PyCache py_row_cache
1241
+
1242
+ def __cinit__(self):
1243
+ # Destroy the existing ColumnFamilyOptions()
1244
+ del self.copts
1245
+ self.opts = NULL
1246
+ self.copts = self.opts = new options.Options()
1247
+ self.in_use = False
1248
+
1249
+ def __dealloc__(self):
1250
+ if not self.opts == NULL:
1251
+ self.copts = NULL
1252
+ del self.opts
1253
+
1254
+ def __init__(self, **kwargs):
1255
+ ColumnFamilyOptions.__init__(self)
1256
+ self.py_row_cache = None
1257
+
1258
+ for key, value in kwargs.items():
1259
+ setattr(self, key, value)
1260
+
1261
+ property create_if_missing:
1262
+ def __get__(self):
1263
+ return self.opts.create_if_missing
1264
+ def __set__(self, value):
1265
+ self.opts.create_if_missing = value
1266
+
1267
+ property error_if_exists:
1268
+ def __get__(self):
1269
+ return self.opts.error_if_exists
1270
+ def __set__(self, value):
1271
+ self.opts.error_if_exists = value
1272
+
1273
+ property paranoid_checks:
1274
+ def __get__(self):
1275
+ return self.opts.paranoid_checks
1276
+ def __set__(self, value):
1277
+ self.opts.paranoid_checks = value
1278
+
1279
+ property max_open_files:
1280
+ def __get__(self):
1281
+ return self.opts.max_open_files
1282
+ def __set__(self, value):
1283
+ self.opts.max_open_files = value
1284
+
1285
+ property use_fsync:
1286
+ def __get__(self):
1287
+ return self.opts.use_fsync
1288
+ def __set__(self, value):
1289
+ self.opts.use_fsync = value
1290
+
1291
+ property db_log_dir:
1292
+ def __get__(self):
1293
+ return string_to_path(self.opts.db_log_dir)
1294
+ def __set__(self, value):
1295
+ self.opts.db_log_dir = path_to_string(value)
1296
+
1297
+ property wal_dir:
1298
+ def __get__(self):
1299
+ return string_to_path(self.opts.wal_dir)
1300
+ def __set__(self, value):
1301
+ self.opts.wal_dir = path_to_string(value)
1302
+
1303
+ property delete_obsolete_files_period_micros:
1304
+ def __get__(self):
1305
+ return self.opts.delete_obsolete_files_period_micros
1306
+ def __set__(self, value):
1307
+ self.opts.delete_obsolete_files_period_micros = value
1308
+
1309
+ property max_background_compactions:
1310
+ def __get__(self):
1311
+ return self.opts.max_background_compactions
1312
+ def __set__(self, value):
1313
+ self.opts.max_background_compactions = value
1314
+
1315
+ property max_background_flushes:
1316
+ def __get__(self):
1317
+ return self.opts.max_background_flushes
1318
+ def __set__(self, value):
1319
+ self.opts.max_background_flushes = value
1320
+
1321
+ property max_log_file_size:
1322
+ def __get__(self):
1323
+ return self.opts.max_log_file_size
1324
+ def __set__(self, value):
1325
+ self.opts.max_log_file_size = value
1326
+
1327
+ property db_write_buffer_size:
1328
+ def __get__(self):
1329
+ return self.opts.db_write_buffer_size
1330
+ def __set__(self, value):
1331
+ self.opts.db_write_buffer_size = value
1332
+
1333
+ property log_file_time_to_roll:
1334
+ def __get__(self):
1335
+ return self.opts.log_file_time_to_roll
1336
+ def __set__(self, value):
1337
+ self.opts.log_file_time_to_roll = value
1338
+
1339
+ property keep_log_file_num:
1340
+ def __get__(self):
1341
+ return self.opts.keep_log_file_num
1342
+ def __set__(self, value):
1343
+ self.opts.keep_log_file_num = value
1344
+
1345
+ property max_manifest_file_size:
1346
+ def __get__(self):
1347
+ return self.opts.max_manifest_file_size
1348
+ def __set__(self, value):
1349
+ self.opts.max_manifest_file_size = value
1350
+
1351
+ property table_cache_numshardbits:
1352
+ def __get__(self):
1353
+ return self.opts.table_cache_numshardbits
1354
+ def __set__(self, value):
1355
+ self.opts.table_cache_numshardbits = value
1356
+
1357
+ property wal_ttl_seconds:
1358
+ def __get__(self):
1359
+ return self.opts.WAL_ttl_seconds
1360
+ def __set__(self, value):
1361
+ self.opts.WAL_ttl_seconds = value
1362
+
1363
+ property wal_size_limit_mb:
1364
+ def __get__(self):
1365
+ return self.opts.WAL_size_limit_MB
1366
+ def __set__(self, value):
1367
+ self.opts.WAL_size_limit_MB = value
1368
+
1369
+ property manifest_preallocation_size:
1370
+ def __get__(self):
1371
+ return self.opts.manifest_preallocation_size
1372
+ def __set__(self, value):
1373
+ self.opts.manifest_preallocation_size = value
1374
+
1375
+ property enable_write_thread_adaptive_yield:
1376
+ def __get__(self):
1377
+ return self.opts.enable_write_thread_adaptive_yield
1378
+ def __set__(self, value):
1379
+ self.opts.enable_write_thread_adaptive_yield = value
1380
+
1381
+ property allow_concurrent_memtable_write:
1382
+ def __get__(self):
1383
+ return self.opts.allow_concurrent_memtable_write
1384
+ def __set__(self, value):
1385
+ self.opts.allow_concurrent_memtable_write = value
1386
+
1387
+ property allow_mmap_reads:
1388
+ def __get__(self):
1389
+ return self.opts.allow_mmap_reads
1390
+ def __set__(self, value):
1391
+ self.opts.allow_mmap_reads = value
1392
+
1393
+ property allow_mmap_writes:
1394
+ def __get__(self):
1395
+ return self.opts.allow_mmap_writes
1396
+ def __set__(self, value):
1397
+ self.opts.allow_mmap_writes = value
1398
+
1399
+ property is_fd_close_on_exec:
1400
+ def __get__(self):
1401
+ return self.opts.is_fd_close_on_exec
1402
+ def __set__(self, value):
1403
+ self.opts.is_fd_close_on_exec = value
1404
+
1405
+ property stats_dump_period_sec:
1406
+ def __get__(self):
1407
+ return self.opts.stats_dump_period_sec
1408
+ def __set__(self, value):
1409
+ self.opts.stats_dump_period_sec = value
1410
+
1411
+ property advise_random_on_open:
1412
+ def __get__(self):
1413
+ return self.opts.advise_random_on_open
1414
+ def __set__(self, value):
1415
+ self.opts.advise_random_on_open = value
1416
+
1417
+ # TODO: need to remove -Wconversion to make this work
1418
+ # property access_hint_on_compaction_start:
1419
+ # def __get__(self):
1420
+ # return self.opts.access_hint_on_compaction_start
1421
+ # def __set__(self, AccessHint value):
1422
+ # self.opts.access_hint_on_compaction_start = value
1423
+
1424
+ property use_adaptive_mutex:
1425
+ def __get__(self):
1426
+ return self.opts.use_adaptive_mutex
1427
+ def __set__(self, value):
1428
+ self.opts.use_adaptive_mutex = value
1429
+
1430
+ property bytes_per_sync:
1431
+ def __get__(self):
1432
+ return self.opts.bytes_per_sync
1433
+ def __set__(self, value):
1434
+ self.opts.bytes_per_sync = value
1435
+
1436
+ property row_cache:
1437
+ def __get__(self):
1438
+ return self.py_row_cache
1439
+
1440
+ def __set__(self, value):
1441
+ if value is None:
1442
+ self.py_row_cache = None
1443
+ self.opts.row_cache.reset()
1444
+ elif not isinstance(value, PyCache):
1445
+ raise Exception("row_cache must be a Cache object")
1446
+ else:
1447
+ self.py_row_cache = value
1448
+ self.opts.row_cache = self.py_row_cache.get_cache()
1449
+
1450
+
1451
+ # cpp_bool skip_checking_sst_file_sizes_on_db_open
1452
+ property skip_checking_sst_file_sizes_on_db_open:
1453
+ def __get__(self):
1454
+ return self.opts.skip_checking_sst_file_sizes_on_db_open
1455
+ def __set__(self, value):
1456
+ self.opts.skip_checking_sst_file_sizes_on_db_open = value
1457
+
1458
+ # cpp_bool skip_stats_update_on_db_open
1459
+ property skip_stats_update_on_db_open:
1460
+ def __get__(self):
1461
+ return self.opts.skip_stats_update_on_db_open
1462
+ def __set__(self, value):
1463
+ self.opts.skip_stats_update_on_db_open = value
1464
+
1465
+
1466
+ cdef class TransactionDBOptions(object):
1467
+ # pxd defines:
1468
+ # cdef transaction.TransactionDBOptions* opts
1469
+ # cdef bint in_use
1470
+
1471
+ def __cinit__(self):
1472
+ self.opts = new transaction.TransactionDBOptions()
1473
+ self.in_use = False
1474
+
1475
+ def __dealloc__(self):
1476
+ if self.opts != NULL:
1477
+ del self.opts
1478
+ self.opts = NULL
1479
+
1480
+ def __init__(self, **kwargs):
1481
+ for key, value in kwargs.items():
1482
+ setattr(self, key, value)
1483
+
1484
+ property max_num_locks:
1485
+ def __get__(self):
1486
+ return self.opts.max_num_locks
1487
+ def __set__(self, value):
1488
+ self.opts.max_num_locks = value
1489
+
1490
+ property max_num_deadlocks:
1491
+ def __get__(self):
1492
+ return self.opts.max_num_deadlocks
1493
+ def __set__(self, value):
1494
+ self.opts.max_num_deadlocks = value
1495
+
1496
+ property num_stripes:
1497
+ def __get__(self):
1498
+ return self.opts.num_stripes
1499
+ def __set__(self, value):
1500
+ self.opts.num_stripes = value
1501
+
1502
+ property transaction_lock_timeout:
1503
+ def __get__(self):
1504
+ return self.opts.transaction_lock_timeout
1505
+ def __set__(self, value):
1506
+ self.opts.transaction_lock_timeout = value
1507
+
1508
+ property default_lock_timeout:
1509
+ def __get__(self):
1510
+ return self.opts.default_lock_timeout
1511
+ def __set__(self, value):
1512
+ self.opts.default_lock_timeout = value
1513
+
1514
+ property write_policy:
1515
+ def __get__(self):
1516
+ if self.opts.write_policy == transaction.WRITE_COMMITTED:
1517
+ return TxnDBWritePolicy.write_committed
1518
+ elif self.opts.write_policy == transaction.WRITE_PREPARED:
1519
+ return TxnDBWritePolicy.write_prepared
1520
+ elif self.opts.write_policy == transaction.WRITE_UNPREPARED:
1521
+ return TxnDBWritePolicy.write_unprepared
1522
+ raise TypeError("Unknown write policy value: %r" % self.opts.write_policy)
1523
+
1524
+ def __set__(self, value):
1525
+ if value in (TxnDBWritePolicy.write_committed, b'write_committed', 'write_committed'):
1526
+ self.opts.write_policy = transaction.WRITE_COMMITTED
1527
+ elif value in (TxnDBWritePolicy.write_prepared, b'write_prepared', 'write_prepared'):
1528
+ self.opts.write_policy = transaction.WRITE_PREPARED
1529
+ elif value in (TxnDBWritePolicy.write_unprepared, b'write_unprepared', 'write_unprepared'):
1530
+ self.opts.write_policy = transaction.WRITE_UNPREPARED
1531
+ else:
1532
+ raise TypeError("Unknown transaction write policy: %r" % value)
1533
+
1534
+ property rollback_merge_operands:
1535
+ def __get__(self):
1536
+ return self.opts.rollback_merge_operands
1537
+ def __set__(self, value):
1538
+ self.opts.rollback_merge_operands = value
1539
+
1540
+ property skip_concurrency_control:
1541
+ def __get__(self):
1542
+ return self.opts.skip_concurrency_control
1543
+ def __set__(self, value):
1544
+ self.opts.skip_concurrency_control = value
1545
+
1546
+ property default_write_batch_flush_threshold:
1547
+ def __get__(self):
1548
+ return self.opts.default_write_batch_flush_threshold
1549
+ def __set__(self, value):
1550
+ self.opts.default_write_batch_flush_threshold = value
1551
+
1552
+
1553
+ cdef class TransactionOptions(object):
1554
+ # pxd defines:
1555
+ # cdef transaction.TransactionOptions* opts
1556
+
1557
+ def __cinit__(self):
1558
+ self.opts = new transaction.TransactionOptions()
1559
+
1560
+ def __dealloc__(self):
1561
+ if self.opts != NULL:
1562
+ del self.opts
1563
+ self.opts = NULL
1564
+
1565
+ def __init__(self, **kwargs):
1566
+ for key, value in kwargs.items():
1567
+ setattr(self, key, value)
1568
+
1569
+ property set_snapshot:
1570
+ def __get__(self):
1571
+ return self.opts.set_snapshot
1572
+ def __set__(self, value):
1573
+ self.opts.set_snapshot = value
1574
+
1575
+ property deadlock_detect:
1576
+ def __get__(self):
1577
+ return self.opts.deadlock_detect
1578
+ def __set__(self, value):
1579
+ self.opts.deadlock_detect = value
1580
+
1581
+ property use_only_the_last_commit_time_batch_for_recovery:
1582
+ def __get__(self):
1583
+ return self.opts.use_only_the_last_commit_time_batch_for_recovery
1584
+ def __set__(self, value):
1585
+ self.opts.use_only_the_last_commit_time_batch_for_recovery = value
1586
+
1587
+ property lock_timeout:
1588
+ def __get__(self):
1589
+ return self.opts.lock_timeout
1590
+ def __set__(self, value):
1591
+ self.opts.lock_timeout = value
1592
+
1593
+ property expiration:
1594
+ def __get__(self):
1595
+ return self.opts.expiration
1596
+ def __set__(self, value):
1597
+ self.opts.expiration = value
1598
+
1599
+ property deadlock_detect_depth:
1600
+ def __get__(self):
1601
+ return self.opts.deadlock_detect_depth
1602
+ def __set__(self, value):
1603
+ self.opts.deadlock_detect_depth = value
1604
+
1605
+ property max_write_batch_size:
1606
+ def __get__(self):
1607
+ return self.opts.max_write_batch_size
1608
+ def __set__(self, value):
1609
+ self.opts.max_write_batch_size = value
1610
+
1611
+ property skip_concurrency_control:
1612
+ def __get__(self):
1613
+ return self.opts.skip_concurrency_control
1614
+ def __set__(self, value):
1615
+ self.opts.skip_concurrency_control = value
1616
+
1617
+ property skip_prepare:
1618
+ def __get__(self):
1619
+ return self.opts.skip_prepare
1620
+ def __set__(self, value):
1621
+ self.opts.skip_prepare = value
1622
+
1623
+ property write_batch_flush_threshold:
1624
+ def __get__(self):
1625
+ return self.opts.write_batch_flush_threshold
1626
+ def __set__(self, value):
1627
+ self.opts.write_batch_flush_threshold = value
1628
+
1629
+
1630
+
1631
+ # TODO inherit from writableDB
1632
+ cdef class WriteBatch(object):
1633
+ # pxd defines:
1634
+ # cdef db.WriteBatch* batch
1635
+
1636
+ def __cinit__(self, data=None):
1637
+ self.batch = NULL
1638
+ if data is not None:
1639
+ self.batch = new db.WriteBatch(bytes_to_string(data))
1640
+ else:
1641
+ self.batch = new db.WriteBatch()
1642
+
1643
+ def __dealloc__(self):
1644
+ if not self.batch == NULL:
1645
+ del self.batch
1646
+
1647
+ cpdef void put(self, bytes key, bytes value, ColumnFamilyHandle column_family = None):
1648
+ cdef db.ColumnFamilyHandle* cf_handle = NULL
1649
+ if column_family is not None:
1650
+ cf_handle = column_family.get_handle()
1651
+ # nullptr is default family
1652
+ self.batch.Put(cf_handle, bytes_to_slice(key), bytes_to_slice(value))
1653
+
1654
+ cpdef void merge(self, bytes key, bytes value, ColumnFamilyHandle column_family = None):
1655
+ cdef db.ColumnFamilyHandle* cf_handle = NULL
1656
+ if column_family is not None:
1657
+ cf_handle = column_family.get_handle()
1658
+ # nullptr is default family
1659
+ self.batch.Merge(cf_handle, bytes_to_slice(key), bytes_to_slice(value))
1660
+
1661
+ # Here delete is with def for backwards compatibility.
1662
+ # Cython has a bug that doesn't allow a method with name `delete`
1663
+ # so we have to use a different name for `cpdef`-ed ones.
1664
+ def delete(self, bytes key, ColumnFamilyHandle column_family = None):
1665
+ return self.delete_single(key, column_family)
1666
+
1667
+ cpdef void delete_single(self, bytes key, ColumnFamilyHandle column_family = None):
1668
+ cdef db.ColumnFamilyHandle* cf_handle = NULL
1669
+ if column_family is not None:
1670
+ cf_handle = column_family.get_handle()
1671
+ # nullptr is default family
1672
+ self.batch.Delete(cf_handle, bytes_to_slice(key))
1673
+
1674
+ cpdef void delete_range(self, bytes begin_key, bytes end_key, ColumnFamilyHandle column_family = None):
1675
+ cdef db.ColumnFamilyHandle* cf_handle = NULL
1676
+ if column_family is not None:
1677
+ cf_handle = column_family.get_handle()
1678
+
1679
+ # nullptr is default family
1680
+ self.batch.DeleteRange(cf_handle, bytes_to_slice(begin_key), bytes_to_slice(end_key))
1681
+
1682
+ cpdef void clear(self):
1683
+ self.batch.Clear()
1684
+
1685
+ cpdef data(self):
1686
+ return string_to_bytes(self.batch.Data())
1687
+
1688
+ cpdef int count(self):
1689
+ return self.batch.Count()
1690
+
1691
+ def __iter__(self):
1692
+ return WriteBatchIterator(self)
1693
+
1694
+
1695
+ cdef class WriteBatchIterator(object):
1696
+ # Need a reference to the WriteBatch.
1697
+ # The BatchItems are only pointers to the memory in WriteBatch.
1698
+
1699
+ # pxd defines:
1700
+ # cdef WriteBatch batch
1701
+ # cdef vector[db.BatchItem] items
1702
+ # cdef size_t pos
1703
+
1704
+ def __init__(self, WriteBatch batch):
1705
+ cdef Status st
1706
+
1707
+ self.batch = batch
1708
+ self.pos = 0
1709
+
1710
+ st = db.get_batch_items(batch.batch, cython.address(self.items))
1711
+ check_status(st)
1712
+
1713
+ def __iter__(self):
1714
+ return self
1715
+
1716
+ def __next__(self):
1717
+ if self.pos == self.items.size():
1718
+ raise StopIteration()
1719
+
1720
+ cdef str op
1721
+
1722
+ if self.items[self.pos].op == db.BatchItemOpPut:
1723
+ op = "Put"
1724
+ elif self.items[self.pos].op == db.BatchItemOpMerge:
1725
+ op = "Merge"
1726
+ elif self.items[self.pos].op == db.BatchItemOpDelte:
1727
+ op = "Delete"
1728
+
1729
+ if self.items[self.pos].column_family_id != 0: # Column Family is set
1730
+ ret = (
1731
+ op,
1732
+ (
1733
+ self.items[self.pos].column_family_id,
1734
+ slice_to_bytes(self.items[self.pos].key)
1735
+ ),
1736
+ slice_to_bytes(self.items[self.pos].value)
1737
+ )
1738
+ else:
1739
+ ret = (
1740
+ op,
1741
+ slice_to_bytes(self.items[self.pos].key),
1742
+ slice_to_bytes(self.items[self.pos].value)
1743
+ )
1744
+ self.pos += 1
1745
+ return ret
1746
+
1747
+
1748
+ # TODO add writableDB, ReadablewritableDB
1749
+ # TODO add WriteBatch iniherited from writableDB
1750
+
1751
+
1752
+ cdef class IDB(object):
1753
+ # Cython requires to implement methods in the class definition
1754
+ # even for abstract classes.
1755
+
1756
+ # TODO move write option arguments to a separate class
1757
+ cpdef void put(self, bytes key, bytes value, cpp_bool sync = False, cpp_bool disable_wal = False, ColumnFamilyHandle column_family = None):
1758
+ pass
1759
+ cpdef void delete_single(self, bytes key, cpp_bool sync = False, cpp_bool disable_wal = False, ColumnFamilyHandle column_family = None):
1760
+ pass
1761
+ cpdef void delete_range(self, bytes begin_key, bytes end_key, cpp_bool sync = False, cpp_bool disable_wal = False, ColumnFamilyHandle column_family = None):
1762
+ pass
1763
+ cpdef void flush(self):
1764
+ pass
1765
+ cpdef void flush_wal(self, cpp_bool sync = False):
1766
+ pass
1767
+ cpdef void merge(self, bytes key, bytes value, cpp_bool sync = False, cpp_bool disable_wal = False, ColumnFamilyHandle column_family = None):
1768
+ pass
1769
+ cpdef void write(self, WriteBatch batch, cpp_bool sync = False, cpp_bool disable_wal = False):
1770
+ pass
1771
+ cpdef get(self, bytes key, ColumnFamilyHandle column_family = None):
1772
+ pass
1773
+ cpdef multi_get(self, keys):
1774
+ pass
1775
+ cpdef key_may_exist(self, bytes key, cpp_bool fetch = False, ColumnFamilyHandle column_family = None):
1776
+ pass
1777
+ cpdef Iterator iterkeys(self, ColumnFamilyHandle column_family = None):
1778
+ pass
1779
+ cpdef Iterator itervalues(self, ColumnFamilyHandle column_family = None):
1780
+ pass
1781
+ cpdef Iterator iteritems(self, ColumnFamilyHandle column_family = None):
1782
+ pass
1783
+
1784
+
1785
+ @cython.no_gc_clear
1786
+ cdef class DB(IDB):
1787
+ # pxd defines:
1788
+ # cdef Options opts
1789
+ # cdef db.DB* db
1790
+ # cdef list cf_handles
1791
+ # cdef list cf_options
1792
+
1793
+ def __cinit__(
1794
+ self,
1795
+ db_name,
1796
+ Options opts,
1797
+ dict column_families=None,
1798
+ read_only=False,
1799
+ secondary_path=None,
1800
+ txn_db_opts=None,
1801
+ ):
1802
+ cdef Status st
1803
+ cdef string db_path
1804
+ cdef string secondary_path_c
1805
+ cdef vector[db.ColumnFamilyDescriptor] column_family_descriptors
1806
+ cdef vector[db.ColumnFamilyHandle*] column_family_handles
1807
+ cdef bytes default_cf_name = db.kDefaultColumnFamilyName
1808
+ self.db = NULL
1809
+ self.opts = None
1810
+ self.cf_handles = []
1811
+ self.cf_options = []
1812
+
1813
+ # TransactionDB subclasses handle opening the underlying C++ handle
1814
+ # themselves. Skipping here prevents double initialisation that leads to
1815
+ # use-after-free during teardown.
1816
+ if isinstance(self, TransactionDB):
1817
+ return
1818
+
1819
+ # TransactionDB passes txn_db_opts so it can manage opening the C++
1820
+ # TransactionDB itself. When present we skip the vanilla DB open here
1821
+ # and let the subclass finish initialisation.
1822
+ if txn_db_opts is not None:
1823
+ return
1824
+
1825
+ if opts.in_use:
1826
+ raise Exception("Options object is already used by another DB")
1827
+
1828
+ db_path = path_to_string(db_name)
1829
+ if not column_families or default_cf_name not in column_families:
1830
+ # Always add the default column family
1831
+ column_family_descriptors.push_back(
1832
+ db.ColumnFamilyDescriptor(
1833
+ db.kDefaultColumnFamilyName,
1834
+ options.ColumnFamilyOptions(deref(opts.opts))
1835
+ )
1836
+ )
1837
+ self.cf_options.append(None) # Since they are the same as db
1838
+ if column_families:
1839
+ for cf_name, cf_options in column_families.items():
1840
+ if not isinstance(cf_name, bytes):
1841
+ raise TypeError(
1842
+ f"column family name {cf_name!r} is not of type {bytes}!"
1843
+ )
1844
+ if not isinstance(cf_options, ColumnFamilyOptions):
1845
+ raise TypeError(
1846
+ f"column family options {cf_options!r} is not of type "
1847
+ f"{ColumnFamilyOptions}!"
1848
+ )
1849
+ if (<ColumnFamilyOptions>cf_options).in_use:
1850
+ raise Exception(
1851
+ f"ColumnFamilyOptions object for {cf_name} is already "
1852
+ "used by another Column Family"
1853
+ )
1854
+ (<ColumnFamilyOptions>cf_options).in_use = True
1855
+ column_family_descriptors.push_back(
1856
+ db.ColumnFamilyDescriptor(
1857
+ cf_name,
1858
+ deref((<ColumnFamilyOptions>cf_options).copts)
1859
+ )
1860
+ )
1861
+ self.cf_options.append(cf_options)
1862
+
1863
+ if secondary_path is not None:
1864
+ assert read_only
1865
+ secondary_path_c = path_to_string(secondary_path)
1866
+ with nogil:
1867
+ st = db.DB_OpenAsSecondary_ColumnFamilies(
1868
+ deref(opts.opts),
1869
+ db_path,
1870
+ secondary_path_c,
1871
+ column_family_descriptors,
1872
+ &column_family_handles,
1873
+ &self.db)
1874
+ elif read_only:
1875
+ with nogil:
1876
+ st = db.DB_OpenForReadOnly_ColumnFamilies(
1877
+ deref(opts.opts),
1878
+ db_path,
1879
+ column_family_descriptors,
1880
+ &column_family_handles,
1881
+ &self.db,
1882
+ False)
1883
+ else:
1884
+ with nogil:
1885
+ st = db.DB_Open_ColumnFamilies(
1886
+ deref(opts.opts),
1887
+ db_path,
1888
+ column_family_descriptors,
1889
+ &column_family_handles,
1890
+ &self.db)
1891
+ check_status(st)
1892
+
1893
+ for handle in column_family_handles:
1894
+ wrapper = _ColumnFamilyHandle.from_handle_ptr(handle)
1895
+ self.cf_handles.append(wrapper)
1896
+
1897
+ # Inject the loggers into the python callbacks
1898
+ cdef shared_ptr[logger.Logger] info_log = self.db.GetOptions(
1899
+ self.db.DefaultColumnFamily()).info_log
1900
+ if opts.py_comparator is not None:
1901
+ opts.py_comparator.set_info_log(info_log)
1902
+
1903
+ if opts.py_table_factory is not None:
1904
+ opts.py_table_factory.set_info_log(info_log)
1905
+
1906
+ if opts.prefix_extractor is not None:
1907
+ opts.py_prefix_extractor.set_info_log(info_log)
1908
+
1909
+ cdef ColumnFamilyOptions copts
1910
+ for idx, copts in enumerate(self.cf_options):
1911
+ if not copts:
1912
+ continue
1913
+
1914
+ info_log = self.db.GetOptions(column_family_handles[idx]).info_log
1915
+
1916
+ if copts.py_comparator is not None:
1917
+ copts.py_comparator.set_info_log(info_log)
1918
+
1919
+ if copts.py_table_factory is not None:
1920
+ copts.py_table_factory.set_info_log(info_log)
1921
+
1922
+ if copts.prefix_extractor is not None:
1923
+ copts.py_prefix_extractor.set_info_log(info_log)
1924
+
1925
+ self.opts = opts
1926
+ self.opts.in_use = True
1927
+
1928
+ def __dealloc__(self):
1929
+ self.close()
1930
+
1931
+ cpdef void close(self):
1932
+ cdef ColumnFamilyOptions copts
1933
+ if self.db != NULL:
1934
+ # We have to make sure we delete the handles so rocksdb doesn't
1935
+ # assert when we delete the db
1936
+ self.cf_handles.clear()
1937
+ for copts in self.cf_options:
1938
+ if copts:
1939
+ copts.in_use = False
1940
+ self.cf_options.clear()
1941
+
1942
+ with nogil:
1943
+ del self.db
1944
+
1945
+ if self.opts is not None:
1946
+ self.opts.in_use = False
1947
+
1948
+ @property
1949
+ def column_families(self):
1950
+ return [handle.weakref for handle in self.cf_handles]
1951
+
1952
+ cpdef get_column_family(self, bytes name):
1953
+ for handle in self.cf_handles:
1954
+ if handle.name == name:
1955
+ return handle.weakref
1956
+
1957
+ cpdef void put(self, bytes key, bytes value, cpp_bool sync = False, cpp_bool disable_wal = False, ColumnFamilyHandle column_family = None):
1958
+ cdef Status st
1959
+ cdef options.WriteOptions opts
1960
+ opts.sync = sync
1961
+ opts.disableWAL = disable_wal
1962
+
1963
+ cdef Slice c_key = bytes_to_slice(key)
1964
+ cdef Slice c_value = bytes_to_slice(value)
1965
+ cdef db.ColumnFamilyHandle* cf_handle = self.db.DefaultColumnFamily()
1966
+
1967
+ if column_family is not None:
1968
+ cf_handle = column_family.get_handle()
1969
+
1970
+ with nogil:
1971
+ st = self.db.Put(opts, cf_handle, c_key, c_value)
1972
+ check_status(st)
1973
+
1974
+ def delete(self, bytes key, cpp_bool sync = False, cpp_bool disable_wal = False, ColumnFamilyHandle column_family = None):
1975
+ self.delete_single(key, sync=sync, disable_wal=disable_wal, column_family=column_family)
1976
+
1977
+ cpdef void delete_single(self, bytes key, cpp_bool sync = False, cpp_bool disable_wal = False, ColumnFamilyHandle column_family = None):
1978
+ cdef Status st
1979
+ cdef options.WriteOptions opts
1980
+ opts.sync = sync
1981
+ opts.disableWAL = disable_wal
1982
+
1983
+ cdef Slice c_key = bytes_to_slice(key)
1984
+ cdef db.ColumnFamilyHandle* cf_handle = self.db.DefaultColumnFamily()
1985
+
1986
+ if column_family is not None:
1987
+ cf_handle = column_family.get_handle()
1988
+
1989
+ with nogil:
1990
+ st = self.db.Delete(opts, cf_handle, c_key)
1991
+ check_status(st)
1992
+
1993
+ cpdef void delete_range(self, bytes begin_key, bytes end_key, cpp_bool sync = False, cpp_bool disable_wal = False, ColumnFamilyHandle column_family = None):
1994
+ cdef Status st
1995
+ cdef options.WriteOptions opts
1996
+ opts.sync = sync
1997
+ opts.disableWAL = disable_wal
1998
+
1999
+ cdef Slice c_begin_key = bytes_to_slice(begin_key)
2000
+ cdef Slice c_end_key = bytes_to_slice(end_key)
2001
+
2002
+ cdef db.ColumnFamilyHandle* cf_handle = self.db.DefaultColumnFamily()
2003
+ if column_family is not None:
2004
+ cf_handle = column_family.get_handle()
2005
+
2006
+ with nogil:
2007
+ st = self.db.DeleteRange(opts, cf_handle, c_begin_key, c_end_key)
2008
+ check_status(st)
2009
+
2010
+ cpdef void flush(self):
2011
+ cdef Status st
2012
+ cdef FlushOptions options
2013
+ cdef db.ColumnFamilyHandle* cf_handle = self.db.DefaultColumnFamily()
2014
+
2015
+ with nogil:
2016
+ st = self.db.Flush(options, cf_handle)
2017
+ check_status(st)
2018
+
2019
+ cpdef void flush_wal(self, cpp_bool sync = False):
2020
+ cdef Status st
2021
+ cdef cpp_bool c_sync = sync
2022
+ with nogil:
2023
+ st = self.db.FlushWAL(c_sync)
2024
+ check_status(st)
2025
+
2026
+ cpdef void merge(self, bytes key, bytes value, cpp_bool sync = False, cpp_bool disable_wal = False, ColumnFamilyHandle column_family = None):
2027
+ cdef Status st
2028
+ cdef options.WriteOptions opts
2029
+ opts.sync = sync
2030
+ opts.disableWAL = disable_wal
2031
+
2032
+ cdef Slice c_key = bytes_to_slice(key)
2033
+ cdef Slice c_value = bytes_to_slice(value)
2034
+ cdef db.ColumnFamilyHandle* cf_handle = self.db.DefaultColumnFamily()
2035
+ if column_family is not None:
2036
+ cf_handle = column_family.get_handle()
2037
+
2038
+ with nogil:
2039
+ st = self.db.Merge(opts, cf_handle, c_key, c_value)
2040
+ check_status(st)
2041
+
2042
+ cpdef void write(self, WriteBatch batch, cpp_bool sync = False, cpp_bool disable_wal = False):
2043
+ cdef Status st
2044
+ cdef options.WriteOptions opts
2045
+ opts.sync = sync
2046
+ opts.disableWAL = disable_wal
2047
+
2048
+ with nogil:
2049
+ st = self.db.Write(opts, batch.batch)
2050
+ check_status(st)
2051
+
2052
+ cpdef get(self, bytes key, ColumnFamilyHandle column_family = None):
2053
+ cdef string res
2054
+ cdef Status st
2055
+ cdef options.ReadOptions opts
2056
+
2057
+ cdef Slice c_key = bytes_to_slice(key)
2058
+ cdef db.ColumnFamilyHandle* cf_handle = self.db.DefaultColumnFamily()
2059
+ if column_family is not None:
2060
+ cf_handle = column_family.get_handle()
2061
+
2062
+ with nogil:
2063
+ st = self.db.Get(opts, cf_handle, c_key, cython.address(res))
2064
+
2065
+ if st.ok():
2066
+ return string_to_bytes(res)
2067
+ elif st.IsNotFound():
2068
+ return None
2069
+ else:
2070
+ check_status(st)
2071
+
2072
+ cpdef multi_get(self, keys):
2073
+ cdef vector[string] values
2074
+ values.resize(len(keys))
2075
+
2076
+ cdef db.ColumnFamilyHandle* cf_handle
2077
+ cdef vector[db.ColumnFamilyHandle*] cf_handles
2078
+ cdef vector[Slice] c_keys
2079
+ for key in keys:
2080
+ if isinstance(key, tuple):
2081
+ py_handle, key = key
2082
+ cf_handle = (<ColumnFamilyHandle?>py_handle).get_handle()
2083
+ else:
2084
+ cf_handle = self.db.DefaultColumnFamily()
2085
+ c_keys.push_back(bytes_to_slice(key))
2086
+ cf_handles.push_back(cf_handle)
2087
+
2088
+ cdef options.ReadOptions opts
2089
+
2090
+ cdef vector[Status] res
2091
+ with nogil:
2092
+ res = self.db.MultiGet(
2093
+ opts,
2094
+ cf_handles,
2095
+ c_keys,
2096
+ cython.address(values))
2097
+
2098
+ cdef dict ret_dict = {}
2099
+ for index in range(len(keys)):
2100
+ if res[index].ok():
2101
+ ret_dict[keys[index]] = string_to_bytes(values[index])
2102
+ elif res[index].IsNotFound():
2103
+ ret_dict[keys[index]] = None
2104
+ else:
2105
+ check_status(res[index])
2106
+
2107
+ return ret_dict
2108
+
2109
+ cpdef key_may_exist(self, bytes key, cpp_bool fetch = False, ColumnFamilyHandle column_family = None):
2110
+ cdef string value
2111
+ cdef cpp_bool value_found
2112
+ cdef cpp_bool exists
2113
+ cdef options.ReadOptions opts
2114
+ cdef Slice c_key
2115
+ cdef db.ColumnFamilyHandle* cf_handle = self.db.DefaultColumnFamily()
2116
+
2117
+ if column_family is not None:
2118
+ cf_handle = column_family.get_handle()
2119
+
2120
+ c_key = bytes_to_slice(key)
2121
+ exists = False
2122
+
2123
+ if fetch:
2124
+ value_found = False
2125
+ with nogil:
2126
+ exists = self.db.KeyMayExist(
2127
+ opts,
2128
+ cf_handle,
2129
+ c_key,
2130
+ cython.address(value),
2131
+ cython.address(value_found))
2132
+
2133
+ if exists:
2134
+ if value_found:
2135
+ return (True, string_to_bytes(value))
2136
+ else:
2137
+ return (True, None)
2138
+ else:
2139
+ return (False, None)
2140
+ else:
2141
+ with nogil:
2142
+ exists = self.db.KeyMayExist(
2143
+ opts,
2144
+ cf_handle,
2145
+ c_key,
2146
+ cython.address(value))
2147
+
2148
+ return (exists, None)
2149
+
2150
+ cpdef Iterator iterkeys(self, ColumnFamilyHandle column_family = None):
2151
+ cdef options.ReadOptions opts
2152
+ cdef KeysIterator it
2153
+ cdef db.ColumnFamilyHandle* cf_handle = self.db.DefaultColumnFamily()
2154
+ if column_family is not None:
2155
+ cf_handle = column_family.get_handle()
2156
+
2157
+ it = KeysIterator(self)
2158
+ it.owner = self
2159
+
2160
+ with nogil:
2161
+ it.ptr = self.db.NewIterator(opts, cf_handle)
2162
+ return it
2163
+
2164
+ cpdef Iterator itervalues(self, ColumnFamilyHandle column_family = None):
2165
+ cdef options.ReadOptions opts
2166
+ cdef ValuesIterator it
2167
+ cdef db.ColumnFamilyHandle* cf_handle = self.db.DefaultColumnFamily()
2168
+ if column_family is not None:
2169
+ cf_handle = column_family.get_handle()
2170
+
2171
+ it = ValuesIterator(self)
2172
+ it.owner = self
2173
+
2174
+ with nogil:
2175
+ it.ptr = self.db.NewIterator(opts, cf_handle)
2176
+ return it
2177
+
2178
+ cpdef Iterator iteritems(self, ColumnFamilyHandle column_family = None):
2179
+ cdef options.ReadOptions opts
2180
+ cdef ItemsIterator it
2181
+ cdef db.ColumnFamilyHandle* cf_handle = self.db.DefaultColumnFamily()
2182
+ if column_family is not None:
2183
+ cf_handle = column_family.get_handle()
2184
+
2185
+ it = ItemsIterator.__new__(ItemsIterator)
2186
+ it.db = self
2187
+ it.owner = self
2188
+
2189
+ with nogil:
2190
+ it.ptr = self.db.NewIterator(opts, cf_handle)
2191
+ return it
2192
+
2193
+ cpdef iterskeys(self, column_families):
2194
+ cdef vector[db.Iterator*] iters
2195
+ iters.resize(len(column_families))
2196
+ cdef options.ReadOptions opts
2197
+ cdef db.Iterator* it_ptr
2198
+ cdef KeysIterator it
2199
+ cdef db.ColumnFamilyHandle* cf_handle
2200
+ cdef vector[db.ColumnFamilyHandle*] cf_handles
2201
+
2202
+ for column_family in column_families:
2203
+ cf_handle = (<ColumnFamilyHandle?>column_family).get_handle()
2204
+ cf_handles.push_back(cf_handle)
2205
+
2206
+ with nogil:
2207
+ self.db.NewIterators(opts, cf_handles, &iters)
2208
+
2209
+ cf_iter = iter(column_families)
2210
+ cdef list ret = []
2211
+ for it_ptr in iters:
2212
+ it = KeysIterator(self, next(cf_iter))
2213
+ it.ptr = it_ptr
2214
+ it.owner = self
2215
+ ret.append(it)
2216
+ return ret
2217
+
2218
+ cpdef itersvalues(self, column_families):
2219
+ cdef vector[db.Iterator*] iters
2220
+ iters.resize(len(column_families))
2221
+ cdef options.ReadOptions opts
2222
+ cdef db.Iterator* it_ptr
2223
+ cdef ValuesIterator it
2224
+ cdef db.ColumnFamilyHandle* cf_handle
2225
+ cdef vector[db.ColumnFamilyHandle*] cf_handles
2226
+
2227
+ for column_family in column_families:
2228
+ cf_handle = (<ColumnFamilyHandle?>column_family).get_handle()
2229
+ cf_handles.push_back(cf_handle)
2230
+
2231
+ with nogil:
2232
+ self.db.NewIterators(opts, cf_handles, &iters)
2233
+
2234
+ cdef list ret = []
2235
+ for it_ptr in iters:
2236
+ it = ValuesIterator(self)
2237
+ it.ptr = it_ptr
2238
+ it.owner = self
2239
+ ret.append(it)
2240
+ return ret
2241
+
2242
+ cpdef itersitems(self, column_families):
2243
+ cdef vector[db.Iterator*] iters
2244
+ iters.resize(len(column_families))
2245
+ cdef options.ReadOptions opts
2246
+ cdef db.Iterator* it_ptr
2247
+ cdef ItemsIterator it
2248
+ cdef db.ColumnFamilyHandle* cf_handle
2249
+ cdef vector[db.ColumnFamilyHandle*] cf_handles
2250
+
2251
+ for column_family in column_families:
2252
+ cf_handle = (<ColumnFamilyHandle?>column_family).get_handle()
2253
+ cf_handles.push_back(cf_handle)
2254
+
2255
+ with nogil:
2256
+ self.db.NewIterators(opts, cf_handles, &iters)
2257
+
2258
+
2259
+ cf_iter = iter(column_families)
2260
+ cdef list ret = []
2261
+ for it_ptr in iters:
2262
+ it = ItemsIterator(self, next(cf_iter))
2263
+ it.ptr = it_ptr
2264
+ it.owner = self
2265
+ ret.append(it)
2266
+ return ret
2267
+
2268
+ cpdef snapshot(self):
2269
+ return Snapshot(self)
2270
+
2271
+ cpdef get_property(self, prop, ColumnFamilyHandle column_family = None):
2272
+ cdef string value
2273
+ cdef Slice c_prop = bytes_to_slice(prop)
2274
+ cdef cpp_bool ret = False
2275
+ cdef db.ColumnFamilyHandle* cf_handle = self.db.DefaultColumnFamily()
2276
+ if column_family:
2277
+ cf_handle = column_family.get_handle()
2278
+
2279
+ with nogil:
2280
+ ret = self.db.GetProperty(cf_handle, c_prop, cython.address(value))
2281
+
2282
+ if ret:
2283
+ return string_to_bytes(value)
2284
+ else:
2285
+ return None
2286
+
2287
+ cpdef try_catch_up_with_primary(self):
2288
+ self.db.TryCatchUpWithPrimary()
2289
+
2290
+ cpdef get_live_files_metadata(self):
2291
+ cdef vector[db.LiveFileMetaData] metadata
2292
+
2293
+ with nogil:
2294
+ self.db.GetLiveFilesMetaData(cython.address(metadata))
2295
+
2296
+ ret = []
2297
+ for ob in metadata:
2298
+ t = {}
2299
+ t['name'] = string_to_path(ob.name)
2300
+ t['level'] = ob.level
2301
+ t['size'] = ob.size
2302
+ t['smallestkey'] = string_to_bytes(ob.smallestkey)
2303
+ t['largestkey'] = string_to_bytes(ob.largestkey)
2304
+ t['smallest_seqno'] = ob.smallest_seqno
2305
+ t['largest_seqno'] = ob.largest_seqno
2306
+
2307
+ ret.append(t)
2308
+
2309
+ return ret
2310
+
2311
+ # TODO replace arguments with a single CompactRangeOptions cython wrapper
2312
+ cpdef void compact_range(self,
2313
+ bytes begin=None,
2314
+ bytes end=None,
2315
+ cpp_bool change_level = False,
2316
+ int target_level = -1,
2317
+ str bottommost_level_compaction = 'if_compaction_filter',
2318
+ ColumnFamilyHandle column_family = None
2319
+ ):
2320
+ cdef options.CompactRangeOptions c_options
2321
+
2322
+ c_options.change_level = change_level
2323
+ c_options.target_level = target_level
2324
+
2325
+ blc = bottommost_level_compaction
2326
+ if blc == 'skip':
2327
+ c_options.bottommost_level_compaction = options.blc_skip
2328
+ elif blc == 'if_compaction_filter':
2329
+ c_options.bottommost_level_compaction = options.blc_is_filter
2330
+ elif blc == 'force':
2331
+ c_options.bottommost_level_compaction = options.blc_force
2332
+ else:
2333
+ raise ValueError("bottommost_level_compaction is not valid")
2334
+
2335
+ cdef Status st
2336
+ cdef Slice begin_val
2337
+ cdef Slice end_val
2338
+
2339
+ cdef Slice* begin_ptr
2340
+ cdef Slice* end_ptr
2341
+
2342
+ begin_ptr = NULL
2343
+ end_ptr = NULL
2344
+
2345
+ if begin is not None:
2346
+ begin_val = bytes_to_slice(begin)
2347
+ begin_ptr = cython.address(begin_val)
2348
+
2349
+ if end is not None:
2350
+ end_val = bytes_to_slice(end)
2351
+ end_ptr = cython.address(end_val)
2352
+
2353
+ cdef db.ColumnFamilyHandle* cf_handle = self.db.DefaultColumnFamily()
2354
+ if column_family is not None:
2355
+ cf_handle = column_family.get_handle()
2356
+
2357
+ st = self.db.CompactRange(c_options, cf_handle, begin_ptr, end_ptr)
2358
+ check_status(st)
2359
+
2360
+ @staticmethod
2361
+ def __parse_read_opts(
2362
+ verify_checksums=False,
2363
+ fill_cache=True,
2364
+ snapshot=None,
2365
+ read_tier="all"):
2366
+
2367
+ # TODO: Is this really effiencet ?
2368
+ return locals()
2369
+
2370
+ cdef options.ReadOptions build_read_opts(self, dict py_opts):
2371
+ cdef options.ReadOptions opts
2372
+ opts.verify_checksums = py_opts['verify_checksums']
2373
+ opts.fill_cache = py_opts['fill_cache']
2374
+ if py_opts['snapshot'] is not None:
2375
+ opts.snapshot = (<Snapshot?>(py_opts['snapshot'])).ptr
2376
+
2377
+ if py_opts['read_tier'] == "all":
2378
+ opts.read_tier = options.kReadAllTier
2379
+ elif py_opts['read_tier'] == 'cache':
2380
+ opts.read_tier = options.kBlockCacheTier
2381
+ else:
2382
+ raise ValueError("Invalid read_tier")
2383
+
2384
+ return opts
2385
+
2386
+ property options:
2387
+ def __get__(self):
2388
+ return self.opts
2389
+
2390
+ cpdef create_column_family(self, bytes name, ColumnFamilyOptions copts):
2391
+ cdef db.ColumnFamilyHandle* cf_handle
2392
+ cdef Status st
2393
+ cdef string c_name = name
2394
+
2395
+ for handle in self.cf_handles:
2396
+ if handle.name == name:
2397
+ raise ValueError(f"{name} is already an existing column family")
2398
+
2399
+ if copts.in_use:
2400
+ raise Exception("ColumnFamilyOptions are in_use by another column family")
2401
+
2402
+ copts.in_use = True
2403
+ with nogil:
2404
+ st = self.db.CreateColumnFamily(deref(copts.copts), c_name, &cf_handle)
2405
+ check_status(st)
2406
+
2407
+ handle = _ColumnFamilyHandle.from_handle_ptr(cf_handle)
2408
+
2409
+ self.cf_handles.append(handle)
2410
+ self.cf_options.append(copts)
2411
+ return handle.weakref
2412
+
2413
+ cpdef void drop_column_family(self, ColumnFamilyHandle weak_handle):
2414
+ cdef db.ColumnFamilyHandle* cf_handle
2415
+ cdef ColumnFamilyOptions copts
2416
+ cdef Status st
2417
+
2418
+ cf_handle = weak_handle.get_handle()
2419
+
2420
+ with nogil:
2421
+ st = self.db.DropColumnFamily(cf_handle)
2422
+ check_status(st)
2423
+
2424
+ py_handle = weak_handle._ref()
2425
+ index = self.cf_handles.index(py_handle)
2426
+ copts = self.cf_options.pop(index)
2427
+ del self.cf_handles[index]
2428
+ del py_handle
2429
+ if copts:
2430
+ copts.in_use = False
2431
+
2432
+
2433
+ @cython.no_gc_clear
2434
+ cdef class TransactionDB(DB):
2435
+ def __cinit__(
2436
+ self,
2437
+ db_name,
2438
+ Options opts,
2439
+ TransactionDBOptions txn_db_opts = None,
2440
+ dict column_families = None):
2441
+ cdef Status st
2442
+ cdef string db_path
2443
+ cdef vector[db.ColumnFamilyDescriptor] column_family_descriptors
2444
+ cdef vector[db.ColumnFamilyHandle*] column_family_handles
2445
+ cdef bytes default_cf_name = db.kDefaultColumnFamilyName
2446
+
2447
+ self.txn_db = NULL
2448
+ self.txn_opts = None
2449
+ self.db = NULL
2450
+ self.opts = None
2451
+ self.cf_handles = []
2452
+ self.cf_options = []
2453
+
2454
+ if opts.in_use:
2455
+ raise Exception("Options object is already used by another DB")
2456
+
2457
+ if txn_db_opts is None:
2458
+ txn_db_opts = TransactionDBOptions()
2459
+
2460
+ if txn_db_opts.in_use:
2461
+ raise Exception("TransactionDBOptions object is already used by another DB")
2462
+
2463
+ db_path = path_to_string(db_name)
2464
+
2465
+ if not column_families or default_cf_name not in column_families:
2466
+ column_family_descriptors.push_back(
2467
+ db.ColumnFamilyDescriptor(
2468
+ db.kDefaultColumnFamilyName,
2469
+ options.ColumnFamilyOptions(deref(opts.opts))
2470
+ )
2471
+ )
2472
+ self.cf_options.append(None)
2473
+
2474
+ if column_families:
2475
+ for cf_name, cf_options in column_families.items():
2476
+ if not isinstance(cf_name, bytes):
2477
+ raise TypeError(
2478
+ f"column family name {cf_name!r} is not of type {bytes}!"
2479
+ )
2480
+ if not isinstance(cf_options, ColumnFamilyOptions):
2481
+ raise TypeError(
2482
+ f"column family options {cf_options!r} is not of type "
2483
+ f"{ColumnFamilyOptions}!"
2484
+ )
2485
+ if (<ColumnFamilyOptions>cf_options).in_use:
2486
+ raise Exception(
2487
+ f"ColumnFamilyOptions object for {cf_name} is already "
2488
+ "used by another Column Family"
2489
+ )
2490
+ (<ColumnFamilyOptions>cf_options).in_use = True
2491
+ column_family_descriptors.push_back(
2492
+ db.ColumnFamilyDescriptor(
2493
+ cf_name,
2494
+ deref((<ColumnFamilyOptions>cf_options).copts)
2495
+ )
2496
+ )
2497
+ self.cf_options.append(cf_options)
2498
+
2499
+ with nogil:
2500
+ st = transaction.TransactionDB_Open_CF(
2501
+ <options.DBOptions&>deref(opts.opts),
2502
+ deref(txn_db_opts.opts),
2503
+ db_path,
2504
+ column_family_descriptors,
2505
+ &column_family_handles,
2506
+ &self.txn_db)
2507
+
2508
+ check_status(st)
2509
+
2510
+ for handle in column_family_handles:
2511
+ wrapper = _ColumnFamilyHandle.from_handle_ptr(handle)
2512
+ self.cf_handles.append(wrapper)
2513
+
2514
+ self.db = <db.DB*>self.txn_db
2515
+
2516
+ cdef shared_ptr[logger.Logger] info_log = self.db.GetOptions(
2517
+ self.db.DefaultColumnFamily()).info_log
2518
+ if opts.py_comparator is not None:
2519
+ opts.py_comparator.set_info_log(info_log)
2520
+
2521
+ if opts.py_table_factory is not None:
2522
+ opts.py_table_factory.set_info_log(info_log)
2523
+
2524
+ if opts.prefix_extractor is not None:
2525
+ opts.py_prefix_extractor.set_info_log(info_log)
2526
+
2527
+ cdef ColumnFamilyOptions copts
2528
+ for idx, copts in enumerate(self.cf_options):
2529
+ if not copts:
2530
+ continue
2531
+
2532
+ info_log = self.db.GetOptions(column_family_handles[idx]).info_log
2533
+
2534
+ if copts.py_comparator is not None:
2535
+ copts.py_comparator.set_info_log(info_log)
2536
+
2537
+ if copts.py_table_factory is not None:
2538
+ copts.py_table_factory.set_info_log(info_log)
2539
+
2540
+ if copts.py_prefix_extractor is not None:
2541
+ copts.py_prefix_extractor.set_info_log(info_log)
2542
+
2543
+ self.opts = opts
2544
+ self.opts.in_use = True
2545
+ self.txn_opts = txn_db_opts
2546
+ self.txn_opts.in_use = True
2547
+
2548
+ cpdef begin_transaction(
2549
+ self,
2550
+ TransactionOptions txn_options = None,
2551
+ dict write_options = None,
2552
+ Transaction reuse = None):
2553
+
2554
+ if self.txn_db == NULL:
2555
+ raise RuntimeError("TransactionDB is closed")
2556
+
2557
+ cdef options.WriteOptions wopts
2558
+ wopts.sync = False
2559
+ wopts.disableWAL = False
2560
+
2561
+ if write_options is not None:
2562
+ if 'sync' in write_options:
2563
+ wopts.sync = write_options['sync']
2564
+ if 'disable_wal' in write_options:
2565
+ wopts.disableWAL = write_options['disable_wal']
2566
+ if 'disableWAL' in write_options:
2567
+ wopts.disableWAL = write_options['disableWAL']
2568
+
2569
+ cdef transaction.TransactionOptions stack_opts = transaction.TransactionOptions()
2570
+ cdef transaction.TransactionOptions* txn_opts_ptr
2571
+
2572
+ if txn_options is not None:
2573
+ if txn_options.opts == NULL:
2574
+ raise ValueError("TransactionOptions is invalid")
2575
+ txn_opts_ptr = txn_options.opts
2576
+ else:
2577
+ txn_opts_ptr = cython.address(stack_opts)
2578
+
2579
+ cdef transaction.Transaction* raw_txn = NULL
2580
+
2581
+ if reuse is not None:
2582
+ if reuse.txn == NULL:
2583
+ raise ValueError("Reuse transaction handle is closed")
2584
+ if reuse.db is not self:
2585
+ raise ValueError("Reuse transaction belongs to a different TransactionDB")
2586
+ raw_txn = self.txn_db.BeginTransaction(
2587
+ wopts,
2588
+ deref(txn_opts_ptr),
2589
+ reuse.txn)
2590
+ if raw_txn == NULL:
2591
+ raise RuntimeError("BeginTransaction returned NULL")
2592
+ reuse.txn = raw_txn
2593
+ reuse.db = self
2594
+ reuse.closed = False
2595
+ return reuse
2596
+
2597
+ raw_txn = self.txn_db.BeginTransaction(
2598
+ wopts,
2599
+ deref(txn_opts_ptr))
2600
+
2601
+ if raw_txn == NULL:
2602
+ raise RuntimeError("BeginTransaction returned NULL")
2603
+
2604
+ cdef Transaction txn = Transaction.__new__(Transaction)
2605
+ txn.txn = raw_txn
2606
+ txn.db = self
2607
+ txn.closed = False
2608
+ return txn
2609
+
2610
+ def __dealloc__(self):
2611
+ # CRITICAL: NO RETURN STATEMENT
2612
+ self.close()
2613
+
2614
+ cpdef void close(self):
2615
+ # Clear handles first
2616
+ self.cf_handles.clear()
2617
+
2618
+ # Delete the C++ object ONCE
2619
+ if self.txn_db != NULL:
2620
+ with nogil:
2621
+ del self.txn_db
2622
+ self.txn_db = NULL
2623
+
2624
+ # CRITICAL: Set aliased pointer to NULL
2625
+ # This prevents parent from trying to delete
2626
+ self.db = NULL
2627
+
2628
+ # Release options
2629
+ if self.opts is not None:
2630
+ self.opts.in_use = False
2631
+
2632
+ @cython.no_gc_clear
2633
+ cdef class Transaction(object):
2634
+ # pxd should define:
2635
+ # cdef transaction.Transaction* txn
2636
+ # cdef object db # Reference to TransactionDB
2637
+ # cdef cpp_bool closed
2638
+
2639
+ def __cinit__(self):
2640
+ self.txn = NULL
2641
+ self.db = None
2642
+ self.closed = True
2643
+
2644
+ def __dealloc__(self):
2645
+ # If still open, rollback. RocksDB handles deletion after rollback.
2646
+ if self.txn != NULL and not self.closed:
2647
+ self.txn.Rollback()
2648
+ self.txn = NULL
2649
+
2650
+ cpdef void close(self):
2651
+ """Close the transaction by rolling it back."""
2652
+ if not self.closed:
2653
+ self.rollback()
2654
+ self.closed = True
2655
+
2656
+ cdef void _ensure_open(self):
2657
+ if self.txn == NULL or self.closed:
2658
+ raise RuntimeError("Transaction is closed")
2659
+ if self.db is None or (<TransactionDB>self.db).txn_db == NULL:
2660
+ raise RuntimeError("Parent TransactionDB is closed")
2661
+
2662
+ cpdef void commit(self):
2663
+ self._ensure_open()
2664
+ cdef Status st
2665
+ st = self.txn.Commit()
2666
+ self.closed = True
2667
+ self.txn = NULL # Pointer invalid after commit
2668
+ check_status(st)
2669
+
2670
+ cpdef void rollback(self):
2671
+ if self.closed:
2672
+ return # Idempotent
2673
+ if self.txn == NULL:
2674
+ return
2675
+ cdef Status st
2676
+ st = self.txn.Rollback()
2677
+ self.closed = True
2678
+ self.txn = NULL # Pointer invalid after rollback
2679
+ check_status(st)
2680
+
2681
+ cpdef void prepare(self):
2682
+ self._ensure_open()
2683
+ cdef Status st
2684
+ st = self.txn.Prepare()
2685
+ check_status(st)
2686
+
2687
+ cpdef void set_snapshot(self):
2688
+ self._ensure_open()
2689
+ self.txn.SetSnapshot()
2690
+
2691
+ cpdef void clear_snapshot(self):
2692
+ self._ensure_open()
2693
+ self.txn.ClearSnapshot()
2694
+
2695
+ cpdef snapshot(self):
2696
+ self._ensure_open()
2697
+ cdef const snapshot.Snapshot* ptr = self.txn.GetSnapshot()
2698
+ if ptr == NULL:
2699
+ return None
2700
+ return TransactionSnapshot(self, <size_t><void*>ptr)
2701
+
2702
+ cpdef void save_point(self):
2703
+ self._ensure_open()
2704
+ self.txn.SetSavePoint()
2705
+
2706
+ cpdef void rollback_to_save_point(self):
2707
+ self._ensure_open()
2708
+ cdef Status st
2709
+ st = self.txn.RollbackToSavePoint()
2710
+ check_status(st)
2711
+
2712
+ cpdef void pop_save_point(self):
2713
+ self._ensure_open()
2714
+ cdef Status st
2715
+ st = self.txn.PopSavePoint()
2716
+ check_status(st)
2717
+
2718
+ cpdef set_name(self, name):
2719
+ self._ensure_open()
2720
+ cdef string c_name
2721
+ if isinstance(name, bytes):
2722
+ c_name = bytes_to_string(name)
2723
+ elif isinstance(name, str):
2724
+ c_name = bytes_to_string(name.encode('utf-8'))
2725
+ else:
2726
+ raise TypeError("Transaction name must be bytes or str")
2727
+
2728
+ cdef Status st
2729
+ st = self.txn.SetName(c_name)
2730
+ check_status(st)
2731
+
2732
+ cpdef get_name(self):
2733
+ self._ensure_open()
2734
+ cdef string name = self.txn.GetName()
2735
+ return string_to_bytes(name).decode('utf-8', 'ignore')
2736
+
2737
+ cpdef get_id(self):
2738
+ self._ensure_open()
2739
+ return self.txn.GetID()
2740
+
2741
+ cpdef void put(self, bytes key, bytes value, ColumnFamilyHandle column_family = None, cpp_bool assume_tracked = False):
2742
+ self._ensure_open()
2743
+ cdef Status st
2744
+ cdef Slice c_key = bytes_to_slice(key)
2745
+ cdef Slice c_value = bytes_to_slice(value)
2746
+ cdef db.ColumnFamilyHandle* cf_handle
2747
+ if column_family is not None:
2748
+ cf_handle = column_family.get_handle()
2749
+ st = self.txn.Put(cf_handle, c_key, c_value, assume_tracked)
2750
+ else:
2751
+ st = self.txn.Put(c_key, c_value)
2752
+ check_status(st)
2753
+
2754
+ cpdef void merge(self, bytes key, bytes value, ColumnFamilyHandle column_family = None, cpp_bool assume_tracked = False):
2755
+ self._ensure_open()
2756
+ cdef Status st
2757
+ cdef Slice c_key = bytes_to_slice(key)
2758
+ cdef Slice c_value = bytes_to_slice(value)
2759
+ cdef db.ColumnFamilyHandle* cf_handle
2760
+ if column_family is not None:
2761
+ cf_handle = column_family.get_handle()
2762
+ st = self.txn.Merge(cf_handle, c_key, c_value, assume_tracked)
2763
+ else:
2764
+ st = self.txn.Merge(c_key, c_value)
2765
+ check_status(st)
2766
+
2767
+ cpdef void delete_single(self, bytes key, ColumnFamilyHandle column_family = None, cpp_bool assume_tracked = False):
2768
+ self._ensure_open()
2769
+ cdef Status st
2770
+ cdef Slice c_key = bytes_to_slice(key)
2771
+ cdef db.ColumnFamilyHandle* cf_handle
2772
+ if column_family is not None:
2773
+ cf_handle = column_family.get_handle()
2774
+ st = self.txn.Delete(cf_handle, c_key, assume_tracked)
2775
+ else:
2776
+ st = self.txn.Delete(c_key)
2777
+ check_status(st)
2778
+
2779
+ cpdef get(self, bytes key, ColumnFamilyHandle column_family = None):
2780
+ self._ensure_open()
2781
+ cdef Status st
2782
+ cdef string res
2783
+ cdef options.ReadOptions opts
2784
+ cdef db.ColumnFamilyHandle* cf_handle = self.db.db.DefaultColumnFamily()
2785
+ if column_family is not None:
2786
+ cf_handle = column_family.get_handle()
2787
+ st = self.txn.Get(opts, cf_handle, bytes_to_slice(key), cython.address(res))
2788
+
2789
+ if st.ok():
2790
+ return string_to_bytes(res)
2791
+ elif st.IsNotFound():
2792
+ return None
2793
+ else:
2794
+ check_status(st)
2795
+
2796
+ cpdef multi_get(self, keys):
2797
+ self._ensure_open()
2798
+ cdef vector[string] values
2799
+ values.resize(len(keys))
2800
+
2801
+ cdef db.ColumnFamilyHandle* cf_handle
2802
+ cdef vector[db.ColumnFamilyHandle*] cf_handles
2803
+ cdef vector[Slice] c_keys
2804
+ for key in keys:
2805
+ if isinstance(key, tuple):
2806
+ py_handle, key = key
2807
+ cf_handle = (<ColumnFamilyHandle?>py_handle).get_handle()
2808
+ else:
2809
+ cf_handle = self.db.db.DefaultColumnFamily()
2810
+ c_keys.push_back(bytes_to_slice(key))
2811
+ cf_handles.push_back(cf_handle)
2812
+
2813
+ cdef options.ReadOptions opts
2814
+ cdef vector[Status] res
2815
+
2816
+ res = self.txn.MultiGet(
2817
+ opts,
2818
+ cf_handles,
2819
+ c_keys,
2820
+ cython.address(values))
2821
+
2822
+ cdef dict ret_dict = {}
2823
+ for index in range(len(keys)):
2824
+ if res[index].ok():
2825
+ ret_dict[keys[index]] = string_to_bytes(values[index])
2826
+ elif res[index].IsNotFound():
2827
+ ret_dict[keys[index]] = None
2828
+ else:
2829
+ check_status(res[index])
2830
+ return ret_dict
2831
+
2832
+ cpdef Iterator iterkeys(self, ColumnFamilyHandle column_family = None):
2833
+ self._ensure_open()
2834
+ cdef options.ReadOptions opts
2835
+ cdef db.ColumnFamilyHandle* cf_handle
2836
+ cdef iterator.Iterator* it_ptr
2837
+ cdef transaction.Transaction* txn_ptr = self.txn
2838
+ cdef KeysIterator it
2839
+
2840
+ if column_family is not None:
2841
+ cf_handle = column_family.get_handle()
2842
+ it_ptr = transaction.Transaction_GetIterator_CF(txn_ptr, opts, cf_handle)
2843
+ else:
2844
+ it_ptr = transaction.Transaction_GetIterator(txn_ptr, opts)
2845
+
2846
+ it = KeysIterator(self.db, column_family)
2847
+ it.owner = self
2848
+ it.ptr = it_ptr
2849
+ return it
2850
+
2851
+ cpdef Iterator itervalues(self, ColumnFamilyHandle column_family = None):
2852
+ self._ensure_open()
2853
+ cdef options.ReadOptions opts
2854
+ cdef db.ColumnFamilyHandle* cf_handle
2855
+ cdef iterator.Iterator* it_ptr
2856
+ cdef transaction.Transaction* txn_ptr = self.txn
2857
+ cdef ValuesIterator it
2858
+
2859
+ if column_family is not None:
2860
+ cf_handle = column_family.get_handle()
2861
+ it_ptr = transaction.Transaction_GetIterator_CF(txn_ptr, opts, cf_handle)
2862
+ else:
2863
+ it_ptr = transaction.Transaction_GetIterator(txn_ptr, opts)
2864
+
2865
+ it = ValuesIterator(self.db, column_family)
2866
+ it.owner = self
2867
+ it.ptr = it_ptr
2868
+ return it
2869
+
2870
+ cpdef Iterator iteritems(self, ColumnFamilyHandle column_family = None):
2871
+ self._ensure_open()
2872
+ cdef options.ReadOptions opts
2873
+ cdef db.ColumnFamilyHandle* cf_handle
2874
+ cdef iterator.Iterator* it_ptr
2875
+ cdef transaction.Transaction* txn_ptr = self.txn
2876
+ cdef ItemsIterator it
2877
+
2878
+ if column_family is not None:
2879
+ cf_handle = column_family.get_handle()
2880
+ it_ptr = transaction.Transaction_GetIterator_CF(txn_ptr, opts, cf_handle)
2881
+ else:
2882
+ it_ptr = transaction.Transaction_GetIterator(txn_ptr, opts)
2883
+
2884
+ it = ItemsIterator.__new__(ItemsIterator)
2885
+ it.db = self.db
2886
+ it.handle = column_family
2887
+ it.owner = self
2888
+ it.ptr = it_ptr
2889
+ return it
2890
+
2891
+ cpdef void disable_indexing(self):
2892
+ self._ensure_open()
2893
+ self.txn.DisableIndexing()
2894
+
2895
+ cpdef void enable_indexing(self):
2896
+ self._ensure_open()
2897
+ self.txn.EnableIndexing()
2898
+
2899
+
2900
+ class TransactionSnapshot:
2901
+ __slots__ = ("transaction", "pointer")
2902
+
2903
+ def __init__(self, transaction, pointer):
2904
+ self.transaction = transaction
2905
+ self.pointer = pointer
2906
+
2907
+ def __bool__(self):
2908
+ return bool(self.pointer)
2909
+
2910
+ def __repr__(self):
2911
+ state = "set" if self.pointer else "unset"
2912
+ return f"<TransactionSnapshot {state}>"
2913
+
2914
+
2915
+ cpdef repair_db(db_name, Options opts):
2916
+ cdef Status st
2917
+ cdef string db_path
2918
+
2919
+ db_path = path_to_string(db_name)
2920
+ st = db.RepairDB(db_path, deref(opts.opts))
2921
+ check_status(st)
2922
+
2923
+
2924
+ cpdef list_column_families(db_name, Options opts):
2925
+ cdef Status st
2926
+ cdef string db_path
2927
+ cdef vector[string] column_families
2928
+
2929
+ db_path = path_to_string(db_name)
2930
+ with nogil:
2931
+ st = db.ListColumnFamilies(deref(opts.opts), db_path, &column_families)
2932
+ check_status(st)
2933
+
2934
+ return column_families
2935
+
2936
+
2937
+ @cython.no_gc_clear
2938
+ cdef class Snapshot(object):
2939
+ # pxd defines:
2940
+ # cdef const snapshot.Snapshot* ptr
2941
+ # cdef DB db
2942
+
2943
+ def __cinit__(self, DB db):
2944
+ self.db = db
2945
+ self.ptr = NULL
2946
+ with nogil:
2947
+ self.ptr = db.db.GetSnapshot()
2948
+
2949
+ def __dealloc__(self):
2950
+ if not self.ptr == NULL:
2951
+ with nogil:
2952
+ self.db.db.ReleaseSnapshot(self.ptr)
2953
+
2954
+
2955
+ cdef class Iterator:
2956
+ def __cinit__(self):
2957
+ self._current_value = None
2958
+
2959
+ def __iter__(self):
2960
+ return self
2961
+
2962
+ def __next__(self):
2963
+ item = self.next()
2964
+ if item is None:
2965
+ raise StopIteration
2966
+ return item
2967
+
2968
+ # NOTE Iterator interface defines sentinel value of `None` so it can't
2969
+ # yield None values. However, one can still overwrite cpython's __next__ instead.
2970
+ cpdef object next(self):
2971
+ return None
2972
+
2973
+ cpdef object get(self):
2974
+ if self._current_value is None:
2975
+ self._current_value = self.next()
2976
+ return self._current_value
2977
+
2978
+ cpdef void skip(self):
2979
+ self._current_value = None
2980
+
2981
+
2982
+ cdef class BaseIterator(Iterator):
2983
+ # pxd defines:
2984
+ # cdef iterator.Iterator* ptr
2985
+ # cdef DB db
2986
+ # cdef ColumnFamilyHandle handle
2987
+
2988
+ def __cinit__(self, DB db = None, ColumnFamilyHandle handle = None):
2989
+ self.db = db
2990
+ self.ptr = NULL
2991
+ self.handle = handle
2992
+ self.owner = db
2993
+
2994
+ def __dealloc__(self):
2995
+ if not self.ptr == NULL:
2996
+ del self.ptr
2997
+
2998
+ cpdef object next(self):
2999
+ if not self.ptr.Valid():
3000
+ return None
3001
+
3002
+ cdef object ret = self.get_ob()
3003
+ with nogil:
3004
+ self.ptr.Next()
3005
+ check_status(self.ptr.status())
3006
+ return ret
3007
+
3008
+ cpdef object get(self):
3009
+ if not self.ptr.Valid():
3010
+ raise ValueError()
3011
+
3012
+ cdef object ret = self.get_ob()
3013
+ return ret
3014
+
3015
+ cpdef void skip(self):
3016
+ if not self.ptr.Valid():
3017
+ raise ValueError()
3018
+ with nogil:
3019
+ self.ptr.Next()
3020
+ check_status(self.ptr.status())
3021
+
3022
+ def __reversed__(self):
3023
+ return ReversedIterator(self)
3024
+
3025
+ cpdef void seek_to_first(self):
3026
+ with nogil:
3027
+ self.ptr.SeekToFirst()
3028
+ check_status(self.ptr.status())
3029
+
3030
+ cpdef void seek_to_last(self):
3031
+ with nogil:
3032
+ self.ptr.SeekToLast()
3033
+ check_status(self.ptr.status())
3034
+
3035
+ cpdef void seek(self, bytes key):
3036
+ cdef Slice c_key = bytes_to_slice(key)
3037
+ with nogil:
3038
+ self.ptr.Seek(c_key)
3039
+ check_status(self.ptr.status())
3040
+
3041
+ cpdef void seek_for_prev(self, bytes key):
3042
+ cdef Slice c_key = bytes_to_slice(key)
3043
+ with nogil:
3044
+ self.ptr.SeekForPrev(c_key)
3045
+ check_status(self.ptr.status())
3046
+
3047
+ cdef object get_ob(self):
3048
+ return None
3049
+
3050
+
3051
+ cdef class KeysIterator(BaseIterator):
3052
+ cdef object get_ob(self):
3053
+ cdef Slice c_key
3054
+ with nogil:
3055
+ c_key = self.ptr.key()
3056
+ check_status(self.ptr.status())
3057
+ return slice_to_bytes(c_key)
3058
+
3059
+
3060
+ cdef class ValuesIterator(BaseIterator):
3061
+ cdef object get_ob(self):
3062
+ cdef Slice c_value
3063
+ with nogil:
3064
+ c_value = self.ptr.value()
3065
+ check_status(self.ptr.status())
3066
+ return slice_to_bytes(c_value)
3067
+
3068
+
3069
+ cdef class ItemsIterator(BaseIterator):
3070
+ cdef object get_ob(self):
3071
+ cdef Slice c_key
3072
+ cdef Slice c_value
3073
+ with nogil:
3074
+ c_key = self.ptr.key()
3075
+ c_value = self.ptr.value()
3076
+ check_status(self.ptr.status())
3077
+ return (slice_to_bytes(c_key), slice_to_bytes(c_value))
3078
+
3079
+
3080
+ cdef class ReversedIterator(object):
3081
+ # pxd defines:
3082
+ # cdef BaseIterator it
3083
+
3084
+ def __cinit__(self, BaseIterator it):
3085
+ self.it = it
3086
+
3087
+ cpdef void seek_to_first(self):
3088
+ self.it.seek_to_first()
3089
+
3090
+ cpdef void seek_to_last(self):
3091
+ self.it.seek_to_last()
3092
+
3093
+ cpdef void seek(self, bytes key):
3094
+ self.it.seek(key)
3095
+
3096
+ cpdef void seek_for_prev(self, bytes key):
3097
+ self.it.seek_for_prev(key)
3098
+
3099
+ cpdef object get(self):
3100
+ return self.it.get()
3101
+
3102
+ def __iter__(self):
3103
+ return self
3104
+
3105
+ def __reversed__(self):
3106
+ return self.it
3107
+
3108
+ def __next__(self):
3109
+ if not self.it.ptr.Valid():
3110
+ raise StopIteration()
3111
+
3112
+ cdef object ret = self.it.get_ob()
3113
+ with nogil:
3114
+ self.it.ptr.Prev()
3115
+ check_status(self.it.ptr.status())
3116
+ return ret
3117
+
3118
+
3119
+ cdef class BackupEngine(object):
3120
+ # pxd defines:
3121
+ # cdef backup.BackupEngine* engine
3122
+
3123
+ def __cinit__(self, backup_dir):
3124
+ cdef Status st
3125
+ cdef string c_backup_dir
3126
+ self.engine = NULL
3127
+
3128
+ c_backup_dir = path_to_string(backup_dir)
3129
+ st = backup.BackupEngine_Open(
3130
+ env.Env_Default(),
3131
+ backup.BackupEngineOptions(c_backup_dir),
3132
+ cython.address(self.engine))
3133
+
3134
+ check_status(st)
3135
+
3136
+ def __dealloc__(self):
3137
+ if not self.engine == NULL:
3138
+ with nogil:
3139
+ del self.engine
3140
+
3141
+ cpdef create_backup(self, DB db, flush_before_backup=False):
3142
+ cdef Status st
3143
+ cdef cpp_bool c_flush_before_backup
3144
+
3145
+ c_flush_before_backup = flush_before_backup
3146
+
3147
+ with nogil:
3148
+ st = self.engine.CreateNewBackup(db.db, c_flush_before_backup)
3149
+ check_status(st)
3150
+
3151
+ cpdef restore_backup(self, backup_id, db_dir, wal_dir):
3152
+ cdef Status st
3153
+ cdef backup.BackupID c_backup_id
3154
+ cdef string c_db_dir
3155
+ cdef string c_wal_dir
3156
+
3157
+ c_backup_id = backup_id
3158
+ c_db_dir = path_to_string(db_dir)
3159
+ c_wal_dir = path_to_string(wal_dir)
3160
+
3161
+ with nogil:
3162
+ st = self.engine.RestoreDBFromBackup(
3163
+ c_backup_id,
3164
+ c_db_dir,
3165
+ c_wal_dir)
3166
+
3167
+ check_status(st)
3168
+
3169
+ cpdef restore_latest_backup(self, db_dir, wal_dir):
3170
+ cdef Status st
3171
+ cdef string c_db_dir
3172
+ cdef string c_wal_dir
3173
+
3174
+ c_db_dir = path_to_string(db_dir)
3175
+ c_wal_dir = path_to_string(wal_dir)
3176
+
3177
+ with nogil:
3178
+ st = self.engine.RestoreDBFromLatestBackup(c_db_dir, c_wal_dir)
3179
+
3180
+ check_status(st)
3181
+
3182
+ cpdef stop_backup(self):
3183
+ with nogil:
3184
+ self.engine.StopBackup()
3185
+
3186
+ cpdef purge_old_backups(self, num_backups_to_keep):
3187
+ cdef Status st
3188
+ cdef uint32_t c_num_backups_to_keep
3189
+
3190
+ c_num_backups_to_keep = num_backups_to_keep
3191
+
3192
+ with nogil:
3193
+ st = self.engine.PurgeOldBackups(c_num_backups_to_keep)
3194
+ check_status(st)
3195
+
3196
+ cpdef delete_backup(self, backup_id):
3197
+ cdef Status st
3198
+ cdef backup.BackupID c_backup_id
3199
+
3200
+ c_backup_id = backup_id
3201
+
3202
+ with nogil:
3203
+ st = self.engine.DeleteBackup(c_backup_id)
3204
+
3205
+ check_status(st)
3206
+
3207
+ cpdef get_backup_info(self):
3208
+ cdef vector[backup.BackupInfo] backup_info
3209
+
3210
+ with nogil:
3211
+ self.engine.GetBackupInfo(cython.address(backup_info))
3212
+
3213
+ ret = []
3214
+ for ob in backup_info:
3215
+ t = {}
3216
+ t['backup_id'] = ob.backup_id
3217
+ t['timestamp'] = ob.timestamp
3218
+ t['size'] = ob.size
3219
+ ret.append(t)
3220
+
3221
+ return ret