ilios 1.0.6 → 1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/Gemfile +1 -1
- data/README.md +55 -1
- data/ext/ilios/future.c +11 -2
- data/ext/ilios/ilios.c +6 -0
- data/ext/ilios/ilios.h +4 -0
- data/ext/ilios/result.c +158 -97
- data/ext/ilios/statement.c +373 -39
- data/ilios.gemspec +1 -1
- data/lib/ilios/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 35886070e9a98cd40ab1628296e5f2a1cf50fcd5f8429ade4d79eeb0a363cf29
|
|
4
|
+
data.tar.gz: e6b9d75e242db36c5fc96774fbf2dd31a135b3702ccaedc57e3efb07cd1fa22b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2219b69eef45cc7977f79354bf4e03fc8e7583f59ed91dc4290653a0e1a0ea669d9b5dd1225b8cb043203824d3c6d4bf5294baee658e3f07d8ac04be87422c3c
|
|
7
|
+
data.tar.gz: 7e7919c71dd7d876f84331ad33f2b66a9c0831bd5b342eaf766a6e6794e018183b1ba48e798eebb1305480031464c426c11ee79ec490d2d5fec56d826239cf14
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 1.1.0
|
|
4
|
+
|
|
5
|
+
- Support Cassandra collection types (`list`, `set` and `map`, including nested collections). `list` maps to `Array`, `set` to `Set`, `map` to `Hash` (#28)
|
|
6
|
+
- Accept `Symbol` values for `text`/`ascii`/`varchar` columns and collection elements (#28)
|
|
7
|
+
- Require Ruby 3.4 or later (#28)
|
|
8
|
+
- Fix `Ilios::Cassandra::Future#await` returning before the registered callback ran (PR #29)
|
|
9
|
+
|
|
3
10
|
## 1.0.6
|
|
4
11
|
|
|
5
12
|
- Fix uninitialized memory being returned for an undecodable column value (#27)
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/rb/ilios)
|
|
4
4
|
[](https://github.com/Watson1978/ilios/actions/workflows/CI.yml)
|
|
5
|
+
[](https://deepwiki.com/Watson1978/ilios)
|
|
5
6
|
|
|
6
7
|
Ilios that Cassandra driver written by C language for Ruby using [DataStax C/C++ Driver](https://github.com/datastax/cpp-driver).
|
|
7
8
|
|
|
@@ -42,7 +43,7 @@ $ gem install ilios -- --with-libuv-dir=/path/to/libuv-installed-dir --with-cass
|
|
|
42
43
|
|
|
43
44
|
## Supported
|
|
44
45
|
|
|
45
|
-
- Ruby 3.
|
|
46
|
+
- Ruby 3.4 or later
|
|
46
47
|
- Cassandra 3.0 or later
|
|
47
48
|
- Linux and macOS platform
|
|
48
49
|
|
|
@@ -117,6 +118,59 @@ while(result.next_page)
|
|
|
117
118
|
end
|
|
118
119
|
```
|
|
119
120
|
|
|
121
|
+
### Collection types
|
|
122
|
+
|
|
123
|
+
`list`, `set` and `map` columns (including nested collections such as
|
|
124
|
+
`list<frozen<list<int>>>`) are supported.
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
statement = session.prepare(<<~CQL)
|
|
128
|
+
CREATE TABLE IF NOT EXISTS ilios.collection_example (
|
|
129
|
+
id bigint,
|
|
130
|
+
tags set<text>,
|
|
131
|
+
scores list<int>,
|
|
132
|
+
attributes map<text, bigint>,
|
|
133
|
+
PRIMARY KEY (id)
|
|
134
|
+
);
|
|
135
|
+
CQL
|
|
136
|
+
session.execute(statement)
|
|
137
|
+
|
|
138
|
+
statement = session.prepare(<<~CQL)
|
|
139
|
+
INSERT INTO ilios.collection_example (
|
|
140
|
+
id,
|
|
141
|
+
tags,
|
|
142
|
+
scores,
|
|
143
|
+
attributes
|
|
144
|
+
) VALUES (?, ?, ?, ?)
|
|
145
|
+
CQL
|
|
146
|
+
statement.bind({
|
|
147
|
+
id: 1,
|
|
148
|
+
tags: Set['ruby', 'cassandra'], # or an Array
|
|
149
|
+
scores: [85, 92],
|
|
150
|
+
attributes: { 'height' => 180 },
|
|
151
|
+
})
|
|
152
|
+
session.execute(statement)
|
|
153
|
+
|
|
154
|
+
statement = session.prepare(<<~CQL)
|
|
155
|
+
SELECT * FROM ilios.collection_example
|
|
156
|
+
CQL
|
|
157
|
+
session.execute(statement).each do |row|
|
|
158
|
+
row['tags'] # => Set["cassandra", "ruby"]
|
|
159
|
+
row['scores'] # => [85, 92]
|
|
160
|
+
row['attributes'] # => {"height" => 180}
|
|
161
|
+
end
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Notes:
|
|
165
|
+
|
|
166
|
+
- A `set` column accepts both `Set` and `Array` on bind, and is always returned as a `Set`.
|
|
167
|
+
- A `list` column also accepts both `Array` and `Set` on bind, but is always returned as an `Array`. Binding a `Set` to a `list` column keeps the order `Set#to_a` returns.
|
|
168
|
+
- Cassandra stores an empty non-frozen collection as `null`, so inserting `[]`, `Set.new` or `{}` returns `nil` on select. This is Cassandra's data model, not an Ilios limitation.
|
|
169
|
+
- `nil` is not allowed as a collection element (Cassandra collections cannot contain `null`).
|
|
170
|
+
- `Symbol` is accepted for `text` (as well as `ascii` and `varchar`) columns and collection elements, and is stored (and returned) as a `String`.
|
|
171
|
+
- Because a `Symbol` map key is stored as its `String` equivalent, binding a map that contains both (for example `{ k1: 1, 'k1' => 2 }`) ends up as a single key on the server; the entry bound last wins.
|
|
172
|
+
- Binding a `String` containing a NUL character (`\0`) to a `text` (or `ascii` / `varchar`) column raises `ArgumentError` (known limitation).
|
|
173
|
+
|
|
120
174
|
### Synchronous API
|
|
121
175
|
`Ilios::Cassandra::Session#prepare` and `Ilios::Cassandra::Session#execute` are provided as synchronous API.
|
|
122
176
|
|
data/ext/ilios/future.c
CHANGED
|
@@ -213,6 +213,7 @@ VALUE future_create(CassFuture *future, VALUE session, VALUE statement, future_k
|
|
|
213
213
|
uv_sem_init(&cassandra_future->sem, 0);
|
|
214
214
|
cassandra_future->already_waited = false;
|
|
215
215
|
cassandra_future->yielded = false;
|
|
216
|
+
cassandra_future->queued = false;
|
|
216
217
|
|
|
217
218
|
return cassandra_future_obj;
|
|
218
219
|
}
|
|
@@ -232,7 +233,11 @@ static VALUE future_on_success_synchronize(VALUE future)
|
|
|
232
233
|
RB_OBJ_WRITE(future, &cassandra_future->on_success_block, rb_block_proc());
|
|
233
234
|
|
|
234
235
|
if (cass_future_ready(cassandra_future->future)) {
|
|
235
|
-
|
|
236
|
+
// The pool thread posts the semaphore itself once it has yielded;
|
|
237
|
+
// posting here too would let #await return before that happens.
|
|
238
|
+
if (!cassandra_future->queued) {
|
|
239
|
+
uv_sem_post(&cassandra_future->sem);
|
|
240
|
+
}
|
|
236
241
|
if (!cassandra_future->yielded &&
|
|
237
242
|
cass_future_error_code(cassandra_future->future) == CASS_OK) {
|
|
238
243
|
cassandra_future->yielded = true;
|
|
@@ -242,6 +247,7 @@ static VALUE future_on_success_synchronize(VALUE future)
|
|
|
242
247
|
}
|
|
243
248
|
|
|
244
249
|
if (wakeup_thread) {
|
|
250
|
+
cassandra_future->queued = true;
|
|
245
251
|
future_queue_push(future_thread_pool_get(cassandra_future), future);
|
|
246
252
|
}
|
|
247
253
|
|
|
@@ -289,7 +295,9 @@ static VALUE future_on_failure_synchronize(VALUE future)
|
|
|
289
295
|
RB_OBJ_WRITE(future, &cassandra_future->on_failure_block, rb_block_proc());
|
|
290
296
|
|
|
291
297
|
if (cass_future_ready(cassandra_future->future)) {
|
|
292
|
-
|
|
298
|
+
if (!cassandra_future->queued) {
|
|
299
|
+
uv_sem_post(&cassandra_future->sem);
|
|
300
|
+
}
|
|
293
301
|
if (!cassandra_future->yielded &&
|
|
294
302
|
cass_future_error_code(cassandra_future->future) != CASS_OK) {
|
|
295
303
|
cassandra_future->yielded = true;
|
|
@@ -299,6 +307,7 @@ static VALUE future_on_failure_synchronize(VALUE future)
|
|
|
299
307
|
}
|
|
300
308
|
|
|
301
309
|
if (wakeup_thread) {
|
|
310
|
+
cassandra_future->queued = true;
|
|
302
311
|
future_queue_push(future_thread_pool_get(cassandra_future), future);
|
|
303
312
|
}
|
|
304
313
|
|
data/ext/ilios/ilios.c
CHANGED
|
@@ -12,8 +12,10 @@ VALUE eExecutionError;
|
|
|
12
12
|
VALUE eStatementError;
|
|
13
13
|
|
|
14
14
|
VALUE cSizedQueue;
|
|
15
|
+
VALUE cSet;
|
|
15
16
|
|
|
16
17
|
VALUE id_to_time;
|
|
18
|
+
VALUE id_to_a;
|
|
17
19
|
VALUE id_new;
|
|
18
20
|
VALUE id_push;
|
|
19
21
|
VALUE id_pop;
|
|
@@ -88,8 +90,12 @@ void Init_ilios(void)
|
|
|
88
90
|
eStatementError = rb_define_class_under(mCassandra, "StatementError", rb_eStandardError);
|
|
89
91
|
|
|
90
92
|
cSizedQueue = rb_const_get(rb_cThread, rb_intern("SizedQueue"));
|
|
93
|
+
rb_require("set");
|
|
94
|
+
cSet = rb_const_get(rb_cObject, rb_intern("Set"));
|
|
95
|
+
rb_gc_register_mark_object(cSet);
|
|
91
96
|
|
|
92
97
|
id_to_time = rb_intern("to_time");
|
|
98
|
+
id_to_a = rb_intern("to_a");
|
|
93
99
|
id_new = rb_intern("new");
|
|
94
100
|
id_push = rb_intern("push");
|
|
95
101
|
id_pop = rb_intern("pop");
|
data/ext/ilios/ilios.h
CHANGED
|
@@ -87,6 +87,8 @@ typedef struct
|
|
|
87
87
|
uv_sem_t sem;
|
|
88
88
|
bool already_waited;
|
|
89
89
|
bool yielded;
|
|
90
|
+
// Handed to the thread pool, which posts the semaphore once it is done.
|
|
91
|
+
bool queued;
|
|
90
92
|
} CassandraFuture;
|
|
91
93
|
|
|
92
94
|
extern const rb_data_type_t cassandra_cluster_data_type;
|
|
@@ -107,8 +109,10 @@ extern VALUE eExecutionError;
|
|
|
107
109
|
extern VALUE eStatementError;
|
|
108
110
|
|
|
109
111
|
extern VALUE cSizedQueue;
|
|
112
|
+
extern VALUE cSet;
|
|
110
113
|
|
|
111
114
|
extern VALUE id_to_time;
|
|
115
|
+
extern VALUE id_to_a;
|
|
112
116
|
extern VALUE id_new;
|
|
113
117
|
extern VALUE id_push;
|
|
114
118
|
extern VALUE id_pop;
|
data/ext/ilios/result.c
CHANGED
|
@@ -87,6 +87,163 @@ static void result_check_value(CassError error_code, VALUE key)
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
static VALUE result_convert_value(const CassValue *value, VALUE key);
|
|
91
|
+
|
|
92
|
+
struct result_convert_collection_arg {
|
|
93
|
+
CassIterator *iterator;
|
|
94
|
+
CassValueType type;
|
|
95
|
+
VALUE key;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
static VALUE result_convert_collection_body(VALUE a)
|
|
99
|
+
{
|
|
100
|
+
struct result_convert_collection_arg *args = (struct result_convert_collection_arg *)a;
|
|
101
|
+
|
|
102
|
+
if (args->type == CASS_VALUE_TYPE_MAP) {
|
|
103
|
+
VALUE hash = rb_hash_new();
|
|
104
|
+
|
|
105
|
+
while (cass_iterator_next(args->iterator)) {
|
|
106
|
+
VALUE k = result_convert_value(cass_iterator_get_map_key(args->iterator), args->key);
|
|
107
|
+
VALUE v = result_convert_value(cass_iterator_get_map_value(args->iterator), args->key);
|
|
108
|
+
rb_hash_aset(hash, k, v);
|
|
109
|
+
}
|
|
110
|
+
return hash;
|
|
111
|
+
} else {
|
|
112
|
+
VALUE array = rb_ary_new();
|
|
113
|
+
|
|
114
|
+
while (cass_iterator_next(args->iterator)) {
|
|
115
|
+
rb_ary_push(array, result_convert_value(cass_iterator_get_value(args->iterator), args->key));
|
|
116
|
+
}
|
|
117
|
+
if (args->type == CASS_VALUE_TYPE_SET) {
|
|
118
|
+
return rb_funcall(cSet, id_new, 1, array);
|
|
119
|
+
}
|
|
120
|
+
return array;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static VALUE result_convert_collection_ensure(VALUE a)
|
|
125
|
+
{
|
|
126
|
+
cass_iterator_free((CassIterator *)a);
|
|
127
|
+
return Qnil;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static VALUE result_convert_collection(const CassValue *value, CassValueType type, VALUE key)
|
|
131
|
+
{
|
|
132
|
+
struct result_convert_collection_arg args;
|
|
133
|
+
CassIterator *iterator;
|
|
134
|
+
|
|
135
|
+
if (type == CASS_VALUE_TYPE_MAP) {
|
|
136
|
+
iterator = cass_iterator_from_map(value);
|
|
137
|
+
} else {
|
|
138
|
+
iterator = cass_iterator_from_collection(value);
|
|
139
|
+
}
|
|
140
|
+
if (iterator == NULL) {
|
|
141
|
+
// NULL collections are filtered out by cass_value_is_null in
|
|
142
|
+
// result_convert_value; this is a safety net.
|
|
143
|
+
return Qnil;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
args.iterator = iterator;
|
|
147
|
+
args.type = type;
|
|
148
|
+
args.key = key;
|
|
149
|
+
return rb_ensure(result_convert_collection_body, (VALUE)&args, result_convert_collection_ensure, (VALUE)iterator);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
static VALUE result_convert_value(const CassValue *value, VALUE key)
|
|
153
|
+
{
|
|
154
|
+
const CassValueType type = cass_value_type(value);
|
|
155
|
+
|
|
156
|
+
if (cass_value_is_null(value)) {
|
|
157
|
+
return Qnil;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
switch (type) {
|
|
161
|
+
case CASS_VALUE_TYPE_TINY_INT:
|
|
162
|
+
{
|
|
163
|
+
cass_int8_t output = 0;
|
|
164
|
+
result_check_value(cass_value_get_int8(value, &output), key);
|
|
165
|
+
return INT2NUM(output);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
case CASS_VALUE_TYPE_SMALL_INT:
|
|
169
|
+
{
|
|
170
|
+
cass_int16_t output = 0;
|
|
171
|
+
result_check_value(cass_value_get_int16(value, &output), key);
|
|
172
|
+
return INT2NUM(output);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
case CASS_VALUE_TYPE_INT:
|
|
176
|
+
{
|
|
177
|
+
cass_int32_t output = 0;
|
|
178
|
+
result_check_value(cass_value_get_int32(value, &output), key);
|
|
179
|
+
return INT2NUM(output);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
case CASS_VALUE_TYPE_BIGINT:
|
|
183
|
+
{
|
|
184
|
+
cass_int64_t output = 0;
|
|
185
|
+
result_check_value(cass_value_get_int64(value, &output), key);
|
|
186
|
+
return LL2NUM(output);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
case CASS_VALUE_TYPE_FLOAT:
|
|
190
|
+
{
|
|
191
|
+
cass_float_t output = 0;
|
|
192
|
+
result_check_value(cass_value_get_float(value, &output), key);
|
|
193
|
+
return DBL2NUM(output);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
case CASS_VALUE_TYPE_DOUBLE:
|
|
197
|
+
{
|
|
198
|
+
cass_double_t output = 0;
|
|
199
|
+
result_check_value(cass_value_get_double(value, &output), key);
|
|
200
|
+
return DBL2NUM(output);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
case CASS_VALUE_TYPE_BOOLEAN:
|
|
204
|
+
{
|
|
205
|
+
cass_bool_t output = cass_false;
|
|
206
|
+
result_check_value(cass_value_get_bool(value, &output), key);
|
|
207
|
+
return output == cass_true ? Qtrue : Qfalse;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
case CASS_VALUE_TYPE_TEXT:
|
|
211
|
+
case CASS_VALUE_TYPE_ASCII:
|
|
212
|
+
case CASS_VALUE_TYPE_VARCHAR:
|
|
213
|
+
{
|
|
214
|
+
const char* s = NULL;
|
|
215
|
+
size_t s_length = 0;
|
|
216
|
+
result_check_value(cass_value_get_string(value, &s, &s_length), key);
|
|
217
|
+
return rb_str_new(s, s_length);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
case CASS_VALUE_TYPE_TIMESTAMP:
|
|
221
|
+
{
|
|
222
|
+
cass_int64_t output = 0;
|
|
223
|
+
result_check_value(cass_value_get_int64(value, &output), key);
|
|
224
|
+
return rb_time_new(output / 1000, output % 1000 * 1000);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
case CASS_VALUE_TYPE_UUID:
|
|
228
|
+
{
|
|
229
|
+
CassUuid output = { 0, 0 };
|
|
230
|
+
char uuid[40];
|
|
231
|
+
result_check_value(cass_value_get_uuid(value, &output), key);
|
|
232
|
+
cass_uuid_string(output, uuid);
|
|
233
|
+
return rb_str_new2(uuid);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
case CASS_VALUE_TYPE_LIST:
|
|
237
|
+
case CASS_VALUE_TYPE_SET:
|
|
238
|
+
case CASS_VALUE_TYPE_MAP:
|
|
239
|
+
return result_convert_collection(value, type, key);
|
|
240
|
+
|
|
241
|
+
default:
|
|
242
|
+
rb_warn("Unsupported type: %d", type);
|
|
243
|
+
return sym_unsupported_column_type;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
90
247
|
static VALUE result_convert_row(const CassResult *result, const CassRow *row, size_t column_count)
|
|
91
248
|
{
|
|
92
249
|
VALUE key;
|
|
@@ -96,106 +253,10 @@ static VALUE result_convert_row(const CassResult *result, const CassRow *row, si
|
|
|
96
253
|
|
|
97
254
|
for (size_t i = 0; i < column_count; i++) {
|
|
98
255
|
const CassValue *value = cass_row_get_column(row, i);
|
|
99
|
-
const CassValueType type = cass_value_type(value);
|
|
100
256
|
|
|
101
257
|
cass_result_column_name(result, i, &name, &name_length);
|
|
102
258
|
key = rb_enc_interned_str(name, name_length, rb_utf8_encoding());
|
|
103
|
-
|
|
104
|
-
if (cass_value_is_null(value)) {
|
|
105
|
-
rb_hash_aset(hash, key, Qnil);
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
switch (type) {
|
|
110
|
-
case CASS_VALUE_TYPE_TINY_INT:
|
|
111
|
-
{
|
|
112
|
-
cass_int8_t output = 0;
|
|
113
|
-
result_check_value(cass_value_get_int8(value, &output), key);
|
|
114
|
-
rb_hash_aset(hash, key, INT2NUM(output));
|
|
115
|
-
}
|
|
116
|
-
break;
|
|
117
|
-
|
|
118
|
-
case CASS_VALUE_TYPE_SMALL_INT:
|
|
119
|
-
{
|
|
120
|
-
cass_int16_t output = 0;
|
|
121
|
-
result_check_value(cass_value_get_int16(value, &output), key);
|
|
122
|
-
rb_hash_aset(hash, key, INT2NUM(output));
|
|
123
|
-
}
|
|
124
|
-
break;
|
|
125
|
-
|
|
126
|
-
case CASS_VALUE_TYPE_INT:
|
|
127
|
-
{
|
|
128
|
-
cass_int32_t output = 0;
|
|
129
|
-
result_check_value(cass_value_get_int32(value, &output), key);
|
|
130
|
-
rb_hash_aset(hash, key, INT2NUM(output));
|
|
131
|
-
}
|
|
132
|
-
break;
|
|
133
|
-
|
|
134
|
-
case CASS_VALUE_TYPE_BIGINT:
|
|
135
|
-
{
|
|
136
|
-
cass_int64_t output = 0;
|
|
137
|
-
result_check_value(cass_value_get_int64(value, &output), key);
|
|
138
|
-
rb_hash_aset(hash, key, LL2NUM(output));
|
|
139
|
-
}
|
|
140
|
-
break;
|
|
141
|
-
|
|
142
|
-
case CASS_VALUE_TYPE_FLOAT:
|
|
143
|
-
{
|
|
144
|
-
cass_float_t output = 0;
|
|
145
|
-
result_check_value(cass_value_get_float(value, &output), key);
|
|
146
|
-
rb_hash_aset(hash, key, DBL2NUM(output));
|
|
147
|
-
}
|
|
148
|
-
break;
|
|
149
|
-
|
|
150
|
-
case CASS_VALUE_TYPE_DOUBLE:
|
|
151
|
-
{
|
|
152
|
-
cass_double_t output = 0;
|
|
153
|
-
result_check_value(cass_value_get_double(value, &output), key);
|
|
154
|
-
rb_hash_aset(hash, key, DBL2NUM(output));
|
|
155
|
-
}
|
|
156
|
-
break;
|
|
157
|
-
|
|
158
|
-
case CASS_VALUE_TYPE_BOOLEAN:
|
|
159
|
-
{
|
|
160
|
-
cass_bool_t output = cass_false;
|
|
161
|
-
result_check_value(cass_value_get_bool(value, &output), key);
|
|
162
|
-
rb_hash_aset(hash, key, output == cass_true ? Qtrue : Qfalse);
|
|
163
|
-
}
|
|
164
|
-
break;
|
|
165
|
-
|
|
166
|
-
case CASS_VALUE_TYPE_TEXT:
|
|
167
|
-
case CASS_VALUE_TYPE_ASCII:
|
|
168
|
-
case CASS_VALUE_TYPE_VARCHAR:
|
|
169
|
-
{
|
|
170
|
-
const char* s = NULL;
|
|
171
|
-
size_t s_length = 0;
|
|
172
|
-
result_check_value(cass_value_get_string(value, &s, &s_length), key);
|
|
173
|
-
rb_hash_aset(hash, key, rb_str_new(s, s_length));
|
|
174
|
-
}
|
|
175
|
-
break;
|
|
176
|
-
|
|
177
|
-
case CASS_VALUE_TYPE_TIMESTAMP:
|
|
178
|
-
{
|
|
179
|
-
cass_int64_t output = 0;
|
|
180
|
-
result_check_value(cass_value_get_int64(value, &output), key);
|
|
181
|
-
rb_hash_aset(hash, key, rb_time_new(output / 1000, output % 1000 * 1000));
|
|
182
|
-
}
|
|
183
|
-
break;
|
|
184
|
-
|
|
185
|
-
case CASS_VALUE_TYPE_UUID:
|
|
186
|
-
{
|
|
187
|
-
CassUuid output = { 0, 0 };
|
|
188
|
-
char uuid[40];
|
|
189
|
-
result_check_value(cass_value_get_uuid(value, &output), key);
|
|
190
|
-
cass_uuid_string(output, uuid);
|
|
191
|
-
rb_hash_aset(hash, key, rb_str_new2(uuid));
|
|
192
|
-
}
|
|
193
|
-
break;
|
|
194
|
-
|
|
195
|
-
default:
|
|
196
|
-
rb_warn("Unsupported type: %d", type);
|
|
197
|
-
rb_hash_aset(hash, key, sym_unsupported_column_type);
|
|
198
|
-
}
|
|
259
|
+
rb_hash_aset(hash, key, result_convert_value(value, key));
|
|
199
260
|
}
|
|
200
261
|
|
|
201
262
|
return hash;
|
data/ext/ilios/statement.c
CHANGED
|
@@ -24,6 +24,107 @@ typedef struct
|
|
|
24
24
|
VALUE bound_values;
|
|
25
25
|
} statement_bind_context;
|
|
26
26
|
|
|
27
|
+
typedef enum {
|
|
28
|
+
bind_target_kind_statement,
|
|
29
|
+
bind_target_kind_collection
|
|
30
|
+
} statement_bind_target_kind;
|
|
31
|
+
|
|
32
|
+
// Where a value is bound to: a named parameter of a statement, or an
|
|
33
|
+
// element of a collection being built. Keeps the type switch in
|
|
34
|
+
// statement_bind_value single so that top-level columns and collection
|
|
35
|
+
// elements always support exactly the same scalar types.
|
|
36
|
+
typedef struct
|
|
37
|
+
{
|
|
38
|
+
statement_bind_target_kind kind;
|
|
39
|
+
union {
|
|
40
|
+
struct {
|
|
41
|
+
CassStatement *statement;
|
|
42
|
+
const char *name;
|
|
43
|
+
} statement;
|
|
44
|
+
CassCollection *collection;
|
|
45
|
+
} as;
|
|
46
|
+
} statement_bind_target;
|
|
47
|
+
|
|
48
|
+
static CassError bind_target_int8(statement_bind_target *target, cass_int8_t value)
|
|
49
|
+
{
|
|
50
|
+
if (target->kind == bind_target_kind_statement) {
|
|
51
|
+
return cass_statement_bind_int8_by_name(target->as.statement.statement, target->as.statement.name, value);
|
|
52
|
+
}
|
|
53
|
+
return cass_collection_append_int8(target->as.collection, value);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static CassError bind_target_int16(statement_bind_target *target, cass_int16_t value)
|
|
57
|
+
{
|
|
58
|
+
if (target->kind == bind_target_kind_statement) {
|
|
59
|
+
return cass_statement_bind_int16_by_name(target->as.statement.statement, target->as.statement.name, value);
|
|
60
|
+
}
|
|
61
|
+
return cass_collection_append_int16(target->as.collection, value);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static CassError bind_target_int32(statement_bind_target *target, cass_int32_t value)
|
|
65
|
+
{
|
|
66
|
+
if (target->kind == bind_target_kind_statement) {
|
|
67
|
+
return cass_statement_bind_int32_by_name(target->as.statement.statement, target->as.statement.name, value);
|
|
68
|
+
}
|
|
69
|
+
return cass_collection_append_int32(target->as.collection, value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static CassError bind_target_int64(statement_bind_target *target, cass_int64_t value)
|
|
73
|
+
{
|
|
74
|
+
if (target->kind == bind_target_kind_statement) {
|
|
75
|
+
return cass_statement_bind_int64_by_name(target->as.statement.statement, target->as.statement.name, value);
|
|
76
|
+
}
|
|
77
|
+
return cass_collection_append_int64(target->as.collection, value);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static CassError bind_target_float(statement_bind_target *target, cass_float_t value)
|
|
81
|
+
{
|
|
82
|
+
if (target->kind == bind_target_kind_statement) {
|
|
83
|
+
return cass_statement_bind_float_by_name(target->as.statement.statement, target->as.statement.name, value);
|
|
84
|
+
}
|
|
85
|
+
return cass_collection_append_float(target->as.collection, value);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static CassError bind_target_double(statement_bind_target *target, cass_double_t value)
|
|
89
|
+
{
|
|
90
|
+
if (target->kind == bind_target_kind_statement) {
|
|
91
|
+
return cass_statement_bind_double_by_name(target->as.statement.statement, target->as.statement.name, value);
|
|
92
|
+
}
|
|
93
|
+
return cass_collection_append_double(target->as.collection, value);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
static CassError bind_target_bool(statement_bind_target *target, cass_bool_t value)
|
|
97
|
+
{
|
|
98
|
+
if (target->kind == bind_target_kind_statement) {
|
|
99
|
+
return cass_statement_bind_bool_by_name(target->as.statement.statement, target->as.statement.name, value);
|
|
100
|
+
}
|
|
101
|
+
return cass_collection_append_bool(target->as.collection, value);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static CassError bind_target_string(statement_bind_target *target, const char *value)
|
|
105
|
+
{
|
|
106
|
+
if (target->kind == bind_target_kind_statement) {
|
|
107
|
+
return cass_statement_bind_string_by_name(target->as.statement.statement, target->as.statement.name, value);
|
|
108
|
+
}
|
|
109
|
+
return cass_collection_append_string(target->as.collection, value);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
static CassError bind_target_uuid(statement_bind_target *target, CassUuid value)
|
|
113
|
+
{
|
|
114
|
+
if (target->kind == bind_target_kind_statement) {
|
|
115
|
+
return cass_statement_bind_uuid_by_name(target->as.statement.statement, target->as.statement.name, value);
|
|
116
|
+
}
|
|
117
|
+
return cass_collection_append_uuid(target->as.collection, value);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static CassError bind_target_collection(statement_bind_target *target, const CassCollection *value)
|
|
121
|
+
{
|
|
122
|
+
if (target->kind == bind_target_kind_statement) {
|
|
123
|
+
return cass_statement_bind_collection_by_name(target->as.statement.statement, target->as.statement.name, value);
|
|
124
|
+
}
|
|
125
|
+
return cass_collection_append_collection(target->as.collection, value);
|
|
126
|
+
}
|
|
127
|
+
|
|
27
128
|
void statement_default_config(CassandraStatement *cassandra_statement)
|
|
28
129
|
{
|
|
29
130
|
cassandra_statement->bound_values = Qnil;
|
|
@@ -32,28 +133,32 @@ void statement_default_config(CassandraStatement *cassandra_statement)
|
|
|
32
133
|
cass_statement_set_paging_size(cassandra_statement->statement, DEFAULT_PAGE_SIZE);
|
|
33
134
|
}
|
|
34
135
|
|
|
35
|
-
static
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
CassValueType value_type;
|
|
40
|
-
CassError result;
|
|
41
|
-
const char *name;
|
|
136
|
+
static void statement_bind_value(statement_bind_target *target, const CassDataType *data_type,
|
|
137
|
+
VALUE value, VALUE key, const char *role);
|
|
138
|
+
static void statement_bind_collection(statement_bind_target *target, const CassDataType *data_type,
|
|
139
|
+
VALUE value, VALUE key);
|
|
42
140
|
|
|
43
|
-
|
|
44
|
-
|
|
141
|
+
static void statement_bind_check(CassError result, VALUE key)
|
|
142
|
+
{
|
|
143
|
+
if (result != CASS_OK) {
|
|
144
|
+
rb_raise(eStatementError, "Failed to bind value of %"PRIsVALUE" column: %s", key, cass_error_desc(result));
|
|
45
145
|
}
|
|
46
|
-
|
|
146
|
+
}
|
|
47
147
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
148
|
+
static void statement_bind_value(statement_bind_target *target, const CassDataType *data_type,
|
|
149
|
+
VALUE value, VALUE key, const char *role)
|
|
150
|
+
{
|
|
151
|
+
const CassValueType value_type = cass_data_type_type(data_type);
|
|
152
|
+
CassError result;
|
|
53
153
|
|
|
54
154
|
if (NIL_P(value)) {
|
|
55
|
-
|
|
56
|
-
|
|
155
|
+
if (target->kind == bind_target_kind_collection) {
|
|
156
|
+
// The driver has no cass_collection_append_null: Cassandra
|
|
157
|
+
// collections cannot contain null elements.
|
|
158
|
+
rb_raise(rb_eTypeError, "nil is not allowed as a collection %s in %"PRIsVALUE"", role, key);
|
|
159
|
+
}
|
|
160
|
+
statement_bind_check(cass_statement_bind_null_by_name(target->as.statement.statement, target->as.statement.name), key);
|
|
161
|
+
return;
|
|
57
162
|
}
|
|
58
163
|
|
|
59
164
|
switch (value_type) {
|
|
@@ -64,7 +169,7 @@ static int hash_cb(VALUE key, VALUE value, VALUE arg)
|
|
|
64
169
|
if (v < INT8_MIN || v > INT8_MAX) {
|
|
65
170
|
rb_raise(rb_eRangeError, "Invalid value: %ld", v);
|
|
66
171
|
}
|
|
67
|
-
result =
|
|
172
|
+
result = bind_target_int8(target, (cass_int8_t)v);
|
|
68
173
|
}
|
|
69
174
|
break;
|
|
70
175
|
|
|
@@ -76,7 +181,7 @@ static int hash_cb(VALUE key, VALUE value, VALUE arg)
|
|
|
76
181
|
rb_raise(rb_eRangeError, "Invalid value: %ld", v);
|
|
77
182
|
}
|
|
78
183
|
|
|
79
|
-
result =
|
|
184
|
+
result = bind_target_int16(target, (cass_int16_t)v);
|
|
80
185
|
}
|
|
81
186
|
break;
|
|
82
187
|
|
|
@@ -88,12 +193,12 @@ static int hash_cb(VALUE key, VALUE value, VALUE arg)
|
|
|
88
193
|
rb_raise(rb_eRangeError, "Invalid value: %ld", v);
|
|
89
194
|
}
|
|
90
195
|
|
|
91
|
-
result =
|
|
196
|
+
result = bind_target_int32(target, (cass_int32_t)v);
|
|
92
197
|
}
|
|
93
198
|
break;
|
|
94
199
|
|
|
95
200
|
case CASS_VALUE_TYPE_BIGINT:
|
|
96
|
-
result =
|
|
201
|
+
result = bind_target_int64(target, NUM2LONG(value));
|
|
97
202
|
break;
|
|
98
203
|
|
|
99
204
|
case CASS_VALUE_TYPE_FLOAT:
|
|
@@ -104,25 +209,27 @@ static int hash_cb(VALUE key, VALUE value, VALUE arg)
|
|
|
104
209
|
rb_raise(rb_eRangeError, "Invalid value: %lf", v);
|
|
105
210
|
}
|
|
106
211
|
|
|
107
|
-
result =
|
|
212
|
+
result = bind_target_float(target, v);
|
|
108
213
|
}
|
|
109
214
|
break;
|
|
110
215
|
|
|
111
216
|
case CASS_VALUE_TYPE_DOUBLE:
|
|
112
|
-
result =
|
|
217
|
+
result = bind_target_double(target, NUM2DBL(value));
|
|
113
218
|
break;
|
|
114
219
|
|
|
115
220
|
case CASS_VALUE_TYPE_BOOLEAN:
|
|
116
|
-
|
|
117
|
-
cass_bool_t v = RTEST(value) ? cass_true : cass_false;
|
|
118
|
-
result = cass_statement_bind_bool_by_name(ctx->statement, name, v);
|
|
119
|
-
}
|
|
221
|
+
result = bind_target_bool(target, RTEST(value) ? cass_true : cass_false);
|
|
120
222
|
break;
|
|
121
223
|
|
|
122
224
|
case CASS_VALUE_TYPE_TEXT:
|
|
123
225
|
case CASS_VALUE_TYPE_ASCII:
|
|
124
226
|
case CASS_VALUE_TYPE_VARCHAR:
|
|
125
|
-
|
|
227
|
+
if (SYMBOL_P(value)) {
|
|
228
|
+
// rb_sym2str returns the Symbol's fstring; no allocation, so
|
|
229
|
+
// converting on every execution is fine.
|
|
230
|
+
value = rb_sym2str(value);
|
|
231
|
+
}
|
|
232
|
+
result = bind_target_string(target, StringValueCStr(value));
|
|
126
233
|
break;
|
|
127
234
|
|
|
128
235
|
case CASS_VALUE_TYPE_TIMESTAMP:
|
|
@@ -133,7 +240,7 @@ static int hash_cb(VALUE key, VALUE value, VALUE arg)
|
|
|
133
240
|
rb_raise(rb_eTypeError, "no implicit conversion of %"PRIsVALUE" to Time", rb_obj_class(value));
|
|
134
241
|
}
|
|
135
242
|
}
|
|
136
|
-
result =
|
|
243
|
+
result = bind_target_int64(target, (cass_int64_t)(NUM2DBL(rb_Float(value)) * 1000));
|
|
137
244
|
break;
|
|
138
245
|
|
|
139
246
|
case CASS_VALUE_TYPE_UUID:
|
|
@@ -143,28 +250,255 @@ static int hash_cb(VALUE key, VALUE value, VALUE arg)
|
|
|
143
250
|
|
|
144
251
|
result = cass_uuid_from_string(uuid_string, &uuid);
|
|
145
252
|
if (result != CASS_OK) {
|
|
146
|
-
rb_raise(eStatementError, "Invalid UUID was given: %
|
|
253
|
+
rb_raise(eStatementError, "Invalid UUID was given: %"PRIsVALUE"=%"PRIsVALUE"", key, value);
|
|
147
254
|
}
|
|
148
255
|
|
|
149
|
-
result =
|
|
256
|
+
result = bind_target_uuid(target, uuid);
|
|
150
257
|
}
|
|
151
258
|
break;
|
|
152
259
|
|
|
260
|
+
case CASS_VALUE_TYPE_LIST:
|
|
261
|
+
case CASS_VALUE_TYPE_SET:
|
|
262
|
+
case CASS_VALUE_TYPE_MAP:
|
|
263
|
+
statement_bind_collection(target, data_type, value, key);
|
|
264
|
+
return;
|
|
265
|
+
|
|
153
266
|
default:
|
|
154
|
-
rb_raise(rb_eTypeError, "Unsupported %"PRIsVALUE" type: %
|
|
267
|
+
rb_raise(rb_eTypeError, "Unsupported %"PRIsVALUE" type: %"PRIsVALUE"=%"PRIsVALUE"", rb_obj_class(value), key, value);
|
|
155
268
|
}
|
|
156
269
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
270
|
+
statement_bind_check(result, key);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
typedef struct
|
|
274
|
+
{
|
|
275
|
+
statement_bind_target target;
|
|
276
|
+
const CassDataType *data_type;
|
|
277
|
+
VALUE value;
|
|
278
|
+
VALUE key;
|
|
279
|
+
} statement_bind_collection_args;
|
|
280
|
+
|
|
281
|
+
struct statement_bind_map_ctx {
|
|
282
|
+
statement_bind_target *target;
|
|
283
|
+
const CassDataType *key_type;
|
|
284
|
+
const CassDataType *value_type;
|
|
285
|
+
VALUE key;
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
static int statement_bind_map_cb(VALUE k, VALUE v, VALUE arg)
|
|
289
|
+
{
|
|
290
|
+
struct statement_bind_map_ctx *ctx = (struct statement_bind_map_ctx *)arg;
|
|
291
|
+
|
|
292
|
+
// A map is appended as alternating key, value pairs.
|
|
293
|
+
statement_bind_value(ctx->target, ctx->key_type, k, ctx->key, "key");
|
|
294
|
+
statement_bind_value(ctx->target, ctx->value_type, v, ctx->key, "value");
|
|
295
|
+
return ST_CONTINUE;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
static VALUE statement_bind_collection_body(VALUE arg)
|
|
299
|
+
{
|
|
300
|
+
statement_bind_collection_args *args = (statement_bind_collection_args *)arg;
|
|
301
|
+
|
|
302
|
+
if (cass_data_type_type(args->data_type) == CASS_VALUE_TYPE_MAP) {
|
|
303
|
+
struct statement_bind_map_ctx ctx;
|
|
304
|
+
|
|
305
|
+
ctx.target = &args->target;
|
|
306
|
+
ctx.key_type = cass_data_type_sub_data_type(args->data_type, 0);
|
|
307
|
+
ctx.value_type = cass_data_type_sub_data_type(args->data_type, 1);
|
|
308
|
+
ctx.key = args->key;
|
|
309
|
+
rb_hash_foreach(args->value, statement_bind_map_cb, (VALUE)&ctx);
|
|
310
|
+
} else {
|
|
311
|
+
const CassDataType *element_type = cass_data_type_sub_data_type(args->data_type, 0);
|
|
312
|
+
long length = RARRAY_LEN(args->value);
|
|
313
|
+
|
|
314
|
+
for (long i = 0; i < length; i++) {
|
|
315
|
+
statement_bind_value(&args->target, element_type, RARRAY_AREF(args->value, i), args->key, "element");
|
|
316
|
+
}
|
|
160
317
|
}
|
|
318
|
+
return Qnil;
|
|
319
|
+
}
|
|
161
320
|
|
|
162
|
-
|
|
321
|
+
/*
|
|
322
|
+
* Builds a CassCollection from a snapshotted (frozen) Array or Hash and binds
|
|
323
|
+
* it to the target. The value is guaranteed to be an Array (list/set) or Hash
|
|
324
|
+
* (map) because statement_snapshot_value normalizes it in Statement#bind
|
|
325
|
+
* before the first statement_bind_value call, and executions only replay
|
|
326
|
+
* values stored in bound_values.
|
|
327
|
+
*/
|
|
328
|
+
static void statement_bind_collection(statement_bind_target *target, const CassDataType *data_type,
|
|
329
|
+
VALUE value, VALUE key)
|
|
330
|
+
{
|
|
331
|
+
const CassValueType type = cass_data_type_type(data_type);
|
|
332
|
+
statement_bind_collection_args args;
|
|
333
|
+
CassCollection *collection;
|
|
334
|
+
size_t item_count;
|
|
335
|
+
CassError result;
|
|
336
|
+
int state = 0;
|
|
337
|
+
|
|
338
|
+
if (type == CASS_VALUE_TYPE_MAP) {
|
|
339
|
+
if (cass_data_type_sub_type_count(data_type) != 2) {
|
|
340
|
+
rb_raise(eStatementError, "Invalid map type of %"PRIsVALUE" column", key);
|
|
341
|
+
}
|
|
342
|
+
Check_Type(value, T_HASH);
|
|
343
|
+
item_count = (size_t)RHASH_SIZE(value);
|
|
344
|
+
} else {
|
|
345
|
+
if (cass_data_type_sub_type_count(data_type) != 1) {
|
|
346
|
+
rb_raise(eStatementError, "Invalid collection type of %"PRIsVALUE" column", key);
|
|
347
|
+
}
|
|
348
|
+
Check_Type(value, T_ARRAY);
|
|
349
|
+
item_count = (size_t)RARRAY_LEN(value);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// cass_collection_new_from_data_type (not cass_collection_new) so that
|
|
353
|
+
// nested element types are propagated to the driver.
|
|
354
|
+
collection = cass_collection_new_from_data_type(data_type, item_count);
|
|
355
|
+
if (collection == NULL) {
|
|
356
|
+
rb_raise(eStatementError, "Failed to create a collection for %"PRIsVALUE" column", key);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
args.target.kind = bind_target_kind_collection;
|
|
360
|
+
args.target.as.collection = collection;
|
|
361
|
+
args.data_type = data_type;
|
|
362
|
+
args.value = value;
|
|
363
|
+
args.key = key;
|
|
364
|
+
|
|
365
|
+
rb_protect(statement_bind_collection_body, (VALUE)&args, &state);
|
|
366
|
+
if (state) {
|
|
367
|
+
cass_collection_free(collection);
|
|
368
|
+
rb_jump_tag(state);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
result = bind_target_collection(target, collection);
|
|
372
|
+
cass_collection_free(collection);
|
|
373
|
+
statement_bind_check(result, key);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
static VALUE statement_snapshot_value(const CassDataType *data_type, VALUE value);
|
|
377
|
+
|
|
378
|
+
struct statement_snapshot_map_ctx {
|
|
379
|
+
const CassDataType *key_type;
|
|
380
|
+
const CassDataType *value_type;
|
|
381
|
+
VALUE snapshot;
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
static int statement_snapshot_map_cb(VALUE k, VALUE v, VALUE arg)
|
|
385
|
+
{
|
|
386
|
+
struct statement_snapshot_map_ctx *ctx = (struct statement_snapshot_map_ctx *)arg;
|
|
387
|
+
|
|
388
|
+
rb_hash_aset(ctx->snapshot,
|
|
389
|
+
statement_snapshot_value(ctx->key_type, k),
|
|
390
|
+
statement_snapshot_value(ctx->value_type, v));
|
|
391
|
+
return ST_CONTINUE;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/*
|
|
395
|
+
* Returns a deep-frozen snapshot of the value so a later in-place mutation by
|
|
396
|
+
* the caller (or another thread) doesn't change what gets bound at execution
|
|
397
|
+
* time. Executions then only walk frozen containers, which makes re-binding
|
|
398
|
+
* on execute safe without re-validating.
|
|
399
|
+
*/
|
|
400
|
+
static VALUE statement_snapshot_value(const CassDataType *data_type, VALUE value)
|
|
401
|
+
{
|
|
402
|
+
if (NIL_P(value)) {
|
|
403
|
+
return Qnil;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
switch (cass_data_type_type(data_type)) {
|
|
407
|
+
case CASS_VALUE_TYPE_LIST:
|
|
408
|
+
case CASS_VALUE_TYPE_SET:
|
|
409
|
+
{
|
|
410
|
+
const CassDataType *element_type = cass_data_type_sub_data_type(data_type, 0);
|
|
411
|
+
VALUE array;
|
|
412
|
+
VALUE snapshot;
|
|
413
|
+
long length;
|
|
414
|
+
|
|
415
|
+
if (element_type == NULL) {
|
|
416
|
+
rb_raise(eStatementError, "Invalid collection type: missing element type");
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (RB_TYPE_P(value, T_ARRAY)) {
|
|
420
|
+
array = value;
|
|
421
|
+
} else if (rb_obj_is_kind_of(value, cSet)) {
|
|
422
|
+
array = rb_funcall(value, id_to_a, 0);
|
|
423
|
+
// A Set subclass may override to_a to return anything.
|
|
424
|
+
Check_Type(array, T_ARRAY);
|
|
425
|
+
} else {
|
|
426
|
+
rb_raise(rb_eTypeError, "no implicit conversion of %"PRIsVALUE" into Array or Set", rb_obj_class(value));
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
length = RARRAY_LEN(array);
|
|
430
|
+
snapshot = rb_ary_new_capa(length);
|
|
431
|
+
for (long i = 0; i < length; i++) {
|
|
432
|
+
rb_ary_push(snapshot, statement_snapshot_value(element_type, RARRAY_AREF(array, i)));
|
|
433
|
+
}
|
|
434
|
+
return rb_ary_freeze(snapshot);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
case CASS_VALUE_TYPE_MAP:
|
|
438
|
+
{
|
|
439
|
+
struct statement_snapshot_map_ctx ctx;
|
|
440
|
+
|
|
441
|
+
Check_Type(value, T_HASH);
|
|
442
|
+
ctx.key_type = cass_data_type_sub_data_type(data_type, 0);
|
|
443
|
+
ctx.value_type = cass_data_type_sub_data_type(data_type, 1);
|
|
444
|
+
if (ctx.key_type == NULL || ctx.value_type == NULL) {
|
|
445
|
+
rb_raise(eStatementError, "Invalid collection type: missing map key or value type");
|
|
446
|
+
}
|
|
447
|
+
ctx.snapshot = rb_hash_new();
|
|
448
|
+
rb_hash_foreach(value, statement_snapshot_map_cb, (VALUE)&ctx);
|
|
449
|
+
return rb_hash_freeze(ctx.snapshot);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
case CASS_VALUE_TYPE_TEXT:
|
|
453
|
+
case CASS_VALUE_TYPE_ASCII:
|
|
454
|
+
case CASS_VALUE_TYPE_VARCHAR:
|
|
455
|
+
if (SYMBOL_P(value)) {
|
|
456
|
+
// Symbols are immutable; no defensive copy needed.
|
|
457
|
+
return value;
|
|
458
|
+
}
|
|
459
|
+
// Also converts to_str objects here so the converted String (not the
|
|
460
|
+
// possibly mutable object) is what gets stored and re-bound.
|
|
461
|
+
StringValue(value);
|
|
462
|
+
return rb_str_new_frozen(value);
|
|
463
|
+
|
|
464
|
+
default:
|
|
163
465
|
if (RB_TYPE_P(value, T_STRING)) {
|
|
164
|
-
|
|
165
|
-
// doesn't change what gets bound at execution time.
|
|
166
|
-
value = rb_str_new_frozen(value);
|
|
466
|
+
return rb_str_new_frozen(value);
|
|
167
467
|
}
|
|
468
|
+
return value;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
static int hash_cb(VALUE key, VALUE value, VALUE arg)
|
|
473
|
+
{
|
|
474
|
+
statement_bind_context *ctx = (statement_bind_context *)arg;
|
|
475
|
+
const CassDataType* data_type;
|
|
476
|
+
const char *name;
|
|
477
|
+
statement_bind_target target;
|
|
478
|
+
|
|
479
|
+
if (SYMBOL_P(key)) {
|
|
480
|
+
key = rb_sym2str(key);
|
|
481
|
+
}
|
|
482
|
+
name = StringValueCStr(key);
|
|
483
|
+
|
|
484
|
+
data_type = cass_prepared_parameter_data_type_by_name(ctx->prepared, name);
|
|
485
|
+
if (data_type == NULL) {
|
|
486
|
+
rb_raise(eStatementError, "Invalid name %s was given.", name);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
target.kind = bind_target_kind_statement;
|
|
490
|
+
target.as.statement.statement = ctx->statement;
|
|
491
|
+
target.as.statement.name = name;
|
|
492
|
+
|
|
493
|
+
if (!NIL_P(ctx->bound_values)) {
|
|
494
|
+
// Snapshot before binding so the value bound now and the value
|
|
495
|
+
// replayed by later executions are the same deep-frozen object.
|
|
496
|
+
value = statement_snapshot_value(data_type, value);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
statement_bind_value(&target, data_type, value, key, NULL);
|
|
500
|
+
|
|
501
|
+
if (!NIL_P(ctx->bound_values)) {
|
|
168
502
|
rb_hash_aset(ctx->bound_values, key, value);
|
|
169
503
|
}
|
|
170
504
|
|
data/ilios.gemspec
CHANGED
|
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
|
12
12
|
spec.description = 'Cassandra driver written by C language'
|
|
13
13
|
spec.homepage = 'https://github.com/Watson1978/ilios'
|
|
14
14
|
spec.license = 'MIT'
|
|
15
|
-
spec.required_ruby_version = '>= 3.
|
|
15
|
+
spec.required_ruby_version = '>= 3.4.0'
|
|
16
16
|
|
|
17
17
|
spec.requirements << 'cmake'
|
|
18
18
|
|
data/lib/ilios/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ilios
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Watson
|
|
@@ -58,7 +58,7 @@ metadata:
|
|
|
58
58
|
homepage_uri: https://github.com/Watson1978/ilios
|
|
59
59
|
source_code_uri: https://github.com/Watson1978/ilios
|
|
60
60
|
bug_tracker_uri: https://github.com/Watson1978/ilios/issues
|
|
61
|
-
documentation_uri: https://www.rubydoc.info/gems/ilios/1.0
|
|
61
|
+
documentation_uri: https://www.rubydoc.info/gems/ilios/1.1.0
|
|
62
62
|
rubygems_mfa_required: 'true'
|
|
63
63
|
rdoc_options: []
|
|
64
64
|
require_paths:
|
|
@@ -67,7 +67,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
67
67
|
requirements:
|
|
68
68
|
- - ">="
|
|
69
69
|
- !ruby/object:Gem::Version
|
|
70
|
-
version: 3.
|
|
70
|
+
version: 3.4.0
|
|
71
71
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
72
|
requirements:
|
|
73
73
|
- - ">="
|
|
@@ -75,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
75
75
|
version: '0'
|
|
76
76
|
requirements:
|
|
77
77
|
- cmake
|
|
78
|
-
rubygems_version: 4.0.
|
|
78
|
+
rubygems_version: 4.0.18
|
|
79
79
|
specification_version: 4
|
|
80
80
|
summary: Cassandra driver written by C language
|
|
81
81
|
test_files: []
|