activerecord 4.2.5 → 4.2.11.1

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 (44) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +156 -0
  3. data/lib/active_record.rb +2 -1
  4. data/lib/active_record/aggregations.rb +6 -3
  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/attribute_assignment.rb +1 -1
  10. data/lib/active_record/attribute_methods.rb +4 -8
  11. data/lib/active_record/attribute_methods/dirty.rb +1 -0
  12. data/lib/active_record/attribute_methods/write.rb +1 -1
  13. data/lib/active_record/attributes.rb +1 -0
  14. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -1
  15. data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -1
  16. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -0
  17. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +2 -2
  18. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +21 -7
  19. data/lib/active_record/connection_adapters/abstract_adapter.rb +12 -0
  20. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +44 -9
  21. data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -19
  22. data/lib/active_record/connection_adapters/mysql_adapter.rb +9 -2
  23. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -0
  24. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -1
  25. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -12
  26. data/lib/active_record/enum.rb +1 -1
  27. data/lib/active_record/gem_version.rb +2 -2
  28. data/lib/active_record/migration.rb +30 -5
  29. data/lib/active_record/model_schema.rb +3 -1
  30. data/lib/active_record/nested_attributes.rb +12 -2
  31. data/lib/active_record/railtie.rb +4 -2
  32. data/lib/active_record/railties/databases.rake +7 -17
  33. data/lib/active_record/reflection.rb +3 -3
  34. data/lib/active_record/relation/calculations.rb +10 -3
  35. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -1
  36. data/lib/active_record/relation/spawn_methods.rb +7 -3
  37. data/lib/active_record/schema_migration.rb +1 -4
  38. data/lib/active_record/tasks/database_tasks.rb +3 -0
  39. data/lib/active_record/tasks/mysql_database_tasks.rb +13 -9
  40. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
  41. data/lib/active_record/type/date.rb +4 -0
  42. data/lib/active_record/type/decimal.rb +17 -3
  43. data/lib/active_record/type/value.rb +5 -0
  44. metadata +7 -8
@@ -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) })
@@ -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
@@ -1,6 +1,6 @@
1
1
  require 'active_record/connection_adapters/abstract_mysql_adapter'
2
2
 
3
- gem 'mysql2', '>= 0.3.13', '< 0.5'
3
+ gem 'mysql2', '>= 0.3.13', '< 0.6.0'
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
@@ -245,7 +245,7 @@ module ActiveRecord
245
245
  return @client_encoding if @client_encoding
246
246
 
247
247
  result = exec_query(
248
- "SHOW VARIABLES WHERE Variable_name = 'character_set_client'",
248
+ "select @@character_set_client",
249
249
  'SCHEMA')
250
250
  @client_encoding = ENCODINGS[result.rows.last.last]
251
251
  end
@@ -282,6 +282,10 @@ module ActiveRecord
282
282
  super
283
283
  end
284
284
  end
285
+
286
+ def has_precision?
287
+ precision || 0
288
+ end
285
289
  end
286
290
 
287
291
  class Time < Type::Time # :nodoc:
@@ -328,8 +332,11 @@ module ActiveRecord
328
332
 
329
333
  def initialize_type_map(m) # :nodoc:
330
334
  super
331
- m.register_type %r(datetime)i, Fields::DateTime.new
332
335
  m.register_type %r(time)i, Fields::Time.new
336
+ m.register_type(%r(datetime)i) do |sql_type|
337
+ precision = extract_precision(sql_type)
338
+ Fields::DateTime.new(precision: precision)
339
+ end
333
340
  end
334
341
 
335
342
  def exec_without_stmt(sql, name = 'SQL') # :nodoc:
@@ -80,6 +80,7 @@ module ActiveRecord
80
80
  value
81
81
  end
82
82
  when nil then "NULL"
83
+ when ::Date, ::DateTime, ::Time then subtype.type_cast_for_schema(value)
83
84
  else value
84
85
  end
85
86
  end
@@ -95,6 +95,16 @@ module ActiveRecord
95
95
  SQL
96
96
  end
97
97
 
98
+ def data_sources # :nodoc
99
+ select_values(<<-SQL, 'SCHEMA')
100
+ SELECT c.relname
101
+ FROM pg_class c
102
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
103
+ WHERE c.relkind IN ('r', 'v','m') -- (r)elation/table, (v)iew, (m)aterialized view
104
+ AND n.nspname = ANY (current_schemas(false))
105
+ SQL
106
+ end
107
+
98
108
  # Returns true if table exists.
99
109
  # If the schema is not specified as part of +name+ then it will only find tables within
100
110
  # the current schema search path (regardless of permissions to access tables in other schemas)
@@ -111,6 +121,7 @@ module ActiveRecord
111
121
  AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
112
122
  SQL
113
123
  end
124
+ alias data_source_exists? table_exists?
114
125
 
115
126
  def drop_table(table_name, options = {})
116
127
  execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
@@ -552,7 +563,7 @@ module ActiveRecord
552
563
  when 1, 2; 'smallint'
553
564
  when 3, 4; 'integer'
554
565
  when 5..8; 'bigint'
555
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
566
+ else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead.")
556
567
  end
557
568
  when 'datetime'
558
569
  return super unless precision
@@ -74,18 +74,6 @@ module ActiveRecord
74
74
  boolean: { name: "boolean" }
75
75
  }
76
76
 
77
- class Version
78
- include Comparable
79
-
80
- def initialize(version_string)
81
- @version = version_string.split('.').map { |v| v.to_i }
82
- end
83
-
84
- def <=>(version_string)
85
- @version <=> version_string.split('.').map { |v| v.to_i }
86
- end
87
- end
88
-
89
77
  class StatementPool < ConnectionAdapters::StatementPool
90
78
  def initialize(connection, max)
91
79
  super
@@ -375,10 +363,12 @@ module ActiveRecord
375
363
  row['name']
376
364
  end
377
365
  end
366
+ alias data_sources tables
378
367
 
379
368
  def table_exists?(table_name)
380
369
  table_name && tables(nil, table_name).any?
381
370
  end
371
+ alias data_source_exists? table_exists?
382
372
 
383
373
  # Returns an array of +Column+ objects for the table specified by +table_name+.
384
374
  def columns(table_name) #:nodoc:
@@ -69,7 +69,7 @@ module ActiveRecord
69
69
  # Where conditions on an enum attribute must use the ordinal value of an enum.
70
70
  module Enum
71
71
  def self.extended(base) # :nodoc:
72
- base.class_attribute(:defined_enums)
72
+ base.class_attribute(:defined_enums, instance_writer: false)
73
73
  base.defined_enums = {}
74
74
  end
75
75
 
@@ -7,8 +7,8 @@ module ActiveRecord
7
7
  module VERSION
8
8
  MAJOR = 4
9
9
  MINOR = 2
10
- TINY = 5
11
- PRE = nil
10
+ TINY = 11
11
+ PRE = "1"
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
14
  end
@@ -880,14 +880,15 @@ module ActiveRecord
880
880
  migrations_paths.first
881
881
  end
882
882
 
883
+ def parse_migration_filename(filename) # :nodoc:
884
+ File.basename(filename).scan(/\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
885
+ end
886
+
883
887
  def migrations(paths)
884
888
  paths = Array(paths)
885
889
 
886
- files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }]
887
-
888
- migrations = files.map do |file|
889
- version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
890
-
890
+ migrations = migration_files(paths).map do |file|
891
+ version, name, scope = parse_migration_filename(file)
891
892
  raise IllegalMigrationNameError.new(file) unless version
892
893
  version = version.to_i
893
894
  name = name.camelize
@@ -898,6 +899,30 @@ module ActiveRecord
898
899
  migrations.sort_by(&:version)
899
900
  end
900
901
 
902
+ def migrations_status(paths)
903
+ paths = Array(paths)
904
+
905
+ db_list = ActiveRecord::SchemaMigration.normalized_versions
906
+
907
+ file_list = migration_files(paths).map do |file|
908
+ version, name, scope = parse_migration_filename(file)
909
+ raise IllegalMigrationNameError.new(file) unless version
910
+ version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
911
+ status = db_list.delete(version) ? "up" : "down"
912
+ [status, version, (name + scope).humanize]
913
+ end.compact
914
+
915
+ db_list.map! do |version|
916
+ ["up", version, "********** NO FILE **********"]
917
+ end
918
+
919
+ (db_list + file_list).sort_by { |_, version, _| version }
920
+ end
921
+
922
+ def migration_files(paths)
923
+ Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
924
+ end
925
+
901
926
  private
902
927
 
903
928
  def move(direction, migrations_paths, steps)
@@ -247,7 +247,7 @@ module ActiveRecord
247
247
  # Returns a hash where the keys are column names and the values are
248
248
  # default values when instantiating the AR object for this table.
249
249
  def column_defaults
250
- _default_attributes.to_hash
250
+ _default_attributes.dup.to_hash
251
251
  end
252
252
 
253
253
  def _default_attributes # :nodoc:
@@ -304,6 +304,8 @@ module ActiveRecord
304
304
  @default_attributes = nil
305
305
  @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
306
306
  @relation = nil
307
+
308
+ initialize_find_by_cache
307
309
  end
308
310
 
309
311
  private
@@ -523,7 +523,7 @@ module ActiveRecord
523
523
  # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
524
524
  # association and evaluates to +true+.
525
525
  def reject_new_record?(association_name, attributes)
526
- has_destroy_flag?(attributes) || call_reject_if(association_name, attributes)
526
+ will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes)
527
527
  end
528
528
 
529
529
  # Determines if a record with the particular +attributes+ should be
@@ -532,7 +532,8 @@ module ActiveRecord
532
532
  #
533
533
  # Returns false if there is a +destroy_flag+ on the attributes.
534
534
  def call_reject_if(association_name, attributes)
535
- return false if has_destroy_flag?(attributes)
535
+ return false if will_be_destroyed?(association_name, attributes)
536
+
536
537
  case callback = self.nested_attributes_options[association_name][:reject_if]
537
538
  when Symbol
538
539
  method(callback).arity == 0 ? send(callback) : send(callback, attributes)
@@ -541,6 +542,15 @@ module ActiveRecord
541
542
  end
542
543
  end
543
544
 
545
+ # Only take into account the destroy flag if <tt>:allow_destroy</tt> is true
546
+ def will_be_destroyed?(association_name, attributes)
547
+ allow_destroy?(association_name) && has_destroy_flag?(attributes)
548
+ end
549
+
550
+ def allow_destroy?(association_name)
551
+ self.nested_attributes_options[association_name][:allow_destroy]
552
+ end
553
+
544
554
  def raise_nested_attributes_record_not_found!(association_name, record_id)
545
555
  raise RecordNotFound, "Couldn't find #{self.class._reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
546
556
  end
@@ -57,8 +57,10 @@ module ActiveRecord
57
57
  console do |app|
58
58
  require "active_record/railties/console_sandbox" if app.sandbox?
59
59
  require "active_record/base"
60
- console = ActiveSupport::Logger.new(STDERR)
61
- Rails.logger.extend ActiveSupport::Logger.broadcast console
60
+ unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDERR, STDOUT)
61
+ console = ActiveSupport::Logger.new(STDERR)
62
+ Rails.logger.extend ActiveSupport::Logger.broadcast console
63
+ end
62
64
  end
63
65
 
64
66
  runner do