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,15 @@
|
|
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
|
+
} //namespace Internal
|
15
|
+
} //namespace Oinky
|
@@ -0,0 +1,403 @@
|
|
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 Utils
|
9
|
+
{
|
10
|
+
using namespace Oinky::Errors;
|
11
|
+
|
12
|
+
//
|
13
|
+
// The merge iterator is bidirectional. We need a concept of a range, where
|
14
|
+
// the iterator can be valid, or at end(), or additionally, before the
|
15
|
+
// beginning of the sequence. In general, we cannot safely decrement an
|
16
|
+
// iterator from begin().
|
17
|
+
//
|
18
|
+
|
19
|
+
template<typename ITR>
|
20
|
+
class symmetric_iterator
|
21
|
+
{
|
22
|
+
typedef ITR iterator;
|
23
|
+
|
24
|
+
iterator _i;
|
25
|
+
iterator _begin;
|
26
|
+
iterator _end;
|
27
|
+
bool _sub_begin;
|
28
|
+
|
29
|
+
iterator begin() const { return _begin; }
|
30
|
+
iterator end() const { return _end; }
|
31
|
+
|
32
|
+
public:
|
33
|
+
symmetric_iterator(
|
34
|
+
const iterator &__i,
|
35
|
+
const iterator &__begin,
|
36
|
+
const iterator &__end) :
|
37
|
+
_i(__i),
|
38
|
+
_begin(__begin),
|
39
|
+
_end(__end),
|
40
|
+
_sub_begin(false)
|
41
|
+
{}
|
42
|
+
|
43
|
+
symmetric_iterator() : _sub_begin(false) {}
|
44
|
+
|
45
|
+
// This need not return iterator &. It merely must return whatever
|
46
|
+
// the comparer expects.
|
47
|
+
const iterator &i() const { return _i; }
|
48
|
+
|
49
|
+
bool before_begin() const { return _sub_begin; }
|
50
|
+
bool at_end() const { return _i == _end; }
|
51
|
+
bool is_valid() const { return !before_begin() && !at_end(); }
|
52
|
+
|
53
|
+
void increment() {
|
54
|
+
OINKY_ASSERT( _sub_begin || (_i != _end) );
|
55
|
+
if (_sub_begin) {
|
56
|
+
_sub_begin = false;
|
57
|
+
} else {
|
58
|
+
++_i;
|
59
|
+
}
|
60
|
+
}
|
61
|
+
void decrement() {
|
62
|
+
OINKY_ASSERT( !_sub_begin );
|
63
|
+
if (_i == _begin) {
|
64
|
+
_sub_begin = true;
|
65
|
+
} else {
|
66
|
+
--_i;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
};
|
70
|
+
|
71
|
+
template<typename DERIVED, typename VALUE_TYPE, typename RANGE1, typename RANGE2, typename COMPARE>
|
72
|
+
class merge_iterator_template : public
|
73
|
+
boost::iterator_facade<
|
74
|
+
DERIVED,
|
75
|
+
VALUE_TYPE,
|
76
|
+
boost::bidirectional_traversal_tag,
|
77
|
+
VALUE_TYPE
|
78
|
+
>
|
79
|
+
{
|
80
|
+
typedef merge_iterator_template<DERIVED,VALUE_TYPE,RANGE1,RANGE2,COMPARE> this_t;
|
81
|
+
typedef boost::iterator_facade<
|
82
|
+
DERIVED,
|
83
|
+
VALUE_TYPE,
|
84
|
+
boost::bidirectional_traversal_tag,
|
85
|
+
VALUE_TYPE
|
86
|
+
> base_t;
|
87
|
+
|
88
|
+
friend class boost::iterator_core_access;
|
89
|
+
|
90
|
+
public:
|
91
|
+
// base iterators should all have good defaults.
|
92
|
+
merge_iterator_template() {}
|
93
|
+
|
94
|
+
merge_iterator_template(
|
95
|
+
const RANGE1 &_l,
|
96
|
+
const RANGE2 &_r) :
|
97
|
+
l(_l),
|
98
|
+
r(_r)
|
99
|
+
{
|
100
|
+
set_fwd_state();
|
101
|
+
}
|
102
|
+
|
103
|
+
private:
|
104
|
+
RANGE1 l;
|
105
|
+
RANGE2 r;
|
106
|
+
|
107
|
+
// -1: *this == *l
|
108
|
+
// 0: *this == *l, *this == *r
|
109
|
+
// 1: *this == *r
|
110
|
+
int active;
|
111
|
+
// -1: *l < *r
|
112
|
+
// 0: *l == *r
|
113
|
+
// 1: *l > r
|
114
|
+
int lsubr;
|
115
|
+
|
116
|
+
void set_fwd_state()
|
117
|
+
{
|
118
|
+
lsubr = 0;
|
119
|
+
bool compare = true;
|
120
|
+
if (l.before_begin()) {
|
121
|
+
lsubr -= 1;
|
122
|
+
compare = false;
|
123
|
+
} else if (l.at_end()) {
|
124
|
+
lsubr += 1;
|
125
|
+
compare = false;
|
126
|
+
}
|
127
|
+
if (r.before_begin()) {
|
128
|
+
lsubr += 1;
|
129
|
+
compare = false;
|
130
|
+
} else if (r.at_end()) {
|
131
|
+
lsubr -= 1;
|
132
|
+
compare = false;
|
133
|
+
}
|
134
|
+
if (compare) {
|
135
|
+
// Compute l-r
|
136
|
+
COMPARE c;
|
137
|
+
lsubr = c(l.i(), r.i());
|
138
|
+
}
|
139
|
+
// current position is the smaller of the two. -1 if left
|
140
|
+
// is smaller, 1 if right is smaller, 0 if equal.
|
141
|
+
active = lsubr;
|
142
|
+
}
|
143
|
+
void set_reverse_state()
|
144
|
+
{
|
145
|
+
set_fwd_state();
|
146
|
+
active = -active;
|
147
|
+
}
|
148
|
+
protected:
|
149
|
+
void increment()
|
150
|
+
{
|
151
|
+
// Advance the iterator.
|
152
|
+
COMPARE c;
|
153
|
+
(void) c;
|
154
|
+
|
155
|
+
// Left is active.
|
156
|
+
if (active < 0) {
|
157
|
+
// If right < left, then this is a change of direction.
|
158
|
+
if (lsubr > 0) {
|
159
|
+
// Grow right.
|
160
|
+
r.increment();
|
161
|
+
// The invariant is that left and right are always less than
|
162
|
+
// one step apart. Stepping either toward the other changes
|
163
|
+
// sign of lsubr, or makes it nonzero.
|
164
|
+
OINKY_ASSERT((r.at_end()) || (c(l.i(),r.i()) < 0));
|
165
|
+
}
|
166
|
+
l.increment();
|
167
|
+
} else if (active > 0) {
|
168
|
+
// If right > left, then this is a change of direction.
|
169
|
+
if (lsubr < 0) {
|
170
|
+
// Grow left to beyond right.
|
171
|
+
l.increment();
|
172
|
+
OINKY_ASSERT((l.at_end()) || (c(l.i(),r.i()) > 0));
|
173
|
+
}
|
174
|
+
r.increment();
|
175
|
+
} else {
|
176
|
+
// Increment both.
|
177
|
+
l.increment();
|
178
|
+
r.increment();
|
179
|
+
}
|
180
|
+
// Make the smallest iterator active.
|
181
|
+
set_fwd_state();
|
182
|
+
}
|
183
|
+
void advance(int n) {
|
184
|
+
while (n>0) {
|
185
|
+
increment();
|
186
|
+
--n;
|
187
|
+
}
|
188
|
+
while (n<0) {
|
189
|
+
decrement();
|
190
|
+
++n;
|
191
|
+
}
|
192
|
+
}
|
193
|
+
|
194
|
+
void decrement()
|
195
|
+
{
|
196
|
+
COMPARE c;
|
197
|
+
(void) c;
|
198
|
+
|
199
|
+
// Left is active.
|
200
|
+
if (active < 0) {
|
201
|
+
// If right > left, then this is a change of direction.
|
202
|
+
if (lsubr < 0) {
|
203
|
+
// Decrement right until it's less than left;
|
204
|
+
r.decrement();
|
205
|
+
// Having decremented, we know r.i != r.end or r.sub_begin.
|
206
|
+
// Also, if the iterators were separated by less than one
|
207
|
+
// position, then right should now be < left.
|
208
|
+
OINKY_ASSERT(r.before_begin() || (c(l.i(),r.i()) > 0));
|
209
|
+
}
|
210
|
+
// Now decrement left.
|
211
|
+
l.decrement();
|
212
|
+
} else if (active > 0) {
|
213
|
+
// If right < left, then this is a change of direction.
|
214
|
+
if (lsubr > 0) {
|
215
|
+
// Decrement left until it's less than right.
|
216
|
+
l.decrement();
|
217
|
+
OINKY_ASSERT(l.before_begin() || (c(l.i(),r.i()) < 0));
|
218
|
+
}
|
219
|
+
// Now decrement right.
|
220
|
+
r.decrement();
|
221
|
+
} else {
|
222
|
+
// Decrement both.
|
223
|
+
l.decrement();
|
224
|
+
r.decrement();
|
225
|
+
}
|
226
|
+
// Make the largest iterator active.
|
227
|
+
set_reverse_state();
|
228
|
+
}
|
229
|
+
|
230
|
+
// This is nontrivial, since the iterators may be traveling different
|
231
|
+
// directions. They may thus be equal (referring to the same element)
|
232
|
+
// but have one of the the component iterators being different.
|
233
|
+
bool equal(const this_t &other) const { return compare_to(_equal(), other); }
|
234
|
+
|
235
|
+
public:
|
236
|
+
const RANGE1 &left() const { return l; }
|
237
|
+
const RANGE2 &right() const { return r; }
|
238
|
+
|
239
|
+
// Remember, it's possible that BOTH left and right will be simultaneously active.
|
240
|
+
bool left_active() const { return (active <= 0) && l.is_valid(); }
|
241
|
+
bool right_active() const { return (active >= 0) && r.is_valid(); }
|
242
|
+
|
243
|
+
bool at_end() const { return l.at_end() && r.at_end(); }
|
244
|
+
bool before_begin() const { return l.before_begin() && r.before_begin(); }
|
245
|
+
|
246
|
+
template<typename FN>
|
247
|
+
bool compare_to(const FN &fn, const this_t &other) const {
|
248
|
+
int x = compare_to(other);
|
249
|
+
return fn(x,0);
|
250
|
+
}
|
251
|
+
|
252
|
+
int compare_to(const this_t &other) const {
|
253
|
+
COMPARE c;
|
254
|
+
|
255
|
+
int stat =
|
256
|
+
(left_active() ? 8 : 0) |
|
257
|
+
(right_active() ? 4 : 0) |
|
258
|
+
(other.left_active() ? 2 : 0) |
|
259
|
+
(other.right_active() ? 1 : 0);
|
260
|
+
switch (stat) {
|
261
|
+
case 0x0 + 0x0 :
|
262
|
+
// Both at end.
|
263
|
+
return 0;
|
264
|
+
case 0x8 + 0x0:
|
265
|
+
case 0x4 + 0x0:
|
266
|
+
case 0xc + 0x0:
|
267
|
+
// other at end. This not.
|
268
|
+
return -1;
|
269
|
+
case 0x0 + 0x2:
|
270
|
+
case 0x0 + 0x1:
|
271
|
+
case 0x0 + 0x3:
|
272
|
+
// this at end. Other not.
|
273
|
+
return 1;
|
274
|
+
case 0x8 + 0x2:
|
275
|
+
case 0xc + 0x2:
|
276
|
+
case 0x8 + 0x3:
|
277
|
+
case 0xc + 0x3:
|
278
|
+
// Left active on both.
|
279
|
+
return c(l.i(), other.l.i());
|
280
|
+
case 0x4 + 0x1:
|
281
|
+
case 0xc + 0x1:
|
282
|
+
case 0x4 + 0x3:
|
283
|
+
//case 0xc + 0x3: dup
|
284
|
+
// Right active on both.
|
285
|
+
return c(r.i(), other.r.i());
|
286
|
+
case 0x8 + 0x1:
|
287
|
+
// This left. Other right.
|
288
|
+
return c(l.i(), other.r.i());
|
289
|
+
case 0x4 + 0x2:
|
290
|
+
// This right. Other left.
|
291
|
+
return c(r.i(), other.l.i());
|
292
|
+
}
|
293
|
+
OINKY_ASSERT(false);
|
294
|
+
throw_error(implementation_bug());
|
295
|
+
return 0;
|
296
|
+
}
|
297
|
+
|
298
|
+
/* This is slow, and not required of bidirectional iterators, so we don't
|
299
|
+
implement it.
|
300
|
+
int distance_to(const this_t &other) const {
|
301
|
+
// This works, but is not efficient, and should not be used.
|
302
|
+
OINKY_ASSERT(false);
|
303
|
+
throw_error(unsupported_operation());
|
304
|
+
|
305
|
+
if (other < *this) {
|
306
|
+
return -other.distance_to(*this);
|
307
|
+
}
|
308
|
+
|
309
|
+
this_t tmp = *this;
|
310
|
+
int count = 0;
|
311
|
+
while (tmp < other) {
|
312
|
+
++tmp;
|
313
|
+
++count;
|
314
|
+
}
|
315
|
+
return count;
|
316
|
+
}*/
|
317
|
+
|
318
|
+
private:
|
319
|
+
struct _equal {template<typename T> bool operator()(const T&l, const T&r) const {return l == r;}};
|
320
|
+
|
321
|
+
struct _less {template<typename T> bool operator()(const T&l, const T&r) const {return l < r;}};
|
322
|
+
struct _gt {template<typename T> bool operator()(const T&l, const T&r) const {return l > r;}};
|
323
|
+
struct _leq {template<typename T> bool operator()(const T&l, const T&r) const {return l <= r;}};
|
324
|
+
struct _geq {template<typename T> bool operator()(const T&l, const T&r) const {return l >= r;}};
|
325
|
+
|
326
|
+
public:
|
327
|
+
bool operator<(const this_t &other) const {
|
328
|
+
return compare_to(_less(), other);
|
329
|
+
}
|
330
|
+
bool operator>(const this_t &other) const {
|
331
|
+
return compare_to(_gt(), other);
|
332
|
+
}
|
333
|
+
bool operator<=(const this_t &other) const {
|
334
|
+
return compare_to(_leq(), other);
|
335
|
+
}
|
336
|
+
bool operator>=(const this_t &other) const {
|
337
|
+
return compare_to(_geq(), other);
|
338
|
+
}
|
339
|
+
};
|
340
|
+
|
341
|
+
|
342
|
+
template<typename ITR1, typename ITR2, typename COMPARE>
|
343
|
+
class merge_iterator : public merge_iterator_template<
|
344
|
+
merge_iterator<ITR1, ITR2, COMPARE>,
|
345
|
+
typename boost::iterator_value<ITR1>::type,
|
346
|
+
symmetric_iterator<ITR1>,
|
347
|
+
symmetric_iterator<ITR2>,
|
348
|
+
COMPARE
|
349
|
+
>
|
350
|
+
{
|
351
|
+
typedef symmetric_iterator<ITR1> range1_t;
|
352
|
+
typedef symmetric_iterator<ITR2> range2_t;
|
353
|
+
|
354
|
+
typedef merge_iterator_template<
|
355
|
+
merge_iterator<ITR1, ITR2, COMPARE>,
|
356
|
+
typename boost::iterator_value<ITR1>::type,
|
357
|
+
range1_t,
|
358
|
+
range2_t,
|
359
|
+
COMPARE
|
360
|
+
> base_t;
|
361
|
+
public:
|
362
|
+
merge_iterator() {}
|
363
|
+
|
364
|
+
merge_iterator(
|
365
|
+
const range1_t &_l,
|
366
|
+
const range2_t &_r) :
|
367
|
+
base_t(_l,_r)
|
368
|
+
{}
|
369
|
+
|
370
|
+
merge_iterator(
|
371
|
+
const ITR1 &_li, const ITR1 &_lb, const ITR1 &_le,
|
372
|
+
const ITR2 &_ri, const ITR2 &_rb, const ITR2 &_re) :
|
373
|
+
base_t(range1_t(_li, _lb, _le),range2_t(_ri, _rb, _re))
|
374
|
+
{}
|
375
|
+
|
376
|
+
BOOST_STATIC_ASSERT((boost::is_same<
|
377
|
+
typename boost::iterator_value<ITR1>::type,
|
378
|
+
typename boost::iterator_value<ITR2>::type>::value));
|
379
|
+
|
380
|
+
typedef typename boost::iterator_value<ITR1>::type value_type;
|
381
|
+
|
382
|
+
const ITR1 &left() const { return base_t::left().i(); }
|
383
|
+
const ITR2 &right() const { return base_t::right().i(); }
|
384
|
+
|
385
|
+
private:
|
386
|
+
value_type dereference() const {
|
387
|
+
if (base_t::left_active()) {
|
388
|
+
return *left();
|
389
|
+
} else if (base_t::right_active()) {
|
390
|
+
return *right();
|
391
|
+
}
|
392
|
+
|
393
|
+
// Dereferencing an iterator at the end.
|
394
|
+
OINKY_ASSERT(false);
|
395
|
+
return value_type();
|
396
|
+
}
|
397
|
+
|
398
|
+
friend class boost::iterator_core_access;
|
399
|
+
};
|
400
|
+
|
401
|
+
} //namespace Utils
|
402
|
+
} //namespace Oinky
|
403
|
+
|
@@ -0,0 +1,110 @@
|
|
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
|
+
// These are some support classes for the code generated by the model compiler,
|
7
|
+
// which is still experimental.
|
8
|
+
//
|
9
|
+
// Everything in this file is subject to change.
|
10
|
+
|
11
|
+
namespace Oinky
|
12
|
+
{
|
13
|
+
namespace Model
|
14
|
+
{
|
15
|
+
typedef db_t::column_selector_t column_selector_t;
|
16
|
+
typedef db_t::index_handle index_handle;
|
17
|
+
typedef db_t::table_handle table_handle;
|
18
|
+
|
19
|
+
class TableBase
|
20
|
+
{
|
21
|
+
public:
|
22
|
+
static void create_column_if(
|
23
|
+
table_handle &th,
|
24
|
+
const char *colname,
|
25
|
+
column_type_code_t coltype,
|
26
|
+
const variant_cv_t &default_value)
|
27
|
+
{
|
28
|
+
db_t::column_itr i = th.columns().find(colname);
|
29
|
+
if (i == th.columns().end()) {
|
30
|
+
i = th.columns().create(colname, coltype, default_value);
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
static void create_index_if(
|
35
|
+
table_handle &th,
|
36
|
+
const db_string &ixname,
|
37
|
+
bool is_unique,
|
38
|
+
const index_column_def *cols_begin,
|
39
|
+
const index_column_def *cols_end)
|
40
|
+
{
|
41
|
+
db_t::index_itr i = th.indices().find(ixname);
|
42
|
+
if (i == th.indices().end()) {
|
43
|
+
i = th.indices().create(ixname, is_unique ? Oinky::Unique : Oinky::NonUnique, cols_begin, cols_end);
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
static index_handle get_index_handle(table_handle &th, const char *indexname) {
|
48
|
+
return th.indices()[indexname];
|
49
|
+
}
|
50
|
+
|
51
|
+
protected:
|
52
|
+
table_handle th;
|
53
|
+
db_t &__db;
|
54
|
+
|
55
|
+
public:
|
56
|
+
db_t &db() const { return __db; }
|
57
|
+
|
58
|
+
void check_init_row_accessor(column_selector_t &cs, const char **cn_begin, const char **cn_end)
|
59
|
+
{
|
60
|
+
// Column count will be zero if we haven't initialized yet. But
|
61
|
+
// we must also account for the fact that selectors are invalidated
|
62
|
+
// by schema changes to the table.
|
63
|
+
if ((cs.column_count() == 0) ||
|
64
|
+
(th.last_colset_change() != cs.last_colset_change()))
|
65
|
+
{
|
66
|
+
// This will raise an exception if one of our columns has been
|
67
|
+
// deleted since we mounted.
|
68
|
+
cs = th.make_selector(cn_begin, cn_end);
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
TableBase(const table_handle &_th, db_t &_db) : th(_th), __db(_db)
|
73
|
+
{}
|
74
|
+
};
|
75
|
+
|
76
|
+
class SchemaBase
|
77
|
+
{
|
78
|
+
db_t &db;
|
79
|
+
|
80
|
+
public:
|
81
|
+
static table_handle get_table_handle(db_t &db, const char *tablename) {
|
82
|
+
return *db.tables().create_if(tablename).first;
|
83
|
+
}
|
84
|
+
|
85
|
+
// Variable part of table-schema update. We invoke the callback when we
|
86
|
+
// detect something wrong, which is a defensive tactic.
|
87
|
+
typedef void (*schema_cb_fn)(db_t &db);
|
88
|
+
|
89
|
+
protected:
|
90
|
+
void up_schema(schema_cb_fn schema_cb)
|
91
|
+
{
|
92
|
+
// Ensure the schema migration is atomic.
|
93
|
+
sp_marker_t lkg = db.new_sp();
|
94
|
+
try {
|
95
|
+
(*schema_cb)(db);
|
96
|
+
} catch (...) {
|
97
|
+
db.sp_rollback(lkg);
|
98
|
+
// Re-throw.
|
99
|
+
throw;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
public:
|
104
|
+
SchemaBase(db_t &_db, schema_cb_fn update_schema) : db(_db) {
|
105
|
+
(*update_schema)(db);
|
106
|
+
}
|
107
|
+
};
|
108
|
+
|
109
|
+
} //namespace Model
|
110
|
+
} //namespace Oinky
|