ruby-mysql 3.0.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +58 -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 +296 -189
- data/lib/mysql/result.rb +212 -0
- data/lib/mysql/stmt.rb +219 -0
- data/lib/mysql.rb +109 -523
- metadata +57 -15
- data/test/test_mysql.rb +0 -1566
- 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,61 @@
|
|
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
|
+
|
51
|
+
## [3.0.1] - 2022-06-18
|
52
|
+
|
53
|
+
- LICENSE: correct author
|
54
|
+
- FIX: correct LOAD DATA LOCAL INFILE result information.
|
55
|
+
- FIX: reset SERVER_MORE_RESULTS_EXISTS when error packet is received.
|
56
|
+
- FIX: close the socket when the connection is disconnected.
|
57
|
+
- FIX: allow multiple results by default.
|
58
|
+
|
1
59
|
## [3.0.0] - 2021-11-16
|
2
60
|
|
3
61
|
- `Mysql.new` no longer connect. use `Mysql.connect` or `Mysql#connect`.
|
@@ -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
|