trilogy 2.4.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +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