trilogy 2.10.0 → 2.11.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/Rakefile +16 -1
- data/ext/trilogy-ruby/cext.c +77 -52
- data/ext/trilogy-ruby/extconf.rb +4 -2
- data/ext/trilogy-ruby/inc/trilogy/client.h +2 -0
- data/ext/trilogy-ruby/inc/trilogy/socket.h +2 -0
- data/ext/trilogy-ruby/src/client.c +350 -60
- data/ext/trilogy-ruby/src/packet_parser.c +1 -1
- data/ext/trilogy-ruby/src/socket.c +6 -0
- data/lib/trilogy/error.rb +7 -0
- data/lib/trilogy/version.rb +1 -1
- data/lib/trilogy.rb +76 -1
- data/trilogy.gemspec +0 -3
- metadata +1 -29
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0625a7ff415d68c8f5e421188e66fe7c41ebadd2afc39a7d6b89c6318ea9ef9d
|
|
4
|
+
data.tar.gz: c6b10922d195af6ec3b5140963749b59d4ef4a967728aeeb826377bc66992bc0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5da895e095a69094c7f1844d341ea2bf0c4461f7744f892a5a6f3aff1552761ebb8f099a5df151450c6c32e894e88cfebf72052c8190aef749be0d8d56ad0611
|
|
7
|
+
data.tar.gz: 60830ae194994fa09a975d3cea8146632300bfef62e98726c7cd6edfdb18a91bd3bce55a9fbf0d5b5d2fa0932d2a264333d87ccb57ae222e59a46bd3c5740fd7
|
data/Rakefile
CHANGED
|
@@ -19,10 +19,25 @@ Rake::TestTask.new do |t|
|
|
|
19
19
|
t.test_files = FileList['test/*_test.rb']
|
|
20
20
|
t.verbose = true
|
|
21
21
|
end
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
task :test => [:compile, "db:clean"]
|
|
23
24
|
|
|
24
25
|
task :default => :test
|
|
25
26
|
|
|
26
27
|
task :console => :compile do
|
|
27
28
|
sh "ruby -I lib -r trilogy -S irb"
|
|
28
29
|
end
|
|
30
|
+
|
|
31
|
+
namespace :db do
|
|
32
|
+
task :create do
|
|
33
|
+
mysql_command = "mysql -uroot -e"
|
|
34
|
+
%x( #{mysql_command} "create DATABASE IF NOT EXISTS test DEFAULT CHARACTER SET utf8mb4" )
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
task :drop do
|
|
38
|
+
mysql_command = "mysql -uroot -e"
|
|
39
|
+
%x( #{mysql_command} "drop DATABASE IF EXISTS test" )
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
task :clean => ["db:drop", "db:create"]
|
|
43
|
+
end
|
data/ext/trilogy-ruby/cext.c
CHANGED
|
@@ -124,7 +124,7 @@ static void buffer_checkout(trilogy_buffer_t *buffer, size_t initial_capacity)
|
|
|
124
124
|
buffer->buff = pool->entries[pool->len].buff;
|
|
125
125
|
buffer->cap = pool->entries[pool->len].cap;
|
|
126
126
|
} else {
|
|
127
|
-
buffer->buff =
|
|
127
|
+
buffer->buff = malloc(initial_capacity);
|
|
128
128
|
buffer->cap = initial_capacity;
|
|
129
129
|
}
|
|
130
130
|
}
|
|
@@ -134,7 +134,7 @@ static bool buffer_checkin(trilogy_buffer_t *buffer)
|
|
|
134
134
|
buffer_pool * pool = get_buffer_pool();
|
|
135
135
|
|
|
136
136
|
if (pool->len >= BUFFER_POOL_MAX_SIZE) {
|
|
137
|
-
|
|
137
|
+
free(buffer->buff);
|
|
138
138
|
buffer->buff = NULL;
|
|
139
139
|
buffer->cap = 0;
|
|
140
140
|
return false;
|
|
@@ -172,9 +172,9 @@ static ID id_socket, id_host, id_port, id_username, id_password, id_found_rows,
|
|
|
172
172
|
|
|
173
173
|
struct trilogy_ctx {
|
|
174
174
|
trilogy_conn_t conn;
|
|
175
|
-
|
|
175
|
+
rb_encoding *encoding;
|
|
176
176
|
unsigned int query_flags;
|
|
177
|
-
|
|
177
|
+
char server_version[TRILOGY_SERVER_VERSION_SIZE + 1];
|
|
178
178
|
};
|
|
179
179
|
|
|
180
180
|
static void rb_trilogy_acquire_buffer(struct trilogy_ctx *ctx)
|
|
@@ -191,12 +191,6 @@ static void rb_trilogy_release_buffer(struct trilogy_ctx *ctx)
|
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
static void mark_trilogy(void *ptr)
|
|
195
|
-
{
|
|
196
|
-
struct trilogy_ctx *ctx = ptr;
|
|
197
|
-
rb_gc_mark(ctx->encoding);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
194
|
static void free_trilogy(void *ptr)
|
|
201
195
|
{
|
|
202
196
|
struct trilogy_ctx *ctx = ptr;
|
|
@@ -218,7 +212,7 @@ static size_t trilogy_memsize(const void *ptr) {
|
|
|
218
212
|
static const rb_data_type_t trilogy_data_type = {
|
|
219
213
|
.wrap_struct_name = "trilogy",
|
|
220
214
|
.function = {
|
|
221
|
-
.dmark =
|
|
215
|
+
.dmark = NULL,
|
|
222
216
|
.dfree = free_trilogy,
|
|
223
217
|
.dsize = trilogy_memsize,
|
|
224
218
|
},
|
|
@@ -455,42 +449,29 @@ static int _cb_ruby_wait(trilogy_sock_t *sock, trilogy_wait_t wait)
|
|
|
455
449
|
return TRILOGY_OK;
|
|
456
450
|
}
|
|
457
451
|
|
|
458
|
-
struct
|
|
459
|
-
int rc;
|
|
460
|
-
trilogy_sock_t *sock;
|
|
461
|
-
};
|
|
462
|
-
|
|
463
|
-
static void *no_gvl_resolve(void *data)
|
|
452
|
+
static int try_connect(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake, const trilogy_sockopt_t *opts, int fd)
|
|
464
453
|
{
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
}
|
|
454
|
+
if (fd < 0) {
|
|
455
|
+
return TRILOGY_ERR;
|
|
456
|
+
}
|
|
469
457
|
|
|
470
|
-
static int try_connect(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake, const trilogy_sockopt_t *opts)
|
|
471
|
-
{
|
|
472
458
|
trilogy_sock_t *sock = trilogy_sock_new(opts);
|
|
473
459
|
if (sock == NULL) {
|
|
474
460
|
return TRILOGY_ERR;
|
|
475
461
|
}
|
|
476
462
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
// Do the DNS resolving with the GVL unlocked. At this point all
|
|
480
|
-
// configuration data is copied and available to the trilogy socket.
|
|
481
|
-
rb_thread_call_without_gvl(no_gvl_resolve, (void *)&args, RUBY_UBF_IO, NULL);
|
|
482
|
-
|
|
483
|
-
int rc = args.rc;
|
|
484
|
-
|
|
485
|
-
if (rc != TRILOGY_OK) {
|
|
486
|
-
trilogy_sock_close(sock);
|
|
487
|
-
return rc;
|
|
488
|
-
}
|
|
463
|
+
int rc;
|
|
489
464
|
|
|
490
465
|
/* replace the default wait callback with our GVL-aware callback so we can
|
|
491
466
|
escape the GVL on each wait operation without going through call_without_gvl */
|
|
492
467
|
sock->wait_cb = _cb_ruby_wait;
|
|
493
|
-
|
|
468
|
+
|
|
469
|
+
int newfd = dup(fd);
|
|
470
|
+
if (newfd < 0) {
|
|
471
|
+
return TRILOGY_ERR;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
rc = trilogy_connect_set_fd(&ctx->conn, sock, newfd);
|
|
494
475
|
if (rc < 0) {
|
|
495
476
|
trilogy_sock_close(sock);
|
|
496
477
|
return rc;
|
|
@@ -534,7 +515,12 @@ static void auth_switch(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake)
|
|
|
534
515
|
}
|
|
535
516
|
|
|
536
517
|
if (rc != TRILOGY_AGAIN) {
|
|
537
|
-
|
|
518
|
+
if (rc == TRILOGY_UNSUPPORTED) {
|
|
519
|
+
handle_trilogy_error(ctx, rc, "trilogy_auth_recv: caching_sha2_password requires either TCP with TLS or a unix socket");
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
|
|
523
|
+
}
|
|
538
524
|
}
|
|
539
525
|
|
|
540
526
|
rc = trilogy_sock_wait_read(ctx->conn.socket);
|
|
@@ -588,12 +574,7 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
|
|
|
588
574
|
}
|
|
589
575
|
|
|
590
576
|
if (rc != TRILOGY_AGAIN) {
|
|
591
|
-
|
|
592
|
-
handle_trilogy_error(ctx, rc, "trilogy_auth_recv: caching_sha2_password requires either TCP with TLS or a unix socket");
|
|
593
|
-
}
|
|
594
|
-
else {
|
|
595
|
-
handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
|
|
596
|
-
}
|
|
577
|
+
handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
|
|
597
578
|
}
|
|
598
579
|
|
|
599
580
|
rc = trilogy_sock_wait_read(ctx->conn.socket);
|
|
@@ -607,14 +588,24 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
|
|
|
607
588
|
}
|
|
608
589
|
}
|
|
609
590
|
|
|
610
|
-
|
|
591
|
+
#ifndef HAVE_RB_IO_DESCRIPTOR /* Ruby < 3.1 */
|
|
592
|
+
static int rb_io_descriptor(VALUE io)
|
|
593
|
+
{
|
|
594
|
+
rb_io_t *fptr;
|
|
595
|
+
GetOpenFile(io, fptr);
|
|
596
|
+
rb_io_check_closed(fptr);
|
|
597
|
+
return fptr->fd;
|
|
598
|
+
}
|
|
599
|
+
#endif
|
|
600
|
+
|
|
601
|
+
static VALUE rb_trilogy_connect(VALUE self, VALUE raw_socket, VALUE encoding, VALUE charset, VALUE opts)
|
|
611
602
|
{
|
|
612
603
|
struct trilogy_ctx *ctx = get_ctx(self);
|
|
613
604
|
trilogy_sockopt_t connopt = {0};
|
|
614
605
|
trilogy_handshake_t handshake;
|
|
615
606
|
VALUE val;
|
|
616
607
|
|
|
617
|
-
|
|
608
|
+
ctx->encoding = rb_to_encoding(encoding);
|
|
618
609
|
connopt.encoding = NUM2INT(charset);
|
|
619
610
|
|
|
620
611
|
Check_Type(opts, T_HASH);
|
|
@@ -762,9 +753,17 @@ static VALUE rb_trilogy_connect(VALUE self, VALUE encoding, VALUE charset, VALUE
|
|
|
762
753
|
connopt.tls_max_version = NUM2INT(val);
|
|
763
754
|
}
|
|
764
755
|
|
|
765
|
-
|
|
756
|
+
VALUE io = rb_io_get_io(raw_socket);
|
|
757
|
+
|
|
758
|
+
rb_io_t *fptr;
|
|
759
|
+
GetOpenFile(io, fptr);
|
|
760
|
+
rb_io_check_readable(fptr);
|
|
761
|
+
rb_io_check_writable(fptr);
|
|
766
762
|
|
|
767
|
-
int
|
|
763
|
+
int fd = rb_io_descriptor(io);
|
|
764
|
+
|
|
765
|
+
rb_trilogy_acquire_buffer(ctx);
|
|
766
|
+
int rc = try_connect(ctx, &handshake, &connopt, fd);
|
|
768
767
|
if (rc != TRILOGY_OK) {
|
|
769
768
|
if (connopt.path) {
|
|
770
769
|
handle_trilogy_error(ctx, rc, "trilogy_connect - unable to connect to %s", connopt.path);
|
|
@@ -983,10 +982,10 @@ static VALUE read_query_response(VALUE vargs)
|
|
|
983
982
|
}
|
|
984
983
|
}
|
|
985
984
|
|
|
986
|
-
#ifdef
|
|
987
|
-
VALUE column_name =
|
|
985
|
+
#ifdef HAVE_RB_ENC_INTERNED_STR
|
|
986
|
+
VALUE column_name = rb_enc_interned_str(column.name, column.name_len, ctx->encoding);
|
|
988
987
|
#else
|
|
989
|
-
VALUE column_name =
|
|
988
|
+
VALUE column_name = rb_enc_str_new(column.name, column.name_len, ctx->encoding);
|
|
990
989
|
OBJ_FREEZE(column_name);
|
|
991
990
|
#endif
|
|
992
991
|
|
|
@@ -1093,12 +1092,37 @@ static VALUE rb_trilogy_more_results_exist(VALUE self)
|
|
|
1093
1092
|
}
|
|
1094
1093
|
}
|
|
1095
1094
|
|
|
1095
|
+
static VALUE rb_trilogy_abandon_results(VALUE self)
|
|
1096
|
+
{
|
|
1097
|
+
struct trilogy_ctx *ctx = get_open_ctx(self);
|
|
1098
|
+
|
|
1099
|
+
long count = 0;
|
|
1100
|
+
while (ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS) {
|
|
1101
|
+
count++;
|
|
1102
|
+
int rc = trilogy_drain_results(&ctx->conn);
|
|
1103
|
+
while (rc == TRILOGY_AGAIN) {
|
|
1104
|
+
rc = trilogy_sock_wait_read(ctx->conn.socket);
|
|
1105
|
+
if (rc != TRILOGY_OK) {
|
|
1106
|
+
handle_trilogy_error(ctx, rc, "trilogy_sock_wait_read");
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
rc = trilogy_drain_results(&ctx->conn);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
if (rc != TRILOGY_OK) {
|
|
1113
|
+
handle_trilogy_error(ctx, rc, "trilogy_drain_results");
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
return LONG2NUM(count);
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1096
1120
|
static VALUE rb_trilogy_query(VALUE self, VALUE query)
|
|
1097
1121
|
{
|
|
1098
1122
|
struct trilogy_ctx *ctx = get_open_ctx(self);
|
|
1099
1123
|
|
|
1100
1124
|
StringValue(query);
|
|
1101
|
-
query = rb_str_export_to_enc(query,
|
|
1125
|
+
query = rb_str_export_to_enc(query, ctx->encoding);
|
|
1102
1126
|
|
|
1103
1127
|
rb_trilogy_acquire_buffer(ctx);
|
|
1104
1128
|
|
|
@@ -1333,7 +1357,7 @@ RUBY_FUNC_EXPORTED void Init_cext(void)
|
|
|
1333
1357
|
VALUE Trilogy = rb_const_get(rb_cObject, rb_intern("Trilogy"));
|
|
1334
1358
|
rb_define_alloc_func(Trilogy, allocate_trilogy);
|
|
1335
1359
|
|
|
1336
|
-
rb_define_private_method(Trilogy, "_connect", rb_trilogy_connect,
|
|
1360
|
+
rb_define_private_method(Trilogy, "_connect", rb_trilogy_connect, 4);
|
|
1337
1361
|
rb_define_method(Trilogy, "change_db", rb_trilogy_change_db, 1);
|
|
1338
1362
|
rb_define_alias(Trilogy, "select_db", "change_db");
|
|
1339
1363
|
rb_define_method(Trilogy, "query", rb_trilogy_query, 1);
|
|
@@ -1357,6 +1381,7 @@ RUBY_FUNC_EXPORTED void Init_cext(void)
|
|
|
1357
1381
|
rb_define_method(Trilogy, "server_version", rb_trilogy_server_version, 0);
|
|
1358
1382
|
rb_define_method(Trilogy, "more_results_exist?", rb_trilogy_more_results_exist, 0);
|
|
1359
1383
|
rb_define_method(Trilogy, "next_result", rb_trilogy_next_result, 0);
|
|
1384
|
+
rb_define_method(Trilogy, "abandon_results!", rb_trilogy_abandon_results, 0);
|
|
1360
1385
|
rb_define_method(Trilogy, "set_server_option", rb_trilogy_set_server_option, 1);
|
|
1361
1386
|
rb_define_const(Trilogy, "TLS_VERSION_10", INT2NUM(TRILOGY_TLS_VERSION_10));
|
|
1362
1387
|
rb_define_const(Trilogy, "TLS_VERSION_11", INT2NUM(TRILOGY_TLS_VERSION_11));
|
data/ext/trilogy-ruby/extconf.rb
CHANGED
|
@@ -12,10 +12,12 @@ File.binwrite("trilogy.c",
|
|
|
12
12
|
$objs = %w[trilogy.o cast.o cext.o]
|
|
13
13
|
append_cflags(["-I #{__dir__}/inc", "-std=gnu99", "-fvisibility=hidden"])
|
|
14
14
|
|
|
15
|
-
dir_config("openssl")
|
|
15
|
+
dir_config("openssl").any? || pkg_config("openssl")
|
|
16
16
|
|
|
17
17
|
have_library("crypto", "CRYPTO_malloc")
|
|
18
18
|
have_library("ssl", "SSL_new")
|
|
19
|
-
have_func("rb_interned_str", "ruby.h")
|
|
20
19
|
have_func("rb_ractor_local_storage_value_newkey", "ruby.h")
|
|
20
|
+
have_func("rb_enc_interned_str", "ruby.h")
|
|
21
|
+
have_func("rb_io_descriptor", "ruby.h") # Ruby 3.1+
|
|
22
|
+
|
|
21
23
|
create_makefile "trilogy/cext"
|
|
@@ -159,6 +159,8 @@ int trilogy_connect_send(trilogy_conn_t *conn, const trilogy_sockopt_t *opts);
|
|
|
159
159
|
*/
|
|
160
160
|
int trilogy_connect_send_socket(trilogy_conn_t *conn, trilogy_sock_t *sock);
|
|
161
161
|
|
|
162
|
+
int trilogy_connect_set_fd(trilogy_conn_t *conn, trilogy_sock_t *sock, int fd);
|
|
163
|
+
|
|
162
164
|
/* trilogy_connect_recv - Read the initial handshake from the server.
|
|
163
165
|
*
|
|
164
166
|
* This should be called after trilogy_connect_send returns TRILOGY_OK. Calling
|
|
@@ -88,6 +88,8 @@ typedef struct trilogy_sock_t {
|
|
|
88
88
|
|
|
89
89
|
static inline int trilogy_sock_connect(trilogy_sock_t *sock) { return sock->connect_cb(sock); }
|
|
90
90
|
|
|
91
|
+
void trilogy_sock_set_fd(trilogy_sock_t *sock, int fd);
|
|
92
|
+
|
|
91
93
|
static inline ssize_t trilogy_sock_read(trilogy_sock_t *sock, void *buf, size_t n)
|
|
92
94
|
{
|
|
93
95
|
return sock->read_cb(sock, buf, n);
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
#include <fcntl.h>
|
|
2
|
+
#include <limits.h>
|
|
3
|
+
#include <openssl/err.h>
|
|
4
|
+
#include <openssl/evp.h>
|
|
5
|
+
#include <openssl/pem.h>
|
|
6
|
+
#include <openssl/rsa.h>
|
|
7
|
+
#include <stdlib.h>
|
|
2
8
|
#include <string.h>
|
|
3
9
|
|
|
4
10
|
#include "trilogy/client.h"
|
|
@@ -118,6 +124,38 @@ static int begin_write(trilogy_conn_t *conn)
|
|
|
118
124
|
return trilogy_flush_writes(conn);
|
|
119
125
|
}
|
|
120
126
|
|
|
127
|
+
static int flush_current_packet(trilogy_conn_t *conn)
|
|
128
|
+
{
|
|
129
|
+
int rc = begin_write(conn);
|
|
130
|
+
|
|
131
|
+
while (rc == TRILOGY_AGAIN) {
|
|
132
|
+
rc = trilogy_sock_wait_write(conn->socket);
|
|
133
|
+
|
|
134
|
+
if (rc != TRILOGY_OK) {
|
|
135
|
+
return rc;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
rc = trilogy_flush_writes(conn);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return rc;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
static int read_packet_blocking(trilogy_conn_t *conn)
|
|
145
|
+
{
|
|
146
|
+
int rc;
|
|
147
|
+
|
|
148
|
+
while ((rc = read_packet(conn)) == TRILOGY_AGAIN) {
|
|
149
|
+
rc = trilogy_sock_wait_read(conn->socket);
|
|
150
|
+
|
|
151
|
+
if (rc != TRILOGY_OK) {
|
|
152
|
+
return rc;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return rc;
|
|
157
|
+
}
|
|
158
|
+
|
|
121
159
|
int trilogy_init_no_buffer(trilogy_conn_t *conn)
|
|
122
160
|
{
|
|
123
161
|
conn->affected_rows = 0;
|
|
@@ -279,7 +317,8 @@ static int read_auth_switch_packet(trilogy_conn_t *conn, trilogy_handshake_t *ha
|
|
|
279
317
|
return TRILOGY_AUTH_SWITCH;
|
|
280
318
|
}
|
|
281
319
|
|
|
282
|
-
static int handle_generic_response(trilogy_conn_t *conn)
|
|
320
|
+
static int handle_generic_response(trilogy_conn_t *conn)
|
|
321
|
+
{
|
|
283
322
|
switch (current_packet_type(conn)) {
|
|
284
323
|
case TRILOGY_PACKET_OK:
|
|
285
324
|
return read_ok_packet(conn);
|
|
@@ -330,6 +369,16 @@ int trilogy_connect_send_socket(trilogy_conn_t *conn, trilogy_sock_t *sock)
|
|
|
330
369
|
return TRILOGY_OK;
|
|
331
370
|
}
|
|
332
371
|
|
|
372
|
+
int trilogy_connect_set_fd(trilogy_conn_t *conn, trilogy_sock_t *sock, int fd)
|
|
373
|
+
{
|
|
374
|
+
trilogy_sock_set_fd(sock, fd);
|
|
375
|
+
|
|
376
|
+
conn->socket = sock;
|
|
377
|
+
conn->packet_parser.sequence_number = 0;
|
|
378
|
+
|
|
379
|
+
return TRILOGY_OK;
|
|
380
|
+
}
|
|
381
|
+
|
|
333
382
|
int trilogy_connect_recv(trilogy_conn_t *conn, trilogy_handshake_t *handshake_out)
|
|
334
383
|
{
|
|
335
384
|
int rc = read_packet(conn);
|
|
@@ -427,9 +476,294 @@ void trilogy_auth_clear_password(trilogy_conn_t *conn)
|
|
|
427
476
|
}
|
|
428
477
|
}
|
|
429
478
|
|
|
479
|
+
#define CACHING_SHA2_REQUEST_PUBLIC_KEY 2
|
|
480
|
+
#define CACHING_SHA2_SCRAMBLE_LEN 20
|
|
430
481
|
#define FAST_AUTH_OK 3
|
|
431
482
|
#define FAST_AUTH_FAIL 4
|
|
432
483
|
|
|
484
|
+
static int read_auth_result(trilogy_conn_t *conn)
|
|
485
|
+
{
|
|
486
|
+
int rc = read_packet_blocking(conn);
|
|
487
|
+
|
|
488
|
+
if (rc < 0) {
|
|
489
|
+
return rc;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
trilogy_auth_clear_password(conn);
|
|
493
|
+
return handle_generic_response(conn);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
static int send_cleartext_password(trilogy_conn_t *conn)
|
|
497
|
+
{
|
|
498
|
+
trilogy_builder_t builder;
|
|
499
|
+
|
|
500
|
+
int rc = begin_command_phase(&builder, conn, conn->packet_parser.sequence_number);
|
|
501
|
+
|
|
502
|
+
if (rc < 0) {
|
|
503
|
+
return rc;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (conn->socket->opts.password_len == 0) {
|
|
507
|
+
rc = trilogy_builder_write_uint8(&builder, 0);
|
|
508
|
+
|
|
509
|
+
if (rc < 0) {
|
|
510
|
+
return rc;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
trilogy_builder_finalize(&builder);
|
|
514
|
+
return flush_current_packet(conn);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
rc = trilogy_build_auth_clear_password(&builder, conn->socket->opts.password, conn->socket->opts.password_len);
|
|
518
|
+
|
|
519
|
+
if (rc < 0) {
|
|
520
|
+
return rc;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return flush_current_packet(conn);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
static int send_auth_buffer(trilogy_conn_t *conn, const void *buff, size_t buff_len)
|
|
527
|
+
{
|
|
528
|
+
trilogy_builder_t builder;
|
|
529
|
+
int rc = begin_command_phase(&builder, conn, conn->packet_parser.sequence_number);
|
|
530
|
+
|
|
531
|
+
if (rc < 0) {
|
|
532
|
+
return rc;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
rc = trilogy_builder_write_buffer(&builder, buff, buff_len);
|
|
536
|
+
if (rc < 0) {
|
|
537
|
+
return rc;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
trilogy_builder_finalize(&builder);
|
|
541
|
+
|
|
542
|
+
return flush_current_packet(conn);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
static int send_public_key_request(trilogy_conn_t *conn)
|
|
546
|
+
{
|
|
547
|
+
uint8_t request = CACHING_SHA2_REQUEST_PUBLIC_KEY;
|
|
548
|
+
|
|
549
|
+
return send_auth_buffer(conn, &request, sizeof(request));
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
static int encrypt_password_with_public_key(const uint8_t *scramble, size_t scramble_len, trilogy_conn_t *conn,
|
|
553
|
+
const uint8_t *key_data, size_t key_data_len, uint8_t **encrypted_out,
|
|
554
|
+
size_t *encrypted_len)
|
|
555
|
+
{
|
|
556
|
+
int rc = TRILOGY_OK;
|
|
557
|
+
uint8_t *ciphertext = NULL;
|
|
558
|
+
size_t ciphertext_len = 0;
|
|
559
|
+
|
|
560
|
+
if (key_data_len == 0 || key_data_len > INT_MAX) {
|
|
561
|
+
return TRILOGY_AUTH_PLUGIN_ERROR;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
size_t password_len = conn->socket->opts.password_len;
|
|
565
|
+
if (password_len == SIZE_MAX) {
|
|
566
|
+
return TRILOGY_MEM_ERROR;
|
|
567
|
+
}
|
|
568
|
+
size_t plaintext_len = password_len + 1;
|
|
569
|
+
uint8_t *plaintext = malloc(plaintext_len);
|
|
570
|
+
|
|
571
|
+
if (plaintext == NULL) {
|
|
572
|
+
return TRILOGY_MEM_ERROR;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (password_len > 0) {
|
|
576
|
+
memcpy(plaintext, conn->socket->opts.password, password_len);
|
|
577
|
+
}
|
|
578
|
+
plaintext[plaintext_len - 1] = '\0';
|
|
579
|
+
|
|
580
|
+
if (scramble_len > 0) {
|
|
581
|
+
for (size_t i = 0; i < plaintext_len; i++) {
|
|
582
|
+
plaintext[i] ^= scramble[i % scramble_len];
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
BIO *bio = BIO_new_mem_buf((void *)key_data, (int)key_data_len);
|
|
587
|
+
if (bio == NULL) {
|
|
588
|
+
free(plaintext);
|
|
589
|
+
return TRILOGY_OPENSSL_ERR;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
|
593
|
+
EVP_PKEY *public_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
|
|
594
|
+
#else
|
|
595
|
+
RSA *public_key = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);
|
|
596
|
+
#endif
|
|
597
|
+
|
|
598
|
+
BIO_free(bio);
|
|
599
|
+
|
|
600
|
+
if (public_key == NULL) {
|
|
601
|
+
ERR_clear_error();
|
|
602
|
+
memset(plaintext, 0, plaintext_len);
|
|
603
|
+
free(plaintext);
|
|
604
|
+
return TRILOGY_AUTH_PLUGIN_ERROR;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
|
608
|
+
int key_size = EVP_PKEY_get_size(public_key);
|
|
609
|
+
if (key_size <= 0) {
|
|
610
|
+
EVP_PKEY_free(public_key);
|
|
611
|
+
memset(plaintext, 0, plaintext_len);
|
|
612
|
+
free(plaintext);
|
|
613
|
+
return TRILOGY_AUTH_PLUGIN_ERROR;
|
|
614
|
+
}
|
|
615
|
+
ciphertext_len = (size_t)key_size;
|
|
616
|
+
#else
|
|
617
|
+
ciphertext_len = (size_t)RSA_size(public_key);
|
|
618
|
+
#endif
|
|
619
|
+
|
|
620
|
+
/*
|
|
621
|
+
When using RSA_PKCS1_OAEP_PADDING the password length must be less
|
|
622
|
+
than RSA_size(rsa) - 41.
|
|
623
|
+
*/
|
|
624
|
+
if (ciphertext_len == 0 || plaintext_len + 41 >= ciphertext_len) {
|
|
625
|
+
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
|
626
|
+
EVP_PKEY_free(public_key);
|
|
627
|
+
#else
|
|
628
|
+
RSA_free(public_key);
|
|
629
|
+
#endif
|
|
630
|
+
memset(plaintext, 0, plaintext_len);
|
|
631
|
+
free(plaintext);
|
|
632
|
+
return TRILOGY_AUTH_PLUGIN_ERROR;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
ciphertext = malloc(ciphertext_len);
|
|
636
|
+
|
|
637
|
+
if (ciphertext == NULL) {
|
|
638
|
+
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
|
639
|
+
EVP_PKEY_free(public_key);
|
|
640
|
+
#else
|
|
641
|
+
RSA_free(public_key);
|
|
642
|
+
#endif
|
|
643
|
+
memset(plaintext, 0, plaintext_len);
|
|
644
|
+
free(plaintext);
|
|
645
|
+
return TRILOGY_MEM_ERROR;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
|
649
|
+
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(public_key, NULL);
|
|
650
|
+
if (ctx == NULL || EVP_PKEY_encrypt_init(ctx) <= 0 ||
|
|
651
|
+
EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
|
|
652
|
+
rc = TRILOGY_OPENSSL_ERR;
|
|
653
|
+
} else {
|
|
654
|
+
size_t out_len = ciphertext_len;
|
|
655
|
+
|
|
656
|
+
if (EVP_PKEY_encrypt(ctx, ciphertext, &out_len, plaintext, plaintext_len) <= 0) {
|
|
657
|
+
rc = TRILOGY_OPENSSL_ERR;
|
|
658
|
+
} else {
|
|
659
|
+
*encrypted_len = out_len;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
if (ctx) {
|
|
664
|
+
EVP_PKEY_CTX_free(ctx);
|
|
665
|
+
}
|
|
666
|
+
EVP_PKEY_free(public_key);
|
|
667
|
+
#else
|
|
668
|
+
int out_len = RSA_public_encrypt((int)plaintext_len, plaintext, ciphertext, public_key, RSA_PKCS1_OAEP_PADDING);
|
|
669
|
+
RSA_free(public_key);
|
|
670
|
+
|
|
671
|
+
if (out_len < 0) {
|
|
672
|
+
rc = TRILOGY_OPENSSL_ERR;
|
|
673
|
+
} else {
|
|
674
|
+
*encrypted_len = (size_t)out_len;
|
|
675
|
+
}
|
|
676
|
+
#endif
|
|
677
|
+
|
|
678
|
+
memset(plaintext, 0, plaintext_len);
|
|
679
|
+
free(plaintext);
|
|
680
|
+
|
|
681
|
+
if (rc == TRILOGY_OK) {
|
|
682
|
+
*encrypted_out = ciphertext;
|
|
683
|
+
} else {
|
|
684
|
+
memset(ciphertext, 0, ciphertext_len);
|
|
685
|
+
free(ciphertext);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
return rc;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
static int handle_fast_auth_fail(trilogy_conn_t *conn, trilogy_handshake_t *handshake, const uint8_t *auth_data,
|
|
692
|
+
size_t auth_data_len)
|
|
693
|
+
{
|
|
694
|
+
int rc;
|
|
695
|
+
bool use_ssl = (conn->socket->opts.flags & TRILOGY_CAPABILITIES_SSL) != 0;
|
|
696
|
+
bool has_unix_socket = (conn->socket->opts.path != NULL);
|
|
697
|
+
|
|
698
|
+
// No password to send, so we can safely respond even without TLS.
|
|
699
|
+
if (conn->socket->opts.password_len == 0) {
|
|
700
|
+
rc = send_cleartext_password(conn);
|
|
701
|
+
if (rc < 0) {
|
|
702
|
+
return rc;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
return read_auth_result(conn);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
if (use_ssl || has_unix_socket) {
|
|
709
|
+
rc = send_cleartext_password(conn);
|
|
710
|
+
if (rc < 0) {
|
|
711
|
+
return rc;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
return read_auth_result(conn);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
const uint8_t *public_key_data = NULL;
|
|
718
|
+
size_t public_key_len = 0;
|
|
719
|
+
|
|
720
|
+
if (auth_data_len > 1) {
|
|
721
|
+
public_key_data = auth_data + 1;
|
|
722
|
+
public_key_len = auth_data_len - 1;
|
|
723
|
+
} else {
|
|
724
|
+
rc = send_public_key_request(conn);
|
|
725
|
+
if (rc < 0) {
|
|
726
|
+
return rc;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
rc = read_packet_blocking(conn);
|
|
730
|
+
if (rc < 0) {
|
|
731
|
+
return rc;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (current_packet_type(conn) == TRILOGY_PACKET_ERR) {
|
|
735
|
+
return read_err_packet(conn);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if (current_packet_type(conn) != TRILOGY_PACKET_AUTH_MORE_DATA || conn->packet_buffer.len < 2) {
|
|
739
|
+
return TRILOGY_PROTOCOL_VIOLATION;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
public_key_data = conn->packet_buffer.buff + 1;
|
|
743
|
+
public_key_len = conn->packet_buffer.len - 1;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
uint8_t *encrypted = NULL;
|
|
747
|
+
size_t encrypted_len = 0;
|
|
748
|
+
|
|
749
|
+
rc = encrypt_password_with_public_key((const uint8_t *)handshake->scramble, CACHING_SHA2_SCRAMBLE_LEN, conn,
|
|
750
|
+
public_key_data, public_key_len, &encrypted, &encrypted_len);
|
|
751
|
+
|
|
752
|
+
if (rc < 0) {
|
|
753
|
+
return rc;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
rc = send_auth_buffer(conn, encrypted, encrypted_len);
|
|
757
|
+
memset(encrypted, 0, encrypted_len);
|
|
758
|
+
free(encrypted);
|
|
759
|
+
|
|
760
|
+
if (rc < 0) {
|
|
761
|
+
return rc;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return read_auth_result(conn);
|
|
765
|
+
}
|
|
766
|
+
|
|
433
767
|
int trilogy_auth_recv(trilogy_conn_t *conn, trilogy_handshake_t *handshake)
|
|
434
768
|
{
|
|
435
769
|
int rc = read_packet(conn);
|
|
@@ -440,67 +774,24 @@ int trilogy_auth_recv(trilogy_conn_t *conn, trilogy_handshake_t *handshake)
|
|
|
440
774
|
|
|
441
775
|
switch (current_packet_type(conn)) {
|
|
442
776
|
case TRILOGY_PACKET_AUTH_MORE_DATA: {
|
|
443
|
-
|
|
444
|
-
|
|
777
|
+
const uint8_t *auth_data = conn->packet_buffer.buff + 1;
|
|
778
|
+
size_t auth_data_len = conn->packet_buffer.len - 1;
|
|
445
779
|
|
|
446
|
-
if (
|
|
447
|
-
return
|
|
780
|
+
if (auth_data_len < 1) {
|
|
781
|
+
return TRILOGY_PROTOCOL_VIOLATION;
|
|
448
782
|
}
|
|
449
783
|
|
|
450
|
-
uint8_t byte =
|
|
784
|
+
uint8_t byte = auth_data[0];
|
|
451
785
|
switch (byte) {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
case FAST_AUTH_FAIL:
|
|
455
|
-
{
|
|
456
|
-
trilogy_builder_t builder;
|
|
457
|
-
int err = begin_command_phase(&builder, conn, conn->packet_parser.sequence_number);
|
|
458
|
-
|
|
459
|
-
if (err < 0) {
|
|
460
|
-
return err;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
err = trilogy_build_auth_clear_password(&builder, conn->socket->opts.password, conn->socket->opts.password_len);
|
|
464
|
-
|
|
465
|
-
if (err < 0) {
|
|
466
|
-
return err;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
int rc = begin_write(conn);
|
|
470
|
-
|
|
471
|
-
while (rc == TRILOGY_AGAIN) {
|
|
472
|
-
rc = trilogy_sock_wait_write(conn->socket);
|
|
473
|
-
if (rc != TRILOGY_OK) {
|
|
474
|
-
return rc;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
rc = trilogy_flush_writes(conn);
|
|
478
|
-
}
|
|
479
|
-
if (rc != TRILOGY_OK) {
|
|
480
|
-
return rc;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
break;
|
|
484
|
-
}
|
|
485
|
-
default:
|
|
486
|
-
return TRILOGY_UNEXPECTED_PACKET;
|
|
487
|
-
}
|
|
488
|
-
while (1) {
|
|
489
|
-
rc = read_packet(conn);
|
|
786
|
+
case FAST_AUTH_OK:
|
|
787
|
+
return read_auth_result(conn);
|
|
490
788
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
}
|
|
494
|
-
else if (rc == TRILOGY_AGAIN) {
|
|
495
|
-
rc = trilogy_sock_wait_read(conn->socket);
|
|
496
|
-
}
|
|
789
|
+
case FAST_AUTH_FAIL:
|
|
790
|
+
return handle_fast_auth_fail(conn, handshake, auth_data, auth_data_len);
|
|
497
791
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
}
|
|
792
|
+
default:
|
|
793
|
+
return TRILOGY_UNEXPECTED_PACKET;
|
|
501
794
|
}
|
|
502
|
-
trilogy_auth_clear_password(conn);
|
|
503
|
-
return handle_generic_response(conn);
|
|
504
795
|
}
|
|
505
796
|
|
|
506
797
|
case TRILOGY_PACKET_EOF:
|
|
@@ -555,7 +846,8 @@ int trilogy_set_option_send(trilogy_conn_t *conn, const uint16_t option)
|
|
|
555
846
|
return begin_write(conn);
|
|
556
847
|
}
|
|
557
848
|
|
|
558
|
-
int trilogy_set_option_recv(trilogy_conn_t *conn)
|
|
849
|
+
int trilogy_set_option_recv(trilogy_conn_t *conn)
|
|
850
|
+
{
|
|
559
851
|
int rc = read_packet(conn);
|
|
560
852
|
|
|
561
853
|
if (rc < 0) {
|
|
@@ -575,7 +867,6 @@ int trilogy_set_option_recv(trilogy_conn_t *conn) {
|
|
|
575
867
|
}
|
|
576
868
|
}
|
|
577
869
|
|
|
578
|
-
|
|
579
870
|
int trilogy_ping_send(trilogy_conn_t *conn)
|
|
580
871
|
{
|
|
581
872
|
trilogy_builder_t builder;
|
|
@@ -727,6 +1018,7 @@ int trilogy_drain_results(trilogy_conn_t *conn)
|
|
|
727
1018
|
}
|
|
728
1019
|
|
|
729
1020
|
if (current_packet_type(conn) == TRILOGY_PACKET_EOF && conn->packet_buffer.len < 9) {
|
|
1021
|
+
read_eof_packet(conn);
|
|
730
1022
|
return TRILOGY_OK;
|
|
731
1023
|
}
|
|
732
1024
|
}
|
|
@@ -998,9 +1290,7 @@ int trilogy_stmt_reset_send(trilogy_conn_t *conn, trilogy_stmt_t *stmt)
|
|
|
998
1290
|
return begin_write(conn);
|
|
999
1291
|
}
|
|
1000
1292
|
|
|
1001
|
-
int trilogy_stmt_reset_recv(trilogy_conn_t *conn) {
|
|
1002
|
-
return read_generic_response(conn);
|
|
1003
|
-
}
|
|
1293
|
+
int trilogy_stmt_reset_recv(trilogy_conn_t *conn) { return read_generic_response(conn); }
|
|
1004
1294
|
|
|
1005
1295
|
int trilogy_stmt_close_send(trilogy_conn_t *conn, trilogy_stmt_t *stmt)
|
|
1006
1296
|
{
|
|
@@ -74,7 +74,7 @@ size_t trilogy_packet_parser_execute(trilogy_packet_parser_t *parser, const uint
|
|
|
74
74
|
break;
|
|
75
75
|
}
|
|
76
76
|
case S_SEQ: {
|
|
77
|
-
if (cur_byte != parser->sequence_number) {
|
|
77
|
+
if (cur_byte != parser->sequence_number && cur_byte > 0) {
|
|
78
78
|
*error = TRILOGY_INVALID_SEQUENCE_ID;
|
|
79
79
|
return i;
|
|
80
80
|
}
|
|
@@ -25,6 +25,12 @@ struct trilogy_sock {
|
|
|
25
25
|
SSL *ssl;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
void trilogy_sock_set_fd(trilogy_sock_t *_sock, int fd)
|
|
29
|
+
{
|
|
30
|
+
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
|
31
|
+
sock->fd = fd;
|
|
32
|
+
}
|
|
33
|
+
|
|
28
34
|
static int _cb_raw_fd(trilogy_sock_t *_sock)
|
|
29
35
|
{
|
|
30
36
|
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
data/lib/trilogy/error.rb
CHANGED
|
@@ -54,6 +54,12 @@ class Trilogy
|
|
|
54
54
|
include ConnectionError
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
+
class SynchronizationError < BaseError
|
|
58
|
+
def initialize(message = "This connection is already in use by another thread or fiber")
|
|
59
|
+
super
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
57
63
|
# Trilogy::ClientError is the base error type for invalid queries or parameters
|
|
58
64
|
# that shouldn't be retried.
|
|
59
65
|
class ClientError < BaseError
|
|
@@ -94,6 +100,7 @@ class Trilogy
|
|
|
94
100
|
1160 => BaseConnectionError, # ER_NET_ERROR_ON_WRITE
|
|
95
101
|
1161 => BaseConnectionError, # ER_NET_WRITE_INTERRUPTED
|
|
96
102
|
1927 => BaseConnectionError, # ER_CONNECTION_KILLED
|
|
103
|
+
4031 => BaseConnectionError, # Disconnected by server
|
|
97
104
|
}
|
|
98
105
|
class << self
|
|
99
106
|
def from_code(message, code)
|
data/lib/trilogy/version.rb
CHANGED
data/lib/trilogy.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "socket"
|
|
3
4
|
require "trilogy/version"
|
|
4
5
|
require "trilogy/error"
|
|
5
6
|
require "trilogy/result"
|
|
@@ -7,7 +8,34 @@ require "trilogy/cext"
|
|
|
7
8
|
require "trilogy/encoding"
|
|
8
9
|
|
|
9
10
|
class Trilogy
|
|
11
|
+
IO_TIMEOUT_ERROR =
|
|
12
|
+
if defined?(IO::TimeoutError)
|
|
13
|
+
IO::TimeoutError
|
|
14
|
+
else
|
|
15
|
+
Class.new(StandardError)
|
|
16
|
+
end
|
|
17
|
+
private_constant :IO_TIMEOUT_ERROR
|
|
18
|
+
|
|
19
|
+
Synchronization = Module.new
|
|
20
|
+
|
|
21
|
+
source = public_instance_methods(false).flat_map do |method|
|
|
22
|
+
[
|
|
23
|
+
"def #{method}(...)",
|
|
24
|
+
"raise SynchronizationError unless @mutex.try_lock",
|
|
25
|
+
"begin",
|
|
26
|
+
"super",
|
|
27
|
+
"ensure",
|
|
28
|
+
"@mutex.unlock",
|
|
29
|
+
"end",
|
|
30
|
+
"end",
|
|
31
|
+
]
|
|
32
|
+
end
|
|
33
|
+
Synchronization.class_eval(source.join(";"), __FILE__, __LINE__)
|
|
34
|
+
|
|
35
|
+
prepend(Synchronization)
|
|
36
|
+
|
|
10
37
|
def initialize(options = {})
|
|
38
|
+
@mutex = Mutex.new
|
|
11
39
|
options[:port] = options[:port].to_i if options[:port]
|
|
12
40
|
mysql_encoding = options[:encoding] || "utf8mb4"
|
|
13
41
|
encoding = Trilogy::Encoding.find(mysql_encoding)
|
|
@@ -15,7 +43,54 @@ class Trilogy
|
|
|
15
43
|
@connection_options = options
|
|
16
44
|
@connected_host = nil
|
|
17
45
|
|
|
18
|
-
|
|
46
|
+
socket = nil
|
|
47
|
+
begin
|
|
48
|
+
if host = options[:host]
|
|
49
|
+
port = options[:port] || 3306
|
|
50
|
+
connect_timeout = options[:connect_timeout] || options[:write_timeout]
|
|
51
|
+
|
|
52
|
+
socket = TCPSocket.new(host, port, connect_timeout: connect_timeout)
|
|
53
|
+
|
|
54
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
|
55
|
+
|
|
56
|
+
if keepalive_enabled = options[:keepalive_enabled]
|
|
57
|
+
keepalive_idle = options[:keepalive_idle]
|
|
58
|
+
keepalive_interval = options[:keepalive_interval]
|
|
59
|
+
keepalive_count = options[:keepalive_count]
|
|
60
|
+
|
|
61
|
+
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
|
|
62
|
+
|
|
63
|
+
if keepalive_idle > 0 && defined?(Socket::TCP_KEEPIDLE)
|
|
64
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPIDLE, keepalive_idle)
|
|
65
|
+
end
|
|
66
|
+
if keepalive_interval > 0 && defined?(Socket::TCP_KEEPINTVL)
|
|
67
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPINTVL, keepalive_interval)
|
|
68
|
+
end
|
|
69
|
+
if keepalive_count > 0 && defined?(Socket::TCP_KEEPCNT)
|
|
70
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPCNT, keepalive_count)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
else
|
|
74
|
+
path = options[:socket] ||= "/tmp/mysql.sock"
|
|
75
|
+
socket = UNIXSocket.new(path)
|
|
76
|
+
end
|
|
77
|
+
rescue Errno::ETIMEDOUT, IO_TIMEOUT_ERROR => e
|
|
78
|
+
raise Trilogy::TimeoutError, e.message
|
|
79
|
+
rescue SocketError => e
|
|
80
|
+
connection_str = host ? "#{host}:#{port}" : path
|
|
81
|
+
raise Trilogy::BaseConnectionError, "unable to connect to \"#{connection_str}\": #{e.message}"
|
|
82
|
+
rescue => e
|
|
83
|
+
if e.respond_to?(:errno)
|
|
84
|
+
raise Trilogy::SyscallError.from_errno(e.errno, e.message)
|
|
85
|
+
else
|
|
86
|
+
raise
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
_connect(socket, encoding, charset, options)
|
|
91
|
+
ensure
|
|
92
|
+
# Socket's fd will be dup'd in C
|
|
93
|
+
socket&.close
|
|
19
94
|
end
|
|
20
95
|
|
|
21
96
|
def connection_options
|
data/trilogy.gemspec
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: trilogy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- GitHub Engineering
|
|
@@ -23,34 +23,6 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0'
|
|
26
|
-
- !ruby/object:Gem::Dependency
|
|
27
|
-
name: rake-compiler
|
|
28
|
-
requirement: !ruby/object:Gem::Requirement
|
|
29
|
-
requirements:
|
|
30
|
-
- - "~>"
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: '1.0'
|
|
33
|
-
type: :development
|
|
34
|
-
prerelease: false
|
|
35
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - "~>"
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '1.0'
|
|
40
|
-
- !ruby/object:Gem::Dependency
|
|
41
|
-
name: minitest
|
|
42
|
-
requirement: !ruby/object:Gem::Requirement
|
|
43
|
-
requirements:
|
|
44
|
-
- - "~>"
|
|
45
|
-
- !ruby/object:Gem::Version
|
|
46
|
-
version: '5.5'
|
|
47
|
-
type: :development
|
|
48
|
-
prerelease: false
|
|
49
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
-
requirements:
|
|
51
|
-
- - "~>"
|
|
52
|
-
- !ruby/object:Gem::Version
|
|
53
|
-
version: '5.5'
|
|
54
26
|
email: opensource+trilogy@github.com
|
|
55
27
|
executables: []
|
|
56
28
|
extensions:
|