activerecord 4.2.0 → 5.2.8.1
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 +640 -928
- data/MIT-LICENSE +2 -2
- data/README.rdoc +10 -11
- data/examples/performance.rb +32 -31
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +264 -247
- data/lib/active_record/association_relation.rb +24 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +87 -41
- data/lib/active_record/associations/association_scope.rb +106 -132
- data/lib/active_record/associations/belongs_to_association.rb +55 -36
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
- data/lib/active_record/associations/builder/association.rb +29 -38
- data/lib/active_record/associations/builder/belongs_to.rb +77 -30
- data/lib/active_record/associations/builder/collection_association.rb +14 -23
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
- data/lib/active_record/associations/builder/has_many.rb +6 -4
- data/lib/active_record/associations/builder/has_one.rb +13 -6
- data/lib/active_record/associations/builder/singular_association.rb +15 -11
- data/lib/active_record/associations/collection_association.rb +145 -266
- data/lib/active_record/associations/collection_proxy.rb +242 -138
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +35 -75
- data/lib/active_record/associations/has_many_through_association.rb +51 -69
- data/lib/active_record/associations/has_one_association.rb +39 -24
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +40 -81
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
- data/lib/active_record/associations/join_dependency.rb +134 -154
- data/lib/active_record/associations/preloader/association.rb +85 -116
- data/lib/active_record/associations/preloader/through_association.rb +85 -74
- data/lib/active_record/associations/preloader.rb +83 -93
- data/lib/active_record/associations/singular_association.rb +27 -40
- data/lib/active_record/associations/through_association.rb +48 -23
- data/lib/active_record/associations.rb +1732 -1596
- data/lib/active_record/attribute_assignment.rb +58 -182
- data/lib/active_record/attribute_decorators.rb +39 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +12 -5
- data/lib/active_record/attribute_methods/dirty.rb +94 -125
- data/lib/active_record/attribute_methods/primary_key.rb +86 -71
- data/lib/active_record/attribute_methods/query.rb +4 -2
- data/lib/active_record/attribute_methods/read.rb +45 -63
- data/lib/active_record/attribute_methods/serialization.rb +40 -20
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +62 -36
- data/lib/active_record/attribute_methods/write.rb +31 -46
- data/lib/active_record/attribute_methods.rb +170 -117
- data/lib/active_record/attributes.rb +201 -74
- data/lib/active_record/autosave_association.rb +118 -45
- data/lib/active_record/base.rb +60 -48
- data/lib/active_record/callbacks.rb +97 -57
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +37 -13
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -217
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +617 -212
- data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
- data/lib/active_record/connection_adapters/column.rb +50 -41
- data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +42 -195
- data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
- 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 +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +466 -280
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +439 -330
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -324
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +40 -27
- data/lib/active_record/core.rb +205 -202
- data/lib/active_record/counter_cache.rb +80 -37
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +136 -90
- data/lib/active_record/errors.rb +180 -52
- data/lib/active_record/explain.rb +23 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixtures.rb +193 -135
- data/lib/active_record/gem_version.rb +5 -3
- data/lib/active_record/inheritance.rb +148 -112
- data/lib/active_record/integration.rb +70 -28
- data/lib/active_record/internal_metadata.rb +45 -0
- data/lib/active_record/legacy_yaml_adapter.rb +48 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +92 -98
- data/lib/active_record/locking/pessimistic.rb +15 -3
- data/lib/active_record/log_subscriber.rb +95 -33
- data/lib/active_record/migration/command_recorder.rb +133 -90
- data/lib/active_record/migration/compatibility.rb +217 -0
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/migration.rb +594 -267
- data/lib/active_record/model_schema.rb +292 -111
- data/lib/active_record/nested_attributes.rb +266 -214
- data/lib/active_record/no_touching.rb +8 -2
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +350 -119
- data/lib/active_record/query_cache.rb +13 -24
- data/lib/active_record/querying.rb +19 -17
- data/lib/active_record/railtie.rb +117 -35
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +9 -3
- data/lib/active_record/railties/databases.rake +160 -174
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +447 -288
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +204 -55
- data/lib/active_record/relation/calculations.rb +259 -244
- data/lib/active_record/relation/delegation.rb +67 -60
- data/lib/active_record/relation/finder_methods.rb +290 -253
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +91 -68
- data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/predicate_builder.rb +118 -92
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +446 -389
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -16
- data/lib/active_record/relation/where_clause.rb +186 -0
- data/lib/active_record/relation/where_clause_factory.rb +34 -0
- data/lib/active_record/relation.rb +287 -339
- data/lib/active_record/result.rb +54 -36
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +155 -124
- data/lib/active_record/schema.rb +30 -24
- data/lib/active_record/schema_dumper.rb +91 -87
- data/lib/active_record/schema_migration.rb +19 -19
- data/lib/active_record/scoping/default.rb +102 -84
- data/lib/active_record/scoping/named.rb +81 -32
- data/lib/active_record/scoping.rb +45 -26
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/statement_cache.rb +45 -35
- data/lib/active_record/store.rb +42 -36
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +82 -0
- data/lib/active_record/tasks/database_tasks.rb +136 -95
- data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
- data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
- data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
- data/lib/active_record/timestamp.rb +70 -38
- data/lib/active_record/touch_later.rb +64 -0
- data/lib/active_record/transactions.rb +208 -123
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type/adapter_specific_registry.rb +136 -0
- data/lib/active_record/type/date.rb +4 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +30 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +11 -16
- data/lib/active_record/type/type_map.rb +15 -17
- data/lib/active_record/type/unsigned_integer.rb +9 -7
- data/lib/active_record/type.rb +79 -23
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +13 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +41 -32
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +36 -21
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -6
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
- data/lib/rails/generators/active_record/migration.rb +18 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +77 -53
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -149
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -30
- data/lib/active_record/type/decimal.rb +0 -40
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -55
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -36
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
data/lib/active_record/result.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
###
|
3
|
-
# This class encapsulates a
|
4
|
-
#
|
5
|
+
# This class encapsulates a result returned from calling
|
6
|
+
# {#exec_query}[rdoc-ref:ConnectionAdapters::DatabaseStatements#exec_query]
|
7
|
+
# on any database connection adapter. For example:
|
5
8
|
#
|
6
9
|
# result = ActiveRecord::Base.connection.exec_query('SELECT id, title, body FROM posts')
|
7
10
|
# result # => #<ActiveRecord::Result:0xdeadbeef>
|
@@ -31,8 +34,6 @@ module ActiveRecord
|
|
31
34
|
class Result
|
32
35
|
include Enumerable
|
33
36
|
|
34
|
-
IDENTITY_TYPE = Type::Value.new # :nodoc:
|
35
|
-
|
36
37
|
attr_reader :columns, :rows, :column_types
|
37
38
|
|
38
39
|
def initialize(columns, rows, column_types = {})
|
@@ -42,10 +43,15 @@ module ActiveRecord
|
|
42
43
|
@column_types = column_types
|
43
44
|
end
|
44
45
|
|
46
|
+
# Returns the number of elements in the rows array.
|
45
47
|
def length
|
46
48
|
@rows.length
|
47
49
|
end
|
48
50
|
|
51
|
+
# Calls the given block once for each element in row collection, passing
|
52
|
+
# row as parameter.
|
53
|
+
#
|
54
|
+
# Returns an +Enumerator+ if no block is given.
|
49
55
|
def each
|
50
56
|
if block_given?
|
51
57
|
hash_rows.each { |row| yield row }
|
@@ -54,6 +60,7 @@ module ActiveRecord
|
|
54
60
|
end
|
55
61
|
end
|
56
62
|
|
63
|
+
# Returns an array of hashes representing each row record.
|
57
64
|
def to_hash
|
58
65
|
hash_rows
|
59
66
|
end
|
@@ -61,11 +68,12 @@ module ActiveRecord
|
|
61
68
|
alias :map! :map
|
62
69
|
alias :collect! :map
|
63
70
|
|
64
|
-
# Returns true if there are no records.
|
71
|
+
# Returns true if there are no records, otherwise false.
|
65
72
|
def empty?
|
66
73
|
rows.empty?
|
67
74
|
end
|
68
75
|
|
76
|
+
# Returns an array of hashes representing each row record.
|
69
77
|
def to_ary
|
70
78
|
hash_rows
|
71
79
|
end
|
@@ -74,14 +82,24 @@ module ActiveRecord
|
|
74
82
|
hash_rows[idx]
|
75
83
|
end
|
76
84
|
|
85
|
+
# Returns the first record from the rows collection.
|
86
|
+
# If the rows collection is empty, returns +nil+.
|
87
|
+
def first
|
88
|
+
return nil if @rows.empty?
|
89
|
+
Hash[@columns.zip(@rows.first)]
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns the last record from the rows collection.
|
93
|
+
# If the rows collection is empty, returns +nil+.
|
77
94
|
def last
|
78
|
-
|
95
|
+
return nil if @rows.empty?
|
96
|
+
Hash[@columns.zip(@rows.last)]
|
79
97
|
end
|
80
98
|
|
81
99
|
def cast_values(type_overrides = {}) # :nodoc:
|
82
100
|
types = columns.map { |name| column_type(name, type_overrides) }
|
83
101
|
result = rows.map do |values|
|
84
|
-
types.zip(values).map { |type, value| type.
|
102
|
+
types.zip(values).map { |type, value| type.deserialize(value) }
|
85
103
|
end
|
86
104
|
|
87
105
|
columns.one? ? result.map!(&:first) : result
|
@@ -96,36 +114,36 @@ module ActiveRecord
|
|
96
114
|
|
97
115
|
private
|
98
116
|
|
99
|
-
|
100
|
-
|
101
|
-
|
117
|
+
def column_type(name, type_overrides = {})
|
118
|
+
type_overrides.fetch(name) do
|
119
|
+
column_types.fetch(name, Type.default_value)
|
120
|
+
end
|
102
121
|
end
|
103
|
-
end
|
104
122
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
123
|
+
def hash_rows
|
124
|
+
@hash_rows ||=
|
125
|
+
begin
|
126
|
+
# We freeze the strings to prevent them getting duped when
|
127
|
+
# used as keys in ActiveRecord::Base's @attributes hash
|
128
|
+
columns = @columns.map { |c| c.dup.freeze }
|
129
|
+
@rows.map { |row|
|
130
|
+
# In the past we used Hash[columns.zip(row)]
|
131
|
+
# though elegant, the verbose way is much more efficient
|
132
|
+
# both time and memory wise cause it avoids a big array allocation
|
133
|
+
# this method is called a lot and needs to be micro optimised
|
134
|
+
hash = {}
|
135
|
+
|
136
|
+
index = 0
|
137
|
+
length = columns.length
|
138
|
+
|
139
|
+
while index < length
|
140
|
+
hash[columns[index]] = row[index]
|
141
|
+
index += 1
|
142
|
+
end
|
143
|
+
|
144
|
+
hash
|
145
|
+
}
|
146
|
+
end
|
147
|
+
end
|
130
148
|
end
|
131
149
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/per_thread_registry"
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
# This is a thread locals registry for Active Record. For example:
|
@@ -7,14 +9,14 @@ module ActiveRecord
|
|
7
9
|
#
|
8
10
|
# returns the connection handler local to the current thread.
|
9
11
|
#
|
10
|
-
# See the documentation of
|
12
|
+
# See the documentation of ActiveSupport::PerThreadRegistry
|
11
13
|
# for further details.
|
12
14
|
class RuntimeRegistry # :nodoc:
|
13
15
|
extend ActiveSupport::PerThreadRegistry
|
14
16
|
|
15
|
-
attr_accessor :connection_handler, :sql_runtime
|
17
|
+
attr_accessor :connection_handler, :sql_runtime
|
16
18
|
|
17
|
-
[:connection_handler, :sql_runtime
|
19
|
+
[:connection_handler, :sql_runtime].each do |val|
|
18
20
|
class_eval %{ def self.#{val}; instance.#{val}; end }, __FILE__, __LINE__
|
19
21
|
class_eval %{ def self.#{val}=(x); instance.#{val}=x; end }, __FILE__, __LINE__
|
20
22
|
end
|
@@ -1,40 +1,49 @@
|
|
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
|
-
|
7
|
-
connection.quote(value, column)
|
8
|
-
end
|
9
|
-
|
10
|
-
# Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
|
11
|
-
def sanitize(object) #:nodoc:
|
12
|
-
connection.quote(object)
|
13
|
-
end
|
14
|
-
|
15
|
-
protected
|
16
|
-
|
17
|
-
# Accepts an array, hash, or string of SQL conditions and sanitizes
|
8
|
+
# Accepts an array or string of SQL conditions and sanitizes
|
18
9
|
# them into a valid SQL fragment for a WHERE clause.
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
|
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
23
|
return nil if condition.blank?
|
24
24
|
|
25
25
|
case condition
|
26
26
|
when Array; sanitize_sql_array(condition)
|
27
|
-
when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
|
28
27
|
else condition
|
29
28
|
end
|
30
29
|
end
|
31
|
-
|
32
|
-
alias_method :sanitize_conditions, :sanitize_sql
|
30
|
+
alias :sanitize_sql :sanitize_sql_for_conditions
|
33
31
|
|
34
32
|
# Accepts an array, hash, or string of SQL conditions and sanitizes
|
35
33
|
# them into a valid SQL fragment for a SET clause.
|
36
|
-
#
|
37
|
-
|
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)
|
38
47
|
case assignments
|
39
48
|
when Array; sanitize_sql_array(assignments)
|
40
49
|
when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
|
@@ -42,76 +51,59 @@ module ActiveRecord
|
|
42
51
|
end
|
43
52
|
end
|
44
53
|
|
45
|
-
# Accepts
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
expanded_attrs[field_attr] = value
|
64
|
-
else
|
65
|
-
expanded_attrs[field_attr] = value.send(aggregate_attr)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
else
|
69
|
-
expanded_attrs[attr] = value
|
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]]
|
70
72
|
end
|
71
|
-
end
|
72
|
-
expanded_attrs
|
73
|
-
end
|
74
73
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
# # => "status IS NULL and group_id IN (1,2,3)"
|
80
|
-
# { age: 13..18 }
|
81
|
-
# # => "age BETWEEN 13 AND 18"
|
82
|
-
# { 'other_records.id' => 7 }
|
83
|
-
# # => "`other_records`.`id` = 7"
|
84
|
-
# { other_records: { id: 7 } }
|
85
|
-
# # => "`other_records`.`id` = 7"
|
86
|
-
# And for value objects on a composed_of relationship:
|
87
|
-
# { address: Address.new("123 abc st.", "chicago") }
|
88
|
-
# # => "address_street='123 abc st.' and address_city='chicago'"
|
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
|
93
|
-
attrs = PredicateBuilder.resolve_column_aliases self, attrs
|
94
|
-
attrs = expand_hash_conditions_for_aggregates(attrs)
|
95
|
-
|
96
|
-
table = Arel::Table.new(table_name, arel_engine).alias(default_table_name)
|
97
|
-
PredicateBuilder.build_from_hash(self, attrs, table).map { |b|
|
98
|
-
connection.visitor.compile b
|
99
|
-
}.join(' AND ')
|
74
|
+
Arel.sql(sanitize_sql_array(condition))
|
75
|
+
else
|
76
|
+
condition
|
77
|
+
end
|
100
78
|
end
|
101
|
-
alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
|
102
79
|
|
103
80
|
# Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
|
104
|
-
#
|
105
|
-
#
|
81
|
+
#
|
82
|
+
# sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
|
83
|
+
# # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
|
106
84
|
def sanitize_sql_hash_for_assignment(attrs, table)
|
107
85
|
c = connection
|
108
86
|
attrs.map do |attr, value|
|
109
|
-
|
110
|
-
|
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(", ")
|
111
91
|
end
|
112
92
|
|
113
93
|
# 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 "%"
|
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"
|
115
107
|
def sanitize_sql_like(string, escape_character = "\\")
|
116
108
|
pattern = Regexp.union(escape_character, "%", "_")
|
117
109
|
string.gsub(pattern) { |x| [escape_character, x].join }
|
@@ -119,12 +111,20 @@ sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
|
|
119
111
|
|
120
112
|
# Accepts an array of conditions. The array has each value
|
121
113
|
# sanitized and interpolated into the SQL statement.
|
122
|
-
#
|
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
123
|
def sanitize_sql_array(ary)
|
124
124
|
statement, *values = ary
|
125
|
-
if values.first.is_a?(Hash) &&
|
125
|
+
if values.first.is_a?(Hash) && /:\w+/.match?(statement)
|
126
126
|
replace_named_bind_variables(statement, values.first)
|
127
|
-
elsif statement.include?(
|
127
|
+
elsif statement.include?("?")
|
128
128
|
replace_bind_variables(statement, values)
|
129
129
|
elsif statement.blank?
|
130
130
|
statement
|
@@ -133,59 +133,90 @@ sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
|
|
133
133
|
end
|
134
134
|
end
|
135
135
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
136
|
+
private
|
137
|
+
# Accepts a hash of SQL conditions and replaces those attributes
|
138
|
+
# that correspond to a {#composed_of}[rdoc-ref:Aggregations::ClassMethods#composed_of]
|
139
|
+
# relationship with their expanded aggregate attribute values.
|
140
|
+
#
|
141
|
+
# Given:
|
142
|
+
#
|
143
|
+
# class Person < ActiveRecord::Base
|
144
|
+
# composed_of :address, class_name: "Address",
|
145
|
+
# mapping: [%w(address_street street), %w(address_city city)]
|
146
|
+
# end
|
147
|
+
#
|
148
|
+
# Then:
|
149
|
+
#
|
150
|
+
# { address: Address.new("813 abc st.", "chicago") }
|
151
|
+
# # => { address_street: "813 abc st.", address_city: "chicago" }
|
152
|
+
def expand_hash_conditions_for_aggregates(attrs) # :doc:
|
153
|
+
expanded_attrs = {}
|
154
|
+
attrs.each do |attr, value|
|
155
|
+
if aggregation = reflect_on_aggregation(attr.to_sym)
|
156
|
+
mapping = aggregation.mapping
|
157
|
+
mapping.each do |field_attr, aggregate_attr|
|
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
|
162
|
+
else
|
163
|
+
value.send(aggregate_attr)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
else
|
167
|
+
expanded_attrs[attr] = value
|
168
|
+
end
|
169
|
+
end
|
170
|
+
expanded_attrs
|
142
171
|
end
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
172
|
+
deprecate :expand_hash_conditions_for_aggregates
|
173
|
+
|
174
|
+
def replace_bind_variables(statement, values)
|
175
|
+
raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size)
|
176
|
+
bound = values.dup
|
177
|
+
c = connection
|
178
|
+
statement.gsub(/\?/) do
|
179
|
+
replace_bind_variable(bound.shift, c)
|
180
|
+
end
|
150
181
|
end
|
151
|
-
end
|
152
182
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
$& # return the whole match
|
157
|
-
elsif bind_vars.include?(match = $2.to_sym)
|
158
|
-
replace_bind_variable(bind_vars[match])
|
183
|
+
def replace_bind_variable(value, c = connection)
|
184
|
+
if ActiveRecord::Relation === value
|
185
|
+
value.to_sql
|
159
186
|
else
|
160
|
-
|
187
|
+
quote_bound_value(value, c)
|
161
188
|
end
|
162
189
|
end
|
163
|
-
end
|
164
190
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
191
|
+
def replace_named_bind_variables(statement, bind_vars)
|
192
|
+
statement.gsub(/(:?):([a-zA-Z]\w*)/) do |match|
|
193
|
+
if $1 == ":" # skip postgresql casts
|
194
|
+
match # return the whole match
|
195
|
+
elsif bind_vars.include?(match = $2.to_sym)
|
196
|
+
replace_bind_variable(bind_vars[match])
|
197
|
+
else
|
198
|
+
raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
|
199
|
+
end
|
173
200
|
end
|
174
|
-
else
|
175
|
-
c.quote(value)
|
176
201
|
end
|
177
|
-
end
|
178
202
|
|
179
|
-
|
180
|
-
|
181
|
-
|
203
|
+
def quote_bound_value(value, c = connection)
|
204
|
+
if value.respond_to?(:map) && !value.acts_like?(:string)
|
205
|
+
if value.respond_to?(:empty?) && value.empty?
|
206
|
+
c.quote(nil)
|
207
|
+
else
|
208
|
+
value.map { |v| c.quote(v) }.join(",")
|
209
|
+
end
|
210
|
+
else
|
211
|
+
c.quote(value)
|
212
|
+
end
|
182
213
|
end
|
183
|
-
end
|
184
|
-
end
|
185
214
|
|
186
|
-
|
187
|
-
|
188
|
-
|
215
|
+
def raise_if_bind_arity_mismatch(statement, expected, provided)
|
216
|
+
unless expected == provided
|
217
|
+
raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
|
218
|
+
end
|
219
|
+
end
|
189
220
|
end
|
190
221
|
end
|
191
222
|
end
|
data/lib/active_record/schema.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
|
-
# = Active Record Schema
|
4
|
+
# = Active Record \Schema
|
3
5
|
#
|
4
6
|
# Allows programmers to programmatically define a schema in a portable
|
5
7
|
# DSL. This means you can define tables, indexes, etc. without using SQL
|
@@ -27,38 +29,42 @@ module ActiveRecord
|
|
27
29
|
#
|
28
30
|
# ActiveRecord::Schema is only supported by database adapters that also
|
29
31
|
# support migrations, the two features being very similar.
|
30
|
-
class Schema < Migration
|
31
|
-
|
32
|
-
# Returns the migrations paths.
|
33
|
-
#
|
34
|
-
# ActiveRecord::Schema.new.migrations_paths
|
35
|
-
# # => ["db/migrate"] # Rails migration path by default.
|
36
|
-
def migrations_paths
|
37
|
-
ActiveRecord::Migrator.migrations_paths
|
38
|
-
end
|
39
|
-
|
40
|
-
def define(info, &block) # :nodoc:
|
41
|
-
instance_eval(&block)
|
42
|
-
|
43
|
-
unless info[:version].blank?
|
44
|
-
initialize_schema_migrations_table
|
45
|
-
connection.assume_migrated_upto_version(info[:version], migrations_paths)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
32
|
+
class Schema < Migration::Current
|
49
33
|
# Eval the given block. All methods available to the current connection
|
50
34
|
# adapter are available within the block, so you can easily use the
|
51
|
-
# database definition DSL to build up your schema (
|
52
|
-
#
|
35
|
+
# database definition DSL to build up your schema (
|
36
|
+
# {create_table}[rdoc-ref:ConnectionAdapters::SchemaStatements#create_table],
|
37
|
+
# {add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index], etc.).
|
53
38
|
#
|
54
39
|
# The +info+ hash is optional, and if given is used to define metadata
|
55
40
|
# about the current schema (currently, only the schema's version):
|
56
41
|
#
|
57
|
-
# ActiveRecord::Schema.define(version:
|
42
|
+
# ActiveRecord::Schema.define(version: 2038_01_19_000001) do
|
58
43
|
# ...
|
59
44
|
# end
|
60
|
-
def self.define(info={}, &block)
|
45
|
+
def self.define(info = {}, &block)
|
61
46
|
new.define(info, &block)
|
62
47
|
end
|
48
|
+
|
49
|
+
def define(info, &block) # :nodoc:
|
50
|
+
instance_eval(&block)
|
51
|
+
|
52
|
+
if info[:version].present?
|
53
|
+
ActiveRecord::SchemaMigration.create_table
|
54
|
+
connection.assume_migrated_upto_version(info[:version], migrations_paths)
|
55
|
+
end
|
56
|
+
|
57
|
+
ActiveRecord::InternalMetadata.create_table
|
58
|
+
ActiveRecord::InternalMetadata[:environment] = connection.migration_context.current_environment
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
# Returns the migrations paths.
|
63
|
+
#
|
64
|
+
# ActiveRecord::Schema.new.migrations_paths
|
65
|
+
# # => ["db/migrate"] # Rails migration path by default.
|
66
|
+
def migrations_paths
|
67
|
+
ActiveRecord::Migrator.migrations_paths
|
68
|
+
end
|
63
69
|
end
|
64
70
|
end
|