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.
- 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
|
+
|