trilogy 2.1.2 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -1
- data/ext/trilogy-ruby/cast.c +11 -6
- data/ext/trilogy-ruby/cext.c +167 -78
- data/ext/trilogy-ruby/inc/trilogy/protocol.h +1 -3
- data/ext/trilogy-ruby/trilogy-ruby.h +2 -1
- data/lib/trilogy/version.rb +1 -1
- data/lib/trilogy.rb +119 -21
- 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: 693efafac411af8e0859eee682d4432a501f17d90c3735faabad4cac4ab07537
|
4
|
+
data.tar.gz: 646bc59fce2e3b26058bf867f448496e5d5caf1344ca0832d706bd3e2db99a2f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 311d5640cdd3da6e3ba331c8222698ff18a10b06886d277712fb8da453924d862f4c9269163a5f748b73dcbc19087993ae2b3cbdc6fccbbd97a91c0c88f83bed
|
7
|
+
data.tar.gz: 77442107a8527f806c2f602d95c000d81c2e7b7a09f6efc8a77a5dcbb091eb4c6890948a1352a72a8fa2f7ef6fe1d74cb0eda84b0d385049514a8dd0236d597d
|
data/README.md
CHANGED
@@ -34,6 +34,12 @@ if client.ping
|
|
34
34
|
result.each_hash do |user|
|
35
35
|
p user
|
36
36
|
end
|
37
|
+
|
38
|
+
# Multi-statement
|
39
|
+
|
40
|
+
results = []
|
41
|
+
results << client.query("SELECT name FROM users WHERE id = 1; SELECT name FROM users WHERE id = 2")
|
42
|
+
results << client.next_result while client.more_results_exist?
|
37
43
|
end
|
38
44
|
```
|
39
45
|
|
@@ -59,7 +65,7 @@ The official Ruby bindings are inside of the canonical trilogy repository itself
|
|
59
65
|
The trilogy API was heavily inspired by the mysql2 gem but has a few notable
|
60
66
|
differences:
|
61
67
|
|
62
|
-
* The `
|
68
|
+
* The `query_flags` don't inherit from the connection options hash.
|
63
69
|
This means that options like turning on/of casting will need to be set before
|
64
70
|
a query and not passed in at connect time.
|
65
71
|
* For performance reasons there is no `application_timezone` query option. If
|
data/ext/trilogy-ruby/cast.c
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
|
8
8
|
#define CAST_STACK_SIZE 64
|
9
9
|
|
10
|
-
static ID id_BigDecimal, id_new, id_local, id_localtime, id_utc;
|
10
|
+
static ID id_BigDecimal, id_Integer, id_new, id_local, id_localtime, id_utc;
|
11
11
|
|
12
12
|
static const char *ruby_encoding_name_map[] = {
|
13
13
|
[TRILOGY_ENCODING_ARMSCII8] = NULL,
|
@@ -71,7 +71,7 @@ static void cstr_from_value(char *buf, const trilogy_value_t *value, const char
|
|
71
71
|
{
|
72
72
|
|
73
73
|
if (value->data_len > CAST_STACK_SIZE - 1) {
|
74
|
-
rb_raise(
|
74
|
+
rb_raise(Trilogy_CastError, errmsg, (int)value->data_len, (char *)value->data);
|
75
75
|
}
|
76
76
|
|
77
77
|
memcpy(buf, value->data, value->data_len);
|
@@ -145,7 +145,11 @@ rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *co
|
|
145
145
|
// TODO - optimize so we don't have to allocate a ruby string for
|
146
146
|
// decimal columns
|
147
147
|
VALUE str = rb_str_new(value->data, value->data_len);
|
148
|
-
|
148
|
+
if (column->decimals == 0) {
|
149
|
+
return rb_funcall(rb_mKernel, id_Integer, 1, str);
|
150
|
+
} else {
|
151
|
+
return rb_funcall(rb_mKernel, id_BigDecimal, 1, str);
|
152
|
+
}
|
149
153
|
}
|
150
154
|
case TRILOGY_TYPE_FLOAT:
|
151
155
|
case TRILOGY_TYPE_DOUBLE: {
|
@@ -156,7 +160,7 @@ rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *co
|
|
156
160
|
double dbl = strtod(cstr, &err);
|
157
161
|
|
158
162
|
if (*err != 0) {
|
159
|
-
rb_raise(
|
163
|
+
rb_raise(Trilogy_CastError, "Invalid double value: %.*s", (int)value->data_len, (char *)value->data);
|
160
164
|
}
|
161
165
|
return rb_float_new(dbl);
|
162
166
|
}
|
@@ -180,7 +184,7 @@ rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *co
|
|
180
184
|
}
|
181
185
|
|
182
186
|
if (month < 1 || day < 1) {
|
183
|
-
rb_raise(
|
187
|
+
rb_raise(Trilogy_CastError, "Invalid date: %.*s", (int)value->data_len, (char *)value->data);
|
184
188
|
}
|
185
189
|
|
186
190
|
// pad out msec_char with zeroes at the end as it could be at any
|
@@ -211,7 +215,7 @@ rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *co
|
|
211
215
|
}
|
212
216
|
|
213
217
|
if (month < 1 || day < 1) {
|
214
|
-
rb_raise(
|
218
|
+
rb_raise(Trilogy_CastError, "Invalid date: %.*s", (int)value->data_len, (char *)value->data);
|
215
219
|
}
|
216
220
|
|
217
221
|
return rb_funcall(Date, id_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day));
|
@@ -265,6 +269,7 @@ void rb_trilogy_cast_init(void)
|
|
265
269
|
rb_require("date");
|
266
270
|
|
267
271
|
id_BigDecimal = rb_intern("BigDecimal");
|
272
|
+
id_Integer = rb_intern("Integer");
|
268
273
|
id_new = rb_intern("new");
|
269
274
|
id_local = rb_intern("local");
|
270
275
|
id_localtime = rb_intern("localtime");
|
data/ext/trilogy-ruby/cext.c
CHANGED
@@ -14,16 +14,16 @@
|
|
14
14
|
|
15
15
|
#define TRILOGY_RB_TIMEOUT 1
|
16
16
|
|
17
|
-
VALUE
|
18
|
-
|
19
|
-
|
20
|
-
static VALUE Trilogy_DatabaseError, Trilogy_Result;
|
17
|
+
VALUE Trilogy_CastError;
|
18
|
+
static VALUE Trilogy_BaseConnectionError, Trilogy_ProtocolError, Trilogy_SSLError, Trilogy_QueryError,
|
19
|
+
Trilogy_ConnectionClosedError, Trilogy_TimeoutError, Trilogy_Result;
|
21
20
|
|
22
21
|
static ID id_socket, id_host, id_port, id_username, id_password, id_found_rows, id_connect_timeout, id_read_timeout,
|
23
22
|
id_write_timeout, id_keepalive_enabled, id_keepalive_idle, id_keepalive_interval, id_keepalive_count,
|
24
23
|
id_ivar_affected_rows, id_ivar_fields, id_ivar_last_insert_id, id_ivar_rows, id_ivar_query_time, id_password,
|
25
24
|
id_database, id_ssl_ca, id_ssl_capath, id_ssl_cert, id_ssl_cipher, id_ssl_crl, id_ssl_crlpath, id_ssl_key,
|
26
|
-
id_ssl_mode, id_tls_ciphersuites, id_tls_min_version, id_tls_max_version
|
25
|
+
id_ssl_mode, id_tls_ciphersuites, id_tls_min_version, id_tls_max_version, id_multi_statement, id_from_code,
|
26
|
+
id_connection_options;
|
27
27
|
|
28
28
|
struct trilogy_ctx {
|
29
29
|
trilogy_conn_t conn;
|
@@ -31,10 +31,39 @@ struct trilogy_ctx {
|
|
31
31
|
unsigned int query_flags;
|
32
32
|
};
|
33
33
|
|
34
|
+
static void free_trilogy(void *ptr)
|
35
|
+
{
|
36
|
+
struct trilogy_ctx *ctx = ptr;
|
37
|
+
if (ctx->conn.socket != NULL) {
|
38
|
+
trilogy_free(&ctx->conn);
|
39
|
+
}
|
40
|
+
xfree(ptr);
|
41
|
+
}
|
42
|
+
|
43
|
+
static size_t trilogy_memsize(const void *ptr) {
|
44
|
+
const struct trilogy_ctx *ctx = ptr;
|
45
|
+
size_t memsize = sizeof(struct trilogy_ctx);
|
46
|
+
if (ctx->conn.socket != NULL) {
|
47
|
+
memsize += sizeof(trilogy_sock_t);
|
48
|
+
}
|
49
|
+
memsize += ctx->conn.packet_buffer.cap;
|
50
|
+
return memsize;
|
51
|
+
}
|
52
|
+
|
53
|
+
const rb_data_type_t trilogy_data_type = {
|
54
|
+
.wrap_struct_name = "trilogy",
|
55
|
+
.function = {
|
56
|
+
.dmark = NULL,
|
57
|
+
.dfree = free_trilogy,
|
58
|
+
.dsize = trilogy_memsize,
|
59
|
+
},
|
60
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
|
61
|
+
};
|
62
|
+
|
34
63
|
static struct trilogy_ctx *get_ctx(VALUE obj)
|
35
64
|
{
|
36
65
|
struct trilogy_ctx *ctx;
|
37
|
-
|
66
|
+
TypedData_Get_Struct(obj, struct trilogy_ctx, &trilogy_data_type, ctx);
|
38
67
|
return ctx;
|
39
68
|
}
|
40
69
|
|
@@ -43,7 +72,7 @@ static struct trilogy_ctx *get_open_ctx(VALUE obj)
|
|
43
72
|
struct trilogy_ctx *ctx = get_ctx(obj);
|
44
73
|
|
45
74
|
if (ctx->conn.socket == NULL) {
|
46
|
-
rb_raise(
|
75
|
+
rb_raise(Trilogy_ConnectionClosedError, "Attempted to use closed connection");
|
47
76
|
}
|
48
77
|
|
49
78
|
return ctx;
|
@@ -59,16 +88,16 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
|
|
59
88
|
|
60
89
|
switch (rc) {
|
61
90
|
case TRILOGY_SYSERR:
|
62
|
-
|
91
|
+
if (errno == ECONNREFUSED || errno == ECONNRESET) {
|
92
|
+
rb_raise(Trilogy_BaseConnectionError, "%" PRIsVALUE, rbmsg);
|
93
|
+
} else {
|
94
|
+
// TODO: All syserr should be wrapped.
|
95
|
+
rb_syserr_fail_str(errno, rbmsg);
|
96
|
+
}
|
63
97
|
|
64
98
|
case TRILOGY_ERR: {
|
65
99
|
VALUE message = rb_str_new(ctx->conn.error_message, ctx->conn.error_message_len);
|
66
|
-
VALUE exc =
|
67
|
-
rb_sprintf("%" PRIsVALUE ": %d %" PRIsVALUE, rbmsg, ctx->conn.error_code, message));
|
68
|
-
|
69
|
-
rb_ivar_set(exc, rb_intern("@error_code"), INT2FIX(ctx->conn.error_code));
|
70
|
-
rb_ivar_set(exc, rb_intern("@error_message"), message);
|
71
|
-
|
100
|
+
VALUE exc = rb_funcall(Trilogy_ProtocolError, id_from_code, 2, message, INT2NUM(ctx->conn.error_code));
|
72
101
|
rb_exc_raise(exc);
|
73
102
|
}
|
74
103
|
|
@@ -76,25 +105,29 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
|
|
76
105
|
unsigned long ossl_error = ERR_get_error();
|
77
106
|
ERR_clear_error();
|
78
107
|
if (ERR_GET_LIB(ossl_error) == ERR_LIB_SYS) {
|
79
|
-
|
108
|
+
int err_reason = ERR_GET_REASON(ossl_error);
|
109
|
+
|
110
|
+
if (err_reason == ECONNREFUSED || err_reason == ECONNRESET) {
|
111
|
+
rb_raise(Trilogy_BaseConnectionError, "%" PRIsVALUE, rbmsg);
|
112
|
+
} else {
|
113
|
+
// TODO: All syserr should be wrapped.
|
114
|
+
rb_syserr_fail_str(err_reason, rbmsg);
|
115
|
+
}
|
80
116
|
}
|
81
117
|
// We can't recover from OpenSSL level errors if there's
|
82
118
|
// an active connection.
|
83
119
|
if (ctx->conn.socket != NULL) {
|
84
120
|
trilogy_sock_shutdown(ctx->conn.socket);
|
85
121
|
}
|
86
|
-
rb_raise(
|
122
|
+
rb_raise(Trilogy_SSLError, "%" PRIsVALUE ": SSL Error: %s", rbmsg, ERR_reason_error_string(ossl_error));
|
87
123
|
}
|
88
124
|
|
89
|
-
|
90
|
-
rb_raise(
|
125
|
+
case TRILOGY_DNS_ERR: {
|
126
|
+
rb_raise(Trilogy_BaseConnectionError, "%" PRIsVALUE ": TRILOGY_DNS_ERROR", rbmsg);
|
91
127
|
}
|
92
|
-
}
|
93
128
|
|
94
|
-
|
95
|
-
|
96
|
-
if (ctx->conn.socket != NULL) {
|
97
|
-
trilogy_free(&ctx->conn);
|
129
|
+
default:
|
130
|
+
rb_raise(Trilogy_QueryError, "%" PRIsVALUE ": %s", rbmsg, trilogy_error(rc));
|
98
131
|
}
|
99
132
|
}
|
100
133
|
|
@@ -102,14 +135,18 @@ static VALUE allocate_trilogy(VALUE klass)
|
|
102
135
|
{
|
103
136
|
struct trilogy_ctx *ctx;
|
104
137
|
|
105
|
-
VALUE obj =
|
106
|
-
|
107
|
-
memset(ctx->server_version, 0, sizeof(ctx->server_version));
|
138
|
+
VALUE obj = TypedData_Make_Struct(klass, struct trilogy_ctx, &trilogy_data_type, ctx);
|
108
139
|
|
109
140
|
ctx->query_flags = TRILOGY_FLAGS_DEFAULT;
|
110
141
|
|
111
142
|
if (trilogy_init(&ctx->conn) < 0) {
|
112
|
-
|
143
|
+
if (errno == ECONNREFUSED || errno == ECONNRESET) {
|
144
|
+
rb_raise(Trilogy_BaseConnectionError, "trilogy_init");
|
145
|
+
} else {
|
146
|
+
// TODO: All syserr should be wrapped.
|
147
|
+
VALUE rbmsg = rb_str_new("trilogy_init", 13);
|
148
|
+
rb_syserr_fail_str(errno, rbmsg);
|
149
|
+
}
|
113
150
|
}
|
114
151
|
|
115
152
|
return obj;
|
@@ -125,7 +162,7 @@ static int flush_writes(struct trilogy_ctx *ctx)
|
|
125
162
|
}
|
126
163
|
|
127
164
|
if (trilogy_sock_wait_write(ctx->conn.socket) < 0) {
|
128
|
-
|
165
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_flush_writes");
|
129
166
|
}
|
130
167
|
}
|
131
168
|
}
|
@@ -259,7 +296,7 @@ static void auth_switch(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake)
|
|
259
296
|
}
|
260
297
|
|
261
298
|
if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
|
262
|
-
|
299
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_auth_recv");
|
263
300
|
}
|
264
301
|
}
|
265
302
|
}
|
@@ -285,7 +322,7 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
|
|
285
322
|
}
|
286
323
|
} else {
|
287
324
|
if (ssl_mode != TRILOGY_SSL_PREFERRED_NOVERIFY) {
|
288
|
-
rb_raise(
|
325
|
+
rb_raise(Trilogy_SSLError, "SSL required, not supported by server");
|
289
326
|
}
|
290
327
|
}
|
291
328
|
}
|
@@ -312,7 +349,7 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
|
|
312
349
|
}
|
313
350
|
|
314
351
|
if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
|
315
|
-
|
352
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_auth_recv");
|
316
353
|
}
|
317
354
|
}
|
318
355
|
|
@@ -329,6 +366,7 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
|
|
329
366
|
VALUE val;
|
330
367
|
|
331
368
|
Check_Type(opts, T_HASH);
|
369
|
+
rb_ivar_set(self, id_connection_options, opts);
|
332
370
|
|
333
371
|
if ((val = rb_hash_lookup(opts, ID2SYM(id_ssl_mode))) != Qnil) {
|
334
372
|
Check_Type(val, T_FIXNUM);
|
@@ -406,6 +444,10 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
|
|
406
444
|
connopt.flags |= TRILOGY_CAPABILITIES_FOUND_ROWS;
|
407
445
|
}
|
408
446
|
|
447
|
+
if (RTEST(rb_hash_aref(opts, ID2SYM(id_multi_statement)))) {
|
448
|
+
connopt.flags |= TRILOGY_CAPABILITIES_MULTI_STATEMENTS;
|
449
|
+
}
|
450
|
+
|
409
451
|
if ((val = rb_hash_aref(opts, ID2SYM(id_ssl_ca))) != Qnil) {
|
410
452
|
Check_Type(val, T_STRING);
|
411
453
|
connopt.ssl_ca = StringValueCStr(val);
|
@@ -458,7 +500,7 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
|
|
458
500
|
|
459
501
|
int rc = try_connect(ctx, &handshake, &connopt);
|
460
502
|
if (rc == TRILOGY_RB_TIMEOUT) {
|
461
|
-
|
503
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_connect_recv");
|
462
504
|
}
|
463
505
|
if (rc != TRILOGY_OK) {
|
464
506
|
if (connopt.path) {
|
@@ -505,7 +547,7 @@ static VALUE rb_trilogy_change_db(VALUE self, VALUE database)
|
|
505
547
|
}
|
506
548
|
|
507
549
|
if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
|
508
|
-
|
550
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_change_db_recv");
|
509
551
|
}
|
510
552
|
}
|
511
553
|
|
@@ -520,10 +562,9 @@ static void load_query_options(unsigned int query_flags, struct rb_trilogy_cast_
|
|
520
562
|
cast_options->flatten_rows = (query_flags & TRILOGY_FLAGS_FLATTEN_ROWS) != 0;
|
521
563
|
}
|
522
564
|
|
523
|
-
struct
|
565
|
+
struct read_query_response_state {
|
524
566
|
struct rb_trilogy_cast_options *cast_options;
|
525
567
|
struct trilogy_ctx *ctx;
|
526
|
-
VALUE query;
|
527
568
|
|
528
569
|
// to free by caller:
|
529
570
|
struct column_info *column_info;
|
@@ -550,34 +591,25 @@ static void get_timespec_monotonic(struct timespec *ts)
|
|
550
591
|
#endif
|
551
592
|
}
|
552
593
|
|
553
|
-
static VALUE read_query_error(struct
|
594
|
+
static VALUE read_query_error(struct read_query_response_state *args, int rc, const char *msg)
|
554
595
|
{
|
555
596
|
args->rc = rc;
|
556
597
|
args->msg = msg;
|
557
598
|
return Qundef;
|
558
599
|
}
|
559
600
|
|
560
|
-
static VALUE
|
601
|
+
static VALUE read_query_response(VALUE vargs)
|
561
602
|
{
|
562
|
-
struct
|
603
|
+
struct read_query_response_state *args = (void *)vargs;
|
563
604
|
struct trilogy_ctx *ctx = args->ctx;
|
564
|
-
VALUE query = args->query;
|
565
605
|
|
566
606
|
struct timespec start;
|
567
607
|
get_timespec_monotonic(&start);
|
568
608
|
|
569
|
-
int rc = trilogy_query_send(&ctx->conn, RSTRING_PTR(query), RSTRING_LEN(query));
|
570
|
-
|
571
|
-
if (rc == TRILOGY_AGAIN) {
|
572
|
-
rc = flush_writes(ctx);
|
573
|
-
}
|
574
|
-
|
575
|
-
if (rc < 0) {
|
576
|
-
return read_query_error(args, rc, "trilogy_query_send");
|
577
|
-
}
|
578
|
-
|
579
609
|
uint64_t column_count = 0;
|
580
610
|
|
611
|
+
int rc;
|
612
|
+
|
581
613
|
while (1) {
|
582
614
|
rc = trilogy_query_recv(&ctx->conn, &column_count);
|
583
615
|
|
@@ -590,7 +622,7 @@ static VALUE execute_read_query(VALUE vargs)
|
|
590
622
|
}
|
591
623
|
|
592
624
|
if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
|
593
|
-
|
625
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_query_recv");
|
594
626
|
}
|
595
627
|
}
|
596
628
|
|
@@ -636,7 +668,7 @@ static VALUE execute_read_query(VALUE vargs)
|
|
636
668
|
}
|
637
669
|
|
638
670
|
if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
|
639
|
-
|
671
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_read_column");
|
640
672
|
}
|
641
673
|
}
|
642
674
|
|
@@ -653,6 +685,7 @@ static VALUE execute_read_query(VALUE vargs)
|
|
653
685
|
column_info[i].flags = column.flags;
|
654
686
|
column_info[i].len = column.len;
|
655
687
|
column_info[i].charset = column.charset;
|
688
|
+
column_info[i].decimals = column.decimals;
|
656
689
|
}
|
657
690
|
|
658
691
|
trilogy_value_t *row_values = ALLOC_N(trilogy_value_t, column_count);
|
@@ -663,7 +696,7 @@ static VALUE execute_read_query(VALUE vargs)
|
|
663
696
|
|
664
697
|
if (rc == TRILOGY_AGAIN) {
|
665
698
|
if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
|
666
|
-
|
699
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_read_row");
|
667
700
|
}
|
668
701
|
continue;
|
669
702
|
}
|
@@ -689,34 +722,25 @@ static VALUE execute_read_query(VALUE vargs)
|
|
689
722
|
}
|
690
723
|
}
|
691
724
|
|
692
|
-
if (ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS) {
|
693
|
-
rb_raise(rb_cTrilogyError, "MORE_RESULTS_EXIST");
|
694
|
-
}
|
695
|
-
|
696
725
|
return result;
|
697
726
|
}
|
698
727
|
|
699
|
-
static VALUE
|
728
|
+
static VALUE execute_read_query_response(struct trilogy_ctx *ctx)
|
700
729
|
{
|
701
|
-
struct trilogy_ctx *ctx = get_open_ctx(self);
|
702
|
-
|
703
|
-
StringValue(query);
|
704
|
-
|
705
730
|
struct rb_trilogy_cast_options cast_options;
|
706
731
|
load_query_options(ctx->query_flags, &cast_options);
|
707
732
|
|
708
|
-
struct
|
733
|
+
struct read_query_response_state args = {
|
709
734
|
.cast_options = &cast_options,
|
710
735
|
.column_info = NULL,
|
711
736
|
.ctx = ctx,
|
712
|
-
.query = query,
|
713
737
|
.row_values = NULL,
|
714
738
|
.rc = TRILOGY_OK,
|
715
739
|
.msg = NULL,
|
716
740
|
};
|
717
741
|
|
718
742
|
int state = 0;
|
719
|
-
VALUE result = rb_protect(
|
743
|
+
VALUE result = rb_protect(read_query_response, (VALUE)&args, &state);
|
720
744
|
|
721
745
|
xfree(args.column_info);
|
722
746
|
xfree(args.row_values);
|
@@ -736,6 +760,47 @@ static VALUE rb_trilogy_query(VALUE self, VALUE query)
|
|
736
760
|
return result;
|
737
761
|
}
|
738
762
|
|
763
|
+
static VALUE rb_trilogy_next_result(VALUE self)
|
764
|
+
{
|
765
|
+
struct trilogy_ctx *ctx = get_open_ctx(self);
|
766
|
+
|
767
|
+
if (!(ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS)) {
|
768
|
+
return Qnil;
|
769
|
+
}
|
770
|
+
|
771
|
+
return execute_read_query_response(ctx);
|
772
|
+
}
|
773
|
+
|
774
|
+
static VALUE rb_trilogy_more_results_exist(VALUE self)
|
775
|
+
{
|
776
|
+
struct trilogy_ctx *ctx = get_open_ctx(self);
|
777
|
+
|
778
|
+
if (ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS) {
|
779
|
+
return Qtrue;
|
780
|
+
} else {
|
781
|
+
return Qfalse;
|
782
|
+
}
|
783
|
+
}
|
784
|
+
|
785
|
+
static VALUE rb_trilogy_query(VALUE self, VALUE query)
|
786
|
+
{
|
787
|
+
struct trilogy_ctx *ctx = get_open_ctx(self);
|
788
|
+
|
789
|
+
StringValue(query);
|
790
|
+
|
791
|
+
int rc = trilogy_query_send(&ctx->conn, RSTRING_PTR(query), RSTRING_LEN(query));
|
792
|
+
|
793
|
+
if (rc == TRILOGY_AGAIN) {
|
794
|
+
rc = flush_writes(ctx);
|
795
|
+
}
|
796
|
+
|
797
|
+
if (rc < 0) {
|
798
|
+
handle_trilogy_error(ctx, rc, "trilogy_query_send");
|
799
|
+
}
|
800
|
+
|
801
|
+
return execute_read_query_response(ctx);
|
802
|
+
}
|
803
|
+
|
739
804
|
static VALUE rb_trilogy_ping(VALUE self)
|
740
805
|
{
|
741
806
|
struct trilogy_ctx *ctx = get_open_ctx(self);
|
@@ -762,7 +827,7 @@ static VALUE rb_trilogy_ping(VALUE self)
|
|
762
827
|
}
|
763
828
|
|
764
829
|
if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
|
765
|
-
|
830
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_ping_recv");
|
766
831
|
}
|
767
832
|
}
|
768
833
|
|
@@ -826,6 +891,17 @@ static VALUE rb_trilogy_close(VALUE self)
|
|
826
891
|
return Qnil;
|
827
892
|
}
|
828
893
|
|
894
|
+
static VALUE rb_trilogy_closed(VALUE self)
|
895
|
+
{
|
896
|
+
struct trilogy_ctx *ctx = get_ctx(self);
|
897
|
+
|
898
|
+
if (ctx->conn.socket == NULL) {
|
899
|
+
return Qtrue;
|
900
|
+
} else {
|
901
|
+
return Qfalse;
|
902
|
+
}
|
903
|
+
}
|
904
|
+
|
829
905
|
static VALUE rb_trilogy_last_insert_id(VALUE self) { return ULL2NUM(get_open_ctx(self)->conn.last_insert_id); }
|
830
906
|
|
831
907
|
static VALUE rb_trilogy_affected_rows(VALUE self) { return ULL2NUM(get_open_ctx(self)->conn.affected_rows); }
|
@@ -887,8 +963,7 @@ static VALUE rb_trilogy_server_version(VALUE self) { return rb_str_new_cstr(get_
|
|
887
963
|
|
888
964
|
void Init_cext()
|
889
965
|
{
|
890
|
-
VALUE Trilogy =
|
891
|
-
|
966
|
+
VALUE Trilogy = rb_const_get(rb_cObject, rb_intern("Trilogy"));
|
892
967
|
rb_define_alloc_func(Trilogy, allocate_trilogy);
|
893
968
|
|
894
969
|
rb_define_method(Trilogy, "initialize", rb_trilogy_initialize, 1);
|
@@ -897,6 +972,7 @@ void Init_cext()
|
|
897
972
|
rb_define_method(Trilogy, "ping", rb_trilogy_ping, 0);
|
898
973
|
rb_define_method(Trilogy, "escape", rb_trilogy_escape, 1);
|
899
974
|
rb_define_method(Trilogy, "close", rb_trilogy_close, 0);
|
975
|
+
rb_define_method(Trilogy, "closed?", rb_trilogy_closed, 0);
|
900
976
|
rb_define_method(Trilogy, "last_insert_id", rb_trilogy_last_insert_id, 0);
|
901
977
|
rb_define_method(Trilogy, "affected_rows", rb_trilogy_affected_rows, 0);
|
902
978
|
rb_define_method(Trilogy, "warning_count", rb_trilogy_warning_count, 0);
|
@@ -909,6 +985,8 @@ void Init_cext()
|
|
909
985
|
rb_define_method(Trilogy, "write_timeout=", rb_trilogy_write_timeout_set, 1);
|
910
986
|
rb_define_method(Trilogy, "server_status", rb_trilogy_server_status, 0);
|
911
987
|
rb_define_method(Trilogy, "server_version", rb_trilogy_server_version, 0);
|
988
|
+
rb_define_method(Trilogy, "more_results_exist?", rb_trilogy_more_results_exist, 0);
|
989
|
+
rb_define_method(Trilogy, "next_result", rb_trilogy_next_result, 0);
|
912
990
|
rb_define_const(Trilogy, "TLS_VERSION_10", INT2NUM(TRILOGY_TLS_VERSION_10));
|
913
991
|
rb_define_const(Trilogy, "TLS_VERSION_11", INT2NUM(TRILOGY_TLS_VERSION_11));
|
914
992
|
rb_define_const(Trilogy, "TLS_VERSION_12", INT2NUM(TRILOGY_TLS_VERSION_12));
|
@@ -927,23 +1005,32 @@ void Init_cext()
|
|
927
1005
|
rb_define_const(Trilogy, "QUERY_FLAGS_FLATTEN_ROWS", INT2NUM(TRILOGY_FLAGS_FLATTEN_ROWS));
|
928
1006
|
rb_define_const(Trilogy, "QUERY_FLAGS_DEFAULT", INT2NUM(TRILOGY_FLAGS_DEFAULT));
|
929
1007
|
|
930
|
-
|
931
|
-
rb_global_variable(&
|
1008
|
+
Trilogy_ProtocolError = rb_const_get(Trilogy, rb_intern("ProtocolError"));
|
1009
|
+
rb_global_variable(&Trilogy_ProtocolError);
|
1010
|
+
|
1011
|
+
Trilogy_SSLError = rb_const_get(Trilogy, rb_intern("SSLError"));
|
1012
|
+
rb_global_variable(&Trilogy_SSLError);
|
1013
|
+
|
1014
|
+
Trilogy_QueryError = rb_const_get(Trilogy, rb_intern("QueryError"));
|
1015
|
+
rb_global_variable(&Trilogy_QueryError);
|
932
1016
|
|
933
|
-
|
934
|
-
rb_global_variable(&
|
1017
|
+
Trilogy_TimeoutError = rb_const_get(Trilogy, rb_intern("TimeoutError"));
|
1018
|
+
rb_global_variable(&Trilogy_TimeoutError);
|
935
1019
|
|
936
|
-
|
937
|
-
|
1020
|
+
Trilogy_BaseConnectionError = rb_const_get(Trilogy, rb_intern("BaseConnectionError"));
|
1021
|
+
rb_global_variable(&Trilogy_BaseConnectionError);
|
938
1022
|
|
939
|
-
|
1023
|
+
Trilogy_ConnectionClosedError = rb_const_get(Trilogy, rb_intern("ConnectionClosed"));
|
1024
|
+
rb_global_variable(&Trilogy_ConnectionClosedError);
|
1025
|
+
|
1026
|
+
Trilogy_Result = rb_const_get(Trilogy, rb_intern("Result"));
|
940
1027
|
rb_global_variable(&Trilogy_Result);
|
941
1028
|
|
1029
|
+
Trilogy_CastError = rb_const_get(Trilogy, rb_intern("CastError"));
|
1030
|
+
rb_global_variable(&Trilogy_CastError);
|
1031
|
+
|
942
1032
|
rb_define_attr(Trilogy_Result, "affected_rows", 1, 0);
|
943
|
-
rb_define_attr(Trilogy_Result, "fields", 1, 0);
|
944
1033
|
rb_define_attr(Trilogy_Result, "last_insert_id", 1, 0);
|
945
|
-
rb_define_attr(Trilogy_Result, "rows", 1, 0);
|
946
|
-
rb_define_attr(Trilogy_Result, "query_time", 1, 0);
|
947
1034
|
|
948
1035
|
id_socket = rb_intern("socket");
|
949
1036
|
id_host = rb_intern("host");
|
@@ -970,12 +1057,14 @@ void Init_cext()
|
|
970
1057
|
id_tls_ciphersuites = rb_intern("tls_ciphersuites");
|
971
1058
|
id_tls_min_version = rb_intern("tls_min_version");
|
972
1059
|
id_tls_max_version = rb_intern("tls_max_version");
|
973
|
-
|
1060
|
+
id_multi_statement = rb_intern("multi_statement");
|
1061
|
+
id_from_code = rb_intern("from_code");
|
974
1062
|
id_ivar_affected_rows = rb_intern("@affected_rows");
|
975
1063
|
id_ivar_fields = rb_intern("@fields");
|
976
1064
|
id_ivar_last_insert_id = rb_intern("@last_insert_id");
|
977
1065
|
id_ivar_rows = rb_intern("@rows");
|
978
1066
|
id_ivar_query_time = rb_intern("@query_time");
|
1067
|
+
id_connection_options = rb_intern("@connection_options");
|
979
1068
|
|
980
1069
|
rb_trilogy_cast_init();
|
981
1070
|
|
@@ -121,9 +121,7 @@
|
|
121
121
|
* query/ prepared statement. \
|
122
122
|
*/ \
|
123
123
|
XX(TRILOGY_CAPABILITIES_MULTI_STATEMENTS, 0x00010000) \
|
124
|
-
/*
|
125
|
-
* \
|
126
|
-
* From server: the server is capable of sending multiple result sets from \
|
124
|
+
/* From server: the server is capable of sending multiple result sets from \
|
127
125
|
* a query. \
|
128
126
|
* \
|
129
127
|
* From client: tells the server it's capable of handling multiple result \
|
@@ -23,9 +23,10 @@ struct column_info {
|
|
23
23
|
TRILOGY_CHARSET_t charset;
|
24
24
|
uint32_t len;
|
25
25
|
uint16_t flags;
|
26
|
+
uint8_t decimals;
|
26
27
|
};
|
27
28
|
|
28
|
-
extern VALUE
|
29
|
+
extern VALUE Trilogy_CastError;
|
29
30
|
|
30
31
|
VALUE
|
31
32
|
rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *column,
|
data/lib/trilogy/version.rb
CHANGED
data/lib/trilogy.rb
CHANGED
@@ -1,7 +1,101 @@
|
|
1
|
-
require "trilogy/cext"
|
2
1
|
require "trilogy/version"
|
3
2
|
|
4
3
|
class Trilogy
|
4
|
+
# Trilogy::Error is the base error type. All errors raised by Trilogy
|
5
|
+
# should be descendants of Trilogy::Error
|
6
|
+
module Error
|
7
|
+
end
|
8
|
+
|
9
|
+
# Trilogy::ConnectionError is the base error type for all potentially transient
|
10
|
+
# network errors.
|
11
|
+
module ConnectionError
|
12
|
+
include Error
|
13
|
+
end
|
14
|
+
|
15
|
+
class BaseError < StandardError
|
16
|
+
include Error
|
17
|
+
|
18
|
+
attr_reader :error_code
|
19
|
+
|
20
|
+
def initialize(error_message = nil, error_code = nil)
|
21
|
+
message = error_code ? "#{error_code}: #{error_message}" : error_message
|
22
|
+
super(message)
|
23
|
+
@error_code = error_code
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class BaseConnectionError < BaseError
|
28
|
+
include ConnectionError
|
29
|
+
end
|
30
|
+
|
31
|
+
# Trilogy::ClientError is the base error type for invalid queries or parameters
|
32
|
+
# that shouldn't be retried.
|
33
|
+
class ClientError < BaseError
|
34
|
+
include Error
|
35
|
+
end
|
36
|
+
|
37
|
+
class QueryError < ClientError
|
38
|
+
end
|
39
|
+
|
40
|
+
class CastError < ClientError
|
41
|
+
end
|
42
|
+
|
43
|
+
class TimeoutError < Errno::ETIMEDOUT
|
44
|
+
include ConnectionError
|
45
|
+
|
46
|
+
attr_reader :error_code
|
47
|
+
|
48
|
+
def initialize(error_message = nil, error_code = nil)
|
49
|
+
super
|
50
|
+
@error_code = error_code
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# DatabaseError was replaced by ProtocolError, but we'll keep it around as an
|
55
|
+
# ancestor of ProtocolError for compatibility reasons (e.g. so `rescue DatabaseError`
|
56
|
+
# still works. We can remove this class in the next major release.
|
57
|
+
module DatabaseError
|
58
|
+
end
|
59
|
+
|
60
|
+
class ProtocolError < BaseError
|
61
|
+
include DatabaseError
|
62
|
+
|
63
|
+
ERROR_CODES = {
|
64
|
+
1205 => TimeoutError, # ER_LOCK_WAIT_TIMEOUT
|
65
|
+
1044 => BaseConnectionError, # ER_DBACCESS_DENIED_ERROR
|
66
|
+
1045 => BaseConnectionError, # ER_ACCESS_DENIED_ERROR
|
67
|
+
1064 => QueryError, # ER_PARSE_ERROR
|
68
|
+
1152 => BaseConnectionError, # ER_ABORTING_CONNECTION
|
69
|
+
1153 => BaseConnectionError, # ER_NET_PACKET_TOO_LARGE
|
70
|
+
1154 => BaseConnectionError, # ER_NET_READ_ERROR_FROM_PIPE
|
71
|
+
1155 => BaseConnectionError, # ER_NET_FCNTL_ERROR
|
72
|
+
1156 => BaseConnectionError, # ER_NET_PACKETS_OUT_OF_ORDER
|
73
|
+
1157 => BaseConnectionError, # ER_NET_UNCOMPRESS_ERROR
|
74
|
+
1158 => BaseConnectionError, # ER_NET_READ_ERROR
|
75
|
+
1159 => BaseConnectionError, # ER_NET_READ_INTERRUPTED
|
76
|
+
1160 => BaseConnectionError, # ER_NET_ERROR_ON_WRITE
|
77
|
+
1161 => BaseConnectionError, # ER_NET_WRITE_INTERRUPTED
|
78
|
+
1927 => BaseConnectionError, # ER_CONNECTION_KILLED
|
79
|
+
}
|
80
|
+
class << self
|
81
|
+
def from_code(message, code)
|
82
|
+
ERROR_CODES.fetch(code, self).new(message, code)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class SSLError < BaseError
|
88
|
+
include ConnectionError
|
89
|
+
end
|
90
|
+
|
91
|
+
class ConnectionClosed < IOError
|
92
|
+
include ConnectionError
|
93
|
+
end
|
94
|
+
|
95
|
+
def connection_options
|
96
|
+
@connection_options.dup.freeze
|
97
|
+
end
|
98
|
+
|
5
99
|
def in_transaction?
|
6
100
|
(server_status & SERVER_STATUS_IN_TRANS) != 0
|
7
101
|
end
|
@@ -28,34 +122,38 @@ class Trilogy
|
|
28
122
|
ensure
|
29
123
|
self.query_flags = old_flags
|
30
124
|
end
|
31
|
-
end
|
32
125
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
126
|
+
class Result
|
127
|
+
attr_reader :fields, :rows, :query_time
|
128
|
+
|
129
|
+
def count
|
130
|
+
rows.count
|
131
|
+
end
|
132
|
+
|
133
|
+
def each_hash
|
134
|
+
return enum_for(:each_hash) unless block_given?
|
37
135
|
|
38
|
-
|
39
|
-
|
136
|
+
rows.each do |row|
|
137
|
+
this_row = {}
|
40
138
|
|
41
|
-
|
42
|
-
|
139
|
+
idx = 0
|
140
|
+
row.each do |col|
|
141
|
+
this_row[fields[idx]] = col
|
142
|
+
idx += 1
|
143
|
+
end
|
43
144
|
|
44
|
-
|
45
|
-
row.each do |col|
|
46
|
-
this_row[fields[idx]] = col
|
47
|
-
idx += 1
|
145
|
+
yield this_row
|
48
146
|
end
|
49
147
|
|
50
|
-
|
148
|
+
self
|
51
149
|
end
|
52
150
|
|
53
|
-
|
54
|
-
|
151
|
+
def each(&bk)
|
152
|
+
rows.each(&bk)
|
153
|
+
end
|
55
154
|
|
56
|
-
|
57
|
-
rows.each(&bk)
|
155
|
+
include Enumerable
|
58
156
|
end
|
59
|
-
|
60
|
-
include Enumerable
|
61
157
|
end
|
158
|
+
|
159
|
+
require "trilogy/cext"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trilogy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub Engineering
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-03-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -99,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
99
99
|
- !ruby/object:Gem::Version
|
100
100
|
version: '0'
|
101
101
|
requirements: []
|
102
|
-
rubygems_version: 3.
|
102
|
+
rubygems_version: 3.4.7
|
103
103
|
signing_key:
|
104
104
|
specification_version: 4
|
105
105
|
summary: A friendly MySQL-compatible library for Ruby, binding to libtrilogy
|