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 +4 -4
- data/CHANGELOG.md +50 -0
- data/lib/mysql/authenticator/caching_sha2_password.rb +3 -2
- data/lib/mysql/authenticator/mysql_native_password.rb +1 -0
- data/lib/mysql/authenticator/sha256_password.rb +1 -0
- data/lib/mysql/authenticator.rb +6 -4
- data/lib/mysql/charset.rb +6 -6
- data/lib/mysql/constants.rb +8 -0
- data/lib/mysql/error.rb +7 -5
- data/lib/mysql/field.rb +95 -0
- data/lib/mysql/packet.rb +6 -5
- data/lib/mysql/protocol.rb +294 -190
- data/lib/mysql/result.rb +212 -0
- data/lib/mysql/stmt.rb +219 -0
- data/lib/mysql.rb +109 -523
- metadata +56 -14
- data/test/test_mysql.rb +0 -1603
- data/test/test_mysql_packet.rb +0 -149
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13656e5ef8a79113c2dce1fac6f1a47e510c2cd6bfb9958518a966b349bdbb13
|
4
|
+
data.tar.gz: 55a2f5e70d73add2e6d71eb0bb02e19d0d498cacd46c78f0415f8f31620c76ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/mysql/authenticator.rb
CHANGED
@@ -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
|
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
|
data/lib/mysql/constants.rb
CHANGED
@@ -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.
|
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
|
data/lib/mysql/field.rb
ADDED
@@ -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.
|
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).
|
60
|
+
@data.slice!(0, 1).unpack1('C')
|
59
61
|
end
|
60
62
|
|
61
63
|
def ushort
|
62
|
-
@data.slice!(0, 2).
|
64
|
+
@data.slice!(0, 2).unpack1('v')
|
63
65
|
end
|
64
66
|
|
65
67
|
def ulong
|
66
|
-
@data.slice!(0, 4).
|
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
|