activerecord 5.1.0 → 5.2.3
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 +5 -5
- data/CHANGELOG.md +596 -450
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -5
- data/examples/performance.rb +2 -0
- data/examples/simple.rb +2 -0
- data/lib/active_record.rb +11 -4
- data/lib/active_record/aggregations.rb +6 -5
- data/lib/active_record/association_relation.rb +7 -5
- data/lib/active_record/associations.rb +77 -85
- data/lib/active_record/associations/alias_tracker.rb +23 -32
- data/lib/active_record/associations/association.rb +49 -35
- data/lib/active_record/associations/association_scope.rb +55 -55
- data/lib/active_record/associations/belongs_to_association.rb +30 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
- data/lib/active_record/associations/builder/association.rb +4 -7
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/collection_association.rb +1 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +2 -0
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +66 -53
- data/lib/active_record/associations/collection_proxy.rb +30 -73
- data/lib/active_record/associations/foreign_association.rb +2 -0
- data/lib/active_record/associations/has_many_association.rb +13 -2
- data/lib/active_record/associations/has_many_through_association.rb +37 -19
- data/lib/active_record/associations/has_one_association.rb +14 -1
- data/lib/active_record/associations/has_one_through_association.rb +13 -8
- data/lib/active_record/associations/join_dependency.rb +52 -96
- data/lib/active_record/associations/join_dependency/join_association.rb +22 -75
- data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
- data/lib/active_record/associations/preloader.rb +17 -37
- data/lib/active_record/associations/preloader/association.rb +53 -92
- data/lib/active_record/associations/preloader/through_association.rb +72 -73
- data/lib/active_record/associations/singular_association.rb +14 -16
- data/lib/active_record/associations/through_association.rb +27 -12
- data/lib/active_record/attribute_assignment.rb +2 -5
- data/lib/active_record/attribute_decorators.rb +3 -2
- data/lib/active_record/attribute_methods.rb +65 -24
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
- data/lib/active_record/attribute_methods/dirty.rb +33 -216
- data/lib/active_record/attribute_methods/primary_key.rb +10 -13
- data/lib/active_record/attribute_methods/query.rb +2 -0
- data/lib/active_record/attribute_methods/read.rb +9 -3
- data/lib/active_record/attribute_methods/serialization.rb +23 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
- data/lib/active_record/attribute_methods/write.rb +22 -19
- data/lib/active_record/attributes.rb +7 -6
- data/lib/active_record/autosave_association.rb +15 -13
- data/lib/active_record/base.rb +2 -0
- data/lib/active_record/callbacks.rb +12 -6
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +2 -0
- data/lib/active_record/collection_cache_key.rb +15 -11
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +120 -39
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +192 -37
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -25
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +65 -7
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +158 -87
- data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
- data/lib/active_record/connection_adapters/abstract_adapter.rb +86 -98
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +126 -189
- data/lib/active_record/connection_adapters/column.rb +4 -2
- data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +13 -2
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -15
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -23
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +258 -129
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -87
- data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +24 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +75 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +90 -96
- data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
- data/lib/active_record/connection_handling.rb +4 -2
- data/lib/active_record/core.rb +41 -61
- data/lib/active_record/counter_cache.rb +20 -15
- data/lib/active_record/define_callbacks.rb +5 -3
- data/lib/active_record/dynamic_matchers.rb +9 -9
- data/lib/active_record/enum.rb +18 -13
- data/lib/active_record/errors.rb +60 -15
- data/lib/active_record/explain.rb +3 -1
- data/lib/active_record/explain_registry.rb +2 -0
- data/lib/active_record/explain_subscriber.rb +2 -0
- data/lib/active_record/fixture_set/file.rb +2 -0
- data/lib/active_record/fixtures.rb +67 -60
- data/lib/active_record/gem_version.rb +4 -2
- data/lib/active_record/inheritance.rb +49 -19
- data/lib/active_record/integration.rb +58 -19
- data/lib/active_record/internal_metadata.rb +2 -0
- data/lib/active_record/legacy_yaml_adapter.rb +3 -1
- data/lib/active_record/locking/optimistic.rb +30 -42
- data/lib/active_record/locking/pessimistic.rb +10 -7
- data/lib/active_record/log_subscriber.rb +46 -4
- data/lib/active_record/migration.rb +189 -139
- data/lib/active_record/migration/command_recorder.rb +11 -9
- data/lib/active_record/migration/compatibility.rb +81 -29
- data/lib/active_record/migration/join_table.rb +2 -0
- data/lib/active_record/model_schema.rb +74 -58
- data/lib/active_record/nested_attributes.rb +18 -6
- data/lib/active_record/no_touching.rb +3 -1
- data/lib/active_record/null_relation.rb +2 -0
- data/lib/active_record/persistence.rb +199 -54
- data/lib/active_record/query_cache.rb +8 -10
- data/lib/active_record/querying.rb +5 -3
- data/lib/active_record/railtie.rb +62 -6
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +2 -0
- data/lib/active_record/railties/databases.rake +48 -38
- data/lib/active_record/readonly_attributes.rb +3 -2
- data/lib/active_record/reflection.rb +137 -207
- data/lib/active_record/relation.rb +132 -207
- data/lib/active_record/relation/batches.rb +32 -17
- data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
- data/lib/active_record/relation/calculations.rb +66 -25
- data/lib/active_record/relation/delegation.rb +45 -29
- data/lib/active_record/relation/finder_methods.rb +76 -85
- data/lib/active_record/relation/from_clause.rb +2 -8
- data/lib/active_record/relation/merger.rb +53 -23
- data/lib/active_record/relation/predicate_builder.rb +60 -79
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
- data/lib/active_record/relation/query_attribute.rb +28 -2
- data/lib/active_record/relation/query_methods.rb +135 -103
- data/lib/active_record/relation/record_fetch_warning.rb +2 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -2
- data/lib/active_record/relation/where_clause.rb +65 -67
- data/lib/active_record/relation/where_clause_factory.rb +5 -48
- data/lib/active_record/result.rb +2 -0
- data/lib/active_record/runtime_registry.rb +2 -0
- data/lib/active_record/sanitization.rb +129 -121
- data/lib/active_record/schema.rb +4 -2
- data/lib/active_record/schema_dumper.rb +36 -26
- data/lib/active_record/schema_migration.rb +2 -0
- data/lib/active_record/scoping.rb +12 -10
- data/lib/active_record/scoping/default.rb +10 -7
- data/lib/active_record/scoping/named.rb +40 -12
- data/lib/active_record/secure_token.rb +2 -0
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +22 -12
- data/lib/active_record/store.rb +3 -1
- data/lib/active_record/suppressor.rb +2 -0
- data/lib/active_record/table_metadata.rb +12 -3
- data/lib/active_record/tasks/database_tasks.rb +38 -26
- data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
- data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
- data/lib/active_record/timestamp.rb +13 -6
- data/lib/active_record/touch_later.rb +2 -0
- data/lib/active_record/transactions.rb +32 -27
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type.rb +4 -1
- data/lib/active_record/type/adapter_specific_registry.rb +2 -0
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +2 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +6 -0
- data/lib/active_record/type/text.rb +2 -0
- data/lib/active_record/type/time.rb +2 -0
- data/lib/active_record/type/type_map.rb +2 -0
- data/lib/active_record/type/unsigned_integer.rb +2 -0
- data/lib/active_record/type_caster.rb +2 -0
- data/lib/active_record/type_caster/connection.rb +2 -0
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/validations.rb +2 -0
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +2 -0
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/presence.rb +2 -0
- data/lib/active_record/validations/uniqueness.rb +36 -6
- data/lib/active_record/version.rb +2 -0
- data/lib/rails/generators/active_record.rb +3 -1
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration.rb +2 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- metadata +24 -36
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- data/lib/active_record/associations/preloader/has_many.rb +0 -15
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -18
- data/lib/active_record/attribute.rb +0 -240
- data/lib/active_record/attribute/user_provided_default.rb +0 -30
- data/lib/active_record/attribute_mutation_tracker.rb +0 -113
- data/lib/active_record/attribute_set.rb +0 -113
- data/lib/active_record/attribute_set/builder.rb +0 -124
- data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
- data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/hash/except"
|
2
4
|
require "active_support/core_ext/hash/slice"
|
3
5
|
require "active_record/relation/merger"
|
@@ -6,7 +8,7 @@ module ActiveRecord
|
|
6
8
|
module SpawnMethods
|
7
9
|
# This is overridden by Associations::CollectionProxy
|
8
10
|
def spawn #:nodoc:
|
9
|
-
clone
|
11
|
+
@delegate_to_klass ? klass.all : clone
|
10
12
|
end
|
11
13
|
|
12
14
|
# Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation.
|
@@ -67,7 +69,7 @@ module ActiveRecord
|
|
67
69
|
private
|
68
70
|
|
69
71
|
def relation_with(values)
|
70
|
-
result = Relation.create(klass,
|
72
|
+
result = Relation.create(klass, values: values)
|
71
73
|
result.extend(*extending_values) if extending_values.any?
|
72
74
|
result
|
73
75
|
end
|
@@ -1,65 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
class Relation
|
3
5
|
class WhereClause # :nodoc:
|
4
|
-
attr_reader :binds
|
5
|
-
|
6
6
|
delegate :any?, :empty?, to: :predicates
|
7
7
|
|
8
|
-
def initialize(predicates
|
8
|
+
def initialize(predicates)
|
9
9
|
@predicates = predicates
|
10
|
-
@binds = binds
|
11
10
|
end
|
12
11
|
|
13
12
|
def +(other)
|
14
13
|
WhereClause.new(
|
15
14
|
predicates + other.predicates,
|
16
|
-
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
def -(other)
|
19
|
+
WhereClause.new(
|
20
|
+
predicates - other.predicates,
|
17
21
|
)
|
18
22
|
end
|
19
23
|
|
20
24
|
def merge(other)
|
21
25
|
WhereClause.new(
|
22
26
|
predicates_unreferenced_by(other) + other.predicates,
|
23
|
-
non_conflicting_binds(other) + other.binds,
|
24
27
|
)
|
25
28
|
end
|
26
29
|
|
27
30
|
def except(*columns)
|
28
|
-
WhereClause.new(
|
31
|
+
WhereClause.new(except_predicates(columns))
|
29
32
|
end
|
30
33
|
|
31
34
|
def or(other)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
35
|
+
left = self - other
|
36
|
+
common = self - left
|
37
|
+
right = other - common
|
38
|
+
|
39
|
+
if left.empty? || right.empty?
|
40
|
+
common
|
36
41
|
else
|
37
|
-
WhereClause.new(
|
38
|
-
[ast.or(
|
39
|
-
binds + other.binds
|
42
|
+
or_clause = WhereClause.new(
|
43
|
+
[left.ast.or(right.ast)],
|
40
44
|
)
|
45
|
+
common + or_clause
|
41
46
|
end
|
42
47
|
end
|
43
48
|
|
44
49
|
def to_h(table_name = nil)
|
45
|
-
equalities = predicates
|
50
|
+
equalities = equalities(predicates)
|
46
51
|
if table_name
|
47
52
|
equalities = equalities.select do |node|
|
48
53
|
node.left.relation.name == table_name
|
49
54
|
end
|
50
55
|
end
|
51
56
|
|
52
|
-
binds = self.binds.map { |attr| [attr.name, attr.value] }.to_h
|
53
|
-
|
54
57
|
equalities.map { |node|
|
55
|
-
name = node.left.name
|
56
|
-
|
57
|
-
|
58
|
-
when Array then node.right.map(&:val)
|
59
|
-
when Arel::Nodes::Casted, Arel::Nodes::Quoted
|
60
|
-
node.right.val
|
61
|
-
end
|
62
|
-
}]
|
58
|
+
name = node.left.name.to_s
|
59
|
+
value = extract_node_value(node.right)
|
60
|
+
[name, value]
|
63
61
|
}.to_h
|
64
62
|
end
|
65
63
|
|
@@ -69,20 +67,17 @@ module ActiveRecord
|
|
69
67
|
|
70
68
|
def ==(other)
|
71
69
|
other.is_a?(WhereClause) &&
|
72
|
-
predicates == other.predicates
|
73
|
-
binds == other.binds
|
70
|
+
predicates == other.predicates
|
74
71
|
end
|
75
72
|
|
76
73
|
def invert
|
77
|
-
WhereClause.new(inverted_predicates
|
74
|
+
WhereClause.new(inverted_predicates)
|
78
75
|
end
|
79
76
|
|
80
77
|
def self.empty
|
81
|
-
@empty ||= new([]
|
78
|
+
@empty ||= new([])
|
82
79
|
end
|
83
80
|
|
84
|
-
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
85
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
86
81
|
protected
|
87
82
|
|
88
83
|
attr_reader :predicates
|
@@ -95,6 +90,20 @@ module ActiveRecord
|
|
95
90
|
end
|
96
91
|
|
97
92
|
private
|
93
|
+
def equalities(predicates)
|
94
|
+
equalities = []
|
95
|
+
|
96
|
+
predicates.each do |node|
|
97
|
+
case node
|
98
|
+
when Arel::Nodes::Equality
|
99
|
+
equalities << node
|
100
|
+
when Arel::Nodes::And
|
101
|
+
equalities.concat equalities(node.children)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
equalities
|
106
|
+
end
|
98
107
|
|
99
108
|
def predicates_unreferenced_by(other)
|
100
109
|
predicates.reject do |n|
|
@@ -106,12 +115,6 @@ module ActiveRecord
|
|
106
115
|
node.respond_to?(:operator) && node.operator == :==
|
107
116
|
end
|
108
117
|
|
109
|
-
def non_conflicting_binds(other)
|
110
|
-
conflicts = referenced_columns & other.referenced_columns
|
111
|
-
conflicts.map! { |node| node.name.to_s }
|
112
|
-
binds.reject { |attr| conflicts.include?(attr.name) }
|
113
|
-
end
|
114
|
-
|
115
118
|
def inverted_predicates
|
116
119
|
predicates.map { |node| invert_predicate(node) }
|
117
120
|
end
|
@@ -131,43 +134,22 @@ module ActiveRecord
|
|
131
134
|
end
|
132
135
|
end
|
133
136
|
|
134
|
-
def
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
case node
|
141
|
-
when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
|
142
|
-
binds_contains = node.grep(Arel::Nodes::BindParam).size
|
143
|
-
subrelation = (node.left.kind_of?(Arel::Attributes::Attribute) ? node.left : node.right)
|
144
|
-
columns.include?(subrelation.name.to_s)
|
145
|
-
end
|
146
|
-
|
147
|
-
if except && binds_contains > 0
|
148
|
-
(binds_index...(binds_index + binds_contains)).each do |i|
|
149
|
-
except_binds[i] = true
|
150
|
-
end
|
137
|
+
def except_predicates(columns)
|
138
|
+
predicates.reject do |node|
|
139
|
+
case node
|
140
|
+
when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
|
141
|
+
subrelation = (node.left.kind_of?(Arel::Attributes::Attribute) ? node.left : node.right)
|
142
|
+
columns.include?(subrelation.name.to_s)
|
151
143
|
end
|
152
|
-
|
153
|
-
binds_index += binds_contains if binds_contains
|
154
|
-
|
155
|
-
except
|
156
144
|
end
|
157
|
-
|
158
|
-
binds = self.binds.reject.with_index do |_, i|
|
159
|
-
except_binds[i]
|
160
|
-
end
|
161
|
-
|
162
|
-
[predicates, binds]
|
163
145
|
end
|
164
146
|
|
165
147
|
def predicates_with_wrapped_sql_literals
|
166
148
|
non_empty_predicates.map do |node|
|
167
|
-
|
168
|
-
|
169
|
-
else
|
149
|
+
case node
|
150
|
+
when Arel::Nodes::SqlLiteral, ::String
|
170
151
|
wrap_sql_literal(node)
|
152
|
+
else node
|
171
153
|
end
|
172
154
|
end
|
173
155
|
end
|
@@ -183,6 +165,22 @@ module ActiveRecord
|
|
183
165
|
end
|
184
166
|
Arel::Nodes::Grouping.new(node)
|
185
167
|
end
|
168
|
+
|
169
|
+
def extract_node_value(node)
|
170
|
+
case node
|
171
|
+
when Array
|
172
|
+
node.map { |v| extract_node_value(v) }
|
173
|
+
when Arel::Nodes::Casted, Arel::Nodes::Quoted
|
174
|
+
node.val
|
175
|
+
when Arel::Nodes::BindParam
|
176
|
+
value = node.value
|
177
|
+
if value.respond_to?(:value_before_type_cast)
|
178
|
+
value.value_before_type_cast
|
179
|
+
else
|
180
|
+
value
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
186
184
|
end
|
187
185
|
end
|
188
186
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
class Relation
|
3
5
|
class WhereClauseFactory # :nodoc:
|
@@ -9,69 +11,24 @@ module ActiveRecord
|
|
9
11
|
def build(opts, other)
|
10
12
|
case opts
|
11
13
|
when String, Array
|
12
|
-
parts = [klass.
|
14
|
+
parts = [klass.sanitize_sql(other.empty? ? opts : ([opts] + other))]
|
13
15
|
when Hash
|
14
16
|
attributes = predicate_builder.resolve_column_aliases(opts)
|
15
|
-
attributes = klass.send(:expand_hash_conditions_for_aggregates, attributes)
|
16
17
|
attributes.stringify_keys!
|
17
18
|
|
18
|
-
|
19
|
-
parts, binds = build_for_case_sensitive(attributes, options)
|
20
|
-
else
|
21
|
-
attributes, binds = predicate_builder.create_binds(attributes)
|
22
|
-
parts = predicate_builder.build_from_hash(attributes)
|
23
|
-
end
|
19
|
+
parts = predicate_builder.build_from_hash(attributes)
|
24
20
|
when Arel::Nodes::Node
|
25
21
|
parts = [opts]
|
26
22
|
else
|
27
23
|
raise ArgumentError, "Unsupported argument type: #{opts} (#{opts.class})"
|
28
24
|
end
|
29
25
|
|
30
|
-
WhereClause.new(parts
|
26
|
+
WhereClause.new(parts)
|
31
27
|
end
|
32
28
|
|
33
|
-
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
34
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
35
29
|
protected
|
36
30
|
|
37
31
|
attr_reader :klass, :predicate_builder
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def perform_case_sensitive?(options)
|
42
|
-
options && options.key?(:case_sensitive)
|
43
|
-
end
|
44
|
-
|
45
|
-
def build_for_case_sensitive(attributes, options)
|
46
|
-
parts, binds = [], []
|
47
|
-
table = klass.arel_table
|
48
|
-
|
49
|
-
attributes.each do |attribute, value|
|
50
|
-
if reflection = klass._reflect_on_association(attribute)
|
51
|
-
attribute = reflection.foreign_key.to_s
|
52
|
-
value = value[reflection.klass.primary_key] unless value.nil?
|
53
|
-
end
|
54
|
-
|
55
|
-
if value.nil?
|
56
|
-
parts << table[attribute].eq(value)
|
57
|
-
else
|
58
|
-
column = klass.column_for_attribute(attribute)
|
59
|
-
|
60
|
-
binds << predicate_builder.send(:build_bind_param, attribute, value)
|
61
|
-
value = Arel::Nodes::BindParam.new
|
62
|
-
|
63
|
-
predicate = if options[:case_sensitive]
|
64
|
-
klass.connection.case_sensitive_comparison(table, attribute, column, value)
|
65
|
-
else
|
66
|
-
klass.connection.case_insensitive_comparison(table, attribute, column, value)
|
67
|
-
end
|
68
|
-
|
69
|
-
parts << predicate
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
[parts, binds]
|
74
|
-
end
|
75
32
|
end
|
76
33
|
end
|
77
34
|
end
|
data/lib/active_record/result.rb
CHANGED
@@ -1,74 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Sanitization
|
3
5
|
extend ActiveSupport::Concern
|
4
6
|
|
5
7
|
module ClassMethods
|
6
|
-
|
8
|
+
# Accepts an array or string of SQL conditions and sanitizes
|
9
|
+
# them into a valid SQL fragment for a WHERE clause.
|
10
|
+
#
|
11
|
+
# sanitize_sql_for_conditions(["name=? and group_id=?", "foo'bar", 4])
|
12
|
+
# # => "name='foo''bar' and group_id=4"
|
13
|
+
#
|
14
|
+
# sanitize_sql_for_conditions(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
|
15
|
+
# # => "name='foo''bar' and group_id='4'"
|
16
|
+
#
|
17
|
+
# sanitize_sql_for_conditions(["name='%s' and group_id='%s'", "foo'bar", 4])
|
18
|
+
# # => "name='foo''bar' and group_id='4'"
|
19
|
+
#
|
20
|
+
# sanitize_sql_for_conditions("name='foo''bar' and group_id='4'")
|
21
|
+
# # => "name='foo''bar' and group_id='4'"
|
22
|
+
def sanitize_sql_for_conditions(condition)
|
23
|
+
return nil if condition.blank?
|
7
24
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# sanitize_sql_for_conditions(["name=? and group_id=?", "foo'bar", 4])
|
12
|
-
# # => "name='foo''bar' and group_id=4"
|
13
|
-
#
|
14
|
-
# sanitize_sql_for_conditions(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
|
15
|
-
# # => "name='foo''bar' and group_id='4'"
|
16
|
-
#
|
17
|
-
# sanitize_sql_for_conditions(["name='%s' and group_id='%s'", "foo'bar", 4])
|
18
|
-
# # => "name='foo''bar' and group_id='4'"
|
19
|
-
#
|
20
|
-
# sanitize_sql_for_conditions("name='foo''bar' and group_id='4'")
|
21
|
-
# # => "name='foo''bar' and group_id='4'"
|
22
|
-
def sanitize_sql_for_conditions(condition) # :doc:
|
23
|
-
return nil if condition.blank?
|
24
|
-
|
25
|
-
case condition
|
26
|
-
when Array; sanitize_sql_array(condition)
|
27
|
-
else condition
|
28
|
-
end
|
25
|
+
case condition
|
26
|
+
when Array; sanitize_sql_array(condition)
|
27
|
+
else condition
|
29
28
|
end
|
30
|
-
|
31
|
-
|
32
|
-
deprecate sanitize_conditions: :sanitize_sql
|
29
|
+
end
|
30
|
+
alias :sanitize_sql :sanitize_sql_for_conditions
|
33
31
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
32
|
+
# Accepts an array, hash, or string of SQL conditions and sanitizes
|
33
|
+
# them into a valid SQL fragment for a SET clause.
|
34
|
+
#
|
35
|
+
# sanitize_sql_for_assignment(["name=? and group_id=?", nil, 4])
|
36
|
+
# # => "name=NULL and group_id=4"
|
37
|
+
#
|
38
|
+
# sanitize_sql_for_assignment(["name=:name and group_id=:group_id", name: nil, group_id: 4])
|
39
|
+
# # => "name=NULL and group_id=4"
|
40
|
+
#
|
41
|
+
# Post.sanitize_sql_for_assignment({ name: nil, group_id: 4 })
|
42
|
+
# # => "`posts`.`name` = NULL, `posts`.`group_id` = 4"
|
43
|
+
#
|
44
|
+
# sanitize_sql_for_assignment("name=NULL and group_id='4'")
|
45
|
+
# # => "name=NULL and group_id='4'"
|
46
|
+
def sanitize_sql_for_assignment(assignments, default_table_name = table_name)
|
47
|
+
case assignments
|
48
|
+
when Array; sanitize_sql_array(assignments)
|
49
|
+
when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
|
50
|
+
else assignments
|
54
51
|
end
|
52
|
+
end
|
55
53
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
54
|
+
# Accepts an array, or string of SQL conditions and sanitizes
|
55
|
+
# them into a valid SQL fragment for an ORDER clause.
|
56
|
+
#
|
57
|
+
# sanitize_sql_for_order(["field(id, ?)", [1,3,2]])
|
58
|
+
# # => "field(id, 1,3,2)"
|
59
|
+
#
|
60
|
+
# sanitize_sql_for_order("id ASC")
|
61
|
+
# # => "id ASC"
|
62
|
+
def sanitize_sql_for_order(condition)
|
63
|
+
if condition.is_a?(Array) && condition.first.to_s.include?("?")
|
64
|
+
enforce_raw_sql_whitelist([condition.first],
|
65
|
+
whitelist: AttributeMethods::ClassMethods::COLUMN_NAME_ORDER_WHITELIST
|
66
|
+
)
|
67
|
+
|
68
|
+
# Ensure we aren't dealing with a subclass of String that might
|
69
|
+
# override methods we use (eg. Arel::Nodes::SqlLiteral).
|
70
|
+
if condition.first.kind_of?(String) && !condition.first.instance_of?(String)
|
71
|
+
condition = [String.new(condition.first), *condition[1..-1]]
|
69
72
|
end
|
73
|
+
|
74
|
+
Arel.sql(sanitize_sql_array(condition))
|
75
|
+
else
|
76
|
+
condition
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
|
81
|
+
#
|
82
|
+
# sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
|
83
|
+
# # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
|
84
|
+
def sanitize_sql_hash_for_assignment(attrs, table)
|
85
|
+
c = connection
|
86
|
+
attrs.map do |attr, value|
|
87
|
+
type = type_for_attribute(attr)
|
88
|
+
value = type.serialize(type.cast(value))
|
89
|
+
"#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}"
|
90
|
+
end.join(", ")
|
91
|
+
end
|
92
|
+
|
93
|
+
# Sanitizes a +string+ so that it is safe to use within an SQL
|
94
|
+
# LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%".
|
95
|
+
#
|
96
|
+
# sanitize_sql_like("100%")
|
97
|
+
# # => "100\\%"
|
98
|
+
#
|
99
|
+
# sanitize_sql_like("snake_cased_string")
|
100
|
+
# # => "snake\\_cased\\_string"
|
101
|
+
#
|
102
|
+
# sanitize_sql_like("100%", "!")
|
103
|
+
# # => "100!%"
|
104
|
+
#
|
105
|
+
# sanitize_sql_like("snake_cased_string", "!")
|
106
|
+
# # => "snake!_cased!_string"
|
107
|
+
def sanitize_sql_like(string, escape_character = "\\")
|
108
|
+
pattern = Regexp.union(escape_character, "%", "_")
|
109
|
+
string.gsub(pattern) { |x| [escape_character, x].join }
|
110
|
+
end
|
111
|
+
|
112
|
+
# Accepts an array of conditions. The array has each value
|
113
|
+
# sanitized and interpolated into the SQL statement.
|
114
|
+
#
|
115
|
+
# sanitize_sql_array(["name=? and group_id=?", "foo'bar", 4])
|
116
|
+
# # => "name='foo''bar' and group_id=4"
|
117
|
+
#
|
118
|
+
# sanitize_sql_array(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
|
119
|
+
# # => "name='foo''bar' and group_id=4"
|
120
|
+
#
|
121
|
+
# sanitize_sql_array(["name='%s' and group_id='%s'", "foo'bar", 4])
|
122
|
+
# # => "name='foo''bar' and group_id='4'"
|
123
|
+
def sanitize_sql_array(ary)
|
124
|
+
statement, *values = ary
|
125
|
+
if values.first.is_a?(Hash) && /:\w+/.match?(statement)
|
126
|
+
replace_named_bind_variables(statement, values.first)
|
127
|
+
elsif statement.include?("?")
|
128
|
+
replace_bind_variables(statement, values)
|
129
|
+
elsif statement.blank?
|
130
|
+
statement
|
131
|
+
else
|
132
|
+
statement % values.collect { |value| connection.quote_string(value.to_s) }
|
70
133
|
end
|
134
|
+
end
|
71
135
|
|
136
|
+
private
|
72
137
|
# Accepts a hash of SQL conditions and replaces those attributes
|
73
138
|
# that correspond to a {#composed_of}[rdoc-ref:Aggregations::ClassMethods#composed_of]
|
74
139
|
# relationship with their expanded aggregate attribute values.
|
@@ -90,10 +155,12 @@ module ActiveRecord
|
|
90
155
|
if aggregation = reflect_on_aggregation(attr.to_sym)
|
91
156
|
mapping = aggregation.mapping
|
92
157
|
mapping.each do |field_attr, aggregate_attr|
|
93
|
-
|
94
|
-
|
158
|
+
expanded_attrs[field_attr] = if value.is_a?(Array)
|
159
|
+
value.map { |it| it.send(aggregate_attr) }
|
160
|
+
elsif mapping.size == 1 && !value.respond_to?(aggregate_attr)
|
161
|
+
value
|
95
162
|
else
|
96
|
-
|
163
|
+
value.send(aggregate_attr)
|
97
164
|
end
|
98
165
|
end
|
99
166
|
else
|
@@ -102,61 +169,7 @@ module ActiveRecord
|
|
102
169
|
end
|
103
170
|
expanded_attrs
|
104
171
|
end
|
105
|
-
|
106
|
-
# Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
|
107
|
-
#
|
108
|
-
# sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
|
109
|
-
# # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
|
110
|
-
def sanitize_sql_hash_for_assignment(attrs, table) # :doc:
|
111
|
-
c = connection
|
112
|
-
attrs.map do |attr, value|
|
113
|
-
value = type_for_attribute(attr.to_s).serialize(value)
|
114
|
-
"#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}"
|
115
|
-
end.join(", ")
|
116
|
-
end
|
117
|
-
|
118
|
-
# Sanitizes a +string+ so that it is safe to use within an SQL
|
119
|
-
# LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%".
|
120
|
-
#
|
121
|
-
# sanitize_sql_like("100%")
|
122
|
-
# # => "100\\%"
|
123
|
-
#
|
124
|
-
# sanitize_sql_like("snake_cased_string")
|
125
|
-
# # => "snake\\_cased\\_string"
|
126
|
-
#
|
127
|
-
# sanitize_sql_like("100%", "!")
|
128
|
-
# # => "100!%"
|
129
|
-
#
|
130
|
-
# sanitize_sql_like("snake_cased_string", "!")
|
131
|
-
# # => "snake!_cased!_string"
|
132
|
-
def sanitize_sql_like(string, escape_character = "\\") # :doc:
|
133
|
-
pattern = Regexp.union(escape_character, "%", "_")
|
134
|
-
string.gsub(pattern) { |x| [escape_character, x].join }
|
135
|
-
end
|
136
|
-
|
137
|
-
# Accepts an array of conditions. The array has each value
|
138
|
-
# sanitized and interpolated into the SQL statement.
|
139
|
-
#
|
140
|
-
# sanitize_sql_array(["name=? and group_id=?", "foo'bar", 4])
|
141
|
-
# # => "name='foo''bar' and group_id=4"
|
142
|
-
#
|
143
|
-
# sanitize_sql_array(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
|
144
|
-
# # => "name='foo''bar' and group_id=4"
|
145
|
-
#
|
146
|
-
# sanitize_sql_array(["name='%s' and group_id='%s'", "foo'bar", 4])
|
147
|
-
# # => "name='foo''bar' and group_id='4'"
|
148
|
-
def sanitize_sql_array(ary) # :doc:
|
149
|
-
statement, *values = ary
|
150
|
-
if values.first.is_a?(Hash) && /:\w+/.match?(statement)
|
151
|
-
replace_named_bind_variables(statement, values.first)
|
152
|
-
elsif statement.include?("?")
|
153
|
-
replace_bind_variables(statement, values)
|
154
|
-
elsif statement.blank?
|
155
|
-
statement
|
156
|
-
else
|
157
|
-
statement % values.collect { |value| connection.quote_string(value.to_s) }
|
158
|
-
end
|
159
|
-
end
|
172
|
+
deprecate :expand_hash_conditions_for_aggregates
|
160
173
|
|
161
174
|
def replace_bind_variables(statement, values)
|
162
175
|
raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size)
|
@@ -205,10 +218,5 @@ module ActiveRecord
|
|
205
218
|
end
|
206
219
|
end
|
207
220
|
end
|
208
|
-
|
209
|
-
def quoted_id # :nodoc:
|
210
|
-
self.class.connection.quote(@attributes[self.class.primary_key].value_for_database)
|
211
|
-
end
|
212
|
-
deprecate :quoted_id
|
213
221
|
end
|
214
222
|
end
|