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,107 @@
|
|
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
|
+
// Need this for stderr
|
7
|
+
#include <stdio.h>
|
8
|
+
|
9
|
+
namespace Oinky
|
10
|
+
{
|
11
|
+
|
12
|
+
typedef uint8_t uint8;
|
13
|
+
typedef uint16_t uint16;
|
14
|
+
typedef uint32_t uint32;
|
15
|
+
typedef uint64_t uint64;
|
16
|
+
typedef int8_t int8;
|
17
|
+
typedef int16_t int16;
|
18
|
+
typedef int32_t int32;
|
19
|
+
typedef int64_t int64;
|
20
|
+
|
21
|
+
typedef float float32;
|
22
|
+
typedef double float64;
|
23
|
+
|
24
|
+
BOOST_STATIC_ASSERT(sizeof(float32) == 4);
|
25
|
+
BOOST_STATIC_ASSERT(sizeof(float64) == 8);
|
26
|
+
|
27
|
+
template<typename DERIVED>
|
28
|
+
struct with_compare_ops
|
29
|
+
{
|
30
|
+
bool operator==(const DERIVED &other) const { return ((const DERIVED *)this)->compare_to(other) == 0; }
|
31
|
+
bool operator!=(const DERIVED &other) const { return ((const DERIVED *)this)->compare_to(other) != 0; }
|
32
|
+
bool operator<(const DERIVED &other) const { return ((const DERIVED *)this)->compare_to(other) < 0; }
|
33
|
+
bool operator>(const DERIVED &other) const { return ((const DERIVED *)this)->compare_to(other) > 0; }
|
34
|
+
bool operator<=(const DERIVED &other) const { return ((const DERIVED *)this)->compare_to(other) <= 0; }
|
35
|
+
bool operator>=(const DERIVED &other) const { return ((const DERIVED *)this)->compare_to(other) >= 0; }
|
36
|
+
};
|
37
|
+
|
38
|
+
// This is used to verify that user-supplied callbacks satisfy the proper function concept.
|
39
|
+
template<typename FN, typename F>
|
40
|
+
static inline void check_function_concept(F f)
|
41
|
+
{
|
42
|
+
// template indirection than the next line. This must be wrapped
|
43
|
+
// in a dead if statement, because otherwise this might allocate
|
44
|
+
// a function object on the heap, depending on the compiler and type of FN.
|
45
|
+
if (false) {
|
46
|
+
boost::function<FN> x(f);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
#ifndef MAX
|
51
|
+
#define MAX(a,b) ((a) < (b) ? (b) : (a))
|
52
|
+
#endif
|
53
|
+
#ifndef MIN
|
54
|
+
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
55
|
+
#endif
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
#define __OINKY_TRACE_ASSERT_QUOTEME_(x) #x
|
60
|
+
#define __OINKY_TRACE_ASSERT_QUOTEME(x) __OINKY_TRACE_ASSERT_QUOTEME_(x)
|
61
|
+
|
62
|
+
#define OINKY_TRACE_ASSERT(VAL) {if (!(VAL)) assert_exit("[" __FILE__ ":" __OINKY_TRACE_ASSERT_QUOTEME(__LINE__) "] Assertion Failed: ", __OINKY_TRACE_ASSERT_QUOTEME_(VAL)); }
|
63
|
+
#define OINKY_TRACE_ASSERT_MSG(VAL,MSG) {if (!(VAL)) assert_exit("[" __FILE__ ":" __OINKY_TRACE_ASSERT_QUOTEME(__LINE__) "] ", MSG); }
|
64
|
+
|
65
|
+
#ifdef DEBUG
|
66
|
+
#define OINKY_ASSERT OINKY_TRACE_ASSERT
|
67
|
+
#define OINKY_ASSERT_MSG OINKY_TRACE_ASSERT_MSG
|
68
|
+
#else
|
69
|
+
#define OINKY_ASSERT(VAL) {}
|
70
|
+
#define OINKY_ASSERT_MSG(VAL,MSG) {}
|
71
|
+
#endif
|
72
|
+
|
73
|
+
static void assert_exit(const char *message1, const char *message2)
|
74
|
+
{
|
75
|
+
fprintf(stderr, "%s%s\n", message1, message2);
|
76
|
+
fflush(stderr);
|
77
|
+
|
78
|
+
::abort();
|
79
|
+
}
|
80
|
+
|
81
|
+
// This just forwards to the boost implementation for now, but
|
82
|
+
// it's nice that we can redirect it later easily, if everyone
|
83
|
+
// goes through this for now.
|
84
|
+
static void throw_error(const boost::system::error_code& err) {
|
85
|
+
// This derives from std::runtime_error which derives from std::exception.
|
86
|
+
boost::system::system_error e(err);
|
87
|
+
boost::throw_exception(e);
|
88
|
+
}
|
89
|
+
|
90
|
+
inline uint32 map_into_range(uint32 ref, uint32 min_inc, uint32 max_inc, uint32 default_v) {
|
91
|
+
// Check that it's greater than or equal to min_inc, and less than or equal to N.
|
92
|
+
int32 fail = ref - min_inc;
|
93
|
+
fail |= max_inc - ref;
|
94
|
+
// The high bit of fail now indicates that the ref is bad.
|
95
|
+
// Right-shift with sign-extend means all bits are set if ref is bad,
|
96
|
+
// all clear if ref is good.
|
97
|
+
fail >>= 31;
|
98
|
+
ref &= ~fail;
|
99
|
+
// Ref is now the same as it was, or zero if it was out of range.
|
100
|
+
// Set the low bit if it was bad.
|
101
|
+
ref |= fail & default_v;
|
102
|
+
return ref;
|
103
|
+
}
|
104
|
+
|
105
|
+
|
106
|
+
} //namespace Oinky
|
107
|
+
|
@@ -0,0 +1,164 @@
|
|
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 Errors
|
9
|
+
{
|
10
|
+
using namespace boost::system;
|
11
|
+
|
12
|
+
// Simulate enum class.
|
13
|
+
namespace oinky_code
|
14
|
+
{
|
15
|
+
enum oinky_code
|
16
|
+
{
|
17
|
+
success = 0,
|
18
|
+
|
19
|
+
implementation_bug = 1,
|
20
|
+
no_resources = 2,
|
21
|
+
invalid_argument = 5,
|
22
|
+
unknown_exception = 6,
|
23
|
+
row_format_mismatch = 8,
|
24
|
+
column_type_mismatch = 9,
|
25
|
+
object_deleted = 10,
|
26
|
+
object_exists = 11,
|
27
|
+
index_entry_not_unique = 12,
|
28
|
+
version_mismatch = 13,
|
29
|
+
unsupported_operation = 14,
|
30
|
+
object_still_referenced = 15,
|
31
|
+
incomparable_value_types = 16,
|
32
|
+
column_not_indexable = 17,
|
33
|
+
object_not_found = 18,
|
34
|
+
buffer_overflow = 19,
|
35
|
+
buffer_underflow = 20,
|
36
|
+
bad_encoding = 21,
|
37
|
+
cursor_out_of_range = 22,
|
38
|
+
limit_exceeded = 23
|
39
|
+
};
|
40
|
+
}
|
41
|
+
|
42
|
+
class oinky_category_impl : public error_category
|
43
|
+
{
|
44
|
+
public:
|
45
|
+
virtual const char* name() const { return "Oinky"; }
|
46
|
+
virtual std::string message(int ev) const {
|
47
|
+
return messagebytes(ev);
|
48
|
+
}
|
49
|
+
static const char *messagebytes(int ev) {
|
50
|
+
switch (ev) {
|
51
|
+
case oinky_code::success : return "Oinky success??";
|
52
|
+
|
53
|
+
case oinky_code::implementation_bug : return "Oh NO! Implementation bug.";
|
54
|
+
case oinky_code::no_resources : return "Out of system resources (probably memory).";
|
55
|
+
case oinky_code::invalid_argument : return "Invalid Argument.";
|
56
|
+
case oinky_code::unknown_exception : return "An unrecognized exception was generated. It does not derive from boost::system::runtime_error.";
|
57
|
+
case oinky_code::row_format_mismatch : return "Bad row format. Number of column values does not match selector.";
|
58
|
+
case oinky_code::column_type_mismatch : return "The given value does not conform to column type.";
|
59
|
+
case oinky_code::object_deleted : return "The object has been deleted.";
|
60
|
+
case oinky_code::object_exists : return "The object already exists.";
|
61
|
+
case oinky_code::index_entry_not_unique : return "A uniqueness constraint has been violated.";
|
62
|
+
case oinky_code::version_mismatch : return "This version of the library cannot handle the target datum.";
|
63
|
+
case oinky_code::unsupported_operation : return "The operation is not supported.";
|
64
|
+
case oinky_code::object_still_referenced : return "The object cannot be deleted because it is still referenced.";
|
65
|
+
case oinky_code::incomparable_value_types : return "The two values can't be compared, because they are of incompatible type.";
|
66
|
+
case oinky_code::column_not_indexable : return "The column can't be indexed, because its cell types are incomparable.";
|
67
|
+
case oinky_code::object_not_found : return "The object was not found.";
|
68
|
+
case oinky_code::buffer_overflow : return "Serialization: buffer overflow";
|
69
|
+
case oinky_code::buffer_underflow : return "Serialization: buffer underflow.";
|
70
|
+
case oinky_code::bad_encoding : return "Serialization: bad encoding.";
|
71
|
+
case oinky_code::cursor_out_of_range : return "The cursor is beyond its valid range.";
|
72
|
+
case oinky_code::limit_exceeded : return "One or more of the semantic limits has been reached (eg: #columns/rows/etc.)";
|
73
|
+
}
|
74
|
+
return "Unknown Oinky failure (invalid code).";
|
75
|
+
}
|
76
|
+
|
77
|
+
// TBD:
|
78
|
+
// Few of these error conditions map very well. Sort it out later with
|
79
|
+
// more context.
|
80
|
+
virtual error_condition default_error_condition(int ev) const {
|
81
|
+
switch (ev) {
|
82
|
+
case oinky_code::success : return boost::system::errc::success;
|
83
|
+
case oinky_code::implementation_bug : return boost::system::errc::argument_out_of_domain;
|
84
|
+
case oinky_code::no_resources : return boost::system::errc::not_enough_memory;
|
85
|
+
case oinky_code::invalid_argument : return boost::system::errc::invalid_argument;
|
86
|
+
case oinky_code::unknown_exception : return boost::system::errc::argument_out_of_domain;
|
87
|
+
case oinky_code::row_format_mismatch : return boost::system::errc::argument_out_of_domain;
|
88
|
+
case oinky_code::column_type_mismatch : return boost::system::errc::argument_out_of_domain;
|
89
|
+
case oinky_code::object_deleted : return boost::system::errc::no_such_file_or_directory;
|
90
|
+
case oinky_code::object_exists : return boost::system::errc::address_in_use;
|
91
|
+
case oinky_code::index_entry_not_unique : return boost::system::errc::address_in_use;
|
92
|
+
case oinky_code::version_mismatch : return boost::system::errc::invalid_argument;
|
93
|
+
case oinky_code::unsupported_operation : return boost::system::errc::invalid_argument;
|
94
|
+
case oinky_code::object_still_referenced : return boost::system::errc::invalid_argument;
|
95
|
+
case oinky_code::incomparable_value_types : return boost::system::errc::invalid_argument;
|
96
|
+
case oinky_code::column_not_indexable : return boost::system::errc::invalid_argument;
|
97
|
+
case oinky_code::object_not_found : return boost::system::errc::no_such_file_or_directory;
|
98
|
+
case oinky_code::buffer_overflow : return boost::system::errc::no_buffer_space;
|
99
|
+
case oinky_code::buffer_underflow : return boost::system::errc::message_size;
|
100
|
+
case oinky_code::bad_encoding : return boost::system::errc::bad_message;
|
101
|
+
case oinky_code::cursor_out_of_range : return boost::system::errc::argument_out_of_domain;
|
102
|
+
case oinky_code::limit_exceeded : return boost::system::errc::argument_out_of_domain;
|
103
|
+
|
104
|
+
default: return boost::system::error_condition(ev, *this);
|
105
|
+
}
|
106
|
+
}
|
107
|
+
};
|
108
|
+
|
109
|
+
namespace ErrDetail
|
110
|
+
{
|
111
|
+
// Templating avoids duplicate implementations when linking. =)
|
112
|
+
template<typename CAT>
|
113
|
+
const error_category& __unique_category()
|
114
|
+
{
|
115
|
+
static CAT instance;
|
116
|
+
return instance;
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
inline const error_category& oinky_category()
|
121
|
+
{
|
122
|
+
return ErrDetail::__unique_category<oinky_category_impl>();
|
123
|
+
}
|
124
|
+
|
125
|
+
template<int intcode>
|
126
|
+
class oinky_error_class : public error_code
|
127
|
+
{
|
128
|
+
public:
|
129
|
+
oinky_error_class() : error_code(intcode,oinky_category()) {}
|
130
|
+
};
|
131
|
+
|
132
|
+
class implementation_bug : public oinky_error_class<oinky_code::implementation_bug> {};
|
133
|
+
class no_resources : public oinky_error_class<oinky_code::no_resources> {};
|
134
|
+
class invalid_argument : public oinky_error_class<oinky_code::invalid_argument> {};
|
135
|
+
class unknown_exception : public oinky_error_class<oinky_code::unknown_exception> {};
|
136
|
+
class row_format_mismatch : public oinky_error_class<oinky_code::row_format_mismatch> {};
|
137
|
+
class column_type_mismatch : public oinky_error_class<oinky_code::column_type_mismatch> {};
|
138
|
+
class object_deleted : public oinky_error_class<oinky_code::object_deleted> {};
|
139
|
+
class object_exists : public oinky_error_class<oinky_code::object_exists> {};
|
140
|
+
class index_entry_not_unique : public oinky_error_class<oinky_code::index_entry_not_unique> {};
|
141
|
+
class version_mismatch : public oinky_error_class<oinky_code::version_mismatch> {};
|
142
|
+
class unsupported_operation : public oinky_error_class<oinky_code::unsupported_operation> {};
|
143
|
+
class object_still_referenced : public oinky_error_class<oinky_code::object_still_referenced> {};
|
144
|
+
class incomparable_value_types : public oinky_error_class<oinky_code::incomparable_value_types> {};
|
145
|
+
class column_not_indexable : public oinky_error_class<oinky_code::column_not_indexable> {};
|
146
|
+
class object_not_found : public oinky_error_class<oinky_code::object_not_found> {};
|
147
|
+
class buffer_overflow : public oinky_error_class<oinky_code::buffer_overflow> {};
|
148
|
+
class buffer_underflow : public oinky_error_class<oinky_code::buffer_underflow> {};
|
149
|
+
class bad_encoding : public oinky_error_class<oinky_code::bad_encoding> {};
|
150
|
+
class cursor_out_of_range : public oinky_error_class<oinky_code::cursor_out_of_range> {};
|
151
|
+
class limit_exceeded : public oinky_error_class<oinky_code::limit_exceeded> {};
|
152
|
+
|
153
|
+
} //namespace Errors
|
154
|
+
} //namespace Oinky
|
155
|
+
|
156
|
+
namespace boost
|
157
|
+
{
|
158
|
+
namespace system
|
159
|
+
{
|
160
|
+
template <>
|
161
|
+
struct is_error_code_enum<Oinky::Errors::oinky_category_impl> : public true_type {};
|
162
|
+
};
|
163
|
+
};
|
164
|
+
|
@@ -0,0 +1,710 @@
|
|
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 Oinky::Serialization::v1_sformat_tag;
|
12
|
+
|
13
|
+
//
|
14
|
+
// This is used to deserialize row values, but also by the
|
15
|
+
// serialization component to (de)serialize default values for columns.
|
16
|
+
//
|
17
|
+
struct i_stored_value
|
18
|
+
{
|
19
|
+
template<typename STRTABLE>
|
20
|
+
static void pack_string(const safe_cv_t &val, char *cursor, const STRTABLE &strtable)
|
21
|
+
{
|
22
|
+
OINKY_ASSERT(val.is_string());
|
23
|
+
Oinky::Serialization::pack_nbc<v1_sformat_tag, u_strtable_ref>(strtable.xform_ref(val), cursor);
|
24
|
+
}
|
25
|
+
|
26
|
+
template<typename STRTABLE>
|
27
|
+
static void pack_variant(const safe_cv_t &val, char *cursor, const STRTABLE &strtable)
|
28
|
+
{
|
29
|
+
// Not all of the values below fill the whole cell.
|
30
|
+
*(int64 *)cursor = 0;
|
31
|
+
switch (val.type()) {
|
32
|
+
case value_types::String :
|
33
|
+
pack_string(val, cursor, strtable);
|
34
|
+
return;
|
35
|
+
case value_types::Datetime:
|
36
|
+
Oinky::Serialization::pack_nbc<v1_sformat_tag, datetime_t>(val.dt_value(), cursor);
|
37
|
+
return;
|
38
|
+
//int
|
39
|
+
case value_types::Int8:
|
40
|
+
Oinky::Serialization::pack_nbc<v1_sformat_tag, int8>(val.int_value(), cursor);
|
41
|
+
return;
|
42
|
+
case value_types::Int16:
|
43
|
+
Oinky::Serialization::pack_nbc<v1_sformat_tag, int16>(val.int_value(), cursor);
|
44
|
+
return;
|
45
|
+
case value_types::Int32:
|
46
|
+
Oinky::Serialization::pack_nbc<v1_sformat_tag, int32>(val.int_value(), cursor);
|
47
|
+
return;
|
48
|
+
case value_types::Int64:
|
49
|
+
Oinky::Serialization::pack_nbc<v1_sformat_tag, int64>(val.int_value(), cursor);
|
50
|
+
return;
|
51
|
+
//uint
|
52
|
+
case value_types::Uint8:
|
53
|
+
Oinky::Serialization::pack_nbc<v1_sformat_tag, uint8>(val.uint_value(), cursor);
|
54
|
+
return;
|
55
|
+
case value_types::Uint16:
|
56
|
+
Oinky::Serialization::pack_nbc<v1_sformat_tag, uint16>(val.uint_value(), cursor);
|
57
|
+
return;
|
58
|
+
case value_types::Uint32:
|
59
|
+
Oinky::Serialization::pack_nbc<v1_sformat_tag, uint32>(val.uint_value(), cursor);
|
60
|
+
return;
|
61
|
+
case value_types::Uint64:
|
62
|
+
Oinky::Serialization::pack_nbc<v1_sformat_tag, uint64>(val.uint_value(), cursor);
|
63
|
+
return;
|
64
|
+
//float
|
65
|
+
case value_types::Float32:
|
66
|
+
Oinky::Serialization::pack_nbc<v1_sformat_tag, float32>(val.f32_value(), cursor);
|
67
|
+
return;
|
68
|
+
case value_types::Float64:
|
69
|
+
Oinky::Serialization::pack_nbc<v1_sformat_tag, float64>(val.f64_value(), cursor);
|
70
|
+
return;
|
71
|
+
|
72
|
+
case value_types::Bit:
|
73
|
+
Oinky::Serialization::pack_nbc<v1_sformat_tag, bool>(val.bit_value(), cursor);
|
74
|
+
return;
|
75
|
+
}
|
76
|
+
// Somewhere there is corruption.
|
77
|
+
throw_error(implementation_bug());
|
78
|
+
}
|
79
|
+
|
80
|
+
template<typename T>
|
81
|
+
static inline T unpack_raw(const char *bytes)
|
82
|
+
{
|
83
|
+
T value;
|
84
|
+
Oinky::Serialization::unpack_nbc<v1_sformat_tag,T>(
|
85
|
+
&value,
|
86
|
+
bytes);
|
87
|
+
return value;
|
88
|
+
};
|
89
|
+
template<typename T>
|
90
|
+
static inline void unpack_variant(variant_cv_t *val, const char *bytes)
|
91
|
+
{
|
92
|
+
T value = unpack_raw<T>(bytes);
|
93
|
+
*val = value;
|
94
|
+
};
|
95
|
+
|
96
|
+
// Can be meta or user strtable.
|
97
|
+
template<typename STRTABLE>
|
98
|
+
static void unpack(safe_cv_t *target, value_type_code_t vt, const char *bytes, const STRTABLE *strtable)
|
99
|
+
{
|
100
|
+
variant_cv_t val;
|
101
|
+
// This should throw if this is a string and the ref is invalid.
|
102
|
+
unpack(&val, vt, bytes, strtable);
|
103
|
+
*target = safe_cv_t::from_fixed(val, strtable);
|
104
|
+
}
|
105
|
+
|
106
|
+
template<typename STRTABLE>
|
107
|
+
static void unpack(variant_cv_t *target, value_type_code_t vt, const char *bytes, const STRTABLE *strtable)
|
108
|
+
{
|
109
|
+
switch (vt) {
|
110
|
+
case value_types::Bit : {
|
111
|
+
int8 v = unpack_raw<int8>(bytes);
|
112
|
+
*target = (v != 0);
|
113
|
+
break;
|
114
|
+
}
|
115
|
+
//int
|
116
|
+
case value_types::Int8 : unpack_variant<int8>(target, bytes); break;
|
117
|
+
case value_types::Int16 : unpack_variant<int16>(target, bytes); break;
|
118
|
+
case value_types::Int32 : unpack_variant<int32>(target, bytes); break;
|
119
|
+
case value_types::Int64 : unpack_variant<int64>(target, bytes); break;
|
120
|
+
//uint
|
121
|
+
case value_types::Uint8 : unpack_variant<uint8>(target, bytes); break;
|
122
|
+
case value_types::Uint16 : unpack_variant<uint16>(target, bytes); break;
|
123
|
+
case value_types::Uint32 : unpack_variant<uint32>(target, bytes); break;
|
124
|
+
case value_types::Uint64 : unpack_variant<uint64>(target, bytes); break;
|
125
|
+
//float
|
126
|
+
case value_types::Float32 : unpack_variant<float32>(target, bytes); break;
|
127
|
+
case value_types::Float64 : unpack_variant<float64>(target, bytes); break;
|
128
|
+
|
129
|
+
case value_types::Datetime : unpack_variant<datetime_t>(target, bytes); break;
|
130
|
+
case value_types::String : {
|
131
|
+
u_strtable_ref value;
|
132
|
+
Oinky::Serialization::unpack_nbc<v1_sformat_tag>(
|
133
|
+
&value,
|
134
|
+
bytes);
|
135
|
+
// This will throw if the ref is invalid.
|
136
|
+
*target = strtable->from_untrusted_ref(value);
|
137
|
+
break;
|
138
|
+
}
|
139
|
+
default :
|
140
|
+
OINKY_ASSERT(false);
|
141
|
+
// Not really an exception. The column is invalid
|
142
|
+
// internally. We should have detected this earlier.
|
143
|
+
throw_error(unknown_exception());
|
144
|
+
}
|
145
|
+
}
|
146
|
+
};
|
147
|
+
|
148
|
+
namespace FixedDetail
|
149
|
+
{
|
150
|
+
// The internal representation for a row is just the seriaized bytes.
|
151
|
+
class i_row_value_t
|
152
|
+
{
|
153
|
+
public:
|
154
|
+
const stringtable_t<u_strtable_ref> *strtable;
|
155
|
+
const char *row_base;
|
156
|
+
row_idx_t row_byte_count;
|
157
|
+
|
158
|
+
template<typename TABULAR_T>
|
159
|
+
i_row_value_t(
|
160
|
+
const stringtable_t<u_strtable_ref> *_strtable,
|
161
|
+
const TABULAR_T &_tabular,
|
162
|
+
row_idx_t _offset) :
|
163
|
+
strtable(_strtable),
|
164
|
+
row_base(_tabular.rowdata_base + (_offset * _tabular.row_width_bytes)),
|
165
|
+
row_byte_count(_tabular.row_width_bytes)
|
166
|
+
{
|
167
|
+
OINKY_ASSERT(_offset < _tabular.row_count);
|
168
|
+
}
|
169
|
+
|
170
|
+
public:
|
171
|
+
// Parameters are a column definition and a value
|
172
|
+
template<typename FN>
|
173
|
+
void each_column_value(const column_ctx *const* cols, uint32 count, FN fn) const
|
174
|
+
{
|
175
|
+
each_column_value_t<variant_cv_t>(cols, count, fn);
|
176
|
+
}
|
177
|
+
|
178
|
+
// Parameters are a column definition and a value
|
179
|
+
template<typename FN>
|
180
|
+
void each_safe_value(const column_ctx *const* cols, uint32 count, FN fn) const
|
181
|
+
{
|
182
|
+
// TBD
|
183
|
+
// We always enumerate the complete set of column values.
|
184
|
+
// This is because if we ever add a column, the table gets
|
185
|
+
// exploded, and this code ceases to run.
|
186
|
+
each_column_value_t<safe_cv_t>(cols, count, fn);
|
187
|
+
}
|
188
|
+
|
189
|
+
template<typename TARGET>
|
190
|
+
TARGET get_value(const column_ctx &cc) const
|
191
|
+
{
|
192
|
+
TARGET val;
|
193
|
+
uint32 offset = cc.frow_byte_offset;
|
194
|
+
value_type_code_t vt;
|
195
|
+
|
196
|
+
// If the column type code is variant, we need to unpack the value
|
197
|
+
// type code first.
|
198
|
+
if (cc.type() == column_types::Variant)
|
199
|
+
{
|
200
|
+
uint8 vt_byte;
|
201
|
+
Oinky::Serialization::unpack_nbc<v1_sformat_tag>(
|
202
|
+
&vt_byte,
|
203
|
+
row_base + offset);
|
204
|
+
|
205
|
+
if (!vt_byte || (vt_byte > OINKY_VALUE_TYPE_MAX)) {
|
206
|
+
// The fixed encoding is bad.
|
207
|
+
// TBD: unfortunately raising now might be confusing.
|
208
|
+
throw_error(bad_encoding());
|
209
|
+
}
|
210
|
+
vt = (value_type_code_t) vt_byte;
|
211
|
+
offset += 1;
|
212
|
+
} else if (cc.type() == column_types::Bit) {
|
213
|
+
uint8 mask = 1 << cc.frow_bit_offset;
|
214
|
+
bool val = (row_base[offset] & mask) != 0;
|
215
|
+
return TARGET(val);
|
216
|
+
} else {
|
217
|
+
vt = (value_type_code_t) cc.type();
|
218
|
+
}
|
219
|
+
|
220
|
+
// This is an unsafe unpack (we do not bounds check the source buffer.
|
221
|
+
// It's safe because we bounds-checked when we unpacked the table
|
222
|
+
// itself, and computed/checked the column offsets.
|
223
|
+
i_stored_value::unpack(&val, vt, row_base + offset, strtable);
|
224
|
+
return val;
|
225
|
+
}
|
226
|
+
|
227
|
+
|
228
|
+
template<template<typename T> class CB, typename CTX>
|
229
|
+
bool dispatch_raw_value(const column_ctx &cc, CTX *ctx) const
|
230
|
+
{
|
231
|
+
const char *data = row_base + cc.frow_byte_offset;
|
232
|
+
|
233
|
+
switch (cc.ctype) {
|
234
|
+
case column_types::Bit : {
|
235
|
+
uint8 mask = 1 << cc.frow_bit_offset;
|
236
|
+
bool v = (*data & mask) != 0;
|
237
|
+
return CB<bool>::call(cc, v,ctx);
|
238
|
+
}
|
239
|
+
//int
|
240
|
+
case column_types::Int8 :
|
241
|
+
return CB<int8>::call(cc, i_stored_value::unpack_raw<int8>(data), ctx);
|
242
|
+
case column_types::Int16 :
|
243
|
+
return CB<int16>::call(cc, i_stored_value::unpack_raw<int16>(data), ctx);
|
244
|
+
case column_types::Int32 :
|
245
|
+
return CB<int32>::call(cc, i_stored_value::unpack_raw<int32>(data), ctx);
|
246
|
+
case column_types::Int64 :
|
247
|
+
return CB<int64>::call(cc, i_stored_value::unpack_raw<int64>(data), ctx);
|
248
|
+
//uint
|
249
|
+
case column_types::Uint8 :
|
250
|
+
return CB<uint8>::call(cc, i_stored_value::unpack_raw<uint8>(data), ctx);
|
251
|
+
case column_types::Uint16 :
|
252
|
+
return CB<uint16>::call(cc, i_stored_value::unpack_raw<uint16>(data), ctx);
|
253
|
+
case column_types::Uint32 :
|
254
|
+
return CB<uint32>::call(cc, i_stored_value::unpack_raw<uint32>(data), ctx);
|
255
|
+
case column_types::Uint64 :
|
256
|
+
return CB<uint64>::call(cc, i_stored_value::unpack_raw<uint64>(data), ctx);
|
257
|
+
//float
|
258
|
+
case column_types::Float32 :
|
259
|
+
return CB<float32>::call(cc, i_stored_value::unpack_raw<float32>(data), ctx);
|
260
|
+
case column_types::Float64 :
|
261
|
+
return CB<float64>::call(cc, i_stored_value::unpack_raw<float64>(data), ctx);
|
262
|
+
|
263
|
+
case column_types::Datetime :
|
264
|
+
return CB<datetime_t>::call(cc, i_stored_value::unpack_raw<datetime_t>(data), ctx);
|
265
|
+
case column_types::String : {
|
266
|
+
safe_cv_t str(strtable->from_untrusted_ref(i_stored_value::unpack_raw<u_strtable_ref>(data)));
|
267
|
+
return CB<safe_cv_t>::call(cc, str, ctx);
|
268
|
+
}
|
269
|
+
case column_types::Variant : {
|
270
|
+
int8 vt_byte = i_stored_value::unpack_raw<int8>(data);
|
271
|
+
value_type_code_t vt = (value_type_code_t) vt_byte;
|
272
|
+
data += 1;
|
273
|
+
switch (vt) {
|
274
|
+
case column_types::Bit : {
|
275
|
+
uint8 mask = 1 << cc.frow_bit_offset;
|
276
|
+
bool v = (*data & mask) != 0;
|
277
|
+
return CB<bool>::call(cc, v,ctx);
|
278
|
+
}
|
279
|
+
//int
|
280
|
+
case column_types::Int8 :
|
281
|
+
return CB<int8>::call(cc, i_stored_value::unpack_raw<int8>(data), ctx);
|
282
|
+
case column_types::Int16 :
|
283
|
+
return CB<int16>::call(cc, i_stored_value::unpack_raw<int16>(data), ctx);
|
284
|
+
case column_types::Int32 :
|
285
|
+
return CB<int32>::call(cc, i_stored_value::unpack_raw<int32>(data), ctx);
|
286
|
+
case column_types::Int64 :
|
287
|
+
return CB<int64>::call(cc, i_stored_value::unpack_raw<int64>(data), ctx);
|
288
|
+
//uint
|
289
|
+
case column_types::Uint8 :
|
290
|
+
return CB<uint8>::call(cc, i_stored_value::unpack_raw<uint8>(data), ctx);
|
291
|
+
case column_types::Uint16 :
|
292
|
+
return CB<uint16>::call(cc, i_stored_value::unpack_raw<uint16>(data), ctx);
|
293
|
+
case column_types::Uint32 :
|
294
|
+
return CB<uint32>::call(cc, i_stored_value::unpack_raw<uint32>(data), ctx);
|
295
|
+
case column_types::Uint64 :
|
296
|
+
return CB<uint64>::call(cc, i_stored_value::unpack_raw<uint64>(data), ctx);
|
297
|
+
//float
|
298
|
+
case column_types::Float32 :
|
299
|
+
return CB<float32>::call(cc, i_stored_value::unpack_raw<float32>(data), ctx);
|
300
|
+
case column_types::Float64 :
|
301
|
+
return CB<float64>::call(cc, i_stored_value::unpack_raw<float64>(data), ctx);
|
302
|
+
|
303
|
+
case column_types::Datetime :
|
304
|
+
return CB<datetime_t>::call(cc, i_stored_value::unpack_raw<datetime_t>(data), ctx);
|
305
|
+
case column_types::String : {
|
306
|
+
safe_cv_t str(strtable->from_untrusted_ref(i_stored_value::unpack_raw<u_strtable_ref>(data)));
|
307
|
+
return CB<safe_cv_t>::call(cc, str, ctx);
|
308
|
+
}
|
309
|
+
}
|
310
|
+
// The fixed encoding is bad.
|
311
|
+
// TBD: unfortunately raising now might be confusing.
|
312
|
+
throw_error(bad_encoding());
|
313
|
+
}
|
314
|
+
}
|
315
|
+
throw_error(implementation_bug());
|
316
|
+
return false;
|
317
|
+
}
|
318
|
+
|
319
|
+
private:
|
320
|
+
template<typename TARGET, typename FN>
|
321
|
+
bool dispatch_column_value_t(const column_ctx &cc, FN fn) const
|
322
|
+
{
|
323
|
+
return fn(cc, get_value<TARGET>(cc));
|
324
|
+
}
|
325
|
+
|
326
|
+
// Parameters are a column definition and a value
|
327
|
+
template<typename TARGET, typename FN>
|
328
|
+
void each_column_value_t(const column_ctx *const* cols, uint32 limit, FN fn) const
|
329
|
+
{
|
330
|
+
for (uint32 i = 0;i != limit;++i) {
|
331
|
+
const column_ctx &cc(*cols[i]);
|
332
|
+
if (!dispatch_column_value_t<TARGET>(cc, fn)) {
|
333
|
+
return;
|
334
|
+
}
|
335
|
+
}
|
336
|
+
}
|
337
|
+
};
|
338
|
+
}
|
339
|
+
|
340
|
+
using namespace FixedDetail;
|
341
|
+
|
342
|
+
// Specialized row value extractor for index comparer
|
343
|
+
template<>
|
344
|
+
struct row_value_extractor<FixedDetail::i_row_value_t>
|
345
|
+
{
|
346
|
+
typedef variant_cv_t ex_value_t;
|
347
|
+
|
348
|
+
template<typename TBL>
|
349
|
+
static inline ex_value_t get_column_value(const i_row_value_t &row, const column_ctx &col, const TBL *tbl) {
|
350
|
+
OINKY_ASSERT(row.strtable == &tbl->db->userstrings);
|
351
|
+
return row.get_value<ex_value_t>(col);
|
352
|
+
}
|
353
|
+
};
|
354
|
+
|
355
|
+
struct tabular_t
|
356
|
+
{
|
357
|
+
row_idx_t row_count;
|
358
|
+
uint32 row_width_bytes;
|
359
|
+
const char *rowdata_base;
|
360
|
+
|
361
|
+
tabular_t() :
|
362
|
+
row_count(0),
|
363
|
+
row_width_bytes(0),
|
364
|
+
rowdata_base(NULL)
|
365
|
+
{}
|
366
|
+
|
367
|
+
tabular_t(
|
368
|
+
row_idx_t _row_count,
|
369
|
+
uint32 _row_width_bytes,
|
370
|
+
const char *_rowdata_base) :
|
371
|
+
row_count(_row_count),
|
372
|
+
row_width_bytes(_row_width_bytes),
|
373
|
+
rowdata_base(_rowdata_base)
|
374
|
+
{}
|
375
|
+
};
|
376
|
+
|
377
|
+
template<typename TABLE_CTX>
|
378
|
+
struct tabledata_fixed
|
379
|
+
{
|
380
|
+
typedef TABLE_CTX table_ctx;
|
381
|
+
typedef tabledata_fixed<table_ctx> host_t;
|
382
|
+
|
383
|
+
tabular_t tabular;
|
384
|
+
const table_ctx *table;
|
385
|
+
|
386
|
+
tabledata_fixed(const table_ctx *_table) : table(_table) {}
|
387
|
+
|
388
|
+
tabledata_fixed(
|
389
|
+
const tabular_t &_tabular,
|
390
|
+
const table_ctx *_table
|
391
|
+
) :
|
392
|
+
tabular(_tabular),
|
393
|
+
table(_table)
|
394
|
+
{}
|
395
|
+
|
396
|
+
class iterator : public boost::iterator_facade<
|
397
|
+
iterator,
|
398
|
+
row_idx_t,
|
399
|
+
boost::random_access_traversal_tag,
|
400
|
+
row_idx_t>
|
401
|
+
{
|
402
|
+
const table_ctx *table;
|
403
|
+
row_idx_t _position;
|
404
|
+
|
405
|
+
row_idx_t dereference() const {
|
406
|
+
return _position;
|
407
|
+
}
|
408
|
+
bool equal(const iterator &other) const {
|
409
|
+
return _position == other._position;
|
410
|
+
}
|
411
|
+
void increment() { ++_position; }
|
412
|
+
void decrement() { --_position; }
|
413
|
+
void advance(int x) { _position += x; }
|
414
|
+
int distance_to(const iterator &other) const {
|
415
|
+
return (int) _position - (int) other._position;
|
416
|
+
}
|
417
|
+
|
418
|
+
friend class boost::iterator_core_access;
|
419
|
+
|
420
|
+
public:
|
421
|
+
iterator() : table(NULL), _position((row_idx_t) -1) {}
|
422
|
+
iterator(const table_ctx *_table, row_idx_t __position) : table(_table), _position(__position) {}
|
423
|
+
|
424
|
+
bool is_deleted() const {
|
425
|
+
return !table->fixed.rowkey_is_present(_position);
|
426
|
+
}
|
427
|
+
};
|
428
|
+
|
429
|
+
bool rowkey_is_present(row_idx_t rowkey) const {
|
430
|
+
// The key can be out of range even if the iterator is not. The
|
431
|
+
// indexes (arrays of rowkeys) were serialized by an untrusted
|
432
|
+
// party, after all.
|
433
|
+
return (rowkey < tabular.row_count) && !table->is_deleted(rowkey);
|
434
|
+
}
|
435
|
+
|
436
|
+
inline iterator iterator_at(row_idx_t where) const {
|
437
|
+
if (where > tabular.row_count) {
|
438
|
+
where = tabular.row_count;
|
439
|
+
}
|
440
|
+
return iterator(table,where);
|
441
|
+
}
|
442
|
+
|
443
|
+
typedef column_selector_template<table_ctx> column_selector_t;
|
444
|
+
|
445
|
+
struct accessor_base
|
446
|
+
{
|
447
|
+
column_selector_t cs;
|
448
|
+
row_idx_t rowkey;
|
449
|
+
const table_ctx *table;
|
450
|
+
|
451
|
+
accessor_base(
|
452
|
+
const column_selector_t &_cs,
|
453
|
+
row_idx_t _rowkey,
|
454
|
+
const table_ctx *_table) :
|
455
|
+
cs(_cs),
|
456
|
+
rowkey(_rowkey),
|
457
|
+
table(_table)
|
458
|
+
{}
|
459
|
+
|
460
|
+
uint32 column_count() const { return cs.column_count(); }
|
461
|
+
|
462
|
+
// Parameters are a column definition and a value
|
463
|
+
template<typename FN>
|
464
|
+
void each_column_value(FN fn) const {
|
465
|
+
table->fixed.each_column_value(cs, rowkey, fn);
|
466
|
+
}
|
467
|
+
};
|
468
|
+
|
469
|
+
iterator begin() const { return iterator_at((row_idx_t) 0); }
|
470
|
+
iterator end() const { return iterator_at(tabular.row_count); }
|
471
|
+
|
472
|
+
iterator itr_at(uint64 where) const {
|
473
|
+
if (where > (uint64) tabular.row_count) {
|
474
|
+
where = (uint64) tabular.row_count;
|
475
|
+
}
|
476
|
+
return iterator_at((row_idx_t) where);
|
477
|
+
}
|
478
|
+
|
479
|
+
typedef multicolumn_value_accessor<accessor_base> select_accessor;
|
480
|
+
|
481
|
+
void check_key(row_idx_t rowkey) const {
|
482
|
+
// They keys come from the serialized table.
|
483
|
+
if (rowkey >= tabular.row_count) {
|
484
|
+
// It's not completely obvious at this level that it's a bad
|
485
|
+
// encoding. The other option is an internal bug.
|
486
|
+
throw_error(bad_encoding());
|
487
|
+
}
|
488
|
+
}
|
489
|
+
|
490
|
+
select_accessor select(const column_selector_t &_selector, row_idx_t rowkey) const {
|
491
|
+
check_key(rowkey);
|
492
|
+
return select_accessor(accessor_base(_selector, rowkey, table));
|
493
|
+
}
|
494
|
+
|
495
|
+
template<typename FN>
|
496
|
+
void each_safe_value(row_idx_t rowkey, FN fn) const {
|
497
|
+
check_key(rowkey);
|
498
|
+
i_row_value_t irv(
|
499
|
+
&table->db->userstrings,
|
500
|
+
table->fixed.tabular,
|
501
|
+
rowkey);
|
502
|
+
irv.each_safe_value(table->cols_by_position.begin(), table->cols_by_position.size(), fn);
|
503
|
+
}
|
504
|
+
|
505
|
+
// Parameters are a column definition and a value
|
506
|
+
template<typename SELECTOR, typename FN>
|
507
|
+
void each_column_value(const SELECTOR &_selector, row_idx_t rowkey, FN fn) const {
|
508
|
+
check_key(rowkey);
|
509
|
+
|
510
|
+
i_row_value_t irv(
|
511
|
+
&table->db->userstrings,
|
512
|
+
table->fixed.tabular,
|
513
|
+
rowkey);
|
514
|
+
|
515
|
+
column_selector_accessor<SELECTOR> selector(_selector);
|
516
|
+
selector.check_valid(table);
|
517
|
+
|
518
|
+
irv.each_column_value(selector.colrefs(), selector.colcount(), fn);
|
519
|
+
}
|
520
|
+
|
521
|
+
// Parameters are a column definition and a value
|
522
|
+
template<template<typename T> class CB, typename CTX>
|
523
|
+
void each_raw_value(const column_ctx *const* cols_begin, const column_ctx *const* cols_end, row_idx_t rowkey, CTX *ctx) const {
|
524
|
+
check_key(rowkey);
|
525
|
+
i_row_value_t irv(
|
526
|
+
&table->db->userstrings,
|
527
|
+
table->fixed.tabular,
|
528
|
+
rowkey);
|
529
|
+
|
530
|
+
for (const column_ctx *const* i = cols_begin; i != cols_end; ++i) {
|
531
|
+
const column_ctx &col(**i);
|
532
|
+
if (!irv.dispatch_raw_value<CB>(col, ctx)) {
|
533
|
+
return;
|
534
|
+
}
|
535
|
+
}
|
536
|
+
}
|
537
|
+
};
|
538
|
+
|
539
|
+
template<typename TABLE_CTX> class index_ctx_t;
|
540
|
+
|
541
|
+
template<typename TABLE_CTX>
|
542
|
+
class indexdata_fixed
|
543
|
+
{
|
544
|
+
public:
|
545
|
+
typedef TABLE_CTX table_ctx;
|
546
|
+
typedef index_ctx_t<table_ctx> index_ctx;
|
547
|
+
typedef indexdata_fixed<table_ctx> host_t;
|
548
|
+
|
549
|
+
tabular_t tabular;
|
550
|
+
|
551
|
+
// These are computed values which help us unpack row indices efficiently,
|
552
|
+
// regardless of width and platform.
|
553
|
+
index_rowkey_xform keyxform;
|
554
|
+
|
555
|
+
const table_ctx *table;
|
556
|
+
index_ctx *index;
|
557
|
+
|
558
|
+
typedef column_selector_template<table_ctx> column_selector_t;
|
559
|
+
|
560
|
+
public:
|
561
|
+
class iterator : public boost::iterator_facade<
|
562
|
+
iterator,
|
563
|
+
row_idx_t,
|
564
|
+
boost::random_access_traversal_tag,
|
565
|
+
row_idx_t>
|
566
|
+
{
|
567
|
+
index_ctx *idx;
|
568
|
+
column_selector_t cs;
|
569
|
+
const table_ctx *table;
|
570
|
+
host_t fixed;
|
571
|
+
row_idx_t _position;
|
572
|
+
|
573
|
+
row_idx_t dereference() const {
|
574
|
+
return fixed.keyxform.template unpack<v1_sformat_tag>(_position, fixed.tabular.rowdata_base);
|
575
|
+
}
|
576
|
+
bool equal(const iterator &other) const {
|
577
|
+
return _position == other._position;
|
578
|
+
}
|
579
|
+
void increment() { ++_position; }
|
580
|
+
void decrement() { --_position; }
|
581
|
+
void advance(int x) { _position += x; }
|
582
|
+
int distance_to(const iterator &other) const {
|
583
|
+
return (int) _position - (int) other._position;
|
584
|
+
}
|
585
|
+
|
586
|
+
friend class boost::iterator_core_access;
|
587
|
+
|
588
|
+
public:
|
589
|
+
iterator() : idx(NULL), table(NULL), _position((row_idx_t) -1) {}
|
590
|
+
|
591
|
+
iterator(index_ctx *_index, row_idx_t __position) :
|
592
|
+
idx(_index),
|
593
|
+
cs(_index->index_columns()),
|
594
|
+
table(_index->table),
|
595
|
+
fixed(_index->fixed),
|
596
|
+
_position(__position)
|
597
|
+
{}
|
598
|
+
|
599
|
+
typedef typename tabledata_fixed<table_ctx>::select_accessor select_accessor;
|
600
|
+
|
601
|
+
select_accessor indexed_values() const {
|
602
|
+
return table->fixed.select(cs, dereference());
|
603
|
+
}
|
604
|
+
|
605
|
+
const index_ctx *index() const { return idx; }
|
606
|
+
|
607
|
+
bool is_deleted() const {
|
608
|
+
row_idx_t rowkey = dereference();
|
609
|
+
return !table->fixed.rowkey_is_present(rowkey);
|
610
|
+
}
|
611
|
+
};
|
612
|
+
|
613
|
+
private:
|
614
|
+
inline iterator iterator_at(row_idx_t where) const {
|
615
|
+
OINKY_ASSERT(where <= tabular.row_count);
|
616
|
+
return iterator(index, where);
|
617
|
+
}
|
618
|
+
|
619
|
+
public:
|
620
|
+
iterator begin() const { return iterator_at((row_idx_t) 0); }
|
621
|
+
iterator end() const { return iterator_at(tabular.row_count); }
|
622
|
+
|
623
|
+
indexdata_fixed() : table(NULL) {}
|
624
|
+
|
625
|
+
indexdata_fixed(
|
626
|
+
const tabular_t &_tabular,
|
627
|
+
const table_ctx *_table,
|
628
|
+
index_ctx *_index
|
629
|
+
) :
|
630
|
+
tabular(_tabular),
|
631
|
+
keyxform(_tabular.row_count),
|
632
|
+
table(_table),
|
633
|
+
index(_index)
|
634
|
+
{
|
635
|
+
OINKY_ASSERT((tabular.row_count == 0) || (tabular.row_width_bytes == keyxform.dr_width));
|
636
|
+
OINKY_ASSERT(!tabular.row_count || (tabular.row_count == table->fixed.tabular.row_count));
|
637
|
+
}
|
638
|
+
|
639
|
+
private:
|
640
|
+
typedef index_row_compare_t<index_ctx, i_row_value_t> index_row_compare;
|
641
|
+
|
642
|
+
struct key_compare
|
643
|
+
{
|
644
|
+
const table_ctx *table;
|
645
|
+
const host_t *indextab;
|
646
|
+
column_selector_t cs;
|
647
|
+
|
648
|
+
key_compare(
|
649
|
+
const table_ctx *_table,
|
650
|
+
const host_t *_indextab,
|
651
|
+
const column_selector_t &_cs) :
|
652
|
+
table(_table),
|
653
|
+
indextab(_indextab),
|
654
|
+
cs(_cs)
|
655
|
+
{}
|
656
|
+
|
657
|
+
template<typename ITR_T>
|
658
|
+
int compare(row_idx_t lkey, const simple_sequence<ITR_T> &right) const {
|
659
|
+
i_row_value_t irv(
|
660
|
+
&table->db->userstrings,
|
661
|
+
table->fixed.tabular,
|
662
|
+
lkey);
|
663
|
+
return index_row_compare(indextab->index).compare(irv, right);
|
664
|
+
}
|
665
|
+
|
666
|
+
template<typename ITR_T>
|
667
|
+
bool operator()(row_idx_t lkey, const simple_sequence<ITR_T> &right) const {
|
668
|
+
return compare(lkey, right) < 0;
|
669
|
+
}
|
670
|
+
template<typename ITR_T>
|
671
|
+
bool operator()(const simple_sequence<ITR_T> &left, row_idx_t rkey) const {
|
672
|
+
return compare(rkey, left) > 0;
|
673
|
+
}
|
674
|
+
};
|
675
|
+
|
676
|
+
public:
|
677
|
+
// These iterators are over the column values of the row we are searching for.
|
678
|
+
template<typename ROW_T>
|
679
|
+
iterator lower_bound(const ROW_T &row) const
|
680
|
+
{
|
681
|
+
key_compare compare(table, this, index->index_columns());
|
682
|
+
return std::lower_bound(begin(), end(), row, compare);
|
683
|
+
}
|
684
|
+
template<typename ROW_T>
|
685
|
+
iterator upper_bound(const ROW_T &row) const
|
686
|
+
{
|
687
|
+
// See lower_bound for comments
|
688
|
+
key_compare compare(table, this, index->index_columns());
|
689
|
+
return std::upper_bound(begin(), end(), row, compare);
|
690
|
+
}
|
691
|
+
template<typename ROW_T>
|
692
|
+
iterator find(const ROW_T &row) const
|
693
|
+
{
|
694
|
+
// Find lower bound.
|
695
|
+
iterator i(lower_bound(row));
|
696
|
+
if (i == end()) {
|
697
|
+
return i;
|
698
|
+
}
|
699
|
+
// If the row spec is below itr, then we've found upper_bound == lower_bound.
|
700
|
+
key_compare compare(table, this, index->index_columns());
|
701
|
+
if (compare(row, *i)) {
|
702
|
+
return end();
|
703
|
+
}
|
704
|
+
return i;
|
705
|
+
}
|
706
|
+
};
|
707
|
+
|
708
|
+
} //namespace Internal
|
709
|
+
} //namespace Oinky
|
710
|
+
|