1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
|
require 'active_record/connection_adapters/abstract_mysql_adapter'
gem 'mysql2', '>= 0.3.18', '< 0.5'
require 'mysql2'
module ActiveRecord
module ConnectionHandling # :nodoc:
# Establishes a connection to the database that's used by all Active Record objects.
def mysql2_connection(config)
config = config.symbolize_keys
config[:username] = 'root' if config[:username].nil?
config[:flags] ||= 0
if Mysql2::Client.const_defined? :FOUND_ROWS
if config[:flags].kind_of? Array
config[:flags].push "FOUND_ROWS".freeze
else
config[:flags] |= Mysql2::Client::FOUND_ROWS
end
end
client = Mysql2::Client.new(config)
ConnectionAdapters::Mysql2Adapter.new(client, logger, nil, config)
rescue Mysql2::Error => error
if error.message.include?("Unknown database")
raise ActiveRecord::NoDatabaseError
else
raise
end
end
end
module ConnectionAdapters
class Mysql2Adapter < AbstractMysqlAdapter
ADAPTER_NAME = 'Mysql2'.freeze
def initialize(connection, logger, connection_options, config)
super
@prepared_statements = false
configure_connection
end
def supports_json?
version >= '5.7.8'
end
# HELPER METHODS ===========================================
def each_hash(result) # :nodoc:
if block_given?
result.each(:as => :hash, :symbolize_keys => true) do |row|
yield row
end
else
to_enum(:each_hash, result)
end
end
def error_number(exception)
exception.error_number if exception.respond_to?(:error_number)
end
#--
# QUOTING ==================================================
#++
def quote_string(string)
@connection.escape(string)
end
#--
# CONNECTION MANAGEMENT ====================================
#++
def active?
return false unless @connection
@connection.ping
end
def reconnect!
super
disconnect!
connect
end
alias :reset! :reconnect!
# Disconnects from the database if already connected.
# Otherwise, this method does nothing.
def disconnect!
super
unless @connection.nil?
@connection.close
@connection = nil
end
end
#--
# DATABASE STATEMENTS ======================================
#++
# FIXME: re-enable the following once a "better" query_cache solution is in core
#
# The overrides below perform much better than the originals in AbstractAdapter
# because we're able to take advantage of mysql2's lazy-loading capabilities
#
# # Returns a record hash with the column names as keys and column values
# # as values.
# def select_one(sql, name = nil)
# result = execute(sql, name)
# result.each(as: :hash) do |r|
# return r
# end
# end
#
# # Returns a single value from a record
# def select_value(sql, name = nil)
# result = execute(sql, name)
# if first = result.first
# first.first
# end
# end
#
# # Returns an array of the values of the first column in a select:
# # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
# def select_values(sql, name = nil)
# execute(sql, name).map { |row| row.first }
# end
# Returns an array of arrays containing the field values.
# Order is the same as that returned by +columns+.
def select_rows(sql, name = nil, binds = [])
result = execute(sql, name)
@connection.next_result while @connection.more_results?
result.to_a
end
# Executes the SQL statement in the context of this connection.
def execute(sql, name = nil)
if @connection
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
# made since we established the connection
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
end
super
end
def exec_query(sql, name = 'SQL', binds = [], prepare: false)
result = execute(sql, name)
@connection.next_result while @connection.more_results?
ActiveRecord::Result.new(result.fields, result.to_a)
end
alias exec_without_stmt exec_query
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
super
id_value || @connection.last_id
end
alias :create :insert_sql
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
execute to_sql(sql, binds), name
end
def exec_delete(sql, name, binds)
execute to_sql(sql, binds), name
@connection.affected_rows
end
alias :exec_update :exec_delete
def last_inserted_id(result)
@connection.last_id
end
private
def connect
@connection = Mysql2::Client.new(@config)
configure_connection
end
def configure_connection
@connection.query_options.merge!(:as => :array)
super
end
def full_version
@full_version ||= @connection.server_info[:version]
end
end
end
end
|