activerecord 6.0.0.beta1 → 6.0.0.beta2

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +99 -2
  3. data/lib/active_record.rb +7 -0
  4. data/lib/active_record/associations/association.rb +17 -0
  5. data/lib/active_record/associations/collection_association.rb +5 -6
  6. data/lib/active_record/associations/collection_proxy.rb +12 -41
  7. data/lib/active_record/associations/has_many_association.rb +1 -9
  8. data/lib/active_record/associations/join_dependency/join_association.rb +11 -6
  9. data/lib/active_record/associations/preloader/association.rb +3 -4
  10. data/lib/active_record/associations/preloader/through_association.rb +9 -20
  11. data/lib/active_record/callbacks.rb +3 -3
  12. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +25 -12
  13. data/lib/active_record/connection_adapters/abstract/database_statements.rb +17 -9
  14. data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -1
  15. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
  16. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +47 -33
  17. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +16 -8
  18. data/lib/active_record/connection_adapters/abstract/transaction.rb +5 -2
  19. data/lib/active_record/connection_adapters/abstract_adapter.rb +6 -4
  20. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +28 -65
  21. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +1 -1
  22. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  23. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  24. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +59 -1
  25. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  26. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  27. data/lib/active_record/connection_adapters/postgresql/quoting.rb +1 -1
  28. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
  29. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
  30. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -27
  31. data/lib/active_record/connection_adapters/postgresql_adapter.rb +30 -0
  32. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +27 -1
  33. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +8 -5
  34. data/lib/active_record/connection_handling.rb +9 -4
  35. data/lib/active_record/core.rb +13 -1
  36. data/lib/active_record/database_configurations.rb +30 -10
  37. data/lib/active_record/database_configurations/hash_config.rb +1 -1
  38. data/lib/active_record/database_configurations/url_config.rb +9 -4
  39. data/lib/active_record/errors.rb +17 -12
  40. data/lib/active_record/gem_version.rb +1 -1
  41. data/lib/active_record/inheritance.rb +1 -1
  42. data/lib/active_record/middleware/database_selector.rb +75 -0
  43. data/lib/active_record/middleware/database_selector/resolver.rb +90 -0
  44. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  45. data/lib/active_record/migration.rb +1 -1
  46. data/lib/active_record/migration/compatibility.rb +62 -63
  47. data/lib/active_record/persistence.rb +6 -6
  48. data/lib/active_record/querying.rb +2 -3
  49. data/lib/active_record/railtie.rb +9 -0
  50. data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
  51. data/lib/active_record/reflection.rb +15 -29
  52. data/lib/active_record/relation.rb +86 -15
  53. data/lib/active_record/relation/calculations.rb +2 -4
  54. data/lib/active_record/relation/delegation.rb +1 -1
  55. data/lib/active_record/relation/finder_methods.rb +8 -4
  56. data/lib/active_record/relation/query_attribute.rb +5 -3
  57. data/lib/active_record/relation/query_methods.rb +28 -8
  58. data/lib/active_record/relation/spawn_methods.rb +1 -1
  59. data/lib/active_record/relation/where_clause.rb +1 -5
  60. data/lib/active_record/scoping.rb +6 -7
  61. data/lib/active_record/scoping/default.rb +1 -8
  62. data/lib/active_record/scoping/named.rb +9 -1
  63. data/lib/active_record/test_fixtures.rb +2 -2
  64. data/lib/active_record/timestamp.rb +9 -3
  65. data/lib/active_record/validations/uniqueness.rb +3 -1
  66. data/lib/arel.rb +7 -0
  67. data/lib/arel/nodes/and.rb +1 -1
  68. data/lib/arel/nodes/case.rb +1 -1
  69. metadata +11 -8
@@ -77,9 +77,6 @@ module ActiveRecord
77
77
 
78
78
  SIMPLE_INT = /\A\d+\z/
79
79
 
80
- attr_writer :visitor
81
- deprecate :visitor=
82
-
83
80
  attr_accessor :pool
84
81
  attr_reader :schema_cache, :visitor, :owner, :logger, :lock, :prepared_statements, :prevent_writes
85
82
  alias :in_use? :owner
@@ -105,7 +102,7 @@ module ActiveRecord
105
102
  end
106
103
 
107
104
  def self.build_read_query_regexp(*parts) # :nodoc:
108
- parts = parts.map { |part| /\A\s*#{part}/i }
105
+ parts = parts.map { |part| /\A[\(\s]*#{part}/i }
109
106
  Regexp.union(*parts)
110
107
  end
111
108
 
@@ -338,6 +335,7 @@ module ActiveRecord
338
335
  def supports_foreign_keys_in_create?
339
336
  supports_foreign_keys?
340
337
  end
338
+ deprecate :supports_foreign_keys_in_create?
341
339
 
342
340
  # Does this adapter support views?
343
341
  def supports_views?
@@ -508,6 +506,10 @@ module ActiveRecord
508
506
  @connection
509
507
  end
510
508
 
509
+ def default_uniqueness_comparison(attribute, value) # :nodoc:
510
+ case_sensitive_comparison(attribute, value)
511
+ end
512
+
511
513
  def case_sensitive_comparison(attribute, value) # :nodoc:
512
514
  attribute.eq(value)
513
515
  end
@@ -29,7 +29,7 @@ module ActiveRecord
29
29
  NATIVE_DATABASE_TYPES = {
30
30
  primary_key: "bigint auto_increment PRIMARY KEY",
31
31
  string: { name: "varchar", limit: 255 },
32
- text: { name: "text", limit: 65535 },
32
+ text: { name: "text" },
33
33
  integer: { name: "int", limit: 4 },
34
34
  float: { name: "float", limit: 24 },
35
35
  decimal: { name: "decimal" },
@@ -37,7 +37,8 @@ module ActiveRecord
37
37
  timestamp: { name: "timestamp" },
38
38
  time: { name: "time" },
39
39
  date: { name: "date" },
40
- binary: { name: "blob", limit: 65535 },
40
+ binary: { name: "blob" },
41
+ blob: { name: "blob" },
41
42
  boolean: { name: "tinyint", limit: 1 },
42
43
  json: { name: "json" },
43
44
  }
@@ -165,9 +166,9 @@ module ActiveRecord
165
166
 
166
167
  def explain(arel, binds = [])
167
168
  sql = "EXPLAIN #{to_sql(arel, binds)}"
168
- start = Time.now
169
+ start = Concurrent.monotonic_time
169
170
  result = exec_query(sql, "EXPLAIN", binds)
170
- elapsed = Time.now - start
171
+ elapsed = Concurrent.monotonic_time - start
171
172
 
172
173
  MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
173
174
  end
@@ -430,30 +431,6 @@ module ActiveRecord
430
431
  table_options
431
432
  end
432
433
 
433
- # Maps logical Rails types to MySQL-specific data types.
434
- def type_to_sql(type, limit: nil, precision: nil, scale: nil, unsigned: nil, **) # :nodoc:
435
- sql = \
436
- case type.to_s
437
- when "integer"
438
- integer_to_sql(limit)
439
- when "text"
440
- text_to_sql(limit)
441
- when "blob"
442
- binary_to_sql(limit)
443
- when "binary"
444
- if (0..0xfff) === limit
445
- "varbinary(#{limit})"
446
- else
447
- binary_to_sql(limit)
448
- end
449
- else
450
- super
451
- end
452
-
453
- sql = "#{sql} unsigned" if unsigned && type != :primary_key
454
- sql
455
- end
456
-
457
434
  # SHOW VARIABLES LIKE 'name'
458
435
  def show_variable(name)
459
436
  query_value("SELECT @@#{name}", "SCHEMA")
@@ -627,6 +604,7 @@ module ActiveRecord
627
604
  ER_LOCK_WAIT_TIMEOUT = 1205
628
605
  ER_QUERY_INTERRUPTED = 1317
629
606
  ER_QUERY_TIMEOUT = 3024
607
+ ER_FK_INCOMPATIBLE_COLUMNS = 3780
630
608
 
631
609
  def translate_exception(exception, message:, sql:, binds:)
632
610
  case error_number(exception)
@@ -634,7 +612,7 @@ module ActiveRecord
634
612
  RecordNotUnique.new(message, sql: sql, binds: binds)
635
613
  when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
636
614
  InvalidForeignKey.new(message, sql: sql, binds: binds)
637
- when ER_CANNOT_ADD_FOREIGN
615
+ when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
638
616
  mismatched_foreign_key(message, sql: sql, binds: binds)
639
617
  when ER_CANNOT_CREATE_TABLE
640
618
  if message.include?("errno: 150")
@@ -708,6 +686,12 @@ module ActiveRecord
708
686
  end
709
687
 
710
688
  def add_timestamps_for_alter(table_name, options = {})
689
+ options[:null] = false if options[:null].nil?
690
+
691
+ if !options.key?(:precision) && supports_datetime_with_precision?
692
+ options[:precision] = 6
693
+ end
694
+
711
695
  [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
712
696
  end
713
697
 
@@ -787,48 +771,27 @@ module ActiveRecord
787
771
  end
788
772
 
789
773
  def mismatched_foreign_key(message, sql:, binds:)
790
- parts = sql.scan(/`(\w+)`[ $)]/).flatten
791
- MismatchedForeignKey.new(
792
- self,
774
+ match = %r/
775
+ (?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
776
+ FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
777
+ REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
778
+ /xmi.match(sql)
779
+
780
+ options = {
793
781
  message: message,
794
782
  sql: sql,
795
783
  binds: binds,
796
- table: parts[0],
797
- foreign_key: parts[1],
798
- target_table: parts[2],
799
- primary_key: parts[3],
800
- )
801
- end
802
-
803
- def integer_to_sql(limit) # :nodoc:
804
- case limit
805
- when 1; "tinyint"
806
- when 2; "smallint"
807
- when 3; "mediumint"
808
- when nil, 4; "int"
809
- when 5..8; "bigint"
810
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead.")
811
- end
812
- end
784
+ }
813
785
 
814
- def text_to_sql(limit) # :nodoc:
815
- case limit
816
- when 0..0xff; "tinytext"
817
- when nil, 0x100..0xffff; "text"
818
- when 0x10000..0xffffff; "mediumtext"
819
- when 0x1000000..0xffffffff; "longtext"
820
- else raise(ActiveRecordError, "No text type has byte length #{limit}")
786
+ if match
787
+ options[:table] = match[:table]
788
+ options[:foreign_key] = match[:foreign_key]
789
+ options[:target_table] = match[:target_table]
790
+ options[:primary_key] = match[:primary_key]
791
+ options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
821
792
  end
822
- end
823
793
 
824
- def binary_to_sql(limit) # :nodoc:
825
- case limit
826
- when 0..0xff; "tinyblob"
827
- when nil, 0x100..0xffff; "blob"
828
- when 0x10000..0xffffff; "mediumblob"
829
- when 0x1000000..0xffffffff; "longblob"
830
- else raise(ActiveRecordError, "No binary type has byte length #{limit}")
831
- end
794
+ MismatchedForeignKey.new(options)
832
795
  end
833
796
 
834
797
  def version_string
@@ -3,7 +3,7 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module DetermineIfPreparableVisitor
6
- attr_reader :preparable
6
+ attr_accessor :preparable
7
7
 
8
8
  def accept(*)
9
9
  @preparable = true
@@ -4,48 +4,56 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module MySQL
6
6
  module ColumnMethods
7
- def blob(*args, **options)
8
- args.each { |name| column(name, :blob, options) }
9
- end
7
+ extend ActiveSupport::Concern
10
8
 
11
- def tinyblob(*args, **options)
12
- args.each { |name| column(name, :tinyblob, options) }
13
- end
9
+ ##
10
+ # :method: blob
11
+ # :call-seq: blob(*names, **options)
14
12
 
15
- def mediumblob(*args, **options)
16
- args.each { |name| column(name, :mediumblob, options) }
17
- end
13
+ ##
14
+ # :method: tinyblob
15
+ # :call-seq: tinyblob(*names, **options)
18
16
 
19
- def longblob(*args, **options)
20
- args.each { |name| column(name, :longblob, options) }
21
- end
17
+ ##
18
+ # :method: mediumblob
19
+ # :call-seq: mediumblob(*names, **options)
22
20
 
23
- def tinytext(*args, **options)
24
- args.each { |name| column(name, :tinytext, options) }
25
- end
21
+ ##
22
+ # :method: longblob
23
+ # :call-seq: longblob(*names, **options)
26
24
 
27
- def mediumtext(*args, **options)
28
- args.each { |name| column(name, :mediumtext, options) }
29
- end
25
+ ##
26
+ # :method: tinytext
27
+ # :call-seq: tinytext(*names, **options)
30
28
 
31
- def longtext(*args, **options)
32
- args.each { |name| column(name, :longtext, options) }
33
- end
29
+ ##
30
+ # :method: mediumtext
31
+ # :call-seq: mediumtext(*names, **options)
34
32
 
35
- def unsigned_integer(*args, **options)
36
- args.each { |name| column(name, :unsigned_integer, options) }
37
- end
33
+ ##
34
+ # :method: longtext
35
+ # :call-seq: longtext(*names, **options)
38
36
 
39
- def unsigned_bigint(*args, **options)
40
- args.each { |name| column(name, :unsigned_bigint, options) }
41
- end
37
+ ##
38
+ # :method: unsigned_integer
39
+ # :call-seq: unsigned_integer(*names, **options)
42
40
 
43
- def unsigned_float(*args, **options)
44
- args.each { |name| column(name, :unsigned_float, options) }
45
- end
41
+ ##
42
+ # :method: unsigned_bigint
43
+ # :call-seq: unsigned_bigint(*names, **options)
44
+
45
+ ##
46
+ # :method: unsigned_float
47
+ # :call-seq: unsigned_float(*names, **options)
48
+
49
+ ##
50
+ # :method: unsigned_decimal
51
+ # :call-seq: unsigned_decimal(*names, **options)
46
52
 
47
- def unsigned_decimal(*args, **options)
48
- args.each { |name| column(name, :unsigned_decimal, options) }
53
+ included do
54
+ define_column_methods :blob, :tinyblob, :mediumblob, :longblob,
55
+ :tinytext, :mediumtext, :longtext, :unsigned_integer, :unsigned_bigint,
56
+ :unsigned_float, :unsigned_decimal
49
57
  end
50
58
  end
51
59
 
@@ -10,6 +10,10 @@ module ActiveRecord
10
10
  spec[:unsigned] = "true" if column.unsigned?
11
11
  spec[:auto_increment] = "true" if column.auto_increment?
12
12
 
13
+ if /\A(?<size>tiny|medium|long)(?:text|blob)/ =~ column.sql_type
14
+ spec = { size: size.to_sym.inspect }.merge!(spec)
15
+ end
16
+
13
17
  if @connection.supports_virtual_columns? && column.virtual?
14
18
  spec[:as] = extract_expression_for_virtual_column(column)
15
19
  spec[:stored] = "true" if /\b(?:STORED|PERSISTENT)\b/.match?(column.extra)
@@ -37,13 +41,15 @@ module ActiveRecord
37
41
  case column.sql_type
38
42
  when /\Atimestamp\b/
39
43
  :timestamp
40
- when "tinyblob"
41
- :blob
42
44
  else
43
45
  super
44
46
  end
45
47
  end
46
48
 
49
+ def schema_limit(column)
50
+ super unless /\A(?:tiny|medium|long)?(?:text|blob)/.match?(column.sql_type)
51
+ end
52
+
47
53
  def schema_precision(column)
48
54
  super unless /\A(?:date)?time(?:stamp)?\b/.match?(column.sql_type) && column.precision == 0
49
55
  end
@@ -97,6 +97,30 @@ module ActiveRecord
97
97
  MySQL::SchemaDumper.create(self, options)
98
98
  end
99
99
 
100
+ # Maps logical Rails types to MySQL-specific data types.
101
+ def type_to_sql(type, limit: nil, precision: nil, scale: nil, size: limit_to_size(limit, type), unsigned: nil, **)
102
+ sql =
103
+ case type.to_s
104
+ when "integer"
105
+ integer_to_sql(limit)
106
+ when "text"
107
+ type_with_size_to_sql("text", size)
108
+ when "blob"
109
+ type_with_size_to_sql("blob", size)
110
+ when "binary"
111
+ if (0..0xfff) === limit
112
+ "varbinary(#{limit})"
113
+ else
114
+ type_with_size_to_sql("blob", size)
115
+ end
116
+ else
117
+ super
118
+ end
119
+
120
+ sql = "#{sql} unsigned" if unsigned && type != :primary_key
121
+ sql
122
+ end
123
+
100
124
  private
101
125
  CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
102
126
 
@@ -127,7 +151,7 @@ module ActiveRecord
127
151
  end
128
152
 
129
153
  def create_table_definition(*args)
130
- MySQL::TableDefinition.new(*args)
154
+ MySQL::TableDefinition.new(self, *args)
131
155
  end
132
156
 
133
157
  def new_column_from_field(table_name, field)
@@ -197,6 +221,40 @@ module ActiveRecord
197
221
  schema, name = nil, schema unless name
198
222
  [schema, name]
199
223
  end
224
+
225
+ def type_with_size_to_sql(type, size)
226
+ case size&.to_s
227
+ when nil, "tiny", "medium", "long"
228
+ "#{size}#{type}"
229
+ else
230
+ raise ArgumentError,
231
+ "#{size.inspect} is invalid :size value. Only :tiny, :medium, and :long are allowed."
232
+ end
233
+ end
234
+
235
+ def limit_to_size(limit, type)
236
+ case type.to_s
237
+ when "text", "blob", "binary"
238
+ case limit
239
+ when 0..0xff; "tiny"
240
+ when nil, 0x100..0xffff; nil
241
+ when 0x10000..0xffffff; "medium"
242
+ when 0x1000000..0xffffffff; "long"
243
+ else raise ActiveRecordError, "No #{type} type has byte size #{limit}"
244
+ end
245
+ end
246
+ end
247
+
248
+ def integer_to_sql(limit)
249
+ case limit
250
+ when 1; "tinyint"
251
+ when 2; "smallint"
252
+ when 3; "mediumint"
253
+ when nil, 4; "int"
254
+ when 5..8; "bigint"
255
+ else raise ActiveRecordError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead."
256
+ end
257
+ end
200
258
  end
201
259
  end
202
260
  end
@@ -64,7 +64,7 @@ module ActiveRecord
64
64
  end
65
65
 
66
66
  def type_cast_single_for_database(value)
67
- infinity?(value) ? value : @subtype.serialize(value)
67
+ infinity?(value) ? value : @subtype.serialize(@subtype.cast(value))
68
68
  end
69
69
 
70
70
  def extract_bounds(value)
@@ -13,9 +13,12 @@ module ActiveRecord
13
13
  :uuid
14
14
  end
15
15
 
16
- def cast(value)
17
- value.to_s[ACCEPTABLE_UUID, 0]
18
- end
16
+ private
17
+
18
+ def cast_value(value)
19
+ casted = value.to_s
20
+ casted if casted.match?(ACCEPTABLE_UUID)
21
+ end
19
22
  end
20
23
  end
21
24
  end
@@ -138,7 +138,7 @@ module ActiveRecord
138
138
  end
139
139
 
140
140
  def encode_range(range)
141
- "[#{type_cast_range_value(range.first)},#{type_cast_range_value(range.last)}#{range.exclude_end? ? ')' : ']'}"
141
+ "[#{type_cast_range_value(range.begin)},#{type_cast_range_value(range.end)}#{range.exclude_end? ? ')' : ']'}"
142
142
  end
143
143
 
144
144
  def determine_encoding_of_strings_in_array(value)
@@ -17,6 +17,42 @@ module ActiveRecord
17
17
  "VALIDATE CONSTRAINT #{quote_column_name(name)}"
18
18
  end
19
19
 
20
+ def visit_ChangeColumnDefinition(o)
21
+ column = o.column
22
+ column.sql_type = type_to_sql(column.type, column.options)
23
+ quoted_column_name = quote_column_name(o.name)
24
+
25
+ change_column_sql = +"ALTER COLUMN #{quoted_column_name} TYPE #{column.sql_type}"
26
+
27
+ options = column_options(column)
28
+
29
+ if options[:collation]
30
+ change_column_sql << " COLLATE \"#{options[:collation]}\""
31
+ end
32
+
33
+ if options[:using]
34
+ change_column_sql << " USING #{options[:using]}"
35
+ elsif options[:cast_as]
36
+ cast_as_type = type_to_sql(options[:cast_as], options)
37
+ change_column_sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
38
+ end
39
+
40
+ if options.key?(:default)
41
+ if options[:default].nil?
42
+ change_column_sql << ", ALTER COLUMN #{quoted_column_name} DROP DEFAULT"
43
+ else
44
+ quoted_default = quote_default_expression(options[:default], column)
45
+ change_column_sql << ", ALTER COLUMN #{quoted_column_name} SET DEFAULT #{quoted_default}"
46
+ end
47
+ end
48
+
49
+ if options.key?(:null)
50
+ change_column_sql << ", ALTER COLUMN #{quoted_column_name} #{options[:null] ? 'DROP' : 'SET'} NOT NULL"
51
+ end
52
+
53
+ change_column_sql
54
+ end
55
+
20
56
  def add_column_options!(sql, options)
21
57
  if options[:collation]
22
58
  sql << " COLLATE \"#{options[:collation]}\""