Skip to content

Commit 630ddc7

Browse files
Move shared database logic to MySQL::DatabaseStatements
1 parent 93b5fc1 commit 630ddc7

File tree

6 files changed

+127
-190
lines changed

6 files changed

+127
-190
lines changed

activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require "active_record/connection_adapters/abstract_adapter"
44
require "active_record/connection_adapters/statement_pool"
55
require "active_record/connection_adapters/mysql/column"
6+
require "active_record/connection_adapters/mysql/database_statements"
67
require "active_record/connection_adapters/mysql/explain_pretty_printer"
78
require "active_record/connection_adapters/mysql/quoting"
89
require "active_record/connection_adapters/mysql/schema_creation"
@@ -14,6 +15,7 @@
1415
module ActiveRecord
1516
module ConnectionAdapters
1617
class AbstractMysqlAdapter < AbstractAdapter
18+
include MySQL::DatabaseStatements
1719
include MySQL::Quoting
1820
include MySQL::SchemaStatements
1921

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# frozen_string_literal: true
2+
3+
module ActiveRecord
4+
module ConnectionAdapters
5+
module MySQL
6+
module DatabaseStatements
7+
READ_QUERY = AbstractAdapter.build_read_query_regexp(
8+
:desc, :describe, :set, :show, :use
9+
) # :nodoc:
10+
private_constant :READ_QUERY
11+
12+
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_current-timestamp
13+
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-syntax.html
14+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP(6)").freeze # :nodoc:
15+
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
16+
17+
def write_query?(sql) # :nodoc:
18+
!READ_QUERY.match?(sql)
19+
rescue ArgumentError # Invalid encoding
20+
!READ_QUERY.match?(sql.b)
21+
end
22+
23+
def high_precision_current_timestamp
24+
HIGH_PRECISION_CURRENT_TIMESTAMP
25+
end
26+
27+
def explain(arel, binds = [], options = [])
28+
sql = build_explain_clause(options) + " " + to_sql(arel, binds)
29+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
30+
result = internal_exec_query(sql, "EXPLAIN", binds)
31+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
32+
33+
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
34+
end
35+
36+
def build_explain_clause(options = [])
37+
return "EXPLAIN" if options.empty?
38+
39+
explain_clause = "EXPLAIN #{options.join(" ").upcase}"
40+
41+
if analyze_without_explain? && explain_clause.include?("ANALYZE")
42+
explain_clause.sub("EXPLAIN ", "")
43+
else
44+
explain_clause
45+
end
46+
end
47+
48+
private
49+
# https://mariadb.com/kb/en/analyze-statement/
50+
def analyze_without_explain?
51+
mariadb? && database_version >= "10.1.0"
52+
end
53+
54+
def default_insert_value(column)
55+
super unless column.auto_increment?
56+
end
57+
58+
def combine_multi_statements(total_sql)
59+
total_sql.each_with_object([]) do |sql, total_sql_chunks|
60+
previous_packet = total_sql_chunks.last
61+
if max_allowed_packet_reached?(sql, previous_packet)
62+
total_sql_chunks << +sql
63+
else
64+
previous_packet << ";\n"
65+
previous_packet << sql
66+
end
67+
end
68+
end
69+
70+
def max_allowed_packet_reached?(current_packet, previous_packet)
71+
if current_packet.bytesize > max_allowed_packet
72+
raise ActiveRecordError,
73+
"Fixtures set is too large #{current_packet.bytesize}. Consider increasing the max_allowed_packet variable."
74+
elsif previous_packet.nil?
75+
true
76+
else
77+
(current_packet.bytesize + previous_packet.bytesize + 2) > max_allowed_packet
78+
end
79+
end
80+
81+
def max_allowed_packet
82+
@max_allowed_packet ||= show_variable("max_allowed_packet")
83+
end
84+
end
85+
end
86+
end
87+
end

activerecord/lib/active_record/connection_adapters/mysql2/database_statements.rb

Lines changed: 0 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,6 @@ def select_all(*, **) # :nodoc:
1818
result
1919
end
2020

21-
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
22-
:desc, :describe, :set, :show, :use
23-
) # :nodoc:
24-
private_constant :READ_QUERY
25-
26-
def write_query?(sql) # :nodoc:
27-
!READ_QUERY.match?(sql)
28-
rescue ArgumentError # Invalid encoding
29-
!READ_QUERY.match?(sql.b)
30-
end
31-
32-
def explain(arel, binds = [], options = [])
33-
sql = build_explain_clause(options) + " " + to_sql(arel, binds)
34-
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
35-
result = internal_exec_query(sql, "EXPLAIN", binds)
36-
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
37-
38-
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
39-
end
40-
4121
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
4222
if without_prepared_statement?(binds)
4323
execute_and_free(sql, name, async: async) do |result|
@@ -70,27 +50,6 @@ def exec_delete(sql, name = nil, binds = []) # :nodoc:
7050
end
7151
alias :exec_update :exec_delete
7252

73-
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_current-timestamp
74-
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-syntax.html
75-
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP(6)").freeze # :nodoc:
76-
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
77-
78-
def high_precision_current_timestamp
79-
HIGH_PRECISION_CURRENT_TIMESTAMP
80-
end
81-
82-
def build_explain_clause(options = [])
83-
return "EXPLAIN" if options.empty?
84-
85-
explain_clause = "EXPLAIN #{options.join(" ").upcase}"
86-
87-
if analyze_without_explain? && explain_clause.include?("ANALYZE")
88-
explain_clause.sub("EXPLAIN ", "")
89-
else
90-
explain_clause
91-
end
92-
end
93-
9453
private
9554
def sync_timezone_changes(raw_connection)
9655
raw_connection.query_options[:database_timezone] = default_timezone
@@ -106,10 +65,6 @@ def execute_batch(statements, name = nil)
10665
end
10766
end
10867

109-
def default_insert_value(column)
110-
super unless column.auto_increment?
111-
end
112-
11368
def last_inserted_id(result)
11469
@raw_connection&.last_id
11570
end
@@ -138,33 +93,6 @@ def with_multi_statements
13893
end
13994
end
14095

141-
def combine_multi_statements(total_sql)
142-
total_sql.each_with_object([]) do |sql, total_sql_chunks|
143-
previous_packet = total_sql_chunks.last
144-
if max_allowed_packet_reached?(sql, previous_packet)
145-
total_sql_chunks << +sql
146-
else
147-
previous_packet << ";\n"
148-
previous_packet << sql
149-
end
150-
end
151-
end
152-
153-
def max_allowed_packet_reached?(current_packet, previous_packet)
154-
if current_packet.bytesize > max_allowed_packet
155-
raise ActiveRecordError,
156-
"Fixtures set is too large #{current_packet.bytesize}. Consider increasing the max_allowed_packet variable."
157-
elsif previous_packet.nil?
158-
true
159-
else
160-
(current_packet.bytesize + previous_packet.bytesize + 2) > max_allowed_packet
161-
end
162-
end
163-
164-
def max_allowed_packet
165-
@max_allowed_packet ||= show_variable("max_allowed_packet")
166-
end
167-
16896
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
16997
log(sql, name, async: async) do
17098
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
@@ -214,11 +142,6 @@ def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
214142
end
215143
end
216144
end
217-
218-
# https://mariadb.com/kb/en/analyze-statement/
219-
def analyze_without_explain?
220-
mariadb? && database_version >= "10.1.0"
221-
end
222145
end
223146
end
224147
end

activerecord/lib/active_record/connection_adapters/trilogy/database_statements.rb

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,6 @@ module ActiveRecord
44
module ConnectionAdapters
55
module Trilogy
66
module DatabaseStatements
7-
READ_QUERY = AbstractAdapter.build_read_query_regexp(
8-
:desc, :describe, :set, :show, :use
9-
) # :nodoc:
10-
private_constant :READ_QUERY
11-
12-
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP(6)").freeze # :nodoc:
13-
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
14-
157
def select_all(*, **) # :nodoc:
168
result = nil
179
with_raw_connection do |conn|
@@ -21,21 +13,6 @@ def select_all(*, **) # :nodoc:
2113
result
2214
end
2315

24-
def write_query?(sql) # :nodoc:
25-
!READ_QUERY.match?(sql)
26-
rescue ArgumentError # Invalid encoding
27-
!READ_QUERY.match?(sql.b)
28-
end
29-
30-
def explain(arel, binds = [], options = [])
31-
sql = build_explain_clause(options) + " " + to_sql(arel, binds)
32-
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
33-
result = internal_exec_query(sql, "EXPLAIN", binds)
34-
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
35-
36-
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
37-
end
38-
3916
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
4017
sql = transform_query(sql)
4118
check_if_write_query(sql)
@@ -64,22 +41,6 @@ def exec_delete(sql, name = nil, binds = []) # :nodoc:
6441

6542
alias :exec_update :exec_delete # :nodoc:
6643

67-
def high_precision_current_timestamp
68-
HIGH_PRECISION_CURRENT_TIMESTAMP
69-
end
70-
71-
def build_explain_clause(options = [])
72-
return "EXPLAIN" if options.empty?
73-
74-
explain_clause = "EXPLAIN #{options.join(" ").upcase}"
75-
76-
if analyze_without_explain? && explain_clause.include?("ANALYZE")
77-
explain_clause.sub("EXPLAIN ", "")
78-
else
79-
explain_clause
80-
end
81-
end
82-
8344
private
8445
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
8546
log(sql, name, async: async) do
@@ -95,6 +56,43 @@ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transac
9556
def last_inserted_id(result)
9657
result.last_insert_id
9758
end
59+
60+
def sync_timezone_changes(conn)
61+
# Sync any changes since connection last established.
62+
if default_timezone == :local
63+
conn.query_flags |= ::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
64+
else
65+
conn.query_flags &= ~::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
66+
end
67+
end
68+
69+
def execute_batch(statements, name = nil)
70+
statements = statements.map { |sql| transform_query(sql) }
71+
combine_multi_statements(statements).each do |statement|
72+
with_raw_connection do |conn|
73+
raw_execute(statement, name)
74+
conn.next_result while conn.more_results_exist?
75+
end
76+
end
77+
end
78+
79+
def multi_statements_enabled?
80+
!!@config[:multi_statement]
81+
end
82+
83+
def with_multi_statements
84+
if multi_statements_enabled?
85+
return yield
86+
end
87+
88+
with_raw_connection do |conn|
89+
conn.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_ON)
90+
91+
yield
92+
ensure
93+
conn.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_OFF)
94+
end
95+
end
9896
end
9997
end
10098
end

0 commit comments

Comments
 (0)