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
@@ -4,6 +4,8 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module PostgreSQL
6
6
  module ColumnMethods
7
+ extend ActiveSupport::Concern
8
+
7
9
  # Defines the primary key field.
8
10
  # Use of the native PostgreSQL UUID type is supported, and can be used
9
11
  # by defining your tables as such:
@@ -51,124 +53,131 @@ module ActiveRecord
51
53
  super
52
54
  end
53
55
 
54
- def bigserial(*args, **options)
55
- args.each { |name| column(name, :bigserial, options) }
56
- end
56
+ ##
57
+ # :method: bigserial
58
+ # :call-seq: bigserial(*names, **options)
57
59
 
58
- def bit(*args, **options)
59
- args.each { |name| column(name, :bit, options) }
60
- end
60
+ ##
61
+ # :method: bit
62
+ # :call-seq: bit(*names, **options)
61
63
 
62
- def bit_varying(*args, **options)
63
- args.each { |name| column(name, :bit_varying, options) }
64
- end
64
+ ##
65
+ # :method: bit_varying
66
+ # :call-seq: bit_varying(*names, **options)
65
67
 
66
- def cidr(*args, **options)
67
- args.each { |name| column(name, :cidr, options) }
68
- end
68
+ ##
69
+ # :method: cidr
70
+ # :call-seq: cidr(*names, **options)
69
71
 
70
- def citext(*args, **options)
71
- args.each { |name| column(name, :citext, options) }
72
- end
72
+ ##
73
+ # :method: citext
74
+ # :call-seq: citext(*names, **options)
73
75
 
74
- def daterange(*args, **options)
75
- args.each { |name| column(name, :daterange, options) }
76
- end
76
+ ##
77
+ # :method: daterange
78
+ # :call-seq: daterange(*names, **options)
77
79
 
78
- def hstore(*args, **options)
79
- args.each { |name| column(name, :hstore, options) }
80
- end
80
+ ##
81
+ # :method: hstore
82
+ # :call-seq: hstore(*names, **options)
81
83
 
82
- def inet(*args, **options)
83
- args.each { |name| column(name, :inet, options) }
84
- end
84
+ ##
85
+ # :method: inet
86
+ # :call-seq: inet(*names, **options)
85
87
 
86
- def interval(*args, **options)
87
- args.each { |name| column(name, :interval, options) }
88
- end
88
+ ##
89
+ # :method: interval
90
+ # :call-seq: interval(*names, **options)
89
91
 
90
- def int4range(*args, **options)
91
- args.each { |name| column(name, :int4range, options) }
92
- end
92
+ ##
93
+ # :method: int4range
94
+ # :call-seq: int4range(*names, **options)
93
95
 
94
- def int8range(*args, **options)
95
- args.each { |name| column(name, :int8range, options) }
96
- end
96
+ ##
97
+ # :method: int8range
98
+ # :call-seq: int8range(*names, **options)
97
99
 
98
- def jsonb(*args, **options)
99
- args.each { |name| column(name, :jsonb, options) }
100
- end
100
+ ##
101
+ # :method: jsonb
102
+ # :call-seq: jsonb(*names, **options)
101
103
 
102
- def ltree(*args, **options)
103
- args.each { |name| column(name, :ltree, options) }
104
- end
104
+ ##
105
+ # :method: ltree
106
+ # :call-seq: ltree(*names, **options)
105
107
 
106
- def macaddr(*args, **options)
107
- args.each { |name| column(name, :macaddr, options) }
108
- end
108
+ ##
109
+ # :method: macaddr
110
+ # :call-seq: macaddr(*names, **options)
109
111
 
110
- def money(*args, **options)
111
- args.each { |name| column(name, :money, options) }
112
- end
112
+ ##
113
+ # :method: money
114
+ # :call-seq: money(*names, **options)
113
115
 
114
- def numrange(*args, **options)
115
- args.each { |name| column(name, :numrange, options) }
116
- end
116
+ ##
117
+ # :method: numrange
118
+ # :call-seq: numrange(*names, **options)
117
119
 
118
- def oid(*args, **options)
119
- args.each { |name| column(name, :oid, options) }
120
- end
120
+ ##
121
+ # :method: oid
122
+ # :call-seq: oid(*names, **options)
121
123
 
122
- def point(*args, **options)
123
- args.each { |name| column(name, :point, options) }
124
- end
124
+ ##
125
+ # :method: point
126
+ # :call-seq: point(*names, **options)
125
127
 
126
- def line(*args, **options)
127
- args.each { |name| column(name, :line, options) }
128
- end
128
+ ##
129
+ # :method: line
130
+ # :call-seq: line(*names, **options)
129
131
 
130
- def lseg(*args, **options)
131
- args.each { |name| column(name, :lseg, options) }
132
- end
132
+ ##
133
+ # :method: lseg
134
+ # :call-seq: lseg(*names, **options)
133
135
 
134
- def box(*args, **options)
135
- args.each { |name| column(name, :box, options) }
136
- end
136
+ ##
137
+ # :method: box
138
+ # :call-seq: box(*names, **options)
137
139
 
138
- def path(*args, **options)
139
- args.each { |name| column(name, :path, options) }
140
- end
140
+ ##
141
+ # :method: path
142
+ # :call-seq: path(*names, **options)
141
143
 
142
- def polygon(*args, **options)
143
- args.each { |name| column(name, :polygon, options) }
144
- end
144
+ ##
145
+ # :method: polygon
146
+ # :call-seq: polygon(*names, **options)
145
147
 
146
- def circle(*args, **options)
147
- args.each { |name| column(name, :circle, options) }
148
- end
148
+ ##
149
+ # :method: circle
150
+ # :call-seq: circle(*names, **options)
149
151
 
150
- def serial(*args, **options)
151
- args.each { |name| column(name, :serial, options) }
152
- end
152
+ ##
153
+ # :method: serial
154
+ # :call-seq: serial(*names, **options)
153
155
 
154
- def tsrange(*args, **options)
155
- args.each { |name| column(name, :tsrange, options) }
156
- end
156
+ ##
157
+ # :method: tsrange
158
+ # :call-seq: tsrange(*names, **options)
157
159
 
158
- def tstzrange(*args, **options)
159
- args.each { |name| column(name, :tstzrange, options) }
160
- end
160
+ ##
161
+ # :method: tstzrange
162
+ # :call-seq: tstzrange(*names, **options)
161
163
 
162
- def tsvector(*args, **options)
163
- args.each { |name| column(name, :tsvector, options) }
164
- end
164
+ ##
165
+ # :method: tsvector
166
+ # :call-seq: tsvector(*names, **options)
165
167
 
166
- def uuid(*args, **options)
167
- args.each { |name| column(name, :uuid, options) }
168
- end
168
+ ##
169
+ # :method: uuid
170
+ # :call-seq: uuid(*names, **options)
171
+
172
+ ##
173
+ # :method: xml
174
+ # :call-seq: xml(*names, **options)
169
175
 
170
- def xml(*args, **options)
171
- args.each { |name| column(name, :xml, options) }
176
+ included do
177
+ define_column_methods :bigserial, :bit, :bit_varying, :cidr, :citext, :daterange,
178
+ :hstore, :inet, :interval, :int4range, :int8range, :jsonb, :ltree, :macaddr,
179
+ :money, :numrange, :oid, :point, :line, :lseg, :box, :path, :polygon, :circle,
180
+ :serial, :tsrange, :tstzrange, :tsvector, :uuid, :xml
172
181
  end
173
182
  end
174
183
 
@@ -548,14 +548,14 @@ module ActiveRecord
548
548
  # The hard limit is 1GB, because of a 32-bit size field, and TOAST.
549
549
  case limit
550
550
  when nil, 0..0x3fffffff; super(type)
551
- else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
551
+ else raise ActiveRecordError, "No binary type has byte size #{limit}. The limit on binary can be at most 1GB - 1byte."
552
552
  end
553
553
  when "text"
554
554
  # PostgreSQL doesn't support limits on text columns.
555
555
  # The hard limit is 1GB, according to section 8.3 in the manual.
556
556
  case limit
557
557
  when nil, 0..0x3fffffff; super(type)
558
- else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
558
+ else raise ActiveRecordError, "No text type has byte size #{limit}. The limit on text can be at most 1GB - 1byte."
559
559
  end
560
560
  when "integer"
561
561
  case limit
@@ -637,7 +637,7 @@ module ActiveRecord
637
637
  end
638
638
 
639
639
  def create_table_definition(*args)
640
- PostgreSQL::TableDefinition.new(*args)
640
+ PostgreSQL::TableDefinition.new(self, *args)
641
641
  end
642
642
 
643
643
  def create_alter_table(name)
@@ -683,38 +683,20 @@ module ActiveRecord
683
683
  end
684
684
  end
685
685
 
686
- def change_column_sql(table_name, column_name, type, options = {})
687
- quoted_column_name = quote_column_name(column_name)
688
- sql_type = type_to_sql(type, options)
689
- sql = +"ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
690
- if options[:collation]
691
- sql << " COLLATE \"#{options[:collation]}\""
692
- end
693
- if options[:using]
694
- sql << " USING #{options[:using]}"
695
- elsif options[:cast_as]
696
- cast_as_type = type_to_sql(options[:cast_as], options)
697
- sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
698
- end
699
-
700
- sql
701
- end
702
-
703
686
  def add_column_for_alter(table_name, column_name, type, options = {})
704
687
  return super unless options.key?(:comment)
705
688
  [super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
706
689
  end
707
690
 
708
691
  def change_column_for_alter(table_name, column_name, type, options = {})
709
- sqls = [change_column_sql(table_name, column_name, type, options)]
710
- sqls << change_column_default_for_alter(table_name, column_name, options[:default]) if options.key?(:default)
711
- sqls << change_column_null_for_alter(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
692
+ td = create_table_definition(table_name)
693
+ cd = td.new_column_definition(column_name, type, options)
694
+ sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
712
695
  sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
713
696
  sqls
714
697
  end
715
698
 
716
- # Changes the default value of a table column.
717
- def change_column_default_for_alter(table_name, column_name, default_or_changes) # :nodoc:
699
+ def change_column_default_for_alter(table_name, column_name, default_or_changes)
718
700
  column = column_for(table_name, column_name)
719
701
  return unless column
720
702
 
@@ -729,11 +711,17 @@ module ActiveRecord
729
711
  end
730
712
  end
731
713
 
732
- def change_column_null_for_alter(table_name, column_name, null, default = nil) #:nodoc:
733
- "ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
714
+ def change_column_null_for_alter(table_name, column_name, null, default = nil)
715
+ "ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
734
716
  end
735
717
 
736
718
  def add_timestamps_for_alter(table_name, options = {})
719
+ options[:null] = false if options[:null].nil?
720
+
721
+ if !options.key?(:precision) && supports_datetime_with_precision?
722
+ options[:precision] = 6
723
+ end
724
+
737
725
  [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
738
726
  end
739
727
 
@@ -236,6 +236,8 @@ module ActiveRecord
236
236
 
237
237
  # @local_tz is initialized as nil to avoid warnings when connect tries to use it
238
238
  @local_tz = nil
239
+ @default_timezone = nil
240
+ @timestamp_decoder = nil
239
241
  @max_identifier_length = nil
240
242
 
241
243
  configure_connection
@@ -628,6 +630,10 @@ module ActiveRecord
628
630
  def exec_no_cache(sql, name, binds)
629
631
  materialize_transactions
630
632
 
633
+ # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
634
+ # made since we established the connection
635
+ update_typemap_for_default_timezone
636
+
631
637
  type_casted_binds = type_casted_binds(binds)
632
638
  log(sql, name, binds, type_casted_binds) do
633
639
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@@ -638,6 +644,7 @@ module ActiveRecord
638
644
 
639
645
  def exec_cache(sql, name, binds)
640
646
  materialize_transactions
647
+ update_typemap_for_default_timezone
641
648
 
642
649
  stmt_key = prepare_statement(sql, binds)
643
650
  type_casted_binds = type_casted_binds(binds)
@@ -826,6 +833,18 @@ module ActiveRecord
826
833
  @connection.type_map_for_queries = map
827
834
  end
828
835
 
836
+ def update_typemap_for_default_timezone
837
+ if @default_timezone != ActiveRecord::Base.default_timezone && @timestamp_decoder
838
+ decoder_class = ActiveRecord::Base.default_timezone == :utc ?
839
+ PG::TextDecoder::TimestampUtc :
840
+ PG::TextDecoder::TimestampWithoutTimeZone
841
+
842
+ @timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
843
+ @connection.type_map_for_results.add_coder(@timestamp_decoder)
844
+ @default_timezone = ActiveRecord::Base.default_timezone
845
+ end
846
+ end
847
+
829
848
  def add_pg_decoders
830
849
  coders_by_name = {
831
850
  "int2" => PG::TextDecoder::Integer,
@@ -836,6 +855,13 @@ module ActiveRecord
836
855
  "float8" => PG::TextDecoder::Float,
837
856
  "bool" => PG::TextDecoder::Boolean,
838
857
  }
858
+
859
+ if defined?(PG::TextDecoder::TimestampUtc)
860
+ # Use native PG encoders available since pg-1.1
861
+ coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
862
+ coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
863
+ end
864
+
839
865
  known_coder_types = coders_by_name.keys.map { |n| quote(n) }
840
866
  query = <<~SQL % known_coder_types.join(", ")
841
867
  SELECT t.oid, t.typname
@@ -851,6 +877,10 @@ module ActiveRecord
851
877
  map = PG::TypeMapByOid.new
852
878
  coders.each { |coder| map.add_coder(coder) }
853
879
  @connection.type_map_for_results = map
880
+
881
+ # extract timestamp decoder for use in update_typemap_for_default_timezone
882
+ @timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
883
+ update_typemap_for_default_timezone
854
884
  end
855
885
 
856
886
  def construct_coder(row, coder_class)
@@ -52,6 +52,32 @@ module ActiveRecord
52
52
  end.compact
53
53
  end
54
54
 
55
+ def add_foreign_key(from_table, to_table, **options)
56
+ alter_table(from_table) do |definition|
57
+ to_table = strip_table_name_prefix_and_suffix(to_table)
58
+ definition.foreign_key(to_table, options)
59
+ end
60
+ end
61
+
62
+ def remove_foreign_key(from_table, to_table = nil, **options)
63
+ to_table ||= options[:to_table]
64
+ options = options.except(:name, :to_table)
65
+ foreign_keys = foreign_keys(from_table)
66
+
67
+ fkey = foreign_keys.detect do |fk|
68
+ table = to_table || begin
69
+ table = options[:column].to_s.delete_suffix("_id")
70
+ Base.pluralize_table_names ? table.pluralize : table
71
+ end
72
+ table = strip_table_name_prefix_and_suffix(table)
73
+ fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
74
+ fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
75
+ end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table}")
76
+
77
+ foreign_keys.delete(fkey)
78
+ alter_table(from_table, foreign_keys)
79
+ end
80
+
55
81
  def create_schema_dumper(options)
56
82
  SQLite3::SchemaDumper.create(self, options)
57
83
  end
@@ -62,7 +88,7 @@ module ActiveRecord
62
88
  end
63
89
 
64
90
  def create_table_definition(*args)
65
- SQLite3::TableDefinition.new(*args)
91
+ SQLite3::TableDefinition.new(self, *args)
66
92
  end
67
93
 
68
94
  def new_column_from_field(table_name, field)
@@ -9,7 +9,7 @@ require "active_record/connection_adapters/sqlite3/schema_definitions"
9
9
  require "active_record/connection_adapters/sqlite3/schema_dumper"
10
10
  require "active_record/connection_adapters/sqlite3/schema_statements"
11
11
 
12
- gem "sqlite3", "~> 1.3.6"
12
+ gem "sqlite3", "~> 1.3", ">= 1.3.6"
13
13
  require "sqlite3"
14
14
 
15
15
  module ActiveRecord
@@ -121,7 +121,7 @@ module ActiveRecord
121
121
  true
122
122
  end
123
123
 
124
- def supports_foreign_keys_in_create?
124
+ def supports_foreign_keys?
125
125
  true
126
126
  end
127
127
 
@@ -320,6 +320,9 @@ module ActiveRecord
320
320
  def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
321
321
  alter_table(table_name) do |definition|
322
322
  definition.remove_column column_name
323
+ definition.foreign_keys.delete_if do |_, fk_options|
324
+ fk_options[:column] == column_name.to_s
325
+ end
323
326
  end
324
327
  end
325
328
 
@@ -421,9 +424,8 @@ module ActiveRecord
421
424
  type.to_sym == :primary_key || options[:primary_key]
422
425
  end
423
426
 
424
- def alter_table(table_name, options = {})
427
+ def alter_table(table_name, foreign_keys = foreign_keys(table_name), **options)
425
428
  altered_table_name = "a#{table_name}"
426
- foreign_keys = foreign_keys(table_name)
427
429
 
428
430
  caller = lambda do |definition|
429
431
  rename = options[:rename] || {}
@@ -431,7 +433,8 @@ module ActiveRecord
431
433
  if column = rename[fk.options[:column]]
432
434
  fk.options[:column] = column
433
435
  end
434
- definition.foreign_key(fk.to_table, fk.options)
436
+ to_table = strip_table_name_prefix_and_suffix(fk.to_table)
437
+ definition.foreign_key(to_table, fk.options)
435
438
  end
436
439
 
437
440
  yield definition if block_given?