activerecord 7.1.5.2 → 8.1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +567 -2443
- data/README.rdoc +16 -16
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +3 -2
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +31 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +18 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +36 -5
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +268 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +8 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +250 -315
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +20 -27
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +96 -68
- data/lib/active_record/autosave_association.rb +93 -58
- data/lib/active_record/base.rb +5 -7
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
- data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
- data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
- data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +43 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +66 -0
- data/lib/active_record/connection_handling.rb +88 -46
- data/lib/active_record/core.rb +142 -58
- data/lib/active_record/counter_cache.rb +52 -18
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +28 -5
- data/lib/active_record/database_configurations/hash_config.rb +103 -41
- data/lib/active_record/database_configurations/url_config.rb +30 -1
- data/lib/active_record/database_configurations.rb +8 -4
- data/lib/active_record/delegated_type.rb +43 -19
- data/lib/active_record/dynamic_matchers.rb +56 -71
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +12 -12
- data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
- data/lib/active_record/encryption/encryptor.rb +58 -22
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +57 -36
- data/lib/active_record/errors.rb +76 -26
- data/lib/active_record/explain.rb +14 -25
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +39 -35
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +5 -3
- data/lib/active_record/insert_all.rb +28 -20
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +15 -7
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -34
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +46 -14
- data/lib/active_record/migration/compatibility.rb +40 -25
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +119 -103
- data/lib/active_record/model_schema.rb +66 -78
- data/lib/active_record/nested_attributes.rb +17 -9
- data/lib/active_record/persistence.rb +161 -454
- data/lib/active_record/query_cache.rb +33 -15
- data/lib/active_record/query_logs.rb +106 -39
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +28 -16
- data/lib/active_record/railtie.rb +61 -74
- data/lib/active_record/railties/controller_runtime.rb +19 -5
- data/lib/active_record/railties/databases.rake +64 -78
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +147 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +153 -73
- data/lib/active_record/relation/calculations.rb +146 -96
- data/lib/active_record/relation/delegation.rb +30 -23
- data/lib/active_record/relation/finder_methods.rb +64 -34
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +19 -11
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +25 -10
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +351 -124
- data/lib/active_record/relation/spawn_methods.rb +9 -25
- data/lib/active_record/relation/where_clause.rb +17 -30
- data/lib/active_record/relation.rb +590 -124
- data/lib/active_record/result.rb +131 -59
- data/lib/active_record/runtime_registry.rb +44 -21
- data/lib/active_record/sanitization.rb +33 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +66 -31
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +68 -20
- data/lib/active_record/statement_cache.rb +29 -25
- data/lib/active_record/store.rb +51 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +7 -33
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +118 -90
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +123 -89
- data/lib/active_record/testing/query_assertions.rb +127 -0
- data/lib/active_record/timestamp.rb +6 -4
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +129 -0
- data/lib/active_record/transactions.rb +97 -18
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +12 -7
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +5 -4
- data/lib/active_record/validations/associated.rb +10 -4
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +203 -59
- data/lib/arel/alias_predication.rb +3 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +6 -5
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +2 -4
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +14 -5
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -3
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +56 -12
- data/lib/arel/visitors/sqlite.rb +80 -8
- data/lib/arel/visitors/to_sql.rb +34 -37
- data/lib/arel.rb +10 -4
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +23 -14
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -167
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
data/lib/active_record/result.rb
CHANGED
|
@@ -8,7 +8,7 @@ module ActiveRecord
|
|
|
8
8
|
# {#exec_query}[rdoc-ref:ConnectionAdapters::DatabaseStatements#exec_query]
|
|
9
9
|
# on any database connection adapter. For example:
|
|
10
10
|
#
|
|
11
|
-
# result = ActiveRecord::Base.
|
|
11
|
+
# result = ActiveRecord::Base.lease_connection.exec_query('SELECT id, title, body FROM posts')
|
|
12
12
|
# result # => #<ActiveRecord::Result:0xdeadbeef>
|
|
13
13
|
#
|
|
14
14
|
# # Get the column names of the result:
|
|
@@ -29,6 +29,11 @@ module ActiveRecord
|
|
|
29
29
|
# ...
|
|
30
30
|
# ]
|
|
31
31
|
#
|
|
32
|
+
# # Get the number of rows affected by the query:
|
|
33
|
+
# result = ActiveRecord::Base.lease_connection.exec_query('INSERT INTO posts (title, body) VALUES ("title_3", "body_3"), ("title_4", "body_4")')
|
|
34
|
+
# result.affected_rows
|
|
35
|
+
# # => 2
|
|
36
|
+
#
|
|
32
37
|
# # ActiveRecord::Result also includes Enumerable.
|
|
33
38
|
# result.each do |row|
|
|
34
39
|
# puts row['title'] + " " + row['body']
|
|
@@ -36,21 +41,79 @@ module ActiveRecord
|
|
|
36
41
|
class Result
|
|
37
42
|
include Enumerable
|
|
38
43
|
|
|
39
|
-
|
|
44
|
+
class IndexedRow
|
|
45
|
+
def initialize(column_indexes, row)
|
|
46
|
+
@column_indexes = column_indexes
|
|
47
|
+
@row = row
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def size
|
|
51
|
+
@column_indexes.size
|
|
52
|
+
end
|
|
53
|
+
alias_method :length, :size
|
|
54
|
+
|
|
55
|
+
def each_key(&block)
|
|
56
|
+
@column_indexes.each_key(&block)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def keys
|
|
60
|
+
@column_indexes.keys
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def ==(other)
|
|
64
|
+
if other.is_a?(Hash)
|
|
65
|
+
to_hash == other
|
|
66
|
+
else
|
|
67
|
+
super
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def key?(column)
|
|
72
|
+
@column_indexes.key?(column)
|
|
73
|
+
end
|
|
40
74
|
|
|
41
|
-
|
|
75
|
+
def fetch(column)
|
|
76
|
+
if index = @column_indexes[column]
|
|
77
|
+
@row[index]
|
|
78
|
+
elsif block_given?
|
|
79
|
+
yield
|
|
80
|
+
else
|
|
81
|
+
raise KeyError, "key not found: #{column.inspect}"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def [](column)
|
|
86
|
+
if index = @column_indexes[column]
|
|
87
|
+
@row[index]
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def to_h
|
|
92
|
+
@column_indexes.transform_values { |index| @row[index] }
|
|
93
|
+
end
|
|
94
|
+
alias_method :to_hash, :to_h
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
attr_reader :columns, :rows, :affected_rows
|
|
98
|
+
|
|
99
|
+
def self.empty(async: false, affected_rows: nil) # :nodoc:
|
|
42
100
|
if async
|
|
43
|
-
|
|
101
|
+
FutureResult.wrap(new(EMPTY_ARRAY, EMPTY_ARRAY, EMPTY_HASH, affected_rows: affected_rows)).freeze
|
|
44
102
|
else
|
|
45
|
-
|
|
103
|
+
new(EMPTY_ARRAY, EMPTY_ARRAY, EMPTY_HASH, affected_rows: affected_rows).freeze
|
|
46
104
|
end
|
|
47
105
|
end
|
|
48
106
|
|
|
49
|
-
def initialize(columns, rows, column_types =
|
|
50
|
-
|
|
107
|
+
def initialize(columns, rows, column_types = nil, affected_rows: nil)
|
|
108
|
+
# We freeze the strings to prevent them getting duped when
|
|
109
|
+
# used as keys in ActiveRecord::Base's @attributes hash
|
|
110
|
+
@columns = columns.each(&:-@).freeze
|
|
51
111
|
@rows = rows
|
|
52
112
|
@hash_rows = nil
|
|
53
|
-
@column_types = column_types
|
|
113
|
+
@column_types = column_types.freeze
|
|
114
|
+
@types_hash = nil
|
|
115
|
+
@column_indexes = nil
|
|
116
|
+
@affected_rows = affected_rows
|
|
54
117
|
end
|
|
55
118
|
|
|
56
119
|
# Returns true if this result set includes the column named +name+
|
|
@@ -64,7 +127,9 @@ module ActiveRecord
|
|
|
64
127
|
end
|
|
65
128
|
|
|
66
129
|
# Calls the given block once for each element in row collection, passing
|
|
67
|
-
# row as parameter.
|
|
130
|
+
# row as parameter. Each row is a Hash-like, read only object.
|
|
131
|
+
#
|
|
132
|
+
# To get real hashes, use +.to_a.each+.
|
|
68
133
|
#
|
|
69
134
|
# Returns an +Enumerator+ if no block is given.
|
|
70
135
|
def each(&block)
|
|
@@ -96,6 +161,24 @@ module ActiveRecord
|
|
|
96
161
|
n ? hash_rows.last(n) : hash_rows.last
|
|
97
162
|
end
|
|
98
163
|
|
|
164
|
+
# Returns the +ActiveRecord::Type+ type of all columns.
|
|
165
|
+
# Note that not all database adapters return the result types,
|
|
166
|
+
# so the hash may be empty.
|
|
167
|
+
def column_types
|
|
168
|
+
if @column_types
|
|
169
|
+
@types_hash ||= begin
|
|
170
|
+
types = {}
|
|
171
|
+
@columns.each_with_index do |name, index|
|
|
172
|
+
type = @column_types[index] || Type.default_value
|
|
173
|
+
types[name] = types[index] = type
|
|
174
|
+
end
|
|
175
|
+
types.freeze
|
|
176
|
+
end
|
|
177
|
+
else
|
|
178
|
+
EMPTY_HASH
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
99
182
|
def result # :nodoc:
|
|
100
183
|
self
|
|
101
184
|
end
|
|
@@ -104,7 +187,7 @@ module ActiveRecord
|
|
|
104
187
|
self
|
|
105
188
|
end
|
|
106
189
|
|
|
107
|
-
def cast_values(type_overrides =
|
|
190
|
+
def cast_values(type_overrides = nil) # :nodoc:
|
|
108
191
|
if columns.one?
|
|
109
192
|
# Separated to avoid allocating an array per row
|
|
110
193
|
|
|
@@ -131,71 +214,60 @@ module ActiveRecord
|
|
|
131
214
|
end
|
|
132
215
|
|
|
133
216
|
def initialize_copy(other)
|
|
134
|
-
@
|
|
135
|
-
@rows = rows.dup
|
|
136
|
-
@column_types = column_types.dup
|
|
217
|
+
@rows = rows.dup
|
|
137
218
|
@hash_rows = nil
|
|
138
219
|
end
|
|
139
220
|
|
|
140
221
|
def freeze # :nodoc:
|
|
141
222
|
hash_rows.freeze
|
|
223
|
+
indexed_rows
|
|
224
|
+
column_types
|
|
142
225
|
super
|
|
143
226
|
end
|
|
144
227
|
|
|
228
|
+
def column_indexes # :nodoc:
|
|
229
|
+
@column_indexes ||= begin
|
|
230
|
+
index = 0
|
|
231
|
+
hash = {}
|
|
232
|
+
length = columns.length
|
|
233
|
+
while index < length
|
|
234
|
+
hash[columns[index]] = index
|
|
235
|
+
index += 1
|
|
236
|
+
end
|
|
237
|
+
hash.freeze
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def indexed_rows # :nodoc:
|
|
242
|
+
@indexed_rows ||= begin
|
|
243
|
+
columns = column_indexes
|
|
244
|
+
@rows.map { |row| IndexedRow.new(columns, row) }.freeze
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
145
248
|
private
|
|
146
249
|
def column_type(name, index, type_overrides)
|
|
147
|
-
type_overrides
|
|
148
|
-
|
|
149
|
-
|
|
250
|
+
if type_overrides
|
|
251
|
+
type_overrides.fetch(name) do
|
|
252
|
+
column_type(name, index, nil)
|
|
150
253
|
end
|
|
254
|
+
elsif @column_types
|
|
255
|
+
@column_types[index] || Type.default_value
|
|
256
|
+
else
|
|
257
|
+
Type.default_value
|
|
151
258
|
end
|
|
152
259
|
end
|
|
153
260
|
|
|
154
261
|
def hash_rows
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
length = columns.length
|
|
161
|
-
template = nil
|
|
162
|
-
|
|
163
|
-
@rows.map { |row|
|
|
164
|
-
if template
|
|
165
|
-
# We use transform_values to build subsequent rows from the
|
|
166
|
-
# hash of the first row. This is faster because we avoid any
|
|
167
|
-
# reallocs and in Ruby 2.7+ avoid hashing entirely.
|
|
168
|
-
index = -1
|
|
169
|
-
template.transform_values do
|
|
170
|
-
row[index += 1]
|
|
171
|
-
end
|
|
172
|
-
else
|
|
173
|
-
# In the past we used Hash[columns.zip(row)]
|
|
174
|
-
# though elegant, the verbose way is much more efficient
|
|
175
|
-
# both time and memory wise cause it avoids a big array allocation
|
|
176
|
-
# this method is called a lot and needs to be micro optimised
|
|
177
|
-
hash = {}
|
|
178
|
-
|
|
179
|
-
index = 0
|
|
180
|
-
while index < length
|
|
181
|
-
hash[columns[index]] = row[index]
|
|
182
|
-
index += 1
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# It's possible to select the same column twice, in which case
|
|
186
|
-
# we can't use a template
|
|
187
|
-
template = hash if hash.length == length
|
|
188
|
-
|
|
189
|
-
hash
|
|
190
|
-
end
|
|
191
|
-
}
|
|
192
|
-
end
|
|
262
|
+
# We use transform_values to rows.
|
|
263
|
+
# This is faster because we avoid any reallocs and avoid hashing entirely.
|
|
264
|
+
@hash_rows ||= @rows.map do |row|
|
|
265
|
+
column_indexes.transform_values { |index| row[index] }
|
|
266
|
+
end
|
|
193
267
|
end
|
|
194
268
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
EMPTY_ASYNC = FutureResult.wrap(EMPTY).freeze
|
|
199
|
-
private_constant :EMPTY_ASYNC
|
|
269
|
+
EMPTY_ARRAY = [].freeze
|
|
270
|
+
EMPTY_HASH = {}.freeze
|
|
271
|
+
private_constant :EMPTY_ARRAY, :EMPTY_HASH
|
|
200
272
|
end
|
|
201
273
|
end
|
|
@@ -3,41 +3,64 @@
|
|
|
3
3
|
module ActiveRecord
|
|
4
4
|
# This is a thread locals registry for Active Record. For example:
|
|
5
5
|
#
|
|
6
|
-
# ActiveRecord::RuntimeRegistry.sql_runtime
|
|
6
|
+
# ActiveRecord::RuntimeRegistry.stats.sql_runtime
|
|
7
7
|
#
|
|
8
8
|
# returns the connection handler local to the current unit of execution (either thread of fiber).
|
|
9
9
|
module RuntimeRegistry # :nodoc:
|
|
10
|
-
|
|
10
|
+
class Stats
|
|
11
|
+
attr_accessor :sql_runtime, :async_sql_runtime, :queries_count, :cached_queries_count
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
@sql_runtime = 0.0
|
|
15
|
+
@async_sql_runtime = 0.0
|
|
16
|
+
@queries_count = 0
|
|
17
|
+
@cached_queries_count = 0
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def reset_runtimes
|
|
21
|
+
sql_runtime_was = @sql_runtime
|
|
22
|
+
@sql_runtime = 0.0
|
|
23
|
+
@async_sql_runtime = 0.0
|
|
24
|
+
sql_runtime_was
|
|
25
|
+
end
|
|
11
26
|
|
|
12
|
-
|
|
13
|
-
ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] ||= 0.0
|
|
27
|
+
public alias_method :reset, :initialize
|
|
14
28
|
end
|
|
15
29
|
|
|
16
|
-
|
|
17
|
-
|
|
30
|
+
extend self
|
|
31
|
+
|
|
32
|
+
def call(name, start, finish, id, payload)
|
|
33
|
+
record(
|
|
34
|
+
payload[:name],
|
|
35
|
+
(finish - start) * 1_000.0,
|
|
36
|
+
cached: payload[:cached],
|
|
37
|
+
async: payload[:async],
|
|
38
|
+
lock_wait: payload[:lock_wait],
|
|
39
|
+
)
|
|
18
40
|
end
|
|
19
41
|
|
|
20
|
-
def
|
|
21
|
-
|
|
42
|
+
def record(query_name, runtime, cached: false, async: false, lock_wait: nil)
|
|
43
|
+
stats = self.stats
|
|
44
|
+
|
|
45
|
+
unless query_name == "TRANSACTION" || query_name == "SCHEMA"
|
|
46
|
+
stats.queries_count += 1
|
|
47
|
+
stats.cached_queries_count += 1 if cached
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
if async
|
|
51
|
+
stats.async_sql_runtime += (runtime - lock_wait)
|
|
52
|
+
end
|
|
53
|
+
stats.sql_runtime += runtime
|
|
22
54
|
end
|
|
23
55
|
|
|
24
|
-
def
|
|
25
|
-
ActiveSupport::IsolatedExecutionState[:
|
|
56
|
+
def stats
|
|
57
|
+
ActiveSupport::IsolatedExecutionState[:active_record_runtime] ||= Stats.new
|
|
26
58
|
end
|
|
27
59
|
|
|
28
60
|
def reset
|
|
29
|
-
|
|
30
|
-
self.async_sql_runtime = 0.0
|
|
31
|
-
rt
|
|
61
|
+
stats.reset
|
|
32
62
|
end
|
|
33
63
|
end
|
|
34
64
|
end
|
|
35
65
|
|
|
36
|
-
ActiveSupport::Notifications.monotonic_subscribe("sql.active_record"
|
|
37
|
-
runtime = (finish - start) * 1_000.0
|
|
38
|
-
|
|
39
|
-
if payload[:async]
|
|
40
|
-
ActiveRecord::RuntimeRegistry.async_sql_runtime += (runtime - payload[:lock_wait])
|
|
41
|
-
end
|
|
42
|
-
ActiveRecord::RuntimeRegistry.sql_runtime += runtime
|
|
43
|
-
end
|
|
66
|
+
ActiveSupport::Notifications.monotonic_subscribe("sql.active_record", ActiveRecord::RuntimeRegistry)
|
|
@@ -17,7 +17,7 @@ module ActiveRecord
|
|
|
17
17
|
# sanitize_sql_for_conditions(["name='%s' and group_id='%s'", "foo'bar", 4])
|
|
18
18
|
# # => "name='foo''bar' and group_id='4'"
|
|
19
19
|
#
|
|
20
|
-
# This method will NOT sanitize
|
|
20
|
+
# This method will NOT sanitize an SQL string since it won't contain
|
|
21
21
|
# any conditions in it and will return the string as is.
|
|
22
22
|
#
|
|
23
23
|
# sanitize_sql_for_conditions("name='foo''bar' and group_id='4'")
|
|
@@ -52,7 +52,7 @@ module ActiveRecord
|
|
|
52
52
|
# Post.sanitize_sql_for_assignment({ name: nil, group_id: 4 })
|
|
53
53
|
# # => "`posts`.`name` = NULL, `posts`.`group_id` = 4"
|
|
54
54
|
#
|
|
55
|
-
# This method will NOT sanitize
|
|
55
|
+
# This method will NOT sanitize an SQL string since it won't contain
|
|
56
56
|
# any conditions in it and will return the string as is.
|
|
57
57
|
#
|
|
58
58
|
# sanitize_sql_for_assignment("name=NULL and group_id='4'")
|
|
@@ -85,7 +85,7 @@ module ActiveRecord
|
|
|
85
85
|
if condition.is_a?(Array) && condition.first.to_s.include?("?")
|
|
86
86
|
disallow_raw_sql!(
|
|
87
87
|
[condition.first],
|
|
88
|
-
permit:
|
|
88
|
+
permit: adapter_class.column_name_with_order_matcher
|
|
89
89
|
)
|
|
90
90
|
|
|
91
91
|
# Ensure we aren't dealing with a subclass of String that might
|
|
@@ -105,12 +105,13 @@ module ActiveRecord
|
|
|
105
105
|
# sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
|
|
106
106
|
# # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
|
|
107
107
|
def sanitize_sql_hash_for_assignment(attrs, table)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
108
|
+
with_connection do |c|
|
|
109
|
+
attrs.map do |attr, value|
|
|
110
|
+
type = type_for_attribute(attr)
|
|
111
|
+
value = type.serialize(type.cast(value))
|
|
112
|
+
"#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}"
|
|
113
|
+
end.join(", ")
|
|
114
|
+
end
|
|
114
115
|
end
|
|
115
116
|
|
|
116
117
|
# Sanitizes a +string+ so that it is safe to use within an SQL
|
|
@@ -160,20 +161,28 @@ module ActiveRecord
|
|
|
160
161
|
#
|
|
161
162
|
# sanitize_sql_array(["role = ?", 0])
|
|
162
163
|
# # => "role = '0'"
|
|
164
|
+
#
|
|
165
|
+
# Before using this method, please consider if Arel.sql would be better for your use-case
|
|
163
166
|
def sanitize_sql_array(ary)
|
|
164
167
|
statement, *values = ary
|
|
165
168
|
if values.first.is_a?(Hash) && /:\w+/.match?(statement)
|
|
166
|
-
|
|
169
|
+
with_connection do |c|
|
|
170
|
+
replace_named_bind_variables(c, statement, values.first)
|
|
171
|
+
end
|
|
167
172
|
elsif statement.include?("?")
|
|
168
|
-
|
|
173
|
+
with_connection do |c|
|
|
174
|
+
replace_bind_variables(c, statement, values)
|
|
175
|
+
end
|
|
169
176
|
elsif statement.blank?
|
|
170
177
|
statement
|
|
171
178
|
else
|
|
172
|
-
|
|
179
|
+
with_connection do |c|
|
|
180
|
+
statement % values.collect { |value| c.quote_string(value.to_s) }
|
|
181
|
+
end
|
|
173
182
|
end
|
|
174
183
|
end
|
|
175
184
|
|
|
176
|
-
def disallow_raw_sql!(args, permit:
|
|
185
|
+
def disallow_raw_sql!(args, permit: adapter_class.column_name_matcher) # :nodoc:
|
|
177
186
|
unexpected = nil
|
|
178
187
|
args.each do |arg|
|
|
179
188
|
next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s.strip)
|
|
@@ -193,48 +202,47 @@ module ActiveRecord
|
|
|
193
202
|
end
|
|
194
203
|
|
|
195
204
|
private
|
|
196
|
-
def replace_bind_variables(statement, values)
|
|
205
|
+
def replace_bind_variables(connection, statement, values)
|
|
197
206
|
raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size)
|
|
198
207
|
bound = values.dup
|
|
199
|
-
c = connection
|
|
200
208
|
statement.gsub(/\?/) do
|
|
201
|
-
replace_bind_variable(bound.shift
|
|
209
|
+
replace_bind_variable(connection, bound.shift)
|
|
202
210
|
end
|
|
203
211
|
end
|
|
204
212
|
|
|
205
|
-
def replace_bind_variable(
|
|
213
|
+
def replace_bind_variable(connection, value)
|
|
206
214
|
if ActiveRecord::Relation === value
|
|
207
215
|
value.to_sql
|
|
208
216
|
else
|
|
209
|
-
quote_bound_value(
|
|
217
|
+
quote_bound_value(connection, value)
|
|
210
218
|
end
|
|
211
219
|
end
|
|
212
220
|
|
|
213
|
-
def replace_named_bind_variables(statement, bind_vars)
|
|
221
|
+
def replace_named_bind_variables(connection, statement, bind_vars)
|
|
214
222
|
statement.gsub(/([:\\]?):([a-zA-Z]\w*)/) do |match|
|
|
215
|
-
if $1 == ":" # skip
|
|
223
|
+
if $1 == ":" # skip PostgreSQL casts
|
|
216
224
|
match # return the whole match
|
|
217
225
|
elsif $1 == "\\" # escaped literal colon
|
|
218
226
|
match[1..-1] # return match with escaping backlash char removed
|
|
219
227
|
elsif bind_vars.include?(match = $2.to_sym)
|
|
220
|
-
replace_bind_variable(bind_vars[match])
|
|
228
|
+
replace_bind_variable(connection, bind_vars[match])
|
|
221
229
|
else
|
|
222
230
|
raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
|
|
223
231
|
end
|
|
224
232
|
end
|
|
225
233
|
end
|
|
226
234
|
|
|
227
|
-
def quote_bound_value(
|
|
235
|
+
def quote_bound_value(connection, value)
|
|
228
236
|
if value.respond_to?(:map) && !value.acts_like?(:string)
|
|
229
237
|
values = value.map { |v| v.respond_to?(:id_for_database) ? v.id_for_database : v }
|
|
230
238
|
if values.empty?
|
|
231
|
-
|
|
239
|
+
connection.quote(connection.cast_bound_value(nil))
|
|
232
240
|
else
|
|
233
|
-
values.map! { |v|
|
|
241
|
+
values.map! { |v| connection.quote(connection.cast_bound_value(v)) }.join(",")
|
|
234
242
|
end
|
|
235
243
|
else
|
|
236
244
|
value = value.id_for_database if value.respond_to?(:id_for_database)
|
|
237
|
-
|
|
245
|
+
connection.quote(connection.cast_bound_value(value))
|
|
238
246
|
end
|
|
239
247
|
end
|
|
240
248
|
|
data/lib/active_record/schema.rb
CHANGED
|
@@ -52,14 +52,16 @@ module ActiveRecord
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def define(info, &block) # :nodoc:
|
|
55
|
-
|
|
55
|
+
connection_pool.with_connection do |connection|
|
|
56
|
+
instance_eval(&block)
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
connection_pool.schema_migration.create_table
|
|
59
|
+
if info[:version].present?
|
|
60
|
+
connection.assume_migrated_upto_version(info[:version])
|
|
61
|
+
end
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
connection_pool.internal_metadata.create_table_and_set_flags(connection_pool.migration_context.current_environment)
|
|
64
|
+
end
|
|
63
65
|
end
|
|
64
66
|
end
|
|
65
67
|
|