ruby-mysql 2.9.12 → 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.
@@ -1,11 +1,11 @@
1
1
  # coding: ascii-8bit
2
- # Copyright (C) 2008-2012 TOMITA Masahiro
2
+ # Copyright (C) 2008 TOMITA Masahiro
3
3
  # mailto:tommy@tmtm.org
4
4
 
5
5
  require "socket"
6
- require "timeout"
7
- require "digest/sha1"
8
6
  require "stringio"
7
+ require "openssl"
8
+ require_relative 'authenticator.rb'
9
9
 
10
10
  class Mysql
11
11
  # MySQL network protocol
@@ -23,7 +23,7 @@ class Mysql
23
23
  # Object :: converted value.
24
24
  def self.net2value(pkt, type, unsigned)
25
25
  case type
26
- when Field::TYPE_STRING, Field::TYPE_VAR_STRING, Field::TYPE_NEWDECIMAL, Field::TYPE_BLOB
26
+ when Field::TYPE_STRING, Field::TYPE_VAR_STRING, Field::TYPE_NEWDECIMAL, Field::TYPE_BLOB, Field::TYPE_JSON
27
27
  return pkt.lcs
28
28
  when Field::TYPE_TINY
29
29
  v = pkt.utiny
@@ -112,12 +112,14 @@ class Mysql
112
112
  attr_reader :server_info
113
113
  attr_reader :server_version
114
114
  attr_reader :thread_id
115
+ attr_reader :client_flags
115
116
  attr_reader :sqlstate
116
117
  attr_reader :affected_rows
117
118
  attr_reader :insert_id
118
119
  attr_reader :server_status
119
120
  attr_reader :warning_count
120
121
  attr_reader :message
122
+ attr_reader :get_server_public_key
121
123
  attr_accessor :charset
122
124
 
123
125
  # @state variable keep state for connection.
@@ -127,39 +129,38 @@ class Mysql
127
129
  # :RESULT :: After retr_fields(), retr_all_records() or stmt_retr_all_records() is needed.
128
130
 
129
131
  # make socket connection to server.
130
- # === Argument
131
- # host :: [String] if "localhost" or "" nil then use UNIXSocket. Otherwise use TCPSocket
132
- # port :: [Integer] port number using by TCPSocket
133
- # socket :: [String] socket file name using by UNIXSocket
134
- # conn_timeout :: [Integer] connect timeout (sec).
135
- # read_timeout :: [Integer] read timeout (sec).
136
- # write_timeout :: [Integer] write timeout (sec).
137
- # === Exception
138
- # [ClientError] :: connection timeout
139
- def initialize(host, port, socket, conn_timeout, read_timeout, write_timeout)
132
+ # @param host [String] if "localhost" or "" or nil then use UNIX socket. Otherwise use TCP socket
133
+ # @param port [Integer] port number using by TCP socket
134
+ # @param socket [String] socket file name using by UNIX socket
135
+ # @param [Hash] opts
136
+ # @option opts :conn_timeout [Integer] connect timeout (sec).
137
+ # @option opts :read_timeout [Integer] read timeout (sec).
138
+ # @option opts :write_timeout [Integer] write timeout (sec).
139
+ # @option opts :local_infile [String] local infile path
140
+ # @option opts :get_server_public_key [Boolean]
141
+ # @raise [ClientError] connection timeout
142
+ def initialize(host, port, socket, opts)
143
+ @opts = opts
140
144
  @insert_id = 0
141
145
  @warning_count = 0
142
146
  @gc_stmt_queue = [] # stmt id list which GC destroy.
143
147
  set_state :INIT
144
- @read_timeout = read_timeout
145
- @write_timeout = write_timeout
148
+ @get_server_public_key = @opts[:get_server_public_key]
146
149
  begin
147
- Timeout.timeout conn_timeout do
148
- if host.nil? or host.empty? or host == "localhost"
149
- socket ||= ENV["MYSQL_UNIX_PORT"] || MYSQL_UNIX_PORT
150
- @sock = UNIXSocket.new socket
151
- else
152
- port ||= ENV["MYSQL_TCP_PORT"] || (Socket.getservbyname("mysql","tcp") rescue MYSQL_TCP_PORT)
153
- @sock = TCPSocket.new host, port
154
- end
150
+ if host.nil? or host.empty? or host == "localhost"
151
+ socket ||= ENV["MYSQL_UNIX_PORT"] || MYSQL_UNIX_PORT
152
+ @socket = Socket.unix(socket)
153
+ else
154
+ port ||= ENV["MYSQL_TCP_PORT"] || (Socket.getservbyname("mysql","tcp") rescue MYSQL_TCP_PORT)
155
+ @socket = Socket.tcp(host, port, connect_timeout: @opts[:connect_timeout])
155
156
  end
156
- rescue Timeout::Error
157
+ rescue Errno::ETIMEDOUT
157
158
  raise ClientError, "connection timeout"
158
159
  end
159
160
  end
160
161
 
161
162
  def close
162
- @sock.close
163
+ @socket.close
163
164
  end
164
165
 
165
166
  # initial negotiate and authenticate.
@@ -178,21 +179,49 @@ class Mysql
178
179
  init_packet = InitialPacket.parse read
179
180
  @server_info = init_packet.server_version
180
181
  @server_version = init_packet.server_version.split(/\D/)[0,3].inject{|a,b|a.to_i*100+b.to_i}
182
+ @server_capabilities = init_packet.server_capabilities
181
183
  @thread_id = init_packet.thread_id
182
- client_flags = CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION
183
- client_flags |= CLIENT_CONNECT_WITH_DB if db
184
- client_flags |= flag
184
+ @client_flags = CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | CLIENT_PLUGIN_AUTH
185
+ @client_flags |= CLIENT_LOCAL_FILES if @opts[:local_infile]
186
+ @client_flags |= CLIENT_CONNECT_WITH_DB if db
187
+ @client_flags |= flag
185
188
  @charset = charset
186
189
  unless @charset
187
190
  @charset = Charset.by_number(init_packet.server_charset)
188
191
  @charset.encoding # raise error if unsupported charset
189
192
  end
190
- netpw = encrypt_password passwd, init_packet.scramble_buff
191
- write AuthenticationPacket.serialize(client_flags, 1024**3, @charset.number, user, netpw, db)
192
- raise ProtocolError, 'The old style password is not supported' if read.to_s == "\xfe"
193
+ enable_ssl
194
+ Authenticator.new(self).authenticate(user, passwd, db, init_packet.scramble_buff, init_packet.auth_plugin)
193
195
  set_state :READY
194
196
  end
195
197
 
198
+ def enable_ssl
199
+ case @opts[:ssl_mode]
200
+ when SSL_MODE_DISABLED
201
+ return
202
+ when SSL_MODE_PREFERRED
203
+ return if @socket.local_address.unix?
204
+ return if @server_capabilities & CLIENT_SSL == 0
205
+ when SSL_MODE_REQUIRED
206
+ if @server_capabilities & CLIENT_SSL == 0
207
+ raise ClientError::SslConnectionError, "SSL is required but the server doesn't support it"
208
+ end
209
+ else
210
+ raise ClientError, "ssl_mode #{@opts[:ssl_mode]} is not supported"
211
+ end
212
+ begin
213
+ @client_flags |= CLIENT_SSL
214
+ write Protocol::TlsAuthenticationPacket.serialize(@client_flags, 1024**3, @charset.number)
215
+ @socket = OpenSSL::SSL::SSLSocket.new(@socket)
216
+ @socket.sync_close = true
217
+ @socket.connect
218
+ rescue => e
219
+ @client_flags &= ~CLIENT_SSL
220
+ return if @opts[:ssl_mode] == SSL_MODE_PREFERRED
221
+ raise e
222
+ end
223
+ end
224
+
196
225
  # Quit command
197
226
  def quit_command
198
227
  synchronize do
@@ -230,10 +259,7 @@ class Mysql
230
259
  return res_packet.field_count
231
260
  end
232
261
  if res_packet.field_count.nil? # LOAD DATA LOCAL INFILE
233
- filename = res_packet.message
234
- File.open(filename){|f| write f}
235
- write nil # EOF mark
236
- read
262
+ send_local_file(res_packet.message)
237
263
  end
238
264
  @affected_rows, @insert_id, @server_status, @warning_count, @message =
239
265
  res_packet.affected_rows, res_packet.insert_id, res_packet.server_status, res_packet.warning_count, res_packet.message
@@ -245,6 +271,19 @@ class Mysql
245
271
  end
246
272
  end
247
273
 
274
+ # send local file to server
275
+ def send_local_file(filename)
276
+ filename = File.absolute_path(filename)
277
+ if filename.start_with? @opts[:local_infile]
278
+ File.open(filename){|f| write f}
279
+ else
280
+ raise ClientError::LoadDataLocalInfileRejected, 'LOAD DATA LOCAL INFILE file request rejected due to restrictions on access.'
281
+ end
282
+ ensure
283
+ write nil # EOF mark
284
+ read
285
+ end
286
+
248
287
  # Retrieve n fields
249
288
  # === Argument
250
289
  # n :: [Integer] number of fields
@@ -429,15 +468,13 @@ class Mysql
429
468
  @gc_stmt_queue.push stmt_id
430
469
  end
431
470
 
432
- private
433
-
434
471
  def check_state(st)
435
472
  raise 'command out of sync' unless @state == st
436
473
  end
437
474
 
438
475
  def set_state(st)
439
476
  @state = st
440
- if st == :READY
477
+ if st == :READY && !@gc_stmt_queue.empty?
441
478
  gc_disabled = GC.disable
442
479
  begin
443
480
  while st = @gc_stmt_queue.shift
@@ -473,20 +510,18 @@ class Mysql
473
510
  data = ''
474
511
  len = nil
475
512
  begin
476
- Timeout.timeout @read_timeout do
477
- header = @sock.read(4)
478
- raise EOFError unless header && header.length == 4
479
- len1, len2, seq = header.unpack("CvC")
480
- len = (len2 << 8) + len1
481
- raise ProtocolError, "invalid packet: sequence number mismatch(#{seq} != #{@seq}(expected))" if @seq != seq
482
- @seq = (@seq + 1) % 256
483
- ret = @sock.read(len)
484
- raise EOFError unless ret && ret.length == len
485
- data.concat ret
486
- end
513
+ header = read_timeout(4, @opts[:read_timeout])
514
+ raise EOFError unless header && header.length == 4
515
+ len1, len2, seq = header.unpack("CvC")
516
+ len = (len2 << 8) + len1
517
+ raise ProtocolError, "invalid packet: sequence number mismatch(#{seq} != #{@seq}(expected))" if @seq != seq
518
+ @seq = (@seq + 1) % 256
519
+ ret = read_timeout(len, @opts[:read_timeout])
520
+ raise EOFError unless ret && ret.length == len
521
+ data.concat ret
487
522
  rescue EOFError
488
523
  raise ClientError::ServerGoneError, 'MySQL server has gone away'
489
- rescue Timeout::Error
524
+ rescue Errno::ETIMEDOUT
490
525
  raise ClientError, "read timeout"
491
526
  end while len == MAX_PACKET_LENGTH
492
527
 
@@ -494,52 +529,87 @@ class Mysql
494
529
 
495
530
  # Error packet
496
531
  if data[0] == ?\xff
497
- f, errno, marker, @sqlstate, message = data.unpack("Cvaa5a*")
532
+ _, errno, marker, @sqlstate, message = data.unpack("Cvaa5a*")
498
533
  unless marker == "#"
499
- f, errno, message = data.unpack("Cva*") # Version 4.0 Error
534
+ _, errno, message = data.unpack("Cva*") # Version 4.0 Error
500
535
  @sqlstate = ""
501
536
  end
502
537
  message.force_encoding(@charset.encoding)
503
538
  if Mysql::ServerError::ERROR_MAP.key? errno
504
539
  raise Mysql::ServerError::ERROR_MAP[errno].new(message, @sqlstate)
505
540
  end
506
- raise Mysql::ServerError.new(message, @sqlstate)
541
+ raise Mysql::ServerError.new(message, @sqlstate, errno)
507
542
  end
508
543
  Packet.new(data)
509
544
  end
510
545
 
546
+ def read_timeout(len, timeout)
547
+ return @socket.read(len) if timeout.nil? || timeout == 0
548
+ result = ''
549
+ e = ::Time.now + timeout
550
+ while result.size < len
551
+ now = ::Time.now
552
+ raise Errno::ETIMEDOUT if now > e
553
+ r = @socket.read_nonblock(len - result.size, exception: false)
554
+ case r
555
+ when :wait_readable
556
+ IO.select([@socket], nil, nil, e - now)
557
+ next
558
+ when :wait_writable
559
+ IO.select(nil, [@socket], nil, e - now)
560
+ next
561
+ else
562
+ result << r
563
+ end
564
+ end
565
+ return result
566
+ end
567
+
511
568
  # Write one packet data
512
569
  # === Argument
513
570
  # data :: [String / IO] packet data. If data is nil, write empty packet.
514
571
  def write(data)
515
572
  begin
516
- @sock.sync = false
573
+ @socket.sync = false
517
574
  if data.nil?
518
- Timeout.timeout @write_timeout do
519
- @sock.write [0, 0, @seq].pack("CvC")
520
- end
575
+ write_timeout([0, 0, @seq].pack("CvC"), @opts[:write_timeout])
521
576
  @seq = (@seq + 1) % 256
522
577
  else
523
578
  data = StringIO.new data if data.is_a? String
524
579
  while d = data.read(MAX_PACKET_LENGTH)
525
- Timeout.timeout @write_timeout do
526
- @sock.write [d.length%256, d.length/256, @seq].pack("CvC")
527
- @sock.write d
528
- end
580
+ write_timeout([d.length%256, d.length/256, @seq].pack("CvC")+d, @opts[:write_timeout])
529
581
  @seq = (@seq + 1) % 256
530
582
  end
531
583
  end
532
- @sock.sync = true
533
- Timeout.timeout @write_timeout do
534
- @sock.flush
535
- end
584
+ @socket.sync = true
585
+ @socket.flush
536
586
  rescue Errno::EPIPE
537
587
  raise ClientError::ServerGoneError, 'MySQL server has gone away'
538
- rescue Timeout::Error
588
+ rescue Errno::ETIMEDOUT
539
589
  raise ClientError, "write timeout"
540
590
  end
541
591
  end
542
592
 
593
+ def write_timeout(data, timeout)
594
+ return @socket.write(data) if timeout.nil? || timeout == 0
595
+ len = 0
596
+ e = ::Time.now + timeout
597
+ while len < data.size
598
+ now = ::Time.now
599
+ raise Errno::ETIMEDOUT if now > e
600
+ l = @socket.write_nonblock(data[len..-1], exception: false)
601
+ case l
602
+ when :wait_readable
603
+ IO.select([@socket], nil, nil, e - now)
604
+ when :wait_writable
605
+ IO.select(nil, [@socket], nil, e - now)
606
+ else
607
+ len += l
608
+ end
609
+ end
610
+ return len
611
+ end
612
+
543
613
  # Read EOF packet
544
614
  # === Exception
545
615
  # [ProtocolError] packet is not EOF
@@ -560,19 +630,6 @@ class Mysql
560
630
  end
561
631
  end
562
632
 
563
- # Encrypt password
564
- # === Argument
565
- # plain :: [String] plain password.
566
- # scramble :: [String] scramble code from initial packet.
567
- # === Return
568
- # [String] encrypted password
569
- def encrypt_password(plain, scramble)
570
- return "" if plain.nil? or plain.empty?
571
- hash_stage1 = Digest::SHA1.digest plain
572
- hash_stage2 = Digest::SHA1.digest hash_stage1
573
- return hash_stage1.unpack("C*").zip(Digest::SHA1.digest(scramble+hash_stage2).unpack("C*")).map{|a,b| a^b}.pack("C*")
574
- end
575
-
576
633
  # Initial packet
577
634
  class InitialPacket
578
635
  def self.parse(pkt)
@@ -584,18 +641,26 @@ class Mysql
584
641
  server_capabilities = pkt.ushort
585
642
  server_charset = pkt.utiny
586
643
  server_status = pkt.ushort
587
- f1 = pkt.read(13)
644
+ server_capabilities2 = pkt.ushort
645
+ scramble_length = pkt.utiny
646
+ _f1 = pkt.read(10)
588
647
  rest_scramble_buff = pkt.string
648
+ auth_plugin = pkt.string
649
+
650
+ server_capabilities |= server_capabilities2 << 16
651
+ scramble_buff.concat rest_scramble_buff
652
+
589
653
  raise ProtocolError, "unsupported version: #{protocol_version}" unless protocol_version == VERSION
590
654
  raise ProtocolError, "invalid packet: f0=#{f0}" unless f0 == 0
591
- scramble_buff.concat rest_scramble_buff
592
- self.new protocol_version, server_version, thread_id, server_capabilities, server_charset, server_status, scramble_buff
655
+ raise ProtocolError, "invalid packet: scramble_length(#{scramble_length}) != length of scramble(#{scramble_buff.size + 1})" unless scramble_length == scramble_buff.size + 1
656
+
657
+ self.new protocol_version, server_version, thread_id, server_capabilities, server_charset, server_status, scramble_buff, auth_plugin
593
658
  end
594
659
 
595
- attr_reader :protocol_version, :server_version, :thread_id, :server_capabilities, :server_charset, :server_status, :scramble_buff
660
+ attr_reader :protocol_version, :server_version, :thread_id, :server_capabilities, :server_charset, :server_status, :scramble_buff, :auth_plugin
596
661
 
597
662
  def initialize(*args)
598
- @protocol_version, @server_version, @thread_id, @server_capabilities, @server_charset, @server_status, @scramble_buff = args
663
+ @protocol_version, @server_version, @thread_id, @server_capabilities, @server_charset, @server_status, @scramble_buff, @auth_plugin = args
599
664
  end
600
665
  end
601
666
 
@@ -627,13 +692,13 @@ class Mysql
627
692
  # Field packet
628
693
  class FieldPacket
629
694
  def self.parse(pkt)
630
- first = pkt.lcs
695
+ _first = pkt.lcs
631
696
  db = pkt.lcs
632
697
  table = pkt.lcs
633
698
  org_table = pkt.lcs
634
699
  name = pkt.lcs
635
700
  org_name = pkt.lcs
636
- f0 = pkt.utiny
701
+ _f0 = pkt.utiny
637
702
  charsetnr = pkt.ushort
638
703
  length = pkt.ulong
639
704
  type = pkt.utiny
@@ -675,16 +740,35 @@ class Mysql
675
740
 
676
741
  # Authentication packet
677
742
  class AuthenticationPacket
678
- def self.serialize(client_flags, max_packet_size, charset_number, username, scrambled_password, databasename)
679
- [
743
+ def self.serialize(client_flags, max_packet_size, charset_number, username, scrambled_password, databasename, auth_plugin)
744
+ data = [
680
745
  client_flags,
681
746
  max_packet_size,
682
- Packet.lcb(charset_number),
747
+ charset_number,
683
748
  "", # always 0x00 * 23
684
749
  username,
685
750
  Packet.lcs(scrambled_password),
686
- databasename
687
- ].pack("VVa*a23Z*A*Z*")
751
+ ]
752
+ pack = "VVCa23Z*A*"
753
+ if databasename
754
+ data.push databasename
755
+ pack.concat "Z*"
756
+ end
757
+ data.push auth_plugin
758
+ pack.concat "Z*"
759
+ data.pack(pack)
760
+ end
761
+ end
762
+
763
+ # TLS Authentication packet
764
+ class TlsAuthenticationPacket
765
+ def self.serialize(client_flags, max_packet_size, charset_number)
766
+ [
767
+ client_flags,
768
+ max_packet_size,
769
+ charset_number,
770
+ "", # always 0x00 * 23
771
+ ].pack("VVCa23")
688
772
  end
689
773
  end
690
774
 
@@ -712,6 +796,21 @@ class Mysql
712
796
  end
713
797
 
714
798
  end
799
+
800
+ class AuthenticationResultPacket
801
+ def self.parse(pkt)
802
+ result = pkt.utiny
803
+ auth_plugin = pkt.string
804
+ scramble = pkt.string
805
+ self.new(result, auth_plugin, scramble)
806
+ end
807
+
808
+ attr_reader :result, :auth_plugin, :scramble
809
+
810
+ def initialize(*args)
811
+ @result, @auth_plugin, @scramble = args
812
+ end
813
+ end
715
814
  end
716
815
 
717
816
  class RawRecord
data/lib/mysql.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # coding: ascii-8bit
2
- # Copyright (C) 2008-2012 TOMITA Masahiro
2
+ # Copyright (C) 2008 TOMITA Masahiro
3
3
  # mailto:tommy@tmtm.org
4
4
 
5
5
  # MySQL connection class.
@@ -16,12 +16,8 @@ class Mysql
16
16
  require "mysql/charset"
17
17
  require "mysql/protocol"
18
18
  require "mysql/packet.rb"
19
- begin
20
- require "mysql/ext.so"
21
- rescue LoadError
22
- end
23
19
 
24
- VERSION = 20912 # Version number of this library
20
+ VERSION = 21100 # Version number of this library
25
21
  MYSQL_UNIX_PORT = "/tmp/mysql.sock" # UNIX domain socket filename
26
22
  MYSQL_TCP_PORT = 3306 # TCP socket port number
27
23
 
@@ -86,16 +82,20 @@ class Mysql
86
82
  @fields = nil
87
83
  @protocol = nil
88
84
  @charset = nil
89
- @connect_timeout = nil
90
- @read_timeout = nil
91
- @write_timeout = nil
92
85
  @init_command = nil
93
86
  @sqlstate = "00000"
94
87
  @query_with_result = true
95
88
  @host_info = nil
96
89
  @last_error = nil
97
90
  @result_exist = false
98
- @local_infile = nil
91
+ @opts = {
92
+ connect_timeout: nil,
93
+ read_timeout: nil,
94
+ write_timeout: nil,
95
+ local_infile: nil,
96
+ ssl_mode: SSL_MODE_PREFERRED,
97
+ get_server_public_key: false,
98
+ }
99
99
  end
100
100
 
101
101
  # Connect to mysqld.
@@ -112,8 +112,8 @@ class Mysql
112
112
  warn 'unsupported flag: CLIENT_COMPRESS' if $VERBOSE
113
113
  flag &= ~CLIENT_COMPRESS
114
114
  end
115
- @protocol = Protocol.new host, port, socket, @connect_timeout, @read_timeout, @write_timeout
116
- @protocol.authenticate user, passwd, db, (@local_infile ? CLIENT_LOCAL_FILES : 0) | flag, @charset
115
+ @protocol = Protocol.new(host, port, socket, @opts)
116
+ @protocol.authenticate user, passwd, db, flag, @charset
117
117
  @charset ||= @protocol.charset
118
118
  @host_info = (host.nil? || host == "localhost") ? 'Localhost via UNIX socket' : "#{host} via TCP/IP"
119
119
  query @init_command if @init_command
@@ -144,36 +144,64 @@ class Mysql
144
144
  # Set option for connection.
145
145
  #
146
146
  # Available options:
147
- # Mysql::INIT_COMMAND, Mysql::OPT_CONNECT_TIMEOUT, Mysql::OPT_READ_TIMEOUT,
148
- # Mysql::OPT_WRITE_TIMEOUT, Mysql::SET_CHARSET_NAME
147
+ # Mysql::INIT_COMMAND, Mysql::OPT_CONNECT_TIMEOUT, Mysql::OPT_GET_SERVER_PUBLIC_KEY,
148
+ # Mysql::OPT_LOAD_DATA_LOCAL_DIR, Mysql::OPT_LOCAL_INFILE, Mysql::OPT_READ_TIMEOUT,
149
+ # Mysql::OPT_SSL_MODE, Mysql::OPT_WRITE_TIMEOUT, Mysql::SET_CHARSET_NAME
149
150
  # @param [Integer] opt option
150
151
  # @param [Integer] value option value that is depend on opt
151
152
  # @return [Mysql] self
152
153
  def options(opt, value=nil)
153
154
  case opt
155
+ # when Mysql::DEFAULT_AUTH
156
+ # when Mysql::ENABLE_CLEARTEXT_PLUGIN
154
157
  when Mysql::INIT_COMMAND
155
158
  @init_command = value.to_s
159
+ # when Mysql::OPT_BIND
160
+ # when Mysql::OPT_CAN_HANDLE_EXPIRED_PASSWORDS
156
161
  # when Mysql::OPT_COMPRESS
162
+ # when Mysql::OPT_COMPRESSION_ALGORITHMS
163
+ # when Mysql::OPT_CONNECT_ATTR_ADD
164
+ # when Mysql::OPT_CONNECT_ATTR_DELETE
165
+ # when Mysql::OPT_CONNECT_ATTR_RESET
157
166
  when Mysql::OPT_CONNECT_TIMEOUT
158
- @connect_timeout = value
159
- # when Mysql::GUESS_CONNECTION
167
+ @opts[:connect_timeout] = value
168
+ when Mysql::OPT_GET_SERVER_PUBLIC_KEY
169
+ @opts[:get_server_public_key] = value
170
+ when Mysql::OPT_LOAD_DATA_LOCAL_DIR
171
+ @opts[:local_infile] = value
160
172
  when Mysql::OPT_LOCAL_INFILE
161
- @local_infile = value
173
+ @opts[:local_infile] = value ? '' : nil
174
+ # when Mysql::OPT_MAX_ALLOWED_PACKET
162
175
  # when Mysql::OPT_NAMED_PIPE
176
+ # when Mysql::OPT_NET_BUFFER_LENGTH
177
+ # when Mysql::OPT_OPTIONAL_RESULTSET_METADATA
163
178
  # when Mysql::OPT_PROTOCOL
164
179
  when Mysql::OPT_READ_TIMEOUT
165
- @read_timeout = value.to_i
180
+ @opts[:read_timeout] = value
166
181
  # when Mysql::OPT_RECONNECT
182
+ # when Mysql::OPT_RETRY_COUNT
167
183
  # when Mysql::SET_CLIENT_IP
168
- # when Mysql::OPT_SSL_VERIFY_SERVER_CERT
169
- # when Mysql::OPT_USE_EMBEDDED_CONNECTION
170
- # when Mysql::OPT_USE_REMOTE_CONNECTION
184
+ # when Mysql::OPT_SSL_CA
185
+ # when Mysql::OPT_SSL_CAPATH
186
+ # when Mysql::OPT_SSL_CERT
187
+ # when Mysql::OPT_SSL_CIPHER
188
+ # when Mysql::OPT_SSL_CRL
189
+ # when Mysql::OPT_SSL_CRLPATH
190
+ # when Mysql::OPT_SSL_FIPS_MODE
191
+ # when Mysql::OPT_SSL_KEY
192
+ when Mysql::OPT_SSL_MODE
193
+ @opts[:ssl_mode] = value
194
+ # when Mysql::OPT_TLS_CIPHERSUITES
195
+ # when Mysql::OPT_TLS_VERSION
196
+ # when Mysql::OPT_USE_RESULT
171
197
  when Mysql::OPT_WRITE_TIMEOUT
172
- @write_timeout = value.to_i
198
+ @opts[:write_timeout] = value
199
+ # when Mysql::OPT_ZSTD_COMPRESSION_LEVEL
200
+ # when Mysql::PLUGIN_DIR
173
201
  # when Mysql::READ_DEFAULT_FILE
174
202
  # when Mysql::READ_DEFAULT_GROUP
175
203
  # when Mysql::REPORT_DATA_TRUNCATION
176
- # when Mysql::SECURE_AUTH
204
+ # when Mysql::SERVER_PUBLIC_KEY
177
205
  # when Mysql::SET_CHARSET_DIR
178
206
  when Mysql::SET_CHARSET_NAME
179
207
  @charset = Charset.by_name value.to_s
@@ -186,14 +214,9 @@ class Mysql
186
214
 
187
215
  # Escape special character in MySQL.
188
216
  #
189
- # In Ruby 1.8, this is not safe for multibyte charset such as 'SJIS'.
190
- # You should use place-holder in prepared-statement.
191
217
  # @param [String] str
192
218
  # return [String]
193
219
  def escape_string(str)
194
- if not defined? Encoding and @charset.unsafe
195
- raise ClientError, 'Mysql#escape_string is called for unsafe multibyte charset'
196
- end
197
220
  self.class.escape_string str
198
221
  end
199
222
  alias quote escape_string
@@ -439,7 +462,6 @@ class Mysql
439
462
  store_result
440
463
  end
441
464
 
442
- # @note for Ruby 1.8: This is not multi-byte safe. Don't use for multi-byte charset such as cp932.
443
465
  # @param [String] table database name that may contain wild card.
444
466
  # @return [Array<String>] list of table name.
445
467
  def list_tables(table=nil)
@@ -731,8 +753,8 @@ class Mysql
731
753
  max_length = Array.new(@fields.size, 0)
732
754
  @records.each_with_index do |rec, i|
733
755
  rec = @records[i] = rec.to_a if rec.is_a? RawRecord
734
- max_length.each_index do |i|
735
- max_length[i] = rec[i].length if rec[i] && rec[i].length > max_length[i]
756
+ max_length.each_index do |j|
757
+ max_length[j] = rec[j].length if rec[j] && rec[j].length > max_length[j]
736
758
  end
737
759
  end
738
760
  max_length.each_with_index do |len, i|
@@ -900,7 +922,7 @@ class Mysql
900
922
  row.zip(@bind_result).map do |col, type|
901
923
  if col.nil?
902
924
  nil
903
- elsif [Numeric, Integer, Fixnum].include? type
925
+ elsif [Numeric, Integer].include? type
904
926
  col.to_i
905
927
  elsif type == String
906
928
  col.to_s
@@ -953,7 +975,7 @@ class Mysql
953
975
  raise ClientError, "bind_result: result value count(#{@fields.length}) != number of argument(#{args.length})"
954
976
  end
955
977
  args.each do |a|
956
- raise TypeError unless [Numeric, Fixnum, Integer, Float, String, Mysql::Time, nil].include? a
978
+ raise TypeError unless [Numeric, Integer, Float, String, Mysql::Time, nil].include? a
957
979
  end
958
980
  @bind_result = args
959
981
  self