trilogy 2.1.2 → 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 +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
|