ilios 1.0.4 → 1.0.6
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 +10 -0
- data/ext/ilios/future.c +10 -0
- data/ext/ilios/ilios.h +22 -0
- data/ext/ilios/result.c +48 -26
- data/ext/ilios/session.c +17 -3
- data/ext/ilios/statement.c +109 -19
- data/lib/ilios/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ae1db76d702cb217eb8145f498dd86044183e441cbd896ac6acfd0d118823d13
|
|
4
|
+
data.tar.gz: 5e5e141d2578bdfd5c2b39b8b7e5b72e5a65de824a4e5868c3388e3308973d8c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2c78135d42da8f1c532c80531d0a493fed7f4bcf96ddf2d8aabdafac649740cb819a6f038640031993fe33fdc82c245e9725868f71149751aaaadf9ca3710d0b
|
|
7
|
+
data.tar.gz: 26771b46cf175596fdd14a19873783c151f3a1b02798ca43fa39b7828b8519a3316bbcafbc93a291d9c44cadb95d562c1e7fc6d30b19e48e2efb28592c72c674
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 1.0.6
|
|
4
|
+
|
|
5
|
+
- Fix uninitialized memory being returned for an undecodable column value (#27)
|
|
6
|
+
- Fix segmentation fault when a Result is used after Result#next_page fails (#26)
|
|
7
|
+
- Fix uninitialized memory being bound for a malformed UUID string (#25)
|
|
8
|
+
|
|
9
|
+
## 1.0.5
|
|
10
|
+
|
|
11
|
+
- Fix use-after-free when re-binding a statement with in-flight async executions (#24)
|
|
12
|
+
|
|
3
13
|
## 1.0.4
|
|
4
14
|
|
|
5
15
|
- Fix macOS build failure with Apple Clang on macOS 26+ (#23)
|
data/ext/ilios/future.c
CHANGED
|
@@ -116,6 +116,10 @@ static void future_result_success_yield(CassandraFuture *cassandra_future)
|
|
|
116
116
|
cassandra_result_obj = CREATE_RESULT(cassandra_result);
|
|
117
117
|
cassandra_result->result = cass_future_get_result(cassandra_future->future);
|
|
118
118
|
cassandra_result->statement_obj = cassandra_future->statement_obj;
|
|
119
|
+
// Hand over the executed CassStatement so Result#next_page
|
|
120
|
+
// can reuse it and it gets freed exactly once.
|
|
121
|
+
cassandra_result->executed_statement = cassandra_future->executed_statement;
|
|
122
|
+
cassandra_future->executed_statement = NULL;
|
|
119
123
|
|
|
120
124
|
obj = cassandra_result_obj;
|
|
121
125
|
}
|
|
@@ -202,6 +206,7 @@ VALUE future_create(CassFuture *future, VALUE session, VALUE statement, future_k
|
|
|
202
206
|
cassandra_future_obj = CREATE_FUTURE(cassandra_future);
|
|
203
207
|
cassandra_future->kind = kind;
|
|
204
208
|
cassandra_future->future = future;
|
|
209
|
+
cassandra_future->executed_statement = NULL;
|
|
205
210
|
cassandra_future->session_obj = session;
|
|
206
211
|
cassandra_future->statement_obj = statement;
|
|
207
212
|
cassandra_future->proc_mutex = rb_mutex_new();
|
|
@@ -366,6 +371,11 @@ static void future_destroy(void *ptr)
|
|
|
366
371
|
if (cassandra_future->future) {
|
|
367
372
|
cass_future_free(cassandra_future->future);
|
|
368
373
|
}
|
|
374
|
+
if (cassandra_future->executed_statement) {
|
|
375
|
+
// Safe even if the request is still in flight: the driver's request
|
|
376
|
+
// holds its own reference to the statement internals.
|
|
377
|
+
cass_statement_free(cassandra_future->executed_statement);
|
|
378
|
+
}
|
|
369
379
|
uv_sem_destroy(&cassandra_future->sem);
|
|
370
380
|
xfree(cassandra_future);
|
|
371
381
|
}
|
data/ext/ilios/ilios.h
CHANGED
|
@@ -27,6 +27,12 @@ typedef enum {
|
|
|
27
27
|
execute_async
|
|
28
28
|
} future_kind;
|
|
29
29
|
|
|
30
|
+
typedef enum {
|
|
31
|
+
idempotency_unset,
|
|
32
|
+
idempotency_false,
|
|
33
|
+
idempotency_true
|
|
34
|
+
} statement_idempotency;
|
|
35
|
+
|
|
30
36
|
typedef struct
|
|
31
37
|
{
|
|
32
38
|
CassCluster* cluster;
|
|
@@ -40,21 +46,36 @@ typedef struct
|
|
|
40
46
|
|
|
41
47
|
typedef struct
|
|
42
48
|
{
|
|
49
|
+
// Scratch statement used for validating values in Statement#bind.
|
|
50
|
+
// It is never handed to the driver: each execution gets its own
|
|
51
|
+
// CassStatement built from `prepared` and `bound_values`, because the
|
|
52
|
+
// driver encodes values asynchronously on its IO thread and re-binding
|
|
53
|
+
// an in-flight statement is a use-after-free (issue #12).
|
|
43
54
|
CassStatement* statement;
|
|
44
55
|
const CassPrepared* prepared;
|
|
45
56
|
VALUE session_obj;
|
|
57
|
+
VALUE bound_values;
|
|
58
|
+
int page_size;
|
|
59
|
+
statement_idempotency idempotent;
|
|
46
60
|
} CassandraStatement;
|
|
47
61
|
|
|
48
62
|
typedef struct
|
|
49
63
|
{
|
|
50
64
|
const CassResult *result;
|
|
51
65
|
CassFuture *future;
|
|
66
|
+
// The CassStatement this result was executed with (owned, freed on destroy).
|
|
67
|
+
// Not to be confused with statement_obj, the Ruby Statement object.
|
|
68
|
+
CassStatement *executed_statement;
|
|
52
69
|
VALUE statement_obj;
|
|
53
70
|
} CassandraResult;
|
|
54
71
|
|
|
55
72
|
typedef struct
|
|
56
73
|
{
|
|
57
74
|
CassFuture *future;
|
|
75
|
+
// The CassStatement this future's execution was submitted with (owned,
|
|
76
|
+
// freed on destroy unless handed over to the result). Not to be confused
|
|
77
|
+
// with statement_obj, the Ruby Statement object.
|
|
78
|
+
CassStatement *executed_statement;
|
|
58
79
|
future_kind kind;
|
|
59
80
|
|
|
60
81
|
VALUE session_obj;
|
|
@@ -108,6 +129,7 @@ extern CassFuture *nogvl_session_execute(CassSession* session, CassStatement* st
|
|
|
108
129
|
extern void nogvl_sem_wait(uv_sem_t *sem);
|
|
109
130
|
|
|
110
131
|
extern void statement_default_config(CassandraStatement *cassandra_statement);
|
|
132
|
+
extern CassStatement *statement_build_for_execution(CassandraStatement *cassandra_statement);
|
|
111
133
|
extern void result_await(CassandraResult *cassandra_result);
|
|
112
134
|
|
|
113
135
|
|
data/ext/ilios/result.c
CHANGED
|
@@ -45,6 +45,7 @@ static VALUE result_next_page(VALUE self)
|
|
|
45
45
|
CassandraStatement *cassandra_statement;
|
|
46
46
|
CassandraSession *cassandra_session;
|
|
47
47
|
CassFuture *result_future;
|
|
48
|
+
CassError error_code;
|
|
48
49
|
|
|
49
50
|
GET_RESULT(self, cassandra_result);
|
|
50
51
|
|
|
@@ -55,19 +56,37 @@ static VALUE result_next_page(VALUE self)
|
|
|
55
56
|
GET_STATEMENT(cassandra_result->statement_obj, cassandra_statement);
|
|
56
57
|
GET_SESSION(cassandra_statement->session_obj, cassandra_session);
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
// Reuse this result's own executed statement: it is not shared with other
|
|
60
|
+
// executions and its previous request has already completed, so setting
|
|
61
|
+
// the paging state cannot race with the driver's IO thread.
|
|
62
|
+
cass_statement_set_paging_state(cassandra_result->executed_statement, cassandra_result->result);
|
|
59
63
|
|
|
60
|
-
result_future = nogvl_session_execute(cassandra_session->session,
|
|
64
|
+
result_future = nogvl_session_execute(cassandra_session->session, cassandra_result->executed_statement);
|
|
65
|
+
nogvl_future_wait(result_future);
|
|
66
|
+
|
|
67
|
+
error_code = cass_future_error_code(result_future);
|
|
68
|
+
if (error_code != CASS_OK) {
|
|
69
|
+
cass_future_free(result_future);
|
|
70
|
+
rb_raise(eExecutionError, "Unable to wait executing: %s", cass_error_desc(error_code));
|
|
71
|
+
}
|
|
61
72
|
|
|
62
73
|
cass_result_free(cassandra_result->result);
|
|
63
|
-
|
|
64
|
-
|
|
74
|
+
if (cassandra_result->future) {
|
|
75
|
+
cass_future_free(cassandra_result->future);
|
|
76
|
+
}
|
|
77
|
+
cassandra_result->result = cass_future_get_result(result_future);
|
|
65
78
|
cassandra_result->future = result_future;
|
|
66
79
|
|
|
67
|
-
result_await(cassandra_result);
|
|
68
80
|
return self;
|
|
69
81
|
}
|
|
70
82
|
|
|
83
|
+
static void result_check_value(CassError error_code, VALUE key)
|
|
84
|
+
{
|
|
85
|
+
if (error_code != CASS_OK) {
|
|
86
|
+
rb_raise(eExecutionError, "Unable to get value of %"PRIsVALUE" column: %s", key, cass_error_desc(error_code));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
71
90
|
static VALUE result_convert_row(const CassResult *result, const CassRow *row, size_t column_count)
|
|
72
91
|
{
|
|
73
92
|
VALUE key;
|
|
@@ -90,56 +109,56 @@ static VALUE result_convert_row(const CassResult *result, const CassRow *row, si
|
|
|
90
109
|
switch (type) {
|
|
91
110
|
case CASS_VALUE_TYPE_TINY_INT:
|
|
92
111
|
{
|
|
93
|
-
cass_int8_t output;
|
|
94
|
-
cass_value_get_int8(value, &output);
|
|
112
|
+
cass_int8_t output = 0;
|
|
113
|
+
result_check_value(cass_value_get_int8(value, &output), key);
|
|
95
114
|
rb_hash_aset(hash, key, INT2NUM(output));
|
|
96
115
|
}
|
|
97
116
|
break;
|
|
98
117
|
|
|
99
118
|
case CASS_VALUE_TYPE_SMALL_INT:
|
|
100
119
|
{
|
|
101
|
-
cass_int16_t output;
|
|
102
|
-
cass_value_get_int16(value, &output);
|
|
120
|
+
cass_int16_t output = 0;
|
|
121
|
+
result_check_value(cass_value_get_int16(value, &output), key);
|
|
103
122
|
rb_hash_aset(hash, key, INT2NUM(output));
|
|
104
123
|
}
|
|
105
124
|
break;
|
|
106
125
|
|
|
107
126
|
case CASS_VALUE_TYPE_INT:
|
|
108
127
|
{
|
|
109
|
-
cass_int32_t output;
|
|
110
|
-
cass_value_get_int32(value, &output);
|
|
128
|
+
cass_int32_t output = 0;
|
|
129
|
+
result_check_value(cass_value_get_int32(value, &output), key);
|
|
111
130
|
rb_hash_aset(hash, key, INT2NUM(output));
|
|
112
131
|
}
|
|
113
132
|
break;
|
|
114
133
|
|
|
115
134
|
case CASS_VALUE_TYPE_BIGINT:
|
|
116
135
|
{
|
|
117
|
-
cass_int64_t output;
|
|
118
|
-
cass_value_get_int64(value, &output);
|
|
136
|
+
cass_int64_t output = 0;
|
|
137
|
+
result_check_value(cass_value_get_int64(value, &output), key);
|
|
119
138
|
rb_hash_aset(hash, key, LL2NUM(output));
|
|
120
139
|
}
|
|
121
140
|
break;
|
|
122
141
|
|
|
123
142
|
case CASS_VALUE_TYPE_FLOAT:
|
|
124
143
|
{
|
|
125
|
-
cass_float_t output;
|
|
126
|
-
cass_value_get_float(value, &output);
|
|
144
|
+
cass_float_t output = 0;
|
|
145
|
+
result_check_value(cass_value_get_float(value, &output), key);
|
|
127
146
|
rb_hash_aset(hash, key, DBL2NUM(output));
|
|
128
147
|
}
|
|
129
148
|
break;
|
|
130
149
|
|
|
131
150
|
case CASS_VALUE_TYPE_DOUBLE:
|
|
132
151
|
{
|
|
133
|
-
cass_double_t output;
|
|
134
|
-
cass_value_get_double(value, &output);
|
|
152
|
+
cass_double_t output = 0;
|
|
153
|
+
result_check_value(cass_value_get_double(value, &output), key);
|
|
135
154
|
rb_hash_aset(hash, key, DBL2NUM(output));
|
|
136
155
|
}
|
|
137
156
|
break;
|
|
138
157
|
|
|
139
158
|
case CASS_VALUE_TYPE_BOOLEAN:
|
|
140
159
|
{
|
|
141
|
-
cass_bool_t output;
|
|
142
|
-
cass_value_get_bool(value, &output);
|
|
160
|
+
cass_bool_t output = cass_false;
|
|
161
|
+
result_check_value(cass_value_get_bool(value, &output), key);
|
|
143
162
|
rb_hash_aset(hash, key, output == cass_true ? Qtrue : Qfalse);
|
|
144
163
|
}
|
|
145
164
|
break;
|
|
@@ -148,26 +167,26 @@ static VALUE result_convert_row(const CassResult *result, const CassRow *row, si
|
|
|
148
167
|
case CASS_VALUE_TYPE_ASCII:
|
|
149
168
|
case CASS_VALUE_TYPE_VARCHAR:
|
|
150
169
|
{
|
|
151
|
-
const char* s;
|
|
152
|
-
size_t s_length;
|
|
153
|
-
cass_value_get_string(value, &s, &s_length);
|
|
170
|
+
const char* s = NULL;
|
|
171
|
+
size_t s_length = 0;
|
|
172
|
+
result_check_value(cass_value_get_string(value, &s, &s_length), key);
|
|
154
173
|
rb_hash_aset(hash, key, rb_str_new(s, s_length));
|
|
155
174
|
}
|
|
156
175
|
break;
|
|
157
176
|
|
|
158
177
|
case CASS_VALUE_TYPE_TIMESTAMP:
|
|
159
178
|
{
|
|
160
|
-
cass_int64_t output;
|
|
161
|
-
cass_value_get_int64(value, &output);
|
|
179
|
+
cass_int64_t output = 0;
|
|
180
|
+
result_check_value(cass_value_get_int64(value, &output), key);
|
|
162
181
|
rb_hash_aset(hash, key, rb_time_new(output / 1000, output % 1000 * 1000));
|
|
163
182
|
}
|
|
164
183
|
break;
|
|
165
184
|
|
|
166
185
|
case CASS_VALUE_TYPE_UUID:
|
|
167
186
|
{
|
|
168
|
-
CassUuid output;
|
|
187
|
+
CassUuid output = { 0, 0 };
|
|
169
188
|
char uuid[40];
|
|
170
|
-
cass_value_get_uuid(value, &output);
|
|
189
|
+
result_check_value(cass_value_get_uuid(value, &output), key);
|
|
171
190
|
cass_uuid_string(output, uuid);
|
|
172
191
|
rb_hash_aset(hash, key, rb_str_new2(uuid));
|
|
173
192
|
}
|
|
@@ -245,6 +264,9 @@ static void result_destroy(void *ptr)
|
|
|
245
264
|
if (cassandra_result->future) {
|
|
246
265
|
cass_future_free(cassandra_result->future);
|
|
247
266
|
}
|
|
267
|
+
if (cassandra_result->executed_statement) {
|
|
268
|
+
cass_statement_free(cassandra_result->executed_statement);
|
|
269
|
+
}
|
|
248
270
|
xfree(cassandra_result);
|
|
249
271
|
}
|
|
250
272
|
|
data/ext/ilios/session.c
CHANGED
|
@@ -85,13 +85,24 @@ static VALUE session_execute_async(VALUE self, VALUE statement)
|
|
|
85
85
|
{
|
|
86
86
|
CassandraSession *cassandra_session;
|
|
87
87
|
CassandraStatement *cassandra_statement;
|
|
88
|
+
CassandraFuture *cassandra_future;
|
|
89
|
+
CassStatement *executed_statement;
|
|
88
90
|
CassFuture *result_future;
|
|
91
|
+
VALUE future;
|
|
89
92
|
|
|
90
93
|
GET_SESSION(self, cassandra_session);
|
|
91
94
|
GET_STATEMENT(statement, cassandra_statement);
|
|
92
95
|
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
// Execute a dedicated statement so that later re-binds of `statement`
|
|
97
|
+
// cannot race with the driver's asynchronous encoding (issue #12).
|
|
98
|
+
executed_statement = statement_build_for_execution(cassandra_statement);
|
|
99
|
+
result_future = nogvl_session_execute(cassandra_session->session, executed_statement);
|
|
100
|
+
|
|
101
|
+
future = future_create(result_future, self, statement, execute_async);
|
|
102
|
+
GET_FUTURE(future, cassandra_future);
|
|
103
|
+
// The future owns the executed statement and frees it on destroy.
|
|
104
|
+
cassandra_future->executed_statement = executed_statement;
|
|
105
|
+
return future;
|
|
95
106
|
}
|
|
96
107
|
|
|
97
108
|
/**
|
|
@@ -107,15 +118,18 @@ static VALUE session_execute(VALUE self, VALUE statement)
|
|
|
107
118
|
CassandraSession *cassandra_session;
|
|
108
119
|
CassandraStatement *cassandra_statement;
|
|
109
120
|
CassandraResult *cassandra_result;
|
|
121
|
+
CassStatement *executed_statement;
|
|
110
122
|
CassFuture *result_future;
|
|
111
123
|
VALUE cassandra_result_obj;
|
|
112
124
|
|
|
113
125
|
GET_SESSION(self, cassandra_session);
|
|
114
126
|
GET_STATEMENT(statement, cassandra_statement);
|
|
115
127
|
|
|
116
|
-
|
|
128
|
+
executed_statement = statement_build_for_execution(cassandra_statement);
|
|
129
|
+
result_future = nogvl_session_execute(cassandra_session->session, executed_statement);
|
|
117
130
|
|
|
118
131
|
cassandra_result_obj = CREATE_RESULT(cassandra_result);
|
|
132
|
+
cassandra_result->executed_statement = executed_statement;
|
|
119
133
|
cassandra_result->future = result_future;
|
|
120
134
|
cassandra_result->statement_obj = statement;
|
|
121
135
|
|
data/ext/ilios/statement.c
CHANGED
|
@@ -17,14 +17,24 @@ const rb_data_type_t cassandra_statement_data_type = {
|
|
|
17
17
|
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
typedef struct
|
|
21
|
+
{
|
|
22
|
+
const CassPrepared *prepared;
|
|
23
|
+
CassStatement *statement;
|
|
24
|
+
VALUE bound_values;
|
|
25
|
+
} statement_bind_context;
|
|
26
|
+
|
|
20
27
|
void statement_default_config(CassandraStatement *cassandra_statement)
|
|
21
28
|
{
|
|
29
|
+
cassandra_statement->bound_values = Qnil;
|
|
30
|
+
cassandra_statement->page_size = DEFAULT_PAGE_SIZE;
|
|
31
|
+
cassandra_statement->idempotent = idempotency_unset;
|
|
22
32
|
cass_statement_set_paging_size(cassandra_statement->statement, DEFAULT_PAGE_SIZE);
|
|
23
33
|
}
|
|
24
34
|
|
|
25
|
-
static int hash_cb(VALUE key, VALUE value, VALUE
|
|
35
|
+
static int hash_cb(VALUE key, VALUE value, VALUE arg)
|
|
26
36
|
{
|
|
27
|
-
|
|
37
|
+
statement_bind_context *ctx = (statement_bind_context *)arg;
|
|
28
38
|
const CassDataType* data_type;
|
|
29
39
|
CassValueType value_type;
|
|
30
40
|
CassError result;
|
|
@@ -35,14 +45,14 @@ static int hash_cb(VALUE key, VALUE value, VALUE statement)
|
|
|
35
45
|
}
|
|
36
46
|
name = StringValueCStr(key);
|
|
37
47
|
|
|
38
|
-
data_type = cass_prepared_parameter_data_type_by_name(
|
|
48
|
+
data_type = cass_prepared_parameter_data_type_by_name(ctx->prepared, name);
|
|
39
49
|
if (data_type == NULL) {
|
|
40
50
|
rb_raise(eStatementError, "Invalid name %s was given.", name);
|
|
41
51
|
}
|
|
42
52
|
value_type = cass_data_type_type(data_type);
|
|
43
53
|
|
|
44
54
|
if (NIL_P(value)) {
|
|
45
|
-
result = cass_statement_bind_null_by_name(
|
|
55
|
+
result = cass_statement_bind_null_by_name(ctx->statement, name);
|
|
46
56
|
goto result_check;
|
|
47
57
|
}
|
|
48
58
|
|
|
@@ -54,7 +64,7 @@ static int hash_cb(VALUE key, VALUE value, VALUE statement)
|
|
|
54
64
|
if (v < INT8_MIN || v > INT8_MAX) {
|
|
55
65
|
rb_raise(rb_eRangeError, "Invalid value: %ld", v);
|
|
56
66
|
}
|
|
57
|
-
result = cass_statement_bind_int8_by_name(
|
|
67
|
+
result = cass_statement_bind_int8_by_name(ctx->statement, name, (cass_int8_t)v);
|
|
58
68
|
}
|
|
59
69
|
break;
|
|
60
70
|
|
|
@@ -66,7 +76,7 @@ static int hash_cb(VALUE key, VALUE value, VALUE statement)
|
|
|
66
76
|
rb_raise(rb_eRangeError, "Invalid value: %ld", v);
|
|
67
77
|
}
|
|
68
78
|
|
|
69
|
-
result = cass_statement_bind_int16_by_name(
|
|
79
|
+
result = cass_statement_bind_int16_by_name(ctx->statement, name, (cass_int16_t)v);
|
|
70
80
|
}
|
|
71
81
|
break;
|
|
72
82
|
|
|
@@ -78,12 +88,12 @@ static int hash_cb(VALUE key, VALUE value, VALUE statement)
|
|
|
78
88
|
rb_raise(rb_eRangeError, "Invalid value: %ld", v);
|
|
79
89
|
}
|
|
80
90
|
|
|
81
|
-
result = cass_statement_bind_int32_by_name(
|
|
91
|
+
result = cass_statement_bind_int32_by_name(ctx->statement, name, (cass_int32_t)v);
|
|
82
92
|
}
|
|
83
93
|
break;
|
|
84
94
|
|
|
85
95
|
case CASS_VALUE_TYPE_BIGINT:
|
|
86
|
-
result = cass_statement_bind_int64_by_name(
|
|
96
|
+
result = cass_statement_bind_int64_by_name(ctx->statement, name, NUM2LONG(value));
|
|
87
97
|
break;
|
|
88
98
|
|
|
89
99
|
case CASS_VALUE_TYPE_FLOAT:
|
|
@@ -94,25 +104,25 @@ static int hash_cb(VALUE key, VALUE value, VALUE statement)
|
|
|
94
104
|
rb_raise(rb_eRangeError, "Invalid value: %lf", v);
|
|
95
105
|
}
|
|
96
106
|
|
|
97
|
-
result = cass_statement_bind_float_by_name(
|
|
107
|
+
result = cass_statement_bind_float_by_name(ctx->statement, name, v);
|
|
98
108
|
}
|
|
99
109
|
break;
|
|
100
110
|
|
|
101
111
|
case CASS_VALUE_TYPE_DOUBLE:
|
|
102
|
-
result = cass_statement_bind_double_by_name(
|
|
112
|
+
result = cass_statement_bind_double_by_name(ctx->statement, name, NUM2DBL(value));
|
|
103
113
|
break;
|
|
104
114
|
|
|
105
115
|
case CASS_VALUE_TYPE_BOOLEAN:
|
|
106
116
|
{
|
|
107
117
|
cass_bool_t v = RTEST(value) ? cass_true : cass_false;
|
|
108
|
-
result = cass_statement_bind_bool_by_name(
|
|
118
|
+
result = cass_statement_bind_bool_by_name(ctx->statement, name, v);
|
|
109
119
|
}
|
|
110
120
|
break;
|
|
111
121
|
|
|
112
122
|
case CASS_VALUE_TYPE_TEXT:
|
|
113
123
|
case CASS_VALUE_TYPE_ASCII:
|
|
114
124
|
case CASS_VALUE_TYPE_VARCHAR:
|
|
115
|
-
result = cass_statement_bind_string_by_name(
|
|
125
|
+
result = cass_statement_bind_string_by_name(ctx->statement, name, StringValueCStr(value));
|
|
116
126
|
break;
|
|
117
127
|
|
|
118
128
|
case CASS_VALUE_TYPE_TIMESTAMP:
|
|
@@ -123,16 +133,20 @@ static int hash_cb(VALUE key, VALUE value, VALUE statement)
|
|
|
123
133
|
rb_raise(rb_eTypeError, "no implicit conversion of %"PRIsVALUE" to Time", rb_obj_class(value));
|
|
124
134
|
}
|
|
125
135
|
}
|
|
126
|
-
result = cass_statement_bind_int64_by_name(
|
|
136
|
+
result = cass_statement_bind_int64_by_name(ctx->statement, name, (cass_int64_t)(NUM2DBL(rb_Float(value)) * 1000));
|
|
127
137
|
break;
|
|
128
138
|
|
|
129
139
|
case CASS_VALUE_TYPE_UUID:
|
|
130
140
|
{
|
|
131
|
-
CassUuid uuid;
|
|
141
|
+
CassUuid uuid = { 0, 0 };
|
|
132
142
|
const char *uuid_string = StringValueCStr(value);
|
|
133
143
|
|
|
134
|
-
cass_uuid_from_string(uuid_string, &uuid);
|
|
135
|
-
|
|
144
|
+
result = cass_uuid_from_string(uuid_string, &uuid);
|
|
145
|
+
if (result != CASS_OK) {
|
|
146
|
+
rb_raise(eStatementError, "Invalid UUID was given: %s=%"PRIsVALUE"", name, value);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
result = cass_statement_bind_uuid_by_name(ctx->statement, name, uuid);
|
|
136
150
|
}
|
|
137
151
|
break;
|
|
138
152
|
|
|
@@ -145,9 +159,65 @@ result_check:
|
|
|
145
159
|
rb_raise(eStatementError, "Failed to bind value: %s", cass_error_desc(result));
|
|
146
160
|
}
|
|
147
161
|
|
|
162
|
+
if (!NIL_P(ctx->bound_values)) {
|
|
163
|
+
if (RB_TYPE_P(value, T_STRING)) {
|
|
164
|
+
// Snapshot the value so a later in-place mutation by the caller
|
|
165
|
+
// doesn't change what gets bound at execution time.
|
|
166
|
+
value = rb_str_new_frozen(value);
|
|
167
|
+
}
|
|
168
|
+
rb_hash_aset(ctx->bound_values, key, value);
|
|
169
|
+
}
|
|
170
|
+
|
|
148
171
|
return ST_CONTINUE;
|
|
149
172
|
}
|
|
150
173
|
|
|
174
|
+
typedef struct
|
|
175
|
+
{
|
|
176
|
+
statement_bind_context ctx;
|
|
177
|
+
VALUE hash;
|
|
178
|
+
} statement_rebind_args;
|
|
179
|
+
|
|
180
|
+
static VALUE statement_rebind_body(VALUE arg)
|
|
181
|
+
{
|
|
182
|
+
statement_rebind_args *args = (statement_rebind_args *)arg;
|
|
183
|
+
|
|
184
|
+
rb_hash_foreach(args->hash, hash_cb, (VALUE)&args->ctx);
|
|
185
|
+
return Qnil;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/*
|
|
189
|
+
* Builds a fresh CassStatement carrying the current configuration and bound
|
|
190
|
+
* values for a single execution. The returned statement must not be mutated
|
|
191
|
+
* once handed to the driver, and the caller owns it: it must stay alive until
|
|
192
|
+
* the execution's future resolves and be freed exactly once afterwards.
|
|
193
|
+
*/
|
|
194
|
+
CassStatement *statement_build_for_execution(CassandraStatement *cassandra_statement)
|
|
195
|
+
{
|
|
196
|
+
CassStatement *statement = cass_prepared_bind(cassandra_statement->prepared);
|
|
197
|
+
|
|
198
|
+
cass_statement_set_paging_size(statement, cassandra_statement->page_size);
|
|
199
|
+
if (cassandra_statement->idempotent != idempotency_unset) {
|
|
200
|
+
cass_statement_set_is_idempotent(statement, cassandra_statement->idempotent == idempotency_true ? cass_true : cass_false);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (!NIL_P(cassandra_statement->bound_values)) {
|
|
204
|
+
statement_rebind_args args;
|
|
205
|
+
int state = 0;
|
|
206
|
+
|
|
207
|
+
args.ctx.prepared = cassandra_statement->prepared;
|
|
208
|
+
args.ctx.statement = statement;
|
|
209
|
+
args.ctx.bound_values = Qnil;
|
|
210
|
+
args.hash = cassandra_statement->bound_values;
|
|
211
|
+
|
|
212
|
+
rb_protect(statement_rebind_body, (VALUE)&args, &state);
|
|
213
|
+
if (state) {
|
|
214
|
+
cass_statement_free(statement);
|
|
215
|
+
rb_jump_tag(state);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return statement;
|
|
219
|
+
}
|
|
220
|
+
|
|
151
221
|
/**
|
|
152
222
|
* Binds a specified column value to a query.
|
|
153
223
|
* A hash object should be given with column name as key.
|
|
@@ -161,11 +231,27 @@ result_check:
|
|
|
161
231
|
static VALUE statement_bind(VALUE self, VALUE hash)
|
|
162
232
|
{
|
|
163
233
|
CassandraStatement *cassandra_statement;
|
|
234
|
+
statement_bind_context ctx;
|
|
235
|
+
VALUE bound_values;
|
|
164
236
|
|
|
165
237
|
Check_Type(hash, T_HASH);
|
|
166
238
|
TypedData_Get_Struct(self, CassandraStatement, &cassandra_statement_data_type, cassandra_statement);
|
|
167
239
|
|
|
168
|
-
|
|
240
|
+
// Merge into a copy instead of mutating in place: the previous hash may be
|
|
241
|
+
// shared with a frozen (Ractor-shareable) statement or be iterated by an
|
|
242
|
+
// execution on another thread.
|
|
243
|
+
if (NIL_P(cassandra_statement->bound_values)) {
|
|
244
|
+
bound_values = rb_hash_new();
|
|
245
|
+
} else {
|
|
246
|
+
bound_values = rb_hash_dup(cassandra_statement->bound_values);
|
|
247
|
+
}
|
|
248
|
+
RB_OBJ_WRITE(self, &cassandra_statement->bound_values, bound_values);
|
|
249
|
+
|
|
250
|
+
ctx.prepared = cassandra_statement->prepared;
|
|
251
|
+
ctx.statement = cassandra_statement->statement;
|
|
252
|
+
ctx.bound_values = bound_values;
|
|
253
|
+
|
|
254
|
+
rb_hash_foreach(hash, hash_cb, (VALUE)&ctx);
|
|
169
255
|
return self;
|
|
170
256
|
}
|
|
171
257
|
|
|
@@ -180,7 +266,8 @@ static VALUE statement_page_size(VALUE self, VALUE page_size)
|
|
|
180
266
|
CassandraStatement *cassandra_statement;
|
|
181
267
|
|
|
182
268
|
GET_STATEMENT(self, cassandra_statement);
|
|
183
|
-
|
|
269
|
+
cassandra_statement->page_size = NUM2INT(page_size);
|
|
270
|
+
cass_statement_set_paging_size(cassandra_statement->statement, cassandra_statement->page_size);
|
|
184
271
|
return self;
|
|
185
272
|
}
|
|
186
273
|
|
|
@@ -197,7 +284,8 @@ static VALUE statement_idempotent(VALUE self, VALUE idempotent)
|
|
|
197
284
|
CassandraStatement *cassandra_statement;
|
|
198
285
|
|
|
199
286
|
GET_STATEMENT(self, cassandra_statement);
|
|
200
|
-
|
|
287
|
+
cassandra_statement->idempotent = RTEST(idempotent) ? idempotency_true : idempotency_false;
|
|
288
|
+
cass_statement_set_is_idempotent(cassandra_statement->statement, cassandra_statement->idempotent == idempotency_true ? cass_true : cass_false);
|
|
201
289
|
return self;
|
|
202
290
|
}
|
|
203
291
|
|
|
@@ -205,6 +293,7 @@ static void statement_mark(void *ptr)
|
|
|
205
293
|
{
|
|
206
294
|
CassandraStatement *cassandra_statement = (CassandraStatement *)ptr;
|
|
207
295
|
rb_gc_mark_movable(cassandra_statement->session_obj);
|
|
296
|
+
rb_gc_mark_movable(cassandra_statement->bound_values);
|
|
208
297
|
}
|
|
209
298
|
|
|
210
299
|
static void statement_destroy(void *ptr)
|
|
@@ -230,6 +319,7 @@ static void statement_compact(void *ptr)
|
|
|
230
319
|
CassandraStatement *cassandra_statement = (CassandraStatement *)ptr;
|
|
231
320
|
|
|
232
321
|
cassandra_statement->session_obj = rb_gc_location(cassandra_statement->session_obj);
|
|
322
|
+
cassandra_statement->bound_values = rb_gc_location(cassandra_statement->bound_values);
|
|
233
323
|
}
|
|
234
324
|
|
|
235
325
|
void Init_statement(void)
|
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.0.6
|
|
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.0.6
|
|
62
62
|
rubygems_mfa_required: 'true'
|
|
63
63
|
rdoc_options: []
|
|
64
64
|
require_paths:
|
|
@@ -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.16
|
|
79
79
|
specification_version: 4
|
|
80
80
|
summary: Cassandra driver written by C language
|
|
81
81
|
test_files: []
|