Skip to content
Permalink
 
 
Cannot retrieve contributors at this time
190 lines (161 sloc) 6.14 KB
# frozen_string_literal: true
module ActiveRecord
module ConnectionAdapters
module MySQL
module DatabaseStatements
# Returns an ActiveRecord::Result instance.
def select_all(*, **) # :nodoc:
result = if ExplainRegistry.collect? && prepared_statements
unprepared_statement { super }
else
super
end
@connection.abandon_results!
result
end
def query(sql, name = nil) # :nodoc:
execute(sql, name).to_a
end
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
:desc, :describe, :set, :show, :use
) # :nodoc:
private_constant :READ_QUERY
def write_query?(sql) # :nodoc:
!READ_QUERY.match?(sql)
end
def explain(arel, binds = [])
sql = "EXPLAIN #{to_sql(arel, binds)}"
start = Concurrent.monotonic_time
result = exec_query(sql, "EXPLAIN", binds)
elapsed = Concurrent.monotonic_time - start
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
end
# Executes the SQL statement in the context of this connection.
def execute(sql, name = nil, async: false)
check_if_write_query(sql)
# 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
super
end
def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false)
if without_prepared_statement?(binds)
execute_and_free(sql, name, async: async) do |result|
if result
build_result(columns: result.fields, rows: result.to_a)
else
build_result(columns: [], rows: [])
end
end
else
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async) do |_, result|
if result
build_result(columns: result.fields, rows: result.to_a)
else
build_result(columns: [], rows: [])
end
end
end
end
def exec_delete(sql, name = nil, binds = [])
if without_prepared_statement?(binds)
@lock.synchronize do
execute_and_free(sql, name) { @connection.affected_rows }
end
else
exec_stmt_and_free(sql, name, binds) { |stmt| stmt.affected_rows }
end
end
alias :exec_update :exec_delete
private
def execute_batch(statements, name = nil)
combine_multi_statements(statements).each do |statement|
execute(statement, name)
end
@connection.abandon_results!
end
def default_insert_value(column)
super unless column.auto_increment?
end
def last_inserted_id(result)
@connection.last_id
end
def multi_statements_enabled?
flags = @config[:flags]
if flags.is_a?(Array)
flags.include?("MULTI_STATEMENTS")
else
flags.anybits?(Mysql2::Client::MULTI_STATEMENTS)
end
end
def with_multi_statements
multi_statements_was = multi_statements_enabled?
unless multi_statements_was
@connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
end
yield
ensure
unless multi_statements_was
@connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
end
end
def combine_multi_statements(total_sql)
total_sql.each_with_object([]) do |sql, total_sql_chunks|
previous_packet = total_sql_chunks.last
if max_allowed_packet_reached?(sql, previous_packet)
total_sql_chunks << +sql
else
previous_packet << ";\n"
previous_packet << sql
end
end
end
def max_allowed_packet_reached?(current_packet, previous_packet)
if current_packet.bytesize > max_allowed_packet
raise ActiveRecordError,
"Fixtures set is too large #{current_packet.bytesize}. Consider increasing the max_allowed_packet variable."
elsif previous_packet.nil?
true
else
(current_packet.bytesize + previous_packet.bytesize + 2) > max_allowed_packet
end
end
def max_allowed_packet
@max_allowed_packet ||= show_variable("max_allowed_packet")
end
def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
check_if_write_query(sql)
materialize_transactions
mark_transaction_written_if_write(sql)
# 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
type_casted_binds = type_casted_binds(binds)
log(sql, name, binds, type_casted_binds, async: async) do
if cache_stmt
stmt = @statements[sql] ||= @connection.prepare(sql)
else
stmt = @connection.prepare(sql)
end
begin
result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
stmt.execute(*type_casted_binds)
end
rescue Mysql2::Error => e
if cache_stmt
@statements.delete(sql)
else
stmt.close
end
raise e
end
ret = yield stmt, result
result.free if result
stmt.close unless cache_stmt
ret
end
end
end
end
end
end