ruby-mysql 3.0.1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 37cc4956fc000b612b24fe104acf96abf27e0a7016cbd05c2c5e7b4ae5e0bade
4
- data.tar.gz: b2c4ded73325a5c4aadce1d81af507e2a7d4144764150fde183f0e541e25e8b8
3
+ metadata.gz: 13656e5ef8a79113c2dce1fac6f1a47e510c2cd6bfb9958518a966b349bdbb13
4
+ data.tar.gz: 55a2f5e70d73add2e6d71eb0bb02e19d0d498cacd46c78f0415f8f31620c76ab
5
5
  SHA512:
6
- metadata.gz: c81163849e312cb8cb861a1c5add93fba0973d1fbf01b9296513914931134230dbf7d2d6437e72097c8426d24daab316c2756621d3302d5ee91efb72ab951cc3
7
- data.tar.gz: c4e13cea550e4659db986772663b04364c8c18f51abc9442516058cbe5ebc420dd19a95e324e149ae0966edefa1259fff9274677295849834029f827667547c2
6
+ metadata.gz: a01515533387aaa8280be8c5244b80d4b245052e2750732f03c6198827dd5975a32f8f472481ba44b14f731135b31aa3744180475444388c7e1158d49bf95556
7
+ data.tar.gz: 53fe3127e8c358f77a3874e26ae1de91dcd72af11fcd45f40b18875c21a884d21105eccce4808b308bde1e938970316cf6d7f54ac21f9bf94cebb9fc2b8dcbd5
data/CHANGELOG.md CHANGED
@@ -1,3 +1,53 @@
1
+ ## [4.0.0] - 2022-11-09
2
+
3
+ ### Incompatible changes
4
+
5
+ - unsupport MySQL 5.5 and Ruby 2.5
6
+ - `Mysql::Result#fetch` returns converted values unless `cast` is false
7
+ - `Mysql::Result#each` always returns records from the beginning
8
+ - Retrieving a DECIMAL type with a prepared statement returns a `BigDecimal` object instead of `String`
9
+ - Retrieving a DATE type value with a prepared statement returns a `Date` object instead of `Time`
10
+ - delete `Mysql#more_results`. use `#more_results?` instead.
11
+ - remove `Mysql::Result#fetch_field`, `#field_tell`, `#field_seek`, `#fetch_field_direct`, `#fetch_lengths`, `#num_fields`.
12
+ - error 'command out of sync' is `Mysql::ClientError::CommandOutOfSync` instead of `RuntimeError`.
13
+ - error 'Authentication requires secure connection' is `Mysql::ClientError::AuthPluginErr` instead of `RuntimeError`.
14
+
15
+ ### Features
16
+
17
+ - `Mysql.default_options` is global options.
18
+ - `Mysql#connect` option `ssl_mode`: support `SSL_MODE_VERIFY_CA`, `SSL_MODE_VERIFY_IDENTITY`.
19
+ - `Mysql#connect` option `ssl_context_params`: see `OpenSSL::SSL::SSLContext#set_params`.
20
+ - `Mysql#connect` option `connect_attrs`.
21
+ - `Mysql::Stmt#more_results?`, `#next_result`, `#info`.
22
+ - `Mysql#close` and `Mysql::Stmt#close` read pending packets.
23
+ - `Mysql#query` and `Mysql::Stmt#execute` option: `return_result` and `yield_null_result`.
24
+ - support session tracking. See https://dev.mysql.com/doc/refman/8.0/en/session-state-tracking.html
25
+ - thread safe.
26
+ - `Mysql#query`, `Mysql::Stmt#execute` option: `auto_store_result`.
27
+ - add `Mysql::Result#server_status`
28
+
29
+ ### Fixes
30
+
31
+ - When using connection that disconnected from client. error 'MySQL client is not connected' is occured instead of 'MySQL server has gone away'.
32
+ - When SSL error, `MySQL::ClientError::ServerLost` or `ServerGoneError` is occured instead of `OpenSSL::SSL::SSLError`.
33
+ - `Mysql#server_version` don't require connection.
34
+ - use `connect_timeout` instead of `read/write_timeout` on initial negotiation.
35
+ - enable to changing `local_infile` for established connection.
36
+ - `Mysql.connect` with host nil ignores parameters
37
+ - raises `IOError` after `Mysql#close`
38
+ - Fractional seconds of time types were ignored when retrieving values using prepared statements
39
+ - `Mysql::Stmt#execute` allows true or false values.
40
+
41
+ ### Others
42
+
43
+ - Mysql#prpare raises Mysql::ClientError if the connection is not connected.
44
+ - using rubocop
45
+ - migrate from GitHub to GitLab
46
+ - ignore Gemfile.lock
47
+ - use rspec instead of test-unit
48
+ - using connection parameter from spec/config.rb for testing
49
+ - split files
50
+
1
51
  ## [3.0.1] - 2022-06-18
2
52
 
3
53
  - LICENSE: correct author
@@ -2,6 +2,7 @@ require 'digest/sha2'
2
2
 
3
3
  class Mysql
4
4
  class Authenticator
5
+ # caching_sha2_password
5
6
  class CachingSha2Password
6
7
  # @param protocol [Mysql::Protocol]
7
8
  def initialize(protocol)
@@ -29,7 +30,7 @@ class Mysql
29
30
  if @protocol.client_flags & CLIENT_SSL != 0
30
31
  @protocol.write passwd+"\0"
31
32
  elsif !@protocol.get_server_public_key
32
- raise 'Authentication requires secure connection'
33
+ raise ClientError::AuthPluginErr, 'Authentication requires secure connection'
33
34
  else
34
35
  @protocol.write "\2" # request public key
35
36
  pkt = @protocol.read
@@ -40,7 +41,7 @@ class Mysql
40
41
  @protocol.write enc
41
42
  end
42
43
  else
43
- raise "invalid auth reply packet: #{data.inspect}"
44
+ raise ClientError, "invalid auth reply packet: #{data.inspect}"
44
45
  end
45
46
  pkt = @protocol.read
46
47
  end
@@ -2,6 +2,7 @@ require 'digest/sha1'
2
2
 
3
3
  class Mysql
4
4
  class Authenticator
5
+ # mysql_native_password
5
6
  class MysqlNativePassword
6
7
  # @param protocol [Mysql::Protocol]
7
8
  def initialize(protocol)
@@ -2,6 +2,7 @@ require 'openssl'
2
2
 
3
3
  class Mysql
4
4
  class Authenticator
5
+ # sha256_password
5
6
  class Sha256Password
6
7
  # @param protocol [Mysql::Protocol]
7
8
  def initialize(protocol)
@@ -1,4 +1,5 @@
1
1
  class Mysql
2
+ # authenticator
2
3
  class Authenticator
3
4
  @plugins = {}
4
5
 
@@ -33,10 +34,10 @@ class Mysql
33
34
  get(plugin) or raise ClientError, "unknown plugin: #{plugin}"
34
35
  end
35
36
 
36
- def authenticate(user, passwd, db, scramble, plugin_name)
37
+ def authenticate(user, passwd, db, scramble, plugin_name, connect_attrs)
37
38
  plugin = (get(plugin_name) || DummyPlugin).new(@protocol)
38
39
  pkt = plugin.authenticate(passwd, scramble) do |hashed|
39
- @protocol.write Protocol::AuthenticationPacket.serialize(@protocol.client_flags, 1024**3, @protocol.charset.number, user, hashed, db, plugin.name)
40
+ @protocol.write Protocol::AuthenticationPacket.serialize(@protocol.client_flags, 1024**3, @protocol.charset.number, user, hashed, db, plugin.name, connect_attrs)
40
41
  end
41
42
  while true
42
43
  res = Protocol::AuthenticationResultPacket.parse(pkt)
@@ -55,11 +56,12 @@ class Mysql
55
56
  end
56
57
  end
57
58
  else
58
- raise ClientError, "invalid packet: #{pkt.to_s}"
59
+ raise ClientError, "invalid packet: #{pkt}"
59
60
  end
60
61
  end
61
62
  end
62
63
 
64
+ # dummy plugin
63
65
  class DummyPlugin
64
66
  # @param protocol [Mysql::Protocol]
65
67
  def initialize(protocol)
@@ -75,7 +77,7 @@ class Mysql
75
77
  # @param scramble [String]
76
78
  # @yield [String] hashed password
77
79
  # @return [Mysql::Packet]
78
- def authenticate(passwd, scramble)
80
+ def authenticate(passwd, scramble) # rubocop:disable Lint/UnusedMethodArgument
79
81
  yield ''
80
82
  @protocol.read
81
83
  end
data/lib/mysql/charset.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # coding: ascii-8bit
2
+
2
3
  # Copyright (C) 2008-2012 TOMITA Masahiro
3
4
  # mailto:tommy@tmtm.org
4
5
 
5
- #
6
6
  class Mysql
7
7
  # @!attribute [r] number
8
8
  # @private
@@ -327,14 +327,14 @@ class Mysql
327
327
  [307, "utf8mb4", "utf8mb4_ru_0900_as_cs", false],
328
328
  [308, "utf8mb4", "utf8mb4_zh_0900_as_cs", false],
329
329
  [309, "utf8mb4", "utf8mb4_0900_bin", false],
330
- ]
330
+ ].freeze
331
331
 
332
332
  # @private
333
- NUMBER_TO_CHARSET = {}
333
+ NUMBER_TO_CHARSET = {} # rubocop:disable Style/MutableConstant
334
334
  # @private
335
- COLLATION_TO_CHARSET = {}
335
+ COLLATION_TO_CHARSET = {} # rubocop:disable Style/MutableConstant
336
336
  # @private
337
- CHARSET_DEFAULT = {}
337
+ CHARSET_DEFAULT = {} # rubocop:disable Style/MutableConstant
338
338
  CHARSETS.each do |number, csname, clname, default|
339
339
  cs = Charset.new number, csname, clname
340
340
  NUMBER_TO_CHARSET[number] = cs
@@ -407,7 +407,7 @@ class Mysql
407
407
  "utf8" => Encoding::UTF_8,
408
408
  "utf8mb3" => Encoding::UTF_8,
409
409
  "utf8mb4" => Encoding::UTF_8,
410
- }
410
+ }.freeze
411
411
 
412
412
  # @private
413
413
  # @param [String] value
@@ -1,4 +1,5 @@
1
1
  # coding: ascii-8bit
2
+
2
3
  # Copyright (C) 2003 TOMITA Masahiro
3
4
  # mailto:tommy@tmtm.org
4
5
 
@@ -168,6 +169,13 @@ class Mysql
168
169
  REFRESH_OPTIMIZER_COSTS = 1 << 21
169
170
  REFRESH_PERSIST = 1 << 22
170
171
 
172
+ SESSION_TRACK_SYSTEM_VARIABLES = 0 # Session system variables
173
+ SESSION_TRACK_SCHEMA = 1 # Current schema
174
+ SESSION_TRACK_STATE_CHANGE = 2 # track session state changes
175
+ SESSION_TRACK_GTIDS = 3 # See also: session_track_gtids
176
+ SESSION_TRACK_TRANSACTION_CHARACTERISTICS = 4 # Transaction chistics
177
+ SESSION_TRACK_TRANSACTION_STATE = 5 # Transaction state
178
+
171
179
  class Field
172
180
  # Field type
173
181
  TYPE_DECIMAL = 0
data/lib/mysql/error.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  # coding: ascii-8bit
2
+
2
3
  # Copyright (C) 2003-2010 TOMITA Masahiro
3
4
  # mailto:tommy@tmtm.org
4
5
 
6
+ # Mysql
5
7
  class Mysql
8
+ # Mysql::Error
6
9
  class Error < StandardError
7
10
  ERRNO = 0
8
11
 
@@ -11,7 +14,7 @@ class Mysql
11
14
  errname = errname.to_s
12
15
  next unless errname =~ prefix_re
13
16
  errno = self.const_get errname
14
- excname = errname.sub(prefix_re,'').gsub(/(\A.|_.)([A-Z]+)/){$1+$2.downcase}.gsub(/_/,'')
17
+ excname = errname.sub(prefix_re, '').gsub(/(\A.|_.)([A-Z]+)/){$1+$2.downcase}.gsub(/_/, '')
15
18
  klass = Class.new self
16
19
  klass.const_set 'ERRNO', errno
17
20
  self.const_set excname, klass
@@ -32,7 +35,7 @@ class Mysql
32
35
 
33
36
  # server side error
34
37
  class ServerError < Error
35
- ERROR_MAP = {}
38
+ ERROR_MAP = {} # rubocop:disable Style/MutableConstant
36
39
 
37
40
  ER_ERROR_FIRST = 1000
38
41
  ER_HASHCHK = 1000
@@ -905,11 +908,11 @@ class Mysql
905
908
  end
906
909
 
907
910
  ServerError.define_error_class(/\AER_/)
908
- ServerError::ERROR_MAP.values.each{|v| Mysql.const_set v.name.split(/::/).last, v} # for compatibility
911
+ ServerError::ERROR_MAP.each_value{|v| Mysql.const_set v.name.split(/::/).last, v} # for compatibility
909
912
 
910
913
  # client side error
911
914
  class ClientError < Error
912
- ERROR_MAP = {}
915
+ ERROR_MAP = {} # rubocop:disable Style/MutableConstant
913
916
 
914
917
  CR_ERROR_FIRST = 2000
915
918
  CR_UNKNOWN_ERROR = 2000
@@ -991,5 +994,4 @@ class Mysql
991
994
  # protocol error
992
995
  class ProtocolError < ClientError
993
996
  end
994
-
995
997
  end
@@ -0,0 +1,95 @@
1
+ class Mysql
2
+ # @!visibility public
3
+ # Field class
4
+ class Field
5
+ # @return [String] database name
6
+ attr_reader :db
7
+ # @return [String] table name
8
+ attr_reader :table
9
+ # @return [String] original table name
10
+ attr_reader :org_table
11
+ # @return [String] field name
12
+ attr_reader :name
13
+ # @return [String] original field name
14
+ attr_reader :org_name
15
+ # @return [Integer] charset id number
16
+ attr_reader :charsetnr
17
+ # @return [Integer] field length
18
+ attr_reader :length
19
+ # @return [Integer] field type
20
+ attr_reader :type
21
+ # @return [Integer] flag
22
+ attr_reader :flags
23
+ # @return [Integer] number of decimals
24
+ attr_reader :decimals
25
+ # @return [String] defualt value
26
+ attr_reader :default
27
+ alias def default
28
+
29
+ # @private
30
+ attr_accessor :result
31
+
32
+ # @attr [Protocol::FieldPacket] packet
33
+ def initialize(packet)
34
+ @db, @table, @org_table, @name, @org_name, @charsetnr, @length, @type, @flags, @decimals, @default =
35
+ packet.db, packet.table, packet.org_table, packet.name, packet.org_name, packet.charsetnr, packet.length, packet.type, packet.flags, packet.decimals, packet.default
36
+ @db.force_encoding('utf-8')
37
+ @table.force_encoding('utf-8')
38
+ @org_table.force_encoding('utf-8')
39
+ @name.force_encoding('utf-8')
40
+ @org_name.force_encoding('utf-8')
41
+ @flags |= NUM_FLAG if is_num_type?
42
+ @max_length = nil
43
+ end
44
+
45
+ # @return [Hash] field information
46
+ def to_hash
47
+ {
48
+ "name" => @name,
49
+ "table" => @table,
50
+ "def" => @default,
51
+ "type" => @type,
52
+ "length" => @length,
53
+ "max_length" => max_length,
54
+ "flags" => @flags,
55
+ "decimals" => @decimals,
56
+ }
57
+ end
58
+
59
+ # @private
60
+ def inspect
61
+ "#<Mysql::Field:#{@name}>"
62
+ end
63
+
64
+ # @return [Boolean] true if numeric field.
65
+ def is_num?
66
+ @flags & NUM_FLAG != 0
67
+ end
68
+
69
+ # @return [Boolean] true if not null field.
70
+ def is_not_null?
71
+ @flags & NOT_NULL_FLAG != 0
72
+ end
73
+
74
+ # @return [Boolean] true if primary key field.
75
+ def is_pri_key?
76
+ @flags & PRI_KEY_FLAG != 0
77
+ end
78
+
79
+ # @return [Integer] maximum width of the field for the result set
80
+ def max_length
81
+ return @max_length if @max_length
82
+ @max_length = 0
83
+ @result&.calculate_field_max_length
84
+ @max_length
85
+ end
86
+
87
+ attr_writer :max_length
88
+
89
+ private
90
+
91
+ def is_num_type?
92
+ [TYPE_DECIMAL, TYPE_TINY, TYPE_SHORT, TYPE_LONG, TYPE_FLOAT, TYPE_DOUBLE, TYPE_LONGLONG, TYPE_INT24].include?(@type) || (@type == TYPE_TIMESTAMP && (@length == 14 || @length == 8))
93
+ end
94
+ end
95
+ end
data/lib/mysql/packet.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # coding: ascii-8bit
2
+
2
3
  class Mysql
4
+ # Mysql::Packet
3
5
  class Packet
4
6
  # convert Numeric to LengthCodedBinary
5
7
  def self.lcb(num)
@@ -49,21 +51,21 @@ class Mysql
49
51
  end
50
52
 
51
53
  def string
52
- str = @data.unpack('Z*').first
54
+ str = @data.unpack1('Z*')
53
55
  @data.slice!(0, str.length+1)
54
56
  str
55
57
  end
56
58
 
57
59
  def utiny
58
- @data.slice!(0, 1).unpack('C').first
60
+ @data.slice!(0, 1).unpack1('C')
59
61
  end
60
62
 
61
63
  def ushort
62
- @data.slice!(0, 2).unpack('v').first
64
+ @data.slice!(0, 2).unpack1('v')
63
65
  end
64
66
 
65
67
  def ulong
66
- @data.slice!(0, 4).unpack('V').first
68
+ @data.slice!(0, 4).unpack1('V')
67
69
  end
68
70
 
69
71
  def eof?
@@ -73,6 +75,5 @@ class Mysql
73
75
  def to_s
74
76
  @data
75
77
  end
76
-
77
78
  end
78
79
  end