activerecord 4.1.0 → 4.2.0
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 +776 -1330
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/association_relation.rb +4 -0
- data/lib/active_record/associations/alias_tracker.rb +14 -13
- data/lib/active_record/associations/association.rb +2 -2
- data/lib/active_record/associations/association_scope.rb +83 -43
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +66 -29
- data/lib/active_record/associations/collection_proxy.rb +22 -26
- data/lib/active_record/associations/has_many_association.rb +65 -18
- data/lib/active_record/associations/has_many_through_association.rb +55 -27
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +20 -12
- data/lib/active_record/associations/preloader/association.rb +34 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +49 -59
- data/lib/active_record/associations/singular_association.rb +25 -4
- data/lib/active_record/associations/through_association.rb +23 -14
- data/lib/active_record/associations.rb +171 -42
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +18 -10
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +98 -44
- data/lib/active_record/attribute_methods/primary_key.rb +14 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +37 -147
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
- data/lib/active_record/attribute_methods/write.rb +14 -21
- data/lib/active_record/attribute_methods.rb +67 -94
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +45 -38
- data/lib/active_record/base.rb +10 -20
- data/lib/active_record/callbacks.rb +7 -7
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
- data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
- data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
- data/lib/active_record/connection_adapters/column.rb +28 -239
- data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
- data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -27
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -374
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
- data/lib/active_record/connection_handling.rb +3 -3
- data/lib/active_record/core.rb +143 -32
- data/lib/active_record/counter_cache.rb +60 -7
- data/lib/active_record/enum.rb +10 -11
- data/lib/active_record/errors.rb +49 -27
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +56 -70
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +35 -17
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +52 -49
- data/lib/active_record/model_schema.rb +49 -57
- data/lib/active_record/nested_attributes.rb +7 -7
- data/lib/active_record/null_relation.rb +19 -5
- data/lib/active_record/persistence.rb +50 -31
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +14 -11
- data/lib/active_record/railties/databases.rake +56 -54
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +286 -102
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +39 -31
- data/lib/active_record/relation/delegation.rb +2 -2
- data/lib/active_record/relation/finder_methods.rb +80 -36
- data/lib/active_record/relation/merger.rb +25 -30
- data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +11 -10
- data/lib/active_record/relation/query_methods.rb +141 -55
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +69 -30
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +58 -26
- data/lib/active_record/schema_migration.rb +11 -0
- data/lib/active_record/scoping/default.rb +8 -7
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +19 -10
- data/lib/active_record/tasks/database_tasks.rb +73 -7
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +11 -9
- data/lib/active_record/transactions.rb +37 -21
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +30 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
- data/lib/active_record/type/integer.rb +55 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +56 -0
- data/lib/active_record/type/string.rb +36 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +6 -4
- data/lib/active_record/validations/uniqueness.rb +11 -17
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +3 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +65 -10
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -19,6 +19,22 @@ module ActiveRecord
|
|
19
19
|
#
|
20
20
|
# Person.group(:city).count
|
21
21
|
# # => { 'Rome' => 5, 'Paris' => 3 }
|
22
|
+
#
|
23
|
+
# If +count+ is used with +group+ for multiple columns, it returns a Hash whose
|
24
|
+
# keys are an array containing the individual values of each column and the value
|
25
|
+
# of each key would be the +count+.
|
26
|
+
#
|
27
|
+
# Article.group(:status, :category).count
|
28
|
+
# # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
|
29
|
+
# ["published", "business"]=>0, ["published", "technology"]=>2}
|
30
|
+
#
|
31
|
+
# If +count+ is used with +select+, it will count the selected columns:
|
32
|
+
#
|
33
|
+
# Person.select(:age).count
|
34
|
+
# # => counts the number of different age values
|
35
|
+
#
|
36
|
+
# Note: not all valid +select+ expressions are valid +count+ expressions. The specifics differ
|
37
|
+
# between databases. In invalid cases, an error from the database is thrown.
|
22
38
|
def count(column_name = nil, options = {})
|
23
39
|
# TODO: Remove options argument as soon we remove support to
|
24
40
|
# activerecord-deprecated_finders.
|
@@ -161,19 +177,8 @@ module ActiveRecord
|
|
161
177
|
relation.select_values = column_names.map { |cn|
|
162
178
|
columns_hash.key?(cn) ? arel_table[cn] : cn
|
163
179
|
}
|
164
|
-
result = klass.connection.select_all(relation.arel, nil, bind_values)
|
165
|
-
|
166
|
-
klass.column_types.fetch(key) {
|
167
|
-
result.column_types.fetch(key) { result.identity_type }
|
168
|
-
}
|
169
|
-
end
|
170
|
-
|
171
|
-
result = result.map do |attributes|
|
172
|
-
values = klass.initialize_attributes(attributes).values
|
173
|
-
|
174
|
-
columns.zip(values).map { |column, value| column.type_cast value }
|
175
|
-
end
|
176
|
-
columns.one? ? result.map!(&:first) : result
|
180
|
+
result = klass.connection.select_all(relation.arel, nil, relation.arel.bind_values + bind_values)
|
181
|
+
result.cast_values(klass.column_types)
|
177
182
|
end
|
178
183
|
end
|
179
184
|
|
@@ -188,7 +193,7 @@ module ActiveRecord
|
|
188
193
|
private
|
189
194
|
|
190
195
|
def has_include?(column_name)
|
191
|
-
eager_loading? || (includes_values.present? && (column_name || references_eager_loaded_tables?))
|
196
|
+
eager_loading? || (includes_values.present? && ((column_name && column_name != :all) || references_eager_loaded_tables?))
|
192
197
|
end
|
193
198
|
|
194
199
|
def perform_calculation(operation, column_name, options = {})
|
@@ -235,27 +240,32 @@ module ActiveRecord
|
|
235
240
|
|
236
241
|
column_alias = column_name
|
237
242
|
|
243
|
+
bind_values = nil
|
244
|
+
|
238
245
|
if operation == "count" && (relation.limit_value || relation.offset_value)
|
239
246
|
# Shortcut when limit is zero.
|
240
247
|
return 0 if relation.limit_value == 0
|
241
248
|
|
242
249
|
query_builder = build_count_subquery(relation, column_name, distinct)
|
250
|
+
bind_values = query_builder.bind_values + relation.bind_values
|
243
251
|
else
|
244
252
|
column = aggregate_column(column_name)
|
245
253
|
|
246
254
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
247
255
|
|
248
256
|
column_alias = select_value.alias
|
257
|
+
column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
|
249
258
|
relation.select_values = [select_value]
|
250
259
|
|
251
260
|
query_builder = relation.arel
|
261
|
+
bind_values = query_builder.bind_values + relation.bind_values
|
252
262
|
end
|
253
263
|
|
254
|
-
result = @klass.connection.select_all(query_builder, nil,
|
264
|
+
result = @klass.connection.select_all(query_builder, nil, bind_values)
|
255
265
|
row = result.first
|
256
266
|
value = row && row.values.first
|
257
267
|
column = result.column_types.fetch(column_alias) do
|
258
|
-
|
268
|
+
type_for(column_name)
|
259
269
|
end
|
260
270
|
|
261
271
|
type_cast_calculated_value(value, column, operation)
|
@@ -265,8 +275,8 @@ module ActiveRecord
|
|
265
275
|
group_attrs = group_values
|
266
276
|
|
267
277
|
if group_attrs.first.respond_to?(:to_sym)
|
268
|
-
association = @klass.
|
269
|
-
associated = group_attrs.size == 1 && association && association.
|
278
|
+
association = @klass._reflect_on_association(group_attrs.first)
|
279
|
+
associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
|
270
280
|
group_fields = Array(associated ? association.foreign_key : group_attrs)
|
271
281
|
else
|
272
282
|
group_fields = group_attrs
|
@@ -307,7 +317,7 @@ module ActiveRecord
|
|
307
317
|
relation.group_values = group
|
308
318
|
relation.select_values = select_values
|
309
319
|
|
310
|
-
calculated_data = @klass.connection.select_all(relation, nil, bind_values)
|
320
|
+
calculated_data = @klass.connection.select_all(relation, nil, relation.arel.bind_values + bind_values)
|
311
321
|
|
312
322
|
if association
|
313
323
|
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
@@ -318,14 +328,14 @@ module ActiveRecord
|
|
318
328
|
Hash[calculated_data.map do |row|
|
319
329
|
key = group_columns.map { |aliaz, col_name|
|
320
330
|
column = calculated_data.column_types.fetch(aliaz) do
|
321
|
-
|
331
|
+
type_for(col_name)
|
322
332
|
end
|
323
333
|
type_cast_calculated_value(row[aliaz], column)
|
324
334
|
}
|
325
335
|
key = key.first if key.size == 1
|
326
336
|
key = key_records[key] if associated
|
327
337
|
|
328
|
-
column_type = calculated_data.column_types.fetch(aggregate_alias) {
|
338
|
+
column_type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
|
329
339
|
[key, type_cast_calculated_value(row[aggregate_alias], column_type, operation)]
|
330
340
|
end]
|
331
341
|
end
|
@@ -352,24 +362,20 @@ module ActiveRecord
|
|
352
362
|
@klass.connection.table_alias_for(table_name)
|
353
363
|
end
|
354
364
|
|
355
|
-
def
|
365
|
+
def type_for(field)
|
356
366
|
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
|
357
|
-
@klass.
|
367
|
+
@klass.type_for_attribute(field_name)
|
358
368
|
end
|
359
369
|
|
360
|
-
def type_cast_calculated_value(value,
|
370
|
+
def type_cast_calculated_value(value, type, operation = nil)
|
361
371
|
case operation
|
362
372
|
when 'count' then value.to_i
|
363
|
-
when 'sum' then
|
373
|
+
when 'sum' then type.type_cast_from_database(value || 0)
|
364
374
|
when 'average' then value.respond_to?(:to_d) ? value.to_d : value
|
365
|
-
else
|
375
|
+
else type.type_cast_from_database(value)
|
366
376
|
end
|
367
377
|
end
|
368
378
|
|
369
|
-
def type_cast_using_column(value, column)
|
370
|
-
column ? column.type_cast(value) : value
|
371
|
-
end
|
372
|
-
|
373
379
|
# TODO: refactor to allow non-string `select_values` (eg. Arel nodes).
|
374
380
|
def select_for_count
|
375
381
|
if select_values.present?
|
@@ -385,9 +391,11 @@ module ActiveRecord
|
|
385
391
|
|
386
392
|
aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
|
387
393
|
relation.select_values = [aliased_column]
|
388
|
-
|
394
|
+
arel = relation.arel
|
395
|
+
subquery = arel.as(subquery_alias)
|
389
396
|
|
390
397
|
sm = Arel::SelectManager.new relation.engine
|
398
|
+
sm.bind_values = arel.bind_values
|
391
399
|
select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
|
392
400
|
sm.project(select_value).from(subquery)
|
393
401
|
end
|
@@ -40,10 +40,10 @@ module ActiveRecord
|
|
40
40
|
BLACKLISTED_ARRAY_METHODS = [
|
41
41
|
:compact!, :flatten!, :reject!, :reverse!, :rotate!, :map!,
|
42
42
|
:shuffle!, :slice!, :sort!, :sort_by!, :delete_if,
|
43
|
-
:keep_if, :pop, :shift, :delete_at, :compact
|
43
|
+
:keep_if, :pop, :shift, :delete_at, :compact, :select!
|
44
44
|
].to_set # :nodoc:
|
45
45
|
|
46
|
-
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, to: :to_a
|
46
|
+
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join, to: :to_a
|
47
47
|
|
48
48
|
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
|
49
49
|
:connection, :columns_hash, :to => :klass
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'active_support/deprecation'
|
2
|
+
require 'active_support/core_ext/string/filters'
|
3
|
+
|
1
4
|
module ActiveRecord
|
2
5
|
module FinderMethods
|
3
6
|
ONE_AS_ONE = '1 AS one'
|
@@ -63,7 +66,7 @@ module ActiveRecord
|
|
63
66
|
# # returns an Array of the required fields, available since Rails 3.1.
|
64
67
|
def find(*args)
|
65
68
|
if block_given?
|
66
|
-
to_a.find { |*block_args| yield(*block_args) }
|
69
|
+
to_a.find(*args) { |*block_args| yield(*block_args) }
|
67
70
|
else
|
68
71
|
find_with_ids(*args)
|
69
72
|
end
|
@@ -79,12 +82,16 @@ module ActiveRecord
|
|
79
82
|
# Post.find_by "published_at < ?", 2.weeks.ago
|
80
83
|
def find_by(*args)
|
81
84
|
where(*args).take
|
85
|
+
rescue RangeError
|
86
|
+
nil
|
82
87
|
end
|
83
88
|
|
84
89
|
# Like <tt>find_by</tt>, except that if no record is found, raises
|
85
90
|
# an <tt>ActiveRecord::RecordNotFound</tt> error.
|
86
91
|
def find_by!(*args)
|
87
92
|
where(*args).take!
|
93
|
+
rescue RangeError
|
94
|
+
raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range value"
|
88
95
|
end
|
89
96
|
|
90
97
|
# Gives a record (or N records if a parameter is supplied) without any implied
|
@@ -101,7 +108,7 @@ module ActiveRecord
|
|
101
108
|
# Same as +take+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
102
109
|
# is found. Note that <tt>take!</tt> accepts no arguments.
|
103
110
|
def take!
|
104
|
-
take or raise RecordNotFound
|
111
|
+
take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
|
105
112
|
end
|
106
113
|
|
107
114
|
# Find the first record (or first N records if a parameter is supplied).
|
@@ -127,16 +134,16 @@ module ActiveRecord
|
|
127
134
|
#
|
128
135
|
def first(limit = nil)
|
129
136
|
if limit
|
130
|
-
find_nth_with_limit(
|
137
|
+
find_nth_with_limit(offset_index, limit)
|
131
138
|
else
|
132
|
-
find_nth(
|
139
|
+
find_nth(0, offset_index)
|
133
140
|
end
|
134
141
|
end
|
135
142
|
|
136
143
|
# Same as +first+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
137
144
|
# is found. Note that <tt>first!</tt> accepts no arguments.
|
138
145
|
def first!
|
139
|
-
|
146
|
+
find_nth! 0
|
140
147
|
end
|
141
148
|
|
142
149
|
# Find the last record (or last N records if a parameter is supplied).
|
@@ -169,7 +176,7 @@ module ActiveRecord
|
|
169
176
|
# Same as +last+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
170
177
|
# is found. Note that <tt>last!</tt> accepts no arguments.
|
171
178
|
def last!
|
172
|
-
last or raise RecordNotFound
|
179
|
+
last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
|
173
180
|
end
|
174
181
|
|
175
182
|
# Find the second record.
|
@@ -179,13 +186,13 @@ module ActiveRecord
|
|
179
186
|
# Person.offset(3).second # returns the second object from OFFSET 3 (which is OFFSET 4)
|
180
187
|
# Person.where(["user_name = :u", { u: user_name }]).second
|
181
188
|
def second
|
182
|
-
find_nth(
|
189
|
+
find_nth(1, offset_index)
|
183
190
|
end
|
184
191
|
|
185
192
|
# Same as +second+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
186
193
|
# is found.
|
187
194
|
def second!
|
188
|
-
|
195
|
+
find_nth! 1
|
189
196
|
end
|
190
197
|
|
191
198
|
# Find the third record.
|
@@ -195,13 +202,13 @@ module ActiveRecord
|
|
195
202
|
# Person.offset(3).third # returns the third object from OFFSET 3 (which is OFFSET 5)
|
196
203
|
# Person.where(["user_name = :u", { u: user_name }]).third
|
197
204
|
def third
|
198
|
-
find_nth(
|
205
|
+
find_nth(2, offset_index)
|
199
206
|
end
|
200
207
|
|
201
208
|
# Same as +third+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
202
209
|
# is found.
|
203
210
|
def third!
|
204
|
-
|
211
|
+
find_nth! 2
|
205
212
|
end
|
206
213
|
|
207
214
|
# Find the fourth record.
|
@@ -211,13 +218,13 @@ module ActiveRecord
|
|
211
218
|
# Person.offset(3).fourth # returns the fourth object from OFFSET 3 (which is OFFSET 6)
|
212
219
|
# Person.where(["user_name = :u", { u: user_name }]).fourth
|
213
220
|
def fourth
|
214
|
-
find_nth(
|
221
|
+
find_nth(3, offset_index)
|
215
222
|
end
|
216
223
|
|
217
224
|
# Same as +fourth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
218
225
|
# is found.
|
219
226
|
def fourth!
|
220
|
-
|
227
|
+
find_nth! 3
|
221
228
|
end
|
222
229
|
|
223
230
|
# Find the fifth record.
|
@@ -227,29 +234,29 @@ module ActiveRecord
|
|
227
234
|
# Person.offset(3).fifth # returns the fifth object from OFFSET 3 (which is OFFSET 7)
|
228
235
|
# Person.where(["user_name = :u", { u: user_name }]).fifth
|
229
236
|
def fifth
|
230
|
-
find_nth(
|
237
|
+
find_nth(4, offset_index)
|
231
238
|
end
|
232
239
|
|
233
240
|
# Same as +fifth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
234
241
|
# is found.
|
235
242
|
def fifth!
|
236
|
-
|
243
|
+
find_nth! 4
|
237
244
|
end
|
238
245
|
|
239
246
|
# Find the forty-second record. Also known as accessing "the reddit".
|
240
247
|
# If no order is defined it will order by primary key.
|
241
248
|
#
|
242
249
|
# Person.forty_two # returns the forty-second object fetched by SELECT * FROM people
|
243
|
-
# Person.offset(3).forty_two # returns the
|
250
|
+
# Person.offset(3).forty_two # returns the forty-second object from OFFSET 3 (which is OFFSET 44)
|
244
251
|
# Person.where(["user_name = :u", { u: user_name }]).forty_two
|
245
252
|
def forty_two
|
246
|
-
find_nth(
|
253
|
+
find_nth(41, offset_index)
|
247
254
|
end
|
248
255
|
|
249
256
|
# Same as +forty_two+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
250
257
|
# is found.
|
251
258
|
def forty_two!
|
252
|
-
|
259
|
+
find_nth! 41
|
253
260
|
end
|
254
261
|
|
255
262
|
# Returns +true+ if a record exists in the table that matches the +id+ or
|
@@ -280,7 +287,14 @@ module ActiveRecord
|
|
280
287
|
# Person.exists?(false)
|
281
288
|
# Person.exists?
|
282
289
|
def exists?(conditions = :none)
|
283
|
-
|
290
|
+
if Base === conditions
|
291
|
+
conditions = conditions.id
|
292
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
293
|
+
You are passing an instance of ActiveRecord::Base to `exists?`.
|
294
|
+
Please pass the id of the object by calling `.id`
|
295
|
+
MSG
|
296
|
+
end
|
297
|
+
|
284
298
|
return false if !conditions
|
285
299
|
|
286
300
|
relation = apply_join_dependency(self, construct_join_dependency)
|
@@ -292,10 +306,12 @@ module ActiveRecord
|
|
292
306
|
when Array, Hash
|
293
307
|
relation = relation.where(conditions)
|
294
308
|
else
|
295
|
-
|
309
|
+
unless conditions == :none
|
310
|
+
relation = where(primary_key => conditions)
|
311
|
+
end
|
296
312
|
end
|
297
313
|
|
298
|
-
connection.select_value(relation, "#{name} Exists", relation.bind_values) ? true : false
|
314
|
+
connection.select_value(relation, "#{name} Exists", relation.arel.bind_values + relation.bind_values) ? true : false
|
299
315
|
end
|
300
316
|
|
301
317
|
# This method is called whenever no records are found with either a single
|
@@ -322,8 +338,21 @@ module ActiveRecord
|
|
322
338
|
|
323
339
|
private
|
324
340
|
|
341
|
+
def offset_index
|
342
|
+
offset_value || 0
|
343
|
+
end
|
344
|
+
|
325
345
|
def find_with_associations
|
326
|
-
|
346
|
+
# NOTE: the JoinDependency constructed here needs to know about
|
347
|
+
# any joins already present in `self`, so pass them in
|
348
|
+
#
|
349
|
+
# failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
|
350
|
+
# incorrect SQL is generated. In that case, the join dependency for
|
351
|
+
# SpecialCategorizations is constructed without knowledge of the
|
352
|
+
# preexisting join in joins_values to categorizations (by way of
|
353
|
+
# the `has_many :through` for categories).
|
354
|
+
#
|
355
|
+
join_dependency = construct_join_dependency(joins_values)
|
327
356
|
|
328
357
|
aliases = join_dependency.aliases
|
329
358
|
relation = select aliases.columns
|
@@ -335,7 +364,8 @@ module ActiveRecord
|
|
335
364
|
if ActiveRecord::NullRelation === relation
|
336
365
|
[]
|
337
366
|
else
|
338
|
-
|
367
|
+
arel = relation.arel
|
368
|
+
rows = connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
|
339
369
|
join_dependency.instantiate(rows, aliases)
|
340
370
|
end
|
341
371
|
end
|
@@ -378,8 +408,9 @@ module ActiveRecord
|
|
378
408
|
"#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
|
379
409
|
|
380
410
|
relation = relation.except(:select).select(values).distinct!
|
411
|
+
arel = relation.arel
|
381
412
|
|
382
|
-
id_rows = @klass.connection.select_all(
|
413
|
+
id_rows = @klass.connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
|
383
414
|
id_rows.map {|row| row[primary_key]}
|
384
415
|
end
|
385
416
|
|
@@ -406,15 +437,20 @@ module ActiveRecord
|
|
406
437
|
else
|
407
438
|
find_some(ids)
|
408
439
|
end
|
440
|
+
rescue RangeError
|
441
|
+
raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
|
409
442
|
end
|
410
443
|
|
411
444
|
def find_one(id)
|
412
|
-
|
445
|
+
if ActiveRecord::Base === id
|
446
|
+
id = id.id
|
447
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
448
|
+
You are passing an instance of ActiveRecord::Base to `find`.
|
449
|
+
Please pass the id of the object by calling `.id`
|
450
|
+
MSG
|
451
|
+
end
|
413
452
|
|
414
|
-
|
415
|
-
substitute = connection.substitute_at(column, bind_values.length)
|
416
|
-
relation = where(table[primary_key].eq(substitute))
|
417
|
-
relation.bind_values += [[column, id]]
|
453
|
+
relation = where(primary_key => id)
|
418
454
|
record = relation.take
|
419
455
|
|
420
456
|
raise_record_not_found_exception!(id, 0, 1) unless record
|
@@ -423,7 +459,7 @@ module ActiveRecord
|
|
423
459
|
end
|
424
460
|
|
425
461
|
def find_some(ids)
|
426
|
-
result = where(
|
462
|
+
result = where(primary_key => ids).to_a
|
427
463
|
|
428
464
|
expected_size =
|
429
465
|
if limit_value && ids.size > limit_value
|
@@ -452,20 +488,28 @@ module ActiveRecord
|
|
452
488
|
end
|
453
489
|
end
|
454
490
|
|
455
|
-
def find_nth(
|
491
|
+
def find_nth(index, offset)
|
456
492
|
if loaded?
|
457
|
-
@records
|
493
|
+
@records[index]
|
458
494
|
else
|
495
|
+
offset += index
|
459
496
|
@offsets[offset] ||= find_nth_with_limit(offset, 1).first
|
460
497
|
end
|
461
498
|
end
|
462
499
|
|
500
|
+
def find_nth!(index)
|
501
|
+
find_nth(index, offset_index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
|
502
|
+
end
|
503
|
+
|
463
504
|
def find_nth_with_limit(offset, limit)
|
464
|
-
if order_values.empty? && primary_key
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
505
|
+
relation = if order_values.empty? && primary_key
|
506
|
+
order(arel_table[primary_key].asc)
|
507
|
+
else
|
508
|
+
self
|
509
|
+
end
|
510
|
+
|
511
|
+
relation = relation.offset(offset) unless offset.zero?
|
512
|
+
relation.limit(limit).to_a
|
469
513
|
end
|
470
514
|
|
471
515
|
def find_last
|
@@ -13,7 +13,7 @@ module ActiveRecord
|
|
13
13
|
@hash = hash
|
14
14
|
end
|
15
15
|
|
16
|
-
def merge
|
16
|
+
def merge #:nodoc:
|
17
17
|
Merger.new(relation, other).merge
|
18
18
|
end
|
19
19
|
|
@@ -30,6 +30,8 @@ module ActiveRecord
|
|
30
30
|
else
|
31
31
|
other.joins!(*v)
|
32
32
|
end
|
33
|
+
elsif k == :select
|
34
|
+
other._select!(v)
|
33
35
|
else
|
34
36
|
other.send("#{k}!", v)
|
35
37
|
end
|
@@ -62,7 +64,13 @@ module ActiveRecord
|
|
62
64
|
# expensive), most of the time the value is going to be `nil` or `.blank?`, the only catch is that
|
63
65
|
# `false.blank?` returns `true`, so there needs to be an extra check so that explicit `false` values
|
64
66
|
# don't fall through the cracks.
|
65
|
-
|
67
|
+
unless value.nil? || (value.blank? && false != value)
|
68
|
+
if name == :select
|
69
|
+
relation._select!(*value)
|
70
|
+
else
|
71
|
+
relation.send("#{name}!", *value)
|
72
|
+
end
|
73
|
+
end
|
66
74
|
end
|
67
75
|
|
68
76
|
merge_multi_values
|
@@ -75,12 +83,12 @@ module ActiveRecord
|
|
75
83
|
private
|
76
84
|
|
77
85
|
def merge_joins
|
78
|
-
return if
|
86
|
+
return if other.joins_values.blank?
|
79
87
|
|
80
88
|
if other.klass == relation.klass
|
81
|
-
relation.joins!(*
|
89
|
+
relation.joins!(*other.joins_values)
|
82
90
|
else
|
83
|
-
joins_dependency, rest =
|
91
|
+
joins_dependency, rest = other.joins_values.partition do |join|
|
84
92
|
case join
|
85
93
|
when Hash, Symbol, Array
|
86
94
|
true
|
@@ -100,56 +108,43 @@ module ActiveRecord
|
|
100
108
|
|
101
109
|
def merge_multi_values
|
102
110
|
lhs_wheres = relation.where_values
|
103
|
-
rhs_wheres =
|
111
|
+
rhs_wheres = other.where_values
|
104
112
|
|
105
113
|
lhs_binds = relation.bind_values
|
106
|
-
rhs_binds =
|
114
|
+
rhs_binds = other.bind_values
|
107
115
|
|
108
116
|
removed, kept = partition_overwrites(lhs_wheres, rhs_wheres)
|
109
117
|
|
110
118
|
where_values = kept + rhs_wheres
|
111
119
|
bind_values = filter_binds(lhs_binds, removed) + rhs_binds
|
112
120
|
|
113
|
-
conn = relation.klass.connection
|
114
|
-
bv_index = 0
|
115
|
-
where_values.map! do |node|
|
116
|
-
if Arel::Nodes::Equality === node && Arel::Nodes::BindParam === node.right
|
117
|
-
substitute = conn.substitute_at(bind_values[bv_index].first, bv_index)
|
118
|
-
bv_index += 1
|
119
|
-
Arel::Nodes::Equality.new(node.left, substitute)
|
120
|
-
else
|
121
|
-
node
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
121
|
relation.where_values = where_values
|
126
122
|
relation.bind_values = bind_values
|
127
123
|
|
128
|
-
if
|
124
|
+
if other.reordering_value
|
129
125
|
# override any order specified in the original relation
|
130
|
-
relation.reorder!
|
131
|
-
elsif
|
126
|
+
relation.reorder! other.order_values
|
127
|
+
elsif other.order_values
|
132
128
|
# merge in order_values from relation
|
133
|
-
relation.order!
|
129
|
+
relation.order! other.order_values
|
134
130
|
end
|
135
131
|
|
136
|
-
relation.extend(*
|
132
|
+
relation.extend(*other.extending_values) unless other.extending_values.blank?
|
137
133
|
end
|
138
134
|
|
139
135
|
def merge_single_values
|
140
|
-
relation.from_value =
|
141
|
-
relation.lock_value =
|
142
|
-
relation.reverse_order_value = values[:reverse_order]
|
136
|
+
relation.from_value = other.from_value unless relation.from_value
|
137
|
+
relation.lock_value = other.lock_value unless relation.lock_value
|
143
138
|
|
144
|
-
unless
|
145
|
-
relation.create_with_value = (relation.create_with_value || {}).merge(
|
139
|
+
unless other.create_with_value.blank?
|
140
|
+
relation.create_with_value = (relation.create_with_value || {}).merge(other.create_with_value)
|
146
141
|
end
|
147
142
|
end
|
148
143
|
|
149
144
|
def filter_binds(lhs_binds, removed_wheres)
|
150
145
|
return lhs_binds if removed_wheres.empty?
|
151
146
|
|
152
|
-
set = Set.new removed_wheres.map { |x| x.left.name }
|
147
|
+
set = Set.new removed_wheres.map { |x| x.left.name.to_s }
|
153
148
|
lhs_binds.dup.delete_if { |col,_| set.include? col.name }
|
154
149
|
end
|
155
150
|
|
@@ -1,29 +1,47 @@
|
|
1
|
+
require 'active_support/core_ext/string/filters'
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
class PredicateBuilder
|
3
5
|
class ArrayHandler # :nodoc:
|
4
6
|
def call(attribute, value)
|
5
7
|
values = value.map { |x| x.is_a?(Base) ? x.id : x }
|
6
|
-
|
8
|
+
nils, values = values.partition(&:nil?)
|
9
|
+
|
10
|
+
if values.any? { |val| val.is_a?(Array) }
|
11
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
12
|
+
Passing a nested array to Active Record finder methods is
|
13
|
+
deprecated and will be removed. Flatten your array before using
|
14
|
+
it for 'IN' conditions.
|
15
|
+
MSG
|
7
16
|
|
8
|
-
|
9
|
-
|
17
|
+
values = values.flatten
|
18
|
+
end
|
19
|
+
|
20
|
+
return attribute.in([]) if values.empty? && nils.empty?
|
10
21
|
|
22
|
+
ranges, values = values.partition { |v| v.is_a?(Range) }
|
23
|
+
|
24
|
+
values_predicate =
|
11
25
|
case values.length
|
12
|
-
when 0
|
13
|
-
|
14
|
-
|
15
|
-
attribute.eq(values.first).or(attribute.eq(nil))
|
16
|
-
else
|
17
|
-
attribute.in(values).or(attribute.eq(nil))
|
26
|
+
when 0 then NullPredicate
|
27
|
+
when 1 then attribute.eq(values.first)
|
28
|
+
else attribute.in(values)
|
18
29
|
end
|
19
|
-
|
20
|
-
|
30
|
+
|
31
|
+
unless nils.empty?
|
32
|
+
values_predicate = values_predicate.or(attribute.eq(nil))
|
21
33
|
end
|
22
34
|
|
23
|
-
array_predicates = ranges.map { |range| attribute.
|
24
|
-
array_predicates
|
35
|
+
array_predicates = ranges.map { |range| attribute.between(range) }
|
36
|
+
array_predicates.unshift(values_predicate)
|
25
37
|
array_predicates.inject { |composite, predicate| composite.or(predicate) }
|
26
38
|
end
|
39
|
+
|
40
|
+
module NullPredicate
|
41
|
+
def self.or(other)
|
42
|
+
other
|
43
|
+
end
|
44
|
+
end
|
27
45
|
end
|
28
46
|
end
|
29
47
|
end
|