ruby-mysql 3.0.1 → 4.0.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.
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