activerecord 5.0.0.beta3 → 5.0.0.beta4
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +225 -8
- data/examples/performance.rb +0 -1
- data/examples/simple.rb +0 -1
- data/lib/active_record.rb +0 -1
- data/lib/active_record/associations.rb +10 -6
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/join_dependency.rb +1 -1
- data/lib/active_record/associations/preloader.rb +1 -0
- data/lib/active_record/associations/preloader/through_association.rb +15 -8
- data/lib/active_record/attribute/user_provided_default.rb +10 -5
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +1 -1
- data/lib/active_record/base.rb +2 -1
- data/lib/active_record/collection_cache_key.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +0 -19
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +7 -8
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -1
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +16 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -4
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +20 -10
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +85 -20
- data/lib/active_record/connection_adapters/abstract/transaction.rb +13 -1
- data/lib/active_record/connection_adapters/abstract_adapter.rb +37 -16
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +119 -108
- data/lib/active_record/connection_adapters/column.rb +5 -6
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +27 -6
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -13
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -55
- data/lib/active_record/connection_adapters/postgresql/column.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +7 -10
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +65 -25
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +59 -30
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +19 -56
- data/lib/active_record/connection_adapters/statement_pool.rb +6 -4
- data/lib/active_record/core.rb +13 -4
- data/lib/active_record/enum.rb +1 -1
- data/lib/active_record/errors.rb +9 -0
- data/lib/active_record/fixture_set/file.rb +7 -1
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +4 -0
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/migration.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +1 -1
- data/lib/active_record/model_schema.rb +12 -0
- data/lib/active_record/nested_attributes.rb +1 -1
- data/lib/active_record/persistence.rb +1 -1
- data/lib/active_record/query_cache.rb +13 -16
- data/lib/active_record/querying.rb +1 -1
- data/lib/active_record/railtie.rb +8 -11
- data/lib/active_record/railties/databases.rake +5 -5
- data/lib/active_record/reflection.rb +21 -4
- data/lib/active_record/relation.rb +10 -10
- data/lib/active_record/relation/batches.rb +29 -9
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +27 -8
- data/lib/active_record/relation/predicate_builder.rb +4 -2
- data/lib/active_record/relation/where_clause.rb +2 -1
- data/lib/active_record/schema_dumper.rb +39 -24
- data/lib/active_record/scoping/default.rb +2 -1
- data/lib/active_record/suppressor.rb +5 -1
- data/lib/active_record/tasks/database_tasks.rb +6 -4
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/type.rb +1 -1
- data/lib/active_record/type/internal/abstract_json.rb +1 -5
- data/lib/active_record/type/time.rb +12 -0
- data/lib/active_record/validations/uniqueness.rb +1 -4
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -11
- data/lib/rails/generators/active_record/model/templates/application_record.rb +2 -0
- metadata +11 -8
@@ -22,7 +22,7 @@ db_namespace = namespace :db do
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
desc 'Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config). Without RAILS_ENV, it defaults to creating the development and test databases.'
|
25
|
+
desc 'Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config). Without RAILS_ENV or when RAILS_ENV is development, it defaults to creating the development and test databases.'
|
26
26
|
task :create => [:load_config] do
|
27
27
|
ActiveRecord::Tasks::DatabaseTasks.create_current
|
28
28
|
end
|
@@ -33,7 +33,7 @@ db_namespace = namespace :db do
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
desc 'Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config). Without RAILS_ENV, it defaults to dropping the development and test databases.'
|
36
|
+
desc 'Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config). Without RAILS_ENV or when RAILS_ENV is development, it defaults to dropping the development and test databases.'
|
37
37
|
task :drop => [:load_config, :check_protected_environments] do
|
38
38
|
db_namespace["drop:_unsafe"].invoke
|
39
39
|
end
|
@@ -256,7 +256,7 @@ db_namespace = namespace :db do
|
|
256
256
|
end
|
257
257
|
|
258
258
|
desc 'Loads a schema.rb file into the database'
|
259
|
-
task :load => [:environment, :load_config] do
|
259
|
+
task :load => [:environment, :load_config, :check_protected_environments] do
|
260
260
|
ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:ruby, ENV['SCHEMA'])
|
261
261
|
end
|
262
262
|
|
@@ -278,7 +278,7 @@ db_namespace = namespace :db do
|
|
278
278
|
desc 'Clears a db/schema_cache.dump file.'
|
279
279
|
task :clear => [:environment, :load_config] do
|
280
280
|
filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.dump")
|
281
|
-
|
281
|
+
rm_f filename, verbose: false
|
282
282
|
end
|
283
283
|
end
|
284
284
|
|
@@ -302,7 +302,7 @@ db_namespace = namespace :db do
|
|
302
302
|
end
|
303
303
|
|
304
304
|
desc "Recreates the databases from the structure.sql file"
|
305
|
-
task :load => [:environment, :load_config] do
|
305
|
+
task :load => [:environment, :load_config, :check_protected_environments] do
|
306
306
|
ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:sql, ENV['SCHEMA'])
|
307
307
|
end
|
308
308
|
|
@@ -138,6 +138,10 @@ module ActiveRecord
|
|
138
138
|
# PolymorphicReflection
|
139
139
|
# RuntimeReflection
|
140
140
|
class AbstractReflection # :nodoc:
|
141
|
+
def through_reflection?
|
142
|
+
false
|
143
|
+
end
|
144
|
+
|
141
145
|
def table_name
|
142
146
|
klass.table_name
|
143
147
|
end
|
@@ -445,6 +449,10 @@ module ActiveRecord
|
|
445
449
|
scope ? [[scope]] : [[]]
|
446
450
|
end
|
447
451
|
|
452
|
+
def has_scope?
|
453
|
+
scope
|
454
|
+
end
|
455
|
+
|
448
456
|
def has_inverse?
|
449
457
|
inverse_name
|
450
458
|
end
|
@@ -700,6 +708,10 @@ module ActiveRecord
|
|
700
708
|
@source_reflection_name = delegate_reflection.options[:source]
|
701
709
|
end
|
702
710
|
|
711
|
+
def through_reflection?
|
712
|
+
true
|
713
|
+
end
|
714
|
+
|
703
715
|
def klass
|
704
716
|
@klass ||= delegate_reflection.compute_class(class_name)
|
705
717
|
end
|
@@ -765,7 +777,6 @@ module ActiveRecord
|
|
765
777
|
# This is for clearing cache on the reflection. Useful for tests that need to compare
|
766
778
|
# SQL queries on associations.
|
767
779
|
def clear_association_scope_cache # :nodoc:
|
768
|
-
@chain = nil
|
769
780
|
delegate_reflection.clear_association_scope_cache
|
770
781
|
source_reflection.clear_association_scope_cache
|
771
782
|
through_reflection.clear_association_scope_cache
|
@@ -812,13 +823,19 @@ module ActiveRecord
|
|
812
823
|
end
|
813
824
|
end
|
814
825
|
|
826
|
+
def has_scope?
|
827
|
+
scope || options[:source_type] ||
|
828
|
+
source_reflection.has_scope? ||
|
829
|
+
through_reflection.has_scope?
|
830
|
+
end
|
831
|
+
|
815
832
|
def join_keys(association_klass)
|
816
833
|
source_reflection.join_keys(association_klass)
|
817
834
|
end
|
818
835
|
|
819
836
|
# A through association is nested if there would be more than one join table
|
820
837
|
def nested?
|
821
|
-
|
838
|
+
source_reflection.through_reflection? || through_reflection.through_reflection?
|
822
839
|
end
|
823
840
|
|
824
841
|
# We want to use the klass from this reflection, rather than just delegate straight to
|
@@ -857,7 +874,7 @@ module ActiveRecord
|
|
857
874
|
example_options = options.dup
|
858
875
|
example_options[:source] = source_reflection_names.first
|
859
876
|
ActiveSupport::Deprecation.warn \
|
860
|
-
"Ambiguous source reflection for through association.
|
877
|
+
"Ambiguous source reflection for through association. Please " \
|
861
878
|
"specify a :source directive on your declaration like:\n" \
|
862
879
|
"\n" \
|
863
880
|
" class #{active_record.name} < ActiveRecord::Base\n" \
|
@@ -995,7 +1012,7 @@ module ActiveRecord
|
|
995
1012
|
end
|
996
1013
|
|
997
1014
|
def constraints
|
998
|
-
[source_type_info]
|
1015
|
+
@reflection.constraints + [source_type_info]
|
999
1016
|
end
|
1000
1017
|
|
1001
1018
|
def source_type_info
|
@@ -45,8 +45,8 @@ module ActiveRecord
|
|
45
45
|
k.name == primary_key
|
46
46
|
}]
|
47
47
|
|
48
|
-
if !primary_key_value &&
|
49
|
-
primary_key_value =
|
48
|
+
if !primary_key_value && klass.prefetch_primary_key?
|
49
|
+
primary_key_value = klass.next_sequence_value
|
50
50
|
values[arel_attribute(klass.primary_key)] = primary_key_value
|
51
51
|
end
|
52
52
|
end
|
@@ -94,12 +94,12 @@ module ActiveRecord
|
|
94
94
|
end
|
95
95
|
|
96
96
|
def substitute_values(values) # :nodoc:
|
97
|
-
binds =
|
98
|
-
|
99
|
-
end
|
97
|
+
binds = []
|
98
|
+
substitutes = []
|
100
99
|
|
101
|
-
|
102
|
-
|
100
|
+
values.each do |arel_attr, value|
|
101
|
+
binds.push QueryAttribute.new(arel_attr.name, value, klass.type_for_attribute(arel_attr.name))
|
102
|
+
substitutes.push [arel_attr, Arel::Nodes::BindParam.new]
|
103
103
|
end
|
104
104
|
|
105
105
|
[substitutes, binds]
|
@@ -428,7 +428,7 @@ module ActiveRecord
|
|
428
428
|
id = id.id
|
429
429
|
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
430
430
|
You are passing an instance of ActiveRecord::Base to `update`.
|
431
|
-
Please pass the id of the object by calling `.id
|
431
|
+
Please pass the id of the object by calling `.id`.
|
432
432
|
MSG
|
433
433
|
end
|
434
434
|
object = find(id)
|
@@ -457,7 +457,7 @@ module ActiveRecord
|
|
457
457
|
if conditions
|
458
458
|
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
459
459
|
Passing conditions to destroy_all is deprecated and will be removed in Rails 5.1.
|
460
|
-
To achieve the same use where(conditions).destroy_all
|
460
|
+
To achieve the same use where(conditions).destroy_all.
|
461
461
|
MESSAGE
|
462
462
|
where(conditions).destroy_all
|
463
463
|
else
|
@@ -527,7 +527,7 @@ module ActiveRecord
|
|
527
527
|
if conditions
|
528
528
|
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
529
529
|
Passing conditions to delete_all is deprecated and will be removed in Rails 5.1.
|
530
|
-
To achieve the same use where(conditions).delete_all
|
530
|
+
To achieve the same use where(conditions).delete_all.
|
531
531
|
MESSAGE
|
532
532
|
where(conditions).delete_all
|
533
533
|
else
|
@@ -2,6 +2,8 @@ require "active_record/relation/batches/batch_enumerator"
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Batches
|
5
|
+
ORDER_OR_LIMIT_IGNORED_MESSAGE = "Scoped order and limit are ignored, it's forced to be batch order and batch size."
|
6
|
+
|
5
7
|
# Looping through a collection of records from the database
|
6
8
|
# (using the Scoping::Named::ClassMethods.all method, for example)
|
7
9
|
# is very inefficient since it will try to instantiate all the objects at once.
|
@@ -31,6 +33,9 @@ module ActiveRecord
|
|
31
33
|
# * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
|
32
34
|
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
|
33
35
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
36
|
+
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
37
|
+
# the order and limit have to be ignored due to batching.
|
38
|
+
#
|
34
39
|
# This is especially useful if you want multiple workers dealing with
|
35
40
|
# the same processing queue. You can make worker 1 handle all the records
|
36
41
|
# between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
|
@@ -48,13 +53,13 @@ module ActiveRecord
|
|
48
53
|
#
|
49
54
|
# NOTE: You can't set the limit either, that's used to control
|
50
55
|
# the batch sizes.
|
51
|
-
def find_each(start: nil, finish: nil, batch_size: 1000)
|
56
|
+
def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
|
52
57
|
if block_given?
|
53
|
-
find_in_batches(start: start, finish: finish, batch_size: batch_size) do |records|
|
58
|
+
find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do |records|
|
54
59
|
records.each { |record| yield record }
|
55
60
|
end
|
56
61
|
else
|
57
|
-
enum_for(:find_each, start: start, finish: finish, batch_size: batch_size) do
|
62
|
+
enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
|
58
63
|
relation = self
|
59
64
|
apply_limits(relation, start, finish).size
|
60
65
|
end
|
@@ -83,6 +88,9 @@ module ActiveRecord
|
|
83
88
|
# * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
|
84
89
|
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
|
85
90
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
91
|
+
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
92
|
+
# the order and limit have to be ignored due to batching.
|
93
|
+
#
|
86
94
|
# This is especially useful if you want multiple workers dealing with
|
87
95
|
# the same processing queue. You can make worker 1 handle all the records
|
88
96
|
# between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
|
@@ -100,16 +108,16 @@ module ActiveRecord
|
|
100
108
|
#
|
101
109
|
# NOTE: You can't set the limit either, that's used to control
|
102
110
|
# the batch sizes.
|
103
|
-
def find_in_batches(start: nil, finish: nil, batch_size: 1000)
|
111
|
+
def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
|
104
112
|
relation = self
|
105
113
|
unless block_given?
|
106
|
-
return to_enum(:find_in_batches, start: start, finish: finish, batch_size: batch_size) do
|
114
|
+
return to_enum(:find_in_batches, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
|
107
115
|
total = apply_limits(relation, start, finish).size
|
108
116
|
(total - 1).div(batch_size) + 1
|
109
117
|
end
|
110
118
|
end
|
111
119
|
|
112
|
-
in_batches(of: batch_size, start: start, finish: finish, load: true) do |batch|
|
120
|
+
in_batches(of: batch_size, start: start, finish: finish, load: true, error_on_ignore: error_on_ignore) do |batch|
|
113
121
|
yield batch.to_a
|
114
122
|
end
|
115
123
|
end
|
@@ -140,6 +148,8 @@ module ActiveRecord
|
|
140
148
|
# * <tt>:load</tt> - Specifies if the relation should be loaded. Default to false.
|
141
149
|
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
|
142
150
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
151
|
+
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
152
|
+
# the order and limit have to be ignored due to batching.
|
143
153
|
#
|
144
154
|
# This is especially useful if you want to work with the
|
145
155
|
# ActiveRecord::Relation object instead of the array of records, or if
|
@@ -171,14 +181,14 @@ module ActiveRecord
|
|
171
181
|
#
|
172
182
|
# NOTE: You can't set the limit either, that's used to control the batch
|
173
183
|
# sizes.
|
174
|
-
def in_batches(of: 1000, start: nil, finish: nil, load: false)
|
184
|
+
def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil)
|
175
185
|
relation = self
|
176
186
|
unless block_given?
|
177
187
|
return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self)
|
178
188
|
end
|
179
189
|
|
180
|
-
if
|
181
|
-
|
190
|
+
if arel.orders.present? || arel.taken.present?
|
191
|
+
act_on_order_or_limit_ignored(error_on_ignore)
|
182
192
|
end
|
183
193
|
|
184
194
|
relation = relation.reorder(batch_order).limit(of)
|
@@ -219,5 +229,15 @@ module ActiveRecord
|
|
219
229
|
def batch_order
|
220
230
|
"#{quoted_table_name}.#{quoted_primary_key} ASC"
|
221
231
|
end
|
232
|
+
|
233
|
+
def act_on_order_or_limit_ignored(error_on_ignore)
|
234
|
+
raise_error = (error_on_ignore.nil? ? self.klass.error_on_ignored_order_or_limit : error_on_ignore)
|
235
|
+
|
236
|
+
if raise_error
|
237
|
+
raise ArgumentError.new(ORDER_OR_LIMIT_IGNORED_MESSAGE)
|
238
|
+
elsif logger
|
239
|
+
logger.warn(ORDER_OR_LIMIT_IGNORED_MESSAGE)
|
240
|
+
end
|
241
|
+
end
|
222
242
|
end
|
223
243
|
end
|
@@ -42,10 +42,10 @@ module ActiveRecord
|
|
42
42
|
# Person.find_by(name: 'Spartacus', rating: 4)
|
43
43
|
# # returns the first item or nil.
|
44
44
|
#
|
45
|
-
# Person.
|
45
|
+
# Person.find_or_initialize_by(name: 'Spartacus', rating: 4)
|
46
46
|
# # returns the first item or returns a new instance (requires you call .save to persist against the database).
|
47
47
|
#
|
48
|
-
# Person.
|
48
|
+
# Person.find_or_create_by(name: 'Spartacus', rating: 4)
|
49
49
|
# # returns the first item or creates it and returns it.
|
50
50
|
#
|
51
51
|
# ==== Alternatives for #find
|
@@ -255,13 +255,13 @@ module ActiveRecord
|
|
255
255
|
# Person.offset(3).third_to_last # returns the third-to-last object from OFFSET 3
|
256
256
|
# Person.where(["user_name = :u", { u: user_name }]).third_to_last
|
257
257
|
def third_to_last
|
258
|
-
|
258
|
+
find_nth_from_last 3
|
259
259
|
end
|
260
260
|
|
261
261
|
# Same as #third_to_last but raises ActiveRecord::RecordNotFound if no record
|
262
262
|
# is found.
|
263
263
|
def third_to_last!
|
264
|
-
|
264
|
+
find_nth_from_last 3 or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
|
265
265
|
end
|
266
266
|
|
267
267
|
# Find the second-to-last record.
|
@@ -271,13 +271,13 @@ module ActiveRecord
|
|
271
271
|
# Person.offset(3).second_to_last # returns the second-to-last object from OFFSET 3
|
272
272
|
# Person.where(["user_name = :u", { u: user_name }]).second_to_last
|
273
273
|
def second_to_last
|
274
|
-
|
274
|
+
find_nth_from_last 2
|
275
275
|
end
|
276
276
|
|
277
277
|
# Same as #second_to_last but raises ActiveRecord::RecordNotFound if no record
|
278
278
|
# is found.
|
279
279
|
def second_to_last!
|
280
|
-
|
280
|
+
find_nth_from_last 2 or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
|
281
281
|
end
|
282
282
|
|
283
283
|
# Returns true if a record exists in the table that matches the +id+ or
|
@@ -312,7 +312,7 @@ module ActiveRecord
|
|
312
312
|
conditions = conditions.id
|
313
313
|
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
314
314
|
You are passing an instance of ActiveRecord::Base to `exists?`.
|
315
|
-
Please pass the id of the object by calling `.id
|
315
|
+
Please pass the id of the object by calling `.id`.
|
316
316
|
MSG
|
317
317
|
end
|
318
318
|
|
@@ -467,7 +467,7 @@ module ActiveRecord
|
|
467
467
|
id = id.id
|
468
468
|
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
469
469
|
You are passing an instance of ActiveRecord::Base to `find`.
|
470
|
-
Please pass the id of the object by calling `.id
|
470
|
+
Please pass the id of the object by calling `.id`.
|
471
471
|
MSG
|
472
472
|
end
|
473
473
|
|
@@ -561,6 +561,25 @@ module ActiveRecord
|
|
561
561
|
relation.limit(limit).to_a
|
562
562
|
end
|
563
563
|
|
564
|
+
def find_nth_from_last(index)
|
565
|
+
if loaded?
|
566
|
+
@records[-index]
|
567
|
+
else
|
568
|
+
relation = if order_values.empty? && primary_key
|
569
|
+
order(arel_attribute(primary_key).asc)
|
570
|
+
else
|
571
|
+
self
|
572
|
+
end
|
573
|
+
|
574
|
+
relation.to_a[-index]
|
575
|
+
# TODO: can be made more performant on large result sets by
|
576
|
+
# for instance, last(index)[-index] (which would require
|
577
|
+
# refactoring the last(n) finder method to make test suite pass),
|
578
|
+
# or by using a combination of reverse_order, limit, and offset,
|
579
|
+
# e.g., reverse_order.offset(index-1).first
|
580
|
+
end
|
581
|
+
end
|
582
|
+
|
564
583
|
private
|
565
584
|
|
566
585
|
def find_nth_with_limit_and_offset(index, limit, offset:) # :nodoc:
|
@@ -136,9 +136,11 @@ module ActiveRecord
|
|
136
136
|
end
|
137
137
|
|
138
138
|
def convert_dot_notation_to_hash(attributes)
|
139
|
-
dot_notation = attributes.
|
139
|
+
dot_notation = attributes.select do |k, v|
|
140
|
+
k.include?(".".freeze) && !v.is_a?(Hash)
|
141
|
+
end
|
140
142
|
|
141
|
-
dot_notation.
|
143
|
+
dot_notation.each_key do |key|
|
142
144
|
table_name, column_name = key.split(".".freeze)
|
143
145
|
value = attributes.delete(key)
|
144
146
|
attributes[table_name] ||= {}
|
@@ -104,10 +104,7 @@ HEADER
|
|
104
104
|
end
|
105
105
|
|
106
106
|
def table(table, stream)
|
107
|
-
columns = @connection.columns(table)
|
108
|
-
column.instance_variable_set(:@table_name, table)
|
109
|
-
column
|
110
|
-
end
|
107
|
+
columns = @connection.columns(table)
|
111
108
|
begin
|
112
109
|
tbl = StringIO.new
|
113
110
|
|
@@ -126,7 +123,7 @@ HEADER
|
|
126
123
|
tbl.print ", primary_key: #{pk.inspect}" unless pk == 'id'
|
127
124
|
pkcol = columns.detect { |c| c.name == pk }
|
128
125
|
pkcolspec = @connection.column_spec_for_primary_key(pkcol)
|
129
|
-
if pkcolspec
|
126
|
+
if pkcolspec.present?
|
130
127
|
pkcolspec.each do |key, value|
|
131
128
|
tbl.print ", #{key}: #{value}"
|
132
129
|
end
|
@@ -141,6 +138,10 @@ HEADER
|
|
141
138
|
table_options = @connection.table_options(table)
|
142
139
|
tbl.print ", options: #{table_options.inspect}" unless table_options.blank?
|
143
140
|
|
141
|
+
if comment = @connection.table_comment(table).presence
|
142
|
+
tbl.print ", comment: #{comment.inspect}"
|
143
|
+
end
|
144
|
+
|
144
145
|
tbl.puts " do |t|"
|
145
146
|
|
146
147
|
# then dump all non-primary key columns
|
@@ -178,11 +179,11 @@ HEADER
|
|
178
179
|
tbl.puts
|
179
180
|
end
|
180
181
|
|
182
|
+
indexes_in_create(table, tbl)
|
183
|
+
|
181
184
|
tbl.puts " end"
|
182
185
|
tbl.puts
|
183
186
|
|
184
|
-
indexes(table, tbl)
|
185
|
-
|
186
187
|
tbl.rewind
|
187
188
|
stream.print tbl.read
|
188
189
|
rescue => e
|
@@ -194,26 +195,12 @@ HEADER
|
|
194
195
|
stream
|
195
196
|
end
|
196
197
|
|
198
|
+
# Keep it for indexing materialized views
|
197
199
|
def indexes(table, stream)
|
198
200
|
if (indexes = @connection.indexes(table)).any?
|
199
201
|
add_index_statements = indexes.map do |index|
|
200
|
-
|
201
|
-
|
202
|
-
index.columns.inspect,
|
203
|
-
"name: #{index.name.inspect}",
|
204
|
-
]
|
205
|
-
statement_parts << 'unique: true' if index.unique
|
206
|
-
|
207
|
-
index_lengths = (index.lengths || []).compact
|
208
|
-
statement_parts << "length: #{Hash[index.columns.zip(index.lengths)].inspect}" if index_lengths.any?
|
209
|
-
|
210
|
-
index_orders = index.orders || {}
|
211
|
-
statement_parts << "order: #{index.orders.inspect}" if index_orders.any?
|
212
|
-
statement_parts << "where: #{index.where.inspect}" if index.where
|
213
|
-
statement_parts << "using: #{index.using.inspect}" if index.using
|
214
|
-
statement_parts << "type: #{index.type.inspect}" if index.type
|
215
|
-
|
216
|
-
" #{statement_parts.join(', ')}"
|
202
|
+
table_name = remove_prefix_and_suffix(index.table).inspect
|
203
|
+
" add_index #{([table_name]+index_parts(index)).join(', ')}"
|
217
204
|
end
|
218
205
|
|
219
206
|
stream.puts add_index_statements.sort.join("\n")
|
@@ -221,6 +208,34 @@ HEADER
|
|
221
208
|
end
|
222
209
|
end
|
223
210
|
|
211
|
+
def indexes_in_create(table, stream)
|
212
|
+
if (indexes = @connection.indexes(table)).any?
|
213
|
+
index_statements = indexes.map do |index|
|
214
|
+
" t.index #{index_parts(index).join(', ')}"
|
215
|
+
end
|
216
|
+
stream.puts index_statements.sort.join("\n")
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def index_parts(index)
|
221
|
+
index_parts = [
|
222
|
+
index.columns.inspect,
|
223
|
+
"name: #{index.name.inspect}",
|
224
|
+
]
|
225
|
+
index_parts << 'unique: true' if index.unique
|
226
|
+
|
227
|
+
index_lengths = (index.lengths || []).compact
|
228
|
+
index_parts << "length: #{Hash[index.columns.zip(index.lengths)].inspect}" if index_lengths.any?
|
229
|
+
|
230
|
+
index_orders = index.orders || {}
|
231
|
+
index_parts << "order: #{index.orders.inspect}" if index_orders.any?
|
232
|
+
index_parts << "where: #{index.where.inspect}" if index.where
|
233
|
+
index_parts << "using: #{index.using.inspect}" if index.using
|
234
|
+
index_parts << "type: #{index.type.inspect}" if index.type
|
235
|
+
index_parts << "comment: #{index.comment.inspect}" if index.comment
|
236
|
+
index_parts
|
237
|
+
end
|
238
|
+
|
224
239
|
def foreign_keys(table, stream)
|
225
240
|
if (foreign_keys = @connection.foreign_keys(table)).any?
|
226
241
|
add_foreign_key_statements = foreign_keys.map do |foreign_key|
|