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,808 @@
|
|
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
|
+
using namespace Oinky::Errors;
|
9
|
+
|
10
|
+
// Simulate an enum-class.
|
11
|
+
namespace value_types
|
12
|
+
{
|
13
|
+
//
|
14
|
+
// Simplified set of SQL value types. int/date/string
|
15
|
+
//
|
16
|
+
enum value_type_code_t
|
17
|
+
{
|
18
|
+
// These are always sorted as integers.
|
19
|
+
Bit = 1,
|
20
|
+
Int8 = 2,
|
21
|
+
Int16 = 3,
|
22
|
+
Int32 = 4,
|
23
|
+
Int64 = 5,
|
24
|
+
Datetime = 6,
|
25
|
+
// strings are always lexically sorted unsigned bytes. They are stored
|
26
|
+
// in the string table, not in the table itself.
|
27
|
+
String = 7,
|
28
|
+
// more inline types
|
29
|
+
Uint8 = 8,
|
30
|
+
Uint16 = 9,
|
31
|
+
Uint32 = 10,
|
32
|
+
Uint64 = 11,
|
33
|
+
Float32 = 12,
|
34
|
+
Float64 = 13,
|
35
|
+
};
|
36
|
+
} //namespace value_types
|
37
|
+
|
38
|
+
#define OINKY_VALUE_TYPE_MAX 13
|
39
|
+
|
40
|
+
namespace column_types
|
41
|
+
{
|
42
|
+
//
|
43
|
+
// This is a superset of the value_type. It additionally defines the
|
44
|
+
// Variant type.
|
45
|
+
//
|
46
|
+
// A value must specify a type, but a column (family of values) need not.
|
47
|
+
// Variant column-type indicates that each row specifies its own type.
|
48
|
+
enum column_type_code_t
|
49
|
+
{
|
50
|
+
Variant = 0,
|
51
|
+
|
52
|
+
// The rest are identical to value_type_code
|
53
|
+
Bit = 1,
|
54
|
+
Int8 = 2,
|
55
|
+
Int16 = 3,
|
56
|
+
Int32 = 4,
|
57
|
+
Int64 = 5,
|
58
|
+
Datetime = 6,
|
59
|
+
String = 7,
|
60
|
+
// more inline types
|
61
|
+
Uint8 = 8,
|
62
|
+
Uint16 = 9,
|
63
|
+
Uint32 = 10,
|
64
|
+
Uint64 = 11,
|
65
|
+
Float32 = 12,
|
66
|
+
Float64 = 13,
|
67
|
+
};
|
68
|
+
} //namespace column_types
|
69
|
+
|
70
|
+
typedef value_types::value_type_code_t value_type_code_t;
|
71
|
+
typedef column_types::column_type_code_t column_type_code_t;
|
72
|
+
|
73
|
+
struct datetime_t
|
74
|
+
{
|
75
|
+
// All times are stored in UTC format.
|
76
|
+
//
|
77
|
+
// map of bits. Y-(year-1900). M-(month-1). D-(day-1). h-hour, m-minute, s-second, u-microsecond
|
78
|
+
// YYYY-YYYY-YYYY-YYYY-YYMM-MMDD-DDDh-hhhh-
|
79
|
+
// mmmm-mmss-ssss-uuuu-uuuu-uuuu-uuuu-uuuu
|
80
|
+
//
|
81
|
+
enum __constants {
|
82
|
+
yearshift = 46,
|
83
|
+
monthshift = 42,
|
84
|
+
dayshift = 37,
|
85
|
+
hourshift = 32,
|
86
|
+
minuteshift = 26,
|
87
|
+
secondshift = 20,
|
88
|
+
usecshift = 0
|
89
|
+
};
|
90
|
+
// Each of the above fields stores more values than are required. Thus, the
|
91
|
+
// representation is sparse. We can't really do equality
|
92
|
+
// testing without adding a lot of date logic and assumptions about
|
93
|
+
// calendars. Was there a year 0, etc? Was the year a leap-year?
|
94
|
+
// were there leap-seconds.
|
95
|
+
//
|
96
|
+
// The above storage is absolute in nature as opposed to relative for that
|
97
|
+
// reason. Errors (in computation of leap-years/seconds) do not accrue.
|
98
|
+
// We do not store the number of days since january 1 1900. We store the
|
99
|
+
// number of years, then months, then days, then hours, etc.
|
100
|
+
//
|
101
|
+
// In general, getting consistent representations of all dates still
|
102
|
+
// requires consistent datetime implementations, but with this scheme, even
|
103
|
+
// different datetime packages will produce results that are close.
|
104
|
+
//
|
105
|
+
int64 _val;
|
106
|
+
|
107
|
+
int32 years_since_1900() const { return _val >> yearshift; }
|
108
|
+
uint16 months_since_january() const { return (_val >> monthshift) & 0xf; }
|
109
|
+
uint8 days_since_first_of_month() const { return (_val >> dayshift) & 0x1f; }
|
110
|
+
uint8 hours_since_midnight() const { return (_val >> hourshift) & 0x1f; }
|
111
|
+
uint8 minutes_since_hour() const { return (_val >> minuteshift) & 0x3f; }
|
112
|
+
uint8 seconds_since_minute() const { return (_val >> secondshift) & 0x3f; }
|
113
|
+
uint32 microseconds_since_second() const { return (_val >> usecshift) & 0xfffff; }
|
114
|
+
|
115
|
+
uint32 seconds_since_midnight() const {
|
116
|
+
return (3600 * hours_since_midnight()) +
|
117
|
+
(60 * minutes_since_hour()) +
|
118
|
+
seconds_since_minute();
|
119
|
+
}
|
120
|
+
uint64 microseconds_since_midnight() const {
|
121
|
+
return (1000000ull * seconds_since_midnight()) +
|
122
|
+
microseconds_since_second();
|
123
|
+
}
|
124
|
+
|
125
|
+
// NOTE: See above about sparseness of representation. Thus, we are not
|
126
|
+
// comparing dates. We are comparing representations of dates. When these
|
127
|
+
// representations are ordered, and then coverted into real dates, the
|
128
|
+
// resulting dates may no longer be identically ordered.
|
129
|
+
bool operator<(const datetime_t &other) const { return _val < other._val; }
|
130
|
+
bool operator<=(const datetime_t &other) const { return _val <= other._val; }
|
131
|
+
bool operator==(const datetime_t &other) const { return _val == other._val; }
|
132
|
+
bool operator!=(const datetime_t &other) const { return _val != other._val; }
|
133
|
+
bool operator>(const datetime_t &other) const { return _val > other._val; }
|
134
|
+
bool operator>=(const datetime_t &other) const { return _val >= other._val; }
|
135
|
+
|
136
|
+
datetime_t() : _val(0) {}
|
137
|
+
|
138
|
+
static datetime_t compose(
|
139
|
+
int years_since_1900,
|
140
|
+
uint8 months_since_january,
|
141
|
+
uint8 days_since_first_of_month,
|
142
|
+
uint8 hours_since_midnight = 0,
|
143
|
+
uint8 minutes_since_hour = 0,
|
144
|
+
uint8 seconds_since_minute = 0,
|
145
|
+
uint32 microseconds_since_second = 0)
|
146
|
+
{
|
147
|
+
// This still permits some odd dates. We can't be perfect without
|
148
|
+
// knowing everything about leap-years and even leap-seconds, and
|
149
|
+
// the leap-seconds throws some things in about is the max hours
|
150
|
+
// 23 or 24, seconds 59 or 60, etc.
|
151
|
+
//
|
152
|
+
// So why do this test at all if we can't do it right? Because we want
|
153
|
+
// to ensure that we don't have bit overflows between fields. That's
|
154
|
+
// much worse than having a funky date that is still *valid*.
|
155
|
+
if ((months_since_january > 11) ||
|
156
|
+
(days_since_first_of_month > 31) ||
|
157
|
+
(hours_since_midnight > 24) ||
|
158
|
+
(minutes_since_hour > 63) ||
|
159
|
+
(seconds_since_minute > 63))
|
160
|
+
{
|
161
|
+
throw_error(invalid_argument());
|
162
|
+
}
|
163
|
+
|
164
|
+
datetime_t dt;
|
165
|
+
dt._val =
|
166
|
+
(((int64) years_since_1900) << yearshift) +
|
167
|
+
(((int64) months_since_january) << monthshift) +
|
168
|
+
(((int64) days_since_first_of_month) << dayshift) +
|
169
|
+
(((int64) hours_since_midnight) << hourshift) +
|
170
|
+
(((int64) minutes_since_hour) << minuteshift) +
|
171
|
+
(((int64) seconds_since_minute) << secondshift) +
|
172
|
+
(((int64) microseconds_since_second) << usecshift);
|
173
|
+
return dt;
|
174
|
+
}
|
175
|
+
|
176
|
+
static datetime_t now() {
|
177
|
+
boost::posix_time::ptime dt = boost::posix_time::microsec_clock::universal_time();
|
178
|
+
return from_boost_ptime(dt);
|
179
|
+
}
|
180
|
+
|
181
|
+
static datetime_t from_boost_ptime(boost::posix_time::ptime dt) {
|
182
|
+
struct tm tmval = boost::posix_time::to_tm(dt);
|
183
|
+
|
184
|
+
// This interface seems very silly to me.
|
185
|
+
boost::posix_time::time_duration duration( dt.time_of_day() );
|
186
|
+
uint64 usec = duration.total_microseconds();
|
187
|
+
|
188
|
+
enum _const_vals {
|
189
|
+
seconds = 1000000,
|
190
|
+
minutes = 60 * seconds
|
191
|
+
};
|
192
|
+
return compose(
|
193
|
+
(int) tmval.tm_year,
|
194
|
+
tmval.tm_mon,
|
195
|
+
tmval.tm_mday - 1,
|
196
|
+
usec / (60ull * minutes),
|
197
|
+
(usec % (60ull * minutes)) / minutes,
|
198
|
+
(usec % minutes) / seconds,
|
199
|
+
usec % seconds);
|
200
|
+
}
|
201
|
+
|
202
|
+
boost::posix_time::ptime to_boost_ptime() const {
|
203
|
+
boost::posix_time::ptime base(boost::gregorian::date(1900 * years_since_1900(), boost::date_time::Jan + months_since_january(), 1 + days_since_first_of_month()));
|
204
|
+
base += boost::posix_time::microseconds(microseconds_since_midnight());
|
205
|
+
return base;
|
206
|
+
}
|
207
|
+
};
|
208
|
+
|
209
|
+
BOOST_STATIC_ASSERT(sizeof(datetime_t) == 8);
|
210
|
+
|
211
|
+
namespace Internal
|
212
|
+
{
|
213
|
+
|
214
|
+
// Forward declare this for friendship.
|
215
|
+
template<class REF_T> class stringtable_t;
|
216
|
+
template<typename REF_T> class u_string_value_safe;
|
217
|
+
class safe_cv_t;
|
218
|
+
|
219
|
+
}
|
220
|
+
|
221
|
+
// This describes a string. The lifetime of the string is equivalent to that
|
222
|
+
// of the database. If the database is destroyed or remounted, the string
|
223
|
+
// becomes invalid. The memory backing it may be deallocated or rewritten.
|
224
|
+
//
|
225
|
+
// All string-sharing with the user is via this type, whether for column-values
|
226
|
+
// or metastrings.
|
227
|
+
//
|
228
|
+
// When the used passes this object to a method, it is the caller's
|
229
|
+
// responsibility to ensure the bytes remain valid until the method returns.
|
230
|
+
class db_string
|
231
|
+
{
|
232
|
+
const char *bytes;
|
233
|
+
uint32 _length;
|
234
|
+
// Note this is agnostic to the table. It could refer to the string
|
235
|
+
// table or metatable. We never trust it.
|
236
|
+
uint32 xref;
|
237
|
+
|
238
|
+
template<typename REF_T> friend class ::Oinky::Internal::u_string_value_safe;
|
239
|
+
template<typename REF_T> friend class ::Oinky::Internal::stringtable_t;
|
240
|
+
friend class ::Oinky::Internal::safe_cv_t;
|
241
|
+
|
242
|
+
public:
|
243
|
+
|
244
|
+
uint32 length() const { return _length; }
|
245
|
+
const char *begin() const { return bytes; }
|
246
|
+
const char *end() const { return bytes + _length; }
|
247
|
+
|
248
|
+
db_string() : bytes(NULL), _length(0), xref(0) {}
|
249
|
+
db_string(const char *str) : bytes(str), _length(strlen(str)), xref(0) {}
|
250
|
+
db_string(const char *str, uint32 __length) : bytes(str), _length(__length), xref(0) {}
|
251
|
+
|
252
|
+
// Metastrings must not contain NULLs. Other than that, all characters
|
253
|
+
// are supported as table/column/index names in sqlite if they are
|
254
|
+
// properly bracketed, so we follow the same pattern.
|
255
|
+
//
|
256
|
+
// The following is valid in sqlite:
|
257
|
+
// "select * from [This should-be a_valid.table+name!?]"
|
258
|
+
bool validate_metastring() const {
|
259
|
+
return strnlen(bytes, _length) == _length;
|
260
|
+
}
|
261
|
+
|
262
|
+
// We can't safely use the xref value to compare strings, because we don't
|
263
|
+
// know where it came from. The two strings could be from distinct
|
264
|
+
// database instances.
|
265
|
+
int compare_to(const db_string &other) const {
|
266
|
+
// Must use memcmp not strncmp. Remember these are arbitrary indirect
|
267
|
+
// byte strings, not null-terminated c-strings.
|
268
|
+
int k;
|
269
|
+
if (_length < other._length) {
|
270
|
+
k = memcmp(bytes, other.bytes, _length);
|
271
|
+
if (k == 0) return -1;
|
272
|
+
} else if (_length > other._length) {
|
273
|
+
k = memcmp(bytes, other.bytes, other._length);
|
274
|
+
if (k == 0) return 1;
|
275
|
+
} else {
|
276
|
+
k = memcmp(bytes, other.bytes, _length);
|
277
|
+
}
|
278
|
+
if (k < 0) return -1;
|
279
|
+
if (k > 0) return 1;
|
280
|
+
return 0;
|
281
|
+
}
|
282
|
+
|
283
|
+
template<typename T>
|
284
|
+
inline int compare_to(const T &other) const {
|
285
|
+
return -other.compare_to(*this);
|
286
|
+
}
|
287
|
+
|
288
|
+
template<typename T>
|
289
|
+
bool operator==(const T &other) const { return compare_to(other) == 0; }
|
290
|
+
template<typename T>
|
291
|
+
bool operator!=(const T &other) const { return compare_to(other) != 0; }
|
292
|
+
template<typename T>
|
293
|
+
bool operator<(const T &other) const { return compare_to(other) < 0; }
|
294
|
+
template<typename T>
|
295
|
+
bool operator<=(const T &other) const { return compare_to(other) <= 0; }
|
296
|
+
template<typename T>
|
297
|
+
bool operator>(const T &other) const { return compare_to(other) > 0; }
|
298
|
+
template<typename T>
|
299
|
+
bool operator>=(const T &other) const { return compare_to(other) >= 0; }
|
300
|
+
};
|
301
|
+
|
302
|
+
namespace Internal
|
303
|
+
{
|
304
|
+
// This is the public variant column value type. It represents the column
|
305
|
+
// value according to its internal encoding.
|
306
|
+
class column_value_payload
|
307
|
+
{
|
308
|
+
enum value_payload_size_ec {
|
309
|
+
value_payload_size = MAX(sizeof(db_string), MAX(sizeof(uint64),sizeof(datetime_t)))
|
310
|
+
};
|
311
|
+
char bytes[value_payload_size];
|
312
|
+
BOOST_STATIC_ASSERT(value_payload_size >= sizeof(db_string));
|
313
|
+
BOOST_STATIC_ASSERT(value_payload_size >= sizeof(uint64));
|
314
|
+
BOOST_STATIC_ASSERT(value_payload_size >= sizeof(datetime_t));
|
315
|
+
|
316
|
+
public:
|
317
|
+
column_value_payload() {}
|
318
|
+
|
319
|
+
column_value_payload(bool x) {
|
320
|
+
bit_value() = x;
|
321
|
+
}
|
322
|
+
column_value_payload(int64 x) {
|
323
|
+
int_value() = x;
|
324
|
+
}
|
325
|
+
column_value_payload(uint64 x) {
|
326
|
+
uint_value() = x;
|
327
|
+
}
|
328
|
+
column_value_payload(float32 x) {
|
329
|
+
f32_value() = x;
|
330
|
+
}
|
331
|
+
column_value_payload(float64 x) {
|
332
|
+
f64_value() = x;
|
333
|
+
}
|
334
|
+
column_value_payload(const db_string &x) {
|
335
|
+
string_value() = x;
|
336
|
+
}
|
337
|
+
column_value_payload(const datetime_t &x) {
|
338
|
+
dt_value() = x;
|
339
|
+
}
|
340
|
+
|
341
|
+
column_value_payload &operator=(const column_value_payload &x) {
|
342
|
+
if (this == &x) return *this;
|
343
|
+
memcpy(bytes, x.bytes, value_payload_size);
|
344
|
+
return *this;
|
345
|
+
}
|
346
|
+
|
347
|
+
template<typename T>
|
348
|
+
column_value_payload &operator=(const T &x) {
|
349
|
+
if (this == (const column_value_payload *) &x) return *this;
|
350
|
+
// If there isn't a constructor for this, then it
|
351
|
+
// can't be assigned.
|
352
|
+
*this = column_value_payload(x);
|
353
|
+
return *this;
|
354
|
+
}
|
355
|
+
|
356
|
+
bool &bit_value() { return *(bool *)bytes; }
|
357
|
+
bool bit_value() const { return *(const bool *)bytes; }
|
358
|
+
|
359
|
+
db_string &string_value() { return *(db_string *)bytes; }
|
360
|
+
const db_string &string_value() const { return *(const db_string *)bytes; }
|
361
|
+
|
362
|
+
int64 &int_value() { return *(int64 *)bytes; }
|
363
|
+
const int64 &int_value() const { return *(const int64 *)bytes; }
|
364
|
+
|
365
|
+
uint64 &uint_value() { return *(uint64 *)bytes; }
|
366
|
+
const uint64 &uint_value() const { return *(const uint64 *)bytes; }
|
367
|
+
|
368
|
+
float64 &f64_value() { return *(float64 *)bytes; }
|
369
|
+
const float64 &f64_value() const { return *(const float64 *)bytes; }
|
370
|
+
|
371
|
+
float32 &f32_value() { return *(float32 *)bytes; }
|
372
|
+
const float32 &f32_value() const { return *(const float32 *)bytes; }
|
373
|
+
|
374
|
+
datetime_t &dt_value() { return *(datetime_t *)bytes; }
|
375
|
+
const datetime_t &dt_value() const { return *(const datetime_t *)bytes; }
|
376
|
+
|
377
|
+
int compare_to(value_type_code_t type, const column_value_payload &right) const
|
378
|
+
{
|
379
|
+
switch (type) {
|
380
|
+
// These are always sorted as integers.
|
381
|
+
case value_types::Bit :
|
382
|
+
if (bit_value() && !right.bit_value()) return 1;
|
383
|
+
if (!bit_value() && right.bit_value()) return -1;
|
384
|
+
return 0;
|
385
|
+
case value_types::Int8 :
|
386
|
+
case value_types::Int16 :
|
387
|
+
case value_types::Int32 :
|
388
|
+
case value_types::Int64 :
|
389
|
+
if (int_value() < right.int_value()) return -1;
|
390
|
+
if (int_value() == right.int_value()) return 0;
|
391
|
+
return 1;
|
392
|
+
case value_types::Uint8 :
|
393
|
+
case value_types::Uint16 :
|
394
|
+
case value_types::Uint32 :
|
395
|
+
case value_types::Uint64 :
|
396
|
+
if (uint_value() < right.uint_value()) return -1;
|
397
|
+
if (uint_value() == right.uint_value()) return 0;
|
398
|
+
return 1;
|
399
|
+
case value_types::Float32 :
|
400
|
+
if (f32_value() < right.f32_value()) return -1;
|
401
|
+
if (f32_value() == right.f32_value()) return 0;
|
402
|
+
return 1;
|
403
|
+
case value_types::Float64 :
|
404
|
+
if (f64_value() < right.f64_value()) return -1;
|
405
|
+
if (f64_value() == right.f64_value()) return 0;
|
406
|
+
return 1;
|
407
|
+
case value_types::Datetime :
|
408
|
+
if (dt_value() < right.dt_value()) return -1;
|
409
|
+
if (dt_value() == right.dt_value()) return 0;
|
410
|
+
return 1;
|
411
|
+
case value_types::String :
|
412
|
+
return string_value().compare_to(right.string_value());
|
413
|
+
}
|
414
|
+
throw_error(bad_encoding());
|
415
|
+
//please the compiler.
|
416
|
+
return 0;
|
417
|
+
}
|
418
|
+
};
|
419
|
+
}
|
420
|
+
|
421
|
+
// This is a user-visible column value type. It's not quite what we store
|
422
|
+
// internally, since internally we know the value-type from the column
|
423
|
+
// definition.
|
424
|
+
class variant_cv_t : public with_compare_ops<variant_cv_t>
|
425
|
+
{
|
426
|
+
value_type_code_t _type;
|
427
|
+
Internal::column_value_payload _value;
|
428
|
+
|
429
|
+
public:
|
430
|
+
variant_cv_t() : _type(value_types::Int8), _value((int64)0) {}
|
431
|
+
|
432
|
+
variant_cv_t(int64 v) : _type(value_types::Int64), _value((int64)v) {}
|
433
|
+
variant_cv_t(int32 v) : _type(value_types::Int32), _value((int64)v) {}
|
434
|
+
variant_cv_t(int16 v) : _type(value_types::Int16), _value((int64)v) {}
|
435
|
+
variant_cv_t(int8 v) : _type(value_types::Int8), _value((int64)v) {}
|
436
|
+
variant_cv_t(uint64 v) : _type(value_types::Uint64), _value((uint64)v) {}
|
437
|
+
variant_cv_t(uint32 v) : _type(value_types::Uint32), _value((uint64)v) {}
|
438
|
+
variant_cv_t(uint16 v) : _type(value_types::Uint16), _value((uint64)v) {}
|
439
|
+
variant_cv_t(uint8 v) : _type(value_types::Uint8), _value((uint64)v) {}
|
440
|
+
variant_cv_t(float64 v) : _type(value_types::Float64), _value(v) {}
|
441
|
+
variant_cv_t(float32 v) : _type(value_types::Float32), _value(v) {}
|
442
|
+
variant_cv_t(bool v) : _type(value_types::Bit), _value(v) {}
|
443
|
+
variant_cv_t(const db_string &s) : _type(value_types::String), _value(s) {}
|
444
|
+
variant_cv_t(const char *s) : _type(value_types::String), _value(db_string(s)) {}
|
445
|
+
variant_cv_t(const datetime_t &dt) : _type(value_types::Datetime), _value(dt) {}
|
446
|
+
|
447
|
+
template<typename T>
|
448
|
+
variant_cv_t& operator=(const T &v) {
|
449
|
+
if (this != (const variant_cv_t *)&v) {
|
450
|
+
*this = variant_cv_t(v);
|
451
|
+
}
|
452
|
+
return *this;
|
453
|
+
}
|
454
|
+
|
455
|
+
value_type_code_t type() const { return _type; }
|
456
|
+
|
457
|
+
bool is_bit() const { return _type == value_types::Bit; }
|
458
|
+
bool is_int() const { return _type >= value_types::Int8 && _type <= value_types::Int64; }
|
459
|
+
bool is_uint() const { return _type >= value_types::Uint8 && _type <= value_types::Uint64; }
|
460
|
+
bool is_date() const { return _type == value_types::Datetime; }
|
461
|
+
bool is_f32() const { return _type == value_types::Float32; }
|
462
|
+
bool is_f64() const { return _type == value_types::Float64; }
|
463
|
+
bool is_string() const { return _type == value_types::String; }
|
464
|
+
|
465
|
+
bool &bit_value() { return _value.bit_value(); }
|
466
|
+
bool bit_value() const { return _value.bit_value(); }
|
467
|
+
|
468
|
+
db_string &string_value() { return _value.string_value(); }
|
469
|
+
const db_string &string_value() const { return _value.string_value(); }
|
470
|
+
|
471
|
+
datetime_t &dt_value() { return _value.dt_value(); }
|
472
|
+
const datetime_t &dt_value() const { return _value.dt_value(); }
|
473
|
+
|
474
|
+
int64 &int_value() { return _value.int_value(); }
|
475
|
+
const int64 &int_value() const { return _value.int_value(); }
|
476
|
+
|
477
|
+
uint64 &uint_value() { return _value.uint_value(); }
|
478
|
+
const uint64 &uint_value() const { return _value.uint_value(); }
|
479
|
+
|
480
|
+
float64 &f64_value() { return _value.f64_value(); }
|
481
|
+
const float64 &f64_value() const { return _value.f64_value(); }
|
482
|
+
|
483
|
+
float32 &f32_value() { return _value.f32_value(); }
|
484
|
+
const float32 &f32_value() const { return _value.f32_value(); }
|
485
|
+
|
486
|
+
// This is a safer way to dispatch. It guarantees that no possibilities
|
487
|
+
// are excluded...that the switch is complete.
|
488
|
+
template<template<typename T> class FN, typename CTX>
|
489
|
+
void dispatch(CTX ctx) const
|
490
|
+
{
|
491
|
+
switch (_type) {
|
492
|
+
case value_types::Bit :
|
493
|
+
FN<bool>::call(bit_value(), ctx);
|
494
|
+
return;
|
495
|
+
// These are always sorted as integers.
|
496
|
+
case value_types::Int8 :
|
497
|
+
FN<int8>::call((int8) int_value(), ctx);
|
498
|
+
return;
|
499
|
+
case value_types::Int16 :
|
500
|
+
FN<int16>::call((int16) int_value(), ctx);
|
501
|
+
return;
|
502
|
+
case value_types::Int32 :
|
503
|
+
FN<int32>::call((int32) int_value(), ctx);
|
504
|
+
return;
|
505
|
+
case value_types::Int64 :
|
506
|
+
FN<int64>::call(int_value(), ctx);
|
507
|
+
return;
|
508
|
+
// These are always sorted as unsigned integers.
|
509
|
+
case value_types::Uint8 :
|
510
|
+
FN<uint8>::call((uint8) uint_value(), ctx);
|
511
|
+
return;
|
512
|
+
case value_types::Uint16 :
|
513
|
+
FN<uint16>::call((uint16) uint_value(), ctx);
|
514
|
+
return;
|
515
|
+
case value_types::Uint32 :
|
516
|
+
FN<uint32>::call((uint32) uint_value(), ctx);
|
517
|
+
return;
|
518
|
+
case value_types::Uint64 :
|
519
|
+
FN<uint64>::call(uint_value(), ctx);
|
520
|
+
return;
|
521
|
+
|
522
|
+
// floating point
|
523
|
+
case value_types::Float32 :
|
524
|
+
FN<float32>::call(f32_value(), ctx);
|
525
|
+
return;
|
526
|
+
case value_types::Float64 :
|
527
|
+
FN<float64>::call(f64_value(), ctx);
|
528
|
+
return;
|
529
|
+
|
530
|
+
case value_types::Datetime :
|
531
|
+
FN<datetime_t>::call(dt_value(), ctx);
|
532
|
+
return;
|
533
|
+
case value_types::String :
|
534
|
+
FN<db_string>::call(string_value(), ctx);
|
535
|
+
return;
|
536
|
+
}
|
537
|
+
throw_error(bad_encoding());
|
538
|
+
}
|
539
|
+
|
540
|
+
int compare_to(const variant_cv_t &other) const
|
541
|
+
{
|
542
|
+
// most common
|
543
|
+
if (_type == other._type) {
|
544
|
+
return _value.compare_to(_type, other._value);
|
545
|
+
} else if (is_int()) {
|
546
|
+
// They're all stored full-width 64-bit internally, and would
|
547
|
+
// have been truncated to size when they were stored, if smaller.
|
548
|
+
if (other.is_int()) {
|
549
|
+
return _value.compare_to(value_types::Int64, other._value);
|
550
|
+
}
|
551
|
+
if (other.is_uint()) {
|
552
|
+
if (int_value() < 0) return -1;
|
553
|
+
return _value.compare_to(value_types::Uint64, other._value);
|
554
|
+
}
|
555
|
+
} else if (is_uint()) {
|
556
|
+
if (other.is_uint()) {
|
557
|
+
return _value.compare_to(value_types::Uint64, other._value);
|
558
|
+
}
|
559
|
+
if (other.is_int()) {
|
560
|
+
if (other.int_value() < 0) return 1;
|
561
|
+
return _value.compare_to(value_types::Uint64, other._value);
|
562
|
+
}
|
563
|
+
} else if (is_f32()) {
|
564
|
+
if (other.is_f64()) {
|
565
|
+
float64 l = f32_value();
|
566
|
+
float64 r = other.f64_value();
|
567
|
+
if (l < r) {
|
568
|
+
return -1;
|
569
|
+
}
|
570
|
+
if (l == r) {
|
571
|
+
return 0;
|
572
|
+
}
|
573
|
+
return 1;
|
574
|
+
}
|
575
|
+
} else if (is_f64()) {
|
576
|
+
if (other.is_f32()) {
|
577
|
+
float64 l = f64_value();
|
578
|
+
float64 r = other.f32_value();
|
579
|
+
if (l < r) {
|
580
|
+
return -1;
|
581
|
+
}
|
582
|
+
if (l == r) {
|
583
|
+
return 0;
|
584
|
+
}
|
585
|
+
return 1;
|
586
|
+
}
|
587
|
+
}
|
588
|
+
|
589
|
+
// Types are not comparable.
|
590
|
+
throw_error(incomparable_value_types());
|
591
|
+
return 0;
|
592
|
+
}
|
593
|
+
|
594
|
+
private:
|
595
|
+
template<typename INT, value_type_code_t vt>
|
596
|
+
inline void coerce_int()
|
597
|
+
{
|
598
|
+
if (!(is_int() || is_uint())) {
|
599
|
+
throw_error(column_type_mismatch());
|
600
|
+
}
|
601
|
+
_value.int_value() = (INT) _value.int_value();
|
602
|
+
_type = vt;
|
603
|
+
}
|
604
|
+
|
605
|
+
inline void check_throw_type(value_type_code_t vt)
|
606
|
+
{
|
607
|
+
if (_type != vt) {
|
608
|
+
throw_error(column_type_mismatch());
|
609
|
+
}
|
610
|
+
}
|
611
|
+
public:
|
612
|
+
// Alters internal value/type to comply with the specified column_type.
|
613
|
+
// If this is an integer, it may truncate/cast. If it's an incompatible
|
614
|
+
// type, it may throw column_type_mismatch(). If the column_type is
|
615
|
+
// variant, it will no-op.
|
616
|
+
void coerce(column_type_code_t ct) {
|
617
|
+
switch (ct) {
|
618
|
+
case column_types::Variant : return;
|
619
|
+
|
620
|
+
case column_types::Int8 :
|
621
|
+
coerce_int<int8, value_types::Int8>(); return;
|
622
|
+
case column_types::Int16 :
|
623
|
+
coerce_int<int16, value_types::Int16>(); return;
|
624
|
+
case column_types::Int32 :
|
625
|
+
coerce_int<int32, value_types::Int32>(); return;
|
626
|
+
case column_types::Int64 :
|
627
|
+
coerce_int<int64, value_types::Int64>(); return;
|
628
|
+
|
629
|
+
case column_types::Uint8 :
|
630
|
+
coerce_int<uint8, value_types::Uint8>(); return;
|
631
|
+
case column_types::Uint16 :
|
632
|
+
coerce_int<uint16, value_types::Uint16>(); return;
|
633
|
+
case column_types::Uint32 :
|
634
|
+
coerce_int<uint32, value_types::Uint32>(); return;
|
635
|
+
case column_types::Uint64 :
|
636
|
+
coerce_int<uint64, value_types::Uint64>(); return;
|
637
|
+
|
638
|
+
//
|
639
|
+
case column_types::Float32 :
|
640
|
+
if (is_f32()) {
|
641
|
+
return;
|
642
|
+
}
|
643
|
+
if (is_f64()) {
|
644
|
+
f32_value() = (float32) _value.f64_value();
|
645
|
+
_type = value_types::Float32;
|
646
|
+
return;
|
647
|
+
}
|
648
|
+
throw_error(column_type_mismatch());
|
649
|
+
case column_types::Float64 :
|
650
|
+
if (is_f64()) {
|
651
|
+
return;
|
652
|
+
}
|
653
|
+
if (is_f32()) {
|
654
|
+
f64_value() = (float64) _value.f32_value();
|
655
|
+
_type = value_types::Float64;
|
656
|
+
return;
|
657
|
+
}
|
658
|
+
throw_error(column_type_mismatch());
|
659
|
+
|
660
|
+
case column_types::Datetime :
|
661
|
+
// Just type-check. No values to change.
|
662
|
+
check_throw_type(value_types::Datetime);
|
663
|
+
return;
|
664
|
+
case column_types::String :
|
665
|
+
check_throw_type(value_types::String);
|
666
|
+
return;
|
667
|
+
case column_types::Bit :
|
668
|
+
check_throw_type(value_types::Bit);
|
669
|
+
return;
|
670
|
+
}
|
671
|
+
throw_error(bad_encoding());
|
672
|
+
}
|
673
|
+
|
674
|
+
template<typename VALUE>
|
675
|
+
struct formatter
|
676
|
+
{
|
677
|
+
template<typename OSTREAM>
|
678
|
+
static void call(const VALUE &v, OSTREAM *os) {
|
679
|
+
(*os) << v;
|
680
|
+
}
|
681
|
+
};
|
682
|
+
};
|
683
|
+
|
684
|
+
// These are the user-visible definition structures.
|
685
|
+
struct table_column_def
|
686
|
+
{
|
687
|
+
db_string column_name;
|
688
|
+
// This may exactly specify the value_type for each row (and default_value)
|
689
|
+
// or it may make no restriction at all (ct == column_types::Variant).
|
690
|
+
column_type_code_t column_type;
|
691
|
+
// Obviously the default value must conform to the column_type.
|
692
|
+
variant_cv_t default_value;
|
693
|
+
|
694
|
+
table_column_def() {}
|
695
|
+
|
696
|
+
table_column_def(
|
697
|
+
db_string _cn,
|
698
|
+
column_type_code_t _ct,
|
699
|
+
variant_cv_t _dv) :
|
700
|
+
column_name(_cn),
|
701
|
+
column_type(_ct),
|
702
|
+
default_value(_dv)
|
703
|
+
{
|
704
|
+
// This will throw if the default_value and column_type are inconsistent.
|
705
|
+
default_value.coerce(column_type);
|
706
|
+
}
|
707
|
+
};
|
708
|
+
|
709
|
+
// This specifier is used when defining an index.
|
710
|
+
struct index_column_def
|
711
|
+
{
|
712
|
+
db_string column_name;
|
713
|
+
bool sort_ascending;
|
714
|
+
};
|
715
|
+
|
716
|
+
// The columns of an existing index can be enumerated to produce these.
|
717
|
+
// Unlike the above, these index columns are already bound to columns in
|
718
|
+
// the target table.
|
719
|
+
/*
|
720
|
+
struct index_column_handle
|
721
|
+
{
|
722
|
+
index_column_spec spec;
|
723
|
+
table_column_handle column;
|
724
|
+
|
725
|
+
index_column_def(
|
726
|
+
index_column_spec _spec,
|
727
|
+
const table_column_def &_column) :
|
728
|
+
spec(_spec),
|
729
|
+
column(_column)
|
730
|
+
{}
|
731
|
+
};*/
|
732
|
+
|
733
|
+
//
|
734
|
+
// This describes the lifespan of an object, relative to the savepoint history.
|
735
|
+
// When creating or deleting an object, we simply set the according insert/
|
736
|
+
// delete stamp to the latest savepoint marker. When rolling back to a
|
737
|
+
// savepoint, we scan all objects in the database and delete those which were
|
738
|
+
// created after the target value, and recreate those which were created
|
739
|
+
// before and deleted after.
|
740
|
+
//
|
741
|
+
// Objects which pre-date the mounting of the DB have an insert_sp of 0,
|
742
|
+
// meaning that no SP rollback will ever uncreate them.
|
743
|
+
//
|
744
|
+
// Obviously, the savepoints are not persistent. The SP marker resets to
|
745
|
+
// zero each time the DB is deserialized.
|
746
|
+
//
|
747
|
+
// FAQ: Aren't savepoints an esoteric feature? Why implement them?
|
748
|
+
// The familiar mechanism of exception handling in DB systems is the
|
749
|
+
// transaction. However, transactions can be expensive because they do too
|
750
|
+
// much. Transactions are also a synchronization mechanism, among other
|
751
|
+
// things. Our system is designed to be externally synchronized by the
|
752
|
+
// user. Possibly by a mutex, and possibly by nothing at all, depending
|
753
|
+
// on the application. But, we still need a mechanism for compositional
|
754
|
+
// exception handling. Savepoints are like transactions without the
|
755
|
+
// feature of synchronization. They are composable and efficient. Complex
|
756
|
+
// operations can be wrapped in a cascading sequence of savepoints.
|
757
|
+
//
|
758
|
+
struct sp_marker_t
|
759
|
+
{
|
760
|
+
uint32 val;
|
761
|
+
sp_marker_t() : val(0) {}
|
762
|
+
sp_marker_t(uint32 _val) : val(_val) {}
|
763
|
+
|
764
|
+
void increment() { val++; }
|
765
|
+
|
766
|
+
template<typename T> inline bool operator==(const T &other) const { return other == val; }
|
767
|
+
template<typename T> inline bool operator!=(const T &other) const { return other != val; }
|
768
|
+
template<typename T> inline bool operator<(const T &other) const { return other > val; }
|
769
|
+
template<typename T> inline bool operator<=(const T &other) const { return other >= val; }
|
770
|
+
template<typename T> inline bool operator>(const T &other) const { return other < val; }
|
771
|
+
template<typename T> inline bool operator>=(const T &other) const { return other <= val; }
|
772
|
+
};
|
773
|
+
|
774
|
+
enum index_uniqueness_spec_t
|
775
|
+
{
|
776
|
+
Unique = 1,
|
777
|
+
NonUnique = 2
|
778
|
+
};
|
779
|
+
|
780
|
+
} //namespace Oinky
|
781
|
+
|
782
|
+
|
783
|
+
namespace std
|
784
|
+
{
|
785
|
+
|
786
|
+
template<class _Traits>
|
787
|
+
inline std::basic_ostream<char, _Traits>&
|
788
|
+
operator<<(
|
789
|
+
std::basic_ostream<char, _Traits> &os,
|
790
|
+
const Oinky::db_string &str
|
791
|
+
)
|
792
|
+
{
|
793
|
+
return os.write(str.begin(), str.length());
|
794
|
+
}
|
795
|
+
|
796
|
+
template<class _Traits>
|
797
|
+
inline std::basic_ostream<char, _Traits>&
|
798
|
+
operator<<(
|
799
|
+
std::basic_ostream<char, _Traits> &os,
|
800
|
+
const Oinky::variant_cv_t &cv
|
801
|
+
)
|
802
|
+
{
|
803
|
+
cv.dispatch<Oinky::variant_cv_t::formatter>(&os);
|
804
|
+
return os;
|
805
|
+
}
|
806
|
+
|
807
|
+
}//namespace std
|
808
|
+
|