trilogy 2.3.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/trilogy-ruby/cast.c +2 -2
- data/ext/trilogy-ruby/cext.c +130 -45
- data/ext/trilogy-ruby/extconf.rb +1 -1
- data/ext/trilogy-ruby/inc/trilogy/blocking.h +18 -1
- data/ext/trilogy-ruby/inc/trilogy/client.h +58 -0
- data/ext/trilogy-ruby/inc/trilogy/protocol.h +55 -32
- data/ext/trilogy-ruby/inc/trilogy/socket.h +2 -0
- data/ext/trilogy-ruby/src/blocking.c +23 -0
- data/ext/trilogy-ruby/src/client.c +51 -3
- data/ext/trilogy-ruby/src/protocol.c +21 -6
- data/ext/trilogy-ruby/src/socket.c +25 -0
- data/ext/trilogy-ruby/trilogy-ruby.h +2 -0
- data/lib/trilogy/version.rb +1 -1
- data/lib/trilogy.rb +128 -8
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e9c5df97a7642030f5c79167a0ff54ab25f500e38555b0303b9755f4102b0b17
|
4
|
+
data.tar.gz: a67ea6052ca5d2346a5b7cb72f976c6c6a445361365e7bc3f343a54cd43151dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d356765239b247f1902582abfb7a724eee0b2b18a3e8e0eabe71db196555bf5749192f259dba8c65f8a0945106df4413f328abeef8241cb3517495725dedabc
|
7
|
+
data.tar.gz: c77e07b1a5e423d1ed7065a613feee4cd038be3606f519f7dc2fbbe87b389cf9d1fd4cc41993023060b504c40f12f85f8e75c688e1b4c418535c8891c192f26f
|
data/ext/trilogy-ruby/cast.c
CHANGED
@@ -145,7 +145,7 @@ 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
|
-
if (column->decimals == 0) {
|
148
|
+
if (column->decimals == 0 && !options->cast_decimals_to_bigdecimals) {
|
149
149
|
return rb_funcall(rb_mKernel, id_Integer, 1, str);
|
150
150
|
} else {
|
151
151
|
return rb_funcall(rb_mKernel, id_BigDecimal, 1, str);
|
@@ -240,7 +240,7 @@ rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *co
|
|
240
240
|
// pad out msec_char with zeroes at the end as it could be at any
|
241
241
|
// level of precision
|
242
242
|
for (size_t i = strlen(msec_char); i < sizeof(msec_char) - 1; i++) {
|
243
|
-
msec_char[i] = 0;
|
243
|
+
msec_char[i] = '0';
|
244
244
|
}
|
245
245
|
|
246
246
|
return rb_funcall(rb_cTime, options->database_local_time ? id_local : id_utc, 7, INT2NUM(2000), INT2NUM(1),
|
data/ext/trilogy-ruby/cext.c
CHANGED
@@ -8,6 +8,9 @@
|
|
8
8
|
#include <sys/time.h>
|
9
9
|
#include <sys/un.h>
|
10
10
|
|
11
|
+
#include <unistd.h>
|
12
|
+
#include <fcntl.h>
|
13
|
+
|
11
14
|
#include <trilogy.h>
|
12
15
|
|
13
16
|
#include "trilogy-ruby.h"
|
@@ -16,21 +19,29 @@
|
|
16
19
|
|
17
20
|
VALUE Trilogy_CastError;
|
18
21
|
static VALUE Trilogy_BaseConnectionError, Trilogy_ProtocolError, Trilogy_SSLError, Trilogy_QueryError,
|
19
|
-
Trilogy_ConnectionClosedError,
|
22
|
+
Trilogy_ConnectionClosedError, Trilogy_ConnectionRefusedError, Trilogy_ConnectionResetError,
|
23
|
+
Trilogy_TimeoutError, Trilogy_SyscallError, Trilogy_Result;
|
20
24
|
|
21
25
|
static ID id_socket, id_host, id_port, id_username, id_password, id_found_rows, id_connect_timeout, id_read_timeout,
|
22
26
|
id_write_timeout, id_keepalive_enabled, id_keepalive_idle, id_keepalive_interval, id_keepalive_count,
|
23
27
|
id_ivar_affected_rows, id_ivar_fields, id_ivar_last_insert_id, id_ivar_rows, id_ivar_query_time, id_password,
|
24
28
|
id_database, id_ssl_ca, id_ssl_capath, id_ssl_cert, id_ssl_cipher, id_ssl_crl, id_ssl_crlpath, id_ssl_key,
|
25
|
-
id_ssl_mode, id_tls_ciphersuites, id_tls_min_version, id_tls_max_version, id_multi_statement,
|
26
|
-
id_connection_options;
|
29
|
+
id_ssl_mode, id_tls_ciphersuites, id_tls_min_version, id_tls_max_version, id_multi_statement, id_multi_result,
|
30
|
+
id_from_code, id_from_errno, id_connection_options;
|
27
31
|
|
28
32
|
struct trilogy_ctx {
|
29
33
|
trilogy_conn_t conn;
|
30
34
|
char server_version[TRILOGY_SERVER_VERSION_SIZE + 1];
|
31
35
|
unsigned int query_flags;
|
36
|
+
VALUE encoding;
|
32
37
|
};
|
33
38
|
|
39
|
+
static void mark_trilogy(void *ptr)
|
40
|
+
{
|
41
|
+
struct trilogy_ctx *ctx = ptr;
|
42
|
+
rb_gc_mark(ctx->encoding);
|
43
|
+
}
|
44
|
+
|
34
45
|
static void free_trilogy(void *ptr)
|
35
46
|
{
|
36
47
|
struct trilogy_ctx *ctx = ptr;
|
@@ -50,10 +61,10 @@ static size_t trilogy_memsize(const void *ptr) {
|
|
50
61
|
return memsize;
|
51
62
|
}
|
52
63
|
|
53
|
-
const rb_data_type_t trilogy_data_type = {
|
64
|
+
static const rb_data_type_t trilogy_data_type = {
|
54
65
|
.wrap_struct_name = "trilogy",
|
55
66
|
.function = {
|
56
|
-
.dmark =
|
67
|
+
.dmark = mark_trilogy,
|
57
68
|
.dfree = free_trilogy,
|
58
69
|
.dsize = trilogy_memsize,
|
59
70
|
},
|
@@ -78,6 +89,19 @@ static struct trilogy_ctx *get_open_ctx(VALUE obj)
|
|
78
89
|
return ctx;
|
79
90
|
}
|
80
91
|
|
92
|
+
NORETURN(static void trilogy_syserr_fail_str(int, VALUE));
|
93
|
+
static void trilogy_syserr_fail_str(int e, VALUE msg)
|
94
|
+
{
|
95
|
+
if (e == ECONNREFUSED) {
|
96
|
+
rb_raise(Trilogy_ConnectionRefusedError, "%" PRIsVALUE, msg);
|
97
|
+
} else if (e == ECONNRESET) {
|
98
|
+
rb_raise(Trilogy_ConnectionResetError, "%" PRIsVALUE, msg);
|
99
|
+
} else {
|
100
|
+
VALUE exc = rb_funcall(Trilogy_SyscallError, id_from_errno, 2, INT2NUM(e), msg);
|
101
|
+
rb_exc_raise(exc);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
81
105
|
NORETURN(static void handle_trilogy_error(struct trilogy_ctx *, int, const char *, ...));
|
82
106
|
static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *msg, ...)
|
83
107
|
{
|
@@ -88,12 +112,7 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
|
|
88
112
|
|
89
113
|
switch (rc) {
|
90
114
|
case TRILOGY_SYSERR:
|
91
|
-
|
92
|
-
rb_raise(Trilogy_BaseConnectionError, "%" PRIsVALUE, rbmsg);
|
93
|
-
} else {
|
94
|
-
// TODO: All syserr should be wrapped.
|
95
|
-
rb_syserr_fail_str(errno, rbmsg);
|
96
|
-
}
|
115
|
+
trilogy_syserr_fail_str(errno, rbmsg);
|
97
116
|
|
98
117
|
case TRILOGY_ERR: {
|
99
118
|
VALUE message = rb_str_new(ctx->conn.error_message, ctx->conn.error_message_len);
|
@@ -106,13 +125,7 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
|
|
106
125
|
ERR_clear_error();
|
107
126
|
if (ERR_GET_LIB(ossl_error) == ERR_LIB_SYS) {
|
108
127
|
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
|
-
}
|
128
|
+
trilogy_syserr_fail_str(err_reason, rbmsg);
|
116
129
|
}
|
117
130
|
// We can't recover from OpenSSL level errors if there's
|
118
131
|
// an active connection.
|
@@ -140,13 +153,8 @@ static VALUE allocate_trilogy(VALUE klass)
|
|
140
153
|
ctx->query_flags = TRILOGY_FLAGS_DEFAULT;
|
141
154
|
|
142
155
|
if (trilogy_init(&ctx->conn) < 0) {
|
143
|
-
|
144
|
-
|
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
|
-
}
|
156
|
+
VALUE rbmsg = rb_str_new("trilogy_init", 13);
|
157
|
+
trilogy_syserr_fail_str(errno, rbmsg);
|
150
158
|
}
|
151
159
|
|
152
160
|
return obj;
|
@@ -358,13 +366,16 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
|
|
358
366
|
}
|
359
367
|
}
|
360
368
|
|
361
|
-
static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
|
369
|
+
static VALUE rb_trilogy_initialize(VALUE self, VALUE encoding, VALUE charset, VALUE opts)
|
362
370
|
{
|
363
371
|
struct trilogy_ctx *ctx = get_ctx(self);
|
364
372
|
trilogy_sockopt_t connopt = {0};
|
365
373
|
trilogy_handshake_t handshake;
|
366
374
|
VALUE val;
|
367
375
|
|
376
|
+
RB_OBJ_WRITE(self, &ctx->encoding, encoding);
|
377
|
+
connopt.encoding = NUM2INT(charset);
|
378
|
+
|
368
379
|
Check_Type(opts, T_HASH);
|
369
380
|
rb_ivar_set(self, id_connection_options, opts);
|
370
381
|
|
@@ -444,8 +455,12 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
|
|
444
455
|
connopt.flags |= TRILOGY_CAPABILITIES_FOUND_ROWS;
|
445
456
|
}
|
446
457
|
|
458
|
+
if (RTEST(rb_hash_aref(opts, ID2SYM(id_multi_result)))) {
|
459
|
+
connopt.flags |= TRILOGY_CAPABILITIES_MULTI_RESULTS;
|
460
|
+
}
|
461
|
+
|
447
462
|
if (RTEST(rb_hash_aref(opts, ID2SYM(id_multi_statement)))) {
|
448
|
-
connopt.flags |= TRILOGY_CAPABILITIES_MULTI_STATEMENTS;
|
463
|
+
connopt.flags |= TRILOGY_CAPABILITIES_MULTI_STATEMENTS | TRILOGY_CAPABILITIES_MULTI_RESULTS;
|
449
464
|
}
|
450
465
|
|
451
466
|
if ((val = rb_hash_aref(opts, ID2SYM(id_ssl_ca))) != Qnil) {
|
@@ -554,10 +569,45 @@ static VALUE rb_trilogy_change_db(VALUE self, VALUE database)
|
|
554
569
|
return Qtrue;
|
555
570
|
}
|
556
571
|
|
572
|
+
static VALUE rb_trilogy_set_server_option(VALUE self, VALUE option)
|
573
|
+
{
|
574
|
+
struct trilogy_ctx *ctx = get_open_ctx(self);
|
575
|
+
|
576
|
+
int rc = trilogy_set_option_send(&ctx->conn, NUM2INT(option));
|
577
|
+
|
578
|
+
if (rc == TRILOGY_AGAIN) {
|
579
|
+
rc = flush_writes(ctx);
|
580
|
+
}
|
581
|
+
|
582
|
+
if (rc != TRILOGY_OK) {
|
583
|
+
handle_trilogy_error(ctx, rc, "trilogy_set_option_send");
|
584
|
+
}
|
585
|
+
|
586
|
+
while (1) {
|
587
|
+
rc = trilogy_set_option_recv(&ctx->conn);
|
588
|
+
|
589
|
+
if (rc == TRILOGY_OK) {
|
590
|
+
break;
|
591
|
+
}
|
592
|
+
|
593
|
+
if (rc != TRILOGY_AGAIN) {
|
594
|
+
handle_trilogy_error(ctx, rc, "trilogy_set_option_recv");
|
595
|
+
}
|
596
|
+
|
597
|
+
if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
|
598
|
+
rb_raise(Trilogy_TimeoutError, "trilogy_set_option_recv");
|
599
|
+
}
|
600
|
+
}
|
601
|
+
|
602
|
+
return Qtrue;
|
603
|
+
}
|
604
|
+
|
605
|
+
|
557
606
|
static void load_query_options(unsigned int query_flags, struct rb_trilogy_cast_options *cast_options)
|
558
607
|
{
|
559
608
|
cast_options->cast = (query_flags & TRILOGY_FLAGS_CAST) != 0;
|
560
609
|
cast_options->cast_booleans = (query_flags & TRILOGY_FLAGS_CAST_BOOLEANS) != 0;
|
610
|
+
cast_options->cast_decimals_to_bigdecimals = (query_flags & TRILOGY_FLAGS_CAST_ALL_DECIMALS_TO_BIGDECIMALS) != 0;
|
561
611
|
cast_options->database_local_time = (query_flags & TRILOGY_FLAGS_LOCAL_TIMEZONE) != 0;
|
562
612
|
cast_options->flatten_rows = (query_flags & TRILOGY_FLAGS_FLATTEN_ROWS) != 0;
|
563
613
|
}
|
@@ -566,10 +616,6 @@ struct read_query_response_state {
|
|
566
616
|
struct rb_trilogy_cast_options *cast_options;
|
567
617
|
struct trilogy_ctx *ctx;
|
568
618
|
|
569
|
-
// to free by caller:
|
570
|
-
struct column_info *column_info;
|
571
|
-
trilogy_value_t *row_values;
|
572
|
-
|
573
619
|
// Error state for tracking
|
574
620
|
const char *msg;
|
575
621
|
int rc;
|
@@ -648,10 +694,13 @@ static VALUE read_query_response(VALUE vargs)
|
|
648
694
|
rb_ivar_set(result, id_ivar_affected_rows, ULL2NUM(ctx->conn.affected_rows));
|
649
695
|
|
650
696
|
return result;
|
697
|
+
} else {
|
698
|
+
rb_ivar_set(result, id_ivar_last_insert_id, Qnil);
|
699
|
+
rb_ivar_set(result, id_ivar_affected_rows, Qnil);
|
651
700
|
}
|
652
701
|
|
653
|
-
|
654
|
-
|
702
|
+
VALUE rb_column_info;
|
703
|
+
struct column_info *column_info = ALLOCV_N(struct column_info, rb_column_info, column_count);
|
655
704
|
|
656
705
|
for (uint64_t i = 0; i < column_count; i++) {
|
657
706
|
trilogy_column_t column;
|
@@ -688,8 +737,8 @@ static VALUE read_query_response(VALUE vargs)
|
|
688
737
|
column_info[i].decimals = column.decimals;
|
689
738
|
}
|
690
739
|
|
691
|
-
|
692
|
-
|
740
|
+
VALUE rb_row_values;
|
741
|
+
trilogy_value_t *row_values = ALLOCV_N(trilogy_value_t, rb_row_values, column_count);
|
693
742
|
|
694
743
|
while (1) {
|
695
744
|
int rc = trilogy_read_row(&ctx->conn, row_values);
|
@@ -732,9 +781,7 @@ static VALUE execute_read_query_response(struct trilogy_ctx *ctx)
|
|
732
781
|
|
733
782
|
struct read_query_response_state args = {
|
734
783
|
.cast_options = &cast_options,
|
735
|
-
.column_info = NULL,
|
736
784
|
.ctx = ctx,
|
737
|
-
.row_values = NULL,
|
738
785
|
.rc = TRILOGY_OK,
|
739
786
|
.msg = NULL,
|
740
787
|
};
|
@@ -742,9 +789,6 @@ static VALUE execute_read_query_response(struct trilogy_ctx *ctx)
|
|
742
789
|
int state = 0;
|
743
790
|
VALUE result = rb_protect(read_query_response, (VALUE)&args, &state);
|
744
791
|
|
745
|
-
xfree(args.column_info);
|
746
|
-
xfree(args.row_values);
|
747
|
-
|
748
792
|
// If we have seen an unexpected exception, jump to it so it gets raised.
|
749
793
|
if (state) {
|
750
794
|
trilogy_sock_shutdown(ctx->conn.socket);
|
@@ -787,6 +831,7 @@ static VALUE rb_trilogy_query(VALUE self, VALUE query)
|
|
787
831
|
struct trilogy_ctx *ctx = get_open_ctx(self);
|
788
832
|
|
789
833
|
StringValue(query);
|
834
|
+
query = rb_str_export_to_enc(query, rb_to_encoding(ctx->encoding));
|
790
835
|
|
791
836
|
int rc = trilogy_query_send(&ctx->conn, RSTRING_PTR(query), RSTRING_LEN(query));
|
792
837
|
|
@@ -902,6 +947,25 @@ static VALUE rb_trilogy_closed(VALUE self)
|
|
902
947
|
}
|
903
948
|
}
|
904
949
|
|
950
|
+
static VALUE rb_trilogy_discard(VALUE self)
|
951
|
+
{
|
952
|
+
struct trilogy_ctx *ctx = get_ctx(self);
|
953
|
+
|
954
|
+
if (ctx->conn.socket == NULL) {
|
955
|
+
return Qtrue;
|
956
|
+
}
|
957
|
+
|
958
|
+
int rc = trilogy_discard(&ctx->conn);
|
959
|
+
switch (rc) {
|
960
|
+
case TRILOGY_OK:
|
961
|
+
return Qtrue;
|
962
|
+
case TRILOGY_SYSERR:
|
963
|
+
trilogy_syserr_fail_str(errno, rb_str_new_cstr("Failed to discard connection"));
|
964
|
+
UNREACHABLE_RETURN(Qfalse);
|
965
|
+
}
|
966
|
+
return Qfalse;
|
967
|
+
}
|
968
|
+
|
905
969
|
static VALUE rb_trilogy_last_insert_id(VALUE self) { return ULL2NUM(get_open_ctx(self)->conn.last_insert_id); }
|
906
970
|
|
907
971
|
static VALUE rb_trilogy_affected_rows(VALUE self) { return ULL2NUM(get_open_ctx(self)->conn.affected_rows); }
|
@@ -961,18 +1025,19 @@ static VALUE rb_trilogy_server_status(VALUE self) { return LONG2FIX(get_open_ctx
|
|
961
1025
|
|
962
1026
|
static VALUE rb_trilogy_server_version(VALUE self) { return rb_str_new_cstr(get_open_ctx(self)->server_version); }
|
963
1027
|
|
964
|
-
void Init_cext()
|
1028
|
+
RUBY_FUNC_EXPORTED void Init_cext()
|
965
1029
|
{
|
966
1030
|
VALUE Trilogy = rb_const_get(rb_cObject, rb_intern("Trilogy"));
|
967
1031
|
rb_define_alloc_func(Trilogy, allocate_trilogy);
|
968
1032
|
|
969
|
-
|
1033
|
+
rb_define_private_method(Trilogy, "_initialize", rb_trilogy_initialize, 3);
|
970
1034
|
rb_define_method(Trilogy, "change_db", rb_trilogy_change_db, 1);
|
971
1035
|
rb_define_method(Trilogy, "query", rb_trilogy_query, 1);
|
972
1036
|
rb_define_method(Trilogy, "ping", rb_trilogy_ping, 0);
|
973
1037
|
rb_define_method(Trilogy, "escape", rb_trilogy_escape, 1);
|
974
1038
|
rb_define_method(Trilogy, "close", rb_trilogy_close, 0);
|
975
1039
|
rb_define_method(Trilogy, "closed?", rb_trilogy_closed, 0);
|
1040
|
+
rb_define_method(Trilogy, "discard!", rb_trilogy_discard, 0);
|
976
1041
|
rb_define_method(Trilogy, "last_insert_id", rb_trilogy_last_insert_id, 0);
|
977
1042
|
rb_define_method(Trilogy, "affected_rows", rb_trilogy_affected_rows, 0);
|
978
1043
|
rb_define_method(Trilogy, "warning_count", rb_trilogy_warning_count, 0);
|
@@ -987,6 +1052,7 @@ void Init_cext()
|
|
987
1052
|
rb_define_method(Trilogy, "server_version", rb_trilogy_server_version, 0);
|
988
1053
|
rb_define_method(Trilogy, "more_results_exist?", rb_trilogy_more_results_exist, 0);
|
989
1054
|
rb_define_method(Trilogy, "next_result", rb_trilogy_next_result, 0);
|
1055
|
+
rb_define_method(Trilogy, "set_server_option", rb_trilogy_set_server_option, 1);
|
990
1056
|
rb_define_const(Trilogy, "TLS_VERSION_10", INT2NUM(TRILOGY_TLS_VERSION_10));
|
991
1057
|
rb_define_const(Trilogy, "TLS_VERSION_11", INT2NUM(TRILOGY_TLS_VERSION_11));
|
992
1058
|
rb_define_const(Trilogy, "TLS_VERSION_12", INT2NUM(TRILOGY_TLS_VERSION_12));
|
@@ -1001,6 +1067,7 @@ void Init_cext()
|
|
1001
1067
|
rb_define_const(Trilogy, "QUERY_FLAGS_NONE", INT2NUM(0));
|
1002
1068
|
rb_define_const(Trilogy, "QUERY_FLAGS_CAST", INT2NUM(TRILOGY_FLAGS_CAST));
|
1003
1069
|
rb_define_const(Trilogy, "QUERY_FLAGS_CAST_BOOLEANS", INT2NUM(TRILOGY_FLAGS_CAST_BOOLEANS));
|
1070
|
+
rb_define_const(Trilogy, "QUERY_FLAGS_CAST_ALL_DECIMALS_TO_BIGDECIMALS", INT2NUM(TRILOGY_FLAGS_CAST_ALL_DECIMALS_TO_BIGDECIMALS));
|
1004
1071
|
rb_define_const(Trilogy, "QUERY_FLAGS_LOCAL_TIMEZONE", INT2NUM(TRILOGY_FLAGS_LOCAL_TIMEZONE));
|
1005
1072
|
rb_define_const(Trilogy, "QUERY_FLAGS_FLATTEN_ROWS", INT2NUM(TRILOGY_FLAGS_FLATTEN_ROWS));
|
1006
1073
|
rb_define_const(Trilogy, "QUERY_FLAGS_DEFAULT", INT2NUM(TRILOGY_FLAGS_DEFAULT));
|
@@ -1017,6 +1084,12 @@ void Init_cext()
|
|
1017
1084
|
Trilogy_TimeoutError = rb_const_get(Trilogy, rb_intern("TimeoutError"));
|
1018
1085
|
rb_global_variable(&Trilogy_TimeoutError);
|
1019
1086
|
|
1087
|
+
Trilogy_ConnectionRefusedError = rb_const_get(Trilogy, rb_intern("ConnectionRefusedError"));
|
1088
|
+
rb_global_variable(&Trilogy_ConnectionRefusedError);
|
1089
|
+
|
1090
|
+
Trilogy_ConnectionResetError = rb_const_get(Trilogy, rb_intern("ConnectionResetError"));
|
1091
|
+
rb_global_variable(&Trilogy_ConnectionResetError);
|
1092
|
+
|
1020
1093
|
Trilogy_BaseConnectionError = rb_const_get(Trilogy, rb_intern("BaseConnectionError"));
|
1021
1094
|
rb_global_variable(&Trilogy_BaseConnectionError);
|
1022
1095
|
|
@@ -1026,12 +1099,12 @@ void Init_cext()
|
|
1026
1099
|
Trilogy_Result = rb_const_get(Trilogy, rb_intern("Result"));
|
1027
1100
|
rb_global_variable(&Trilogy_Result);
|
1028
1101
|
|
1102
|
+
Trilogy_SyscallError = rb_const_get(Trilogy, rb_intern("SyscallError"));
|
1103
|
+
rb_global_variable(&Trilogy_SyscallError);
|
1104
|
+
|
1029
1105
|
Trilogy_CastError = rb_const_get(Trilogy, rb_intern("CastError"));
|
1030
1106
|
rb_global_variable(&Trilogy_CastError);
|
1031
1107
|
|
1032
|
-
rb_define_attr(Trilogy_Result, "affected_rows", 1, 0);
|
1033
|
-
rb_define_attr(Trilogy_Result, "last_insert_id", 1, 0);
|
1034
|
-
|
1035
1108
|
id_socket = rb_intern("socket");
|
1036
1109
|
id_host = rb_intern("host");
|
1037
1110
|
id_port = rb_intern("port");
|
@@ -1058,7 +1131,9 @@ void Init_cext()
|
|
1058
1131
|
id_tls_min_version = rb_intern("tls_min_version");
|
1059
1132
|
id_tls_max_version = rb_intern("tls_max_version");
|
1060
1133
|
id_multi_statement = rb_intern("multi_statement");
|
1134
|
+
id_multi_result = rb_intern("multi_result");
|
1061
1135
|
id_from_code = rb_intern("from_code");
|
1136
|
+
id_from_errno = rb_intern("from_errno");
|
1062
1137
|
id_ivar_affected_rows = rb_intern("@affected_rows");
|
1063
1138
|
id_ivar_fields = rb_intern("@fields");
|
1064
1139
|
id_ivar_last_insert_id = rb_intern("@last_insert_id");
|
@@ -1072,4 +1147,14 @@ void Init_cext()
|
|
1072
1147
|
#define XX(name, code) rb_const_set(Trilogy, rb_intern((char *)#name + strlen("TRILOGY_")), LONG2NUM(name));
|
1073
1148
|
TRILOGY_SERVER_STATUS(XX)
|
1074
1149
|
#undef XX
|
1150
|
+
|
1151
|
+
// set_server_option options
|
1152
|
+
#define XX(name, code) rb_const_set(Trilogy, rb_intern((char *)#name + strlen("TRILOGY_")), LONG2NUM(name));
|
1153
|
+
TRILOGY_SET_SERVER_OPTION(XX)
|
1154
|
+
#undef XX
|
1155
|
+
|
1156
|
+
// charsets
|
1157
|
+
#define XX(name, code) rb_const_set(Trilogy, rb_intern((char *)#name + strlen("TRILOGY_")), LONG2NUM(name));
|
1158
|
+
TRILOGY_CHARSETS(XX)
|
1159
|
+
#undef XX
|
1075
1160
|
}
|
data/ext/trilogy-ruby/extconf.rb
CHANGED
@@ -6,7 +6,7 @@ File.binwrite("trilogy.c",
|
|
6
6
|
Dir["#{__dir__}/src/**/*.c"].map { |src| File.binread(src) }.join)
|
7
7
|
|
8
8
|
$objs = %w[trilogy.o cast.o cext.o]
|
9
|
-
$CFLAGS << " -I #{__dir__}/inc -std=gnu99"
|
9
|
+
$CFLAGS << " -I #{__dir__}/inc -std=gnu99 -fvisibility=hidden"
|
10
10
|
|
11
11
|
dir_config("openssl")
|
12
12
|
|
@@ -50,7 +50,8 @@ int trilogy_connect_sock(trilogy_conn_t *conn, trilogy_sock_t *sock);
|
|
50
50
|
/* trilogy_change_db - Change the default database for a connection.
|
51
51
|
*
|
52
52
|
* conn - A connected trilogy_conn_t pointer. Using a disconnected
|
53
|
-
*
|
53
|
+
* trilogy_conn_t is undefined.
|
54
|
+
* name - Name of the database to set as default.
|
54
55
|
* name_len - Length of the database name string in bytes.
|
55
56
|
*
|
56
57
|
* Return values
|
@@ -63,6 +64,22 @@ int trilogy_connect_sock(trilogy_conn_t *conn, trilogy_sock_t *sock);
|
|
63
64
|
*/
|
64
65
|
int trilogy_change_db(trilogy_conn_t *conn, const char *name, size_t name_len);
|
65
66
|
|
67
|
+
/* trilogy_set_option - Set server options for the connection.
|
68
|
+
*
|
69
|
+
* conn - A connected trilogy_conn_t pointer. Using a disconnected
|
70
|
+
* trilogy_conn_t is undefined.
|
71
|
+
* option - The server option to set. See: TRILOGY_SET_SERVER_OPTION_TYPE_t;
|
72
|
+
*
|
73
|
+
* Return values
|
74
|
+
* TRILOGY_OK - The change db command completed successfully.
|
75
|
+
* TRILOGY_ERR - The server returned an error.
|
76
|
+
* TRILOGY_SYSERR - A system error occurred, check errno.
|
77
|
+
* TRILOGY_CLOSED_CONNECTION - The connection is closed.
|
78
|
+
* TRILOGY_PROTOCOL_VIOLATION - An error occurred while processing a network
|
79
|
+
* packet.
|
80
|
+
*/
|
81
|
+
int trilogy_set_option(trilogy_conn_t *conn, const uint16_t option);
|
82
|
+
|
66
83
|
/* trilogy_query - Send and execute a query.
|
67
84
|
*
|
68
85
|
* conn - A connected trilogy_conn_t pointer. Using a disconnected
|
@@ -299,6 +299,50 @@ int trilogy_change_db_send(trilogy_conn_t *conn, const char *name, size_t name_l
|
|
299
299
|
*/
|
300
300
|
int trilogy_change_db_recv(trilogy_conn_t *conn);
|
301
301
|
|
302
|
+
/* trilogy_set_option_send - Send a set option command to the server. This
|
303
|
+
* will change server capabilities based on the option selected.
|
304
|
+
*
|
305
|
+
* This should only be called while the connection is ready for commands.
|
306
|
+
*
|
307
|
+
* conn - A connected trilogy_conn_t pointer. Using a disconnected
|
308
|
+
* trilogy_conn_t is undefined.
|
309
|
+
* option - The server option to send.
|
310
|
+
*
|
311
|
+
* Return values:
|
312
|
+
* TRILOGY_OK - The change database command was successfully sent to the
|
313
|
+
* server.
|
314
|
+
* TRILOGY_AGAIN - The socket wasn't ready for writing. The caller should wait
|
315
|
+
* for writeability using `conn->sock`. Then call
|
316
|
+
* trilogy_flush_writes.
|
317
|
+
* TRILOGY_SYSERR - A system error occurred, check errno.
|
318
|
+
*/
|
319
|
+
int trilogy_set_option_send(trilogy_conn_t *conn, const uint16_t option);
|
320
|
+
|
321
|
+
/* trilogy_set_option_recv - Read the set option command response from the
|
322
|
+
* server.
|
323
|
+
*
|
324
|
+
* This should be called after all data written by trilogy_set_option_send is
|
325
|
+
* flushed to the network. Calling this at any other time during the connection
|
326
|
+
* lifecycle is undefined.
|
327
|
+
*
|
328
|
+
* conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
|
329
|
+
* undefined.
|
330
|
+
*
|
331
|
+
* Return values:
|
332
|
+
* TRILOGY_OK - The set option command was successfully
|
333
|
+
* sent to the server.
|
334
|
+
* TRILOGY_AGAIN - The socket wasn't ready for reading. The
|
335
|
+
* caller should wait for readability using
|
336
|
+
* `conn->sock`. Then call this function until
|
337
|
+
* it returns a different value.
|
338
|
+
* TRILOGY_UNEXPECTED_PACKET - The response packet wasn't what was expected.
|
339
|
+
* TRILOGY_PROTOCOL_VIOLATION - An error occurred while processing a network
|
340
|
+
* packet.
|
341
|
+
* TRILOGY_CLOSED_CONNECTION - The connection is closed.
|
342
|
+
* TRILOGY_SYSERR - A system error occurred, check errno.
|
343
|
+
*/
|
344
|
+
int trilogy_set_option_recv(trilogy_conn_t *conn);
|
345
|
+
|
302
346
|
/* trilogy_query_send - Send a query command to the server.
|
303
347
|
*
|
304
348
|
* This should only be called while the connection is ready for commands.
|
@@ -543,4 +587,18 @@ int trilogy_close_recv(trilogy_conn_t *conn);
|
|
543
587
|
*/
|
544
588
|
void trilogy_free(trilogy_conn_t *conn);
|
545
589
|
|
590
|
+
/* trilogy_free - Discard the connection and free any internal buffers.
|
591
|
+
*
|
592
|
+
* The server won't be notified that connection was closed. This is useful to
|
593
|
+
* silently close connections that were inherited after forking without disrupting
|
594
|
+
* the parent's process connections.
|
595
|
+
*
|
596
|
+
* conn - A pre-initialized trilogy_conn_t pointer.
|
597
|
+
*
|
598
|
+
* Return values:
|
599
|
+
* TRILOGY_OK - The connection was successfuly discarded and freed.
|
600
|
+
* TRILOGY_SYSERR - A system error occurred, check errno. The connection wasn't freed.
|
601
|
+
*/
|
602
|
+
int trilogy_discard(trilogy_conn_t *conn);
|
603
|
+
|
546
604
|
#endif
|
@@ -90,16 +90,14 @@
|
|
90
90
|
* From client: the client is interactive. \
|
91
91
|
*/ \
|
92
92
|
XX(TRILOGY_CAPABILITIES_INTERACTIVE, 0x00000400) \
|
93
|
-
/*
|
94
|
-
* \
|
95
|
-
* From server: the server supports ssl. \
|
93
|
+
/* From server: the server supports ssl. \
|
96
94
|
* \
|
97
95
|
* From client: tells the server it should switch to an ssl connection. \
|
98
96
|
*/ \
|
99
97
|
XX(TRILOGY_CAPABILITIES_SSL, 0x00000800) \
|
100
|
-
/*
|
98
|
+
/* From server: the server supports transactions and is capable of reporting transaction status. \
|
101
99
|
* \
|
102
|
-
*
|
100
|
+
* From client: the client is aware of servers that support transactions. \
|
103
101
|
*/ \
|
104
102
|
XX(TRILOGY_CAPABILITIES_TRANSACTIONS, 0x00002000) \
|
105
103
|
/* Not used. \
|
@@ -112,9 +110,7 @@
|
|
112
110
|
* scheme. This will always be set. \
|
113
111
|
*/ \
|
114
112
|
XX(TRILOGY_CAPABILITIES_SECURE_CONNECTION, 0x00008000) \
|
115
|
-
/*
|
116
|
-
* \
|
117
|
-
* From server: the server can handle multiple statements per \
|
113
|
+
/* From server: the server can handle multiple statements per \
|
118
114
|
* query/prepared statement. \
|
119
115
|
* \
|
120
116
|
* From client: tells the server it may send multiple statements per \
|
@@ -128,9 +124,7 @@
|
|
128
124
|
* sets from a query. \
|
129
125
|
*/ \
|
130
126
|
XX(TRILOGY_CAPABILITIES_MULTI_RESULTS, 0x00020000) \
|
131
|
-
/*
|
132
|
-
* \
|
133
|
-
* From server: the server is capable of sending multiple result sets from \
|
127
|
+
/* From server: the server is capable of sending multiple result sets from \
|
134
128
|
* a prepared statement. \
|
135
129
|
* \
|
136
130
|
* From client: tells the server it's capable of handling multiple result \
|
@@ -191,7 +185,8 @@ typedef enum {
|
|
191
185
|
/* A convenience bitmask with common client capabilities set. */
|
192
186
|
TRILOGY_CAPABILITIES_CLIENT = (TRILOGY_CAPABILITIES_PROTOCOL_41 | TRILOGY_CAPABILITIES_SECURE_CONNECTION |
|
193
187
|
TRILOGY_CAPABILITIES_DEPRECATE_EOF | TRILOGY_CAPABILITIES_SESSION_TRACK |
|
194
|
-
TRILOGY_CAPABILITIES_PLUGIN_AUTH | TRILOGY_CAPABILITIES_TRANSACTIONS
|
188
|
+
TRILOGY_CAPABILITIES_PLUGIN_AUTH | TRILOGY_CAPABILITIES_TRANSACTIONS |
|
189
|
+
TRILOGY_CAPABILITIES_MULTI_RESULTS)
|
195
190
|
} TRILOGY_CAPABILITIES_t;
|
196
191
|
|
197
192
|
#define TRILOGY_SERVER_STATUS(XX) \
|
@@ -397,22 +392,34 @@ typedef enum {
|
|
397
392
|
#undef XX
|
398
393
|
} TRILOGY_SESSION_TRACK_TYPE_t;
|
399
394
|
|
395
|
+
#define TRILOGY_SET_SERVER_OPTION(XX) \
|
396
|
+
XX(TRILOGY_SET_SERVER_MULTI_STATEMENTS_ON, 0x00) \
|
397
|
+
XX(TRILOGY_SET_SERVER_MULTI_STATEMENTS_OFF, 0x01) \
|
398
|
+
|
399
|
+
typedef enum {
|
400
|
+
#define XX(name, code) name = code,
|
401
|
+
TRILOGY_SET_SERVER_OPTION(XX)
|
402
|
+
#undef XX
|
403
|
+
} TRILOGY_SET_SERVER_OPTION_TYPE_t;
|
404
|
+
|
400
405
|
/* trilogy_build_auth_packet - Build a handshake response (or authentication)
|
401
406
|
* packet.
|
402
407
|
*
|
403
408
|
* This should be sent in response to the initial handshake packet the server
|
404
409
|
* sends upon connection.
|
405
410
|
*
|
406
|
-
* builder
|
407
|
-
* user
|
408
|
-
* pass
|
409
|
-
* pass_len
|
410
|
-
*
|
411
|
-
*
|
412
|
-
*
|
413
|
-
*
|
414
|
-
*
|
415
|
-
*
|
411
|
+
* builder - A pointer to a pre-initialized trilogy_builder_t.
|
412
|
+
* user - The username to use for authentication. Must be a C-string.
|
413
|
+
* pass - The password to use for authentication. Optional, and can be NULL.
|
414
|
+
* pass_len - The length of password in bytes.
|
415
|
+
* database - The initial database to connect to. Optional, and can be NULL.
|
416
|
+
* client_encoding - The charset to use for the connection.
|
417
|
+
* auth_plugin - Plugin authentication mechanism that the server requested.
|
418
|
+
* scramble - The scramble value the server sent in the initial handshake.
|
419
|
+
* flags - Bitmask of TRILOGY_CAPABILITIES_t flags.
|
420
|
+
* The TRILOGY_CAPABILITIES_PROTOCOL_41 and
|
421
|
+
* TRILOGY_CAPABILITIES_SECURE_CONNECTION flags will always be set
|
422
|
+
* internally.
|
416
423
|
*
|
417
424
|
* Return values:
|
418
425
|
* TRILOGY_OK - The packet was successfully built and written to the
|
@@ -420,8 +427,8 @@ typedef enum {
|
|
420
427
|
* TRILOGY_SYSERR - A system error occurred, check errno.
|
421
428
|
*/
|
422
429
|
int trilogy_build_auth_packet(trilogy_builder_t *builder, const char *user, const char *pass, size_t pass_len,
|
423
|
-
const char *database,
|
424
|
-
TRILOGY_CAPABILITIES_t flags);
|
430
|
+
const char *database, TRILOGY_CHARSET_t client_encoding, const char *auth_plugin,
|
431
|
+
const char *scramble, TRILOGY_CAPABILITIES_t flags);
|
425
432
|
|
426
433
|
/* trilogy_build_auth_switch_response_packet - Build a response for when
|
427
434
|
* authentication switching it requested.
|
@@ -447,7 +454,7 @@ int trilogy_build_auth_switch_response_packet(trilogy_builder_t *builder, const
|
|
447
454
|
* command will change the default database for the connection.
|
448
455
|
*
|
449
456
|
* builder - A pointer to a pre-initialized trilogy_builder_t.
|
450
|
-
* name - The name of the
|
457
|
+
* name - The name of the database to set as the default.
|
451
458
|
* name_len - The length of name in bytes.
|
452
459
|
*
|
453
460
|
* Return values:
|
@@ -457,6 +464,20 @@ int trilogy_build_auth_switch_response_packet(trilogy_builder_t *builder, const
|
|
457
464
|
*/
|
458
465
|
int trilogy_build_change_db_packet(trilogy_builder_t *builder, const char *name, size_t name_len);
|
459
466
|
|
467
|
+
/* trilogy_build_set_option_packet - Build a set option command packet. This
|
468
|
+
* command will enable/disable server capabilities for the connection. Options
|
469
|
+
* must be one of `enum_mysql_set_option`.
|
470
|
+
*
|
471
|
+
* builder - A pointer to a pre-initialized trilogy_builder_t.
|
472
|
+
* option - An integer corresponding to the operation to perform.
|
473
|
+
*
|
474
|
+
* Return values:
|
475
|
+
* TRILOGY_OK - The packet was successfully built and written to the
|
476
|
+
* builder's internal buffer.
|
477
|
+
* TRILOGY_SYSERR - A system error occurred, check errno.
|
478
|
+
*/
|
479
|
+
int trilogy_build_set_option_packet(trilogy_builder_t *builder, const uint16_t option);
|
480
|
+
|
460
481
|
/* trilogy_build_ping_packet - Build a ping command packet.
|
461
482
|
*
|
462
483
|
* builder - A pointer to a pre-initialized trilogy_builder_t.
|
@@ -497,19 +518,21 @@ int trilogy_build_quit_packet(trilogy_builder_t *builder);
|
|
497
518
|
* sends upon connection, where an auth packet would normally be sent. A regular
|
498
519
|
* auth packet is to be sent after the SSL handshake completes.
|
499
520
|
*
|
500
|
-
* builder
|
501
|
-
* flags
|
502
|
-
*
|
503
|
-
*
|
504
|
-
*
|
505
|
-
*
|
521
|
+
* builder - A pointer to a pre-initialized trilogy_builder_t.
|
522
|
+
* flags - Bitmask of TRILOGY_CAPABILITIES_t flags.
|
523
|
+
* The TRILOGY_CAPABILITIES_PROTOCOL_41 and
|
524
|
+
* TRILOGY_CAPABILITIES_SECURE_CONNECTION flags will always be set
|
525
|
+
* internally.
|
526
|
+
* The TRILOGY_CAPABILITIES_SSL flag will also be set.
|
527
|
+
* client_encoding - The charset to use for the connection.
|
506
528
|
*
|
507
529
|
* Return values:
|
508
530
|
* TRILOGY_OK - The packet was successfully built and written to the
|
509
531
|
* builder's internal buffer.
|
510
532
|
* TRILOGY_SYSERR - A system error occurred, check errno.
|
511
533
|
*/
|
512
|
-
int trilogy_build_ssl_request_packet(trilogy_builder_t *builder, TRILOGY_CAPABILITIES_t flags
|
534
|
+
int trilogy_build_ssl_request_packet(trilogy_builder_t *builder, TRILOGY_CAPABILITIES_t flags,
|
535
|
+
TRILOGY_CHARSET_t client_encoding);
|
513
536
|
|
514
537
|
#define TRILOGY_SERVER_VERSION_SIZE 32
|
515
538
|
|
@@ -41,6 +41,7 @@ typedef struct {
|
|
41
41
|
char *username;
|
42
42
|
char *password;
|
43
43
|
size_t password_len;
|
44
|
+
uint8_t encoding;
|
44
45
|
|
45
46
|
trilogy_ssl_mode_t ssl_mode;
|
46
47
|
trilogy_tls_version_t tls_min_version;
|
@@ -107,5 +108,6 @@ static inline int trilogy_sock_fd(trilogy_sock_t *sock) { return sock->fd_cb(soc
|
|
107
108
|
trilogy_sock_t *trilogy_sock_new(const trilogy_sockopt_t *opts);
|
108
109
|
int trilogy_sock_resolve(trilogy_sock_t *raw);
|
109
110
|
int trilogy_sock_upgrade_ssl(trilogy_sock_t *raw);
|
111
|
+
int trilogy_sock_discard(trilogy_sock_t *sock);
|
110
112
|
|
111
113
|
#endif
|
@@ -139,6 +139,29 @@ int trilogy_change_db(trilogy_conn_t *conn, const char *name, size_t name_len)
|
|
139
139
|
}
|
140
140
|
}
|
141
141
|
|
142
|
+
int trilogy_set_option(trilogy_conn_t *conn, const uint16_t option)
|
143
|
+
{
|
144
|
+
int rc = trilogy_set_option_send(conn, option);
|
145
|
+
|
146
|
+
if (rc == TRILOGY_AGAIN) {
|
147
|
+
rc = flush_full(conn);
|
148
|
+
}
|
149
|
+
|
150
|
+
if (rc < 0) {
|
151
|
+
return rc;
|
152
|
+
}
|
153
|
+
|
154
|
+
while (1) {
|
155
|
+
rc = trilogy_set_option_recv(conn);
|
156
|
+
|
157
|
+
if (rc != TRILOGY_AGAIN) {
|
158
|
+
return rc;
|
159
|
+
}
|
160
|
+
|
161
|
+
CHECKED(trilogy_sock_wait_read(conn->socket));
|
162
|
+
}
|
163
|
+
}
|
164
|
+
|
142
165
|
int trilogy_ping(trilogy_conn_t *conn)
|
143
166
|
{
|
144
167
|
int rc = trilogy_ping_send(conn);
|
@@ -357,8 +357,9 @@ int trilogy_auth_send(trilogy_conn_t *conn, const trilogy_handshake_t *handshake
|
|
357
357
|
}
|
358
358
|
|
359
359
|
rc = trilogy_build_auth_packet(&builder, conn->socket->opts.username, conn->socket->opts.password,
|
360
|
-
conn->socket->opts.password_len, conn->socket->opts.database,
|
361
|
-
|
360
|
+
conn->socket->opts.password_len, conn->socket->opts.database,
|
361
|
+
conn->socket->opts.encoding, handshake->auth_plugin, handshake->scramble,
|
362
|
+
conn->socket->opts.flags);
|
362
363
|
|
363
364
|
if (rc < 0) {
|
364
365
|
return rc;
|
@@ -378,7 +379,7 @@ int trilogy_ssl_request_send(trilogy_conn_t *conn)
|
|
378
379
|
}
|
379
380
|
|
380
381
|
conn->socket->opts.flags |= TRILOGY_CAPABILITIES_SSL;
|
381
|
-
rc = trilogy_build_ssl_request_packet(&builder, conn->socket->opts.flags);
|
382
|
+
rc = trilogy_build_ssl_request_packet(&builder, conn->socket->opts.flags, conn->socket->opts.encoding);
|
382
383
|
|
383
384
|
if (rc < 0) {
|
384
385
|
return rc;
|
@@ -466,6 +467,44 @@ int trilogy_change_db_send(trilogy_conn_t *conn, const char *name, size_t name_l
|
|
466
467
|
|
467
468
|
int trilogy_change_db_recv(trilogy_conn_t *conn) { return read_generic_response(conn); }
|
468
469
|
|
470
|
+
int trilogy_set_option_send(trilogy_conn_t *conn, const uint16_t option)
|
471
|
+
{
|
472
|
+
trilogy_builder_t builder;
|
473
|
+
int err = begin_command_phase(&builder, conn, 0);
|
474
|
+
if (err < 0) {
|
475
|
+
return err;
|
476
|
+
}
|
477
|
+
|
478
|
+
err = trilogy_build_set_option_packet(&builder, option);
|
479
|
+
|
480
|
+
if (err < 0) {
|
481
|
+
return err;
|
482
|
+
}
|
483
|
+
|
484
|
+
return begin_write(conn);
|
485
|
+
}
|
486
|
+
|
487
|
+
int trilogy_set_option_recv(trilogy_conn_t *conn) {
|
488
|
+
int rc = read_packet(conn);
|
489
|
+
|
490
|
+
if (rc < 0) {
|
491
|
+
return rc;
|
492
|
+
}
|
493
|
+
|
494
|
+
switch (current_packet_type(conn)) {
|
495
|
+
case TRILOGY_PACKET_OK:
|
496
|
+
case TRILOGY_PACKET_EOF: // COM_SET_OPTION returns an EOF packet, but it should be treated as an OK packet.
|
497
|
+
return read_ok_packet(conn);
|
498
|
+
|
499
|
+
case TRILOGY_PACKET_ERR:
|
500
|
+
return read_err_packet(conn);
|
501
|
+
|
502
|
+
default:
|
503
|
+
return TRILOGY_UNEXPECTED_PACKET;
|
504
|
+
}
|
505
|
+
}
|
506
|
+
|
507
|
+
|
469
508
|
int trilogy_ping_send(trilogy_conn_t *conn)
|
470
509
|
{
|
471
510
|
trilogy_builder_t builder;
|
@@ -725,4 +764,13 @@ void trilogy_free(trilogy_conn_t *conn)
|
|
725
764
|
trilogy_buffer_free(&conn->packet_buffer);
|
726
765
|
}
|
727
766
|
|
767
|
+
int trilogy_discard(trilogy_conn_t *conn)
|
768
|
+
{
|
769
|
+
int rc = trilogy_sock_discard(conn->socket);
|
770
|
+
if (rc == TRILOGY_OK) {
|
771
|
+
trilogy_free(conn);
|
772
|
+
}
|
773
|
+
return rc;
|
774
|
+
}
|
775
|
+
|
728
776
|
#undef CHECKED
|
@@ -10,6 +10,7 @@
|
|
10
10
|
#define TRILOGY_CMD_CHANGE_DB 0x02
|
11
11
|
#define TRILOGY_CMD_QUERY 0x03
|
12
12
|
#define TRILOGY_CMD_PING 0x0e
|
13
|
+
#define TRILOGY_CMD_SET_OPTION 0x1b
|
13
14
|
|
14
15
|
#define SCRAMBLE_LEN 20
|
15
16
|
|
@@ -493,8 +494,8 @@ static void trilogy_pack_scramble_sha2_hash(const char *scramble, const char *pa
|
|
493
494
|
}
|
494
495
|
|
495
496
|
int trilogy_build_auth_packet(trilogy_builder_t *builder, const char *user, const char *pass, size_t pass_len,
|
496
|
-
const char *database,
|
497
|
-
TRILOGY_CAPABILITIES_t flags)
|
497
|
+
const char *database, TRILOGY_CHARSET_t client_encoding, const char *auth_plugin,
|
498
|
+
const char *scramble, TRILOGY_CAPABILITIES_t flags)
|
498
499
|
{
|
499
500
|
int rc = TRILOGY_OK;
|
500
501
|
|
@@ -506,8 +507,6 @@ int trilogy_build_auth_packet(trilogy_builder_t *builder, const char *user, cons
|
|
506
507
|
|
507
508
|
uint32_t max_packet_len = TRILOGY_MAX_PACKET_LEN;
|
508
509
|
|
509
|
-
uint8_t client_encoding = TRILOGY_CHARSET_UTF8_GENERAL_CI;
|
510
|
-
|
511
510
|
unsigned int auth_response_len = 0;
|
512
511
|
uint8_t auth_response[EVP_MAX_MD_SIZE];
|
513
512
|
|
@@ -646,12 +645,28 @@ fail:
|
|
646
645
|
return rc;
|
647
646
|
}
|
648
647
|
|
649
|
-
int
|
648
|
+
int trilogy_build_set_option_packet(trilogy_builder_t *builder, const uint16_t option)
|
649
|
+
{
|
650
|
+
int rc = TRILOGY_OK;
|
651
|
+
|
652
|
+
CHECKED(trilogy_builder_write_uint8(builder, TRILOGY_CMD_SET_OPTION));
|
653
|
+
CHECKED(trilogy_builder_write_uint16(builder, option));
|
654
|
+
|
655
|
+
trilogy_builder_finalize(builder);
|
656
|
+
|
657
|
+
return TRILOGY_OK;
|
658
|
+
|
659
|
+
fail:
|
660
|
+
return rc;
|
661
|
+
}
|
662
|
+
|
663
|
+
|
664
|
+
int trilogy_build_ssl_request_packet(trilogy_builder_t *builder, TRILOGY_CAPABILITIES_t flags,
|
665
|
+
TRILOGY_CHARSET_t client_encoding)
|
650
666
|
{
|
651
667
|
static const char zeroes[23] = {0};
|
652
668
|
|
653
669
|
const uint32_t max_packet_len = TRILOGY_MAX_PACKET_LEN;
|
654
|
-
const uint8_t client_encoding = TRILOGY_CHARSET_UTF8_GENERAL_CI;
|
655
670
|
const uint32_t capabilities = flags | TRILOGY_CAPABILITIES_CLIENT | TRILOGY_CAPABILITIES_SSL;
|
656
671
|
|
657
672
|
int rc = TRILOGY_OK;
|
@@ -621,3 +621,28 @@ fail:
|
|
621
621
|
sock->ssl = NULL;
|
622
622
|
return TRILOGY_OPENSSL_ERR;
|
623
623
|
}
|
624
|
+
|
625
|
+
int trilogy_sock_discard(trilogy_sock_t *_sock)
|
626
|
+
{
|
627
|
+
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
628
|
+
|
629
|
+
if (sock->fd < 0) {
|
630
|
+
return TRILOGY_OK;
|
631
|
+
}
|
632
|
+
|
633
|
+
int null_fd = open("/dev/null", O_RDWR | O_CLOEXEC);
|
634
|
+
if (null_fd < 0) {
|
635
|
+
return TRILOGY_SYSERR;
|
636
|
+
}
|
637
|
+
|
638
|
+
if (dup2(null_fd, sock->fd) < 0) {
|
639
|
+
close(null_fd);
|
640
|
+
return TRILOGY_SYSERR;
|
641
|
+
}
|
642
|
+
|
643
|
+
if (close(null_fd) < 0) {
|
644
|
+
return TRILOGY_SYSERR;
|
645
|
+
}
|
646
|
+
|
647
|
+
return TRILOGY_OK;
|
648
|
+
}
|
@@ -9,6 +9,7 @@
|
|
9
9
|
#define TRILOGY_FLAGS_CAST_BOOLEANS 2
|
10
10
|
#define TRILOGY_FLAGS_LOCAL_TIMEZONE 4
|
11
11
|
#define TRILOGY_FLAGS_FLATTEN_ROWS 8
|
12
|
+
#define TRILOGY_FLAGS_CAST_ALL_DECIMALS_TO_BIGDECIMALS 16
|
12
13
|
#define TRILOGY_FLAGS_DEFAULT (TRILOGY_FLAGS_CAST)
|
13
14
|
|
14
15
|
struct rb_trilogy_cast_options {
|
@@ -16,6 +17,7 @@ struct rb_trilogy_cast_options {
|
|
16
17
|
bool cast_booleans;
|
17
18
|
bool database_local_time;
|
18
19
|
bool flatten_rows;
|
20
|
+
bool cast_decimals_to_bigdecimals;
|
19
21
|
};
|
20
22
|
|
21
23
|
struct column_info {
|
data/lib/trilogy/version.rb
CHANGED
data/lib/trilogy.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "trilogy/version"
|
2
4
|
|
3
5
|
class Trilogy
|
4
6
|
# Trilogy::Error is the base error type. All errors raised by Trilogy
|
5
7
|
# should be descendants of Trilogy::Error
|
6
8
|
module Error
|
9
|
+
attr_reader :error_code
|
7
10
|
end
|
8
11
|
|
9
12
|
# Trilogy::ConnectionError is the base error type for all potentially transient
|
@@ -12,11 +15,30 @@ class Trilogy
|
|
12
15
|
include Error
|
13
16
|
end
|
14
17
|
|
18
|
+
# Trilogy may raise various syscall errors, which we treat as Trilogy::Errors.
|
19
|
+
class SyscallError
|
20
|
+
ERRORS = {}
|
21
|
+
|
22
|
+
Errno.constants
|
23
|
+
.map { |c| Errno.const_get(c) }.uniq
|
24
|
+
.select { |c| c.is_a?(Class) && c < SystemCallError }
|
25
|
+
.each do |c|
|
26
|
+
errno_name = c.to_s.split('::').last
|
27
|
+
ERRORS[c::Errno] = const_set(errno_name, Class.new(c) { include Trilogy::Error })
|
28
|
+
end
|
29
|
+
|
30
|
+
ERRORS.freeze
|
31
|
+
|
32
|
+
class << self
|
33
|
+
def from_errno(errno, message)
|
34
|
+
ERRORS[errno].new(message)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
15
39
|
class BaseError < StandardError
|
16
40
|
include Error
|
17
41
|
|
18
|
-
attr_reader :error_code
|
19
|
-
|
20
42
|
def initialize(error_message = nil, error_code = nil)
|
21
43
|
message = error_code ? "#{error_code}: #{error_message}" : error_message
|
22
44
|
super(message)
|
@@ -42,13 +64,14 @@ class Trilogy
|
|
42
64
|
|
43
65
|
class TimeoutError < Errno::ETIMEDOUT
|
44
66
|
include ConnectionError
|
67
|
+
end
|
45
68
|
|
46
|
-
|
69
|
+
class ConnectionRefusedError < Errno::ECONNREFUSED
|
70
|
+
include ConnectionError
|
71
|
+
end
|
47
72
|
|
48
|
-
|
49
|
-
|
50
|
-
@error_code = error_code
|
51
|
-
end
|
73
|
+
class ConnectionResetError < Errno::ECONNRESET
|
74
|
+
include ConnectionError
|
52
75
|
end
|
53
76
|
|
54
77
|
# DatabaseError was replaced by ProtocolError, but we'll keep it around as an
|
@@ -92,6 +115,60 @@ class Trilogy
|
|
92
115
|
include ConnectionError
|
93
116
|
end
|
94
117
|
|
118
|
+
MYSQL_TO_RUBY_ENCODINGS_MAP = {
|
119
|
+
"big5" => "Big5",
|
120
|
+
"dec8" => nil,
|
121
|
+
"cp850" => "CP850",
|
122
|
+
"hp8" => nil,
|
123
|
+
"koi8r" => "KOI8-R",
|
124
|
+
"latin1" => "ISO-8859-1",
|
125
|
+
"latin2" => "ISO-8859-2",
|
126
|
+
"swe7" => nil,
|
127
|
+
"ascii" => "US-ASCII",
|
128
|
+
"ujis" => "eucJP-ms",
|
129
|
+
"sjis" => "Shift_JIS",
|
130
|
+
"hebrew" => "ISO-8859-8",
|
131
|
+
"tis620" => "TIS-620",
|
132
|
+
"euckr" => "EUC-KR",
|
133
|
+
"koi8u" => "KOI8-R",
|
134
|
+
"gb2312" => "GB2312",
|
135
|
+
"greek" => "ISO-8859-7",
|
136
|
+
"cp1250" => "Windows-1250",
|
137
|
+
"gbk" => "GBK",
|
138
|
+
"latin5" => "ISO-8859-9",
|
139
|
+
"armscii8" => nil,
|
140
|
+
"utf8" => "UTF-8",
|
141
|
+
"ucs2" => "UTF-16BE",
|
142
|
+
"cp866" => "IBM866",
|
143
|
+
"keybcs2" => nil,
|
144
|
+
"macce" => "macCentEuro",
|
145
|
+
"macroman" => "macRoman",
|
146
|
+
"cp852" => "CP852",
|
147
|
+
"latin7" => "ISO-8859-13",
|
148
|
+
"utf8mb4" => "UTF-8",
|
149
|
+
"cp1251" => "Windows-1251",
|
150
|
+
"utf16" => "UTF-16",
|
151
|
+
"cp1256" => "Windows-1256",
|
152
|
+
"cp1257" => "Windows-1257",
|
153
|
+
"utf32" => "UTF-32",
|
154
|
+
"binary" => "ASCII-8BIT",
|
155
|
+
"geostd8" => nil,
|
156
|
+
"cp932" => "Windows-31J",
|
157
|
+
"eucjpms" => "eucJP-ms",
|
158
|
+
"utf16le" => "UTF-16LE",
|
159
|
+
"gb18030" => "GB18030",
|
160
|
+
}.freeze
|
161
|
+
|
162
|
+
def initialize(options = {})
|
163
|
+
mysql_encoding = options[:encoding] || "utf8mb4"
|
164
|
+
unless rb_encoding = MYSQL_TO_RUBY_ENCODINGS_MAP[mysql_encoding]
|
165
|
+
raise ArgumentError, "Unknown or unsupported encoding: #{mysql_encoding}"
|
166
|
+
end
|
167
|
+
encoding = Encoding.find(rb_encoding)
|
168
|
+
charset = charset_for_mysql_encoding(mysql_encoding)
|
169
|
+
_initialize(encoding, charset, **options)
|
170
|
+
end
|
171
|
+
|
95
172
|
def connection_options
|
96
173
|
@connection_options.dup.freeze
|
97
174
|
end
|
@@ -124,7 +201,7 @@ class Trilogy
|
|
124
201
|
end
|
125
202
|
|
126
203
|
class Result
|
127
|
-
attr_reader :fields, :rows, :query_time
|
204
|
+
attr_reader :fields, :rows, :query_time, :affected_rows, :last_insert_id
|
128
205
|
|
129
206
|
def count
|
130
207
|
rows.count
|
@@ -154,6 +231,49 @@ class Trilogy
|
|
154
231
|
|
155
232
|
include Enumerable
|
156
233
|
end
|
234
|
+
|
235
|
+
private
|
236
|
+
|
237
|
+
def charset_for_mysql_encoding(mysql_encoding)
|
238
|
+
@mysql_encodings_map ||= {
|
239
|
+
"big5" => CHARSET_BIG5_CHINESE_CI,
|
240
|
+
"cp850" => CHARSET_CP850_GENERAL_CI,
|
241
|
+
"koi8r" => CHARSET_KOI8R_GENERAL_CI,
|
242
|
+
"latin1" => CHARSET_LATIN1_GENERAL_CI,
|
243
|
+
"latin2" => CHARSET_LATIN2_GENERAL_CI,
|
244
|
+
"ascii" => CHARSET_ASCII_GENERAL_CI,
|
245
|
+
"ujis" => CHARSET_UJIS_JAPANESE_CI,
|
246
|
+
"sjis" => CHARSET_SJIS_JAPANESE_CI,
|
247
|
+
"hebrew" => CHARSET_HEBREW_GENERAL_CI,
|
248
|
+
"tis620" => CHARSET_TIS620_THAI_CI,
|
249
|
+
"euckr" => CHARSET_EUCKR_KOREAN_CI,
|
250
|
+
"koi8u" => CHARSET_KOI8U_GENERAL_CI,
|
251
|
+
"gb2312" => CHARSET_GB2312_CHINESE_CI,
|
252
|
+
"greek" => CHARSET_GREEK_GENERAL_CI,
|
253
|
+
"cp1250" => CHARSET_CP1250_GENERAL_CI,
|
254
|
+
"gbk" => CHARSET_GBK_CHINESE_CI,
|
255
|
+
"latin5" => CHARSET_LATIN5_TURKISH_CI,
|
256
|
+
"utf8" => CHARSET_UTF8_GENERAL_CI,
|
257
|
+
"ucs2" => CHARSET_UCS2_GENERAL_CI,
|
258
|
+
"cp866" => CHARSET_CP866_GENERAL_CI,
|
259
|
+
"cp932" => CHARSET_CP932_JAPANESE_CI,
|
260
|
+
"eucjpms" => CHARSET_EUCJPMS_JAPANESE_CI,
|
261
|
+
"utf16le" => CHARSET_UTF16_GENERAL_CI,
|
262
|
+
"gb18030" => CHARSET_GB18030_CHINESE_CI,
|
263
|
+
"macce" => CHARSET_MACCE_GENERAL_CI,
|
264
|
+
"macroman" => CHARSET_MACROMAN_GENERAL_CI,
|
265
|
+
"cp852" => CHARSET_CP852_GENERAL_CI,
|
266
|
+
"latin7" => CHARSET_LATIN7_GENERAL_CI,
|
267
|
+
"utf8mb4" => CHARSET_UTF8MB4_GENERAL_CI,
|
268
|
+
"cp1251" => CHARSET_CP1251_GENERAL_CI,
|
269
|
+
"utf16" => CHARSET_UTF16_GENERAL_CI,
|
270
|
+
"cp1256" => CHARSET_CP1256_GENERAL_CI,
|
271
|
+
"cp1257" => CHARSET_CP1257_GENERAL_CI,
|
272
|
+
"utf32" => CHARSET_UTF32_GENERAL_CI,
|
273
|
+
"binary" => CHARSET_BINARY,
|
274
|
+
}.freeze
|
275
|
+
@mysql_encodings_map[mysql_encoding]
|
276
|
+
end
|
157
277
|
end
|
158
278
|
|
159
279
|
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.4.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: 2023-
|
11
|
+
date: 2023-04-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|