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.
@@ -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
- return (ssize_t)TRILOGY_SYSERR;
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 _cb_raw_shutdown(trilogy_sock_t *_sock) { return shutdown(trilogy_sock_fd(_sock), SHUT_RDWR); }
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 < 0) {
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 && errno != 0) {
314
- return (ssize_t)TRILOGY_SYSERR;
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
- SSL_shutdown(sock->ssl);
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
- #if OPENSSL_VERSION_NUMBER >= 0x1010000fL
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
@@ -1,3 +1,3 @@
1
1
  class Trilogy
2
- VERSION = "2.4.1"
2
+ VERSION = "2.6.0"
3
3
  end