trilogy 2.2.0 → 2.3.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/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
|