ilios 1.1.2 → 1.2.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 +9 -0
- data/README.md +6 -2
- data/ext/ilios/cluster.c +66 -48
- data/ext/ilios/future.c +10 -1
- data/ext/ilios/ilios.c +48 -0
- data/ext/ilios/ilios.h +23 -1
- data/ext/ilios/result.c +16 -5
- data/ext/ilios/session.c +2 -3
- data/ext/ilios/statement.c +4 -1
- data/lib/ilios/version.rb +1 -1
- data/sig/ilios.rbs +14 -2
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc846b97514ac6b86eccf9a06eee87d35396f26746974e6927d7d99f0bcf33ed
|
|
4
|
+
data.tar.gz: cd91086e384b4af2fc3a39cb0c5234722947dbb8dc097782a4508df9bf73615f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bf94440639db92113db0a8c06a05f2409139644268988aa7c2c94f202bab494d035fd6107596010f07222feecbe92cad8c3f14ed169061d16d91fe69db8f05f9
|
|
7
|
+
data.tar.gz: 2b514895a3a71380e5fab10198a92e1dc1a9cdea7d84d10dad344658f14efbc7884c8fa49e3c80bcd9a8cb7942f0cb1d846a7e92cfca9ae02ff8b785989c6db5
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 1.2.0
|
|
4
|
+
|
|
5
|
+
- Fix cluster setters silently ignoring invalid values in `Cluster#port`, `#protocol_version`, `#connect_timeout`, `#request_timeout` and `#resolve_timeout` (#36)
|
|
6
|
+
- Fix segfault when a `Cluster` created by `allocate` or `dup` is used without running `initialize` (#37)
|
|
7
|
+
- Yield the failure reason as an `Ilios::Cassandra::ExecutionError` to `Future#on_failure` blocks that accept an argument, including variadic blocks (e.g. `{ |*args| }`); zero-arity blocks are unchanged (#40)
|
|
8
|
+
- The synchronous API (`Session#prepare`, `Session#execute`, `Result#next_page`, `Cluster#connect`) now raises errors carrying the server-reported message and a `#code` Integer, same as `Future#on_failure` (#40)
|
|
9
|
+
- `Ilios::Cassandra::StatementError` also carries a `#code` Integer, so `#code` is answered by every error class the driver raises (#40)
|
|
10
|
+
- Fix `Future#on_success` RBS signature to allow the `Statement` yielded by `prepare_async` (#42)
|
|
11
|
+
|
|
3
12
|
## 1.1.2
|
|
4
13
|
|
|
5
14
|
- Support username/password authentication with `Cluster#credentials` (#33)
|
data/README.md
CHANGED
|
@@ -226,8 +226,12 @@ prepare_future.on_success { |statement|
|
|
|
226
226
|
p result
|
|
227
227
|
p "success"
|
|
228
228
|
}
|
|
229
|
-
|
|
230
|
-
|
|
229
|
+
# `error` is an `Ilios::Cassandra::ExecutionError` (a StandardError with
|
|
230
|
+
# a #code Integer attribute); the block is optional-arity, so `{ p "fail" }`
|
|
231
|
+
# still works without it.
|
|
232
|
+
result_future.on_failure { |error|
|
|
233
|
+
p error.message
|
|
234
|
+
p error.code
|
|
231
235
|
}
|
|
232
236
|
|
|
233
237
|
futures << result_future
|
data/ext/ilios/cluster.c
CHANGED
|
@@ -23,6 +23,36 @@ static VALUE cluster_allocator(VALUE klass)
|
|
|
23
23
|
return CREATE_CLUSTER(cassandra_cluster);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
static void cluster_check_error(CassError error, const char *name)
|
|
27
|
+
{
|
|
28
|
+
if (error != CASS_OK) {
|
|
29
|
+
rb_raise(rb_eArgError, "Invalid %s: %s", name, cass_error_desc(error));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Casting a negative value to unsigned wraps it into a huge number, so check
|
|
34
|
+
// the range on the signed value NUM2LONG/NUM2LL already produced. Converting a
|
|
35
|
+
// second time would run to_int again, which may return a different value.
|
|
36
|
+
static unsigned cluster_value_to_uint(VALUE value, const char *name)
|
|
37
|
+
{
|
|
38
|
+
long v = NUM2LONG(value);
|
|
39
|
+
|
|
40
|
+
if (v < 0 || v > UINT_MAX) {
|
|
41
|
+
rb_raise(rb_eRangeError, "Invalid %s: %ld", name, v);
|
|
42
|
+
}
|
|
43
|
+
return (unsigned)v;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static cass_uint64_t cluster_value_to_uint64(VALUE value, const char *name)
|
|
47
|
+
{
|
|
48
|
+
long long v = NUM2LL(value);
|
|
49
|
+
|
|
50
|
+
if (v < 0) {
|
|
51
|
+
rb_raise(rb_eRangeError, "Invalid %s: %lld", name, v);
|
|
52
|
+
}
|
|
53
|
+
return (cass_uint64_t)v;
|
|
54
|
+
}
|
|
55
|
+
|
|
26
56
|
/**
|
|
27
57
|
* Creates a new cluster.
|
|
28
58
|
*
|
|
@@ -32,7 +62,7 @@ static VALUE cluster_initialize(VALUE self)
|
|
|
32
62
|
{
|
|
33
63
|
CassandraCluster *cassandra_cluster;
|
|
34
64
|
|
|
35
|
-
|
|
65
|
+
GET_UNINITIALIZED_CLUSTER(self, cassandra_cluster);
|
|
36
66
|
cassandra_cluster->cluster = cass_cluster_new();
|
|
37
67
|
|
|
38
68
|
return self;
|
|
@@ -65,12 +95,10 @@ static VALUE cluster_connect(VALUE self)
|
|
|
65
95
|
nogvl_future_wait(connect_future);
|
|
66
96
|
|
|
67
97
|
if (cass_future_error_code(connect_future) != CASS_OK) {
|
|
68
|
-
|
|
98
|
+
VALUE error = ilios_future_error_new(eConnectError, "Unable to connect", connect_future);
|
|
69
99
|
|
|
70
|
-
strncpy(error, cass_error_desc(cass_future_error_code(connect_future)), sizeof(error) - 1);
|
|
71
100
|
cass_future_free(connect_future);
|
|
72
|
-
|
|
73
|
-
return Qnil;
|
|
101
|
+
rb_exc_raise(error);
|
|
74
102
|
}
|
|
75
103
|
cass_future_free(connect_future);
|
|
76
104
|
|
|
@@ -110,13 +138,14 @@ static VALUE cluster_hosts(VALUE self, VALUE hosts)
|
|
|
110
138
|
*
|
|
111
139
|
* @param port [Integer] A port number.
|
|
112
140
|
* @return [Cassandra::Cluster] self.
|
|
141
|
+
* @raise [ArgumentError] If the port is zero or negative.
|
|
113
142
|
*/
|
|
114
143
|
static VALUE cluster_port(VALUE self, VALUE port)
|
|
115
144
|
{
|
|
116
145
|
CassandraCluster *cassandra_cluster;
|
|
117
146
|
|
|
118
147
|
GET_CLUSTER(self, cassandra_cluster);
|
|
119
|
-
cass_cluster_set_port(cassandra_cluster->cluster, NUM2INT(port));
|
|
148
|
+
cluster_check_error(cass_cluster_set_port(cassandra_cluster->cluster, NUM2INT(port)), "port");
|
|
120
149
|
|
|
121
150
|
return self;
|
|
122
151
|
}
|
|
@@ -141,17 +170,30 @@ static VALUE cluster_keyspace(VALUE self, VALUE keyspace)
|
|
|
141
170
|
|
|
142
171
|
/**
|
|
143
172
|
* Sets the protocol version. The driver will automatically downgrade to the lowest supported protocol version.
|
|
144
|
-
* Default is +PROTOCOL_VERSION_V4
|
|
173
|
+
* Default is +PROTOCOL_VERSION_V4+, or +PROTOCOL_VERSION_DSEV1+ against DSE.
|
|
174
|
+
*
|
|
175
|
+
* The driver accepts +PROTOCOL_VERSION_V3+, +PROTOCOL_VERSION_V4+,
|
|
176
|
+
* +PROTOCOL_VERSION_DSEV1+ and +PROTOCOL_VERSION_DSEV2+ only. It ignores
|
|
177
|
+
* +PROTOCOL_VERSION_V1+ and +PROTOCOL_VERSION_V2+, which are below the lowest
|
|
178
|
+
* version it supports, and +PROTOCOL_VERSION_V5+, which is a beta version it
|
|
179
|
+
* enables through a separate setting instead. Each of those leaves the version
|
|
180
|
+
* unchanged and is reported on the driver's error log.
|
|
145
181
|
*
|
|
146
182
|
* @param version [Integer] A protocol version.
|
|
147
183
|
* @return [Cassandra::Cluster] self.
|
|
184
|
+
* @raise [RangeError] If a negative value was given.
|
|
148
185
|
*/
|
|
149
186
|
static VALUE cluster_protocol_version(VALUE self, VALUE version)
|
|
150
187
|
{
|
|
151
188
|
CassandraCluster *cassandra_cluster;
|
|
189
|
+
int v = NUM2INT(version);
|
|
190
|
+
|
|
191
|
+
if (v < 0) {
|
|
192
|
+
rb_raise(rb_eRangeError, "Invalid protocol_version: %d", v);
|
|
193
|
+
}
|
|
152
194
|
|
|
153
195
|
GET_CLUSTER(self, cassandra_cluster);
|
|
154
|
-
cass_cluster_set_protocol_version(cassandra_cluster->cluster,
|
|
196
|
+
cass_cluster_set_protocol_version(cassandra_cluster->cluster, v);
|
|
155
197
|
|
|
156
198
|
return self;
|
|
157
199
|
}
|
|
@@ -162,13 +204,14 @@ static VALUE cluster_protocol_version(VALUE self, VALUE version)
|
|
|
162
204
|
*
|
|
163
205
|
* @param timeout_ms [Integer] A connect timeout in milliseconds.
|
|
164
206
|
* @return [Cassandra::Cluster] self.
|
|
207
|
+
* @raise [RangeError] If a negative value was given.
|
|
165
208
|
*/
|
|
166
209
|
static VALUE cluster_connect_timeout(VALUE self, VALUE timeout_ms)
|
|
167
210
|
{
|
|
168
211
|
CassandraCluster *cassandra_cluster;
|
|
169
212
|
|
|
170
213
|
GET_CLUSTER(self, cassandra_cluster);
|
|
171
|
-
cass_cluster_set_connect_timeout(cassandra_cluster->cluster,
|
|
214
|
+
cass_cluster_set_connect_timeout(cassandra_cluster->cluster, cluster_value_to_uint(timeout_ms, "connect_timeout"));
|
|
172
215
|
|
|
173
216
|
return self;
|
|
174
217
|
}
|
|
@@ -179,13 +222,14 @@ static VALUE cluster_connect_timeout(VALUE self, VALUE timeout_ms)
|
|
|
179
222
|
*
|
|
180
223
|
* @param timeout_ms [Integer] A request timeout in milliseconds.
|
|
181
224
|
* @return [Cassandra::Cluster] self.
|
|
225
|
+
* @raise [RangeError] If a negative value was given.
|
|
182
226
|
*/
|
|
183
227
|
static VALUE cluster_request_timeout(VALUE self, VALUE timeout_ms)
|
|
184
228
|
{
|
|
185
229
|
CassandraCluster *cassandra_cluster;
|
|
186
230
|
|
|
187
231
|
GET_CLUSTER(self, cassandra_cluster);
|
|
188
|
-
cass_cluster_set_request_timeout(cassandra_cluster->cluster,
|
|
232
|
+
cass_cluster_set_request_timeout(cassandra_cluster->cluster, cluster_value_to_uint(timeout_ms, "request_timeout"));
|
|
189
233
|
|
|
190
234
|
return self;
|
|
191
235
|
}
|
|
@@ -196,13 +240,14 @@ static VALUE cluster_request_timeout(VALUE self, VALUE timeout_ms)
|
|
|
196
240
|
*
|
|
197
241
|
* @param timeout_ms [Integer] A request timeout in milliseconds.
|
|
198
242
|
* @return [Cassandra::Cluster] self.
|
|
243
|
+
* @raise [RangeError] If a negative value was given.
|
|
199
244
|
*/
|
|
200
245
|
static VALUE cluster_resolve_timeout(VALUE self, VALUE timeout_ms)
|
|
201
246
|
{
|
|
202
247
|
CassandraCluster *cassandra_cluster;
|
|
203
248
|
|
|
204
249
|
GET_CLUSTER(self, cassandra_cluster);
|
|
205
|
-
cass_cluster_set_resolve_timeout(cassandra_cluster->cluster,
|
|
250
|
+
cass_cluster_set_resolve_timeout(cassandra_cluster->cluster, cluster_value_to_uint(timeout_ms, "resolve_timeout"));
|
|
206
251
|
|
|
207
252
|
return self;
|
|
208
253
|
}
|
|
@@ -217,13 +262,15 @@ static VALUE cluster_resolve_timeout(VALUE self, VALUE timeout_ms)
|
|
|
217
262
|
static VALUE cluster_constant_speculative_execution_policy(VALUE self, VALUE constant_delay_ms, VALUE max_speculative_executions)
|
|
218
263
|
{
|
|
219
264
|
CassandraCluster *cassandra_cluster;
|
|
265
|
+
long delay = NUM2LONG(constant_delay_ms);
|
|
266
|
+
int max_executions = NUM2INT(max_speculative_executions);
|
|
220
267
|
|
|
221
|
-
if (
|
|
268
|
+
if (delay < 0 || max_executions < 0) {
|
|
222
269
|
rb_raise(rb_eArgError, "Bad parameters.");
|
|
223
270
|
}
|
|
224
271
|
|
|
225
272
|
GET_CLUSTER(self, cassandra_cluster);
|
|
226
|
-
cass_cluster_set_constant_speculative_execution_policy(cassandra_cluster->cluster,
|
|
273
|
+
cluster_check_error(cass_cluster_set_constant_speculative_execution_policy(cassandra_cluster->cluster, delay, max_executions), "constant_speculative_execution_policy");
|
|
227
274
|
|
|
228
275
|
return self;
|
|
229
276
|
}
|
|
@@ -248,39 +295,10 @@ static VALUE cluster_credentials(VALUE self, VALUE username, VALUE password)
|
|
|
248
295
|
return self;
|
|
249
296
|
}
|
|
250
297
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Casting a negative value to unsigned wraps it into a huge number, so check
|
|
259
|
-
// the range on the signed value NUM2LONG/NUM2LL already produced. Converting a
|
|
260
|
-
// second time would run to_int again, which may return a different value.
|
|
261
|
-
static unsigned cluster_value_to_uint(VALUE value, const char *name)
|
|
262
|
-
{
|
|
263
|
-
long v = NUM2LONG(value);
|
|
264
|
-
|
|
265
|
-
if (v < 0 || v > UINT_MAX) {
|
|
266
|
-
rb_raise(rb_eRangeError, "Invalid %s: %ld", name, v);
|
|
267
|
-
}
|
|
268
|
-
return (unsigned)v;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
static cass_uint64_t cluster_value_to_uint64(VALUE value, const char *name)
|
|
272
|
-
{
|
|
273
|
-
long long v = NUM2LL(value);
|
|
274
|
-
|
|
275
|
-
if (v < 0) {
|
|
276
|
-
rb_raise(rb_eRangeError, "Invalid %s: %lld", name, v);
|
|
277
|
-
}
|
|
278
|
-
return (cass_uint64_t)v;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// The driver only rejects CASS_CONSISTENCY_UNKNOWN at set time; any other
|
|
282
|
-
// out-of-range value is accepted and later fails every query with an
|
|
283
|
-
// opaque protocol error, so validate the range here.
|
|
298
|
+
// The driver only rejects CASS_CONSISTENCY_UNKNOWN (0xFFFF) at set time; any
|
|
299
|
+
// other out-of-range value is accepted and later fails every query with an
|
|
300
|
+
// opaque protocol error, so validate the range here. This also keeps UNKNOWN
|
|
301
|
+
// away from the driver, which is why the setters ignore its return value.
|
|
284
302
|
static void cluster_check_consistency(long value, const char *name)
|
|
285
303
|
{
|
|
286
304
|
if (value < CASS_CONSISTENCY_ANY || value > CASS_CONSISTENCY_LOCAL_ONE) {
|
|
@@ -347,7 +365,7 @@ static VALUE cluster_consistency(VALUE self, VALUE consistency)
|
|
|
347
365
|
CassConsistency consistency_value = cluster_value_to_consistency(consistency, "consistency");
|
|
348
366
|
|
|
349
367
|
GET_CLUSTER(self, cassandra_cluster);
|
|
350
|
-
|
|
368
|
+
cass_cluster_set_consistency(cassandra_cluster->cluster, consistency_value);
|
|
351
369
|
|
|
352
370
|
return self;
|
|
353
371
|
}
|
|
@@ -378,7 +396,7 @@ static VALUE cluster_serial_consistency(VALUE self, VALUE consistency)
|
|
|
378
396
|
}
|
|
379
397
|
|
|
380
398
|
GET_CLUSTER(self, cassandra_cluster);
|
|
381
|
-
|
|
399
|
+
cass_cluster_set_serial_consistency(cassandra_cluster->cluster, consistency_value);
|
|
382
400
|
|
|
383
401
|
return self;
|
|
384
402
|
}
|
data/ext/ilios/future.c
CHANGED
|
@@ -136,7 +136,13 @@ static void future_result_success_yield(CassandraFuture *cassandra_future)
|
|
|
136
136
|
static void future_result_failure_yield(CassandraFuture *cassandra_future)
|
|
137
137
|
{
|
|
138
138
|
if (cassandra_future->on_failure_block) {
|
|
139
|
-
|
|
139
|
+
if (rb_proc_arity(cassandra_future->on_failure_block)) {
|
|
140
|
+
VALUE error = ilios_future_error_new(eExecutionError, NULL, cassandra_future->future);
|
|
141
|
+
|
|
142
|
+
rb_proc_call_with_block(cassandra_future->on_failure_block, 1, &error, Qnil);
|
|
143
|
+
} else {
|
|
144
|
+
rb_proc_call_with_block(cassandra_future->on_failure_block, 0, NULL, Qnil);
|
|
145
|
+
}
|
|
140
146
|
}
|
|
141
147
|
}
|
|
142
148
|
|
|
@@ -317,6 +323,9 @@ static VALUE future_on_failure_synchronize(VALUE future)
|
|
|
317
323
|
/**
|
|
318
324
|
* Run block when future resolves to error.
|
|
319
325
|
*
|
|
326
|
+
* @yieldparam error [Cassandra::ExecutionError] The failure reason. Only
|
|
327
|
+
* yielded when the block accepts an argument; a zero-arity block is still
|
|
328
|
+
* called with no arguments.
|
|
320
329
|
* @return [Cassandra::Future] self.
|
|
321
330
|
* @raise [Cassandra::ExecutionError] If this method will be called twice.
|
|
322
331
|
* @raise [ArgumentError] If no block was given.
|
data/ext/ilios/ilios.c
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#include "ilios.h"
|
|
2
2
|
|
|
3
|
+
#include <string.h>
|
|
4
|
+
|
|
3
5
|
VALUE mIlios;
|
|
4
6
|
VALUE mCassandra;
|
|
5
7
|
VALUE cCluster;
|
|
@@ -21,6 +23,7 @@ VALUE id_push;
|
|
|
21
23
|
VALUE id_pop;
|
|
22
24
|
VALUE id_alive;
|
|
23
25
|
VALUE id_report_on_exception;
|
|
26
|
+
VALUE id_code;
|
|
24
27
|
VALUE sym_unsupported_column_type;
|
|
25
28
|
|
|
26
29
|
#if defined(HAVE_MALLOC_USABLE_SIZE)
|
|
@@ -74,6 +77,45 @@ static VALUE cassandra_set_log_level(VALUE self, VALUE log_level)
|
|
|
74
77
|
return self;
|
|
75
78
|
}
|
|
76
79
|
|
|
80
|
+
VALUE ilios_error_new(VALUE exception_class, VALUE message, CassError error_code)
|
|
81
|
+
{
|
|
82
|
+
VALUE error = rb_exc_new_str(exception_class, message);
|
|
83
|
+
|
|
84
|
+
rb_ivar_set(error, id_code, INT2NUM(error_code));
|
|
85
|
+
|
|
86
|
+
return error;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
VALUE ilios_future_error_new(VALUE exception_class, const char *prefix, CassFuture *future)
|
|
90
|
+
{
|
|
91
|
+
CassError error_code;
|
|
92
|
+
const char *message;
|
|
93
|
+
size_t message_length;
|
|
94
|
+
VALUE body;
|
|
95
|
+
VALUE full_message;
|
|
96
|
+
|
|
97
|
+
error_code = cass_future_error_code(future);
|
|
98
|
+
cass_future_error_message(future, &message, &message_length);
|
|
99
|
+
if (message_length == 0) {
|
|
100
|
+
message = cass_error_desc(error_code);
|
|
101
|
+
message_length = strlen(message);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// The driver hands back the server's raw bytes, which rb_str_new would
|
|
105
|
+
// tag ASCII-8BIT; CQL identifiers and literals are UTF-8, so tag it
|
|
106
|
+
// UTF-8 and keep the prefixed message in that encoding too. The message
|
|
107
|
+
// can also carry driver-local text (strerror output, host names), which
|
|
108
|
+
// is not guaranteed UTF-8, so replace invalid bytes rather than handing
|
|
109
|
+
// back a string that raises ArgumentError on the first regexp match.
|
|
110
|
+
body = rb_utf8_str_new(message, message_length);
|
|
111
|
+
if (rb_enc_str_coderange(body) == ENC_CODERANGE_BROKEN) {
|
|
112
|
+
body = rb_str_scrub(body, Qnil);
|
|
113
|
+
}
|
|
114
|
+
full_message = prefix ? rb_enc_sprintf(rb_utf8_encoding(), "%s: %"PRIsVALUE, prefix, body) : body;
|
|
115
|
+
|
|
116
|
+
return ilios_error_new(exception_class, full_message, error_code);
|
|
117
|
+
}
|
|
118
|
+
|
|
77
119
|
void Init_ilios(void)
|
|
78
120
|
{
|
|
79
121
|
rb_ext_ractor_safe(true);
|
|
@@ -88,6 +130,11 @@ void Init_ilios(void)
|
|
|
88
130
|
eConnectError = rb_define_class_under(mCassandra, "ConnectError", rb_eStandardError);
|
|
89
131
|
eExecutionError = rb_define_class_under(mCassandra, "ExecutionError", rb_eStandardError);
|
|
90
132
|
eStatementError = rb_define_class_under(mCassandra, "StatementError", rb_eStandardError);
|
|
133
|
+
// @code is only set on errors built by ilios_error_new; the ones raised
|
|
134
|
+
// elsewhere via plain rb_raise leave #code nil.
|
|
135
|
+
rb_define_attr(eExecutionError, "code", 1, 0);
|
|
136
|
+
rb_define_attr(eConnectError, "code", 1, 0);
|
|
137
|
+
rb_define_attr(eStatementError, "code", 1, 0);
|
|
91
138
|
|
|
92
139
|
cSizedQueue = rb_const_get(rb_cThread, rb_intern("SizedQueue"));
|
|
93
140
|
rb_require("set");
|
|
@@ -101,6 +148,7 @@ void Init_ilios(void)
|
|
|
101
148
|
id_pop = rb_intern("pop");
|
|
102
149
|
id_alive = rb_intern("alive?");
|
|
103
150
|
id_report_on_exception = rb_intern("report_on_exception=");
|
|
151
|
+
id_code = rb_intern("@code");
|
|
104
152
|
sym_unsupported_column_type = ID2SYM(rb_intern("unsupported_column_type"));
|
|
105
153
|
|
|
106
154
|
rb_define_module_function(mCassandra, "log_level", cassandra_set_log_level, 1);
|
data/ext/ilios/ilios.h
CHANGED
|
@@ -11,7 +11,16 @@
|
|
|
11
11
|
|
|
12
12
|
#define DEFAULT_PAGE_SIZE 10000
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
// Cluster exposes an allocator, so allocate and dup both produce an instance
|
|
15
|
+
// that never ran initialize. Reject it here instead of handing NULL to the
|
|
16
|
+
// driver, which segfaults.
|
|
17
|
+
#define GET_CLUSTER(obj, var) do { \
|
|
18
|
+
GET_UNINITIALIZED_CLUSTER(obj, var); \
|
|
19
|
+
if ((var)->cluster == NULL) { \
|
|
20
|
+
rb_raise(rb_eRuntimeError, "uninitialized Ilios::Cassandra::Cluster"); \
|
|
21
|
+
} \
|
|
22
|
+
} while (0)
|
|
23
|
+
#define GET_UNINITIALIZED_CLUSTER(obj, var) TypedData_Get_Struct(obj, CassandraCluster, &cassandra_cluster_data_type, var)
|
|
15
24
|
#define GET_SESSION(obj, var) TypedData_Get_Struct(obj, CassandraSession, &cassandra_session_data_type, var)
|
|
16
25
|
#define GET_STATEMENT(obj, var) TypedData_Get_Struct(obj, CassandraStatement, &cassandra_statement_data_type, var)
|
|
17
26
|
#define GET_RESULT(obj, var) TypedData_Get_Struct(obj, CassandraResult, &cassandra_result_data_type, var)
|
|
@@ -118,6 +127,7 @@ extern VALUE id_push;
|
|
|
118
127
|
extern VALUE id_pop;
|
|
119
128
|
extern VALUE id_alive;
|
|
120
129
|
extern VALUE id_report_on_exception;
|
|
130
|
+
extern VALUE id_code;
|
|
121
131
|
extern VALUE sym_unsupported_column_type;
|
|
122
132
|
|
|
123
133
|
extern void Init_cluster(void);
|
|
@@ -136,5 +146,17 @@ extern void statement_default_config(CassandraStatement *cassandra_statement);
|
|
|
136
146
|
extern CassStatement *statement_build_for_execution(CassandraStatement *cassandra_statement);
|
|
137
147
|
extern void result_await(CassandraResult *cassandra_result);
|
|
138
148
|
|
|
149
|
+
// Builds an exception of `exception_class` carrying `message` and `@code`
|
|
150
|
+
// (readable via #code) set to the CassError value.
|
|
151
|
+
extern VALUE ilios_error_new(VALUE exception_class, VALUE message, CassError error_code);
|
|
152
|
+
|
|
153
|
+
// Builds an exception of `exception_class` from `future`'s error: the
|
|
154
|
+
// driver's server-reported message (falling back to the generic
|
|
155
|
+
// cass_error_desc when the driver returns none), formatted as
|
|
156
|
+
// "<prefix>: <message>" when `prefix` is non-NULL, or the bare message when
|
|
157
|
+
// `prefix` is NULL. `future` must still be alive (not yet cass_future_free'd)
|
|
158
|
+
// when this is called.
|
|
159
|
+
extern VALUE ilios_future_error_new(VALUE exception_class, const char *prefix, CassFuture *future);
|
|
160
|
+
|
|
139
161
|
|
|
140
162
|
#endif // ILIOS_H
|
data/ext/ilios/result.c
CHANGED
|
@@ -22,10 +22,9 @@ void result_await(CassandraResult *cassandra_result)
|
|
|
22
22
|
nogvl_future_wait(cassandra_result->future);
|
|
23
23
|
|
|
24
24
|
if (cass_future_error_code(cassandra_result->future) != CASS_OK) {
|
|
25
|
-
|
|
25
|
+
VALUE error = ilios_future_error_new(eExecutionError, "Unable to wait executing", cassandra_result->future);
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
rb_raise(eExecutionError, "Unable to wait executing: %s", error);
|
|
27
|
+
rb_exc_raise(error);
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
if (cassandra_result->result == NULL) {
|
|
@@ -66,8 +65,10 @@ static VALUE result_next_page(VALUE self)
|
|
|
66
65
|
|
|
67
66
|
error_code = cass_future_error_code(result_future);
|
|
68
67
|
if (error_code != CASS_OK) {
|
|
68
|
+
VALUE error = ilios_future_error_new(eExecutionError, "Unable to wait executing", result_future);
|
|
69
|
+
|
|
69
70
|
cass_future_free(result_future);
|
|
70
|
-
|
|
71
|
+
rb_exc_raise(error);
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
cass_result_free(cassandra_result->result);
|
|
@@ -80,10 +81,20 @@ static VALUE result_next_page(VALUE self)
|
|
|
80
81
|
return self;
|
|
81
82
|
}
|
|
82
83
|
|
|
84
|
+
// No CassFuture is involved here: this fires while decoding an already
|
|
85
|
+
// fetched row's column value.
|
|
86
|
+
static VALUE result_check_value_error_new(CassError error_code, VALUE key)
|
|
87
|
+
{
|
|
88
|
+
VALUE message = rb_enc_sprintf(rb_utf8_encoding(), "Unable to get value of %"PRIsVALUE" column: %s",
|
|
89
|
+
key, cass_error_desc(error_code));
|
|
90
|
+
|
|
91
|
+
return ilios_error_new(eExecutionError, message, error_code);
|
|
92
|
+
}
|
|
93
|
+
|
|
83
94
|
static void result_check_value(CassError error_code, VALUE key)
|
|
84
95
|
{
|
|
85
96
|
if (error_code != CASS_OK) {
|
|
86
|
-
|
|
97
|
+
rb_exc_raise(result_check_value_error_new(error_code, key));
|
|
87
98
|
}
|
|
88
99
|
}
|
|
89
100
|
|
data/ext/ilios/session.c
CHANGED
|
@@ -56,11 +56,10 @@ static VALUE session_prepare(VALUE self, VALUE query)
|
|
|
56
56
|
nogvl_future_wait(prepare_future);
|
|
57
57
|
|
|
58
58
|
if (cass_future_error_code(prepare_future) != CASS_OK) {
|
|
59
|
-
|
|
59
|
+
VALUE error = ilios_future_error_new(eExecutionError, "Unable to prepare query", prepare_future);
|
|
60
60
|
|
|
61
|
-
strncpy(error, cass_error_desc(cass_future_error_code(prepare_future)), sizeof(error) - 1);
|
|
62
61
|
cass_future_free(prepare_future);
|
|
63
|
-
|
|
62
|
+
rb_exc_raise(error);
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
cassandra_statement_obj = CREATE_STATEMENT(cassandra_statement);
|
data/ext/ilios/statement.c
CHANGED
|
@@ -141,7 +141,10 @@ static void statement_bind_collection(statement_bind_target *target, const CassD
|
|
|
141
141
|
static void statement_bind_check(CassError result, VALUE key)
|
|
142
142
|
{
|
|
143
143
|
if (result != CASS_OK) {
|
|
144
|
-
|
|
144
|
+
VALUE message = rb_enc_sprintf(rb_utf8_encoding(), "Failed to bind value of %"PRIsVALUE" column: %s",
|
|
145
|
+
key, cass_error_desc(result));
|
|
146
|
+
|
|
147
|
+
rb_exc_raise(ilios_error_new(eStatementError, message, result));
|
|
145
148
|
}
|
|
146
149
|
}
|
|
147
150
|
|
data/lib/ilios/version.rb
CHANGED
data/sig/ilios.rbs
CHANGED
|
@@ -75,9 +75,21 @@ module Ilios
|
|
|
75
75
|
def idempotent=: (bool) -> self
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
+
class ExecutionError < StandardError
|
|
79
|
+
def code: () -> Integer?
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class ConnectError < StandardError
|
|
83
|
+
def code: () -> Integer?
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class StatementError < StandardError
|
|
87
|
+
def code: () -> Integer?
|
|
88
|
+
end
|
|
89
|
+
|
|
78
90
|
class Future
|
|
79
|
-
def on_success: () { (Ilios::Cassandra::Result) -> void } -> self
|
|
80
|
-
def on_failure: () { () -> void } -> self
|
|
91
|
+
def on_success: () { (Ilios::Cassandra::Result | Ilios::Cassandra::Statement) -> void } -> self
|
|
92
|
+
def on_failure: () { (Ilios::Cassandra::ExecutionError) -> void } -> self
|
|
81
93
|
def await: () -> self
|
|
82
94
|
end
|
|
83
95
|
|
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.
|
|
4
|
+
version: 1.2.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.
|
|
61
|
+
documentation_uri: https://www.rubydoc.info/gems/ilios/1.2.0
|
|
62
62
|
rubygems_mfa_required: 'true'
|
|
63
63
|
rdoc_options: []
|
|
64
64
|
require_paths:
|