oinky 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+