oinky 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +22 -0
- data/README.md +141 -0
- data/ext/extconf.rb +79 -0
- data/ext/include/oinky.h +424 -0
- data/ext/include/oinky.hpp +63 -0
- data/ext/include/oinky/nky_base.hpp +1116 -0
- data/ext/include/oinky/nky_core.hpp +1603 -0
- data/ext/include/oinky/nky_cursor.hpp +665 -0
- data/ext/include/oinky/nky_dialect.hpp +107 -0
- data/ext/include/oinky/nky_error.hpp +164 -0
- data/ext/include/oinky/nky_fixed_table.hpp +710 -0
- data/ext/include/oinky/nky_handle.hpp +334 -0
- data/ext/include/oinky/nky_index.hpp +1038 -0
- data/ext/include/oinky/nky_log.hpp +15 -0
- data/ext/include/oinky/nky_merge_itr.hpp +403 -0
- data/ext/include/oinky/nky_model.hpp +110 -0
- data/ext/include/oinky/nky_pool.hpp +760 -0
- data/ext/include/oinky/nky_public.hpp +808 -0
- data/ext/include/oinky/nky_serializer.hpp +1625 -0
- data/ext/include/oinky/nky_strtable.hpp +504 -0
- data/ext/include/oinky/nky_table.hpp +1996 -0
- data/ext/nky_lib.cpp +390 -0
- data/ext/nky_lib_core.hpp +212 -0
- data/ext/nky_lib_index.cpp +158 -0
- data/ext/nky_lib_table.cpp +224 -0
- data/lib/oinky.rb +1284 -0
- data/lib/oinky/compiler.rb +106 -0
- data/lib/oinky/cpp_emitter.rb +311 -0
- data/lib/oinky/dsl.rb +167 -0
- data/lib/oinky/error.rb +19 -0
- data/lib/oinky/modelbase.rb +12 -0
- data/lib/oinky/nbuffer.rb +152 -0
- data/lib/oinky/normalize.rb +132 -0
- data/lib/oinky/oc_builder.rb +44 -0
- data/lib/oinky/query.rb +193 -0
- data/lib/oinky/rb_emitter.rb +147 -0
- data/lib/oinky/shard.rb +40 -0
- data/lib/oinky/testsup.rb +104 -0
- data/lib/oinky/version.rb +9 -0
- data/oinky.gemspec +36 -0
- 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
|
+
|