trilogy 2.2.0 → 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 +125 -68
- 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;
|
@@ -72,7 +72,7 @@ static struct trilogy_ctx *get_open_ctx(VALUE obj)
|
|
72
72
|
struct trilogy_ctx *ctx = get_ctx(obj);
|
73
73
|
|
74
74
|
if (ctx->conn.socket == NULL) {
|
75
|
-
rb_raise(
|
75
|
+
rb_raise(Trilogy_ConnectionClosedError, "Attempted to use closed connection");
|
76
76
|
}
|
77
77
|
|
78
78
|
return ctx;
|
@@ -88,16 +88,16 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
|
|
88
88
|
|
89
89
|
switch (rc) {
|
90
90
|
case TRILOGY_SYSERR:
|
91
|
-
|
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
|
+
}
|
92
97
|
|
93
98
|
case TRILOGY_ERR: {
|
94
99
|
VALUE message = rb_str_new(ctx->conn.error_message, ctx->conn.error_message_len);
|
95
|
-
VALUE exc =
|
96
|
-
rb_sprintf("%" PRIsVALUE ": %d %" PRIsVALUE, rbmsg, ctx->conn.error_code, message));
|
97
|
-
|
98
|
-
rb_ivar_set(exc, rb_intern("@error_code"), INT2FIX(ctx->conn.error_code));
|
99
|
-
rb_ivar_set(exc, rb_intern("@error_message"), message);
|
100
|
-
|
100
|
+
VALUE exc = rb_funcall(Trilogy_ProtocolError, id_from_code, 2, message, INT2NUM(ctx->conn.error_code));
|
101
101
|
rb_exc_raise(exc);
|
102
102
|
}
|
103
103
|
|
@@ -105,18 +105,29 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
|
|
105
105
|
unsigned long ossl_error = ERR_get_error();
|
106
106
|
ERR_clear_error();
|
107
107
|
if (ERR_GET_LIB(ossl_error) == ERR_LIB_SYS) {
|
108
|
-
|
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
|
+
}
|
109
116
|
}
|
110
117
|
// We can't recover from OpenSSL level errors if there's
|
111
118
|
// an active connection.
|
112
119
|
if (ctx->conn.socket != NULL) {
|
113
120
|
trilogy_sock_shutdown(ctx->conn.socket);
|
114
121
|
}
|
115
|
-
rb_raise(
|
122
|
+
rb_raise(Trilogy_SSLError, "%" PRIsVALUE ": SSL Error: %s", rbmsg, ERR_reason_error_string(ossl_error));
|
123
|
+
}
|
124
|
+
|
125
|
+
case TRILOGY_DNS_ERR: {
|
126
|
+
rb_raise(Trilogy_BaseConnectionError, "%" PRIsVALUE ": TRILOGY_DNS_ERROR", rbmsg);
|
116
127
|
}
|
117
128
|
|
118
129
|
default:
|
119
|
-
rb_raise(
|
130
|
+
rb_raise(Trilogy_QueryError, "%" PRIsVALUE ": %s", rbmsg, trilogy_error(rc));
|
120
131
|
}
|
121
132
|
}
|
122
133
|
|
@@ -129,7 +140,13 @@ static VALUE allocate_trilogy(VALUE klass)
|
|
129
140
|
ctx->query_flags = TRILOGY_FLAGS_DEFAULT;
|
130
141
|
|
131
142
|
if (trilogy_init(&ctx->conn) < 0) {
|
132
|
-
|
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
|
+
}
|
133
150
|
}
|
134
151
|
|
135
152
|
return obj;
|
@@ -145,7 +162,7 @@ static int flush_writes(struct trilogy_ctx *ctx)
|
|
145
162
|
}
|
146
163
|
|
147
164
|
if (trilogy_sock_wait_write(ctx->conn.socket) < 0) {
|
148
|
-
|
165
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_flush_writes");
|
149
166
|
}
|
150
167
|
}
|
151
168
|
}
|
@@ -279,7 +296,7 @@ static void auth_switch(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake)
|
|
279
296
|
}
|
280
297
|
|
281
298
|
if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
|
282
|
-
|
299
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_auth_recv");
|
283
300
|
}
|
284
301
|
}
|
285
302
|
}
|
@@ -305,7 +322,7 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
|
|
305
322
|
}
|
306
323
|
} else {
|
307
324
|
if (ssl_mode != TRILOGY_SSL_PREFERRED_NOVERIFY) {
|
308
|
-
rb_raise(
|
325
|
+
rb_raise(Trilogy_SSLError, "SSL required, not supported by server");
|
309
326
|
}
|
310
327
|
}
|
311
328
|
}
|
@@ -332,7 +349,7 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
|
|
332
349
|
}
|
333
350
|
|
334
351
|
if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
|
335
|
-
|
352
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_auth_recv");
|
336
353
|
}
|
337
354
|
}
|
338
355
|
|
@@ -349,6 +366,7 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
|
|
349
366
|
VALUE val;
|
350
367
|
|
351
368
|
Check_Type(opts, T_HASH);
|
369
|
+
rb_ivar_set(self, id_connection_options, opts);
|
352
370
|
|
353
371
|
if ((val = rb_hash_lookup(opts, ID2SYM(id_ssl_mode))) != Qnil) {
|
354
372
|
Check_Type(val, T_FIXNUM);
|
@@ -426,6 +444,10 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
|
|
426
444
|
connopt.flags |= TRILOGY_CAPABILITIES_FOUND_ROWS;
|
427
445
|
}
|
428
446
|
|
447
|
+
if (RTEST(rb_hash_aref(opts, ID2SYM(id_multi_statement)))) {
|
448
|
+
connopt.flags |= TRILOGY_CAPABILITIES_MULTI_STATEMENTS;
|
449
|
+
}
|
450
|
+
|
429
451
|
if ((val = rb_hash_aref(opts, ID2SYM(id_ssl_ca))) != Qnil) {
|
430
452
|
Check_Type(val, T_STRING);
|
431
453
|
connopt.ssl_ca = StringValueCStr(val);
|
@@ -478,7 +500,7 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
|
|
478
500
|
|
479
501
|
int rc = try_connect(ctx, &handshake, &connopt);
|
480
502
|
if (rc == TRILOGY_RB_TIMEOUT) {
|
481
|
-
|
503
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_connect_recv");
|
482
504
|
}
|
483
505
|
if (rc != TRILOGY_OK) {
|
484
506
|
if (connopt.path) {
|
@@ -525,7 +547,7 @@ static VALUE rb_trilogy_change_db(VALUE self, VALUE database)
|
|
525
547
|
}
|
526
548
|
|
527
549
|
if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
|
528
|
-
|
550
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_change_db_recv");
|
529
551
|
}
|
530
552
|
}
|
531
553
|
|
@@ -540,10 +562,9 @@ static void load_query_options(unsigned int query_flags, struct rb_trilogy_cast_
|
|
540
562
|
cast_options->flatten_rows = (query_flags & TRILOGY_FLAGS_FLATTEN_ROWS) != 0;
|
541
563
|
}
|
542
564
|
|
543
|
-
struct
|
565
|
+
struct read_query_response_state {
|
544
566
|
struct rb_trilogy_cast_options *cast_options;
|
545
567
|
struct trilogy_ctx *ctx;
|
546
|
-
VALUE query;
|
547
568
|
|
548
569
|
// to free by caller:
|
549
570
|
struct column_info *column_info;
|
@@ -570,34 +591,25 @@ static void get_timespec_monotonic(struct timespec *ts)
|
|
570
591
|
#endif
|
571
592
|
}
|
572
593
|
|
573
|
-
static VALUE read_query_error(struct
|
594
|
+
static VALUE read_query_error(struct read_query_response_state *args, int rc, const char *msg)
|
574
595
|
{
|
575
596
|
args->rc = rc;
|
576
597
|
args->msg = msg;
|
577
598
|
return Qundef;
|
578
599
|
}
|
579
600
|
|
580
|
-
static VALUE
|
601
|
+
static VALUE read_query_response(VALUE vargs)
|
581
602
|
{
|
582
|
-
struct
|
603
|
+
struct read_query_response_state *args = (void *)vargs;
|
583
604
|
struct trilogy_ctx *ctx = args->ctx;
|
584
|
-
VALUE query = args->query;
|
585
605
|
|
586
606
|
struct timespec start;
|
587
607
|
get_timespec_monotonic(&start);
|
588
608
|
|
589
|
-
int rc = trilogy_query_send(&ctx->conn, RSTRING_PTR(query), RSTRING_LEN(query));
|
590
|
-
|
591
|
-
if (rc == TRILOGY_AGAIN) {
|
592
|
-
rc = flush_writes(ctx);
|
593
|
-
}
|
594
|
-
|
595
|
-
if (rc < 0) {
|
596
|
-
return read_query_error(args, rc, "trilogy_query_send");
|
597
|
-
}
|
598
|
-
|
599
609
|
uint64_t column_count = 0;
|
600
610
|
|
611
|
+
int rc;
|
612
|
+
|
601
613
|
while (1) {
|
602
614
|
rc = trilogy_query_recv(&ctx->conn, &column_count);
|
603
615
|
|
@@ -610,7 +622,7 @@ static VALUE execute_read_query(VALUE vargs)
|
|
610
622
|
}
|
611
623
|
|
612
624
|
if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
|
613
|
-
|
625
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_query_recv");
|
614
626
|
}
|
615
627
|
}
|
616
628
|
|
@@ -656,7 +668,7 @@ static VALUE execute_read_query(VALUE vargs)
|
|
656
668
|
}
|
657
669
|
|
658
670
|
if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
|
659
|
-
|
671
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_read_column");
|
660
672
|
}
|
661
673
|
}
|
662
674
|
|
@@ -673,6 +685,7 @@ static VALUE execute_read_query(VALUE vargs)
|
|
673
685
|
column_info[i].flags = column.flags;
|
674
686
|
column_info[i].len = column.len;
|
675
687
|
column_info[i].charset = column.charset;
|
688
|
+
column_info[i].decimals = column.decimals;
|
676
689
|
}
|
677
690
|
|
678
691
|
trilogy_value_t *row_values = ALLOC_N(trilogy_value_t, column_count);
|
@@ -683,7 +696,7 @@ static VALUE execute_read_query(VALUE vargs)
|
|
683
696
|
|
684
697
|
if (rc == TRILOGY_AGAIN) {
|
685
698
|
if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
|
686
|
-
|
699
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_read_row");
|
687
700
|
}
|
688
701
|
continue;
|
689
702
|
}
|
@@ -709,34 +722,25 @@ static VALUE execute_read_query(VALUE vargs)
|
|
709
722
|
}
|
710
723
|
}
|
711
724
|
|
712
|
-
if (ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS) {
|
713
|
-
rb_raise(rb_cTrilogyError, "MORE_RESULTS_EXIST");
|
714
|
-
}
|
715
|
-
|
716
725
|
return result;
|
717
726
|
}
|
718
727
|
|
719
|
-
static VALUE
|
728
|
+
static VALUE execute_read_query_response(struct trilogy_ctx *ctx)
|
720
729
|
{
|
721
|
-
struct trilogy_ctx *ctx = get_open_ctx(self);
|
722
|
-
|
723
|
-
StringValue(query);
|
724
|
-
|
725
730
|
struct rb_trilogy_cast_options cast_options;
|
726
731
|
load_query_options(ctx->query_flags, &cast_options);
|
727
732
|
|
728
|
-
struct
|
733
|
+
struct read_query_response_state args = {
|
729
734
|
.cast_options = &cast_options,
|
730
735
|
.column_info = NULL,
|
731
736
|
.ctx = ctx,
|
732
|
-
.query = query,
|
733
737
|
.row_values = NULL,
|
734
738
|
.rc = TRILOGY_OK,
|
735
739
|
.msg = NULL,
|
736
740
|
};
|
737
741
|
|
738
742
|
int state = 0;
|
739
|
-
VALUE result = rb_protect(
|
743
|
+
VALUE result = rb_protect(read_query_response, (VALUE)&args, &state);
|
740
744
|
|
741
745
|
xfree(args.column_info);
|
742
746
|
xfree(args.row_values);
|
@@ -756,6 +760,47 @@ static VALUE rb_trilogy_query(VALUE self, VALUE query)
|
|
756
760
|
return result;
|
757
761
|
}
|
758
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
|
+
|
759
804
|
static VALUE rb_trilogy_ping(VALUE self)
|
760
805
|
{
|
761
806
|
struct trilogy_ctx *ctx = get_open_ctx(self);
|
@@ -782,7 +827,7 @@ static VALUE rb_trilogy_ping(VALUE self)
|
|
782
827
|
}
|
783
828
|
|
784
829
|
if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
|
785
|
-
|
830
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_ping_recv");
|
786
831
|
}
|
787
832
|
}
|
788
833
|
|
@@ -918,8 +963,7 @@ static VALUE rb_trilogy_server_version(VALUE self) { return rb_str_new_cstr(get_
|
|
918
963
|
|
919
964
|
void Init_cext()
|
920
965
|
{
|
921
|
-
VALUE Trilogy =
|
922
|
-
|
966
|
+
VALUE Trilogy = rb_const_get(rb_cObject, rb_intern("Trilogy"));
|
923
967
|
rb_define_alloc_func(Trilogy, allocate_trilogy);
|
924
968
|
|
925
969
|
rb_define_method(Trilogy, "initialize", rb_trilogy_initialize, 1);
|
@@ -941,6 +985,8 @@ void Init_cext()
|
|
941
985
|
rb_define_method(Trilogy, "write_timeout=", rb_trilogy_write_timeout_set, 1);
|
942
986
|
rb_define_method(Trilogy, "server_status", rb_trilogy_server_status, 0);
|
943
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);
|
944
990
|
rb_define_const(Trilogy, "TLS_VERSION_10", INT2NUM(TRILOGY_TLS_VERSION_10));
|
945
991
|
rb_define_const(Trilogy, "TLS_VERSION_11", INT2NUM(TRILOGY_TLS_VERSION_11));
|
946
992
|
rb_define_const(Trilogy, "TLS_VERSION_12", INT2NUM(TRILOGY_TLS_VERSION_12));
|
@@ -959,23 +1005,32 @@ void Init_cext()
|
|
959
1005
|
rb_define_const(Trilogy, "QUERY_FLAGS_FLATTEN_ROWS", INT2NUM(TRILOGY_FLAGS_FLATTEN_ROWS));
|
960
1006
|
rb_define_const(Trilogy, "QUERY_FLAGS_DEFAULT", INT2NUM(TRILOGY_FLAGS_DEFAULT));
|
961
1007
|
|
962
|
-
|
963
|
-
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);
|
964
1013
|
|
965
|
-
|
966
|
-
rb_global_variable(&
|
1014
|
+
Trilogy_QueryError = rb_const_get(Trilogy, rb_intern("QueryError"));
|
1015
|
+
rb_global_variable(&Trilogy_QueryError);
|
967
1016
|
|
968
|
-
|
969
|
-
|
1017
|
+
Trilogy_TimeoutError = rb_const_get(Trilogy, rb_intern("TimeoutError"));
|
1018
|
+
rb_global_variable(&Trilogy_TimeoutError);
|
970
1019
|
|
971
|
-
|
1020
|
+
Trilogy_BaseConnectionError = rb_const_get(Trilogy, rb_intern("BaseConnectionError"));
|
1021
|
+
rb_global_variable(&Trilogy_BaseConnectionError);
|
1022
|
+
|
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"));
|
972
1027
|
rb_global_variable(&Trilogy_Result);
|
973
1028
|
|
1029
|
+
Trilogy_CastError = rb_const_get(Trilogy, rb_intern("CastError"));
|
1030
|
+
rb_global_variable(&Trilogy_CastError);
|
1031
|
+
|
974
1032
|
rb_define_attr(Trilogy_Result, "affected_rows", 1, 0);
|
975
|
-
rb_define_attr(Trilogy_Result, "fields", 1, 0);
|
976
1033
|
rb_define_attr(Trilogy_Result, "last_insert_id", 1, 0);
|
977
|
-
rb_define_attr(Trilogy_Result, "rows", 1, 0);
|
978
|
-
rb_define_attr(Trilogy_Result, "query_time", 1, 0);
|
979
1034
|
|
980
1035
|
id_socket = rb_intern("socket");
|
981
1036
|
id_host = rb_intern("host");
|
@@ -1002,12 +1057,14 @@ void Init_cext()
|
|
1002
1057
|
id_tls_ciphersuites = rb_intern("tls_ciphersuites");
|
1003
1058
|
id_tls_min_version = rb_intern("tls_min_version");
|
1004
1059
|
id_tls_max_version = rb_intern("tls_max_version");
|
1005
|
-
|
1060
|
+
id_multi_statement = rb_intern("multi_statement");
|
1061
|
+
id_from_code = rb_intern("from_code");
|
1006
1062
|
id_ivar_affected_rows = rb_intern("@affected_rows");
|
1007
1063
|
id_ivar_fields = rb_intern("@fields");
|
1008
1064
|
id_ivar_last_insert_id = rb_intern("@last_insert_id");
|
1009
1065
|
id_ivar_rows = rb_intern("@rows");
|
1010
1066
|
id_ivar_query_time = rb_intern("@query_time");
|
1067
|
+
id_connection_options = rb_intern("@connection_options");
|
1011
1068
|
|
1012
1069
|
rb_trilogy_cast_init();
|
1013
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
|