activerecord 4.2.4 → 4.2.10
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +249 -0
- data/lib/active_record/aggregations.rb +6 -3
- data/lib/active_record/associations/association_scope.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +17 -6
- data/lib/active_record/associations/collection_proxy.rb +2 -2
- data/lib/active_record/associations/has_many_through_association.rb +5 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +7 -1
- data/lib/active_record/associations/join_dependency.rb +2 -1
- data/lib/active_record/associations/preloader/association.rb +5 -1
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +1 -0
- data/lib/active_record/attribute_methods/write.rb +1 -1
- data/lib/active_record/attribute_methods.rb +4 -8
- data/lib/active_record/attribute_set/builder.rb +21 -11
- data/lib/active_record/attribute_set.rb +4 -0
- data/lib/active_record/attributes.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +21 -7
- data/lib/active_record/connection_adapters/abstract_adapter.rb +12 -1
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +44 -9
- data/lib/active_record/connection_adapters/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -19
- data/lib/active_record/connection_adapters/mysql_adapter.rb +9 -2
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +13 -2
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +2 -6
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -12
- data/lib/active_record/core.rb +2 -0
- data/lib/active_record/enum.rb +2 -3
- data/lib/active_record/errors.rb +4 -3
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/migration.rb +34 -6
- data/lib/active_record/model_schema.rb +3 -1
- data/lib/active_record/nested_attributes.rb +12 -2
- data/lib/active_record/railtie.rb +4 -2
- data/lib/active_record/railties/databases.rake +7 -17
- data/lib/active_record/reflection.rb +37 -25
- data/lib/active_record/relation/calculations.rb +10 -3
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +1 -1
- data/lib/active_record/relation/spawn_methods.rb +7 -3
- data/lib/active_record/schema_migration.rb +1 -4
- data/lib/active_record/tasks/database_tasks.rb +4 -1
- data/lib/active_record/tasks/mysql_database_tasks.rb +30 -16
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -8
- data/lib/active_record/type/date.rb +4 -0
- data/lib/active_record/type/decimal.rb +19 -3
- data/lib/active_record/type/value.rb +5 -0
- data/lib/active_record/validations/uniqueness.rb +7 -1
- data/lib/active_record.rb +2 -0
- metadata +8 -8
@@ -287,9 +287,8 @@ module ActiveRecord
|
|
287
287
|
# Returns an <tt>#inspect</tt>-like string for the value of the
|
288
288
|
# attribute +attr_name+. String attributes are truncated up to 50
|
289
289
|
# characters, Date and Time attributes are returned in the
|
290
|
-
# <tt>:db</tt> format
|
291
|
-
#
|
292
|
-
# modification.
|
290
|
+
# <tt>:db</tt> format. Other attributes return the value of
|
291
|
+
# <tt>#inspect</tt> without modification.
|
293
292
|
#
|
294
293
|
# person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
|
295
294
|
#
|
@@ -300,7 +299,7 @@ module ActiveRecord
|
|
300
299
|
# # => "\"2012-10-22 00:15:07\""
|
301
300
|
#
|
302
301
|
# person.attribute_for_inspect(:tag_ids)
|
303
|
-
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
302
|
+
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
|
304
303
|
def attribute_for_inspect(attr_name)
|
305
304
|
value = read_attribute(attr_name)
|
306
305
|
|
@@ -308,9 +307,6 @@ module ActiveRecord
|
|
308
307
|
"#{value[0, 50]}...".inspect
|
309
308
|
elsif value.is_a?(Date) || value.is_a?(Time)
|
310
309
|
%("#{value.to_s(:db)}")
|
311
|
-
elsif value.is_a?(Array) && value.size > 10
|
312
|
-
inspected = value.first(10).inspect
|
313
|
-
%(#{inspected[0...-1]}, ...])
|
314
310
|
else
|
315
311
|
value.inspect
|
316
312
|
end
|
@@ -368,7 +364,7 @@ module ActiveRecord
|
|
368
364
|
# person = Person.new
|
369
365
|
# person[:age] = '22'
|
370
366
|
# person[:age] # => 22
|
371
|
-
# person[:age] # =>
|
367
|
+
# person[:age].class # => Integer
|
372
368
|
def []=(attr_name, value)
|
373
369
|
write_attribute(attr_name, value)
|
374
370
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'active_record/attribute'
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
class AttributeSet # :nodoc:
|
3
5
|
class Builder # :nodoc:
|
@@ -64,10 +66,29 @@ module ActiveRecord
|
|
64
66
|
end
|
65
67
|
end
|
66
68
|
|
69
|
+
def ==(other)
|
70
|
+
if other.is_a?(LazyAttributeHash)
|
71
|
+
materialize == other.materialize
|
72
|
+
else
|
73
|
+
materialize == other
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
67
77
|
protected
|
68
78
|
|
69
79
|
attr_reader :types, :values, :additional_types, :delegate_hash
|
70
80
|
|
81
|
+
def materialize
|
82
|
+
unless @materialized
|
83
|
+
values.each_key { |key| self[key] }
|
84
|
+
types.each_key { |key| self[key] }
|
85
|
+
unless frozen?
|
86
|
+
@materialized = true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
delegate_hash
|
90
|
+
end
|
91
|
+
|
71
92
|
private
|
72
93
|
|
73
94
|
def assign_default_value(name)
|
@@ -81,16 +102,5 @@ module ActiveRecord
|
|
81
102
|
delegate_hash[name] = Attribute.uninitialized(name, type)
|
82
103
|
end
|
83
104
|
end
|
84
|
-
|
85
|
-
def materialize
|
86
|
-
unless @materialized
|
87
|
-
values.each_key { |key| self[key] }
|
88
|
-
types.each_key { |key| self[key] }
|
89
|
-
unless frozen?
|
90
|
-
@materialized = true
|
91
|
-
end
|
92
|
-
end
|
93
|
-
delegate_hash
|
94
|
-
end
|
95
105
|
end
|
96
106
|
end
|
@@ -637,7 +637,7 @@ module ActiveRecord
|
|
637
637
|
end
|
638
638
|
|
639
639
|
def pool_from_any_process_for(owner)
|
640
|
-
owner_to_pool = @owner_to_pool.values.find { |v| v[owner.name] }
|
640
|
+
owner_to_pool = @owner_to_pool.values.reverse.find { |v| v[owner.name] }
|
641
641
|
owner_to_pool && owner_to_pool[owner.name]
|
642
642
|
end
|
643
643
|
end
|
@@ -287,12 +287,17 @@ module ActiveRecord
|
|
287
287
|
# Inserts the given fixture into the table. Overridden in adapters that require
|
288
288
|
# something beyond a simple insert (eg. Oracle).
|
289
289
|
def insert_fixture(fixture, table_name)
|
290
|
+
fixture = fixture.stringify_keys
|
290
291
|
columns = schema_cache.columns_hash(table_name)
|
291
292
|
|
292
293
|
key_list = []
|
293
294
|
value_list = fixture.map do |name, value|
|
294
|
-
|
295
|
-
|
295
|
+
if column = columns[name]
|
296
|
+
key_list << quote_column_name(name)
|
297
|
+
quote(value, column)
|
298
|
+
else
|
299
|
+
raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
|
300
|
+
end
|
296
301
|
end
|
297
302
|
|
298
303
|
execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
|
@@ -99,7 +99,7 @@ module ActiveRecord
|
|
99
99
|
def initialize(types, name, temporary, options, as = nil)
|
100
100
|
@columns_hash = {}
|
101
101
|
@indexes = {}
|
102
|
-
@foreign_keys =
|
102
|
+
@foreign_keys = []
|
103
103
|
@native = types
|
104
104
|
@temporary = temporary
|
105
105
|
@options = options
|
@@ -289,7 +289,7 @@ module ActiveRecord
|
|
289
289
|
end
|
290
290
|
|
291
291
|
def foreign_key(table_name, options = {}) # :nodoc:
|
292
|
-
foreign_keys[table_name
|
292
|
+
foreign_keys.push([table_name, options])
|
293
293
|
end
|
294
294
|
|
295
295
|
# Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
|
@@ -19,6 +19,20 @@ module ActiveRecord
|
|
19
19
|
table_name[0...table_alias_length].tr('.', '_')
|
20
20
|
end
|
21
21
|
|
22
|
+
# Returns the relation names useable to back Active Record models.
|
23
|
+
# For most adapters this means all tables and views.
|
24
|
+
def data_sources
|
25
|
+
tables
|
26
|
+
end
|
27
|
+
|
28
|
+
# Checks to see if the data source +name+ exists on the database.
|
29
|
+
#
|
30
|
+
# data_source_exists?(:ebooks)
|
31
|
+
#
|
32
|
+
def data_source_exists?(name)
|
33
|
+
data_sources.include?(name.to_s)
|
34
|
+
end
|
35
|
+
|
22
36
|
# Checks to see if the table +table_name+ exists on the database.
|
23
37
|
#
|
24
38
|
# table_exists?(:developers)
|
@@ -213,7 +227,7 @@ module ActiveRecord
|
|
213
227
|
end
|
214
228
|
end
|
215
229
|
|
216
|
-
td.foreign_keys.
|
230
|
+
td.foreign_keys.each do |other_table_name, foreign_key_options|
|
217
231
|
add_foreign_key(table_name, other_table_name, foreign_key_options)
|
218
232
|
end
|
219
233
|
|
@@ -827,10 +841,9 @@ module ActiveRecord
|
|
827
841
|
version = version.to_i
|
828
842
|
sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
|
829
843
|
|
830
|
-
migrated = select_values("SELECT version FROM #{sm_table}").map
|
831
|
-
|
832
|
-
|
833
|
-
filename.split('/').last.split('_').first.to_i
|
844
|
+
migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
|
845
|
+
versions = ActiveRecord::Migrator.migration_files(migrations_paths).map do |file|
|
846
|
+
ActiveRecord::Migrator.parse_migration_filename(file).first.to_i
|
834
847
|
end
|
835
848
|
|
836
849
|
unless migrated.include?(version)
|
@@ -876,11 +889,12 @@ module ActiveRecord
|
|
876
889
|
end
|
877
890
|
|
878
891
|
# Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
|
879
|
-
#
|
892
|
+
# PostgreSQL, MySQL, and Oracle overrides this for custom DISTINCT syntax - they
|
880
893
|
# require the order columns appear in the SELECT.
|
881
894
|
#
|
882
895
|
# columns_for_distinct("posts.id", ["posts.created_at desc"])
|
883
|
-
|
896
|
+
#
|
897
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
884
898
|
columns
|
885
899
|
end
|
886
900
|
|
@@ -111,6 +111,18 @@ module ActiveRecord
|
|
111
111
|
@prepared_statements = false
|
112
112
|
end
|
113
113
|
|
114
|
+
class Version
|
115
|
+
include Comparable
|
116
|
+
|
117
|
+
def initialize(version_string)
|
118
|
+
@version = version_string.split('.').map(&:to_i)
|
119
|
+
end
|
120
|
+
|
121
|
+
def <=>(version_string)
|
122
|
+
@version <=> version_string.split('.').map(&:to_i)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
114
126
|
class BindCollector < Arel::Collectors::Bind
|
115
127
|
def compile(bvs, conn)
|
116
128
|
super(bvs.map { |bv| conn.quote(*bv.reverse) })
|
@@ -457,7 +469,6 @@ module ActiveRecord
|
|
457
469
|
message = "#{e.class.name}: #{e.message.force_encoding sql.encoding}: #{sql}"
|
458
470
|
end
|
459
471
|
|
460
|
-
@logger.error message if @logger
|
461
472
|
exception = translate_exception(e, message)
|
462
473
|
exception.set_backtrace e.backtrace
|
463
474
|
exception
|
@@ -203,7 +203,7 @@ module ActiveRecord
|
|
203
203
|
#
|
204
204
|
# http://bugs.mysql.com/bug.php?id=39170
|
205
205
|
def supports_transaction_isolation?
|
206
|
-
version
|
206
|
+
version >= '5.0.0'
|
207
207
|
end
|
208
208
|
|
209
209
|
def supports_indexes_in_create?
|
@@ -215,7 +215,11 @@ module ActiveRecord
|
|
215
215
|
end
|
216
216
|
|
217
217
|
def supports_views?
|
218
|
-
version
|
218
|
+
version >= '5.0.0'
|
219
|
+
end
|
220
|
+
|
221
|
+
def supports_datetime_with_precision?
|
222
|
+
version >= '5.6.4'
|
219
223
|
end
|
220
224
|
|
221
225
|
def native_database_types
|
@@ -278,6 +282,14 @@ module ActiveRecord
|
|
278
282
|
0
|
279
283
|
end
|
280
284
|
|
285
|
+
def quoted_date(value)
|
286
|
+
if supports_datetime_with_precision? && value.acts_like?(:time) && value.respond_to?(:usec)
|
287
|
+
"#{super}.#{sprintf("%06d", value.usec)}"
|
288
|
+
else
|
289
|
+
super
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
281
293
|
# REFERENTIAL INTEGRITY ====================================
|
282
294
|
|
283
295
|
def disable_referential_integrity #:nodoc:
|
@@ -407,6 +419,7 @@ module ActiveRecord
|
|
407
419
|
result.collect { |field| field.first }
|
408
420
|
end
|
409
421
|
end
|
422
|
+
alias data_sources tables
|
410
423
|
|
411
424
|
def truncate(table_name, name = nil)
|
412
425
|
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
@@ -426,6 +439,7 @@ module ActiveRecord
|
|
426
439
|
|
427
440
|
tables(nil, schema, table).any?
|
428
441
|
end
|
442
|
+
alias data_source_exists? table_exists?
|
429
443
|
|
430
444
|
# Returns an array of indexes for the given table.
|
431
445
|
def indexes(table_name, name = nil) #:nodoc:
|
@@ -604,8 +618,10 @@ module ActiveRecord
|
|
604
618
|
|
605
619
|
# SHOW VARIABLES LIKE 'name'
|
606
620
|
def show_variable(name)
|
607
|
-
variables = select_all("
|
621
|
+
variables = select_all("select @@#{name} as 'Value'", 'SCHEMA')
|
608
622
|
variables.first['Value'] unless variables.empty?
|
623
|
+
rescue ActiveRecord::StatementInvalid
|
624
|
+
nil
|
609
625
|
end
|
610
626
|
|
611
627
|
# Returns a table's primary key and belonging sequence.
|
@@ -648,6 +664,21 @@ module ActiveRecord
|
|
648
664
|
end
|
649
665
|
end
|
650
666
|
|
667
|
+
# In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
|
668
|
+
# DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
|
669
|
+
# distinct queries, and requires that the ORDER BY include the distinct column.
|
670
|
+
# See https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
|
671
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
672
|
+
order_columns = orders.reject(&:blank?).map { |s|
|
673
|
+
# Convert Arel node to string
|
674
|
+
s = s.to_sql unless s.is_a?(String)
|
675
|
+
# Remove any ASC/DESC modifiers
|
676
|
+
s.gsub(/\s+(?:ASC|DESC)\b/i, '')
|
677
|
+
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
678
|
+
|
679
|
+
[super, *order_columns].join(', ')
|
680
|
+
end
|
681
|
+
|
651
682
|
def strict_mode?
|
652
683
|
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
|
653
684
|
end
|
@@ -713,6 +744,10 @@ module ActiveRecord
|
|
713
744
|
subsubselect = select.clone
|
714
745
|
subsubselect.projections = [key]
|
715
746
|
|
747
|
+
# Materialize subquery by adding distinct
|
748
|
+
# to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
|
749
|
+
subsubselect.distinct unless select.limit || select.offset || select.orders.any?
|
750
|
+
|
716
751
|
subselect = Arel::SelectManager.new(select.engine)
|
717
752
|
subselect.project Arel.sql(key.name)
|
718
753
|
subselect.from subsubselect.as('__active_record_temp')
|
@@ -723,7 +758,7 @@ module ActiveRecord
|
|
723
758
|
case length
|
724
759
|
when Hash
|
725
760
|
column_names.each {|name| option_strings[name] += "(#{length[name]})" if length.has_key?(name) && length[name].present?}
|
726
|
-
when
|
761
|
+
when Integer
|
727
762
|
column_names.each {|name| option_strings[name] += "(#{length})"}
|
728
763
|
end
|
729
764
|
end
|
@@ -817,7 +852,7 @@ module ActiveRecord
|
|
817
852
|
private
|
818
853
|
|
819
854
|
def version
|
820
|
-
@version ||= full_version.
|
855
|
+
@version ||= Version.new(full_version.match(/^\d+\.\d+\.\d+/)[0])
|
821
856
|
end
|
822
857
|
|
823
858
|
def mariadb?
|
@@ -825,7 +860,7 @@ module ActiveRecord
|
|
825
860
|
end
|
826
861
|
|
827
862
|
def supports_rename_index?
|
828
|
-
mariadb? ? false :
|
863
|
+
mariadb? ? false : version >= '5.7.6'
|
829
864
|
end
|
830
865
|
|
831
866
|
def configure_connection
|
@@ -836,9 +871,9 @@ module ActiveRecord
|
|
836
871
|
variables['sql_auto_is_null'] = 0
|
837
872
|
|
838
873
|
# Increase timeout so the server doesn't disconnect us.
|
839
|
-
wait_timeout = @config[:wait_timeout]
|
840
|
-
wait_timeout = 2147483 unless wait_timeout.is_a?(
|
841
|
-
variables[
|
874
|
+
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
875
|
+
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
876
|
+
variables["wait_timeout"] = wait_timeout
|
842
877
|
|
843
878
|
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
844
879
|
# http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
# It will be mapped to one of the standard Rails SQL types in the <tt>type</tt> attribute.
|
32
32
|
# +null+ determines if this column allows +NULL+ values.
|
33
33
|
def initialize(name, default, cast_type, sql_type = nil, null = true)
|
34
|
-
@name = name
|
34
|
+
@name = name.freeze
|
35
35
|
@cast_type = cast_type
|
36
36
|
@sql_type = sql_type
|
37
37
|
@null = null
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'active_record/connection_adapters/abstract_mysql_adapter'
|
2
2
|
|
3
|
-
gem 'mysql2', '
|
3
|
+
gem 'mysql2', '>= 0.3.13', '< 0.5'
|
4
4
|
require 'mysql2'
|
5
5
|
|
6
6
|
module ActiveRecord
|
@@ -74,20 +74,11 @@ module ActiveRecord
|
|
74
74
|
@connection.escape(string)
|
75
75
|
end
|
76
76
|
|
77
|
-
def quoted_date(value)
|
78
|
-
if value.acts_like?(:time) && value.respond_to?(:usec)
|
79
|
-
"#{super}.#{sprintf("%06d", value.usec)}"
|
80
|
-
else
|
81
|
-
super
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
77
|
#--
|
86
78
|
# CONNECTION MANAGEMENT ====================================
|
87
79
|
#++
|
88
80
|
|
89
81
|
def active?
|
90
|
-
return false unless @connection
|
91
82
|
@connection.ping
|
92
83
|
end
|
93
84
|
|
@@ -102,10 +93,7 @@ module ActiveRecord
|
|
102
93
|
# Otherwise, this method does nothing.
|
103
94
|
def disconnect!
|
104
95
|
super
|
105
|
-
|
106
|
-
@connection.close
|
107
|
-
@connection = nil
|
108
|
-
end
|
96
|
+
@connection.close
|
109
97
|
end
|
110
98
|
|
111
99
|
#--
|
@@ -222,11 +210,9 @@ module ActiveRecord
|
|
222
210
|
|
223
211
|
# Executes the SQL statement in the context of this connection.
|
224
212
|
def execute(sql, name = nil)
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
229
|
-
end
|
213
|
+
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
214
|
+
# made since we established the connection
|
215
|
+
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
230
216
|
|
231
217
|
super
|
232
218
|
end
|