trilogy 2.3.0 → 2.4.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/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
|