aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r--activerecord/lib/active_record/associations.rb2
-rw-r--r--activerecord/lib/active_record/associations/association.rb2
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb15
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb2
-rw-r--r--activerecord/lib/active_record/attribute_assignment.rb10
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb2
-rw-r--r--activerecord/lib/active_record/autosave_association.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb110
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb35
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb70
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb35
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/cast.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb53
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb2
-rw-r--r--activerecord/lib/active_record/core.rb1
-rw-r--r--activerecord/lib/active_record/fixtures.rb2
-rw-r--r--activerecord/lib/active_record/migration.rb2
-rw-r--r--activerecord/lib/active_record/persistence.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb2
-rw-r--r--activerecord/lib/active_record/scoping/default.rb8
-rw-r--r--activerecord/lib/active_record/version.rb13
29 files changed, 300 insertions, 147 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 0c670bdaa1..eb08f72286 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -966,7 +966,7 @@ module ActiveRecord
# For +has_and_belongs_to_many+, <tt>delete</tt> and <tt>destroy</tt> are the same: they
# cause the records in the join table to be removed.
#
- # For +has_many+, <tt>destroy</tt> and <tt>destory_all</tt> will always call the <tt>destroy</tt> method of the
+ # For +has_many+, <tt>destroy</tt> and <tt>destroy_all</tt> will always call the <tt>destroy</tt> method of the
# record(s) being removed so that callbacks are run. However <tt>delete</tt> and <tt>delete_all</tt> will either
# do the deletion according to the strategy specified by the <tt>:dependent</tt> option, or
# if no <tt>:dependent</tt> option is given, then it will follow the default strategy.
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 868095f068..4c4b0f08e5 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -203,7 +203,7 @@ module ActiveRecord
# Raises ActiveRecord::AssociationTypeMismatch unless +record+ is of
# the kind of the class of the associated objects. Meant to be used as
# a sanity check when you are about to assign an associated record.
- def raise_on_type_mismatch(record)
+ def raise_on_type_mismatch!(record)
unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize)
message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
raise ActiveRecord::AssociationTypeMismatch, message
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index 54b1a69774..8eec4f56af 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -8,7 +8,7 @@ module ActiveRecord
end
def replace(record)
- raise_on_type_mismatch(record) if record
+ raise_on_type_mismatch!(record) if record
update_counters(record)
replace_keys(record)
diff --git a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
index 88ce03a3cd..eae5eed3a1 100644
--- a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
@@ -22,7 +22,7 @@ module ActiveRecord
reflection.polymorphic_inverse_of(record.class)
end
- def raise_on_type_mismatch(record)
+ def raise_on_type_mismatch!(record)
# A polymorphic association cannot have a type mismatch, by definition
end
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index fbcb21118d..9ac561b997 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -31,7 +31,7 @@ module ActiveRecord::Associations::Builder
end
def belongs_to_counter_cache_before_destroy_for_#{name}
- unless marked_for_destruction?
+ unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == #{foreign_key.to_sym.inspect}
record = #{name}
record.class.decrement_counter(:#{cache_column}, record.id) unless record.nil?
end
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 906560bd44..2385c90c1a 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -79,7 +79,7 @@ module ActiveRecord
if block_given?
load_target.find(*args) { |*block_args| yield(*block_args) }
else
- if options[:finder_sql]
+ if options[:finder_sql] || options[:inverse_of]
find_by_scan(*args)
else
scope.find(*args)
@@ -318,7 +318,7 @@ module ActiveRecord
# Replace this collection with +other_array+. This will perform a diff
# and delete/add only records that have changed.
def replace(other_array)
- other_array.each { |val| raise_on_type_mismatch(val) }
+ other_array.each { |val| raise_on_type_mismatch!(val) }
original_target = load_target.dup
if owner.new_record?
@@ -465,7 +465,7 @@ module ActiveRecord
def delete_or_destroy(records, method)
records = records.flatten
- records.each { |record| raise_on_type_mismatch(record) }
+ records.each { |record| raise_on_type_mismatch!(record) }
existing_records = records.reject { |r| r.new_record? }
if existing_records.empty?
@@ -506,9 +506,9 @@ module ActiveRecord
result = true
records.flatten.each do |record|
- raise_on_type_mismatch(record)
- add_to_target(record) do |r|
- result &&= insert_record(record) unless owner.new_record?
+ raise_on_type_mismatch!(record)
+ add_to_target(record) do |rec|
+ result &&= insert_record(rec) unless owner.new_record?
end
end
@@ -567,7 +567,8 @@ module ActiveRecord
end
end
- # If using a custom finder_sql, #find scans the entire collection.
+ # If using a custom finder_sql or if the :inverse_of option has been
+ # specified, then #find scans the entire collection.
def find_by_scan(*args)
expects_array = args.first.kind_of?(Array)
ids = args.flatten.compact.map{ |arg| arg.to_i }.uniq
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index b7b4d7e3ae..29fae809da 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -22,7 +22,7 @@ module ActiveRecord
else
if options[:dependent] == :destroy
# No point in executing the counter update since we're going to destroy the parent anyway
- load_target.each(&:mark_for_destruction)
+ load_target.each { |t| t.destroyed_by_association = reflection }
destroy_all
else
delete_all
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 d1458f30ba..a74dd1cdab 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -29,7 +29,7 @@ module ActiveRecord
def concat(*records)
unless owner.new_record?
records.flatten.each do |record|
- raise_on_type_mismatch(record)
+ raise_on_type_mismatch!(record)
record.save! if record.new_record?
end
end
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index ee816d2392..98bd010f70 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -22,7 +22,7 @@ module ActiveRecord
end
def replace(record, save = true)
- raise_on_type_mismatch(record) if record
+ raise_on_type_mismatch!(record) if record
load_target
# If target and record are nil, or target is equal to record,
diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb
index ecfa556ab4..e536f5ebcc 100644
--- a/activerecord/lib/active_record/attribute_assignment.rb
+++ b/activerecord/lib/active_record/attribute_assignment.rb
@@ -81,7 +81,7 @@ module ActiveRecord
end
def extract_callstack_for_multiparameter_attributes(pairs)
- attributes = { }
+ attributes = {}
pairs.each do |(multiparameter_name, value)|
attribute_name = multiparameter_name.split("(").first
@@ -146,7 +146,7 @@ module ActiveRecord
end
else
# else column is a timestamp, so if Date bits were not provided, error
- validate_missing_parameters!([1,2,3])
+ validate_required_parameters!([1,2,3])
# If Date bits were provided but blank, then return nil
return if blank_date_parameter?
@@ -172,14 +172,14 @@ module ActiveRecord
def read_other(klass)
max_position = extract_max_param
positions = (1..max_position)
- validate_missing_parameters!(positions)
+ validate_required_parameters!(positions)
set_values = values.values_at(*positions)
klass.new(*set_values)
end
# Checks whether some blank date parameter exists. Note that this is different
- # than the validate_missing_parameters! method, since it just checks for blank
+ # than the validate_required_parameters! method, since it just checks for blank
# positions instead of missing ones, and does not raise in case one blank position
# exists. The caller is responsible to handle the case of this returning true.
def blank_date_parameter?
@@ -187,7 +187,7 @@ module ActiveRecord
end
# If some position is not provided, it errors out a missing parameter exception.
- def validate_missing_parameters!(positions)
+ def validate_required_parameters!(positions)
if missing_parameter = positions.detect { |position| !values.key?(position) }
raise ArgumentError.new("Missing Parameter - #{name}(#{missing_parameter})")
end
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index aa92343f76..769e005c8f 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -348,7 +348,7 @@ module ActiveRecord
# Filters the primary keys and readonly attributes from the attribute names.
def attributes_for_update(attribute_names)
attribute_names.select do |name|
- column_for_attribute(name) && !pk_attribute?(name) && !readonly_attribute?(name)
+ column_for_attribute(name) && !readonly_attribute?(name)
end
end
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 55542262b0..0df3e57947 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -212,6 +212,7 @@ module ActiveRecord
# Reloads the attributes of the object as usual and clears <tt>marked_for_destruction</tt> flag.
def reload(options = nil)
@marked_for_destruction = false
+ @destroyed_by_association = nil
super
end
@@ -231,6 +232,19 @@ module ActiveRecord
@marked_for_destruction
end
+ # Records the association that is being destroyed and destroying this
+ # record in the process.
+ def destroyed_by_association=(reflection)
+ @destroyed_by_association = reflection
+ end
+
+ # Returns the association for the parent being destroyed.
+ #
+ # Used to avoid updating the counter cache unnecessarily.
+ def destroyed_by_association
+ @destroyed_by_association
+ end
+
# Returns whether or not this record has been changed in any way (including whether
# any of its nested autosave associations are likewise changed)
def changed_for_autosave?
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 9137504d15..bf2f945448 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -238,7 +238,7 @@ module ActiveRecord
@checkout_timeout = spec.config[:checkout_timeout] || 5
@dead_connection_timeout = spec.config[:dead_connection_timeout] || 5
- @reaper = Reaper.new(self, spec.config[:reaping_frequency] || 10)
+ @reaper = Reaper.new self, spec.config[:reaping_frequency]
@reaper.run
# default max pool size to 5
@@ -406,7 +406,9 @@ module ActiveRecord
synchronize do
stale = Time.now - @dead_connection_timeout
connections.dup.each do |conn|
- remove conn if conn.in_use? && stale > conn.last_use && !conn.active?
+ if conn.in_use? && stale > conn.last_use && !conn.active?
+ remove conn
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 902dbd148e..2f17e47b7c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -8,41 +8,21 @@ module ActiveRecord
# Abstract representation of an index definition on a table. Instances of
# this type are typically created and returned by methods in database
# adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes
- class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where) #:nodoc:
+ class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :using) #:nodoc:
end
# Abstract representation of a column definition. Instances of this type
# are typically created by methods in TableDefinition, and added to the
# +columns+ attribute of said TableDefinition object, in order to be used
# for generating a number of table creation or table changing SQL statements.
- class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :precision, :scale, :default, :null) #:nodoc:
+ class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :primary_key) #:nodoc:
def string_to_binary(value)
value
end
- def sql_type
- base.type_to_sql(type.to_sym, limit, precision, scale)
- end
-
def primary_key?
- type.to_sym == :primary_key
- end
-
- def to_sql
- column_sql = "#{base.quote_column_name(name)} #{sql_type}"
- column_options = {}
- column_options[:null] = null unless null.nil?
- column_options[:default] = default unless default.nil?
- column_options[:column] = self
- add_column_options!(column_sql, column_options) unless primary_key?
- column_sql
+ primary_key || type.to_sym == :primary_key
end
-
- private
-
- def add_column_options!(sql, options)
- base.add_column_options!(sql, options)
- end
end
# Represents the schema of an SQL table in an abstract way. This class
@@ -68,19 +48,25 @@ module ActiveRecord
class TableDefinition
# An array of ColumnDefinition objects, representing the column changes
# that have been defined.
- attr_accessor :columns, :indexes
+ attr_accessor :indexes
+ attr_reader :name, :temporary, :options
- def initialize(base)
- @columns = []
+ def initialize(types, name, temporary, options)
@columns_hash = {}
@indexes = {}
- @base = base
+ @native = types
+ @temporary = temporary
+ @options = options
+ @name = name
end
+ def columns; @columns_hash.values; end
+
# Appends a primary key definition to the table definition.
# Can be called multiple times, but this is probably not a good idea.
- def primary_key(name)
- column(name, :primary_key)
+ def primary_key(name, type = :primary_key, options = {})
+ options[:primary_key] = true
+ column(name, type, options)
end
# Returns a ColumnDefinition for the column with name +name+.
@@ -233,20 +219,14 @@ module ActiveRecord
raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
end
- column = self[name] || new_column_definition(@base, name, type)
-
- limit = options.fetch(:limit) do
- native[type][:limit] if native[type].is_a?(Hash)
- end
-
- column.limit = limit
- column.precision = options[:precision]
- column.scale = options[:scale]
- column.default = options[:default]
- column.null = options[:null]
+ @columns_hash[name] = new_column_definition(name, type, options)
self
end
+ def remove_column(name)
+ @columns_hash.delete name.to_s
+ end
+
[:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
define_method column_type do |*args|
options = args.extract_options!
@@ -283,23 +263,26 @@ module ActiveRecord
end
alias :belongs_to :references
- # Returns a String whose contents are the column definitions
- # concatenated together. This string can then be prepended and appended to
- # to generate the final SQL to create the table.
- def to_sql
- columns.map { |c| c.to_sql } * ', '
- end
+ def new_column_definition(name, type, options) # :nodoc:
+ column = create_column_definition name, type
+ limit = options.fetch(:limit) do
+ native[type][:limit] if native[type].is_a?(Hash)
+ end
- private
- def create_column_definition(base, name, type)
- ColumnDefinition.new base, name, type
+ column.limit = limit
+ column.precision = options[:precision]
+ column.scale = options[:scale]
+ column.default = options[:default]
+ column.null = options[:null]
+ column.first = options[:first]
+ column.after = options[:after]
+ column.primary_key = type == :primary_key || options[:primary_key]
+ column
end
- def new_column_definition(base, name, type)
- definition = create_column_definition base, name, type
- @columns << definition
- @columns_hash[name] = definition
- definition
+ private
+ def create_column_definition(name, type)
+ ColumnDefinition.new name, type
end
def primary_key_column_name
@@ -308,7 +291,24 @@ module ActiveRecord
end
def native
- @base.native_database_types
+ @native
+ end
+ end
+
+ class AlterTable # :nodoc:
+ attr_reader :adds
+
+ def initialize(td)
+ @td = td
+ @adds = []
+ end
+
+ def name; @td.name; end
+
+ def add_column(name, type, options)
+ name = name.to_s
+ type = type.to_sym
+ @adds << @td.new_column_definition(name, type, options)
end
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 cd4409295f..26cacbde37 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -171,14 +171,14 @@ module ActiveRecord
#
# See also TableDefinition#column for details on how to create columns.
def create_table(table_name, options = {})
- td = create_table_definition
+ td = create_table_definition table_name, options[:temporary], options[:options]
unless options[:id] == false
pk = options.fetch(:primary_key) {
Base.get_primary_key table_name.to_s.singularize
}
- td.primary_key pk
+ td.primary_key pk, options.fetch(:id, :primary_key), options
end
yield td if block_given?
@@ -187,11 +187,7 @@ module ActiveRecord
drop_table(table_name, options)
end
- create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
- create_sql << "#{quote_table_name(table_name)} ("
- create_sql << td.to_sql
- create_sql << ") #{options[:options]}"
- execute create_sql
+ execute schema_creation.accept td
td.indexes.each_pair { |c,o| add_index table_name, c, o }
end
@@ -359,9 +355,9 @@ module ActiveRecord
# Adds a new column to the named table.
# See TableDefinition#column for details of the options you can use.
def add_column(table_name, column_name, type, options = {})
- add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
- add_column_options!(add_column_sql, options)
- execute(add_column_sql)
+ at = create_alter_table table_name
+ at.add_column(column_name, type, options)
+ execute schema_creation.accept at
end
# Removes the given columns from the table definition.
@@ -501,7 +497,14 @@ module ActiveRecord
#
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
#
- # Note: only supported by PostgreSQL.
+ # ====== Creating an index with a specific method
+ # add_index(:developers, :name, :using => 'btree')
+ # generates
+ # CREATE INDEX index_developers_on_name ON developers USING btree (name) -- PostgreSQL
+ # CREATE INDEX index_developers_on_name USING btree ON developers (name) -- MySQL
+ #
+ # Note: only supported by PostgreSQL and MySQL
+ #
def add_index(table_name, column_name, options = {})
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
@@ -749,7 +752,7 @@ module ActiveRecord
index_name = index_name(table_name, column: column_names)
if Hash === options # legacy support, since this param was a string
- options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal)
+ options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using)
index_type = options[:unique] ? "UNIQUE" : ""
index_name = options[:name].to_s if options.key?(:name)
@@ -829,8 +832,12 @@ module ActiveRecord
end
private
- def create_table_definition
- TableDefinition.new(self)
+ def create_table_definition(name, temporary, options)
+ TableDefinition.new native_database_types, name, temporary, options
+ end
+
+ def create_alter_table(name)
+ AlterTable.new create_table_definition(name, false, {})
end
def update_table_definition(table_name, base)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 7949bcb5ce..0f4ab68b61 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -18,6 +18,7 @@ module ActiveRecord
autoload :ColumnDefinition
autoload :TableDefinition
autoload :Table
+ autoload :AlterTable
end
autoload_at 'active_record/connection_adapters/abstract/connection_pool' do
@@ -100,6 +101,75 @@ module ActiveRecord
@visitor = nil
end
+ class SchemaCreation
+ def initialize(conn)
+ @conn = conn
+ @cache = {}
+ end
+
+ def accept(o)
+ m = @cache[o.class] ||= "visit_#{o.class.name.split('::').last}"
+ send m, o
+ end
+
+ private
+
+ def visit_AlterTable(o)
+ sql = "ALTER TABLE #{quote_table_name(o.name)} "
+ sql << o.adds.map { |col| visit_AddColumn col }.join(' ')
+ end
+
+ def visit_AddColumn(o)
+ sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
+ sql = "ADD #{quote_column_name(o.name)} #{sql_type}"
+ add_column_options!(sql, column_options(o))
+ end
+
+ def visit_ColumnDefinition(o)
+ sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
+ column_sql = "#{quote_column_name(o.name)} #{sql_type}"
+ add_column_options!(column_sql, column_options(o)) unless o.primary_key?
+ column_sql
+ end
+
+ def visit_TableDefinition(o)
+ create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE "
+ create_sql << "#{quote_table_name(o.name)} ("
+ create_sql << o.columns.map { |c| accept c }.join(', ')
+ create_sql << ") #{o.options}"
+ create_sql
+ end
+
+ def column_options(o)
+ column_options = {}
+ column_options[:null] = o.null unless o.null.nil?
+ column_options[:default] = o.default unless o.default.nil?
+ column_options[:column] = o
+ column_options
+ end
+
+ def quote_column_name(name)
+ @conn.quote_column_name name
+ end
+
+ def quote_table_name(name)
+ @conn.quote_table_name name
+ end
+
+ def type_to_sql(type, limit, precision, scale)
+ @conn.type_to_sql type.to_sym, limit, precision, scale
+ end
+
+ def add_column_options!(column_sql, column_options)
+ @conn.add_column_options! column_sql, column_options
+ column_sql
+ end
+ end
+
+ def schema_creation
+ SchemaCreation.new self
+ end
+
def lease
synchronize do
unless in_use
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 f88f5742a8..1f111a0976 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -3,6 +3,27 @@ require 'arel/visitors/bind_visitor'
module ActiveRecord
module ConnectionAdapters
class AbstractMysqlAdapter < AbstractAdapter
+ class SchemaCreation < AbstractAdapter::SchemaCreation
+ private
+
+ def visit_AddColumn(o)
+ add_column_position!(super, o)
+ end
+
+ def add_column_position!(sql, column)
+ if column.first
+ sql << " FIRST"
+ elsif column.after
+ sql << " AFTER #{quote_column_name(column.after)}"
+ end
+ sql
+ end
+ end
+
+ def schema_creation
+ SchemaCreation.new self
+ end
+
class Column < ConnectionAdapters::Column # :nodoc:
attr_reader :collation, :strict
@@ -411,6 +432,7 @@ module ActiveRecord
next if row[:Key_name] == 'PRIMARY' # skip the primary key
current_index = row[:Key_name]
indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [])
+ indexes.last.using = row[:Index_type].downcase.to_sym
end
indexes.last.columns << row[:Column_name]
@@ -459,10 +481,6 @@ module ActiveRecord
rename_table_indexes(table_name, new_name)
end
- def add_column(table_name, column_name, type, options = {})
- execute("ALTER TABLE #{quote_table_name(table_name)} #{add_column_sql(table_name, column_name, type, options)}")
- end
-
def change_column_default(table_name, column_name, default)
column = column_for(table_name, column_name)
change_column table_name, column_name, column.sql_type, :default => default
@@ -487,6 +505,15 @@ module ActiveRecord
rename_column_indexes(table_name, column_name, new_column_name)
end
+ def add_index(table_name, column_name, options = {}) #:nodoc:
+ if options.is_a?(Hash) && options[:using]
+ index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} USING #{options[:using]} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
+ else
+ super
+ end
+ end
+
# Maps logical Rails types to MySQL-specific data types.
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
case type.to_s
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
index 3d8f0b575c..fcbd8fd88a 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
@@ -30,8 +30,8 @@ module ActiveRecord
nil
elsif String === string
Hash[string.scan(HstorePair).map { |k,v|
- v = v.upcase == 'NULL' ? nil : v.gsub(/^"(.*)"$/,'\1').gsub(/\\(.)/, '\1')
- k = k.gsub(/^"(.*)"$/,'\1').gsub(/\\(.)/, '\1')
+ v = v.upcase == 'NULL' ? nil : v.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
+ k = k.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
[k,v]
}]
else
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index 43f991b362..49b93e5626 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -18,10 +18,12 @@ module ActiveRecord
def quote(value, column = nil) #:nodoc:
return super unless column
+ sql_type = type_to_sql(column.type, column.limit, column.precision, column.scale)
+
case value
when Range
- if /range$/ =~ column.sql_type
- "'#{PostgreSQLColumn.range_to_string(value)}'::#{column.sql_type}"
+ if /range$/ =~ sql_type
+ "'#{PostgreSQLColumn.range_to_string(value)}'::#{sql_type}"
else
super
end
@@ -32,13 +34,13 @@ module ActiveRecord
super
end
when Hash
- case column.sql_type
+ case sql_type
when 'hstore' then super(PostgreSQLColumn.hstore_to_string(value), column)
when 'json' then super(PostgreSQLColumn.json_to_string(value), column)
else super
end
when IPAddr
- case column.sql_type
+ case sql_type
when 'inet', 'cidr' then super(PostgreSQLColumn.cidr_to_string(value), column)
else super
end
@@ -51,14 +53,14 @@ module ActiveRecord
super
end
when Numeric
- if column.sql_type == 'money' || [:string, :text].include?(column.type)
+ if sql_type == 'money' || [:string, :text].include?(column.type)
# Not truly string input, so doesn't require (or allow) escape string syntax.
"'#{value}'"
else
super
end
when String
- case column.sql_type
+ case sql_type
when 'bytea' then "'#{escape_bytea(value)}'"
when 'xml' then "xml '#{quote_string(value)}'"
when /^bit/
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 3bc61c5e0c..688bf1774b 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -1,6 +1,42 @@
module ActiveRecord
module ConnectionAdapters
class PostgreSQLAdapter < AbstractAdapter
+ class SchemaCreation < AbstractAdapter::SchemaCreation
+ private
+
+ def visit_AddColumn(o)
+ sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
+ sql = "ADD COLUMN #{quote_column_name(o.name)} #{sql_type}"
+ add_column_options!(sql, column_options(o))
+ end
+
+ def visit_ColumnDefinition(o)
+ sql = super
+ if o.primary_key? && o.type == :uuid
+ sql << " PRIMARY KEY "
+ add_column_options!(sql, column_options(o))
+ end
+ sql
+ end
+
+ def add_column_options!(sql, options)
+ if options[:array] || options[:column].try(:array)
+ sql << '[]'
+ end
+
+ column = options.fetch(:column) { return super }
+ if column.type == :uuid && options[:default] =~ /\(\)/
+ sql << " DEFAULT #{options[:default]}"
+ else
+ super
+ end
+ end
+ end
+
+ def schema_creation
+ SchemaCreation.new self
+ end
+
module SchemaStatements
# Drops the database specified on the +name+ attribute
# and creates it again using the provided +options+.
@@ -124,8 +160,9 @@ module ActiveRecord
desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
where = inddef.scan(/WHERE (.+)$/).flatten[0]
+ type = inddef.scan(/USING (.+?) /).flatten[0].to_sym
- column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where)
+ column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, type)
end.compact
end
@@ -337,10 +374,7 @@ module ActiveRecord
# See TableDefinition#column for details of the options you can use.
def add_column(table_name, column_name, type, options = {})
clear_cache!
- add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
- add_column_options!(add_column_sql, options)
-
- execute add_column_sql
+ super
end
# Changes the column of a table.
@@ -375,6 +409,15 @@ module ActiveRecord
rename_column_indexes(table_name, column_name, new_column_name)
end
+ def add_index(table_name, column_name, options = {}) #:nodoc:
+ if options.is_a?(Hash) && options[:using]
+ index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} USING #{options[:using]} (#{index_columns})#{index_options}"
+ else
+ super
+ end
+ end
+
def remove_index!(table_name, index_name) #:nodoc:
execute "DROP INDEX #{quote_table_name(index_name)}"
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index dfa4c3967a..6f1eb25990 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -330,6 +330,13 @@ module ActiveRecord
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
include ColumnMethods
+ def primary_key(name, type = :primary_key, options = {})
+ return super unless type == :uuid
+ options[:default] ||= 'uuid_generate_v4()'
+ options[:primary_key] = true
+ column name, type, options
+ end
+
def column(name, type = nil, options = {})
super
column = self[name]
@@ -344,8 +351,8 @@ module ActiveRecord
private
- def create_column_definition(base, name, type)
- ColumnDefinition.new base, name, type
+ def create_column_definition(name, type)
+ ColumnDefinition.new name, type
end
end
@@ -519,8 +526,7 @@ module ActiveRecord
# Is this connection alive and ready for queries?
def active?
- @connection.query 'SELECT 1'
- true
+ @connection.connect_poll != PG::PGRES_POLLING_FAILED
rescue PGError
false
end
@@ -628,19 +634,6 @@ module ActiveRecord
@table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
end
- def add_column_options!(sql, options)
- if options[:array] || options[:column].try(:array)
- sql << '[]'
- end
-
- column = options.fetch(:column) { return super }
- if column.type == :uuid && options[:default] =~ /\(\)/
- sql << " DEFAULT #{options[:default]}"
- else
- super
- end
- end
-
# Set the authorized user for this session
def session_auth=(user)
clear_cache!
@@ -901,8 +894,8 @@ module ActiveRecord
$1.strip if $1
end
- def create_table_definition
- TableDefinition.new(self)
+ def create_table_definition(name, temporary, options)
+ TableDefinition.new native_database_types, name, temporary, options
end
def update_table_definition(table_name, base)
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index d3ffee3a8b..50d189d27a 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -458,7 +458,7 @@ module ActiveRecord
def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
alter_table(table_name) do |definition|
- definition.columns.delete(definition[column_name])
+ definition.remove_column column_name
end
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 1908448f90..013f9b92b9 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -426,6 +426,7 @@ module ActiveRecord
@readonly = false
@destroyed = false
@marked_for_destruction = false
+ @destroyed_by_association = nil
@new_record = true
@txn = nil
@_start_transaction_state = {}
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 2958d08210..c26fc76515 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -752,7 +752,7 @@ module ActiveRecord
def fixtures(*fixture_set_names)
if fixture_set_names.first == :all
fixture_set_names = Dir["#{fixture_path}/**/*.{yml}"]
- fixture_set_names.map! { |f| f[(fixture_path.size + 1)..-5] }
+ fixture_set_names.map! { |f| f[(fixture_path.to_s.size + 1)..-5] }
else
fixture_set_names = fixture_set_names.flatten.map { |n| n.to_s }
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 5d7762ec3a..27d37766b7 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -858,7 +858,7 @@ module ActiveRecord
end
def current_version
- migrated.sort.last || 0
+ migrated.max || 0
end
def current_migration
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index b25d0601cb..f881778591 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -443,7 +443,7 @@ module ActiveRecord
real_column = db_columns_with_values[i].first
bind_attrs[column] = klass.connection.substitute_at(real_column, i)
end
- stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.compile_update(bind_attrs)
+ stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id_was)).arel.compile_update(bind_attrs)
klass.connection.update stmt, 'SQL', db_columns_with_values
end
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 10a31109d5..257221174b 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -614,7 +614,7 @@ module ActiveRecord
#
# The returned <tt>ActiveRecord::NullRelation</tt> inherits from Relation and implements the
# Null Object pattern. It is an object with defined null behavior and always returns an empty
- # array of records without quering the database.
+ # array of records without querying the database.
#
# Any subsequent condition chained to the returned relation will continue
# generating an empty relation and will not fire any query to the database.
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index 5bd481082e..cde4f29d08 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -27,14 +27,6 @@ module ActiveRecord
# Post.unscoped {
# Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
# }
- #
- # It is recommended that you use the block form of unscoped because
- # chaining unscoped with +scope+ does not work. Assuming that
- # +published+ is a +scope+, the following two statements
- # are equal: the +default_scope+ is applied on both.
- #
- # Post.unscoped.published
- # Post.published
def unscoped
block_given? ? relation.scoping { yield } : relation
end
diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb
index c0471bb506..f2b041ad97 100644
--- a/activerecord/lib/active_record/version.rb
+++ b/activerecord/lib/active_record/version.rb
@@ -1,10 +1,11 @@
module ActiveRecord
- module VERSION #:nodoc:
- MAJOR = 4
- MINOR = 0
- TINY = 0
- PRE = "beta1"
+ # Returns the version of the currently loaded ActiveRecord as a Gem::Version
+ def self.version
+ Gem::Version.new "4.0.0.beta1"
+ end
- STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
+ module VERSION #:nodoc:
+ MAJOR, MINOR, TINY, PRE = ActiveRecord.version.segments
+ STRING = ActiveRecord.version.to_s
end
end