activerecord 4.1.8 → 4.2.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1165 -1591
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +84 -43
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -14
- 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 +87 -30
- data/lib/active_record/associations/collection_proxy.rb +33 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +26 -12
- data/lib/active_record/associations/preloader/association.rb +14 -10
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +37 -26
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +16 -12
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +20 -12
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -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 +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -28
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_methods.rb +57 -95
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +30 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +85 -53
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +139 -57
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +271 -74
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -60
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +295 -141
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +17 -33
- data/lib/active_record/connection_adapters/mysql_adapter.rb +68 -145
- 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 +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -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 +15 -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 +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -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 +19 -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 +109 -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 -385
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
- 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 +134 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -40
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +10 -12
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +62 -74
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- 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 +79 -47
- data/lib/active_record/model_schema.rb +52 -58
- data/lib/active_record/nested_attributes.rb +18 -8
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +48 -27
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +19 -14
- data/lib/active_record/railties/databases.rake +55 -56
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +281 -117
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +71 -48
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +42 -12
- data/lib/active_record/relation/query_methods.rb +130 -73
- data/lib/active_record/relation/spawn_methods.rb +10 -3
- data/lib/active_record/relation.rb +57 -25
- 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 +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- 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 +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -8
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +54 -28
- 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 +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -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 +23 -0
- data/lib/active_record/type/integer.rb +59 -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 +62 -0
- data/lib/active_record/type/string.rb +40 -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 +110 -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 +5 -3
- data/lib/active_record/validations/uniqueness.rb +24 -20
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +5 -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 +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +66 -11
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -1,16 +1,16 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
+
require 'arel/collectors/bind'
|
2
3
|
|
3
4
|
module ActiveRecord
|
4
5
|
# = Active Record Relation
|
5
6
|
class Relation
|
6
|
-
JoinOperation = Struct.new(:relation, :join_class, :on)
|
7
|
-
|
8
7
|
MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
|
9
8
|
:order, :joins, :where, :having, :bind, :references,
|
10
9
|
:extending, :unscope]
|
11
10
|
|
12
11
|
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering,
|
13
12
|
:reverse_order, :distinct, :create_with, :uniq]
|
13
|
+
INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having]
|
14
14
|
|
15
15
|
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS
|
16
16
|
|
@@ -79,22 +79,26 @@ module ActiveRecord
|
|
79
79
|
scope.unscope!(where: @klass.inheritance_column)
|
80
80
|
end
|
81
81
|
|
82
|
-
|
82
|
+
relation = scope.where(@klass.primary_key => (id_was || id))
|
83
|
+
bvs = binds + relation.bind_values
|
84
|
+
um = relation
|
85
|
+
.arel
|
86
|
+
.compile_update(substitutes, @klass.primary_key)
|
83
87
|
|
84
88
|
@klass.connection.update(
|
85
89
|
um,
|
86
90
|
'SQL',
|
87
|
-
|
91
|
+
bvs,
|
92
|
+
)
|
88
93
|
end
|
89
94
|
|
90
95
|
def substitute_values(values) # :nodoc:
|
91
|
-
|
92
|
-
binds = substitutes.map do |arel_attr, value|
|
96
|
+
binds = values.map do |arel_attr, value|
|
93
97
|
[@klass.columns_hash[arel_attr.name], value]
|
94
98
|
end
|
95
99
|
|
96
|
-
substitutes.each_with_index do |
|
97
|
-
|
100
|
+
substitutes = values.each_with_index.map do |(arel_attr, _), i|
|
101
|
+
[arel_attr, @klass.connection.substitute_at(binds[i][0])]
|
98
102
|
end
|
99
103
|
|
100
104
|
[substitutes, binds]
|
@@ -230,6 +234,7 @@ module ActiveRecord
|
|
230
234
|
# Please see further details in the
|
231
235
|
# {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
|
232
236
|
def explain
|
237
|
+
#TODO: Fix for binds.
|
233
238
|
exec_explain(collecting_queries_for_explain { exec_queries })
|
234
239
|
end
|
235
240
|
|
@@ -239,6 +244,11 @@ module ActiveRecord
|
|
239
244
|
@records
|
240
245
|
end
|
241
246
|
|
247
|
+
# Serializes the relation objects Array.
|
248
|
+
def encode_with(coder)
|
249
|
+
coder.represent_seq(nil, to_a)
|
250
|
+
end
|
251
|
+
|
242
252
|
def as_json(options = nil) #:nodoc:
|
243
253
|
to_a.as_json(options)
|
244
254
|
end
|
@@ -294,10 +304,11 @@ module ActiveRecord
|
|
294
304
|
klass.current_scope = previous
|
295
305
|
end
|
296
306
|
|
297
|
-
# Updates all records with details given
|
298
|
-
#
|
299
|
-
#
|
300
|
-
#
|
307
|
+
# Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
|
308
|
+
# statement and sends it straight to the database. It does not instantiate the involved models and it does not
|
309
|
+
# trigger Active Record callbacks or validations. Values passed to `update_all` will not go through
|
310
|
+
# ActiveRecord's type-casting behavior. It should receive only values that can be passed as-is to the SQL
|
311
|
+
# database.
|
301
312
|
#
|
302
313
|
# ==== Parameters
|
303
314
|
#
|
@@ -330,7 +341,8 @@ module ActiveRecord
|
|
330
341
|
stmt.wheres = arel.constraints
|
331
342
|
end
|
332
343
|
|
333
|
-
|
344
|
+
bvs = arel.bind_values + bind_values
|
345
|
+
@klass.connection.update stmt, 'SQL', bvs
|
334
346
|
end
|
335
347
|
|
336
348
|
# Updates an object (or multiple objects) and saves it to the database, if validations pass.
|
@@ -434,12 +446,21 @@ module ActiveRecord
|
|
434
446
|
# If you need to destroy dependent associations or call your <tt>before_*</tt> or
|
435
447
|
# +after_destroy+ callbacks, use the +destroy_all+ method instead.
|
436
448
|
#
|
437
|
-
# If
|
449
|
+
# If an invalid method is supplied, +delete_all+ raises an ActiveRecord error:
|
438
450
|
#
|
439
451
|
# Post.limit(100).delete_all
|
440
|
-
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
|
452
|
+
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
|
441
453
|
def delete_all(conditions = nil)
|
442
|
-
|
454
|
+
invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method|
|
455
|
+
if MULTI_VALUE_METHODS.include?(method)
|
456
|
+
send("#{method}_values").any?
|
457
|
+
else
|
458
|
+
send("#{method}_value")
|
459
|
+
end
|
460
|
+
}
|
461
|
+
if invalid_methods.any?
|
462
|
+
raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
|
463
|
+
end
|
443
464
|
|
444
465
|
if conditions
|
445
466
|
where(conditions).delete_all
|
@@ -453,7 +474,8 @@ module ActiveRecord
|
|
453
474
|
stmt.wheres = arel.constraints
|
454
475
|
end
|
455
476
|
|
456
|
-
|
477
|
+
bvs = arel.bind_values + bind_values
|
478
|
+
affected = @klass.connection.delete(stmt, 'SQL', bvs)
|
457
479
|
|
458
480
|
reset
|
459
481
|
affected
|
@@ -523,11 +545,11 @@ module ActiveRecord
|
|
523
545
|
find_with_associations { |rel| relation = rel }
|
524
546
|
end
|
525
547
|
|
526
|
-
|
527
|
-
binds = relation.bind_values.dup
|
528
|
-
|
529
|
-
|
530
|
-
|
548
|
+
arel = relation.arel
|
549
|
+
binds = (arel.bind_values + relation.bind_values).dup
|
550
|
+
binds.map! { |bv| connection.quote(*bv.reverse) }
|
551
|
+
collect = visitor.accept(arel.ast, Arel::Collectors::Bind.new)
|
552
|
+
collect.substitute_binds(binds).join
|
531
553
|
end
|
532
554
|
end
|
533
555
|
|
@@ -544,7 +566,13 @@ module ActiveRecord
|
|
544
566
|
|
545
567
|
Hash[equalities.map { |where|
|
546
568
|
name = where.left.name
|
547
|
-
[name, binds.fetch(name.to_s) {
|
569
|
+
[name, binds.fetch(name.to_s) {
|
570
|
+
case where.right
|
571
|
+
when Array then where.right.map(&:val)
|
572
|
+
when Arel::Nodes::Casted
|
573
|
+
where.right.val
|
574
|
+
end
|
575
|
+
}]
|
548
576
|
}]
|
549
577
|
end
|
550
578
|
|
@@ -608,11 +636,11 @@ module ActiveRecord
|
|
608
636
|
private
|
609
637
|
|
610
638
|
def exec_queries
|
611
|
-
@records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bind_values)
|
639
|
+
@records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, arel.bind_values + bind_values)
|
612
640
|
|
613
641
|
preload = preload_values
|
614
642
|
preload += includes_values unless eager_loading?
|
615
|
-
preloader =
|
643
|
+
preloader = build_preloader
|
616
644
|
preload.each do |associations|
|
617
645
|
preloader.preload @records, associations
|
618
646
|
end
|
@@ -623,6 +651,10 @@ module ActiveRecord
|
|
623
651
|
@records
|
624
652
|
end
|
625
653
|
|
654
|
+
def build_preloader
|
655
|
+
ActiveRecord::Associations::Preloader.new
|
656
|
+
end
|
657
|
+
|
626
658
|
def references_eager_loaded_tables?
|
627
659
|
joined_tables = arel.join_sources.map do |join|
|
628
660
|
if join.is_a?(Arel::Nodes::StringJoin)
|
data/lib/active_record/result.rb
CHANGED
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
class Result
|
32
32
|
include Enumerable
|
33
33
|
|
34
|
-
IDENTITY_TYPE =
|
34
|
+
IDENTITY_TYPE = Type::Value.new # :nodoc:
|
35
35
|
|
36
36
|
attr_reader :columns, :rows, :column_types
|
37
37
|
|
@@ -42,12 +42,8 @@ module ActiveRecord
|
|
42
42
|
@column_types = column_types
|
43
43
|
end
|
44
44
|
|
45
|
-
def
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
def column_type(name)
|
50
|
-
@column_types[name] || identity_type
|
45
|
+
def length
|
46
|
+
@rows.length
|
51
47
|
end
|
52
48
|
|
53
49
|
def each
|
@@ -82,6 +78,15 @@ module ActiveRecord
|
|
82
78
|
hash_rows.last
|
83
79
|
end
|
84
80
|
|
81
|
+
def cast_values(type_overrides = {}) # :nodoc:
|
82
|
+
types = columns.map { |name| column_type(name, type_overrides) }
|
83
|
+
result = rows.map do |values|
|
84
|
+
types.zip(values).map { |type, value| type.type_cast_from_database(value) }
|
85
|
+
end
|
86
|
+
|
87
|
+
columns.one? ? result.map!(&:first) : result
|
88
|
+
end
|
89
|
+
|
85
90
|
def initialize_copy(other)
|
86
91
|
@columns = columns.dup
|
87
92
|
@rows = rows.dup
|
@@ -91,6 +96,12 @@ module ActiveRecord
|
|
91
96
|
|
92
97
|
private
|
93
98
|
|
99
|
+
def column_type(name, type_overrides = {})
|
100
|
+
type_overrides.fetch(name) do
|
101
|
+
column_types.fetch(name, IDENTITY_TYPE)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
94
105
|
def hash_rows
|
95
106
|
@hash_rows ||=
|
96
107
|
begin
|
@@ -87,12 +87,15 @@ module ActiveRecord
|
|
87
87
|
# { address: Address.new("123 abc st.", "chicago") }
|
88
88
|
# # => "address_street='123 abc st.' and address_city='chicago'"
|
89
89
|
def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
|
90
|
+
ActiveSupport::Deprecation.warn(<<-EOWARN)
|
91
|
+
sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
|
92
|
+
EOWARN
|
90
93
|
attrs = PredicateBuilder.resolve_column_aliases self, attrs
|
91
94
|
attrs = expand_hash_conditions_for_aggregates(attrs)
|
92
95
|
|
93
96
|
table = Arel::Table.new(table_name, arel_engine).alias(default_table_name)
|
94
97
|
PredicateBuilder.build_from_hash(self, attrs, table).map { |b|
|
95
|
-
connection.visitor.
|
98
|
+
connection.visitor.compile b
|
96
99
|
}.join(' AND ')
|
97
100
|
end
|
98
101
|
alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
|
@@ -107,6 +110,13 @@ module ActiveRecord
|
|
107
110
|
end.join(', ')
|
108
111
|
end
|
109
112
|
|
113
|
+
# Sanitizes a +string+ so that it is safe to use within an SQL
|
114
|
+
# LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%"
|
115
|
+
def sanitize_sql_like(string, escape_character = "\\")
|
116
|
+
pattern = Regexp.union(escape_character, "%", "_")
|
117
|
+
string.gsub(pattern) { |x| [escape_character, x].join }
|
118
|
+
end
|
119
|
+
|
110
120
|
# Accepts an array of conditions. The array has each value
|
111
121
|
# sanitized and interpolated into the SQL statement.
|
112
122
|
# ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
|
@@ -127,7 +137,7 @@ module ActiveRecord
|
|
127
137
|
raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
|
128
138
|
bound = values.dup
|
129
139
|
c = connection
|
130
|
-
statement.gsub(
|
140
|
+
statement.gsub(/\?/) do
|
131
141
|
replace_bind_variable(bound.shift, c)
|
132
142
|
end
|
133
143
|
end
|
data/lib/active_record/schema.rb
CHANGED
@@ -91,16 +91,17 @@ HEADER
|
|
91
91
|
end
|
92
92
|
|
93
93
|
def tables(stream)
|
94
|
-
@connection.tables.sort
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
94
|
+
sorted_tables = @connection.tables.sort
|
95
|
+
|
96
|
+
sorted_tables.each do |table_name|
|
97
|
+
table(table_name, stream) unless ignored?(table_name)
|
98
|
+
end
|
99
|
+
|
100
|
+
# dump foreign keys at the end to make sure all dependent tables exist.
|
101
|
+
if @connection.supports_foreign_keys?
|
102
|
+
sorted_tables.each do |tbl|
|
103
|
+
foreign_keys(tbl, stream) unless ignored?(tbl)
|
102
104
|
end
|
103
|
-
table(tbl, stream)
|
104
105
|
end
|
105
106
|
end
|
106
107
|
|
@@ -110,26 +111,23 @@ HEADER
|
|
110
111
|
tbl = StringIO.new
|
111
112
|
|
112
113
|
# first dump primary key column
|
113
|
-
|
114
|
-
pk, _ = @connection.pk_and_sequence_for(table)
|
115
|
-
end
|
116
|
-
if !pk && @connection.respond_to?(:primary_key)
|
117
|
-
pk = @connection.primary_key(table)
|
118
|
-
end
|
114
|
+
pk = @connection.primary_key(table)
|
119
115
|
|
120
116
|
tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
|
121
117
|
pkcol = columns.detect { |c| c.name == pk }
|
122
118
|
if pkcol
|
123
119
|
if pk != 'id'
|
124
120
|
tbl.print %Q(, primary_key: "#{pk}")
|
121
|
+
elsif pkcol.sql_type == 'bigint'
|
122
|
+
tbl.print ", id: :bigserial"
|
125
123
|
elsif pkcol.sql_type == 'uuid'
|
126
124
|
tbl.print ", id: :uuid"
|
127
|
-
tbl.print %Q(, default:
|
125
|
+
tbl.print %Q(, default: #{pkcol.default_function.inspect})
|
128
126
|
end
|
129
127
|
else
|
130
128
|
tbl.print ", id: false"
|
131
129
|
end
|
132
|
-
tbl.print ", force:
|
130
|
+
tbl.print ", force: :cascade"
|
133
131
|
tbl.puts " do |t|"
|
134
132
|
|
135
133
|
# then dump all non-primary key columns
|
@@ -187,34 +185,67 @@ HEADER
|
|
187
185
|
if (indexes = @connection.indexes(table)).any?
|
188
186
|
add_index_statements = indexes.map do |index|
|
189
187
|
statement_parts = [
|
190
|
-
|
188
|
+
"add_index #{remove_prefix_and_suffix(index.table).inspect}",
|
191
189
|
index.columns.inspect,
|
192
|
-
|
190
|
+
"name: #{index.name.inspect}",
|
193
191
|
]
|
194
192
|
statement_parts << 'unique: true' if index.unique
|
195
193
|
|
196
194
|
index_lengths = (index.lengths || []).compact
|
197
|
-
statement_parts <<
|
195
|
+
statement_parts << "length: #{Hash[index.columns.zip(index.lengths)].inspect}" if index_lengths.any?
|
196
|
+
|
197
|
+
index_orders = index.orders || {}
|
198
|
+
statement_parts << "order: #{index.orders.inspect}" if index_orders.any?
|
199
|
+
statement_parts << "where: #{index.where.inspect}" if index.where
|
200
|
+
statement_parts << "using: #{index.using.inspect}" if index.using
|
201
|
+
statement_parts << "type: #{index.type.inspect}" if index.type
|
202
|
+
|
203
|
+
" #{statement_parts.join(', ')}"
|
204
|
+
end
|
205
|
+
|
206
|
+
stream.puts add_index_statements.sort.join("\n")
|
207
|
+
stream.puts
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def foreign_keys(table, stream)
|
212
|
+
if (foreign_keys = @connection.foreign_keys(table)).any?
|
213
|
+
add_foreign_key_statements = foreign_keys.map do |foreign_key|
|
214
|
+
parts = [
|
215
|
+
"add_foreign_key #{remove_prefix_and_suffix(foreign_key.from_table).inspect}",
|
216
|
+
remove_prefix_and_suffix(foreign_key.to_table).inspect,
|
217
|
+
]
|
198
218
|
|
199
|
-
|
200
|
-
|
219
|
+
if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table)
|
220
|
+
parts << "column: #{foreign_key.column.inspect}"
|
221
|
+
end
|
201
222
|
|
202
|
-
|
223
|
+
if foreign_key.custom_primary_key?
|
224
|
+
parts << "primary_key: #{foreign_key.primary_key.inspect}"
|
225
|
+
end
|
203
226
|
|
204
|
-
|
227
|
+
if foreign_key.name !~ /^fk_rails_[0-9a-f]{10}$/
|
228
|
+
parts << "name: #{foreign_key.name.inspect}"
|
229
|
+
end
|
205
230
|
|
206
|
-
|
231
|
+
parts << "on_update: #{foreign_key.on_update.inspect}" if foreign_key.on_update
|
232
|
+
parts << "on_delete: #{foreign_key.on_delete.inspect}" if foreign_key.on_delete
|
207
233
|
|
208
|
-
|
234
|
+
" #{parts.join(', ')}"
|
209
235
|
end
|
210
236
|
|
211
|
-
stream.puts
|
212
|
-
stream.puts
|
237
|
+
stream.puts add_foreign_key_statements.sort.join("\n")
|
213
238
|
end
|
214
239
|
end
|
215
240
|
|
216
241
|
def remove_prefix_and_suffix(table)
|
217
242
|
table.gsub(/^(#{@options[:table_name_prefix]})(.+)(#{@options[:table_name_suffix]})$/, "\\2")
|
218
243
|
end
|
244
|
+
|
245
|
+
def ignored?(table_name)
|
246
|
+
['schema_migrations', ignore_tables].flatten.any? do |ignored|
|
247
|
+
ignored === remove_prefix_and_suffix(table_name)
|
248
|
+
end
|
249
|
+
end
|
219
250
|
end
|
220
251
|
end
|
@@ -34,15 +34,16 @@ module ActiveRecord
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def drop_table
|
37
|
-
if table_exists?
|
38
|
-
connection.remove_index table_name, name: index_name
|
39
|
-
connection.drop_table(table_name)
|
40
|
-
end
|
37
|
+
connection.drop_table table_name if table_exists?
|
41
38
|
end
|
42
39
|
|
43
40
|
def normalize_migration_number(number)
|
44
41
|
"%.3d" % number.to_i
|
45
42
|
end
|
43
|
+
|
44
|
+
def normalized_versions
|
45
|
+
pluck(:version).map { |v| normalize_migration_number v }
|
46
|
+
end
|
46
47
|
end
|
47
48
|
|
48
49
|
def version
|
@@ -11,7 +11,7 @@ module ActiveRecord
|
|
11
11
|
end
|
12
12
|
|
13
13
|
module ClassMethods
|
14
|
-
# Returns a scope for the model without the
|
14
|
+
# Returns a scope for the model without the previously set scopes.
|
15
15
|
#
|
16
16
|
# class Post < ActiveRecord::Base
|
17
17
|
# def self.default_scope
|
@@ -19,11 +19,12 @@ module ActiveRecord
|
|
19
19
|
# end
|
20
20
|
# end
|
21
21
|
#
|
22
|
-
# Post.all
|
23
|
-
# Post.unscoped.all
|
22
|
+
# Post.all # Fires "SELECT * FROM posts WHERE published = true"
|
23
|
+
# Post.unscoped.all # Fires "SELECT * FROM posts"
|
24
|
+
# Post.where(published: false).unscoped.all # Fires "SELECT * FROM posts"
|
24
25
|
#
|
25
26
|
# This method also accepts a block. All queries inside the block will
|
26
|
-
# not use the
|
27
|
+
# not use the previously set scopes.
|
27
28
|
#
|
28
29
|
# Post.unscoped {
|
29
30
|
# Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
|
@@ -94,6 +95,7 @@ module ActiveRecord
|
|
94
95
|
end
|
95
96
|
|
96
97
|
def build_default_scope(base_rel = relation) # :nodoc:
|
98
|
+
return if abstract_class?
|
97
99
|
if !Base.is_a?(method(:default_scope).owner)
|
98
100
|
# The user has defined their own default scope method, so call that
|
99
101
|
evaluate_default_scope { default_scope }
|
@@ -139,6 +139,10 @@ module ActiveRecord
|
|
139
139
|
# Article.published.featured.latest_article
|
140
140
|
# Article.featured.titles
|
141
141
|
def scope(name, body, &block)
|
142
|
+
unless body.respond_to?(:call)
|
143
|
+
raise ArgumentError, 'The scope body needs to be callable.'
|
144
|
+
end
|
145
|
+
|
142
146
|
if dangerous_class_method?(name)
|
143
147
|
raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
|
144
148
|
"on the model \"#{self.name}\", but Active Record already defined " \
|
@@ -180,13 +180,9 @@ module ActiveRecord #:nodoc:
|
|
180
180
|
class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
|
181
181
|
def compute_type
|
182
182
|
klass = @serializable.class
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
klass.columns_hash[name].type
|
187
|
-
else
|
188
|
-
NilClass
|
189
|
-
end
|
183
|
+
column = klass.columns_hash[name] || Type::Value.new
|
184
|
+
|
185
|
+
type = ActiveSupport::XmlMini::TYPE_NAMES[value.class.name] || column.type
|
190
186
|
|
191
187
|
{ :text => :string,
|
192
188
|
:time => :datetime }[type] || type
|
@@ -1,26 +1,111 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
|
3
3
|
# Statement cache is used to cache a single statement in order to avoid creating the AST again.
|
4
|
-
# Initializing the cache is done by passing the statement in the
|
4
|
+
# Initializing the cache is done by passing the statement in the create block:
|
5
5
|
#
|
6
|
-
# cache =
|
7
|
-
# Book.where(name: "my book").
|
6
|
+
# cache = StatementCache.create(Book.connection) do |params|
|
7
|
+
# Book.where(name: "my book").where("author_id > 3")
|
8
8
|
# end
|
9
9
|
#
|
10
10
|
# The cached statement is executed by using the +execute+ method:
|
11
11
|
#
|
12
|
-
# cache.execute
|
12
|
+
# cache.execute([], Book, Book.connection)
|
13
13
|
#
|
14
14
|
# The relation returned by the block is cached, and for each +execute+ call the cached relation gets duped.
|
15
15
|
# Database is queried when +to_a+ is called on the relation.
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
#
|
17
|
+
# If you want to cache the statement without the values you can use the +bind+ method of the
|
18
|
+
# block parameter.
|
19
|
+
#
|
20
|
+
# cache = StatementCache.create(Book.connection) do |params|
|
21
|
+
# Book.where(name: params.bind)
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# And pass the bind values as the first argument of +execute+ call.
|
25
|
+
#
|
26
|
+
# cache.execute(["my book"], Book, Book.connection)
|
27
|
+
class StatementCache # :nodoc:
|
28
|
+
class Substitute; end # :nodoc:
|
29
|
+
|
30
|
+
class Query # :nodoc:
|
31
|
+
def initialize(sql)
|
32
|
+
@sql = sql
|
33
|
+
end
|
34
|
+
|
35
|
+
def sql_for(binds, connection)
|
36
|
+
@sql
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class PartialQuery < Query # :nodoc:
|
41
|
+
def initialize values
|
42
|
+
@values = values
|
43
|
+
@indexes = values.each_with_index.find_all { |thing,i|
|
44
|
+
Arel::Nodes::BindParam === thing
|
45
|
+
}.map(&:last)
|
46
|
+
end
|
47
|
+
|
48
|
+
def sql_for(binds, connection)
|
49
|
+
val = @values.dup
|
50
|
+
binds = binds.dup
|
51
|
+
@indexes.each { |i| val[i] = connection.quote(*binds.shift.reverse) }
|
52
|
+
val.join
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.query(visitor, ast)
|
57
|
+
Query.new visitor.accept(ast, Arel::Collectors::SQLString.new).value
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.partial_query(visitor, ast, collector)
|
61
|
+
collected = visitor.accept(ast, collector).value
|
62
|
+
PartialQuery.new collected
|
20
63
|
end
|
21
64
|
|
22
|
-
|
23
|
-
|
65
|
+
class Params # :nodoc:
|
66
|
+
def bind; Substitute.new; end
|
67
|
+
end
|
68
|
+
|
69
|
+
class BindMap # :nodoc:
|
70
|
+
def initialize(bind_values)
|
71
|
+
@indexes = []
|
72
|
+
@bind_values = bind_values
|
73
|
+
|
74
|
+
bind_values.each_with_index do |(_, value), i|
|
75
|
+
if Substitute === value
|
76
|
+
@indexes << i
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def bind(values)
|
82
|
+
bvs = @bind_values.map { |pair| pair.dup }
|
83
|
+
@indexes.each_with_index { |offset,i| bvs[offset][1] = values[i] }
|
84
|
+
bvs
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
attr_reader :bind_map, :query_builder
|
89
|
+
|
90
|
+
def self.create(connection, block = Proc.new)
|
91
|
+
relation = block.call Params.new
|
92
|
+
bind_map = BindMap.new relation.bind_values
|
93
|
+
query_builder = connection.cacheable_query relation.arel
|
94
|
+
new query_builder, bind_map
|
95
|
+
end
|
96
|
+
|
97
|
+
def initialize(query_builder, bind_map)
|
98
|
+
@query_builder = query_builder
|
99
|
+
@bind_map = bind_map
|
100
|
+
end
|
101
|
+
|
102
|
+
def execute(params, klass, connection)
|
103
|
+
bind_values = bind_map.bind params
|
104
|
+
|
105
|
+
sql = query_builder.sql_for bind_values, connection
|
106
|
+
|
107
|
+
klass.find_by_sql sql, bind_values
|
24
108
|
end
|
109
|
+
alias :call :execute
|
25
110
|
end
|
26
111
|
end
|
data/lib/active_record/store.rb
CHANGED
@@ -99,7 +99,7 @@ module ActiveRecord
|
|
99
99
|
self.local_stored_attributes[store_attribute] |= keys
|
100
100
|
end
|
101
101
|
|
102
|
-
def _store_accessors_module
|
102
|
+
def _store_accessors_module # :nodoc:
|
103
103
|
@_store_accessors_module ||= begin
|
104
104
|
mod = Module.new
|
105
105
|
include mod
|
@@ -129,10 +129,10 @@ module ActiveRecord
|
|
129
129
|
|
130
130
|
private
|
131
131
|
def store_accessor_for(store_attribute)
|
132
|
-
|
132
|
+
type_for_attribute(store_attribute.to_s).accessor
|
133
133
|
end
|
134
134
|
|
135
|
-
class HashAccessor
|
135
|
+
class HashAccessor # :nodoc:
|
136
136
|
def self.read(object, attribute, key)
|
137
137
|
prepare(object, attribute)
|
138
138
|
object.public_send(attribute)[key]
|
@@ -151,7 +151,7 @@ module ActiveRecord
|
|
151
151
|
end
|
152
152
|
end
|
153
153
|
|
154
|
-
class StringKeyedHashAccessor < HashAccessor
|
154
|
+
class StringKeyedHashAccessor < HashAccessor # :nodoc:
|
155
155
|
def self.read(object, attribute, key)
|
156
156
|
super object, attribute, key.to_s
|
157
157
|
end
|
@@ -161,7 +161,7 @@ module ActiveRecord
|
|
161
161
|
end
|
162
162
|
end
|
163
163
|
|
164
|
-
class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor
|
164
|
+
class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor # :nodoc:
|
165
165
|
def self.prepare(object, store_attribute)
|
166
166
|
attribute = object.send(store_attribute)
|
167
167
|
unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess)
|