ruby-mysql2 0.5.4
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 +7 -0
- data/CHANGELOG.md +1 -0
- data/LICENSE +21 -0
- data/README.md +20 -0
- data/README.org.md +676 -0
- data/lib/mysql2/client.rb +461 -0
- data/lib/mysql2/console.rb +5 -0
- data/lib/mysql2/error.rb +101 -0
- data/lib/mysql2/field.rb +3 -0
- data/lib/mysql2/result.rb +176 -0
- data/lib/mysql2/statement.rb +103 -0
- data/lib/mysql2/version.rb +3 -0
- data/lib/mysql2.rb +87 -0
- metadata +102 -0
@@ -0,0 +1,176 @@
|
|
1
|
+
module Mysql2
|
2
|
+
# Mysql2::Result
|
3
|
+
class Result
|
4
|
+
attr_reader :server_flags
|
5
|
+
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# @param result [Mysql::Result]
|
9
|
+
# @param options [Hash]
|
10
|
+
def initialize(result, options)
|
11
|
+
@result = result
|
12
|
+
@query_options = options
|
13
|
+
server_status = result.server_status
|
14
|
+
@server_flags = {
|
15
|
+
no_good_index_used: server_status & Mysql::SERVER_QUERY_NO_GOOD_INDEX_USED != 0,
|
16
|
+
no_index_used: server_status & Mysql::SERVER_QUERY_NO_INDEX_USED != 0,
|
17
|
+
query_was_slow: server_status & Mysql::SERVER_QUERY_WAS_SLOW != 0,
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def size
|
22
|
+
@result.size
|
23
|
+
end
|
24
|
+
alias count size
|
25
|
+
|
26
|
+
def free
|
27
|
+
# dummy
|
28
|
+
end
|
29
|
+
|
30
|
+
def fields
|
31
|
+
@result.fields.map{|f| f.name.freeze}
|
32
|
+
end
|
33
|
+
|
34
|
+
def field_types
|
35
|
+
@result.fields.map{|f| field_type_string(f)}
|
36
|
+
end
|
37
|
+
|
38
|
+
def each(**opts, &block)
|
39
|
+
opts = @query_options.merge(opts)
|
40
|
+
|
41
|
+
raise Mysql2::Error, 'Result set has already been freed' if opts[:stream] && @all_retrieved
|
42
|
+
return enum_for(:each, **opts) unless block
|
43
|
+
|
44
|
+
@pos ||= 0
|
45
|
+
@cached ||= []
|
46
|
+
@pos = 0 unless opts[:stream]
|
47
|
+
|
48
|
+
while @pos < @cached.size
|
49
|
+
block.call @cached[@pos]
|
50
|
+
@pos += 1
|
51
|
+
end
|
52
|
+
|
53
|
+
@result.data_seek @pos
|
54
|
+
while (row = @result.fetch(**opts))
|
55
|
+
row = @result.fields.map.with_index do |f, i|
|
56
|
+
opts[:cast] ? convert_type(row[i], f, opts) : row[i]
|
57
|
+
end
|
58
|
+
if opts[:as] == :hash
|
59
|
+
row = @result.fields.map.with_index.to_h do |f, i|
|
60
|
+
key = opts[:symbolize_keys] ? f.name.intern : f.name
|
61
|
+
[key, row[i]]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
@cached.push row if opts[:cache_rows]
|
65
|
+
@pos += 1
|
66
|
+
block.call row
|
67
|
+
end
|
68
|
+
@all_retrieved = true
|
69
|
+
self
|
70
|
+
rescue Mysql::Error => e
|
71
|
+
raise Mysql2::Error, e.message
|
72
|
+
end
|
73
|
+
|
74
|
+
FIELD_TYPE_STRING = {
|
75
|
+
Mysql::Field::TYPE_DECIMAL => "decimal",
|
76
|
+
Mysql::Field::TYPE_TINY => "tinyint",
|
77
|
+
Mysql::Field::TYPE_SHORT => "smallint",
|
78
|
+
Mysql::Field::TYPE_LONG => "int",
|
79
|
+
Mysql::Field::TYPE_FLOAT => "float",
|
80
|
+
Mysql::Field::TYPE_DOUBLE => "double",
|
81
|
+
Mysql::Field::TYPE_NULL => "null",
|
82
|
+
Mysql::Field::TYPE_TIMESTAMP => "timestamp",
|
83
|
+
Mysql::Field::TYPE_LONGLONG => "bigint",
|
84
|
+
Mysql::Field::TYPE_INT24 => "mediumint",
|
85
|
+
Mysql::Field::TYPE_DATE => "date",
|
86
|
+
Mysql::Field::TYPE_TIME => "time",
|
87
|
+
Mysql::Field::TYPE_DATETIME => "datetime",
|
88
|
+
Mysql::Field::TYPE_YEAR => "year",
|
89
|
+
Mysql::Field::TYPE_NEWDATE => "date",
|
90
|
+
Mysql::Field::TYPE_VARCHAR => "varchar",
|
91
|
+
Mysql::Field::TYPE_BIT => "bit",
|
92
|
+
Mysql::Field::TYPE_TIMESTAMP2 => "timestamp",
|
93
|
+
Mysql::Field::TYPE_DATETIME2 => "datetime",
|
94
|
+
Mysql::Field::TYPE_TIME2 => "time",
|
95
|
+
Mysql::Field::TYPE_TYPED_ARRAY => "typed_array",
|
96
|
+
Mysql::Field::TYPE_INVALID => "invalid",
|
97
|
+
Mysql::Field::TYPE_BOOL => "bool",
|
98
|
+
Mysql::Field::TYPE_JSON => "json",
|
99
|
+
Mysql::Field::TYPE_NEWDECIMAL => "decimal",
|
100
|
+
Mysql::Field::TYPE_ENUM => "enum",
|
101
|
+
Mysql::Field::TYPE_SET => "set",
|
102
|
+
Mysql::Field::TYPE_TINY_BLOB => "tinyblob",
|
103
|
+
Mysql::Field::TYPE_MEDIUM_BLOB => "mediumblob",
|
104
|
+
Mysql::Field::TYPE_LONG_BLOB => "longblob",
|
105
|
+
Mysql::Field::TYPE_BLOB => "blob",
|
106
|
+
Mysql::Field::TYPE_VAR_STRING => "varchar",
|
107
|
+
Mysql::Field::TYPE_STRING => "char",
|
108
|
+
Mysql::Field::TYPE_GEOMETRY => "geometry",
|
109
|
+
Mysql::Field::TYPE_CHAR => "tinyint",
|
110
|
+
Mysql::Field::TYPE_INTERVAL => "interval",
|
111
|
+
}.freeze
|
112
|
+
|
113
|
+
BINARY_CHARSET = 63
|
114
|
+
|
115
|
+
def field_type_string(field)
|
116
|
+
if field.type == Mysql::Field::TYPE_STRING
|
117
|
+
return 'enum' if field.flags & Mysql::Field::ENUM_FLAG != 0
|
118
|
+
return 'set' if field.flags & Mysql::Field::SET_FLAG != 0
|
119
|
+
end
|
120
|
+
type = FIELD_TYPE_STRING[field.type]
|
121
|
+
if type =~ /int$|bit$|year$/
|
122
|
+
type += "(#{field.length})"
|
123
|
+
elsif type =~ /char$/
|
124
|
+
if field.charsetnr == BINARY_CHARSET
|
125
|
+
type = type.sub(/char/, 'binary') + "(#{field.length})"
|
126
|
+
else
|
127
|
+
type += "(#{field.length / 3})"
|
128
|
+
end
|
129
|
+
elsif type =~ /float|double/
|
130
|
+
type += "(#{field.length},#{field.decimals})"
|
131
|
+
elsif type == 'decimal'
|
132
|
+
type += "(#{field.length - (field.decimals > 0 ? 2 : 1)},#{field.decimals})"
|
133
|
+
elsif type == 'blob'
|
134
|
+
if field.charsetnr == BINARY_CHARSET
|
135
|
+
case field.length
|
136
|
+
when 255
|
137
|
+
type = 'tinyblob'
|
138
|
+
when 65535
|
139
|
+
type = 'blob'
|
140
|
+
when 16777215
|
141
|
+
type = 'mediumblob'
|
142
|
+
when 4294967295
|
143
|
+
type = 'longblob'
|
144
|
+
end
|
145
|
+
else
|
146
|
+
type = 'text'
|
147
|
+
case field.length
|
148
|
+
when 255 * 3
|
149
|
+
type = 'tinytext'
|
150
|
+
when 65535 * 3
|
151
|
+
type = 'text'
|
152
|
+
when 16777215 * 3
|
153
|
+
type = 'mediumtext'
|
154
|
+
when 4294967295
|
155
|
+
type = 'longtext'
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
type
|
160
|
+
end
|
161
|
+
|
162
|
+
def convert_type(value, field, opts)
|
163
|
+
return nil if value.nil?
|
164
|
+
case field.type
|
165
|
+
when Mysql::Field::TYPE_BIT
|
166
|
+
opts[:cast_booleans] ? value != "\x00" : value
|
167
|
+
when Mysql::Field::TYPE_TINY
|
168
|
+
opts[:cast_booleans] && field.length == 1 ? value != 0 : value
|
169
|
+
when Mysql::Field::TYPE_TIME
|
170
|
+
Time.new(2000, 1, 1) + value
|
171
|
+
else
|
172
|
+
value
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Mysql2
|
2
|
+
class Statement
|
3
|
+
# @param stmt [Mysql::Stmt]
|
4
|
+
def initialize(stmt, **opts)
|
5
|
+
@stmt = stmt
|
6
|
+
@opts = opts
|
7
|
+
end
|
8
|
+
|
9
|
+
def param_count
|
10
|
+
@stmt.param_count
|
11
|
+
end
|
12
|
+
|
13
|
+
def field_count
|
14
|
+
@stmt.field_count
|
15
|
+
end
|
16
|
+
|
17
|
+
def fields
|
18
|
+
field_count == 0 ? nil : @stmt.fields.map(&:name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute(*args, **opts)
|
22
|
+
res = @stmt.execute(*args, **opts)
|
23
|
+
opts = @opts.merge(opts)
|
24
|
+
StatementResult.new(res, **opts)
|
25
|
+
rescue Mysql::Error => e
|
26
|
+
raise Mysql2::Error, e.message
|
27
|
+
end
|
28
|
+
|
29
|
+
def affected_rows
|
30
|
+
@stmt.affected_rows
|
31
|
+
end
|
32
|
+
|
33
|
+
def last_id
|
34
|
+
@stmt.insert_id
|
35
|
+
end
|
36
|
+
|
37
|
+
def close
|
38
|
+
@stmt.close
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class StatementResult
|
43
|
+
include Enumerable
|
44
|
+
|
45
|
+
def initialize(result, **opts)
|
46
|
+
if !opts[:cache_rows] && !opts[:stream]
|
47
|
+
warn ':cache_rows is forced for prepared statements (if not streaming)'
|
48
|
+
end
|
49
|
+
@result = result
|
50
|
+
@opts = opts
|
51
|
+
end
|
52
|
+
|
53
|
+
def each(**opts, &block)
|
54
|
+
opts = @opts.merge(opts)
|
55
|
+
|
56
|
+
raise Mysql2::Error, 'Result set has already been freed' if opts[:stream] && @all_retrieved
|
57
|
+
return enum_for(:each, **opts) unless block
|
58
|
+
|
59
|
+
@pos ||= 0
|
60
|
+
@cached ||= []
|
61
|
+
@pos = 0 unless opts[:stream]
|
62
|
+
|
63
|
+
while @pos < @cached.size
|
64
|
+
block.call @cached[@pos]
|
65
|
+
@pos += 1
|
66
|
+
end
|
67
|
+
|
68
|
+
@result.data_seek @pos
|
69
|
+
while (row = @result.fetch(**opts))
|
70
|
+
row = @result.fields.map.with_index do |f, i|
|
71
|
+
convert_type(row[i], f, opts)
|
72
|
+
end
|
73
|
+
if opts[:as] == :hash
|
74
|
+
row = @result.fields.map.with_index.to_h do |f, i|
|
75
|
+
key = opts[:symbolize_keys] ? f.name.intern : f.name
|
76
|
+
[key, row[i]]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
@cached.push row if opts[:cache_rows]
|
80
|
+
@pos += 1
|
81
|
+
block.call row
|
82
|
+
end
|
83
|
+
@all_retrieved = true
|
84
|
+
self
|
85
|
+
rescue Mysql::Error => e
|
86
|
+
raise Mysql2::Error, e.message
|
87
|
+
end
|
88
|
+
|
89
|
+
def convert_type(value, field, opts)
|
90
|
+
return nil if value.nil?
|
91
|
+
case field.type
|
92
|
+
when Mysql::Field::TYPE_BIT
|
93
|
+
opts[:cast_booleans] ? value != "\x00" : value
|
94
|
+
when Mysql::Field::TYPE_TINY
|
95
|
+
opts[:cast_booleans] && field.length == 1 ? value != 0 : value
|
96
|
+
when Mysql::Field::TYPE_TIME
|
97
|
+
Time.new(2000, 1, 1) + value
|
98
|
+
else
|
99
|
+
value
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/mysql2.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'mysql'
|
2
|
+
require 'date'
|
3
|
+
require 'bigdecimal'
|
4
|
+
|
5
|
+
# Load libmysql.dll before requiring mysql2/mysql2.so
|
6
|
+
# This gives a chance to be flexible about the load path
|
7
|
+
# Or to bomb out with a clear error message instead of a linker crash
|
8
|
+
if RUBY_PLATFORM =~ /mswin|mingw/
|
9
|
+
dll_path = if ENV['RUBY_MYSQL2_LIBMYSQL_DLL']
|
10
|
+
# If this environment variable is set, it overrides any other paths
|
11
|
+
# The user is advised to use backslashes not forward slashes
|
12
|
+
ENV['RUBY_MYSQL2_LIBMYSQL_DLL']
|
13
|
+
elsif File.exist?(File.expand_path('../vendor/libmysql.dll', File.dirname(__FILE__)))
|
14
|
+
# Use vendor/libmysql.dll if it exists, convert slashes for Win32 LoadLibrary
|
15
|
+
File.expand_path('../vendor/libmysql.dll', File.dirname(__FILE__))
|
16
|
+
elsif defined?(RubyInstaller)
|
17
|
+
# RubyInstaller-2.4+ native build doesn't need DLL preloading
|
18
|
+
else
|
19
|
+
# This will use default / system library paths
|
20
|
+
'libmysql.dll'
|
21
|
+
end
|
22
|
+
|
23
|
+
if dll_path
|
24
|
+
require 'fiddle'
|
25
|
+
kernel32 = Fiddle.dlopen 'kernel32'
|
26
|
+
load_library = Fiddle::Function.new(
|
27
|
+
kernel32['LoadLibraryW'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT,
|
28
|
+
)
|
29
|
+
if load_library.call(dll_path.encode('utf-16le')).zero?
|
30
|
+
abort "Failed to load libmysql.dll from #{dll_path}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'mysql2/version' unless defined? Mysql2::VERSION
|
36
|
+
require 'mysql2/error'
|
37
|
+
require 'mysql2/result'
|
38
|
+
require 'mysql2/client'
|
39
|
+
require 'mysql2/field'
|
40
|
+
require 'mysql2/statement'
|
41
|
+
|
42
|
+
# = Mysql2
|
43
|
+
#
|
44
|
+
# A modern, simple and very fast Mysql library for Ruby - binding to libmysql
|
45
|
+
module Mysql2
|
46
|
+
end
|
47
|
+
|
48
|
+
if defined?(ActiveRecord::VERSION::STRING) && ActiveRecord::VERSION::STRING < "3.1"
|
49
|
+
begin
|
50
|
+
require 'active_record/connection_adapters/mysql2_adapter'
|
51
|
+
rescue LoadError
|
52
|
+
warn "============= WARNING FROM mysql2 ============="
|
53
|
+
warn "This version of mysql2 (#{Mysql2::VERSION}) doesn't ship with the ActiveRecord adapter."
|
54
|
+
warn "In Rails version 3.1.0 and up, the mysql2 ActiveRecord adapter is included with rails."
|
55
|
+
warn "If you want to use the mysql2 gem with Rails <= 3.0.x, please use the latest mysql2 in the 0.2.x series."
|
56
|
+
warn "============= END WARNING FROM mysql2 ============="
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# For holding utility methods
|
61
|
+
module Mysql2
|
62
|
+
module Util
|
63
|
+
#
|
64
|
+
# Rekey a string-keyed hash with equivalent symbols.
|
65
|
+
#
|
66
|
+
def self.key_hash_as_symbols(hash)
|
67
|
+
return nil unless hash
|
68
|
+
Hash[hash.map { |k, v| [k.to_sym, v] }]
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# In Mysql2::Client#query and Mysql2::Statement#execute,
|
73
|
+
# Thread#handle_interrupt is used to prevent Timeout#timeout
|
74
|
+
# from interrupting query execution.
|
75
|
+
#
|
76
|
+
# Timeout::ExitException was removed in Ruby 2.3.0, 2.2.3, and 2.1.8,
|
77
|
+
# but is present in earlier 2.1.x and 2.2.x, so we provide a shim.
|
78
|
+
#
|
79
|
+
require 'timeout'
|
80
|
+
TIMEOUT_ERROR_CLASS = if defined?(::Timeout::ExitException)
|
81
|
+
::Timeout::ExitException
|
82
|
+
else
|
83
|
+
::Timeout::Error
|
84
|
+
end
|
85
|
+
TIMEOUT_ERROR_NEVER = { TIMEOUT_ERROR_CLASS => :never }.freeze
|
86
|
+
end
|
87
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-mysql2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tomita Masahiro
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-11-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ruby-mysql
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description:
|
56
|
+
email: tommy@tmtm.org
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- CHANGELOG.md
|
62
|
+
- LICENSE
|
63
|
+
- README.md
|
64
|
+
- README.org.md
|
65
|
+
- lib/mysql2.rb
|
66
|
+
- lib/mysql2/client.rb
|
67
|
+
- lib/mysql2/console.rb
|
68
|
+
- lib/mysql2/error.rb
|
69
|
+
- lib/mysql2/field.rb
|
70
|
+
- lib/mysql2/result.rb
|
71
|
+
- lib/mysql2/statement.rb
|
72
|
+
- lib/mysql2/version.rb
|
73
|
+
homepage: https://gitlab.com/tmtms/ruby-mysql2
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
metadata:
|
77
|
+
bug_tracker_uri: https://gitlab.com/tmtms/ruby-mysql2/issues
|
78
|
+
changelog_uri: https://gitlab.com/tmtms/ruby-mysql2/releases/tag/0.5.4
|
79
|
+
documentation_uri: https://www.rubydoc.info/gems/ruby-mysql2/0.5.4
|
80
|
+
homepage_uri: https://gitlab.com/tmtms/ruby-mysql2
|
81
|
+
source_code_uri: https://gitlab.com/tmtms/ruby-mysql2/tree/0.5.4
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options:
|
84
|
+
- "--charset=UTF-8"
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: 2.6.0
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubygems_version: 3.4.0.dev
|
99
|
+
signing_key:
|
100
|
+
specification_version: 4
|
101
|
+
summary: Mysql2 compatible pure Ruby library
|
102
|
+
test_files: []
|