From b06f64c3480cd389d14618540d62da4978918af0 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Tue, 27 Jan 2015 13:42:02 -0700 Subject: Remove Relation#bind_params `bound_attributes` is now used universally across the board, removing the need for the conversion layer. These changes are mostly mechanical, with the exception of the log subscriber. Additional, we had to implement `hash` on the attribute objects, so they could be used as a key for query caching. --- .../associations/has_many_through_association.rb | 2 +- .../associations/join_dependency/join_association.rb | 2 +- activerecord/lib/active_record/attribute.rb | 5 +++++ .../abstract/database_statements.rb | 6 +++--- .../connection_adapters/abstract/quoting.rb | 8 +------- .../connection_adapters/abstract_adapter.rb | 2 +- .../connection_adapters/mysql_adapter.rb | 8 +++----- .../connection_adapters/postgresql_adapter.rb | 8 +++----- .../connection_adapters/sqlite3_adapter.rb | 8 +++----- activerecord/lib/active_record/log_subscriber.rb | 20 +++++++------------- activerecord/lib/active_record/relation.rb | 18 +++++++++--------- .../lib/active_record/relation/calculations.rb | 6 +++--- .../lib/active_record/relation/finder_methods.rb | 6 +++--- .../lib/active_record/relation/predicate_builder.rb | 2 +- .../lib/active_record/relation/query_attribute.rb | 19 +++++++++++++++++++ .../lib/active_record/relation/query_methods.rb | 11 +---------- activerecord/lib/active_record/statement_cache.rb | 18 +++++++++--------- activerecord/test/cases/adapter_test.rb | 4 ++-- .../test/cases/adapters/mysql/connection_test.rb | 6 +++--- .../test/cases/adapters/mysql/mysql_adapter_test.rb | 6 +++--- .../cases/adapters/postgresql/connection_test.rb | 6 +++--- .../adapters/postgresql/postgresql_adapter_test.rb | 16 ++++++++++------ .../adapters/postgresql/schema_authorization_test.rb | 7 +++++-- .../test/cases/adapters/postgresql/schema_test.rb | 8 ++++++-- .../cases/adapters/sqlite3/sqlite3_adapter_test.rb | 8 +++----- activerecord/test/cases/bind_parameter_test.rb | 20 ++++---------------- activerecord/test/cases/explain_test.rb | 2 +- activerecord/test/cases/log_subscriber_test.rb | 8 -------- activerecord/test/cases/relation/merging_test.rb | 4 ++-- activerecord/test/cases/relations_test.rb | 2 +- 30 files changed, 116 insertions(+), 130 deletions(-) create mode 100644 activerecord/lib/active_record/relation/query_attribute.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index f1e784d771..4fcbc98c62 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -143,7 +143,7 @@ module ActiveRecord stmt.from scope.klass.arel_table stmt.wheres = arel.constraints - count = scope.klass.connection.delete(stmt, 'SQL', scope.bind_values) + count = scope.klass.connection.delete(stmt, 'SQL', scope.bound_attributes) end when :nullify count = scope.update_all(source_reflection.foreign_key => nil) diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb index 3a184cee37..a6ad09a38a 100644 --- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb @@ -75,7 +75,7 @@ module ActiveRecord column = klass.columns_hash[reflection.type.to_s] substitute = klass.connection.substitute_at(column) - binds << Attribute.with_cast_value(column.name, value, klass.type_for_attribute(column.name)) + binds << Relation::QueryAttribute.new(column.name, value, klass.type_for_attribute(column.name)) constraint = constraint.and table[reflection.type].eq substitute end diff --git a/activerecord/lib/active_record/attribute.rb b/activerecord/lib/active_record/attribute.rb index eadc17035e..48b50d9017 100644 --- a/activerecord/lib/active_record/attribute.rb +++ b/activerecord/lib/active_record/attribute.rb @@ -88,6 +88,11 @@ module ActiveRecord value_before_type_cast == other.value_before_type_cast && type == other.type end + alias eql? == + + def hash + [self.class, name, value_before_type_cast, type].hash + end protected 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 787d07c4c2..6022d0102b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -286,10 +286,10 @@ module ActiveRecord columns = schema_cache.columns_hash(table_name) binds = fixture.map do |name, value| - [columns[name], value] + Relation::QueryAttribute.new(name, value, columns[name].cast_type) end key_list = fixture.keys.map { |name| quote_column_name(name) } - value_list = prepare_binds_for_database(binds).map do |_, value| + value_list = prepare_binds_for_database(binds).map do |value| begin quote(value) rescue TypeError @@ -381,7 +381,7 @@ module ActiveRecord def binds_from_relation(relation, binds) if relation.is_a?(Relation) && binds.empty? - relation, binds = relation.arel, relation.bind_values + relation, binds = relation.arel, relation.bound_attributes end [relation, binds] end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index c18caa2a2f..62123cdc1f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -97,13 +97,7 @@ module ActiveRecord end def prepare_binds_for_database(binds) # :nodoc: - binds.map do |column, value| - if column - column_name = column.name - value = column.cast_type.type_cast_for_database(value) - end - [column_name, value] - end + binds.map(&:value_for_database) end private diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index c941c9f1eb..436552d300 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -114,7 +114,7 @@ module ActiveRecord class BindCollector < Arel::Collectors::Bind def compile(bvs, conn) casted_binds = conn.prepare_binds_for_database(bvs) - super(casted_binds.map { |_, value| conn.quote(value) }) + super(casted_binds.map { |value| conn.quote(value) }) end end diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 23d8389abb..16f50fc594 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -395,11 +395,9 @@ module ActiveRecord def exec_stmt(sql, name, binds) cache = {} - type_casted_binds = binds.map { |col, val| - [col, type_cast(val, col)] - } + type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) } - log(sql, name, type_casted_binds) do + log(sql, name, binds) do if binds.empty? stmt = @connection.prepare(sql) else @@ -410,7 +408,7 @@ module ActiveRecord end begin - stmt.execute(*type_casted_binds.map { |_, val| val }) + stmt.execute(*type_casted_binds) rescue Mysql::Error => e # Older versions of MySQL leave the prepared statement in a bad # place when an error occurs. To support older MySQL versions, we diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index f4f9747359..d789069f0b 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -609,12 +609,10 @@ module ActiveRecord def exec_cache(sql, name, binds) stmt_key = prepare_statement(sql) - type_casted_binds = binds.map { |col, val| - [col, type_cast(val, col)] - } + type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) } - log(sql, name, type_casted_binds, stmt_key) do - @connection.exec_prepared(stmt_key, type_casted_binds.map { |_, val| val }) + log(sql, name, binds, stmt_key) do + @connection.exec_prepared(stmt_key, type_casted_binds) end rescue ActiveRecord::StatementInvalid => e pgerror = e.original_exception diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 52dce6291a..225c2a8587 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -280,11 +280,9 @@ module ActiveRecord end def exec_query(sql, name = nil, binds = []) - type_casted_binds = binds.map { |col, val| - [col, type_cast(val, col)] - } + type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) } - log(sql, name, type_casted_binds) do + log(sql, name, binds) do # Don't cache statements if they are not prepared if without_prepared_statement?(binds) stmt = @connection.prepare(sql) @@ -302,7 +300,7 @@ module ActiveRecord stmt = cache[:stmt] cols = cache[:cols] ||= stmt.columns stmt.reset! - stmt.bind_params type_casted_binds.map { |_, val| val } + stmt.bind_params type_casted_binds end ActiveRecord::Result.new(cols, stmt.to_a) diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index a5c7279db9..6b26d7be78 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -20,18 +20,14 @@ module ActiveRecord @odd = false end - def render_bind(column, value) - if column - if column.binary? && value - # This specifically deals with the PG adapter that casts bytea columns into a Hash. - value = value[:value] if value.is_a?(Hash) - value = "<#{value.bytesize} bytes of binary data>" - end - - [column.name, value] + def render_bind(attribute) + value = if attribute.type.binary? && attribute.value + "<#{attribute.value.bytesize} bytes of binary data>" else - [nil, value] + attribute.value_for_database end + + [attribute.name, value] end def sql(event) @@ -47,9 +43,7 @@ module ActiveRecord binds = nil unless (payload[:binds] || []).empty? - binds = " " + payload[:binds].map { |col,v| - render_bind(col, v) - }.inspect + binds = " " + payload[:binds].map { |attr| render_bind(attr) }.inspect end if odd? diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 07e27fe59e..8073eb99dd 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -81,7 +81,7 @@ module ActiveRecord end relation = scope.where(@klass.primary_key => (id_was || id)) - bvs = binds + relation.bind_values + bvs = binds + relation.bound_attributes um = relation .arel .compile_update(substitutes, @klass.primary_key) @@ -95,11 +95,11 @@ module ActiveRecord def substitute_values(values) # :nodoc: binds = values.map do |arel_attr, value| - [@klass.columns_hash[arel_attr.name], value] + QueryAttribute.new(arel_attr.name, value, klass.type_for_attribute(arel_attr.name)) end - substitutes = values.each_with_index.map do |(arel_attr, _), i| - [arel_attr, @klass.connection.substitute_at(binds[i][0])] + substitutes = values.map do |(arel_attr, _)| + [arel_attr, connection.substitute_at(klass.columns_hash[arel_attr.name])] end [substitutes, binds] @@ -342,7 +342,7 @@ module ActiveRecord stmt.wheres = arel.constraints end - @klass.connection.update stmt, 'SQL', bind_values + @klass.connection.update stmt, 'SQL', bound_attributes end # Updates an object (or multiple objects) and saves it to the database, if validations pass. @@ -488,7 +488,7 @@ module ActiveRecord stmt.wheres = arel.constraints end - affected = @klass.connection.delete(stmt, 'SQL', bind_values) + affected = @klass.connection.delete(stmt, 'SQL', bound_attributes) reset affected @@ -558,9 +558,9 @@ module ActiveRecord find_with_associations { |rel| relation = rel } end - binds = relation.bind_values + binds = relation.bound_attributes binds = connection.prepare_binds_for_database(binds) - binds.map! { |_, value| connection.quote(value) } + binds.map! { |value| connection.quote(value) } collect = visitor.accept(relation.arel.ast, Arel::Collectors::Bind.new) collect.substitute_binds(binds).join end @@ -634,7 +634,7 @@ module ActiveRecord private def exec_queries - @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bind_values) + @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bound_attributes) preload = preload_values preload += includes_values unless eager_loading? diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 724bba5f87..83745bc169 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -166,7 +166,7 @@ module ActiveRecord relation.select_values = column_names.map { |cn| columns_hash.key?(cn) ? arel_table[cn] : cn } - result = klass.connection.select_all(relation.arel, nil, bind_values) + result = klass.connection.select_all(relation.arel, nil, bound_attributes) result.cast_values(klass.column_types) end end @@ -244,7 +244,7 @@ module ActiveRecord query_builder = relation.arel end - result = @klass.connection.select_all(query_builder, nil, bind_values) + result = @klass.connection.select_all(query_builder, nil, bound_attributes) row = result.first value = row && row.values.first column = result.column_types.fetch(column_alias) do @@ -300,7 +300,7 @@ module ActiveRecord relation.group_values = group relation.select_values = select_values - calculated_data = @klass.connection.select_all(relation, nil, relation.bind_values) + calculated_data = @klass.connection.select_all(relation, nil, relation.bound_attributes) if association key_ids = calculated_data.collect { |row| row[group_aliases.first] } diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 77bf1217e6..fb47d915ff 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -311,7 +311,7 @@ module ActiveRecord end end - connection.select_value(relation, "#{name} Exists", relation.bind_values) ? true : false + connection.select_value(relation, "#{name} Exists", relation.bound_attributes) ? true : false end # This method is called whenever no records are found with either a single @@ -365,7 +365,7 @@ module ActiveRecord [] else arel = relation.arel - rows = connection.select_all(arel, 'SQL', relation.bind_values) + rows = connection.select_all(arel, 'SQL', relation.bound_attributes) join_dependency.instantiate(rows, aliases) end end @@ -410,7 +410,7 @@ module ActiveRecord relation = relation.except(:select).select(values).distinct! arel = relation.arel - id_rows = @klass.connection.select_all(arel, 'SQL', relation.bind_values) + id_rows = @klass.connection.select_all(arel, 'SQL', relation.bound_attributes) id_rows.map {|row| row[primary_key]} end diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 7b605db88c..43e9afe853 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -108,7 +108,7 @@ module ActiveRecord else if can_be_bound?(column_name, value) result[column_name] = Arel::Nodes::BindParam.new - binds << Attribute.with_cast_value(column_name.to_s, value, table.type(column_name)) + binds << Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name)) end end end diff --git a/activerecord/lib/active_record/relation/query_attribute.rb b/activerecord/lib/active_record/relation/query_attribute.rb new file mode 100644 index 0000000000..e69319b4de --- /dev/null +++ b/activerecord/lib/active_record/relation/query_attribute.rb @@ -0,0 +1,19 @@ +require 'active_record/attribute' + +module ActiveRecord + class Relation + class QueryAttribute < Attribute + def type_cast(value) + value + end + + def value_for_database + @value_for_database ||= super + end + + def with_cast_value(value) + QueryAttribute.new(name, value, type) + end + end + end +end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index bc5f126a23..0078b0f32e 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1,4 +1,5 @@ require "active_record/relation/from_clause" +require "active_record/relation/query_attribute" require "active_record/relation/where_clause" require "active_record/relation/where_clause_factory" require 'active_model/forbidden_attributes_protection' @@ -96,16 +97,6 @@ module ActiveRecord from_clause.binds + arel.bind_values + where_clause.binds + having_clause.binds end - def bind_values - # convert to old style - bound_attributes.map do |attribute| - if attribute.name - column = ConnectionAdapters::Column.new(attribute.name, nil, attribute.type) - end - [column, attribute.value_before_type_cast] - end - end - def create_with_value # :nodoc: @values[:create_with] || {} end diff --git a/activerecord/lib/active_record/statement_cache.rb b/activerecord/lib/active_record/statement_cache.rb index 3047a81ec4..95986c820c 100644 --- a/activerecord/lib/active_record/statement_cache.rb +++ b/activerecord/lib/active_record/statement_cache.rb @@ -48,7 +48,7 @@ module ActiveRecord def sql_for(binds, connection) val = @values.dup binds = connection.prepare_binds_for_database(binds) - @indexes.each { |i| val[i] = connection.quote(binds.shift.last) } + @indexes.each { |i| val[i] = connection.quote(binds.shift) } val.join end end @@ -67,21 +67,21 @@ module ActiveRecord end class BindMap # :nodoc: - def initialize(bind_values) + def initialize(bound_attributes) @indexes = [] - @bind_values = bind_values + @bound_attributes = bound_attributes - bind_values.each_with_index do |(_, value), i| - if Substitute === value + bound_attributes.each_with_index do |attr, i| + if Substitute === attr.value @indexes << i end end end def bind(values) - bvs = @bind_values.map(&:dup) - @indexes.each_with_index { |offset,i| bvs[offset][1] = values[i] } - bvs + bas = @bound_attributes.dup + @indexes.each_with_index { |offset,i| bas[offset] = bas[offset].with_cast_value(values[i]) } + bas end end @@ -89,7 +89,7 @@ module ActiveRecord def self.create(connection, block = Proc.new) relation = block.call Params.new - bind_map = BindMap.new relation.bind_values + bind_map = BindMap.new relation.bound_attributes query_builder = connection.cacheable_query relation.arel new query_builder, bind_map end diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index 99e3d7021d..ecf1368a91 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -193,7 +193,7 @@ module ActiveRecord author = Author.create!(name: 'john') Post.create!(author: author, title: 'foo', body: 'bar') query = author.posts.where(title: 'foo').select(:title) - assert_equal({"title" => "foo"}, @connection.select_one(query.arel, nil, query.bind_values)) + assert_equal({"title" => "foo"}, @connection.select_one(query.arel, nil, query.bound_attributes)) assert_equal({"title" => "foo"}, @connection.select_one(query)) assert @connection.select_all(query).is_a?(ActiveRecord::Result) assert_equal "foo", @connection.select_value(query) @@ -203,7 +203,7 @@ module ActiveRecord def test_select_methods_passing_a_relation Post.create!(title: 'foo', body: 'bar') query = Post.where(title: 'foo').select(:title) - assert_equal({"title" => "foo"}, @connection.select_one(query.arel, nil, query.bind_values)) + assert_equal({"title" => "foo"}, @connection.select_one(query.arel, nil, query.bound_attributes)) assert_equal({"title" => "foo"}, @connection.select_one(query)) assert @connection.select_all(query).is_a?(ActiveRecord::Result) assert_equal "foo", @connection.select_value(query) diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb index ce01b16362..4762ef43b5 100644 --- a/activerecord/test/cases/adapters/mysql/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql/connection_test.rb @@ -94,7 +94,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase with_example_table do @connection.exec_query('INSERT INTO ex (id, data) VALUES (1, "foo")') result = @connection.exec_query( - 'SELECT id, data FROM ex WHERE id = ?', nil, [[nil, 1]]) + 'SELECT id, data FROM ex WHERE id = ?', nil, [ActiveRecord::Relation::QueryAttribute.new("id", 1, ActiveRecord::Type::Value.new)]) assert_equal 1, result.rows.length assert_equal 2, result.columns.length @@ -106,10 +106,10 @@ class MysqlConnectionTest < ActiveRecord::TestCase def test_exec_typecasts_bind_vals with_example_table do @connection.exec_query('INSERT INTO ex (id, data) VALUES (1, "foo")') - column = @connection.columns('ex').find { |col| col.name == 'id' } + bind = ActiveRecord::Relation::QueryAttribute.new("id", "1-fuu", ActiveRecord::Type::Integer.new) result = @connection.exec_query( - 'SELECT id, data FROM ex WHERE id = ?', nil, [[column, '1-fuu']]) + 'SELECT id, data FROM ex WHERE id = ?', nil, [bind]) assert_equal 1, result.rows.length assert_equal 2, result.columns.length diff --git a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb index 85db8f4614..1e6511620a 100644 --- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb +++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb @@ -129,10 +129,10 @@ module ActiveRecord private def insert(ctx, data, table='ex') - binds = data.map { |name, value| - [ctx.columns(table).find { |x| x.name == name }, value] + binds = data.map { |name, value| + Relation::QueryAttribute.new(name, value, Type::Value.new) } - columns = binds.map(&:first).map(&:name) + columns = binds.map(&:name) sql = "INSERT INTO #{table} (#{columns.join(", ")}) VALUES (#{(['?'] * columns.length).join(', ')})" diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb index ab7fd3c6d5..7bf8d12eae 100644 --- a/activerecord/test/cases/adapters/postgresql/connection_test.rb +++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb @@ -126,11 +126,11 @@ module ActiveRecord end def test_statement_key_is_logged - bindval = 1 - @connection.exec_query('SELECT $1::integer', 'SQL', [[nil, bindval]]) + bind = Relation::QueryAttribute.new(nil, 1, Type::Value.new) + @connection.exec_query('SELECT $1::integer', 'SQL', [bind]) name = @subscriber.payloads.last[:statement_name] assert name - res = @connection.exec_query("EXPLAIN (FORMAT JSON) EXECUTE #{name}(#{bindval})") + res = @connection.exec_query("EXPLAIN (FORMAT JSON) EXECUTE #{name}(1)") plan = res.column_types['QUERY PLAN'].type_cast_from_database res.rows.first.first assert_operator plan.length, :>, 0 end diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb index 6bb2b26cd5..15c2f48ede 100644 --- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb +++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb @@ -284,7 +284,7 @@ module ActiveRecord string = @connection.quote('foo') @connection.exec_query("INSERT INTO ex (id, data) VALUES (1, #{string})") result = @connection.exec_query( - 'SELECT id, data FROM ex WHERE id = $1', nil, [[nil, 1]]) + 'SELECT id, data FROM ex WHERE id = $1', nil, [bind_param(1)]) assert_equal 1, result.rows.length assert_equal 2, result.columns.length @@ -298,9 +298,9 @@ module ActiveRecord string = @connection.quote('foo') @connection.exec_query("INSERT INTO ex (id, data) VALUES (1, #{string})") - column = @connection.columns('ex').find { |col| col.name == 'id' } + bind = ActiveRecord::Relation::QueryAttribute.new("id", "1-fuu", ActiveRecord::Type::Integer.new) result = @connection.exec_query( - 'SELECT id, data FROM ex WHERE id = $1', nil, [[column, '1-fuu']]) + 'SELECT id, data FROM ex WHERE id = $1', nil, [bind]) assert_equal 1, result.rows.length assert_equal 2, result.columns.length @@ -437,10 +437,10 @@ module ActiveRecord private def insert(ctx, data) - binds = data.map { |name, value| - [ctx.columns('ex').find { |x| x.name == name }, value] + binds = data.map { |name, value| + bind_param(value, name) } - columns = binds.map(&:first).map(&:name) + columns = binds.map(&:name) bind_subs = columns.length.times.map { |x| "$#{x + 1}" } @@ -457,6 +457,10 @@ module ActiveRecord def connection_without_insert_returning ActiveRecord::Base.postgresql_connection(ActiveRecord::Base.configurations['arunit'].merge(:insert_returning => false)) end + + def bind_param(value, name = nil) + ActiveRecord::Relation::QueryAttribute.new(name, value, ActiveRecord::Type::Value.new) + end end end end diff --git a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb index 99c26c4bf7..6937145439 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb @@ -55,7 +55,7 @@ class SchemaAuthorizationTest < ActiveRecord::TestCase set_session_auth USERS.each do |u| set_session_auth u - assert_equal u, @connection.exec_query("SELECT name FROM #{TABLE_NAME} WHERE id = $1", 'SQL', [[nil, 1]]).first['name'] + assert_equal u, @connection.exec_query("SELECT name FROM #{TABLE_NAME} WHERE id = $1", 'SQL', [bind_param(1)]).first['name'] set_session_auth end end @@ -67,7 +67,7 @@ class SchemaAuthorizationTest < ActiveRecord::TestCase USERS.each do |u| @connection.clear_cache! set_session_auth u - assert_equal u, @connection.exec_query("SELECT name FROM #{TABLE_NAME} WHERE id = $1", 'SQL', [[nil, 1]]).first['name'] + assert_equal u, @connection.exec_query("SELECT name FROM #{TABLE_NAME} WHERE id = $1", 'SQL', [bind_param(1)]).first['name'] set_session_auth end end @@ -111,4 +111,7 @@ class SchemaAuthorizationTest < ActiveRecord::TestCase @connection.session_auth = auth || 'default' end + def bind_param(value) + ActiveRecord::Relation::QueryAttribute.new(nil, value, ActiveRecord::Type::Value.new) + end end diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index 642b63357f..83e35ad1a1 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -151,10 +151,10 @@ class SchemaTest < ActiveRecord::TestCase def test_schema_change_with_prepared_stmt altered = false - @connection.exec_query "select * from developers where id = $1", 'sql', [[nil, 1]] + @connection.exec_query "select * from developers where id = $1", 'sql', [bind_param(1)] @connection.exec_query "alter table developers add column zomg int", 'sql', [] altered = true - @connection.exec_query "select * from developers where id = $1", 'sql', [[nil, 1]] + @connection.exec_query "select * from developers where id = $1", 'sql', [bind_param(1)] ensure # We are not using DROP COLUMN IF EXISTS because that syntax is only # supported by pg 9.X @@ -435,6 +435,10 @@ class SchemaTest < ActiveRecord::TestCase assert_equal this_index_column, this_index.columns[0] assert_equal this_index_name, this_index.name end + + def bind_param(value) + ActiveRecord::Relation::QueryAttribute.new(nil, value, ActiveRecord::Type::Value.new) + end end class SchemaForeignKeyTest < ActiveRecord::TestCase diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb index 029663e7f4..0527bf8b15 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb @@ -83,8 +83,7 @@ module ActiveRecord def test_exec_insert with_example_table do - column = @conn.columns('ex').find { |col| col.name == 'number' } - vals = [[column, 10]] + vals = [Relation::QueryAttribute.new("number", 10, Type::Value.new)] @conn.exec_insert('insert into ex (number) VALUES (?)', 'SQL', vals) result = @conn.exec_query( @@ -157,7 +156,7 @@ module ActiveRecord with_example_table 'id int, data string' do @conn.exec_query('INSERT INTO ex (id, data) VALUES (1, "foo")') result = @conn.exec_query( - 'SELECT id, data FROM ex WHERE id = ?', nil, [[nil, 1]]) + 'SELECT id, data FROM ex WHERE id = ?', nil, [Relation::QueryAttribute.new(nil, 1, Type::Value.new)]) assert_equal 1, result.rows.length assert_equal 2, result.columns.length @@ -169,10 +168,9 @@ module ActiveRecord def test_exec_query_typecasts_bind_vals with_example_table 'id int, data string' do @conn.exec_query('INSERT INTO ex (id, data) VALUES (1, "foo")') - column = @conn.columns('ex').find { |col| col.name == 'id' } result = @conn.exec_query( - 'SELECT id, data FROM ex WHERE id = ?', nil, [[column, '1-fuu']]) + 'SELECT id, data FROM ex WHERE id = ?', nil, [Relation::QueryAttribute.new("id", "1-fuu", Type::Integer.new)]) assert_equal 1, result.rows.length assert_equal 2, result.columns.length diff --git a/activerecord/test/cases/bind_parameter_test.rb b/activerecord/test/cases/bind_parameter_test.rb index b6ab576d76..68d5a232ba 100644 --- a/activerecord/test/cases/bind_parameter_test.rb +++ b/activerecord/test/cases/bind_parameter_test.rb @@ -40,7 +40,7 @@ module ActiveRecord def test_binds_are_logged sub = @connection.substitute_at(@pk) - binds = [[@pk, 1]] + binds = [Relation::QueryAttribute.new("id", 1, Type::Value.new)] sql = "select * from topics where id = #{sub.to_sql}" @connection.exec_query(sql, 'SQL', binds) @@ -49,29 +49,17 @@ module ActiveRecord assert_equal binds, message[4][:binds] end - def test_binds_are_logged_after_type_cast - sub = @connection.substitute_at(@pk) - binds = [[@pk, "3"]] - sql = "select * from topics where id = #{sub.to_sql}" - - @connection.exec_query(sql, 'SQL', binds) - - message = @subscriber.calls.find { |args| args[4][:sql] == sql } - assert_equal [[@pk, 3]], message[4][:binds] - end - def test_find_one_uses_binds Topic.find(1) - binds = [[@pk, 1]] - message = @subscriber.calls.find { |args| args[4][:binds] == binds } + message = @subscriber.calls.find { |args| args[4][:binds].any? { |attr| attr.value == 1 } } assert message, 'expected a message with binds' end - def test_logs_bind_vars + def test_logs_bind_vars_after_type_cast payload = { :name => 'SQL', :sql => 'select * from topics where id = ?', - :binds => [[@pk, 10]] + :binds => [Relation::QueryAttribute.new("id", "10", Type::Integer.new)] } event = ActiveSupport::Notifications::Event.new( 'foo', diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb index 9d25bdd82a..f1d5511bb8 100644 --- a/activerecord/test/cases/explain_test.rb +++ b/activerecord/test/cases/explain_test.rb @@ -28,7 +28,7 @@ if ActiveRecord::Base.connection.supports_explain? assert_match "SELECT", sql if binds.any? assert_equal 1, binds.length - assert_equal "honda", binds.flatten.last + assert_equal "honda", binds.last.value else assert_match 'honda', sql end diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index 97c0350911..4192d12ff4 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -63,14 +63,6 @@ class LogSubscriberTest < ActiveRecord::TestCase assert_match(/ruby rails/, logger.debugs.first) end - def test_ignore_binds_payload_with_nil_column - event = Struct.new(:duration, :payload) - - logger = TestDebugLogSubscriber.new - logger.sql(event.new(0, sql: 'hi mom!', binds: [[nil, 1]])) - assert_equal 1, logger.debugs.length - end - def test_basic_query_logging Developer.all.load wait diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb index fe0854b7b4..0a2e874e4f 100644 --- a/activerecord/test/cases/relation/merging_test.rb +++ b/activerecord/test/cases/relation/merging_test.rb @@ -82,10 +82,10 @@ class RelationMergingTest < ActiveRecord::TestCase left = Post.where(title: "omg").where(comments_count: 1) right = Post.where(title: "wtf").where(title: "bbq") - expected = [left.bind_values[1]] + right.bind_values + expected = [left.bound_attributes[1]] + right.bound_attributes merged = left.merge(right) - assert_equal expected, merged.bind_values + assert_equal expected, merged.bound_attributes assert !merged.to_sql.include?("omg") assert merged.to_sql.include?("wtf") assert merged.to_sql.include?("bbq") diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 1e39a9bd28..8eb65d39ca 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -1758,7 +1758,7 @@ class RelationTest < ActiveRecord::TestCase end def test_merging_keeps_lhs_bind_parameters - binds = [ActiveRecord::Attribute.with_cast_value("id", 20, Post.type_for_attribute("id"))] + binds = [ActiveRecord::Relation::QueryAttribute.new("id", 20, Post.type_for_attribute("id"))] right = Post.where(id: 20) left = Post.where(id: 10) -- cgit v1.2.3