activerecord 5.1.7 → 5.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 +372 -765
- 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/aggregations.rb +6 -5
- data/lib/active_record/association_relation.rb +4 -2
- data/lib/active_record/associations/alias_tracker.rb +19 -27
- data/lib/active_record/associations/association.rb +16 -27
- data/lib/active_record/associations/association_scope.rb +38 -50
- data/lib/active_record/associations/belongs_to_association.rb +20 -10
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -7
- data/lib/active_record/associations/builder/association.rb +4 -7
- data/lib/active_record/associations/builder/belongs_to.rb +4 -5
- 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 +43 -35
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/foreign_association.rb +2 -0
- data/lib/active_record/associations/has_many_association.rb +3 -1
- data/lib/active_record/associations/has_many_through_association.rb +7 -18
- data/lib/active_record/associations/has_one_association.rb +4 -1
- data/lib/active_record/associations/has_one_through_association.rb +8 -7
- data/lib/active_record/associations/join_dependency/join_association.rb +17 -56
- data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -9
- data/lib/active_record/associations/join_dependency.rb +23 -43
- data/lib/active_record/associations/preloader/association.rb +45 -61
- data/lib/active_record/associations/preloader/through_association.rb +71 -79
- data/lib/active_record/associations/preloader.rb +17 -37
- data/lib/active_record/associations/singular_association.rb +14 -10
- data/lib/active_record/associations/through_association.rb +25 -10
- data/lib/active_record/associations.rb +31 -54
- data/lib/active_record/attribute_assignment.rb +2 -5
- data/lib/active_record/attribute_decorators.rb +3 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
- data/lib/active_record/attribute_methods/dirty.rb +25 -214
- data/lib/active_record/attribute_methods/primary_key.rb +7 -6
- data/lib/active_record/attribute_methods/query.rb +2 -0
- data/lib/active_record/attribute_methods/read.rb +8 -2
- 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 +21 -9
- data/lib/active_record/attribute_methods.rb +65 -24
- data/lib/active_record/attributes.rb +6 -5
- data/lib/active_record/autosave_association.rb +8 -11
- data/lib/active_record/base.rb +2 -0
- data/lib/active_record/callbacks.rb +8 -10
- 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 +11 -7
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +111 -38
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +157 -29
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -32
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +57 -2
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +158 -78
- data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
- data/lib/active_record/connection_adapters/abstract_adapter.rb +81 -96
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +111 -183
- data/lib/active_record/connection_adapters/column.rb +3 -1
- data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -2
- 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 -30
- 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 +2 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -11
- 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 +2 -0
- 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 -1
- 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 +4 -6
- 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/oid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -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 +246 -110
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +58 -82
- 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 +18 -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 +71 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +80 -90
- 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 +39 -60
- data/lib/active_record/counter_cache.rb +15 -12
- data/lib/active_record/define_callbacks.rb +5 -3
- data/lib/active_record/dynamic_matchers.rb +9 -9
- data/lib/active_record/enum.rb +17 -13
- data/lib/active_record/errors.rb +54 -21
- 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 +14 -17
- data/lib/active_record/locking/pessimistic.rb +9 -6
- data/lib/active_record/log_subscriber.rb +43 -0
- data/lib/active_record/migration/command_recorder.rb +11 -9
- data/lib/active_record/migration/compatibility.rb +40 -2
- data/lib/active_record/migration/join_table.rb +2 -0
- data/lib/active_record/migration.rb +189 -139
- data/lib/active_record/model_schema.rb +16 -21
- 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 +166 -16
- data/lib/active_record/query_cache.rb +11 -6
- data/lib/active_record/querying.rb +3 -1
- data/lib/active_record/railtie.rb +61 -3
- 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 +46 -36
- data/lib/active_record/readonly_attributes.rb +3 -2
- data/lib/active_record/reflection.rb +110 -192
- data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
- data/lib/active_record/relation/batches.rb +20 -5
- data/lib/active_record/relation/calculations.rb +30 -8
- data/lib/active_record/relation/delegation.rb +15 -27
- data/lib/active_record/relation/finder_methods.rb +75 -78
- data/lib/active_record/relation/from_clause.rb +2 -8
- data/lib/active_record/relation/merger.rb +51 -20
- 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/predicate_builder.rb +53 -78
- data/lib/active_record/relation/query_attribute.rb +26 -2
- data/lib/active_record/relation/query_methods.rb +89 -88
- data/lib/active_record/relation/record_fetch_warning.rb +2 -0
- data/lib/active_record/relation/spawn_methods.rb +3 -1
- data/lib/active_record/relation/where_clause.rb +65 -68
- data/lib/active_record/relation/where_clause_factory.rb +5 -48
- data/lib/active_record/relation.rb +95 -208
- 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/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +21 -7
- data/lib/active_record/scoping.rb +9 -8
- 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 +26 -15
- data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
- data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
- data/lib/active_record/timestamp.rb +5 -12
- data/lib/active_record/touch_later.rb +2 -0
- data/lib/active_record/transactions.rb +9 -7
- data/lib/active_record/translation.rb +2 -0
- 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 +2 -4
- 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.rb +4 -1
- data/lib/active_record/type_caster/connection.rb +2 -0
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/type_caster.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 +35 -5
- data/lib/active_record/validations.rb +2 -0
- data/lib/active_record/version.rb +2 -0
- data/lib/active_record.rb +11 -4
- 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/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/migration.rb +2 -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
- data/lib/rails/generators/active_record.rb +3 -1
- 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/user_provided_default.rb +0 -30
- data/lib/active_record/attribute.rb +0 -240
- data/lib/active_record/attribute_mutation_tracker.rb +0 -122
- data/lib/active_record/attribute_set/builder.rb +0 -126
- data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
- data/lib/active_record/attribute_set.rb +0 -113
- 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 -37
@@ -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
|
data/lib/active_record/schema.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
# = Active Record \Schema
|
3
5
|
#
|
@@ -37,7 +39,7 @@ module ActiveRecord
|
|
37
39
|
# The +info+ hash is optional, and if given is used to define metadata
|
38
40
|
# about the current schema (currently, only the schema's version):
|
39
41
|
#
|
40
|
-
# ActiveRecord::Schema.define(version:
|
42
|
+
# ActiveRecord::Schema.define(version: 2038_01_19_000001) do
|
41
43
|
# ...
|
42
44
|
# end
|
43
45
|
def self.define(info = {}, &block)
|
@@ -53,7 +55,7 @@ module ActiveRecord
|
|
53
55
|
end
|
54
56
|
|
55
57
|
ActiveRecord::InternalMetadata.create_table
|
56
|
-
ActiveRecord::InternalMetadata[:environment] =
|
58
|
+
ActiveRecord::InternalMetadata[:environment] = connection.migration_context.current_environment
|
57
59
|
end
|
58
60
|
|
59
61
|
private
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "stringio"
|
2
4
|
|
3
5
|
module ActiveRecord
|
@@ -11,14 +13,13 @@ module ActiveRecord
|
|
11
13
|
##
|
12
14
|
# :singleton-method:
|
13
15
|
# A list of tables which should not be dumped to the schema.
|
14
|
-
# Acceptable values are strings as well as regexp.
|
15
|
-
#
|
16
|
-
cattr_accessor :ignore_tables
|
17
|
-
@@ignore_tables = []
|
16
|
+
# Acceptable values are strings as well as regexp if ActiveRecord::Base.schema_format == :ruby.
|
17
|
+
# Only strings are accepted if ActiveRecord::Base.schema_format == :sql.
|
18
|
+
cattr_accessor :ignore_tables, default: []
|
18
19
|
|
19
20
|
class << self
|
20
21
|
def dump(connection = ActiveRecord::Base.connection, stream = STDOUT, config = ActiveRecord::Base)
|
21
|
-
|
22
|
+
connection.create_schema_dumper(generate_options(config)).dump(stream)
|
22
23
|
stream
|
23
24
|
end
|
24
25
|
|
@@ -43,13 +44,22 @@ module ActiveRecord
|
|
43
44
|
|
44
45
|
def initialize(connection, options = {})
|
45
46
|
@connection = connection
|
46
|
-
@version =
|
47
|
+
@version = connection.migration_context.current_version rescue nil
|
47
48
|
@options = options
|
48
49
|
end
|
49
50
|
|
50
|
-
|
51
|
-
|
51
|
+
# turns 20170404131909 into "2017_04_04_131909"
|
52
|
+
def formatted_version
|
53
|
+
stringified = @version.to_s
|
54
|
+
return stringified unless stringified.length == 14
|
55
|
+
stringified.insert(4, "_").insert(7, "_").insert(10, "_")
|
56
|
+
end
|
52
57
|
|
58
|
+
def define_params
|
59
|
+
@version ? "version: #{formatted_version}" : ""
|
60
|
+
end
|
61
|
+
|
62
|
+
def header(stream)
|
53
63
|
stream.puts <<HEADER
|
54
64
|
# This file is auto-generated from the current state of the database. Instead
|
55
65
|
# of editing this file, please use the migrations feature of Active Record to
|
@@ -72,16 +82,8 @@ HEADER
|
|
72
82
|
stream.puts "end"
|
73
83
|
end
|
74
84
|
|
85
|
+
# extensions are only supported by PostgreSQL
|
75
86
|
def extensions(stream)
|
76
|
-
return unless @connection.supports_extensions?
|
77
|
-
extensions = @connection.extensions
|
78
|
-
if extensions.any?
|
79
|
-
stream.puts " # These are extensions that must be enabled in order to support this database"
|
80
|
-
extensions.each do |extension|
|
81
|
-
stream.puts " enable_extension #{extension.inspect}"
|
82
|
-
end
|
83
|
-
stream.puts
|
84
|
-
end
|
85
87
|
end
|
86
88
|
|
87
89
|
def tables(stream)
|
@@ -113,7 +115,7 @@ HEADER
|
|
113
115
|
when String
|
114
116
|
tbl.print ", primary_key: #{pk.inspect}" unless pk == "id"
|
115
117
|
pkcol = columns.detect { |c| c.name == pk }
|
116
|
-
pkcolspec =
|
118
|
+
pkcolspec = column_spec_for_primary_key(pkcol)
|
117
119
|
if pkcolspec.present?
|
118
120
|
tbl.print ", #{format_colspec(pkcolspec)}"
|
119
121
|
end
|
@@ -122,20 +124,19 @@ HEADER
|
|
122
124
|
else
|
123
125
|
tbl.print ", id: false"
|
124
126
|
end
|
125
|
-
tbl.print ", force: :cascade"
|
126
127
|
|
127
128
|
table_options = @connection.table_options(table)
|
128
129
|
if table_options.present?
|
129
130
|
tbl.print ", #{format_options(table_options)}"
|
130
131
|
end
|
131
132
|
|
132
|
-
tbl.puts " do |t|"
|
133
|
+
tbl.puts ", force: :cascade do |t|"
|
133
134
|
|
134
135
|
# then dump all non-primary key columns
|
135
136
|
columns.each do |column|
|
136
137
|
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
|
137
138
|
next if column.name == pk
|
138
|
-
type, colspec =
|
139
|
+
type, colspec = column_spec(column)
|
139
140
|
tbl.print " t.#{type} #{column.name.inspect}"
|
140
141
|
tbl.print ", #{format_colspec(colspec)}" if colspec.present?
|
141
142
|
tbl.puts
|
@@ -153,8 +154,6 @@ HEADER
|
|
153
154
|
stream.puts "# #{e.message}"
|
154
155
|
stream.puts
|
155
156
|
end
|
156
|
-
|
157
|
-
stream
|
158
157
|
end
|
159
158
|
|
160
159
|
# Keep it for indexing materialized views
|
@@ -185,8 +184,9 @@ HEADER
|
|
185
184
|
"name: #{index.name.inspect}",
|
186
185
|
]
|
187
186
|
index_parts << "unique: true" if index.unique
|
188
|
-
index_parts << "length:
|
189
|
-
index_parts << "order:
|
187
|
+
index_parts << "length: #{format_index_parts(index.lengths)}" if index.lengths.present?
|
188
|
+
index_parts << "order: #{format_index_parts(index.orders)}" if index.orders.present?
|
189
|
+
index_parts << "opclass: #{format_index_parts(index.opclasses)}" if index.opclasses.present?
|
190
190
|
index_parts << "where: #{index.where.inspect}" if index.where
|
191
191
|
index_parts << "using: #{index.using.inspect}" if !@connection.default_index_type?(index)
|
192
192
|
index_parts << "type: #{index.type.inspect}" if index.type
|
@@ -232,8 +232,18 @@ HEADER
|
|
232
232
|
options.map { |key, value| "#{key}: #{value.inspect}" }.join(", ")
|
233
233
|
end
|
234
234
|
|
235
|
+
def format_index_parts(options)
|
236
|
+
if options.is_a?(Hash)
|
237
|
+
"{ #{format_options(options)} }"
|
238
|
+
else
|
239
|
+
options.inspect
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
235
243
|
def remove_prefix_and_suffix(table)
|
236
|
-
|
244
|
+
prefix = Regexp.escape(@options[:table_name_prefix].to_s)
|
245
|
+
suffix = Regexp.escape(@options[:table_name_suffix].to_s)
|
246
|
+
table.sub(/\A#{prefix}(.+)#{suffix}\z/, "\\1")
|
237
247
|
end
|
238
248
|
|
239
249
|
def ignored?(table_name)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Scoping
|
3
5
|
module Default
|
@@ -5,11 +7,8 @@ module ActiveRecord
|
|
5
7
|
|
6
8
|
included do
|
7
9
|
# Stores the default scope for the class.
|
8
|
-
class_attribute :default_scopes, instance_writer: false, instance_predicate: false
|
9
|
-
class_attribute :default_scope_override, instance_writer: false, instance_predicate: false
|
10
|
-
|
11
|
-
self.default_scopes = []
|
12
|
-
self.default_scope_override = nil
|
10
|
+
class_attribute :default_scopes, instance_writer: false, instance_predicate: false, default: []
|
11
|
+
class_attribute :default_scope_override, instance_writer: false, instance_predicate: false, default: nil
|
13
12
|
end
|
14
13
|
|
15
14
|
module ClassMethods
|
@@ -112,7 +111,7 @@ module ActiveRecord
|
|
112
111
|
# The user has defined their own default scope method, so call that
|
113
112
|
evaluate_default_scope do
|
114
113
|
if scope = default_scope
|
115
|
-
(base_rel ||= relation).merge(scope)
|
114
|
+
(base_rel ||= relation).merge!(scope)
|
116
115
|
end
|
117
116
|
end
|
118
117
|
elsif default_scopes.any?
|
@@ -120,7 +119,7 @@ module ActiveRecord
|
|
120
119
|
evaluate_default_scope do
|
121
120
|
default_scopes.inject(base_rel) do |default_scope, scope|
|
122
121
|
scope = scope.respond_to?(:to_proc) ? scope : scope.method(:call)
|
123
|
-
default_scope.merge(base_rel.instance_exec(&scope))
|
122
|
+
default_scope.merge!(base_rel.instance_exec(&scope))
|
124
123
|
end
|
125
124
|
end
|
126
125
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/array"
|
2
4
|
require "active_support/core_ext/hash/except"
|
3
5
|
require "active_support/core_ext/kernel/singleton_class"
|
@@ -22,8 +24,14 @@ module ActiveRecord
|
|
22
24
|
# You can define a scope that applies to all finders using
|
23
25
|
# {default_scope}[rdoc-ref:Scoping::Default::ClassMethods#default_scope].
|
24
26
|
def all
|
27
|
+
current_scope = self.current_scope
|
28
|
+
|
25
29
|
if current_scope
|
26
|
-
current_scope.
|
30
|
+
if self == current_scope.klass
|
31
|
+
current_scope.clone
|
32
|
+
else
|
33
|
+
relation.merge!(current_scope)
|
34
|
+
end
|
27
35
|
else
|
28
36
|
default_scoped
|
29
37
|
end
|
@@ -163,22 +171,28 @@ module ActiveRecord
|
|
163
171
|
"a class method with the same name."
|
164
172
|
end
|
165
173
|
|
174
|
+
if method_defined_within?(name, Relation)
|
175
|
+
raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
|
176
|
+
"on the model \"#{self.name}\", but ActiveRecord::Relation already defined " \
|
177
|
+
"an instance method with the same name."
|
178
|
+
end
|
179
|
+
|
166
180
|
valid_scope_name?(name)
|
167
181
|
extension = Module.new(&block) if block
|
168
182
|
|
169
183
|
if body.respond_to?(:to_proc)
|
170
184
|
singleton_class.send(:define_method, name) do |*args|
|
171
|
-
scope = all
|
185
|
+
scope = all
|
186
|
+
scope = scope._exec_scope(*args, &body)
|
172
187
|
scope = scope.extending(extension) if extension
|
173
|
-
|
174
|
-
scope || all
|
188
|
+
scope
|
175
189
|
end
|
176
190
|
else
|
177
191
|
singleton_class.send(:define_method, name) do |*args|
|
178
|
-
scope = all
|
192
|
+
scope = all
|
193
|
+
scope = scope.scoping { body.call(*args) || scope }
|
179
194
|
scope = scope.extending(extension) if extension
|
180
|
-
|
181
|
-
scope || all
|
195
|
+
scope
|
182
196
|
end
|
183
197
|
end
|
184
198
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/per_thread_registry"
|
2
4
|
|
3
5
|
module ActiveRecord
|
@@ -9,23 +11,23 @@ module ActiveRecord
|
|
9
11
|
include Named
|
10
12
|
end
|
11
13
|
|
12
|
-
module ClassMethods
|
13
|
-
def current_scope(skip_inherited_scope = false)
|
14
|
+
module ClassMethods # :nodoc:
|
15
|
+
def current_scope(skip_inherited_scope = false)
|
14
16
|
ScopeRegistry.value_for(:current_scope, self, skip_inherited_scope)
|
15
17
|
end
|
16
18
|
|
17
|
-
def current_scope=(scope)
|
19
|
+
def current_scope=(scope)
|
18
20
|
ScopeRegistry.set_value_for(:current_scope, self, scope)
|
19
21
|
end
|
20
22
|
|
21
23
|
# Collects attributes from scopes that should be applied when creating
|
22
24
|
# an AR instance for the particular class this is called on.
|
23
|
-
def scope_attributes
|
25
|
+
def scope_attributes
|
24
26
|
all.scope_for_create
|
25
27
|
end
|
26
28
|
|
27
29
|
# Are there attributes associated with this scope?
|
28
|
-
def scope_attributes?
|
30
|
+
def scope_attributes?
|
29
31
|
current_scope
|
30
32
|
end
|
31
33
|
end
|
@@ -33,9 +35,8 @@ module ActiveRecord
|
|
33
35
|
def populate_with_current_scope_attributes # :nodoc:
|
34
36
|
return unless self.class.scope_attributes?
|
35
37
|
|
36
|
-
self.class.scope_attributes
|
37
|
-
|
38
|
-
end
|
38
|
+
attributes = self.class.scope_attributes
|
39
|
+
_assign_attributes(attributes) if attributes.any?
|
39
40
|
end
|
40
41
|
|
41
42
|
def initialize_internals_callback # :nodoc:
|