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.

Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +249 -0
  3. data/lib/active_record/aggregations.rb +6 -3
  4. data/lib/active_record/associations/association_scope.rb +1 -1
  5. data/lib/active_record/associations/collection_association.rb +17 -6
  6. data/lib/active_record/associations/collection_proxy.rb +2 -2
  7. data/lib/active_record/associations/has_many_through_association.rb +5 -0
  8. data/lib/active_record/associations/join_dependency/join_association.rb +7 -1
  9. data/lib/active_record/associations/join_dependency.rb +2 -1
  10. data/lib/active_record/associations/preloader/association.rb +5 -1
  11. data/lib/active_record/attribute_assignment.rb +1 -1
  12. data/lib/active_record/attribute_methods/dirty.rb +1 -0
  13. data/lib/active_record/attribute_methods/write.rb +1 -1
  14. data/lib/active_record/attribute_methods.rb +4 -8
  15. data/lib/active_record/attribute_set/builder.rb +21 -11
  16. data/lib/active_record/attribute_set.rb +4 -0
  17. data/lib/active_record/attributes.rb +1 -0
  18. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -1
  19. data/lib/active_record/connection_adapters/abstract/database_statements.rb +7 -2
  20. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -0
  21. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +2 -2
  22. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +21 -7
  23. data/lib/active_record/connection_adapters/abstract_adapter.rb +12 -1
  24. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +44 -9
  25. data/lib/active_record/connection_adapters/column.rb +1 -1
  26. data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -19
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +9 -2
  28. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -0
  29. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  30. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +13 -2
  31. data/lib/active_record/connection_adapters/postgresql_adapter.rb +2 -6
  32. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -12
  33. data/lib/active_record/core.rb +2 -0
  34. data/lib/active_record/enum.rb +2 -3
  35. data/lib/active_record/errors.rb +4 -3
  36. data/lib/active_record/gem_version.rb +1 -1
  37. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  38. data/lib/active_record/migration.rb +34 -6
  39. data/lib/active_record/model_schema.rb +3 -1
  40. data/lib/active_record/nested_attributes.rb +12 -2
  41. data/lib/active_record/railtie.rb +4 -2
  42. data/lib/active_record/railties/databases.rake +7 -17
  43. data/lib/active_record/reflection.rb +37 -25
  44. data/lib/active_record/relation/calculations.rb +10 -3
  45. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -1
  46. data/lib/active_record/relation/query_methods.rb +1 -1
  47. data/lib/active_record/relation/spawn_methods.rb +7 -3
  48. data/lib/active_record/schema_migration.rb +1 -4
  49. data/lib/active_record/tasks/database_tasks.rb +4 -1
  50. data/lib/active_record/tasks/mysql_database_tasks.rb +30 -16
  51. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -8
  52. data/lib/active_record/type/date.rb +4 -0
  53. data/lib/active_record/type/decimal.rb +19 -3
  54. data/lib/active_record/type/value.rb +5 -0
  55. data/lib/active_record/validations/uniqueness.rb +7 -1
  56. data/lib/active_record.rb +2 -0
  57. 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, Array attributes are truncated up to 10 values.
291
- # Other attributes return the value of <tt>#inspect</tt> without
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] # => Fixnum
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
@@ -64,6 +64,10 @@ module ActiveRecord
64
64
  end
65
65
  end
66
66
 
67
+ def ==(other)
68
+ attributes == other.attributes
69
+ end
70
+
67
71
  protected
68
72
 
69
73
  attr_reader :attributes
@@ -136,6 +136,7 @@ module ActiveRecord
136
136
  @content_columns = nil
137
137
  @default_attributes = nil
138
138
  @persistable_attribute_names = nil
139
+ @attribute_names = nil
139
140
  end
140
141
 
141
142
  def raw_default_values
@@ -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
- key_list << quote_column_name(name)
295
- quote(value, columns[name])
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'
@@ -1,4 +1,5 @@
1
1
  require 'active_support/core_ext/big_decimal/conversions'
2
+ require "active_support/multibyte/chars"
2
3
 
3
4
  module ActiveRecord
4
5
  module ConnectionAdapters # :nodoc:
@@ -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] = options
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.each_pair do |other_table_name, foreign_key_options|
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 { |v| v.to_i }
831
- paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
832
- versions = Dir[*paths].map do |filename|
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
- # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax - they
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
- def columns_for_distinct(columns, orders) #:nodoc:
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[0] >= 5
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[0] >= 5
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("SHOW VARIABLES LIKE '#{name}'", 'SCHEMA')
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 Fixnum
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.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
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 : (version[0] == 5 && version[1] >= 7) || version[0] >= 6
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?(Fixnum)
841
- variables['wait_timeout'] = self.class.type_cast_config_to_integer(wait_timeout)
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', '~> 0.3.13'
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
- unless @connection.nil?
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
- if @connection
226
- # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
227
- # made since we established the connection
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