oinky 0.1.0

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 (41) hide show
  1. data/LICENSE +22 -0
  2. data/README.md +141 -0
  3. data/ext/extconf.rb +79 -0
  4. data/ext/include/oinky.h +424 -0
  5. data/ext/include/oinky.hpp +63 -0
  6. data/ext/include/oinky/nky_base.hpp +1116 -0
  7. data/ext/include/oinky/nky_core.hpp +1603 -0
  8. data/ext/include/oinky/nky_cursor.hpp +665 -0
  9. data/ext/include/oinky/nky_dialect.hpp +107 -0
  10. data/ext/include/oinky/nky_error.hpp +164 -0
  11. data/ext/include/oinky/nky_fixed_table.hpp +710 -0
  12. data/ext/include/oinky/nky_handle.hpp +334 -0
  13. data/ext/include/oinky/nky_index.hpp +1038 -0
  14. data/ext/include/oinky/nky_log.hpp +15 -0
  15. data/ext/include/oinky/nky_merge_itr.hpp +403 -0
  16. data/ext/include/oinky/nky_model.hpp +110 -0
  17. data/ext/include/oinky/nky_pool.hpp +760 -0
  18. data/ext/include/oinky/nky_public.hpp +808 -0
  19. data/ext/include/oinky/nky_serializer.hpp +1625 -0
  20. data/ext/include/oinky/nky_strtable.hpp +504 -0
  21. data/ext/include/oinky/nky_table.hpp +1996 -0
  22. data/ext/nky_lib.cpp +390 -0
  23. data/ext/nky_lib_core.hpp +212 -0
  24. data/ext/nky_lib_index.cpp +158 -0
  25. data/ext/nky_lib_table.cpp +224 -0
  26. data/lib/oinky.rb +1284 -0
  27. data/lib/oinky/compiler.rb +106 -0
  28. data/lib/oinky/cpp_emitter.rb +311 -0
  29. data/lib/oinky/dsl.rb +167 -0
  30. data/lib/oinky/error.rb +19 -0
  31. data/lib/oinky/modelbase.rb +12 -0
  32. data/lib/oinky/nbuffer.rb +152 -0
  33. data/lib/oinky/normalize.rb +132 -0
  34. data/lib/oinky/oc_builder.rb +44 -0
  35. data/lib/oinky/query.rb +193 -0
  36. data/lib/oinky/rb_emitter.rb +147 -0
  37. data/lib/oinky/shard.rb +40 -0
  38. data/lib/oinky/testsup.rb +104 -0
  39. data/lib/oinky/version.rb +9 -0
  40. data/oinky.gemspec +36 -0
  41. metadata +120 -0
@@ -0,0 +1,665 @@
1
+ // This source is distributed under the terms of the MIT License. Refer
2
+ // to the 'LICENSE' file for details.
3
+ //
4
+ // Copyright (c) Jacob Lacouture, 2012
5
+
6
+ namespace Oinky
7
+ {
8
+ namespace Internal
9
+ {
10
+ using namespace Oinky::Errors;
11
+ using namespace Oinky::Utils;
12
+
13
+
14
+ // The row iterator template provides table/select access mechanisms to any
15
+ // enumerator over selectable rows.
16
+ template<typename TABLE_CTX, typename DERIVED, typename BASE>
17
+ class row_iterator_template : public boost::iterator_adaptor<
18
+ DERIVED,
19
+ BASE,
20
+ const row_iterator_template<TABLE_CTX,DERIVED,BASE> &,
21
+ boost::bidirectional_traversal_tag,
22
+ const row_iterator_template<TABLE_CTX,DERIVED,BASE> &
23
+ >
24
+ {
25
+ template<typename DB> friend class table_ctx_t;
26
+ typedef boost::iterator_adaptor<
27
+ DERIVED,
28
+ BASE,
29
+ const row_iterator_template<TABLE_CTX,DERIVED,BASE> &,
30
+ boost::bidirectional_traversal_tag,
31
+ const row_iterator_template<TABLE_CTX,DERIVED,BASE> &
32
+ > base_t;
33
+
34
+ typedef row_iterator_template<TABLE_CTX,DERIVED,BASE> this_t;
35
+ typedef pending_row<TABLE_CTX> pending_row_t;
36
+ typedef column_selector_template<TABLE_CTX> column_selector_t;
37
+ typedef TABLE_CTX table_ctx;
38
+
39
+ friend class boost::iterator_core_access;
40
+
41
+ protected:
42
+ struct select_accessor_data
43
+ {
44
+ row_idx_t rowkey;
45
+ const pending_row_t *prow;
46
+ const table_ctx *table;
47
+
48
+ select_accessor_data(
49
+ row_idx_t _rowkey,
50
+ const pending_row_t *_prow,
51
+ const table_ctx *_table) :
52
+ rowkey(_rowkey),
53
+ prow(_prow),
54
+ table(_table)
55
+ {}
56
+ };
57
+ private:
58
+ struct select_accessor_base
59
+ {
60
+ const column_selector_t &selector;
61
+ select_accessor_data data;
62
+ public:
63
+ select_accessor_base(
64
+ const column_selector_t &_selector,
65
+ const select_accessor_data _data) :
66
+ selector(_selector),
67
+ data(_data)
68
+ {}
69
+
70
+ uint32 column_count() const { return selector.column_count(); }
71
+
72
+ // Parameters are a column definition and a value
73
+ template<typename FN>
74
+ void each_column_value(FN fn) const{
75
+ if (data.prow) {
76
+ data.prow->each_column_value(selector, fn);
77
+ } else {
78
+ data.table->fixed.each_column_value(selector, data.rowkey, fn);
79
+ }
80
+ }
81
+ };
82
+
83
+ const this_t &dereference() const {
84
+ return *this;
85
+ }
86
+
87
+ public:
88
+ bool at_end() const { return base_t::base().at_end(); }
89
+ bool before_begin() const { return base_t::base().before_begin(); }
90
+
91
+ row_iterator_template() {}
92
+ row_iterator_template(const BASE &val) : base_t(val) {}
93
+
94
+ typedef multicolumn_value_accessor<select_accessor_base> select_accessor;
95
+
96
+ select_accessor select(const column_selector_t &cs) const {
97
+ const DERIVED *dp = (const DERIVED *) this;
98
+ return select_accessor(
99
+ select_accessor_base(
100
+ cs,
101
+ dp->get_selector_data()));
102
+ }
103
+
104
+ bool operator<(const this_t &other) const { return base_t::base() < other.base(); }
105
+ bool operator>(const this_t &other) const { return base_t::base() > other.base(); }
106
+ bool operator<=(const this_t &other) const { return base_t::base() <= other.base(); }
107
+ bool operator>=(const this_t &other) const { return base_t::base() >= other.base(); }
108
+ };
109
+
110
+ //
111
+ // It is an axiom of the programming model that all objects exposed to
112
+ // the user (other than the database handle itself) have trivial
113
+ // destructors. This fits well with the C++/STL container paradigm
114
+ // where the iterators have trivial destructors, and do not reference
115
+ // the container itself. The caller must be responsible enough not to
116
+ // use expired iterators, but the implementation does not force the caller
117
+ // to destroy iterators prior to the container.
118
+ //
119
+ // The cursor concept is a deviation from the C++/STL iterator concept
120
+ // in that it is safer. While a savepoint rollback, alter-table,
121
+ // row-delete, row-update, create-index, etc. can all invalidate an
122
+ // iterator, the cursor will survive. Only database unmount (handle
123
+ // destruction) will invalidate the cursor.
124
+ //
125
+ // This simplicity requires a bit of internal referencing, managed by
126
+ // splitting the cursor into an internal and external part. The external
127
+ // part behaves like a handle. It's a pointer to the internal cursor
128
+ // object. Copying the handle does not clone the cursor. To clone the
129
+ // cursor, the handle's clone method must be invoked, which makes a copy
130
+ // of the internal cursor object and returns a new pointer to it.
131
+ //
132
+ // These cursor objects are linked together among cursors over the same
133
+ // parent (index/table). They are linked on allocation and unlinked
134
+ // either when the cursor gets deleted or when the parent gets deleted.
135
+ // This is a simpler alternative to refcounting the parent, as the lifetime
136
+ // of parent objects (what with create/drop/undrop) is complex enough
137
+ // as it is.
138
+ //
139
+ template<typename SAFE_ITR>
140
+ struct cursor_internal_t :
141
+ boost::intrusive::list_base_hook<
142
+ boost::intrusive::link_mode<
143
+ boost::intrusive::safe_link
144
+ > >
145
+ {
146
+ typedef SAFE_ITR safe_itr_t;
147
+
148
+ safe_itr_t itr;
149
+ uint64 cookie;
150
+
151
+ cursor_internal_t(const safe_itr_t &_itr) : itr(_itr) {}
152
+
153
+ // This is called by the host when it invalides its active cursors.
154
+ void invalidate() {
155
+ cookie = 0;
156
+ }
157
+
158
+ ~cursor_internal_t()
159
+ {
160
+ invalidate();
161
+
162
+ // We should have already been unlinked from our host.
163
+ // This method is on the safe-mode hook from which we are derived.
164
+ OINKY_ASSERT(!is_linked());
165
+
166
+ // ~itr does the dereferencing of the pending_row
167
+ }
168
+ };
169
+
170
+ template<typename INTERNAL_T, typename HOST_T>
171
+ class cursor_handle_t
172
+ {
173
+ template<typename TABLE_CTX> friend class index_ctx_t;
174
+ template<typename INDEX_CTX> friend class index_handle_t;
175
+ template<typename TABLE_CTX> friend class table_handle_t;
176
+ template<typename DB> friend class table_ctx_t;
177
+
178
+ typedef HOST_T host_t;
179
+ typedef INTERNAL_T cursor_internal_t;
180
+ typedef typename cursor_internal_t::safe_itr_t safe_itr_t;
181
+ typedef cursor_handle_t<INTERNAL_T,HOST_T> cursor_handle;
182
+
183
+ cursor_internal_t *cursor;
184
+ host_t host;
185
+ safe_itr_t *itr;
186
+
187
+ static uint64 cookie_value() { return 0xe3c1290b4a75f6d8ull; }
188
+
189
+ cursor_handle_t(cursor_internal_t *_cursor) :
190
+ cursor(_cursor),
191
+ host(_cursor->itr.pending().host),
192
+ itr(&_cursor->itr)
193
+ {
194
+ }
195
+
196
+ cursor_handle_t(const host_t &_host, const safe_itr_t &where) :
197
+ host(_host)
198
+ {
199
+ // Convert the unsafe pending iterator type to a safe one.
200
+ cursor = (cursor_internal_t *) host.allocator().malloc(sizeof(cursor_internal_t));
201
+ ::new(cursor) cursor_internal_t(where);
202
+ OINKY_ASSERT(cursor->itr.pending().host == host);
203
+ host.link_cursor(cursor);
204
+ cursor->cookie = cookie_value();
205
+ itr = &cursor->itr;
206
+ }
207
+ public:
208
+ //###############
209
+ // For the C API. The type is completely opaque, but an equivalent
210
+ // handle object can be recovered from it.
211
+ //
212
+ // The RAW transformation follows the same pattern as that for table/index
213
+ // handles. The difference is that reconstruction of a bad handle
214
+ // can raise, whereas reconstruction of a bad table handle won't raise
215
+ // until the reconstructed handle is used.
216
+ class raw_handle_t {};
217
+ raw_handle_t *raw() const { return (raw_handle_t *) cursor; }
218
+ cursor_handle_t(raw_handle_t *_raw) {
219
+ // Just forward to the normal constructor.
220
+ *this = cursor_handle_t((cursor_internal_t *)_raw);
221
+ }
222
+
223
+ private:
224
+ void check_valid() const {
225
+ host.check_state();
226
+
227
+ if (!itr || !cursor ||
228
+ (cursor->cookie != cookie_value()) ||
229
+ (host != cursor->itr.pending().host))
230
+ {
231
+ // Invalid cursor.
232
+ throw_error(invalid_argument());
233
+ }
234
+ }
235
+
236
+ template<typename TABLE_CTX, typename PROW>
237
+ void unpack_check_active(const TABLE_CTX *table, row_idx_t *fixed, PROW **row) const
238
+ {
239
+ check_valid();
240
+
241
+ if (!valid_data() || (host.table() != table)) {
242
+ throw_error(invalid_argument());
243
+ }
244
+ OINKY_ASSERT(itr == &cursor->itr);
245
+
246
+ if (cursor->itr.pending_active()) {
247
+ PROW *rtmp = cursor->itr.pending().to_row();
248
+ *row = rtmp;
249
+ *fixed = (row_idx_t) -1;
250
+
251
+ if (rtmp->is_inactive()) {
252
+ // The cursor's target-row has been modified or deleted since
253
+ // the cursor position was last set.
254
+ throw_error(object_deleted());
255
+ }
256
+ } else if (cursor->itr.fixed_active()) {
257
+ *row = NULL;
258
+ *fixed = *cursor->itr.fixed();
259
+
260
+ if (table->is_deleted(*fixed)) {
261
+ // The cursor's target-row has been modified or deleted since
262
+ // the cursor position was last set.
263
+ throw_error(object_deleted());
264
+ }
265
+ } else {
266
+ // Cursor is not even valid.
267
+ throw_error(invalid_argument());
268
+ }
269
+ }
270
+
271
+ // See notes before calling this method. Free is implicit when the
272
+ // DB is destroyed. Calling free twice is bad. Calling it after
273
+ // destroying the DB object is equivalently bad. An app really
274
+ // only need call it on DB instances that will be very long lived and
275
+ // instantiate many cursors.
276
+ void free()
277
+ {
278
+ check_valid();
279
+ host.unlink_cursor(cursor);
280
+ cursor->~cursor_internal_t();
281
+ host.allocator().free(cursor);
282
+ cursor = NULL;
283
+ host = host_t();
284
+ itr = NULL;
285
+ }
286
+ public:
287
+ // Copy-construct the internal cursor object (which makes a deep copy)
288
+ // and return a new reference to it.
289
+ cursor_handle clone() const {
290
+ check_valid();
291
+ cursor_internal_t *crs = (cursor_internal_t *) host.allocator().malloc(sizeof(cursor_internal_t));
292
+ ::new(crs) cursor_internal_t(*cursor);
293
+ cursor_handle nh(crs);
294
+ nh.host.link_cursor(crs);
295
+ return nh;
296
+ }
297
+
298
+ // This tells if the handle value is uninitialized. If this returns false,
299
+ // it does NOT mean the handle is valid. It may still be an expired handle
300
+ // or just invalid. But it is a test as to whether it's initialized at all.
301
+ bool is_null() const { return cursor == NULL; }
302
+
303
+ cursor_handle_t() : cursor(NULL), itr(NULL) {}
304
+
305
+ bool seek_next() {
306
+ check_valid();
307
+ if (!itr->at_end()) {
308
+ ++(*itr);
309
+ }
310
+ return valid_data();
311
+ }
312
+
313
+ bool seek_prev() {
314
+ check_valid();
315
+ if (!itr->before_begin()) {
316
+ --(*itr);
317
+ }
318
+ return valid_data();
319
+ }
320
+
321
+ safe_itr_t &iterator() { return *itr; }
322
+
323
+ // Is the cursor referencing valid data?
324
+ // Note that this method will throw if the cursor itself is bad.
325
+ // (cursor erased/index dropped).
326
+ bool valid_data() const {
327
+ check_valid();
328
+ return itr->fixed_active() || itr->pending_active();
329
+ }
330
+
331
+ bool at_end() const {
332
+ check_valid();
333
+ return itr->at_end();
334
+ }
335
+ bool before_begin() const {
336
+ check_valid();
337
+ return itr->before_begin();
338
+ }
339
+
340
+ typedef typename safe_itr_t::select_accessor select_accessor;
341
+
342
+ template<typename SELECTOR>
343
+ select_accessor select(const SELECTOR &cs) const {
344
+ check_valid();
345
+ if (!valid_data() || (host.table() != cs.table)) {
346
+ throw_error(invalid_argument());
347
+ }
348
+ return itr->select(cs);
349
+ }
350
+
351
+ int compare_to(const cursor_handle &other) const {
352
+ check_valid();
353
+ other.check_valid();
354
+
355
+ if (host != other.host) {
356
+ // Can't compare cursors over different indices.
357
+ //
358
+ // The user may intend to compare selects over different
359
+ // indices, which is reasonable.
360
+ //
361
+ // t1i1.select(table1.select("foo")).compare_to(t2i4.select("bar"))
362
+ // --compares the column "foo" at cursor/iterator t1i1 to
363
+ // column "bar" at cursor/iterator t2i4
364
+ //
365
+ throw_error(invalid_argument());
366
+ }
367
+ return itr->compare_to(*other.itr);
368
+ }
369
+
370
+ bool operator==(const cursor_handle &other) const { return compare_to(other) == 0; }
371
+ bool operator!=(const cursor_handle &other) const { return compare_to(other) != 0; }
372
+ bool operator<(const cursor_handle &other) const { return compare_to(other) < 0; }
373
+ bool operator>(const cursor_handle &other) const { return compare_to(other) > 0; }
374
+ bool operator<=(const cursor_handle &other) const { return compare_to(other) <= 0; }
375
+ bool operator>=(const cursor_handle &other) const { return compare_to(other) >= 0; }
376
+ };
377
+
378
+ template<typename TABLE_CTX, typename FITR, typename FRANGE, typename PITR, typename PRANGE, typename COMPARE, typename TO_ROW>
379
+ struct iterator_builder
380
+ {
381
+ typedef TABLE_CTX table_ctx;
382
+
383
+ class iterator_base : public merge_iterator_template<
384
+ iterator_base, //derived
385
+ int, //value
386
+ FRANGE,
387
+ PRANGE,
388
+ COMPARE >
389
+ {
390
+ typedef merge_iterator_template<
391
+ iterator_base, //derived
392
+ int, //value
393
+ FRANGE,
394
+ PRANGE,
395
+ COMPARE > base_t;
396
+
397
+ friend class boost::iterator_core_access;
398
+
399
+ void skip_deleted()
400
+ {
401
+ while (base_t::left_active() && base_t::left().i().is_deleted()) {
402
+ OINKY_ASSERT(!base_t::right_active());
403
+ base_t::increment();
404
+ }
405
+ }
406
+
407
+ // Identical to base items, but must skip fixed entries (without
408
+ // skipping too far).
409
+ void increment()
410
+ {
411
+ base_t::increment();
412
+ skip_deleted();
413
+ }
414
+ void decrement()
415
+ {
416
+ base_t::decrement();
417
+ while (base_t::left_active() && base_t::left().i().is_deleted()) {
418
+ OINKY_ASSERT(!base_t::right_active());
419
+ base_t::decrement();
420
+ }
421
+ }
422
+
423
+ // Irrelevant.
424
+ int dereference() const { return 0; }
425
+ public:
426
+ iterator_base(
427
+ const FRANGE &_l,
428
+ const PRANGE &_r) :
429
+ base_t(_l,_r)
430
+ {
431
+ skip_deleted();
432
+ }
433
+
434
+ iterator_base() {}
435
+ };
436
+
437
+ class iterator : public row_iterator_template<table_ctx, iterator, iterator_base>
438
+ {
439
+ friend class boost::iterator_core_access;
440
+ friend class index_ctx_t<table_ctx>;
441
+ template<typename INDEX_CTX> friend class index_handle_t;
442
+ template<typename CRS, typename HOST> friend class cursor_handle_t;
443
+ template<typename DB> friend class table_ctx_t;
444
+
445
+ typedef row_iterator_template<table_ctx, iterator, iterator_base> base_t;
446
+ friend class row_iterator_template<table_ctx, iterator, iterator_base>;
447
+ friend class table_handle_t<table_ctx>;
448
+
449
+ TABLE_CTX *table;
450
+ iterator(TABLE_CTX *_table, const iterator_base &itr) : base_t(itr), table(_table) {}
451
+
452
+ iterator(
453
+ TABLE_CTX *_table,
454
+ const FRANGE &_l,
455
+ const PRANGE &_r) :
456
+ base_t(iterator_base(_l,_r)),
457
+ table(_table)
458
+ {}
459
+
460
+ public:
461
+ iterator() : table(NULL) {}
462
+
463
+ private:
464
+ template<typename X,typename SCHEME> friend class ::Oinky::Serialization::Serializer;
465
+
466
+ const FRANGE &fixed_range() const { return base_t::base().left(); }
467
+ const FITR &fixed() const { return fixed_range().i(); }
468
+ const PRANGE &pending_range() const { return base_t::base().right(); }
469
+ const PITR &pending() const { return base_t::base().right().i(); }
470
+
471
+ bool fixed_active() const { return base_t::base().left_active(); }
472
+ bool pending_active() const { return base_t::base().right_active(); }
473
+
474
+ int compare_to(const iterator &other) const {
475
+ return base_t::base().compare_to(other.base_t::base());
476
+ }
477
+
478
+ typedef typename base_t::select_accessor_data select_accessor_data;
479
+ select_accessor_data get_selector_data() const {
480
+ bool fa = fixed_active();
481
+ bool pa = pending_active();
482
+ if (!fa && !pa) {
483
+ // Selecting on invalid iterator.
484
+ throw_error(invalid_argument());
485
+ }
486
+
487
+ return select_accessor_data(
488
+ fa ? *fixed() : (row_idx_t) -1,
489
+ pa ? TO_ROW()(pending()) : NULL,
490
+ table);
491
+ }
492
+ };
493
+ };
494
+
495
+ // This is a safe iterator over the pending rows. It can traverse
496
+ // an index even when the table is being modified.
497
+ template<typename PROW, typename HOSTOPS>
498
+ class safe_pending_cursor_t
499
+ {
500
+ PROW *row;
501
+ public:
502
+ typedef HOSTOPS host_t;
503
+ HOSTOPS host;
504
+ private:
505
+ bool bbegin;
506
+
507
+ static void update_row_reference(PROW *old, PROW *nr)
508
+ {
509
+ OINKY_ASSERT((old != nr) || !old);
510
+ if (nr) {
511
+ nr->cursor_reference();
512
+ }
513
+ if (old) {
514
+ old->cursor_dereference();
515
+ }
516
+ }
517
+
518
+ public:
519
+ safe_pending_cursor_t() : row(NULL), bbegin(false) {}
520
+
521
+ safe_pending_cursor_t(const safe_pending_cursor_t &src) :
522
+ row(src.row),
523
+ host(src.host),
524
+ bbegin(src.bbegin)
525
+ {
526
+ if (row) {
527
+ update_row_reference(NULL, row);
528
+ }
529
+ }
530
+
531
+ safe_pending_cursor_t &operator=(const safe_pending_cursor_t &src) {
532
+ if (&src == this) {
533
+ return *this;
534
+ }
535
+ if (row != src.row) {
536
+ update_row_reference(row, src.row);
537
+ }
538
+ row = src.row;
539
+ host = src.host;
540
+ bbegin = src.bbegin;
541
+ return *this;
542
+ }
543
+
544
+ safe_pending_cursor_t(const host_t &_host, PROW *_row, bool _bbegin) :
545
+ row(_row),
546
+ host(_host),
547
+ bbegin(_bbegin)
548
+ {
549
+ OINKY_ASSERT(!bbegin || !row);
550
+ update_row_reference(NULL, row);
551
+ }
552
+
553
+ ~safe_pending_cursor_t() {
554
+ if (row) {
555
+ update_row_reference(row, NULL);
556
+ }
557
+ row = NULL;
558
+ }
559
+
560
+ bool before_begin() const { return bbegin; }
561
+ bool at_end() const { return !bbegin && !row; }
562
+ bool is_valid() const { return row != NULL; }
563
+
564
+ void decrement()
565
+ {
566
+ // First check that the index still exists.
567
+ host.check_state();
568
+
569
+ // Already invalid. No-op.
570
+ if (bbegin) {
571
+ return;
572
+ }
573
+
574
+ // At end. Find last.
575
+ if (!row) {
576
+ find_last:
577
+ OINKY_ASSERT(at_end());
578
+ PROW *new_row = host.seek_prev(NULL);
579
+ if (!new_row) {
580
+ bbegin = true;
581
+ } else {
582
+ update_row_reference(NULL, new_row);
583
+ row = new_row;
584
+ }
585
+ return;
586
+ }
587
+
588
+ // We're pointing to a real row, though it may be deleted.
589
+ host.assert_row_valid(row);
590
+
591
+ // See comment in increment()
592
+ if (row->is_inactive()) {
593
+ // Seek using the lower bound.
594
+ PROW *new_row = host.seek_pending(true, row);
595
+ if (!new_row) {
596
+ update_row_reference(row, NULL);
597
+ row = NULL;
598
+ goto find_last;
599
+ }
600
+ }
601
+ // Seek one previous. (result might be NULL)
602
+ PROW *new_row = host.seek_prev(row);
603
+ update_row_reference(row, new_row);
604
+ row = new_row;
605
+ if (!row) {
606
+ bbegin = true;
607
+ }
608
+ }
609
+ void increment()
610
+ {
611
+ // First check that the index still exists.
612
+ host.check_state();
613
+
614
+ // Seek begin.
615
+ if (bbegin) {
616
+ bbegin = false;
617
+ row = host.seek_next(NULL);
618
+ if (row) {
619
+ update_row_reference(NULL, row);
620
+ }
621
+ return;
622
+ }
623
+
624
+ // Already at end. No-op.
625
+ if (at_end()) {
626
+ return;
627
+ }
628
+
629
+ // Now it's a real increment.
630
+ host.assert_row_valid(row);
631
+
632
+ // If the row was deleted since we last iterated to it, then
633
+ // we need to re-seek. Note that if the row gets updated while
634
+ // it has a cursor on it, we perform the update by delete/insert
635
+ // rather than rewriting any values. This allows cursors to
636
+ // maintain consistent state until they are moved.
637
+ //
638
+ // Note that we check if it's deleted, not whether it's pending
639
+ // destruction. Deletion means it out of the live tree, out of
640
+ // sequence. It may not yet be pending destruction.
641
+ PROW *new_row;
642
+ if (row->is_inactive()) {
643
+ // Seek using the upper bound.
644
+ new_row = host.seek_pending(false, row);
645
+ } else {
646
+ new_row = host.seek_next(row);
647
+ }
648
+ update_row_reference(row, new_row);
649
+ row = new_row;
650
+ }
651
+
652
+ const safe_pending_cursor_t &i() const {
653
+ return *this;
654
+ }
655
+
656
+ PROW *operator->() const {
657
+ return row;
658
+ }
659
+
660
+ PROW *to_row() const { return row; }
661
+ };//safe_pending_cursor_t
662
+
663
+ } //namespace Internal
664
+ } //namespace Oinky
665
+