activerecord 5.2.0 → 5.2.1.rc1

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +65 -0
  3. data/lib/active_record/associations.rb +9 -9
  4. data/lib/active_record/associations/alias_tracker.rb +1 -1
  5. data/lib/active_record/associations/association.rb +17 -10
  6. data/lib/active_record/associations/belongs_to_association.rb +14 -5
  7. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -1
  8. data/lib/active_record/associations/builder/belongs_to.rb +11 -2
  9. data/lib/active_record/associations/collection_association.rb +10 -7
  10. data/lib/active_record/associations/has_many_through_association.rb +1 -1
  11. data/lib/active_record/associations/has_one_association.rb +8 -0
  12. data/lib/active_record/associations/has_one_through_association.rb +5 -1
  13. data/lib/active_record/associations/join_dependency.rb +39 -64
  14. data/lib/active_record/associations/join_dependency/join_association.rb +12 -18
  15. data/lib/active_record/associations/join_dependency/join_part.rb +7 -0
  16. data/lib/active_record/associations/singular_association.rb +4 -10
  17. data/lib/active_record/associations/through_association.rb +1 -1
  18. data/lib/active_record/attribute_methods/dirty.rb +2 -2
  19. data/lib/active_record/attribute_methods/read.rb +1 -1
  20. data/lib/active_record/autosave_association.rb +8 -3
  21. data/lib/active_record/callbacks.rb +4 -0
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -0
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -4
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +3 -12
  25. data/lib/active_record/connection_adapters/abstract/transaction.rb +23 -14
  26. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +0 -11
  27. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -0
  28. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +2 -2
  29. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +4 -0
  30. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -0
  31. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +2 -2
  32. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -0
  33. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +5 -1
  34. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +0 -3
  35. data/lib/active_record/counter_cache.rb +17 -13
  36. data/lib/active_record/gem_version.rb +2 -2
  37. data/lib/active_record/log_subscriber.rb +1 -1
  38. data/lib/active_record/persistence.rb +1 -1
  39. data/lib/active_record/query_cache.rb +4 -11
  40. data/lib/active_record/relation.rb +13 -13
  41. data/lib/active_record/relation/finder_methods.rb +2 -4
  42. data/lib/active_record/relation/merger.rb +2 -6
  43. data/lib/active_record/relation/predicate_builder.rb +6 -5
  44. data/lib/active_record/relation/query_methods.rb +16 -13
  45. data/lib/active_record/timestamp.rb +8 -1
  46. data/lib/active_record/transactions.rb +23 -20
  47. data/lib/active_record/type/serialized.rb +4 -0
  48. metadata +11 -11
@@ -6,17 +6,14 @@ module ActiveRecord
6
6
  module Associations
7
7
  class JoinDependency # :nodoc:
8
8
  class JoinAssociation < JoinPart # :nodoc:
9
- # The reflection of the association represented
10
- attr_reader :reflection
9
+ attr_reader :reflection, :tables
10
+ attr_accessor :table
11
11
 
12
- attr_accessor :tables
13
-
14
- def initialize(reflection, children, alias_tracker)
12
+ def initialize(reflection, children)
15
13
  super(reflection.klass, children)
16
14
 
17
- @alias_tracker = alias_tracker
18
- @reflection = reflection
19
- @tables = nil
15
+ @reflection = reflection
16
+ @tables = nil
20
17
  end
21
18
 
22
19
  def match?(other)
@@ -24,14 +21,13 @@ module ActiveRecord
24
21
  super && reflection == other.reflection
25
22
  end
26
23
 
27
- def join_constraints(foreign_table, foreign_klass, join_type, tables, chain)
28
- joins = []
29
- tables = tables.reverse
24
+ def join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
25
+ joins = []
30
26
 
31
27
  # The chain starts with the target table, but we want to end with it here (makes
32
28
  # more sense in this context), so we reverse
33
- chain.reverse_each do |reflection|
34
- table = tables.shift
29
+ reflection.chain.reverse_each.with_index(1) do |reflection, i|
30
+ table = tables[-i]
35
31
  klass = reflection.klass
36
32
 
37
33
  constraint = reflection.build_join_constraint(table, foreign_table)
@@ -54,12 +50,10 @@ module ActiveRecord
54
50
  joins
55
51
  end
56
52
 
57
- def table
58
- tables.first
53
+ def tables=(tables)
54
+ @tables = tables
55
+ @table = tables.first
59
56
  end
60
-
61
- protected
62
- attr_reader :alias_tracker
63
57
  end
64
58
  end
65
59
  end
@@ -33,6 +33,13 @@ module ActiveRecord
33
33
  children.each { |child| child.each(&block) }
34
34
  end
35
35
 
36
+ def each_children(&block)
37
+ children.each do |child|
38
+ yield self, child
39
+ child.each_children(&block)
40
+ end
41
+ end
42
+
36
43
  # An Arel::Table for the active_record
37
44
  def table
38
45
  raise NotImplementedError
@@ -17,9 +17,8 @@ module ActiveRecord
17
17
  replace(record)
18
18
  end
19
19
 
20
- def build(attributes = {})
21
- record = build_record(attributes)
22
- yield(record) if block_given?
20
+ def build(attributes = {}, &block)
21
+ record = build_record(attributes, &block)
23
22
  set_new_record(record)
24
23
  record
25
24
  end
@@ -62,13 +61,8 @@ module ActiveRecord
62
61
  replace(record)
63
62
  end
64
63
 
65
- def _create_record(attributes, raise_error = false)
66
- unless owner.persisted?
67
- raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
68
- end
69
-
70
- record = build_record(attributes)
71
- yield(record) if block_given?
64
+ def _create_record(attributes, raise_error = false, &block)
65
+ record = build_record(attributes, &block)
72
66
  saved = record.save
73
67
  set_new_record(record)
74
68
  raise RecordInvalid.new(record) if !saved && raise_error
@@ -114,7 +114,7 @@ module ActiveRecord
114
114
  attributes[inverse.foreign_key] = target.id
115
115
  end
116
116
 
117
- super(attributes)
117
+ super
118
118
  end
119
119
  end
120
120
  end
@@ -114,12 +114,12 @@ module ActiveRecord
114
114
 
115
115
  # Alias for +changed+
116
116
  def changed_attribute_names_to_save
117
- changes_to_save.keys
117
+ mutations_from_database.changed_attribute_names
118
118
  end
119
119
 
120
120
  # Alias for +changed_attributes+
121
121
  def attributes_in_database
122
- changes_to_save.transform_values(&:first)
122
+ mutations_from_database.changed_values
123
123
  end
124
124
 
125
125
  private
@@ -69,7 +69,7 @@ module ActiveRecord
69
69
  if defined?(JRUBY_VERSION)
70
70
  # This form is significantly faster on JRuby, and this is one of our biggest hotspots.
71
71
  # https://github.com/jruby/jruby/pull/2562
72
- def _read_attribute(attr_name, &block) # :nodoc
72
+ def _read_attribute(attr_name, &block) # :nodoc:
73
73
  @attributes.fetch_value(attr_name.to_s, &block)
74
74
  end
75
75
  else
@@ -392,7 +392,7 @@ module ActiveRecord
392
392
  records -= records_to_destroy
393
393
  end
394
394
 
395
- records.each do |record|
395
+ records.each_with_index do |record, index|
396
396
  next if record.destroyed?
397
397
 
398
398
  saved = true
@@ -400,8 +400,13 @@ module ActiveRecord
400
400
  if autosave != false && (@new_record_before_save || record.new_record?)
401
401
  if autosave
402
402
  saved = association.insert_record(record, false)
403
- else
404
- association.insert_record(record) unless reflection.nested?
403
+ elsif !reflection.nested?
404
+ if reflection.validate?
405
+ valid = association_valid?(reflection, record, index)
406
+ saved = valid ? association.insert_record(record, false) : false
407
+ else
408
+ association.insert_record(record)
409
+ end
405
410
  end
406
411
  elsif autosave
407
412
  saved = record.save(validate: false)
@@ -332,6 +332,10 @@ module ActiveRecord
332
332
  _run_touch_callbacks { super }
333
333
  end
334
334
 
335
+ def increment!(*, touch: nil) # :nodoc:
336
+ touch ? _run_touch_callbacks { super } : super
337
+ end
338
+
335
339
  private
336
340
 
337
341
  def create_or_update(*)
@@ -130,6 +130,7 @@ module ActiveRecord
130
130
  end
131
131
 
132
132
  def quoted_time(value) # :nodoc:
133
+ value = value.change(year: 2000, month: 1, day: 1)
133
134
  quoted_date(value).sub(/\A\d\d\d\d-\d\d-\d\d /, "")
134
135
  end
135
136
 
@@ -498,6 +498,9 @@ module ActiveRecord
498
498
  # t.date
499
499
  # t.binary
500
500
  # t.boolean
501
+ # t.foreign_key
502
+ # t.json
503
+ # t.virtual
501
504
  # t.remove
502
505
  # t.remove_references
503
506
  # t.remove_belongs_to
@@ -662,19 +665,19 @@ module ActiveRecord
662
665
 
663
666
  # Adds a foreign key.
664
667
  #
665
- # t.foreign_key(:authors)
668
+ # t.foreign_key(:authors)
666
669
  #
667
670
  # See {connection.add_foreign_key}[rdoc-ref:SchemaStatements#add_foreign_key]
668
- def foreign_key(*args) # :nodoc:
671
+ def foreign_key(*args)
669
672
  @base.add_foreign_key(name, *args)
670
673
  end
671
674
 
672
675
  # Checks to see if a foreign key exists.
673
676
  #
674
- # t.foreign_key(:authors) unless t.foreign_key_exists?(:authors)
677
+ # t.foreign_key(:authors) unless t.foreign_key_exists?(:authors)
675
678
  #
676
679
  # See {connection.foreign_key_exists?}[rdoc-ref:SchemaStatements#foreign_key_exists?]
677
- def foreign_key_exists?(*args) # :nodoc:
680
+ def foreign_key_exists?(*args)
678
681
  @base.foreign_key_exists?(name, *args)
679
682
  end
680
683
  end
@@ -741,22 +741,13 @@ module ActiveRecord
741
741
  # ====== Creating an index with a specific operator class
742
742
  #
743
743
  # add_index(:developers, :name, using: 'gist', opclass: :gist_trgm_ops)
744
- #
745
- # generates:
746
- #
747
- # CREATE INDEX developers_on_name ON developers USING gist (name gist_trgm_ops) -- PostgreSQL
744
+ # # CREATE INDEX developers_on_name ON developers USING gist (name gist_trgm_ops) -- PostgreSQL
748
745
  #
749
746
  # add_index(:developers, [:name, :city], using: 'gist', opclass: { city: :gist_trgm_ops })
750
- #
751
- # generates:
752
- #
753
- # CREATE INDEX developers_on_name_and_city ON developers USING gist (name, city gist_trgm_ops) -- PostgreSQL
747
+ # # CREATE INDEX developers_on_name_and_city ON developers USING gist (name, city gist_trgm_ops) -- PostgreSQL
754
748
  #
755
749
  # add_index(:developers, [:name, :city], using: 'gist', opclass: :gist_trgm_ops)
756
- #
757
- # generates:
758
- #
759
- # CREATE INDEX developers_on_name_and_city ON developers USING gist (name gist_trgm_ops, city gist_trgm_ops) -- PostgreSQL
750
+ # # CREATE INDEX developers_on_name_and_city ON developers USING gist (name gist_trgm_ops, city gist_trgm_ops) -- PostgreSQL
760
751
  #
761
752
  # Note: only supported by PostgreSQL
762
753
  #
@@ -17,11 +17,19 @@ module ActiveRecord
17
17
  end
18
18
 
19
19
  def committed?
20
- @state == :committed
20
+ @state == :committed || @state == :fully_committed
21
+ end
22
+
23
+ def fully_committed?
24
+ @state == :fully_committed
21
25
  end
22
26
 
23
27
  def rolledback?
24
- @state == :rolledback
28
+ @state == :rolledback || @state == :fully_rolledback
29
+ end
30
+
31
+ def fully_rolledback?
32
+ @state == :fully_rolledback
25
33
  end
26
34
 
27
35
  def fully_completed?
@@ -55,10 +63,19 @@ module ActiveRecord
55
63
  @state = :rolledback
56
64
  end
57
65
 
66
+ def full_rollback!
67
+ @children.each { |c| c.rollback! }
68
+ @state = :fully_rolledback
69
+ end
70
+
58
71
  def commit!
59
72
  @state = :committed
60
73
  end
61
74
 
75
+ def full_commit!
76
+ @state = :fully_committed
77
+ end
78
+
62
79
  def nullify!
63
80
  @state = nil
64
81
  end
@@ -89,10 +106,6 @@ module ActiveRecord
89
106
  records << record
90
107
  end
91
108
 
92
- def rollback
93
- @state.rollback!
94
- end
95
-
96
109
  def rollback_records
97
110
  ite = records.uniq
98
111
  while record = ite.shift
@@ -104,10 +117,6 @@ module ActiveRecord
104
117
  end
105
118
  end
106
119
 
107
- def commit
108
- @state.commit!
109
- end
110
-
111
120
  def before_commit_records
112
121
  records.uniq.each(&:before_committed!) if @run_commit_callbacks
113
122
  end
@@ -146,12 +155,12 @@ module ActiveRecord
146
155
 
147
156
  def rollback
148
157
  connection.rollback_to_savepoint(savepoint_name)
149
- super
158
+ @state.rollback!
150
159
  end
151
160
 
152
161
  def commit
153
162
  connection.release_savepoint(savepoint_name)
154
- super
163
+ @state.commit!
155
164
  end
156
165
 
157
166
  def full_rollback?; false; end
@@ -169,12 +178,12 @@ module ActiveRecord
169
178
 
170
179
  def rollback
171
180
  connection.rollback_db_transaction
172
- super
181
+ @state.full_rollback!
173
182
  end
174
183
 
175
184
  def commit
176
185
  connection.commit_db_transaction
177
- super
186
+ @state.full_commit!
178
187
  end
179
188
  end
180
189
 
@@ -560,17 +560,6 @@ module ActiveRecord
560
560
  @max_allowed_packet ||= (show_variable("max_allowed_packet") - bytes_margin)
561
561
  end
562
562
 
563
- def with_multi_statements
564
- previous_flags = @config[:flags]
565
- @config[:flags] = Mysql2::Client::MULTI_STATEMENTS
566
- reconnect!
567
-
568
- yield
569
- ensure
570
- @config[:flags] = previous_flags
571
- reconnect!
572
- end
573
-
574
563
  def initialize_type_map(m = type_map)
575
564
  super
576
565
 
@@ -62,6 +62,42 @@ module ActiveRecord
62
62
  @connection.next_result while @connection.more_results?
63
63
  end
64
64
 
65
+ def supports_set_server_option?
66
+ @connection.respond_to?(:set_server_option)
67
+ end
68
+
69
+ def multi_statements_enabled?(flags)
70
+ if flags.is_a?(Array)
71
+ flags.include?("MULTI_STATEMENTS")
72
+ else
73
+ (flags & Mysql2::Client::MULTI_STATEMENTS) != 0
74
+ end
75
+ end
76
+
77
+ def with_multi_statements
78
+ previous_flags = @config[:flags]
79
+
80
+ unless multi_statements_enabled?(previous_flags)
81
+ if supports_set_server_option?
82
+ @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
83
+ else
84
+ @config[:flags] = Mysql2::Client::MULTI_STATEMENTS
85
+ reconnect!
86
+ end
87
+ end
88
+
89
+ yield
90
+ ensure
91
+ unless multi_statements_enabled?(previous_flags)
92
+ if supports_set_server_option?
93
+ @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
94
+ else
95
+ @config[:flags] = previous_flags
96
+ reconnect!
97
+ end
98
+ end
99
+ end
100
+
65
101
  def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
66
102
  # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
67
103
  # made since we established the connection
@@ -80,8 +80,8 @@ module ActiveRecord
80
80
 
81
81
  def new_column_from_field(table_name, field)
82
82
  type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
83
- if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\(\))?\z/i.match?(field[:Default])
84
- default, default_function = nil, "CURRENT_TIMESTAMP"
83
+ if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(field[:Default])
84
+ default, default_function = nil, field[:Default]
85
85
  else
86
86
  default, default_function = field[:Default], nil
87
87
  end
@@ -66,6 +66,10 @@ module ActiveRecord
66
66
  deserialize(raw_old_value) != new_value
67
67
  end
68
68
 
69
+ def force_equality?(value)
70
+ value.is_a?(::Array)
71
+ end
72
+
69
73
  private
70
74
 
71
75
  def type_cast_array(value, method)
@@ -53,6 +53,10 @@ module ActiveRecord
53
53
  ::Range.new(new_begin, new_end, value.exclude_end?)
54
54
  end
55
55
 
56
+ def force_equality?(value)
57
+ value.is_a?(::Range)
58
+ end
59
+
56
60
  private
57
61
 
58
62
  def type_cast_single(value)
@@ -751,7 +751,7 @@ module ActiveRecord
751
751
 
752
752
  def data_source_sql(name = nil, type: nil)
753
753
  scope = quoted_scope(name, type: type)
754
- scope[:type] ||= "'r','v','m','f'" # (r)elation/table, (v)iew, (m)aterialized view, (f)oreign table
754
+ scope[:type] ||= "'r','v','m','p','f'" # (r)elation/table, (v)iew, (m)aterialized view, (p)artitioned table, (f)oreign table
755
755
 
756
756
  sql = "SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace".dup
757
757
  sql << " WHERE n.nspname = #{scope[:schema]}"
@@ -765,7 +765,7 @@ module ActiveRecord
765
765
  type = \
766
766
  case type
767
767
  when "BASE TABLE"
768
- "'r'"
768
+ "'r','p'"
769
769
  when "VIEW"
770
770
  "'v','m'"
771
771
  when "FOREIGN TABLE"