trilogy 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/trilogy.rb CHANGED
@@ -1,7 +1,178 @@
1
- require "trilogy/cext"
1
+ # frozen_string_literal: true
2
+
2
3
  require "trilogy/version"
3
4
 
4
5
  class Trilogy
6
+ # Trilogy::Error is the base error type. All errors raised by Trilogy
7
+ # should be descendants of Trilogy::Error
8
+ module Error
9
+ attr_reader :error_code
10
+ end
11
+
12
+ # Trilogy::ConnectionError is the base error type for all potentially transient
13
+ # network errors.
14
+ module ConnectionError
15
+ include Error
16
+ end
17
+
18
+ # Trilogy may raise various syscall errors, which we treat as Trilogy::Errors.
19
+ class SyscallError
20
+ ERRORS = {}
21
+
22
+ Errno.constants
23
+ .map { |c| Errno.const_get(c) }.uniq
24
+ .select { |c| c.is_a?(Class) && c < SystemCallError }
25
+ .each do |c|
26
+ errno_name = c.to_s.split('::').last
27
+ ERRORS[c::Errno] = const_set(errno_name, Class.new(c) { include Trilogy::Error })
28
+ end
29
+
30
+ ERRORS.freeze
31
+
32
+ class << self
33
+ def from_errno(errno, message)
34
+ ERRORS[errno].new(message)
35
+ end
36
+ end
37
+ end
38
+
39
+ class BaseError < StandardError
40
+ include Error
41
+
42
+ def initialize(error_message = nil, error_code = nil)
43
+ message = error_code ? "#{error_code}: #{error_message}" : error_message
44
+ super(message)
45
+ @error_code = error_code
46
+ end
47
+ end
48
+
49
+ class BaseConnectionError < BaseError
50
+ include ConnectionError
51
+ end
52
+
53
+ # Trilogy::ClientError is the base error type for invalid queries or parameters
54
+ # that shouldn't be retried.
55
+ class ClientError < BaseError
56
+ include Error
57
+ end
58
+
59
+ class QueryError < ClientError
60
+ end
61
+
62
+ class CastError < ClientError
63
+ end
64
+
65
+ class TimeoutError < Errno::ETIMEDOUT
66
+ include ConnectionError
67
+ end
68
+
69
+ class ConnectionRefusedError < Errno::ECONNREFUSED
70
+ include ConnectionError
71
+ end
72
+
73
+ class ConnectionResetError < Errno::ECONNRESET
74
+ include ConnectionError
75
+ end
76
+
77
+ # DatabaseError was replaced by ProtocolError, but we'll keep it around as an
78
+ # ancestor of ProtocolError for compatibility reasons (e.g. so `rescue DatabaseError`
79
+ # still works. We can remove this class in the next major release.
80
+ module DatabaseError
81
+ end
82
+
83
+ class ProtocolError < BaseError
84
+ include DatabaseError
85
+
86
+ ERROR_CODES = {
87
+ 1205 => TimeoutError, # ER_LOCK_WAIT_TIMEOUT
88
+ 1044 => BaseConnectionError, # ER_DBACCESS_DENIED_ERROR
89
+ 1045 => BaseConnectionError, # ER_ACCESS_DENIED_ERROR
90
+ 1064 => QueryError, # ER_PARSE_ERROR
91
+ 1152 => BaseConnectionError, # ER_ABORTING_CONNECTION
92
+ 1153 => BaseConnectionError, # ER_NET_PACKET_TOO_LARGE
93
+ 1154 => BaseConnectionError, # ER_NET_READ_ERROR_FROM_PIPE
94
+ 1155 => BaseConnectionError, # ER_NET_FCNTL_ERROR
95
+ 1156 => BaseConnectionError, # ER_NET_PACKETS_OUT_OF_ORDER
96
+ 1157 => BaseConnectionError, # ER_NET_UNCOMPRESS_ERROR
97
+ 1158 => BaseConnectionError, # ER_NET_READ_ERROR
98
+ 1159 => BaseConnectionError, # ER_NET_READ_INTERRUPTED
99
+ 1160 => BaseConnectionError, # ER_NET_ERROR_ON_WRITE
100
+ 1161 => BaseConnectionError, # ER_NET_WRITE_INTERRUPTED
101
+ 1927 => BaseConnectionError, # ER_CONNECTION_KILLED
102
+ }
103
+ class << self
104
+ def from_code(message, code)
105
+ ERROR_CODES.fetch(code, self).new(message, code)
106
+ end
107
+ end
108
+ end
109
+
110
+ class SSLError < BaseError
111
+ include ConnectionError
112
+ end
113
+
114
+ class ConnectionClosed < IOError
115
+ include ConnectionError
116
+ end
117
+
118
+ MYSQL_TO_RUBY_ENCODINGS_MAP = {
119
+ "big5" => "Big5",
120
+ "dec8" => nil,
121
+ "cp850" => "CP850",
122
+ "hp8" => nil,
123
+ "koi8r" => "KOI8-R",
124
+ "latin1" => "ISO-8859-1",
125
+ "latin2" => "ISO-8859-2",
126
+ "swe7" => nil,
127
+ "ascii" => "US-ASCII",
128
+ "ujis" => "eucJP-ms",
129
+ "sjis" => "Shift_JIS",
130
+ "hebrew" => "ISO-8859-8",
131
+ "tis620" => "TIS-620",
132
+ "euckr" => "EUC-KR",
133
+ "koi8u" => "KOI8-R",
134
+ "gb2312" => "GB2312",
135
+ "greek" => "ISO-8859-7",
136
+ "cp1250" => "Windows-1250",
137
+ "gbk" => "GBK",
138
+ "latin5" => "ISO-8859-9",
139
+ "armscii8" => nil,
140
+ "utf8" => "UTF-8",
141
+ "ucs2" => "UTF-16BE",
142
+ "cp866" => "IBM866",
143
+ "keybcs2" => nil,
144
+ "macce" => "macCentEuro",
145
+ "macroman" => "macRoman",
146
+ "cp852" => "CP852",
147
+ "latin7" => "ISO-8859-13",
148
+ "utf8mb4" => "UTF-8",
149
+ "cp1251" => "Windows-1251",
150
+ "utf16" => "UTF-16",
151
+ "cp1256" => "Windows-1256",
152
+ "cp1257" => "Windows-1257",
153
+ "utf32" => "UTF-32",
154
+ "binary" => "ASCII-8BIT",
155
+ "geostd8" => nil,
156
+ "cp932" => "Windows-31J",
157
+ "eucjpms" => "eucJP-ms",
158
+ "utf16le" => "UTF-16LE",
159
+ "gb18030" => "GB18030",
160
+ }.freeze
161
+
162
+ def initialize(options = {})
163
+ mysql_encoding = options[:encoding] || "utf8mb4"
164
+ unless rb_encoding = MYSQL_TO_RUBY_ENCODINGS_MAP[mysql_encoding]
165
+ raise ArgumentError, "Unknown or unsupported encoding: #{mysql_encoding}"
166
+ end
167
+ encoding = Encoding.find(rb_encoding)
168
+ charset = charset_for_mysql_encoding(mysql_encoding)
169
+ _initialize(encoding, charset, **options)
170
+ end
171
+
172
+ def connection_options
173
+ @connection_options.dup.freeze
174
+ end
175
+
5
176
  def in_transaction?
6
177
  (server_status & SERVER_STATUS_IN_TRANS) != 0
7
178
  end
@@ -28,34 +199,81 @@ class Trilogy
28
199
  ensure
29
200
  self.query_flags = old_flags
30
201
  end
31
- end
32
202
 
33
- Trilogy::Result.class_eval do
34
- def count
35
- rows.count
36
- end
203
+ class Result
204
+ attr_reader :fields, :rows, :query_time, :affected_rows, :last_insert_id
205
+
206
+ def count
207
+ rows.count
208
+ end
37
209
 
38
- def each_hash
39
- return enum_for(:each_hash) unless block_given?
210
+ def each_hash
211
+ return enum_for(:each_hash) unless block_given?
40
212
 
41
- rows.each do |row|
42
- this_row = {}
213
+ rows.each do |row|
214
+ this_row = {}
43
215
 
44
- idx = 0
45
- row.each do |col|
46
- this_row[fields[idx]] = col
47
- idx += 1
216
+ idx = 0
217
+ row.each do |col|
218
+ this_row[fields[idx]] = col
219
+ idx += 1
220
+ end
221
+
222
+ yield this_row
48
223
  end
49
224
 
50
- yield this_row
225
+ self
51
226
  end
52
227
 
53
- self
54
- end
228
+ def each(&bk)
229
+ rows.each(&bk)
230
+ end
55
231
 
56
- def each(&bk)
57
- rows.each(&bk)
232
+ include Enumerable
58
233
  end
59
234
 
60
- include Enumerable
235
+ private
236
+
237
+ def charset_for_mysql_encoding(mysql_encoding)
238
+ @mysql_encodings_map ||= {
239
+ "big5" => CHARSET_BIG5_CHINESE_CI,
240
+ "cp850" => CHARSET_CP850_GENERAL_CI,
241
+ "koi8r" => CHARSET_KOI8R_GENERAL_CI,
242
+ "latin1" => CHARSET_LATIN1_GENERAL_CI,
243
+ "latin2" => CHARSET_LATIN2_GENERAL_CI,
244
+ "ascii" => CHARSET_ASCII_GENERAL_CI,
245
+ "ujis" => CHARSET_UJIS_JAPANESE_CI,
246
+ "sjis" => CHARSET_SJIS_JAPANESE_CI,
247
+ "hebrew" => CHARSET_HEBREW_GENERAL_CI,
248
+ "tis620" => CHARSET_TIS620_THAI_CI,
249
+ "euckr" => CHARSET_EUCKR_KOREAN_CI,
250
+ "koi8u" => CHARSET_KOI8U_GENERAL_CI,
251
+ "gb2312" => CHARSET_GB2312_CHINESE_CI,
252
+ "greek" => CHARSET_GREEK_GENERAL_CI,
253
+ "cp1250" => CHARSET_CP1250_GENERAL_CI,
254
+ "gbk" => CHARSET_GBK_CHINESE_CI,
255
+ "latin5" => CHARSET_LATIN5_TURKISH_CI,
256
+ "utf8" => CHARSET_UTF8_GENERAL_CI,
257
+ "ucs2" => CHARSET_UCS2_GENERAL_CI,
258
+ "cp866" => CHARSET_CP866_GENERAL_CI,
259
+ "cp932" => CHARSET_CP932_JAPANESE_CI,
260
+ "eucjpms" => CHARSET_EUCJPMS_JAPANESE_CI,
261
+ "utf16le" => CHARSET_UTF16_GENERAL_CI,
262
+ "gb18030" => CHARSET_GB18030_CHINESE_CI,
263
+ "macce" => CHARSET_MACCE_GENERAL_CI,
264
+ "macroman" => CHARSET_MACROMAN_GENERAL_CI,
265
+ "cp852" => CHARSET_CP852_GENERAL_CI,
266
+ "latin7" => CHARSET_LATIN7_GENERAL_CI,
267
+ "utf8mb4" => CHARSET_UTF8MB4_GENERAL_CI,
268
+ "cp1251" => CHARSET_CP1251_GENERAL_CI,
269
+ "utf16" => CHARSET_UTF16_GENERAL_CI,
270
+ "cp1256" => CHARSET_CP1256_GENERAL_CI,
271
+ "cp1257" => CHARSET_CP1257_GENERAL_CI,
272
+ "utf32" => CHARSET_UTF32_GENERAL_CI,
273
+ "binary" => CHARSET_BINARY,
274
+ }.freeze
275
+ @mysql_encodings_map[mysql_encoding]
276
+ end
61
277
  end
278
+
279
+ require "trilogy/cext"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trilogy
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub Engineering
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-17 00:00:00.000000000 Z
11
+ date: 2023-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -99,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
99
99
  - !ruby/object:Gem::Version
100
100
  version: '0'
101
101
  requirements: []
102
- rubygems_version: 3.3.3
102
+ rubygems_version: 3.4.7
103
103
  signing_key:
104
104
  specification_version: 4
105
105
  summary: A friendly MySQL-compatible library for Ruby, binding to libtrilogy