ruby-mysql 2.9.12 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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