aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/associations/alias_tracker.rb19
-rw-r--r--activerecord/lib/active_record/associations/association.rb12
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb21
-rw-r--r--activerecord/lib/active_record/associations/builder/collection_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb23
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb1
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb5
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb11
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_association.rb58
-rw-r--r--activerecord/lib/active_record/associations/preloader.rb14
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb6
-rw-r--r--activerecord/lib/active_record/associations/singular_association.rb5
-rw-r--r--activerecord/lib/active_record/attribute.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb86
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb37
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb19
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb36
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb44
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb18
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb12
-rw-r--r--activerecord/lib/active_record/fixtures.rb4
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb9
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb7
-rw-r--r--activerecord/lib/active_record/persistence.rb2
-rw-r--r--activerecord/lib/active_record/query_cache.rb12
-rw-r--r--activerecord/lib/active_record/reflection.rb51
-rw-r--r--activerecord/lib/active_record/relation.rb4
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb6
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb14
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb31
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb2
-rw-r--r--activerecord/lib/active_record/scoping.rb7
-rw-r--r--activerecord/lib/active_record/scoping/named.rb5
-rw-r--r--activerecord/lib/active_record/tasks/mysql_database_tasks.rb4
-rw-r--r--activerecord/lib/active_record/timestamp.rb2
40 files changed, 301 insertions, 312 deletions
diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb
index 4a5c821607..104de4f69d 100644
--- a/activerecord/lib/active_record/associations/alias_tracker.rb
+++ b/activerecord/lib/active_record/associations/alias_tracker.rb
@@ -4,21 +4,21 @@ module ActiveRecord
module Associations
# Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
class AliasTracker # :nodoc:
- def self.create(connection, initial_table, type_caster)
+ def self.create(connection, initial_table)
aliases = Hash.new(0)
aliases[initial_table] = 1
- new connection, aliases, type_caster
+ new(connection, aliases)
end
- def self.create_with_joins(connection, initial_table, joins, type_caster)
+ def self.create_with_joins(connection, initial_table, joins)
if joins.empty?
- create(connection, initial_table, type_caster)
+ create(connection, initial_table)
else
aliases = Hash.new { |h, k|
h[k] = initial_count_for(connection, k, joins)
}
aliases[initial_table] = 1
- new connection, aliases, type_caster
+ new(connection, aliases)
end
end
@@ -51,17 +51,16 @@ module ActiveRecord
end
# table_joins is an array of arel joins which might conflict with the aliases we assign here
- def initialize(connection, aliases, type_caster)
+ def initialize(connection, aliases)
@aliases = aliases
@connection = connection
- @type_caster = type_caster
end
- def aliased_table_for(table_name, aliased_name)
+ def aliased_table_for(table_name, aliased_name, type_caster)
if aliases[table_name].zero?
# If it's zero, we can have our table_name
aliases[table_name] = 1
- Arel::Table.new(table_name, type_caster: @type_caster)
+ Arel::Table.new(table_name, type_caster: type_caster)
else
# Otherwise, we need to use an alias
aliased_name = @connection.table_alias_for(aliased_name)
@@ -74,7 +73,7 @@ module ActiveRecord
else
aliased_name
end
- Arel::Table.new(table_name, type_caster: @type_caster).alias(table_alias)
+ Arel::Table.new(table_name, type_caster: type_caster).alias(table_alias)
end
end
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index ee2e79889f..1138ae3462 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -30,14 +30,6 @@ module ActiveRecord
reset_scope
end
- # Returns the name of the table of the associated class:
- #
- # post.comments.aliased_table_name # => "comments"
- #
- def aliased_table_name
- klass.table_name
- end
-
# Resets the \loaded flag to +false+ and sets the \target to +nil+.
def reset
@loaded = false
@@ -94,7 +86,7 @@ module ActiveRecord
# actually gets built.
def association_scope
if klass
- @association_scope ||= AssociationScope.scope(self, klass.connection)
+ @association_scope ||= AssociationScope.scope(self)
end
end
@@ -276,7 +268,7 @@ module ActiveRecord
end
# Returns true if statement cache should be skipped on the association reader.
- def skip_statement_cache?
+ def skip_statement_cache?(scope)
reflection.has_scope? ||
scope.eager_loading? ||
klass.scope_attributes? ||
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index 1593b94f0c..6ef225b725 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -1,8 +1,8 @@
module ActiveRecord
module Associations
class AssociationScope #:nodoc:
- def self.scope(association, connection)
- INSTANCE.scope(association, connection)
+ def self.scope(association)
+ INSTANCE.scope(association)
end
def self.create(&block)
@@ -16,12 +16,12 @@ module ActiveRecord
INSTANCE = create
- def scope(association, connection)
+ def scope(association)
klass = association.klass
reflection = association.reflection
scope = klass.unscoped
owner = association.owner
- alias_tracker = AliasTracker.create connection, association.klass.table_name, klass.type_caster
+ alias_tracker = AliasTracker.create(klass.connection, klass.table_name)
chain_head, chain_tail = get_chain(reflection, association, alias_tracker)
scope.extending! reflection.extensions
@@ -112,7 +112,11 @@ module ActiveRecord
runtime_reflection = Reflection::RuntimeReflection.new(reflection, association)
previous_reflection = runtime_reflection
reflection.chain.drop(1).each do |refl|
- alias_name = tracker.aliased_table_for(refl.table_name, refl.alias_candidate(name))
+ alias_name = tracker.aliased_table_for(
+ refl.table_name,
+ refl.alias_candidate(name),
+ refl.klass.type_caster
+ )
proxy = ReflectionProxy.new(refl, alias_name)
previous_reflection.next = proxy
previous_reflection = proxy
@@ -138,7 +142,7 @@ module ActiveRecord
# Exclude the scope of the association itself, because that
# was already merged in the #scope method.
reflection.constraints.each do |scope_chain_item|
- item = eval_scope(reflection.klass, table, scope_chain_item, owner)
+ item = eval_scope(reflection, table, scope_chain_item, owner)
if scope_chain_item == refl.scope
scope.merge! item.except(:where, :includes)
@@ -159,9 +163,8 @@ module ActiveRecord
scope
end
- def eval_scope(klass, table, scope, owner)
- predicate_builder = PredicateBuilder.new(TableMetadata.new(klass, table))
- ActiveRecord::Relation.create(klass, table, predicate_builder).instance_exec(owner, &scope)
+ def eval_scope(reflection, table, scope, owner)
+ reflection.build_scope(table).instance_exec(owner, &scope)
end
end
end
diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb
index edeb6491bd..1fb8f76f2e 100644
--- a/activerecord/lib/active_record/associations/builder/collection_association.rb
+++ b/activerecord/lib/active_record/associations/builder/collection_association.rb
@@ -1,5 +1,3 @@
-# This class is inherited by the has_many and has_many_and_belongs_to_many association classes
-
require "active_record/associations"
module ActiveRecord::Associations::Builder # :nodoc:
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index edc53e2517..a49fb155ee 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -44,10 +44,7 @@ module ActiveRecord
if loaded?
target.pluck(reflection.association_primary_key)
else
- @association_ids ||= (
- column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
- scope.pluck(column)
- )
+ @association_ids ||= scope.pluck(reflection.association_primary_key)
end
end
@@ -69,6 +66,7 @@ module ActiveRecord
def reset
super
@target = []
+ @association_ids = nil
end
def find(*args)
@@ -300,13 +298,14 @@ module ActiveRecord
private
def find_target
- return scope.to_a if skip_statement_cache?
+ scope = self.scope
+ return scope.to_a if skip_statement_cache?(scope)
conn = klass.connection
sc = reflection.association_scope_cache(conn, owner) do
StatementCache.create(conn) { |params|
as = AssociationScope.create { params.bind }
- target_scope.merge as.scope(self, conn)
+ target_scope.merge!(as.scope(self))
}
end
@@ -357,7 +356,10 @@ module ActiveRecord
transaction do
add_to_target(build_record(attributes)) do |record|
yield(record) if block_given?
- insert_record(record, true, raise) { @_was_loaded = loaded? }
+ insert_record(record, true, raise) {
+ @_was_loaded = loaded?
+ @association_ids = nil
+ }
end
end
end
@@ -430,7 +432,12 @@ module ActiveRecord
records.each do |record|
raise_on_type_mismatch!(record)
add_to_target(record) do
- result &&= insert_record(record, true, raise) { @_was_loaded = loaded? } unless owner.new_record?
+ unless owner.new_record?
+ result &&= insert_record(record, true, raise) {
+ @_was_loaded = loaded?
+ @association_ids = nil
+ }
+ end
end
end
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 8cdb508c43..d77fcaf668 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -1114,6 +1114,7 @@ module ActiveRecord
end
def reset_scope # :nodoc:
+ @offsets = {}
@scope = nil
self
end
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 53ffb3b68d..2fd20b4368 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -109,6 +109,11 @@ module ActiveRecord
record
end
+ def remove_records(existing_records, records, method)
+ super
+ delete_through_records(records)
+ end
+
def target_reflection_has_associated_record?
!(through_reflection.belongs_to? && owner[through_reflection.foreign_key].blank?)
end
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index dfe8f221d8..04cdcb6a7f 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -93,7 +93,7 @@ module ActiveRecord
# joins # => []
#
def initialize(base, associations, joins, eager_loading: true)
- @alias_tracker = AliasTracker.create_with_joins(base.connection, base.table_name, joins, base.type_caster)
+ @alias_tracker = AliasTracker.create_with_joins(base.connection, base.table_name, joins)
@eager_loading = eager_loading
tree = self.class.make_tree associations
@join_root = JoinBase.new base, build(tree, base)
@@ -104,17 +104,17 @@ module ActiveRecord
join_root.drop(1).map!(&:reflection)
end
- def join_constraints(outer_joins, join_type)
+ def join_constraints(joins_to_add, join_type)
joins = join_root.children.flat_map { |child|
make_join_constraints(join_root, child, join_type)
}
- joins.concat outer_joins.flat_map { |oj|
+ joins.concat joins_to_add.flat_map { |oj|
if join_root.match? oj.join_root
walk join_root, oj.join_root
else
oj.join_root.children.flat_map { |child|
- make_outer_joins oj.join_root, child
+ make_join_constraints(oj.join_root, child, join_type)
}
end
}
@@ -185,7 +185,8 @@ module ActiveRecord
node.reflection.chain.map { |reflection|
alias_tracker.aliased_table_for(
reflection.table_name,
- table_alias_for(reflection, parent, reflection != node.reflection)
+ table_alias_for(reflection, parent, reflection != node.reflection),
+ reflection.klass.type_caster
)
}
end
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 97cfec0302..005410d598 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
@@ -34,34 +34,10 @@ module ActiveRecord
table = tables.shift
klass = reflection.klass
- join_keys = reflection.join_keys
- key = join_keys.key
- foreign_key = join_keys.foreign_key
+ join_scope = reflection.join_scope(table, foreign_table, foreign_klass)
- constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
-
- predicate_builder = PredicateBuilder.new(TableMetadata.new(klass, table))
- scope_chain_items = reflection.join_scopes(table, predicate_builder)
- klass_scope = reflection.klass_join_scope(table, predicate_builder)
-
- scope_chain_items.concat [klass_scope].compact
-
- rel = scope_chain_items.inject(scope_chain_items.shift) do |left, right|
- left.merge right
- end
-
- if rel && !rel.arel.constraints.empty?
- binds += rel.bound_attributes
- constraint = constraint.and rel.arel.constraints
- end
-
- if reflection.type
- value = foreign_klass.base_class.name
- column = klass.columns_hash[reflection.type.to_s]
-
- binds << Relation::QueryAttribute.new(column.name, value, klass.type_for_attribute(column.name))
- constraint = constraint.and klass.arel_attribute(reflection.type, table).eq(Arel::Nodes::BindParam.new)
- end
+ binds.concat join_scope.bound_attributes
+ constraint = join_scope.arel.constraints
joins << table.create_join(table, table.create_on(constraint), join_type)
@@ -72,34 +48,6 @@ module ActiveRecord
JoinInformation.new joins, binds
end
- # Builds equality condition.
- #
- # Example:
- #
- # class Physician < ActiveRecord::Base
- # has_many :appointments
- # end
- #
- # If I execute `Physician.joins(:appointments).to_a` then
- # klass # => Physician
- # table # => #<Arel::Table @name="appointments" ...>
- # key # => physician_id
- # foreign_table # => #<Arel::Table @name="physicians" ...>
- # foreign_key # => id
- #
- def build_constraint(klass, table, key, foreign_table, foreign_key)
- constraint = table[key].eq(foreign_table[foreign_key])
-
- if klass.finder_needs_type_condition?
- constraint = table.create_and([
- constraint,
- klass.send(:type_condition, table)
- ])
- end
-
- constraint
- end
-
def table
tables.first
end
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb
index 9f77f38b35..208d1b2670 100644
--- a/activerecord/lib/active_record/associations/preloader.rb
+++ b/activerecord/lib/active_record/associations/preloader.rb
@@ -147,7 +147,7 @@ module ActiveRecord
def preloaders_for_one(association, records, scope)
grouped_records(association, records).flat_map do |reflection, klasses|
klasses.map do |rhs_klass, rs|
- loader = preloader_for(reflection, rs, rhs_klass).new(rhs_klass, rs, reflection, scope)
+ loader = preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope)
loader.run self
loader
end
@@ -159,6 +159,7 @@ module ActiveRecord
records.each do |record|
next unless record
assoc = record.association(association)
+ next unless assoc.klass
klasses = h[assoc.reflection] ||= {}
(klasses[assoc.klass] ||= []) << record
end
@@ -180,20 +181,11 @@ module ActiveRecord
end
end
- class NullPreloader # :nodoc:
- def self.new(klass, owners, reflection, preload_scope); self; end
- def self.run(preloader); end
- def self.preloaded_records; []; end
- def self.owners; []; end
- end
-
# Returns a class containing the logic needed to load preload the data
# and attach it to a relation. For example +Preloader::Association+ or
# +Preloader::HasManyThrough+. The class returned implements a `run` method
# that accepts a preloader.
- def preloader_for(reflection, owners, rhs_klass)
- return NullPreloader unless rhs_klass
-
+ def preloader_for(reflection, owners)
if owners.first.association(reflection.name).loaded?
return AlreadyLoaded
end
diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb
index 8b954138cd..34587fd3f5 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -11,6 +11,8 @@ module ActiveRecord
end
def associated_records_by_owner(preloader)
+ through_scope = through_scope()
+
preloader.preload(owners,
through_reflection.name,
through_scope)
@@ -20,7 +22,7 @@ module ActiveRecord
[owner, Array(center)]
end
- reset_association owners, through_reflection.name
+ reset_association(owners, through_reflection.name, through_scope)
middle_records = through_records.flat_map(&:last)
@@ -63,7 +65,7 @@ module ActiveRecord
id_map
end
- def reset_association(owners, association_name)
+ def reset_association(owners, association_name, through_scope)
should_reset = (through_scope != through_reflection.klass.unscoped) ||
(options[:source_type] && through_reflection.collection?)
diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb
index ca138635d2..f8bbe4c2ed 100644
--- a/activerecord/lib/active_record/associations/singular_association.rb
+++ b/activerecord/lib/active_record/associations/singular_association.rb
@@ -36,13 +36,14 @@ module ActiveRecord
end
def find_target
- return scope.take if skip_statement_cache?
+ scope = self.scope
+ return scope.take if skip_statement_cache?(scope)
conn = klass.connection
sc = reflection.association_scope_cache(conn, owner) do
StatementCache.create(conn) { |params|
as = AssociationScope.create { params.bind }
- target_scope.merge(as.scope(self, conn)).limit(1)
+ target_scope.merge!(as.scope(self)).limit(1)
}
end
diff --git a/activerecord/lib/active_record/attribute.rb b/activerecord/lib/active_record/attribute.rb
index 38281158d8..78662433eb 100644
--- a/activerecord/lib/active_record/attribute.rb
+++ b/activerecord/lib/active_record/attribute.rb
@@ -122,7 +122,7 @@ module ActiveRecord
def encode_with(coder)
coder["name"] = name
- coder["value_before_type_cast"] = value_before_type_cast if value_before_type_cast
+ coder["value_before_type_cast"] = value_before_type_cast unless value_before_type_cast.nil?
coder["type"] = type if type
coder["original_attribute"] = original_attribute if original_attribute
coder["value"] = value if defined?(@value)
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 c6811a4802..879626b72a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -51,9 +51,7 @@ module ActiveRecord
# Returns a single value from a record
def select_value(arel, name = nil, binds = [])
- if result = select_rows(arel, name, binds).first
- result.first
- end
+ single_value_from_rows(select_rows(arel, name, binds))
end
# Returns an array of the values of the first column in a select:
@@ -68,6 +66,18 @@ module ActiveRecord
select_all(arel, name, binds).rows
end
+ def query_value(sql, name = nil) # :nodoc:
+ single_value_from_rows(query(sql, name))
+ end
+
+ def query_values(sql, name = nil) # :nodoc:
+ query(sql, name).map(&:first)
+ end
+
+ def query(sql, name = nil) # :nodoc:
+ exec_query(sql, name).rows
+ end
+
# Executes the SQL statement in the context of this connection and returns
# the raw result from the connection adapter.
# Note: depending on your database connector, the result returned by this
@@ -296,6 +306,9 @@ module ActiveRecord
# Inserts the given fixture into the table. Overridden in adapters that require
# something beyond a simple insert (eg. Oracle).
+ # Most of adapters should implement `insert_fixtures` that leverages bulk SQL insert.
+ # We keep this method to provide fallback
+ # for databases like sqlite that do not support bulk inserts.
def insert_fixture(fixture, table_name)
fixture = fixture.stringify_keys
@@ -308,16 +321,52 @@ module ActiveRecord
raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
end
end
- key_list = fixture.keys.map { |name| quote_column_name(name) }
- value_list = binds.map(&:value_for_database).map do |value|
- begin
- quote(value)
- rescue TypeError
- quote(YAML.dump(value))
+
+ table = Arel::Table.new(table_name)
+
+ values = binds.map do |bind|
+ value = with_yaml_fallback(bind.value_for_database)
+ [table[bind.name], value]
+ end
+
+ manager = Arel::InsertManager.new
+ manager.into(table)
+ manager.insert(values)
+ execute manager.to_sql, "Fixture Insert"
+ end
+
+ # Inserts a set of fixtures into the table. Overridden in adapters that require
+ # something beyond a simple insert (eg. Oracle).
+ def insert_fixtures(fixtures, table_name)
+ return if fixtures.empty?
+
+ columns = schema_cache.columns_hash(table_name)
+
+ values = fixtures.map do |fixture|
+ fixture = fixture.stringify_keys
+
+ unknown_columns = fixture.keys - columns.keys
+ if unknown_columns.any?
+ raise Fixture::FixtureError, %(table "#{table_name}" has no columns named #{unknown_columns.map(&:inspect).join(', ')}.)
+ end
+
+ columns.map do |name, column|
+ if fixture.key?(name)
+ type = lookup_cast_type_from_column(column)
+ bind = Relation::QueryAttribute.new(name, fixture[name], type)
+ with_yaml_fallback(bind.value_for_database)
+ else
+ Arel.sql("DEFAULT")
+ end
end
end
- execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", "Fixture Insert"
+ table = Arel::Table.new(table_name)
+ manager = Arel::InsertManager.new
+ manager.into(table)
+ columns.each_key { |column| manager.columns << table[column] }
+ manager.values = manager.create_values_list(values)
+ execute manager.to_sql, "Fixtures Insert"
end
def empty_insert_statement_value
@@ -371,7 +420,11 @@ module ActiveRecord
end
def last_inserted_id(result)
- row = result.rows.first
+ single_value_from_rows(result.rows)
+ end
+
+ def single_value_from_rows(rows)
+ row = rows.first
row && row.first
end
@@ -381,6 +434,17 @@ module ActiveRecord
end
[relation, binds]
end
+
+ # Fixture value is quoted by Arel, however scalar values
+ # are not quotable. In this case we want to convert
+ # the column value to YAML.
+ def with_yaml_fallback(value)
+ if value.is_a?(Hash) || value.is_a?(Array)
+ YAML.dump(value)
+ else
+ value
+ end
+ end
end
end
end
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 e53ba4e666..c352ddfc11 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -108,6 +108,7 @@ module ActiveRecord
"sql.active_record",
sql: sql,
binds: binds,
+ type_casted_binds: -> { type_casted_binds(binds) },
name: name,
connection_id: object_id,
cached: true,
@@ -123,6 +124,7 @@ module ActiveRecord
# If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
# queries should not be cached.
def locked?(arel)
+ arel = arel.arel if arel.is_a?(Relation)
arel.respond_to?(:locked) && arel.locked
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 9bcd1e85f7..6aed00584a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -31,7 +31,7 @@ module ActiveRecord
# Returns the relation names useable to back Active Record models.
# For most adapters this means all #tables and #views.
def data_sources
- select_values(data_source_sql, "SCHEMA")
+ query_values(data_source_sql, "SCHEMA")
rescue NotImplementedError
tables | views
end
@@ -41,14 +41,14 @@ module ActiveRecord
# data_source_exists?(:ebooks)
#
def data_source_exists?(name)
- select_values(data_source_sql(name), "SCHEMA").any? if name.present?
+ query_values(data_source_sql(name), "SCHEMA").any? if name.present?
rescue NotImplementedError
data_sources.include?(name.to_s)
end
# Returns an array of table names defined in the database.
def tables
- select_values(data_source_sql(type: "BASE TABLE"), "SCHEMA")
+ query_values(data_source_sql(type: "BASE TABLE"), "SCHEMA")
end
# Checks to see if the table +table_name+ exists on the database.
@@ -56,14 +56,14 @@ module ActiveRecord
# table_exists?(:developers)
#
def table_exists?(table_name)
- select_values(data_source_sql(table_name, type: "BASE TABLE"), "SCHEMA").any? if table_name.present?
+ query_values(data_source_sql(table_name, type: "BASE TABLE"), "SCHEMA").any? if table_name.present?
rescue NotImplementedError
tables.include?(table_name.to_s)
end
# Returns an array of view names defined in the database.
def views
- select_values(data_source_sql(type: "VIEW"), "SCHEMA")
+ query_values(data_source_sql(type: "VIEW"), "SCHEMA")
end
# Checks to see if the view +view_name+ exists on the database.
@@ -71,7 +71,7 @@ module ActiveRecord
# view_exists?(:ebooks)
#
def view_exists?(view_name)
- select_values(data_source_sql(view_name, type: "VIEW"), "SCHEMA").any? if view_name.present?
+ query_values(data_source_sql(view_name, type: "VIEW"), "SCHEMA").any? if view_name.present?
rescue NotImplementedError
views.include?(view_name.to_s)
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index ca20ef89da..062fede1ce 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -120,11 +120,11 @@ module ActiveRecord
end
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
- select_value("SELECT GET_LOCK(#{quote(lock_name)}, #{timeout})") == 1
+ query_value("SELECT GET_LOCK(#{quote(lock_name)}, #{timeout})") == 1
end
def release_advisory_lock(lock_name) # :nodoc:
- select_value("SELECT RELEASE_LOCK(#{quote(lock_name)})") == 1
+ query_value("SELECT RELEASE_LOCK(#{quote(lock_name)})") == 1
end
def native_database_types
@@ -152,7 +152,7 @@ module ActiveRecord
# REFERENTIAL INTEGRITY ====================================
def disable_referential_integrity #:nodoc:
- old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
+ old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
begin
update("SET FOREIGN_KEY_CHECKS = 0")
@@ -267,7 +267,7 @@ module ActiveRecord
end
def current_database
- select_value "SELECT DATABASE() as db"
+ query_value("SELECT database()", "SCHEMA")
end
# Returns the database character set.
@@ -287,7 +287,7 @@ module ActiveRecord
def table_comment(table_name) # :nodoc:
scope = quoted_scope(table_name)
- select_value(<<-SQL.strip_heredoc, "SCHEMA")
+ query_value(<<-SQL.strip_heredoc, "SCHEMA")
SELECT table_comment
FROM information_schema.tables
WHERE table_schema = #{scope[:schema]}
@@ -393,7 +393,7 @@ module ActiveRecord
scope = quoted_scope(table_name)
- fk_info = select_all(<<-SQL.strip_heredoc, "SCHEMA")
+ fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA")
SELECT fk.referenced_table_name AS 'to_table',
fk.referenced_column_name AS 'primary_key',
fk.column_name AS 'column',
@@ -470,7 +470,7 @@ module ActiveRecord
# SHOW VARIABLES LIKE 'name'
def show_variable(name)
- select_value("SELECT @@#{name}", "SCHEMA")
+ query_value("SELECT @@#{name}", "SCHEMA")
rescue ActiveRecord::StatementInvalid
nil
end
@@ -480,7 +480,7 @@ module ActiveRecord
scope = quoted_scope(table_name)
- select_values(<<-SQL.strip_heredoc, "SCHEMA")
+ query_values(<<-SQL.strip_heredoc, "SCHEMA")
SELECT column_name
FROM information_schema.key_column_usage
WHERE constraint_name = 'PRIMARY'
@@ -526,8 +526,25 @@ module ActiveRecord
index.using == :btree || super
end
+ def insert_fixtures(*)
+ without_sql_mode("NO_AUTO_VALUE_ON_ZERO") { super }
+ end
+
private
+ def without_sql_mode(mode)
+ result = execute("SELECT @@SESSION.sql_mode")
+ current_mode = result.first[0]
+ return yield unless current_mode.include?(mode)
+
+ sql_mode = "REPLACE(@@sql_mode, '#{mode}', '')"
+ execute("SET @@SESSION.sql_mode = #{sql_mode}")
+ yield
+ ensure
+ sql_mode = "CONCAT(@@sql_mode, ',#{mode}')"
+ execute("SET @@SESSION.sql_mode = #{sql_mode}")
+ end
+
def initialize_type_map(m)
super
@@ -677,7 +694,7 @@ module ActiveRecord
auto_increment: column.auto_increment?
}
- current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", "SCHEMA")["Type"]
+ current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
td = create_table_definition(table_name)
cd = td.new_column_definition(new_column_name, current_type, options)
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
@@ -787,7 +804,7 @@ module ActiveRecord
end
def create_table_info(table_name) # :nodoc:
- select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
+ exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
end
def arel_visitor
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
index 8c67a7a80b..bda482a00f 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
@@ -13,13 +13,8 @@ module ActiveRecord
result
end
- # Returns an array of arrays containing the field values.
- # Order is the same as that returned by +columns+.
- def select_rows(arel, name = nil, binds = []) # :nodoc:
- select_result(arel, name, binds) do |result|
- @connection.next_result while @connection.more_results?
- result.to_a
- end
+ def query(sql, name = nil) # :nodoc:
+ execute(sql, name).to_a
end
# Executes the SQL statement in the context of this connection.
@@ -58,16 +53,6 @@ module ActiveRecord
@connection.last_id
end
- def select_result(arel, name, binds)
- arel, binds = binds_from_relation(arel, binds)
- sql = to_sql(arel, binds)
- if without_prepared_statement?(binds)
- execute_and_free(sql, name) { |result| yield result }
- else
- exec_stmt_and_free(sql, name, binds, cache_stmt: true) { |_, result| yield result }
- end
- end
-
def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
# made since we established the connection
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
index e2ba0ba1a0..eff96e329f 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
@@ -47,7 +47,7 @@ module ActiveRecord
def schema_collation(column)
if column.collation && table_name = column.table_name
@table_collation_cache ||= {}
- @table_collation_cache[table_name] ||= select_one("SHOW TABLE STATUS LIKE '#{table_name}'")["Collation"]
+ @table_collation_cache[table_name] ||= exec_query("SHOW TABLE STATUS LIKE #{quote(table_name)}", "SCHEMA").first["Collation"]
column.collation.inspect if column.collation != @table_collation_cache[table_name]
end
end
@@ -64,7 +64,7 @@ module ActiveRecord
" WHERE table_schema = #{scope[:schema]}" \
" AND table_name = #{scope[:name]}" \
" AND column_name = #{quote(column.name)}"
- select_value(sql, "SCHEMA").inspect
+ query_value(sql, "SCHEMA").inspect
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
index 705e6063dc..ebf1715ed0 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
@@ -7,30 +7,6 @@ module ActiveRecord
PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", binds))
end
- def select_value(arel, name = nil, binds = []) # :nodoc:
- select_result(arel, name, binds) do |result|
- result.getvalue(0, 0) if result.ntuples > 0 && result.nfields > 0
- end
- end
-
- def select_values(arel, name = nil, binds = []) # :nodoc:
- select_result(arel, name, binds) do |result|
- if result.nfields > 0
- result.column_values(0)
- else
- []
- end
- end
- end
-
- # Executes a SELECT query and returns an array of rows. Each row is an
- # array of field values.
- def select_rows(arel, name = nil, binds = []) # :nodoc:
- select_result(arel, name, binds) do |result|
- result.values
- end
- end
-
# The internal PostgreSQL identifier of the money data type.
MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
# The internal PostgreSQL identifier of the BYTEA data type.
@@ -171,18 +147,14 @@ module ActiveRecord
end
private
+ # Returns the current ID of a table's sequence.
+ def last_insert_id_result(sequence_name)
+ exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
+ end
def suppress_composite_primary_key(pk)
pk unless pk.is_a?(Array)
end
-
- def select_result(arel, name, binds)
- arel, binds = binds_from_relation(arel, binds)
- sql = to_sql(arel, binds)
- execute_and_clear(sql, name, binds) do |result|
- yield result
- end
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index 44eb666965..ee4230c6f2 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -78,7 +78,7 @@ module ActiveRecord
private
def lookup_cast_type(sql_type)
- super(select_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
+ super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
end
def _quote(value)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 0a614e133d..a710ea6cc9 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -60,7 +60,7 @@ module ActiveRecord
# Returns true if schema exists.
def schema_exists?(name)
- select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
+ query_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
end
# Verifies existence of an index with a given name.
@@ -73,7 +73,7 @@ module ActiveRecord
table = quoted_scope(table_name)
index = quoted_scope(index_name)
- select_value(<<-SQL, "SCHEMA").to_i > 0
+ query_value(<<-SQL, "SCHEMA").to_i > 0
SELECT COUNT(*)
FROM pg_class t
INNER JOIN pg_index d ON t.oid = d.indrelid
@@ -163,7 +163,7 @@ module ActiveRecord
def table_comment(table_name) # :nodoc:
scope = quoted_scope(table_name, type: "BASE TABLE")
if scope[:name]
- select_value(<<-SQL.strip_heredoc, "SCHEMA")
+ query_value(<<-SQL.strip_heredoc, "SCHEMA")
SELECT pg_catalog.obj_description(c.oid, 'pg_class')
FROM pg_catalog.pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
@@ -176,32 +176,32 @@ module ActiveRecord
# Returns the current database name.
def current_database
- select_value("SELECT current_database()", "SCHEMA")
+ query_value("SELECT current_database()", "SCHEMA")
end
# Returns the current schema name.
def current_schema
- select_value("SELECT current_schema", "SCHEMA")
+ query_value("SELECT current_schema", "SCHEMA")
end
# Returns the current database encoding format.
def encoding
- select_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")
+ query_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")
end
# Returns the current database collation.
def collation
- select_value("SELECT datcollate FROM pg_database WHERE datname = current_database()", "SCHEMA")
+ query_value("SELECT datcollate FROM pg_database WHERE datname = current_database()", "SCHEMA")
end
# Returns the current database ctype.
def ctype
- select_value("SELECT datctype FROM pg_database WHERE datname = current_database()", "SCHEMA")
+ query_value("SELECT datctype FROM pg_database WHERE datname = current_database()", "SCHEMA")
end
# Returns an array of schema names.
def schema_names
- select_values(<<-SQL, "SCHEMA")
+ query_values(<<-SQL, "SCHEMA")
SELECT nspname
FROM pg_namespace
WHERE nspname !~ '^pg_.*'
@@ -234,12 +234,12 @@ module ActiveRecord
# Returns the active schema search path.
def schema_search_path
- @schema_search_path ||= select_value("SHOW search_path", "SCHEMA")
+ @schema_search_path ||= query_value("SHOW search_path", "SCHEMA")
end
# Returns the current client message level.
def client_min_messages
- select_value("SHOW client_min_messages", "SCHEMA")
+ query_value("SHOW client_min_messages", "SCHEMA")
end
# Set the client message level.
@@ -257,7 +257,7 @@ module ActiveRecord
end
def serial_sequence(table, column)
- select_value("SELECT pg_get_serial_sequence('#{table}', '#{column}')", "SCHEMA")
+ query_value("SELECT pg_get_serial_sequence(#{quote(table)}, #{quote(column)})", "SCHEMA")
end
# Sets the sequence of a table's primary key to the specified value.
@@ -268,7 +268,7 @@ module ActiveRecord
if sequence
quoted_sequence = quote_table_name(sequence)
- select_value("SELECT setval('#{quoted_sequence}', #{value})", "SCHEMA")
+ query_value("SELECT setval(#{quote(quoted_sequence)}, #{value})", "SCHEMA")
else
@logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
end
@@ -290,18 +290,16 @@ module ActiveRecord
if pk && sequence
quoted_sequence = quote_table_name(sequence)
- max_pk = select_value("select MAX(#{quote_column_name pk}) from #{quote_table_name(table)}")
+ max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
if max_pk.nil?
if postgresql_version >= 100000
- minvalue = select_value("SELECT seqmin from pg_sequence where seqrelid = '#{quoted_sequence}'::regclass")
+ minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
else
- minvalue = select_value("SELECT min_value FROM #{quoted_sequence}")
+ minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
end
end
- select_value(<<-end_sql, "SCHEMA")
- SELECT setval('#{quoted_sequence}', #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})
- end_sql
+ query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})", "SCHEMA")
end
end
@@ -325,7 +323,7 @@ module ActiveRecord
AND seq.relnamespace = nsp.oid
AND cons.contype = 'p'
AND dep.classid = 'pg_class'::regclass
- AND dep.refobjid = '#{quote_table_name(table)}'::regclass
+ AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
end_sql
if result.nil? || result.empty?
@@ -343,7 +341,7 @@ module ActiveRecord
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
- WHERE t.oid = '#{quote_table_name(table)}'::regclass
+ WHERE t.oid = #{quote(quote_table_name(table))}::regclass
AND cons.contype = 'p'
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
end_sql
@@ -360,7 +358,7 @@ module ActiveRecord
end
def primary_keys(table_name) # :nodoc:
- select_values(<<-SQL.strip_heredoc, "SCHEMA")
+ query_values(<<-SQL.strip_heredoc, "SCHEMA")
SELECT a.attname
FROM (
SELECT indrelid, indkey, generate_subscripts(indkey, 1) idx
@@ -511,7 +509,7 @@ module ActiveRecord
def foreign_keys(table_name)
scope = quoted_scope(table_name)
- fk_info = select_all(<<-SQL.strip_heredoc, "SCHEMA")
+ fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA")
SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete
FROM pg_constraint c
JOIN pg_class t1 ON c.conrelid = t1.oid
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 5287dd6a51..e63563afc4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -312,14 +312,14 @@ module ActiveRecord
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
end
- select_value("SELECT pg_try_advisory_lock(#{lock_id});")
+ query_value("SELECT pg_try_advisory_lock(#{lock_id})")
end
def release_advisory_lock(lock_id) # :nodoc:
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
end
- select_value("SELECT pg_advisory_unlock(#{lock_id})")
+ query_value("SELECT pg_advisory_unlock(#{lock_id})")
end
def enable_extension(name)
@@ -336,15 +336,14 @@ module ActiveRecord
def extension_enabled?(name)
if supports_extensions?
- res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
- "SCHEMA"
+ res = exec_query("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", "SCHEMA")
res.cast_values.first
end
end
def extensions
if supports_extensions?
- exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values
+ exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
else
super
end
@@ -352,14 +351,14 @@ module ActiveRecord
# Returns the configured supported identifier length supported by PostgreSQL
def table_alias_length
- @max_identifier_length ||= select_value("SHOW max_identifier_length", "SCHEMA").to_i
+ @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
end
alias index_name_length table_alias_length
# Set the authorized user for this session
def session_auth=(user)
clear_cache!
- exec_query "SET SESSION AUTHORIZATION #{user}"
+ execute("SET SESSION AUTHORIZATION #{user}")
end
def use_insert_returning?
@@ -723,11 +722,6 @@ module ActiveRecord
end
end
- # Returns the current ID of a table's sequence.
- def last_insert_id_result(sequence_name)
- exec_query("SELECT currval('#{sequence_name}')", "SQL")
- end
-
# Returns the list of a table's column names, data types, and default values.
#
# The underlying query is roughly:
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb
index 118f373bdc..31e83f9260 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb
@@ -11,7 +11,7 @@ module ActiveRecord
end
exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row|
- index_sql = select_value(<<-SQL, "SCHEMA")
+ index_sql = query_value(<<-SQL, "SCHEMA")
SELECT sql
FROM sqlite_master
WHERE name = #{quote(row['name'])} AND type = 'index'
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 7233325d5a..17c63d4b5f 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -169,7 +169,7 @@ module ActiveRecord
# REFERENTIAL INTEGRITY ====================================
def disable_referential_integrity # :nodoc:
- old = select_value("PRAGMA foreign_keys")
+ old = query_value("PRAGMA foreign_keys")
begin
execute("PRAGMA foreign_keys = OFF")
@@ -337,7 +337,7 @@ module ActiveRecord
alias :add_belongs_to :add_reference
def foreign_keys(table_name)
- fk_info = select_all("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
+ fk_info = exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
fk_info.map do |row|
options = {
column: row["from"],
@@ -349,6 +349,12 @@ module ActiveRecord
end
end
+ def insert_fixtures(rows, table_name)
+ rows.each do |row|
+ insert_fixture(row, table_name)
+ end
+ end
+
private
def table_structure(table_name)
@@ -437,7 +443,7 @@ module ActiveRecord
end
def sqlite_version
- @sqlite_version ||= SQLite3Adapter::Version.new(select_value("SELECT sqlite_version(*)"))
+ @sqlite_version ||= SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
end
def translate_exception(exception, message)
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index a6b66c91e3..e9acb8acae 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -567,9 +567,7 @@ module ActiveRecord
end
table_rows.each do |fixture_set_name, rows|
- rows.each do |row|
- conn.insert_fixture(row, fixture_set_name)
- end
+ conn.insert_fixtures(rows, fixture_set_name)
end
# Cap primary key sequences to max(pk).
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 3c7110369b..522da6a571 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -62,8 +62,8 @@ module ActiveRecord
def increment_lock
lock_col = self.class.locking_column
- previous_lock_value = send(lock_col).to_i
- send(lock_col + "=", previous_lock_value + 1)
+ previous_lock_value = send(lock_col)
+ send("#{lock_col}=", previous_lock_value + 1)
end
def _create_record(attribute_names = self.attribute_names, *)
@@ -107,7 +107,8 @@ module ActiveRecord
# If something went wrong, revert the locking_column value.
rescue Exception
- send(lock_col + "=", previous_lock_value.to_i)
+ send("#{lock_col}=", previous_lock_value.to_i)
+
raise
end
end
@@ -127,7 +128,7 @@ module ActiveRecord
if locking_enabled?
locking_column = self.class.locking_column
- relation = relation.where(locking_column => _read_attribute(locking_column))
+ relation = relation.where(locking_column => read_attribute_before_type_cast(locking_column))
end
relation
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
index 2297c77835..e39ca5f6dc 100644
--- a/activerecord/lib/active_record/log_subscriber.rb
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -29,7 +29,7 @@ module ActiveRecord
binds = nil
unless (payload[:binds] || []).empty?
- casted_params = type_casted_binds(payload[:binds], payload[:type_casted_binds])
+ casted_params = type_casted_binds(payload[:type_casted_binds])
binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
render_bind(attr, value)
}.inspect
@@ -42,9 +42,8 @@ module ActiveRecord
end
private
-
- def type_casted_binds(binds, casted_binds)
- casted_binds || ActiveRecord::Base.connection.type_casted_binds(binds)
+ def type_casted_binds(casted_binds)
+ casted_binds.respond_to?(:call) ? casted_binds.call : casted_binds
end
def render_bind(attr, value)
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index f652c7c3a1..b2dba5516e 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -526,7 +526,7 @@ module ActiveRecord
if locking_enabled?
locking_column = self.class.locking_column
- scope = scope.where(locking_column => _read_attribute(locking_column))
+ scope = scope.where(locking_column => read_attribute_before_type_cast(locking_column))
changes[locking_column] = increment_lock
end
diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb
index ec246e97bc..e4c2e1f86f 100644
--- a/activerecord/lib/active_record/query_cache.rb
+++ b/activerecord/lib/active_record/query_cache.rb
@@ -5,20 +5,20 @@ module ActiveRecord
# Enable the query cache within the block if Active Record is configured.
# If it's not, it will execute the given block.
def cache(&block)
- if connected?
- connection.cache(&block)
- else
+ if configurations.empty?
yield
+ else
+ connection.cache(&block)
end
end
# Disable the query cache within the block if Active Record is configured.
# If it's not, it will execute the given block.
def uncached(&block)
- if connected?
- connection.uncached(&block)
- else
+ if configurations.empty?
yield
+ else
+ connection.uncached(&block)
end
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index e8ee8279fd..8ff2f50fdb 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -171,7 +171,7 @@ module ActiveRecord
JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
def join_keys
- get_join_keys klass
+ @join_keys ||= get_join_keys(klass)
end
# Returns a list of scopes that should be applied for this Reflection
@@ -185,10 +185,30 @@ module ActiveRecord
end
deprecate :scope_chain
+ def join_scope(table, foreign_table, foreign_klass)
+ predicate_builder = predicate_builder(table)
+ scope_chain_items = join_scopes(table, predicate_builder)
+ klass_scope = klass_join_scope(table, predicate_builder)
+
+ key = join_keys.key
+ foreign_key = join_keys.foreign_key
+
+ klass_scope.where!(table[key].eq(foreign_table[foreign_key]))
+
+ if klass.finder_needs_type_condition?
+ klass_scope.where!(klass.send(:type_condition, table))
+ end
+
+ if type
+ klass_scope.where!(type => foreign_klass.base_class.sti_name)
+ end
+
+ scope_chain_items.inject(klass_scope, &:merge!)
+ end
+
def join_scopes(table, predicate_builder) # :nodoc:
if scope
- [ActiveRecord::Relation.create(klass, table, predicate_builder)
- .instance_exec(&scope)]
+ [build_scope(table, predicate_builder).instance_exec(&scope)]
else
[]
end
@@ -200,12 +220,7 @@ module ActiveRecord
scope.joins_values = scope.left_outer_joins_values = [].freeze
}
else
- relation = ActiveRecord::Relation.create(
- klass,
- table,
- predicate_builder,
- )
- klass.send(:build_default_scope, relation)
+ klass.default_scoped(build_scope(table, predicate_builder))
end
end
@@ -287,12 +302,19 @@ module ActiveRecord
JoinKeys.new(join_pk(association_klass), join_fk)
end
+ def build_scope(table, predicate_builder = predicate_builder(table))
+ Relation.create(klass, table, predicate_builder)
+ end
+
protected
def actual_source_reflection # FIXME: this is a horrible name
self
end
private
+ def predicate_builder(table)
+ PredicateBuilder.new(TableMetadata.new(klass, table))
+ end
def join_pk(_)
foreign_key
@@ -570,7 +592,7 @@ module ActiveRecord
end
VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
- INVALID_AUTOMATIC_INVERSE_OPTIONS = [:conditions, :through, :polymorphic, :foreign_key]
+ INVALID_AUTOMATIC_INVERSE_OPTIONS = [:conditions, :through, :foreign_key]
def add_as_source(seed)
seed
@@ -641,9 +663,8 @@ module ActiveRecord
# us from being able to guess the inverse automatically. First, the
# <tt>inverse_of</tt> option cannot be set to false. Second, we must
# have <tt>has_many</tt>, <tt>has_one</tt>, <tt>belongs_to</tt> associations.
- # Third, we must not have options such as <tt>:polymorphic</tt> or
- # <tt>:foreign_key</tt> which prevent us from correctly guessing the
- # inverse association.
+ # Third, we must not have options such as <tt>:foreign_key</tt>
+ # which prevent us from correctly guessing the inverse association.
#
# Anything with a scope can additionally ruin our attempt at finding an
# inverse, so we exclude reflections with scopes.
@@ -746,10 +767,6 @@ module ActiveRecord
end
class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
- def initialize(name, scope, options, active_record)
- super
- end
-
def macro; :has_and_belongs_to_many; end
def collection?
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 7a8f9abb36..52f5d5f3e3 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -18,7 +18,7 @@ module ActiveRecord
attr_reader :table, :klass, :loaded, :predicate_builder
alias :model :klass
alias :loaded? :loaded
- alias :locked? :locked
+ alias :locked? :lock_value
def initialize(klass, table, predicate_builder, values = {})
@klass = klass
@@ -333,7 +333,7 @@ module ActiveRecord
# Please check unscoped if you want to remove all previous scopes (including
# the default_scope) during the execution of a block.
def scoping
- previous, klass.current_scope = klass.current_scope, self
+ previous, klass.current_scope = klass.current_scope(true), self
yield
ensure
klass.current_scope = previous
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index c562f214c9..8a54f8f2c3 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -41,7 +41,7 @@ module ActiveRecord
unless column_name.nil?
ActiveSupport::Deprecation.warn \
"When `count' is called with a block, it ignores other arguments. " \
- "This behavior is now deprecated and will result in an ArgumentError in Rails 5.3."
+ "This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
end
return super()
@@ -86,7 +86,7 @@ module ActiveRecord
unless column_name.nil?
ActiveSupport::Deprecation.warn \
"When `sum' is called with a block, it ignores other arguments. " \
- "This behavior is now deprecated and will result in an ArgumentError in Rails 5.3."
+ "This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
end
return super()
@@ -311,7 +311,7 @@ module ActiveRecord
relation.group_values = group_fields
relation.select_values = select_values
- calculated_data = @klass.connection.select_all(relation, nil, relation.bound_attributes)
+ calculated_data = @klass.connection.select_all(relation.arel, nil, relation.bound_attributes)
if association
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index dada0fddf8..48c4dcdef4 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -44,8 +44,6 @@ module ActiveRecord
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
:connection, :columns_hash, to: :klass
- delegate :ast, :locked, to: :arel
-
module ClassSpecificRelation # :nodoc:
extend ActiveSupport::Concern
@@ -91,6 +89,8 @@ module ActiveRecord
self.class.delegate_to_scoped_klass(method)
scoping { @klass.public_send(method, *args, &block) }
elsif arel.respond_to?(method)
+ ActiveSupport::Deprecation.warn \
+ "Delegating #{method} to arel is deprecated and will be removed in Rails 6.0."
self.class.delegate method, to: :arel
arel.public_send(method, *args, &block)
else
@@ -115,15 +115,5 @@ module ActiveRecord
def respond_to_missing?(method, _)
super || @klass.respond_to?(method) || arel.respond_to?(method)
end
-
- def method_missing(method, *args, &block)
- if @klass.respond_to?(method)
- scoping { @klass.public_send(method, *args, &block) }
- elsif arel.respond_to?(method)
- arel.public_send(method, *args, &block)
- else
- super
- end
- end
end
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 3c848d3206..eee0f36f63 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -147,8 +147,7 @@ module ActiveRecord
def last(limit = nil)
return find_last(limit) if loaded? || limit_value
- result = limit(limit)
- result.order!(arel_attribute(primary_key)) if order_values.empty? && primary_key
+ result = ordered_relation.limit(limit)
result = result.reverse_order!
limit ? result.reverse : result.first
@@ -316,7 +315,7 @@ module ActiveRecord
relation = construct_relation_for_exists(relation, conditions)
- connection.select_value(relation, "#{name} Exists", relation.bound_attributes) ? true : false
+ connection.select_value(relation.arel, "#{name} Exists", relation.bound_attributes) ? true : false
rescue ::RangeError
false
end
@@ -377,8 +376,7 @@ module ActiveRecord
if ActiveRecord::NullRelation === relation
[]
else
- arel = relation.arel
- rows = connection.select_all(arel, "SQL", relation.bound_attributes)
+ rows = connection.select_all(relation.arel, "SQL", relation.bound_attributes)
join_dependency.instantiate(rows, aliases)
end
end
@@ -425,9 +423,8 @@ module ActiveRecord
"#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
relation = relation.except(:select).select(values).distinct!
- arel = relation.arel
- id_rows = @klass.connection.select_all(arel, "SQL", relation.bound_attributes)
+ id_rows = @klass.connection.select_all(relation.arel, "SQL", relation.bound_attributes)
id_rows.map { |row| row[primary_key] }
end
@@ -535,11 +532,7 @@ module ActiveRecord
if loaded?
records[index, limit] || []
else
- relation = if order_values.empty? && primary_key
- order(arel_attribute(primary_key).asc)
- else
- self
- end
+ relation = ordered_relation
if limit_value.nil? || index < limit_value
relation = relation.offset(offset_index + index) unless index.zero?
@@ -554,11 +547,7 @@ module ActiveRecord
if loaded?
records[-index]
else
- relation = if order_values.empty? && primary_key
- order(arel_attribute(primary_key).asc)
- else
- self
- end
+ relation = ordered_relation
relation.to_a[-index]
# TODO: can be made more performant on large result sets by
@@ -572,5 +561,13 @@ module ActiveRecord
def find_last(limit)
limit ? records.last(limit) : records.last
end
+
+ def ordered_relation
+ if order_values.empty? && primary_key
+ order(arel_attribute(primary_key).asc)
+ else
+ self
+ 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 6ccdd7adcb..d44f6fd572 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -1122,7 +1122,7 @@ module ActiveRecord
validate_order_args(order_args)
references = order_args.grep(String)
- references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
+ references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
references!(references) if references.any?
# if a symbol is given we prepend the quoted table name
diff --git a/activerecord/lib/active_record/scoping.rb b/activerecord/lib/active_record/scoping.rb
index 7c00e7e4ed..94e0ef6724 100644
--- a/activerecord/lib/active_record/scoping.rb
+++ b/activerecord/lib/active_record/scoping.rb
@@ -10,8 +10,8 @@ module ActiveRecord
end
module ClassMethods
- def current_scope #:nodoc:
- ScopeRegistry.value_for(:current_scope, self)
+ def current_scope(skip_inherited_scope = false) # :nodoc:
+ ScopeRegistry.value_for(:current_scope, self, skip_inherited_scope)
end
def current_scope=(scope) #:nodoc:
@@ -75,8 +75,9 @@ module ActiveRecord
end
# Obtains the value for a given +scope_type+ and +model+.
- def value_for(scope_type, model)
+ def value_for(scope_type, model, skip_inherited_scope = false)
raise_invalid_scope_type!(scope_type)
+ return @registry[scope_type][model.name] if skip_inherited_scope
klass = model
base = model.base_class
while klass <= base
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index a61fdd6454..b4026fabb2 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -29,8 +29,7 @@ module ActiveRecord
end
end
- def default_scoped # :nodoc:
- scope = relation
+ def default_scoped(scope = relation) # :nodoc:
build_default_scope(scope) || scope
end
@@ -160,7 +159,7 @@ module ActiveRecord
if body.respond_to?(:to_proc)
singleton_class.send(:define_method, name) do |*args|
scope = all
- scope = scope.scoping { instance_exec(*args, &body) || scope }
+ scope = scope.instance_exec(*args, &body) || scope
scope = scope.extending(extension) if extension
scope
end
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index fedc21010b..88cbd6f6f5 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -59,7 +59,6 @@ module ActiveRecord
args.concat(["--no-data"])
args.concat(["--routines"])
args.concat(["--skip-comments"])
- args.concat(Array(extra_flags)) if extra_flags
ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
if ignore_tables.any?
@@ -67,6 +66,7 @@ module ActiveRecord
end
args.concat(["#{configuration['database']}"])
+ args.unshift(*extra_flags) if extra_flags
run_cmd("mysqldump", args, "dumping")
end
@@ -75,7 +75,7 @@ module ActiveRecord
args = prepare_command_options
args.concat(["--execute", %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}])
args.concat(["--database", "#{configuration['database']}"])
- args.concat(Array(extra_flags)) if extra_flags
+ args.unshift(*extra_flags) if extra_flags
run_cmd("mysql", args, "loading")
end
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index 55f3a194a9..dc4540eea6 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -126,7 +126,7 @@ module ActiveRecord
self.class.send(:current_time_from_proper_timezone)
end
- def max_updated_column_timestamp(timestamp_names = self.class.send(:timestamp_attributes_for_update))
+ def max_updated_column_timestamp(timestamp_names = timestamp_attributes_for_update_in_model)
timestamp_names
.map { |attr| self[attr] }
.compact