diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index b6db250526afc5a5dac1645e8d827e388d88b462..ddf6403a435e59587cecb6f8dcd3aed75b5ff781 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -14,31 +14,32 @@ def to_sql(arel_or_sql_string, binds = []) sql end - def to_sql_and_binds(arel_or_sql_string, binds = []) # :nodoc: + def to_sql_and_binds(arel_or_sql_string, binds = [], preparable = nil) # :nodoc: if arel_or_sql_string.respond_to?(:ast) unless binds.empty? raise "Passing bind parameters with an arel AST is forbidden. " \ "The values must be stored on the AST directly" end + collector = collector() + if prepared_statements + collector.preparable = true sql, binds = visitor.compile(arel_or_sql_string.ast, collector) if binds.length > bind_params_length unprepared_statement do - sql, binds = to_sql_and_binds(arel_or_sql_string) - visitor.preparable = false + return to_sql_and_binds(arel_or_sql_string) end end + preparable = collector.preparable else sql = visitor.compile(arel_or_sql_string.ast, collector) end - [sql.freeze, binds] + [sql.freeze, binds, preparable] else - visitor.preparable = false if prepared_statements - arel_or_sql_string = arel_or_sql_string.dup.freeze unless arel_or_sql_string.frozen? - [arel_or_sql_string, binds] + [arel_or_sql_string, binds, preparable] end end private :to_sql_and_binds @@ -60,11 +61,7 @@ def cacheable_query(klass, arel) # :nodoc: # Returns an ActiveRecord::Result instance. def select_all(arel, name = nil, binds = [], preparable: nil) arel = arel_from_relation(arel) - sql, binds = to_sql_and_binds(arel, binds) - - if preparable.nil? - preparable = prepared_statements ? visitor.preparable : false - end + sql, binds, preparable = to_sql_and_binds(arel, binds, preparable) if prepared_statements && preparable select_prepared(sql, name, binds) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb index 819e0162622ab41148360efae357444bae86d8b1..b91cfe410a404309bcc93fd4f1609daa013c4fc2 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -96,11 +96,7 @@ def clear_query_cache def select_all(arel, name = nil, binds = [], preparable: nil) if @query_cache_enabled && !locked?(arel) arel = arel_from_relation(arel) - sql, binds = to_sql_and_binds(arel, binds) - - if preparable.nil? - preparable = prepared_statements ? visitor.preparable : false - end + sql, binds, preparable = to_sql_and_binds(arel, binds, preparable) cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable) } else diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 622b365099f78dcf24698138173b2b77511a77ce..95d6cb24bacdcb2905bbe7fa6b13ddf3af32b90a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "set" -require "active_record/connection_adapters/determine_if_preparable_visitor" require "active_record/connection_adapters/schema_cache" require "active_record/connection_adapters/sql_type_metadata" require "active_record/connection_adapters/abstract/schema_dumper" @@ -95,12 +94,9 @@ def initialize(connection, logger = nil, config = {}) # :nodoc: @statements = build_statement_pool @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new - if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true }) - @prepared_statements = true - @visitor.extend(DetermineIfPreparableVisitor) - else - @prepared_statements = false - end + @prepared_statements = self.class.type_cast_config_to_boolean( + config.fetch(:prepared_statements, true) + ) @advisory_locks_enabled = self.class.type_cast_config_to_boolean( config.fetch(:advisory_locks, true) diff --git a/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb b/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb deleted file mode 100644 index 97d74df52930aa3273f6b0a671807210d73b7467..0000000000000000000000000000000000000000 --- a/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module ActiveRecord - module ConnectionAdapters - module DetermineIfPreparableVisitor - attr_accessor :preparable - - def accept(object, collector) - @preparable = true - super - end - - def visit_Arel_Nodes_In(o, collector) - @preparable = false - super - end - - def visit_Arel_Nodes_NotIn(o, collector) - @preparable = false - super - end - - def visit_Arel_Nodes_SqlLiteral(o, collector) - @preparable = false - super - end - end - end -end diff --git a/activerecord/lib/active_record/statement_cache.rb b/activerecord/lib/active_record/statement_cache.rb index 93bce15230f6f2adb0711538619943a9427acca6..f8047163a34c2edc9a2a364f2273aff90928349d 100644 --- a/activerecord/lib/active_record/statement_cache.rb +++ b/activerecord/lib/active_record/statement_cache.rb @@ -57,6 +57,8 @@ def sql_for(binds, connection) end class PartialQueryCollector + attr_accessor :preparable + def initialize @parts = [] @binds = [] diff --git a/activerecord/lib/arel/collectors/composite.rb b/activerecord/lib/arel/collectors/composite.rb index 053354499397e2e0ab0648c3b5eda1a774b12e8d..0df073164e99bae837ea45dd073faf35f9438d86 100644 --- a/activerecord/lib/arel/collectors/composite.rb +++ b/activerecord/lib/arel/collectors/composite.rb @@ -3,6 +3,8 @@ module Arel # :nodoc: all module Collectors class Composite + attr_accessor :preparable + def initialize(left, right) @left = left @right = right diff --git a/activerecord/lib/arel/collectors/sql_string.rb b/activerecord/lib/arel/collectors/sql_string.rb index 54e1e562c21a63d7637ee2b0c9f7fc3e3e941006..6c52335b4590fe4b67e2c5502c65d6c498f096a3 100644 --- a/activerecord/lib/arel/collectors/sql_string.rb +++ b/activerecord/lib/arel/collectors/sql_string.rb @@ -5,6 +5,8 @@ module Arel # :nodoc: all module Collectors class SQLString < PlainString + attr_accessor :preparable + def initialize(*) super @bind_index = 1 diff --git a/activerecord/lib/arel/collectors/substitute_binds.rb b/activerecord/lib/arel/collectors/substitute_binds.rb index 068e52fe5cb3ff4c80d8351ccf23589c2c54b88b..ea77db0215d0fc18e4c92816278b5b9d52fba651 100644 --- a/activerecord/lib/arel/collectors/substitute_binds.rb +++ b/activerecord/lib/arel/collectors/substitute_binds.rb @@ -3,6 +3,8 @@ module Arel # :nodoc: all module Collectors class SubstituteBinds + attr_accessor :preparable + def initialize(quoter, delegate_collector) @quoter = quoter @delegate = delegate_collector diff --git a/activerecord/lib/arel/visitors/to_sql.rb b/activerecord/lib/arel/visitors/to_sql.rb index 8ed7a80de9e151a524d60b71f3562cd1f84603ca..465460b2a528e0d0832ffe894e08c4acd172465b 100644 --- a/activerecord/lib/arel/visitors/to_sql.rb +++ b/activerecord/lib/arel/visitors/to_sql.rb @@ -510,6 +510,7 @@ def visit_Arel_Table(o, collector) end def visit_Arel_Nodes_In(o, collector) + collector.preparable = false attr, values = o.left, o.right if Array === values @@ -525,6 +526,7 @@ def visit_Arel_Nodes_In(o, collector) end def visit_Arel_Nodes_NotIn(o, collector) + collector.preparable = false attr, values = o.left, o.right if Array === values @@ -656,14 +658,18 @@ def visit_Arel_Attributes_Attribute(o, collector) collector << quote_table_name(join_name) << "." << quote_column_name(o.name) end - def literal(o, collector); collector << o.to_s; end - def visit_Arel_Nodes_BindParam(o, collector) collector.add_bind(o.value) { "?" } end - alias :visit_Arel_Nodes_SqlLiteral :literal - alias :visit_Integer :literal + def visit_Arel_Nodes_SqlLiteral(o, collector) + collector.preparable = false + collector << o.to_s + end + + def visit_Integer(o, collector) + collector << o.to_s + end def unsupported(o, collector) raise UnsupportedVisitError.new(o)