trilogy 2.4.1 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +14 -5
- data/Rakefile +6 -0
- data/ext/trilogy-ruby/cast.c +0 -4
- data/ext/trilogy-ruby/cext.c +118 -34
- data/ext/trilogy-ruby/inc/trilogy/blocking.h +118 -0
- data/ext/trilogy-ruby/inc/trilogy/builder.h +60 -0
- data/ext/trilogy-ruby/inc/trilogy/client.h +214 -0
- data/ext/trilogy-ruby/inc/trilogy/error.h +4 -1
- data/ext/trilogy-ruby/inc/trilogy/protocol.h +266 -2
- data/ext/trilogy-ruby/inc/trilogy/reader.h +4 -0
- data/ext/trilogy-ruby/inc/trilogy/socket.h +2 -1
- data/ext/trilogy-ruby/src/blocking.c +117 -0
- data/ext/trilogy-ruby/src/builder.c +63 -0
- data/ext/trilogy-ruby/src/client.c +180 -17
- data/ext/trilogy-ruby/src/protocol.c +503 -0
- data/ext/trilogy-ruby/src/reader.c +38 -0
- data/ext/trilogy-ruby/src/socket.c +104 -39
- data/lib/trilogy/encoding.rb +97 -0
- data/lib/trilogy/error.rb +124 -0
- data/lib/trilogy/result.rb +33 -0
- data/lib/trilogy/version.rb +1 -1
- data/lib/trilogy.rb +9 -244
- data/trilogy.gemspec +1 -1
- metadata +7 -4
@@ -1,5 +1,6 @@
|
|
1
1
|
#include <netdb.h>
|
2
2
|
#include <netinet/tcp.h>
|
3
|
+
#include <netinet/in.h>
|
3
4
|
#include <poll.h>
|
4
5
|
#include <sys/socket.h>
|
5
6
|
#include <sys/types.h>
|
@@ -65,7 +66,11 @@ static ssize_t _cb_raw_read(trilogy_sock_t *_sock, void *buf, size_t nread)
|
|
65
66
|
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
66
67
|
ssize_t data_read = read(sock->fd, buf, nread);
|
67
68
|
if (data_read < 0) {
|
68
|
-
|
69
|
+
if (errno == EINTR || errno == EAGAIN) {
|
70
|
+
return (ssize_t)TRILOGY_AGAIN;
|
71
|
+
} else {
|
72
|
+
return (ssize_t)TRILOGY_SYSERR;
|
73
|
+
}
|
69
74
|
}
|
70
75
|
return data_read;
|
71
76
|
}
|
@@ -75,6 +80,14 @@ static ssize_t _cb_raw_write(trilogy_sock_t *_sock, const void *buf, size_t nwri
|
|
75
80
|
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
76
81
|
ssize_t data_written = write(sock->fd, buf, nwrite);
|
77
82
|
if (data_written < 0) {
|
83
|
+
if (errno == EINTR || errno == EAGAIN) {
|
84
|
+
return (ssize_t)TRILOGY_AGAIN;
|
85
|
+
}
|
86
|
+
|
87
|
+
if (errno == EPIPE) {
|
88
|
+
return (ssize_t)TRILOGY_CLOSED_CONNECTION;
|
89
|
+
}
|
90
|
+
|
78
91
|
return (ssize_t)TRILOGY_SYSERR;
|
79
92
|
}
|
80
93
|
return data_written;
|
@@ -109,7 +122,53 @@ static int _cb_raw_close(trilogy_sock_t *_sock)
|
|
109
122
|
return rc;
|
110
123
|
}
|
111
124
|
|
112
|
-
static int
|
125
|
+
static int _cb_shutdown_connect(trilogy_sock_t *_sock) {
|
126
|
+
(void)_sock;
|
127
|
+
return TRILOGY_CLOSED_CONNECTION;
|
128
|
+
}
|
129
|
+
static ssize_t _cb_shutdown_write(trilogy_sock_t *_sock, const void *buf, size_t nwrite) {
|
130
|
+
(void)_sock;
|
131
|
+
(void)buf;
|
132
|
+
(void)nwrite;
|
133
|
+
return TRILOGY_CLOSED_CONNECTION;
|
134
|
+
}
|
135
|
+
static ssize_t _cb_shutdown_read(trilogy_sock_t *_sock, void *buf, size_t nread) {
|
136
|
+
(void)_sock;
|
137
|
+
(void)buf;
|
138
|
+
(void)nread;
|
139
|
+
return TRILOGY_CLOSED_CONNECTION;
|
140
|
+
}
|
141
|
+
static int _cb_shutdown_wait(trilogy_sock_t *_sock, trilogy_wait_t wait) {
|
142
|
+
(void)_sock;
|
143
|
+
(void)wait;
|
144
|
+
return TRILOGY_OK;
|
145
|
+
}
|
146
|
+
static int _cb_shutdown_shutdown(trilogy_sock_t *_sock) {
|
147
|
+
(void)_sock;
|
148
|
+
return TRILOGY_OK;
|
149
|
+
}
|
150
|
+
|
151
|
+
// Shutdown will close the underlying socket fd and replace all I/O operations with stubs which perform no action.
|
152
|
+
static int _cb_raw_shutdown(trilogy_sock_t *_sock) {
|
153
|
+
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
154
|
+
|
155
|
+
// Replace all operations with stubs which return immediately
|
156
|
+
sock->base.connect_cb = _cb_shutdown_connect;
|
157
|
+
sock->base.read_cb = _cb_shutdown_read;
|
158
|
+
sock->base.write_cb = _cb_shutdown_write;
|
159
|
+
sock->base.wait_cb = _cb_shutdown_wait;
|
160
|
+
sock->base.shutdown_cb = _cb_shutdown_shutdown;
|
161
|
+
|
162
|
+
// These "raw" callbacks won't attempt further operations on the socket and work correctly with fd set to -1
|
163
|
+
sock->base.close_cb = _cb_raw_close;
|
164
|
+
sock->base.fd_cb = _cb_raw_fd;
|
165
|
+
|
166
|
+
if (sock->fd != -1)
|
167
|
+
close(sock->fd);
|
168
|
+
sock->fd = -1;
|
169
|
+
|
170
|
+
return TRILOGY_OK;
|
171
|
+
}
|
113
172
|
|
114
173
|
static int set_nonblocking_fd(int sock)
|
115
174
|
{
|
@@ -136,6 +195,14 @@ static int raw_connect_internal(struct trilogy_sock *sock, const struct addrinfo
|
|
136
195
|
return TRILOGY_SYSERR;
|
137
196
|
}
|
138
197
|
|
198
|
+
#ifdef TCP_NODELAY
|
199
|
+
if (sock->addr->ai_family != PF_UNIX) {
|
200
|
+
int flags = 1;
|
201
|
+
if (setsockopt(sock->fd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)) < 0) {
|
202
|
+
goto fail;
|
203
|
+
}
|
204
|
+
}
|
205
|
+
#endif
|
139
206
|
if (sock->base.opts.keepalive_enabled) {
|
140
207
|
int flags = 1;
|
141
208
|
if (setsockopt(sock->fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)) < 0) {
|
@@ -306,12 +373,18 @@ fail:
|
|
306
373
|
|
307
374
|
static ssize_t ssl_io_return(struct trilogy_sock *sock, ssize_t ret)
|
308
375
|
{
|
309
|
-
if (ret
|
376
|
+
if (ret <= 0) {
|
310
377
|
int rc = SSL_get_error(sock->ssl, (int)ret);
|
311
378
|
if (rc == SSL_ERROR_WANT_WRITE || rc == SSL_ERROR_WANT_READ) {
|
312
379
|
return (ssize_t)TRILOGY_AGAIN;
|
313
|
-
} else if (rc == SSL_ERROR_SYSCALL &&
|
314
|
-
|
380
|
+
} else if (rc == SSL_ERROR_SYSCALL && !ERR_peek_error()) {
|
381
|
+
if (errno == 0) {
|
382
|
+
// On OpenSSL <= 1.1.1, SSL_ERROR_SYSCALL with an errno value
|
383
|
+
// of 0 indicates unexpected EOF from the peer.
|
384
|
+
return (ssize_t)TRILOGY_CLOSED_CONNECTION;
|
385
|
+
} else {
|
386
|
+
return (ssize_t)TRILOGY_SYSERR;
|
387
|
+
}
|
315
388
|
}
|
316
389
|
return (ssize_t)TRILOGY_OPENSSL_ERR;
|
317
390
|
}
|
@@ -321,6 +394,11 @@ static ssize_t ssl_io_return(struct trilogy_sock *sock, ssize_t ret)
|
|
321
394
|
static ssize_t _cb_ssl_read(trilogy_sock_t *_sock, void *buf, size_t nread)
|
322
395
|
{
|
323
396
|
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
397
|
+
|
398
|
+
// This shouldn't be necessary, but protects against other libraries in the same process incorrectly leaving errors
|
399
|
+
// in the queue.
|
400
|
+
ERR_clear_error();
|
401
|
+
|
324
402
|
ssize_t data_read = (ssize_t)SSL_read(sock->ssl, buf, (int)nread);
|
325
403
|
return ssl_io_return(sock, data_read);
|
326
404
|
}
|
@@ -328,6 +406,11 @@ static ssize_t _cb_ssl_read(trilogy_sock_t *_sock, void *buf, size_t nread)
|
|
328
406
|
static ssize_t _cb_ssl_write(trilogy_sock_t *_sock, const void *buf, size_t nwrite)
|
329
407
|
{
|
330
408
|
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
409
|
+
|
410
|
+
// This shouldn't be necessary, but protects against other libraries in the same process incorrectly leaving errors
|
411
|
+
// in the queue.
|
412
|
+
ERR_clear_error();
|
413
|
+
|
331
414
|
ssize_t data_written = (ssize_t)SSL_write(sock->ssl, buf, (int)nwrite);
|
332
415
|
return ssl_io_return(sock, data_written);
|
333
416
|
}
|
@@ -340,15 +423,9 @@ static int _cb_ssl_shutdown(trilogy_sock_t *_sock)
|
|
340
423
|
// we need to close it. The OpenSSL explicitly states
|
341
424
|
// not to call SSL_shutdown on a broken SSL socket.
|
342
425
|
SSL_free(sock->ssl);
|
343
|
-
// Reset the handlers since we tore down SSL, so we
|
344
|
-
// fall back to the regular methods for detecting
|
345
|
-
// we have a closed connection and for the cleanup.
|
346
|
-
sock->base.read_cb = _cb_raw_read;
|
347
|
-
sock->base.write_cb = _cb_raw_write;
|
348
|
-
sock->base.shutdown_cb = _cb_raw_shutdown;
|
349
|
-
sock->base.close_cb = _cb_raw_close;
|
350
426
|
sock->ssl = NULL;
|
351
427
|
|
428
|
+
// This will rewrite the handlers
|
352
429
|
return _cb_raw_shutdown(_sock);
|
353
430
|
}
|
354
431
|
|
@@ -356,7 +433,12 @@ static int _cb_ssl_close(trilogy_sock_t *_sock)
|
|
356
433
|
{
|
357
434
|
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
358
435
|
if (sock->ssl != NULL) {
|
359
|
-
|
436
|
+
if (SSL_in_init(sock->ssl) == 0) {
|
437
|
+
(void)SSL_shutdown(sock->ssl);
|
438
|
+
// SSL_shutdown might return WANT_WRITE or WANT_READ. Ideally we would retry but we don't want to block.
|
439
|
+
// It may also push an error onto the OpenSSL error queue, so clear that.
|
440
|
+
ERR_clear_error();
|
441
|
+
}
|
360
442
|
SSL_free(sock->ssl);
|
361
443
|
sock->ssl = NULL;
|
362
444
|
}
|
@@ -537,6 +619,10 @@ int trilogy_sock_upgrade_ssl(trilogy_sock_t *_sock)
|
|
537
619
|
{
|
538
620
|
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
539
621
|
|
622
|
+
// This shouldn't be necessary, but protects against other libraries in the same process incorrectly leaving errors
|
623
|
+
// in the queue.
|
624
|
+
ERR_clear_error();
|
625
|
+
|
540
626
|
SSL_CTX *ctx = trilogy_ssl_ctx(&sock->base.opts);
|
541
627
|
|
542
628
|
if (!ctx) {
|
@@ -548,7 +634,7 @@ int trilogy_sock_upgrade_ssl(trilogy_sock_t *_sock)
|
|
548
634
|
|
549
635
|
if (sock->base.opts.ssl_mode == TRILOGY_SSL_VERIFY_IDENTITY && sock->base.opts.hostname == NULL) {
|
550
636
|
// If hostname validation is requested and no hostname provided, treat it as an error.
|
551
|
-
#
|
637
|
+
#ifdef SSL_F_TLS_PROCESS_SERVER_CERTIFICATE
|
552
638
|
ERR_put_error(ERR_LIB_SSL, SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, SSL_R_CERTIFICATE_VERIFY_FAILED, NULL, 0);
|
553
639
|
#else
|
554
640
|
ERR_put_error(ERR_LIB_SSL, SSL_F_SSL3_GET_SERVER_CERTIFICATE, SSL_R_CERTIFICATE_VERIFY_FAILED, NULL, 0);
|
@@ -579,6 +665,10 @@ int trilogy_sock_upgrade_ssl(trilogy_sock_t *_sock)
|
|
579
665
|
goto fail;
|
580
666
|
|
581
667
|
for (;;) {
|
668
|
+
// This shouldn't be necessary, but protects against other libraries in the same process incorrectly leaving errors
|
669
|
+
// in the queue.
|
670
|
+
ERR_clear_error();
|
671
|
+
|
582
672
|
int ret = SSL_connect(sock->ssl);
|
583
673
|
if (ret == 1) {
|
584
674
|
#if OPENSSL_VERSION_NUMBER < 0x1000200fL
|
@@ -621,28 +711,3 @@ fail:
|
|
621
711
|
sock->ssl = NULL;
|
622
712
|
return TRILOGY_OPENSSL_ERR;
|
623
713
|
}
|
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
|
-
}
|
@@ -0,0 +1,97 @@
|
|
1
|
+
class Trilogy
|
2
|
+
module Encoding
|
3
|
+
RUBY_ENCODINGS = {
|
4
|
+
"big5" => "Big5",
|
5
|
+
"dec8" => nil,
|
6
|
+
"cp850" => "CP850",
|
7
|
+
"hp8" => nil,
|
8
|
+
"koi8r" => "KOI8-R",
|
9
|
+
"latin1" => "ISO-8859-1",
|
10
|
+
"latin2" => "ISO-8859-2",
|
11
|
+
"swe7" => nil,
|
12
|
+
"ascii" => "US-ASCII",
|
13
|
+
"ujis" => "eucJP-ms",
|
14
|
+
"sjis" => "Shift_JIS",
|
15
|
+
"hebrew" => "ISO-8859-8",
|
16
|
+
"tis620" => "TIS-620",
|
17
|
+
"euckr" => "EUC-KR",
|
18
|
+
"koi8u" => "KOI8-R",
|
19
|
+
"gb2312" => "GB2312",
|
20
|
+
"greek" => "ISO-8859-7",
|
21
|
+
"cp1250" => "Windows-1250",
|
22
|
+
"gbk" => "GBK",
|
23
|
+
"latin5" => "ISO-8859-9",
|
24
|
+
"armscii8" => nil,
|
25
|
+
"utf8" => "UTF-8",
|
26
|
+
"ucs2" => "UTF-16BE",
|
27
|
+
"cp866" => "IBM866",
|
28
|
+
"keybcs2" => nil,
|
29
|
+
"macce" => "macCentEuro",
|
30
|
+
"macroman" => "macRoman",
|
31
|
+
"cp852" => "CP852",
|
32
|
+
"latin7" => "ISO-8859-13",
|
33
|
+
"utf8mb4" => "UTF-8",
|
34
|
+
"cp1251" => "Windows-1251",
|
35
|
+
"utf16" => "UTF-16",
|
36
|
+
"cp1256" => "Windows-1256",
|
37
|
+
"cp1257" => "Windows-1257",
|
38
|
+
"utf32" => "UTF-32",
|
39
|
+
"binary" => "ASCII-8BIT",
|
40
|
+
"geostd8" => nil,
|
41
|
+
"cp932" => "Windows-31J",
|
42
|
+
"eucjpms" => "eucJP-ms",
|
43
|
+
"utf16le" => "UTF-16LE",
|
44
|
+
"gb18030" => "GB18030",
|
45
|
+
}.freeze
|
46
|
+
|
47
|
+
CHARSETS = {
|
48
|
+
"big5" => CHARSET_BIG5_CHINESE_CI,
|
49
|
+
"cp850" => CHARSET_CP850_GENERAL_CI,
|
50
|
+
"koi8r" => CHARSET_KOI8R_GENERAL_CI,
|
51
|
+
"latin1" => CHARSET_LATIN1_GENERAL_CI,
|
52
|
+
"latin2" => CHARSET_LATIN2_GENERAL_CI,
|
53
|
+
"ascii" => CHARSET_ASCII_GENERAL_CI,
|
54
|
+
"ujis" => CHARSET_UJIS_JAPANESE_CI,
|
55
|
+
"sjis" => CHARSET_SJIS_JAPANESE_CI,
|
56
|
+
"hebrew" => CHARSET_HEBREW_GENERAL_CI,
|
57
|
+
"tis620" => CHARSET_TIS620_THAI_CI,
|
58
|
+
"euckr" => CHARSET_EUCKR_KOREAN_CI,
|
59
|
+
"koi8u" => CHARSET_KOI8U_GENERAL_CI,
|
60
|
+
"gb2312" => CHARSET_GB2312_CHINESE_CI,
|
61
|
+
"greek" => CHARSET_GREEK_GENERAL_CI,
|
62
|
+
"cp1250" => CHARSET_CP1250_GENERAL_CI,
|
63
|
+
"gbk" => CHARSET_GBK_CHINESE_CI,
|
64
|
+
"latin5" => CHARSET_LATIN5_TURKISH_CI,
|
65
|
+
"utf8" => CHARSET_UTF8_GENERAL_CI,
|
66
|
+
"ucs2" => CHARSET_UCS2_GENERAL_CI,
|
67
|
+
"cp866" => CHARSET_CP866_GENERAL_CI,
|
68
|
+
"cp932" => CHARSET_CP932_JAPANESE_CI,
|
69
|
+
"eucjpms" => CHARSET_EUCJPMS_JAPANESE_CI,
|
70
|
+
"utf16le" => CHARSET_UTF16_GENERAL_CI,
|
71
|
+
"gb18030" => CHARSET_GB18030_CHINESE_CI,
|
72
|
+
"macce" => CHARSET_MACCE_GENERAL_CI,
|
73
|
+
"macroman" => CHARSET_MACROMAN_GENERAL_CI,
|
74
|
+
"cp852" => CHARSET_CP852_GENERAL_CI,
|
75
|
+
"latin7" => CHARSET_LATIN7_GENERAL_CI,
|
76
|
+
"utf8mb4" => CHARSET_UTF8MB4_GENERAL_CI,
|
77
|
+
"cp1251" => CHARSET_CP1251_GENERAL_CI,
|
78
|
+
"utf16" => CHARSET_UTF16_GENERAL_CI,
|
79
|
+
"cp1256" => CHARSET_CP1256_GENERAL_CI,
|
80
|
+
"cp1257" => CHARSET_CP1257_GENERAL_CI,
|
81
|
+
"utf32" => CHARSET_UTF32_GENERAL_CI,
|
82
|
+
"binary" => CHARSET_BINARY,
|
83
|
+
}.freeze
|
84
|
+
|
85
|
+
def self.find(mysql_encoding)
|
86
|
+
unless rb_encoding = RUBY_ENCODINGS[mysql_encoding]
|
87
|
+
raise ArgumentError, "Unknown or unsupported encoding: #{mysql_encoding}"
|
88
|
+
end
|
89
|
+
|
90
|
+
::Encoding.find(rb_encoding)
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.charset(mysql_encoding)
|
94
|
+
CHARSETS[mysql_encoding]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
class Trilogy
|
2
|
+
# Trilogy::Error is the base error type. All errors raised by Trilogy
|
3
|
+
# should be descendants of Trilogy::Error
|
4
|
+
module Error
|
5
|
+
attr_reader :error_code
|
6
|
+
end
|
7
|
+
|
8
|
+
# Trilogy::ConnectionError is the base error type for all potentially transient
|
9
|
+
# network errors.
|
10
|
+
module ConnectionError
|
11
|
+
include Error
|
12
|
+
end
|
13
|
+
|
14
|
+
# Trilogy may raise various syscall errors, which we treat as Trilogy::Errors.
|
15
|
+
class SyscallError
|
16
|
+
ERRORS = {}
|
17
|
+
|
18
|
+
Errno.constants
|
19
|
+
.map { |c| Errno.const_get(c) }.uniq
|
20
|
+
.select { |c| c.is_a?(Class) && c < SystemCallError }
|
21
|
+
.each do |c|
|
22
|
+
errno_name = c.to_s.split('::').last
|
23
|
+
ERRORS[c::Errno] = const_set(errno_name, Class.new(c) { include Trilogy::ConnectionError })
|
24
|
+
end
|
25
|
+
|
26
|
+
ERRORS.freeze
|
27
|
+
|
28
|
+
class << self
|
29
|
+
def from_errno(errno, message)
|
30
|
+
ERRORS[errno].new(message)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class BaseError < StandardError
|
36
|
+
include Error
|
37
|
+
|
38
|
+
def initialize(error_message = nil, error_code = nil)
|
39
|
+
message = error_code ? "#{error_code}: #{error_message}" : error_message
|
40
|
+
super(message)
|
41
|
+
@error_code = error_code
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class BaseConnectionError < BaseError
|
46
|
+
include ConnectionError
|
47
|
+
end
|
48
|
+
|
49
|
+
# Trilogy::ClientError is the base error type for invalid queries or parameters
|
50
|
+
# that shouldn't be retried.
|
51
|
+
class ClientError < BaseError
|
52
|
+
include Error
|
53
|
+
end
|
54
|
+
|
55
|
+
class QueryError < ClientError
|
56
|
+
end
|
57
|
+
|
58
|
+
class CastError < ClientError
|
59
|
+
end
|
60
|
+
|
61
|
+
class TimeoutError < Errno::ETIMEDOUT
|
62
|
+
include ConnectionError
|
63
|
+
|
64
|
+
def initialize(error_message = nil, error_code = nil)
|
65
|
+
super
|
66
|
+
@error_code = error_code
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class ConnectionRefusedError < Errno::ECONNREFUSED
|
71
|
+
include ConnectionError
|
72
|
+
end
|
73
|
+
|
74
|
+
class ConnectionResetError < Errno::ECONNRESET
|
75
|
+
include ConnectionError
|
76
|
+
end
|
77
|
+
|
78
|
+
# DatabaseError was replaced by ProtocolError, but we'll keep it around as an
|
79
|
+
# ancestor of ProtocolError for compatibility reasons (e.g. so `rescue DatabaseError`
|
80
|
+
# still works. We can remove this class in the next major release.
|
81
|
+
module DatabaseError
|
82
|
+
end
|
83
|
+
|
84
|
+
class ProtocolError < BaseError
|
85
|
+
include DatabaseError
|
86
|
+
|
87
|
+
ERROR_CODES = {
|
88
|
+
1205 => TimeoutError, # ER_LOCK_WAIT_TIMEOUT
|
89
|
+
1044 => BaseConnectionError, # ER_DBACCESS_DENIED_ERROR
|
90
|
+
1045 => BaseConnectionError, # ER_ACCESS_DENIED_ERROR
|
91
|
+
1064 => QueryError, # ER_PARSE_ERROR
|
92
|
+
1152 => BaseConnectionError, # ER_ABORTING_CONNECTION
|
93
|
+
1153 => BaseConnectionError, # ER_NET_PACKET_TOO_LARGE
|
94
|
+
1154 => BaseConnectionError, # ER_NET_READ_ERROR_FROM_PIPE
|
95
|
+
1155 => BaseConnectionError, # ER_NET_FCNTL_ERROR
|
96
|
+
1156 => BaseConnectionError, # ER_NET_PACKETS_OUT_OF_ORDER
|
97
|
+
1157 => BaseConnectionError, # ER_NET_UNCOMPRESS_ERROR
|
98
|
+
1158 => BaseConnectionError, # ER_NET_READ_ERROR
|
99
|
+
1159 => BaseConnectionError, # ER_NET_READ_INTERRUPTED
|
100
|
+
1160 => BaseConnectionError, # ER_NET_ERROR_ON_WRITE
|
101
|
+
1161 => BaseConnectionError, # ER_NET_WRITE_INTERRUPTED
|
102
|
+
1927 => BaseConnectionError, # ER_CONNECTION_KILLED
|
103
|
+
}
|
104
|
+
class << self
|
105
|
+
def from_code(message, code)
|
106
|
+
ERROR_CODES.fetch(code, self).new(message, code)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class SSLError < BaseError
|
112
|
+
include ConnectionError
|
113
|
+
end
|
114
|
+
|
115
|
+
# Raised on attempt to use connection which was explicitly closed by the user
|
116
|
+
class ConnectionClosed < IOError
|
117
|
+
include ConnectionError
|
118
|
+
end
|
119
|
+
|
120
|
+
# Occurrs when a socket read or write returns EOF or when an operation is
|
121
|
+
# attempted on a socket which previously encountered an error.
|
122
|
+
class EOFError < BaseConnectionError
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Trilogy
|
2
|
+
class Result
|
3
|
+
attr_reader :fields, :rows, :query_time, :affected_rows, :last_insert_id
|
4
|
+
|
5
|
+
def count
|
6
|
+
rows.count
|
7
|
+
end
|
8
|
+
|
9
|
+
def each_hash
|
10
|
+
return enum_for(:each_hash) unless block_given?
|
11
|
+
|
12
|
+
rows.each do |row|
|
13
|
+
this_row = {}
|
14
|
+
|
15
|
+
idx = 0
|
16
|
+
row.each do |col|
|
17
|
+
this_row[fields[idx]] = col
|
18
|
+
idx += 1
|
19
|
+
end
|
20
|
+
|
21
|
+
yield this_row
|
22
|
+
end
|
23
|
+
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def each(&bk)
|
28
|
+
rows.each(&bk)
|
29
|
+
end
|
30
|
+
|
31
|
+
include Enumerable
|
32
|
+
end
|
33
|
+
end
|
data/lib/trilogy/version.rb
CHANGED