mirror of
https://github.com/github/rails.git
synced 2026-04-26 03:00:59 -04:00
prepared statements can be disabled
This commit is contained in:
@@ -2,9 +2,11 @@ module ActiveRecord
|
||||
module ConnectionAdapters # :nodoc:
|
||||
module DatabaseStatements
|
||||
# Converts an arel AST to SQL
|
||||
def to_sql(arel)
|
||||
def to_sql(arel, binds = [])
|
||||
if arel.respond_to?(:ast)
|
||||
visitor.accept(arel.ast)
|
||||
visitor.accept(arel.ast) do
|
||||
quote(*binds.shift.reverse)
|
||||
end
|
||||
else
|
||||
arel
|
||||
end
|
||||
@@ -13,7 +15,7 @@ module ActiveRecord
|
||||
# Returns an array of record hashes with the column names as keys and
|
||||
# column values as values.
|
||||
def select_all(arel, name = nil, binds = [])
|
||||
select(to_sql(arel), name, binds)
|
||||
select(to_sql(arel, binds), name, binds)
|
||||
end
|
||||
|
||||
# Returns a record hash with the column names as keys and column values
|
||||
@@ -33,7 +35,7 @@ module ActiveRecord
|
||||
# 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(arel, name = nil)
|
||||
result = select_rows(to_sql(arel), name)
|
||||
result = select_rows(to_sql(arel, []), name)
|
||||
result.map { |v| v[0] }
|
||||
end
|
||||
|
||||
@@ -84,19 +86,19 @@ module ActiveRecord
|
||||
# If the next id was calculated in advance (as in Oracle), it should be
|
||||
# passed in as +id_value+.
|
||||
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
||||
sql, binds = sql_for_insert(to_sql(arel), pk, id_value, sequence_name, binds)
|
||||
sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
|
||||
value = exec_insert(sql, name, binds)
|
||||
id_value || last_inserted_id(value)
|
||||
end
|
||||
|
||||
# Executes the update statement and returns the number of rows affected.
|
||||
def update(arel, name = nil, binds = [])
|
||||
exec_update(to_sql(arel), name, binds)
|
||||
exec_update(to_sql(arel, binds), name, binds)
|
||||
end
|
||||
|
||||
# Executes the delete statement and returns the number of rows affected.
|
||||
def delete(arel, name = nil, binds = [])
|
||||
exec_delete(to_sql(arel), name, binds)
|
||||
exec_delete(to_sql(arel, binds), name, binds)
|
||||
end
|
||||
|
||||
# Checks whether there is currently no transaction active. This is done
|
||||
|
||||
@@ -57,7 +57,7 @@ module ActiveRecord
|
||||
|
||||
def select_all(arel, name = nil, binds = [])
|
||||
if @query_cache_enabled
|
||||
sql = to_sql(arel)
|
||||
sql = to_sql(arel, binds)
|
||||
cache_sql(sql, binds) { super(sql, name, binds) }
|
||||
else
|
||||
super
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'arel/visitors/bind_visitor'
|
||||
|
||||
module ActiveRecord
|
||||
module ConnectionAdapters
|
||||
@@ -122,12 +123,21 @@ module ActiveRecord
|
||||
:boolean => { :name => "tinyint", :limit => 1 }
|
||||
}
|
||||
|
||||
class BindSubstitution < Arel::Visitors::MySQL # :nodoc:
|
||||
include Arel::Visitors::BindVisitor
|
||||
end
|
||||
|
||||
# FIXME: Make the first parameter more similar for the two adapters
|
||||
def initialize(connection, logger, connection_options, config)
|
||||
super(connection, logger)
|
||||
@connection_options, @config = connection_options, config
|
||||
@quoted_column_names, @quoted_table_names = {}, {}
|
||||
@visitor = Arel::Visitors::MySQL.new self
|
||||
|
||||
if config.fetch(:prepared_statements) { true }
|
||||
@visitor = Arel::Visitors::MySQL.new self
|
||||
else
|
||||
@visitor = BindSubstitution.new self
|
||||
end
|
||||
end
|
||||
|
||||
def adapter_name #:nodoc:
|
||||
|
||||
@@ -32,6 +32,7 @@ module ActiveRecord
|
||||
|
||||
def initialize(connection, logger, connection_options, config)
|
||||
super
|
||||
@visitor = BindSubstitution.new self
|
||||
configure_connection
|
||||
end
|
||||
|
||||
@@ -65,10 +66,6 @@ module ActiveRecord
|
||||
@connection.escape(string)
|
||||
end
|
||||
|
||||
def substitute_at(column, index)
|
||||
Arel::Nodes::BindParam.new "\0"
|
||||
end
|
||||
|
||||
# CONNECTION MANAGEMENT ====================================
|
||||
|
||||
def active?
|
||||
@@ -98,7 +95,7 @@ module ActiveRecord
|
||||
# DATABASE STATEMENTS ======================================
|
||||
|
||||
def explain(arel, binds = [])
|
||||
sql = "EXPLAIN #{to_sql(arel)}"
|
||||
sql = "EXPLAIN #{to_sql(arel, binds.dup)}"
|
||||
start = Time.now
|
||||
result = exec_query(sql, 'EXPLAIN', binds)
|
||||
elapsed = Time.now - start
|
||||
@@ -224,8 +221,7 @@ module ActiveRecord
|
||||
# Returns an array of record hashes with the column names as keys and
|
||||
# column values as values.
|
||||
def select(sql, name = nil, binds = [])
|
||||
binds = binds.dup
|
||||
exec_query(sql.gsub("\0") { quote(*binds.shift.reverse) }, name).to_a
|
||||
exec_query(sql, name).to_a
|
||||
end
|
||||
|
||||
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
||||
@@ -235,17 +231,11 @@ module ActiveRecord
|
||||
alias :create :insert_sql
|
||||
|
||||
def exec_insert(sql, name, binds)
|
||||
binds = binds.dup
|
||||
|
||||
# Pretend to support bind parameters
|
||||
execute sql.gsub("\0") { quote(*binds.shift.reverse) }, name
|
||||
execute to_sql(sql, binds), name
|
||||
end
|
||||
|
||||
def exec_delete(sql, name, binds)
|
||||
binds = binds.dup
|
||||
|
||||
# Pretend to support bind parameters
|
||||
execute sql.gsub("\0") { quote(*binds.shift.reverse) }, name
|
||||
execute to_sql(sql, binds), name
|
||||
@connection.affected_rows
|
||||
end
|
||||
alias :exec_update :exec_delete
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
require 'active_record/connection_adapters/abstract_adapter'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'active_record/connection_adapters/statement_pool'
|
||||
require 'arel/visitors/bind_visitor'
|
||||
|
||||
# Make sure we're using pg high enough for PGResult#values
|
||||
gem 'pg', '~> 0.11'
|
||||
@@ -303,11 +304,23 @@ module ActiveRecord
|
||||
end
|
||||
end
|
||||
|
||||
class BindSubstitution < Arel::Visitors::PostgreSQL # :nodoc:
|
||||
include Arel::Visitors::BindVisitor
|
||||
end
|
||||
|
||||
# Initializes and connects a PostgreSQL adapter.
|
||||
def initialize(connection, logger, connection_parameters, config)
|
||||
super(connection, logger)
|
||||
|
||||
if config.fetch(:prepared_statements) { true }
|
||||
@visitor = Arel::Visitors::PostgreSQL.new self
|
||||
else
|
||||
@visitor = BindSubstitution.new self
|
||||
end
|
||||
|
||||
connection_parameters.delete :prepared_statements
|
||||
|
||||
@connection_parameters, @config = connection_parameters, config
|
||||
@visitor = Arel::Visitors::PostgreSQL.new self
|
||||
|
||||
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
|
||||
@local_tz = nil
|
||||
@@ -520,7 +533,7 @@ module ActiveRecord
|
||||
# DATABASE STATEMENTS ======================================
|
||||
|
||||
def explain(arel, binds = [])
|
||||
sql = "EXPLAIN #{to_sql(arel)}"
|
||||
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
||||
ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
|
||||
end
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
require 'active_record/connection_adapters/abstract_adapter'
|
||||
require 'active_record/connection_adapters/statement_pool'
|
||||
require 'active_support/core_ext/string/encoding'
|
||||
require 'arel/visitors/bind_visitor'
|
||||
|
||||
module ActiveRecord
|
||||
module ConnectionAdapters #:nodoc:
|
||||
@@ -69,12 +70,21 @@ module ActiveRecord
|
||||
end
|
||||
end
|
||||
|
||||
class BindSubstitution < Arel::Visitors::SQLite # :nodoc:
|
||||
include Arel::Visitors::BindVisitor
|
||||
end
|
||||
|
||||
def initialize(connection, logger, config)
|
||||
super(connection, logger)
|
||||
@statements = StatementPool.new(@connection,
|
||||
config.fetch(:statement_limit) { 1000 })
|
||||
@config = config
|
||||
@visitor = Arel::Visitors::SQLite.new self
|
||||
|
||||
if config.fetch(:prepared_statements) { true }
|
||||
@visitor = Arel::Visitors::SQLite.new self
|
||||
else
|
||||
@visitor = BindSubstitution.new self
|
||||
end
|
||||
end
|
||||
|
||||
def adapter_name #:nodoc:
|
||||
@@ -210,7 +220,7 @@ module ActiveRecord
|
||||
# DATABASE STATEMENTS ======================================
|
||||
|
||||
def explain(arel, binds = [])
|
||||
sql = "EXPLAIN QUERY PLAN #{to_sql(arel)}"
|
||||
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
||||
ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
|
||||
end
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ module ActiveRecord
|
||||
end
|
||||
|
||||
def initialize_copy(other)
|
||||
@bind_values = @bind_values.dup
|
||||
reset
|
||||
end
|
||||
|
||||
@@ -453,7 +454,7 @@ module ActiveRecord
|
||||
end
|
||||
|
||||
def to_sql
|
||||
@to_sql ||= klass.connection.to_sql(arel)
|
||||
@to_sql ||= klass.connection.to_sql(arel, @bind_values.dup)
|
||||
end
|
||||
|
||||
def where_values_hash
|
||||
|
||||
@@ -208,7 +208,7 @@ module ActiveRecord
|
||||
def find_with_associations
|
||||
join_dependency = construct_join_dependency_for_association_find
|
||||
relation = construct_relation_for_association_find(join_dependency)
|
||||
rows = connection.select_all(relation, 'SQL', relation.bind_values)
|
||||
rows = connection.select_all(relation, 'SQL', relation.bind_values.dup)
|
||||
join_dependency.instantiate(rows)
|
||||
rescue ThrowResult
|
||||
[]
|
||||
|
||||
Reference in New Issue
Block a user