activerecord 5.2.1.1 → 6.0.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 +4 -4
- data/CHANGELOG.md +738 -445
- data/MIT-LICENSE +3 -1
- data/README.rdoc +4 -2
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +9 -2
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/association_relation.rb +18 -9
- data/lib/active_record/associations.rb +20 -15
- data/lib/active_record/associations/association.rb +69 -20
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +19 -52
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +15 -29
- data/lib/active_record/associations/collection_proxy.rb +19 -48
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +11 -10
- data/lib/active_record/associations/has_many_through_association.rb +42 -25
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +28 -28
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -7
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +39 -31
- data/lib/active_record/associations/preloader/association.rb +38 -36
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods.rb +28 -100
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +114 -38
- data/lib/active_record/attribute_methods/primary_key.rb +15 -22
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +15 -53
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +17 -24
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +27 -13
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +6 -20
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +140 -27
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +22 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +116 -127
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +26 -11
- data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +135 -56
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +189 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +151 -198
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +55 -45
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +9 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -13
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +65 -77
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql/utils.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +172 -74
- data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +131 -143
- data/lib/active_record/connection_handling.rb +155 -26
- data/lib/active_record/core.rb +104 -59
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +38 -7
- data/lib/active_record/errors.rb +30 -16
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +145 -472
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +13 -3
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +68 -16
- data/lib/active_record/internal_metadata.rb +10 -2
- data/lib/active_record/locking/optimistic.rb +5 -6
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +100 -81
- data/lib/active_record/migration/command_recorder.rb +50 -6
- data/lib/active_record/migration/compatibility.rb +91 -64
- data/lib/active_record/model_schema.rb +34 -10
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +233 -28
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +33 -21
- data/lib/active_record/railtie.rb +81 -46
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +196 -46
- data/lib/active_record/reflection.rb +42 -44
- data/lib/active_record/relation.rb +320 -70
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +67 -57
- data/lib/active_record/relation/delegation.rb +48 -35
- data/lib/active_record/relation/finder_methods.rb +30 -30
- data/lib/active_record/relation/merger.rb +19 -25
- data/lib/active_record/relation/predicate_builder.rb +18 -15
- data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -6
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/query_attribute.rb +17 -10
- data/lib/active_record/relation/query_methods.rb +236 -73
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +14 -10
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +32 -40
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +22 -7
- data/lib/active_record/schema_migration.rb +5 -1
- data/lib/active_record/scoping.rb +8 -8
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +21 -15
- data/lib/active_record/statement_cache.rb +32 -5
- data/lib/active_record/store.rb +87 -8
- data/lib/active_record/table_metadata.rb +10 -17
- data/lib/active_record/tasks/database_tasks.rb +195 -26
- data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +57 -66
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type_caster/connection.rb +15 -14
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/arel.rb +58 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +111 -27
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -75,21 +75,7 @@ module ActiveRecord
|
|
75
75
|
# end
|
76
76
|
#
|
77
77
|
# Now, when <tt>Topic#destroy</tt> is run only +destroy_author+ is called. When <tt>Reply#destroy</tt> is
|
78
|
-
# run, both +destroy_author+ and +destroy_readers+ are called.
|
79
|
-
# where the +before_destroy+ method is overridden:
|
80
|
-
#
|
81
|
-
# class Topic < ActiveRecord::Base
|
82
|
-
# def before_destroy() destroy_author end
|
83
|
-
# end
|
84
|
-
#
|
85
|
-
# class Reply < Topic
|
86
|
-
# def before_destroy() destroy_readers end
|
87
|
-
# end
|
88
|
-
#
|
89
|
-
# In that case, <tt>Reply#destroy</tt> would only run +destroy_readers+ and _not_ +destroy_author+.
|
90
|
-
# So, use the callback macros when you want to ensure that a certain callback is called for the entire
|
91
|
-
# hierarchy, and use the regular overwritable methods when you want to leave it up to each descendant
|
92
|
-
# to decide whether they want to call +super+ and trigger the inherited callbacks.
|
78
|
+
# run, both +destroy_author+ and +destroy_readers+ are called.
|
93
79
|
#
|
94
80
|
# *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the
|
95
81
|
# callbacks before specifying the associations. Otherwise, you might trigger the loading of a
|
@@ -109,7 +95,7 @@ module ActiveRecord
|
|
109
95
|
#
|
110
96
|
# private
|
111
97
|
# def delete_parents
|
112
|
-
# self.class.
|
98
|
+
# self.class.delete_by(parent_id: id)
|
113
99
|
# end
|
114
100
|
# end
|
115
101
|
#
|
@@ -142,7 +128,7 @@ module ActiveRecord
|
|
142
128
|
# end
|
143
129
|
# end
|
144
130
|
#
|
145
|
-
# So you specify the object you want messaged on a given callback. When that callback is triggered, the object has
|
131
|
+
# So you specify the object you want to be messaged on a given callback. When that callback is triggered, the object has
|
146
132
|
# a method by the name of the callback messaged. You can make these callbacks more flexible by passing in other
|
147
133
|
# initialization data such as the name of the attribute to work with:
|
148
134
|
#
|
@@ -332,13 +318,13 @@ module ActiveRecord
|
|
332
318
|
_run_touch_callbacks { super }
|
333
319
|
end
|
334
320
|
|
335
|
-
def increment!(
|
321
|
+
def increment!(attribute, by = 1, touch: nil) # :nodoc:
|
336
322
|
touch ? _run_touch_callbacks { super } : super
|
337
323
|
end
|
338
324
|
|
339
325
|
private
|
340
326
|
|
341
|
-
def create_or_update(
|
327
|
+
def create_or_update(**)
|
342
328
|
_run_save_callbacks { super }
|
343
329
|
end
|
344
330
|
|
@@ -346,7 +332,7 @@ module ActiveRecord
|
|
346
332
|
_run_create_callbacks { super }
|
347
333
|
end
|
348
334
|
|
349
|
-
def _update_record
|
335
|
+
def _update_record
|
350
336
|
_run_update_callbacks { super }
|
351
337
|
end
|
352
338
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require "thread"
|
4
4
|
require "concurrent/map"
|
5
5
|
require "monitor"
|
6
|
+
require "weakref"
|
6
7
|
|
7
8
|
module ActiveRecord
|
8
9
|
# Raised when a connection could not be obtained within the connection
|
@@ -19,6 +20,26 @@ module ActiveRecord
|
|
19
20
|
end
|
20
21
|
|
21
22
|
module ConnectionAdapters
|
23
|
+
module AbstractPool # :nodoc:
|
24
|
+
def get_schema_cache(connection)
|
25
|
+
@schema_cache ||= SchemaCache.new(connection)
|
26
|
+
@schema_cache.connection = connection
|
27
|
+
@schema_cache
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_schema_cache(cache)
|
31
|
+
@schema_cache = cache
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class NullPool # :nodoc:
|
36
|
+
include ConnectionAdapters::AbstractPool
|
37
|
+
|
38
|
+
def initialize
|
39
|
+
@schema_cache = nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
22
43
|
# Connection pool base class for managing Active Record database
|
23
44
|
# connections.
|
24
45
|
#
|
@@ -185,14 +206,16 @@ module ActiveRecord
|
|
185
206
|
def wait_poll(timeout)
|
186
207
|
@num_waiting += 1
|
187
208
|
|
188
|
-
t0 =
|
209
|
+
t0 = Concurrent.monotonic_time
|
189
210
|
elapsed = 0
|
190
211
|
loop do
|
191
|
-
|
212
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
213
|
+
@cond.wait(timeout - elapsed)
|
214
|
+
end
|
192
215
|
|
193
216
|
return remove if any?
|
194
217
|
|
195
|
-
elapsed =
|
218
|
+
elapsed = Concurrent.monotonic_time - t0
|
196
219
|
if elapsed >= timeout
|
197
220
|
msg = "could not obtain a connection from the pool within %0.3f seconds (waited %0.3f seconds); all pooled connections were in use" %
|
198
221
|
[timeout, elapsed]
|
@@ -292,23 +315,59 @@ module ActiveRecord
|
|
292
315
|
@frequency = frequency
|
293
316
|
end
|
294
317
|
|
318
|
+
@mutex = Mutex.new
|
319
|
+
@pools = {}
|
320
|
+
@threads = {}
|
321
|
+
|
322
|
+
class << self
|
323
|
+
def register_pool(pool, frequency) # :nodoc:
|
324
|
+
@mutex.synchronize do
|
325
|
+
unless @threads[frequency]&.alive?
|
326
|
+
@threads[frequency] = spawn_thread(frequency)
|
327
|
+
end
|
328
|
+
@pools[frequency] ||= []
|
329
|
+
@pools[frequency] << WeakRef.new(pool)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
private
|
334
|
+
|
335
|
+
def spawn_thread(frequency)
|
336
|
+
Thread.new(frequency) do |t|
|
337
|
+
running = true
|
338
|
+
while running
|
339
|
+
sleep t
|
340
|
+
@mutex.synchronize do
|
341
|
+
@pools[frequency].select!(&:weakref_alive?)
|
342
|
+
@pools[frequency].each do |p|
|
343
|
+
p.reap
|
344
|
+
p.flush
|
345
|
+
rescue WeakRef::RefError
|
346
|
+
end
|
347
|
+
|
348
|
+
if @pools[frequency].empty?
|
349
|
+
@pools.delete(frequency)
|
350
|
+
@threads.delete(frequency)
|
351
|
+
running = false
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
295
359
|
def run
|
296
360
|
return unless frequency && frequency > 0
|
297
|
-
|
298
|
-
loop do
|
299
|
-
sleep t
|
300
|
-
p.reap
|
301
|
-
p.flush
|
302
|
-
end
|
303
|
-
}
|
361
|
+
self.class.register_pool(pool, frequency)
|
304
362
|
end
|
305
363
|
end
|
306
364
|
|
307
365
|
include MonitorMixin
|
308
366
|
include QueryCache::ConnectionPoolConfiguration
|
367
|
+
include ConnectionAdapters::AbstractPool
|
309
368
|
|
310
369
|
attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
|
311
|
-
attr_reader :spec, :
|
370
|
+
attr_reader :spec, :size, :reaper
|
312
371
|
|
313
372
|
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
|
314
373
|
# object which describes database connection information (e.g. adapter,
|
@@ -377,7 +436,7 @@ module ActiveRecord
|
|
377
436
|
# #connection can be called any number of times; the connection is
|
378
437
|
# held in a cache keyed by a thread.
|
379
438
|
def connection
|
380
|
-
@thread_cached_conns[connection_cache_key(
|
439
|
+
@thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
|
381
440
|
end
|
382
441
|
|
383
442
|
# Returns true if there is an open connection being used for the current thread.
|
@@ -386,7 +445,7 @@ module ActiveRecord
|
|
386
445
|
# #connection or #with_connection methods. Connections obtained through
|
387
446
|
# #checkout will not be detected by #active_connection?
|
388
447
|
def active_connection?
|
389
|
-
@thread_cached_conns[connection_cache_key(
|
448
|
+
@thread_cached_conns[connection_cache_key(current_thread)]
|
390
449
|
end
|
391
450
|
|
392
451
|
# Signal that the thread is finished with the current connection.
|
@@ -421,6 +480,21 @@ module ActiveRecord
|
|
421
480
|
synchronize { @connections.any? }
|
422
481
|
end
|
423
482
|
|
483
|
+
# Returns an array containing the connections currently in the pool.
|
484
|
+
# Access to the array does not require synchronization on the pool because
|
485
|
+
# the array is newly created and not retained by the pool.
|
486
|
+
#
|
487
|
+
# However; this method bypasses the ConnectionPool's thread-safe connection
|
488
|
+
# access pattern. A returned connection may be owned by another thread,
|
489
|
+
# unowned, or by happen-stance owned by the calling thread.
|
490
|
+
#
|
491
|
+
# Calling methods on a connection without ownership is subject to the
|
492
|
+
# thread-safety guarantees of the underlying method. Many of the methods
|
493
|
+
# on connection adapter classes are inherently multi-thread unsafe.
|
494
|
+
def connections
|
495
|
+
synchronize { @connections.dup }
|
496
|
+
end
|
497
|
+
|
424
498
|
# Disconnects all connections in the pool, and clears the pool.
|
425
499
|
#
|
426
500
|
# Raises:
|
@@ -576,6 +650,7 @@ module ActiveRecord
|
|
576
650
|
# or a thread dies unexpectedly.
|
577
651
|
def reap
|
578
652
|
stale_connections = synchronize do
|
653
|
+
return unless @connections
|
579
654
|
@connections.select do |conn|
|
580
655
|
conn.in_use? && !conn.owner.alive?
|
581
656
|
end.each do |conn|
|
@@ -600,6 +675,7 @@ module ActiveRecord
|
|
600
675
|
return if minimum_idle.nil?
|
601
676
|
|
602
677
|
idle_connections = synchronize do
|
678
|
+
return unless @connections
|
603
679
|
@connections.select do |conn|
|
604
680
|
!conn.in_use? && conn.seconds_idle >= minimum_idle
|
605
681
|
end.each do |conn|
|
@@ -666,6 +742,10 @@ module ActiveRecord
|
|
666
742
|
thread
|
667
743
|
end
|
668
744
|
|
745
|
+
def current_thread
|
746
|
+
@lock_thread || Thread.current
|
747
|
+
end
|
748
|
+
|
669
749
|
# Take control of all existing connections so a "group" action such as
|
670
750
|
# reload/disconnect can be performed safely. It is no longer enough to
|
671
751
|
# wrap it in +synchronize+ because some pool's actions are allowed
|
@@ -684,13 +764,13 @@ module ActiveRecord
|
|
684
764
|
end
|
685
765
|
|
686
766
|
newly_checked_out = []
|
687
|
-
timeout_time =
|
767
|
+
timeout_time = Concurrent.monotonic_time + (@checkout_timeout * 2)
|
688
768
|
|
689
769
|
@available.with_a_bias_for(Thread.current) do
|
690
770
|
loop do
|
691
771
|
synchronize do
|
692
772
|
return if collected_conns.size == @connections.size && @now_connecting == 0
|
693
|
-
remaining_timeout = timeout_time -
|
773
|
+
remaining_timeout = timeout_time - Concurrent.monotonic_time
|
694
774
|
remaining_timeout = 0 if remaining_timeout < 0
|
695
775
|
conn = checkout_for_exclusive_access(remaining_timeout)
|
696
776
|
collected_conns << conn
|
@@ -729,7 +809,7 @@ module ActiveRecord
|
|
729
809
|
# this block can't be easily moved into attempt_to_checkout_all_existing_connections's
|
730
810
|
# rescue block, because doing so would put it outside of synchronize section, without
|
731
811
|
# being in a critical section thread_report might become inaccurate
|
732
|
-
msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds"
|
812
|
+
msg = +"could not obtain ownership of all database connections in #{checkout_timeout} seconds"
|
733
813
|
|
734
814
|
thread_report = []
|
735
815
|
@connections.each do |conn|
|
@@ -807,7 +887,7 @@ module ActiveRecord
|
|
807
887
|
|
808
888
|
def new_connection
|
809
889
|
Base.send(spec.adapter_method, spec.config).tap do |conn|
|
810
|
-
conn.
|
890
|
+
conn.check_version
|
811
891
|
end
|
812
892
|
end
|
813
893
|
|
@@ -913,6 +993,16 @@ module ActiveRecord
|
|
913
993
|
# about the model. The model needs to pass a specification name to the handler,
|
914
994
|
# in order to look up the correct connection pool.
|
915
995
|
class ConnectionHandler
|
996
|
+
def self.create_owner_to_pool # :nodoc:
|
997
|
+
Concurrent::Map.new(initial_capacity: 2) do |h, k|
|
998
|
+
# Discard the parent's connection pools immediately; we have no need
|
999
|
+
# of them
|
1000
|
+
discard_unowned_pools(h)
|
1001
|
+
|
1002
|
+
h[k] = Concurrent::Map.new(initial_capacity: 2)
|
1003
|
+
end
|
1004
|
+
end
|
1005
|
+
|
916
1006
|
def self.unowned_pool_finalizer(pid_map) # :nodoc:
|
917
1007
|
lambda do |_|
|
918
1008
|
discard_unowned_pools(pid_map)
|
@@ -927,19 +1017,33 @@ module ActiveRecord
|
|
927
1017
|
|
928
1018
|
def initialize
|
929
1019
|
# These caches are keyed by spec.name (ConnectionSpecification#name).
|
930
|
-
@owner_to_pool =
|
931
|
-
# Discard the parent's connection pools immediately; we have no need
|
932
|
-
# of them
|
933
|
-
ConnectionHandler.discard_unowned_pools(h)
|
934
|
-
|
935
|
-
h[k] = Concurrent::Map.new(initial_capacity: 2)
|
936
|
-
end
|
1020
|
+
@owner_to_pool = ConnectionHandler.create_owner_to_pool
|
937
1021
|
|
938
1022
|
# Backup finalizer: if the forked child never needed a pool, the above
|
939
1023
|
# early discard has not occurred
|
940
1024
|
ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
|
941
1025
|
end
|
942
1026
|
|
1027
|
+
def prevent_writes # :nodoc:
|
1028
|
+
Thread.current[:prevent_writes]
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
def prevent_writes=(prevent_writes) # :nodoc:
|
1032
|
+
Thread.current[:prevent_writes] = prevent_writes
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
# Prevent writing to the database regardless of role.
|
1036
|
+
#
|
1037
|
+
# In some cases you may want to prevent writes to the database
|
1038
|
+
# even if you are on a database that can write. `while_preventing_writes`
|
1039
|
+
# will prevent writes to the database for the duration of the block.
|
1040
|
+
def while_preventing_writes(enabled = true)
|
1041
|
+
original, self.prevent_writes = self.prevent_writes, enabled
|
1042
|
+
yield
|
1043
|
+
ensure
|
1044
|
+
self.prevent_writes = original
|
1045
|
+
end
|
1046
|
+
|
943
1047
|
def connection_pool_list
|
944
1048
|
owner_to_pool.values.compact
|
945
1049
|
end
|
@@ -1004,15 +1108,24 @@ module ActiveRecord
|
|
1004
1108
|
# for (not necessarily the current class).
|
1005
1109
|
def retrieve_connection(spec_name) #:nodoc:
|
1006
1110
|
pool = retrieve_connection_pool(spec_name)
|
1007
|
-
|
1111
|
+
|
1112
|
+
unless pool
|
1113
|
+
# multiple database application
|
1114
|
+
if ActiveRecord::Base.connection_handler != ActiveRecord::Base.default_connection_handler
|
1115
|
+
raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found for the '#{ActiveRecord::Base.current_role}' role."
|
1116
|
+
else
|
1117
|
+
raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found."
|
1118
|
+
end
|
1119
|
+
end
|
1120
|
+
|
1008
1121
|
pool.connection
|
1009
1122
|
end
|
1010
1123
|
|
1011
1124
|
# Returns true if a connection that's accessible to this class has
|
1012
1125
|
# already been opened.
|
1013
1126
|
def connected?(spec_name)
|
1014
|
-
|
1015
|
-
|
1127
|
+
pool = retrieve_connection_pool(spec_name)
|
1128
|
+
pool && pool.connected?
|
1016
1129
|
end
|
1017
1130
|
|
1018
1131
|
# Remove the connection for this class. This will close the active
|
@@ -1,22 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/deprecation"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters # :nodoc:
|
5
7
|
module DatabaseLimits
|
8
|
+
def max_identifier_length # :nodoc:
|
9
|
+
64
|
10
|
+
end
|
11
|
+
|
6
12
|
# Returns the maximum length of a table alias.
|
7
13
|
def table_alias_length
|
8
|
-
|
14
|
+
max_identifier_length
|
9
15
|
end
|
10
16
|
|
11
17
|
# Returns the maximum length of a column name.
|
12
18
|
def column_name_length
|
13
|
-
|
19
|
+
max_identifier_length
|
14
20
|
end
|
21
|
+
deprecate :column_name_length
|
15
22
|
|
16
23
|
# Returns the maximum length of a table name.
|
17
24
|
def table_name_length
|
18
|
-
|
25
|
+
max_identifier_length
|
19
26
|
end
|
27
|
+
deprecate :table_name_length
|
20
28
|
|
21
29
|
# Returns the maximum allowed length for an index name. This
|
22
30
|
# limit is enforced by \Rails and is less than or equal to
|
@@ -29,23 +37,26 @@ module ActiveRecord
|
|
29
37
|
|
30
38
|
# Returns the maximum length of an index name.
|
31
39
|
def index_name_length
|
32
|
-
|
40
|
+
max_identifier_length
|
33
41
|
end
|
34
42
|
|
35
43
|
# Returns the maximum number of columns per table.
|
36
44
|
def columns_per_table
|
37
45
|
1024
|
38
46
|
end
|
47
|
+
deprecate :columns_per_table
|
39
48
|
|
40
49
|
# Returns the maximum number of indexes per table.
|
41
50
|
def indexes_per_table
|
42
51
|
16
|
43
52
|
end
|
53
|
+
deprecate :indexes_per_table
|
44
54
|
|
45
55
|
# Returns the maximum number of columns in a multicolumn index.
|
46
56
|
def columns_per_multicolumn_index
|
47
57
|
16
|
48
58
|
end
|
59
|
+
deprecate :columns_per_multicolumn_index
|
49
60
|
|
50
61
|
# Returns the maximum number of elements in an IN (x,y,z) clause.
|
51
62
|
# +nil+ means no limit.
|
@@ -57,11 +68,18 @@ module ActiveRecord
|
|
57
68
|
def sql_query_length
|
58
69
|
1048575
|
59
70
|
end
|
71
|
+
deprecate :sql_query_length
|
60
72
|
|
61
73
|
# Returns maximum number of joins in a single query.
|
62
74
|
def joins_per_query
|
63
75
|
256
|
64
76
|
end
|
77
|
+
deprecate :joins_per_query
|
78
|
+
|
79
|
+
private
|
80
|
+
def bind_params_length
|
81
|
+
65535
|
82
|
+
end
|
65
83
|
end
|
66
84
|
end
|
67
85
|
end
|
@@ -20,9 +20,22 @@ module ActiveRecord
|
|
20
20
|
raise "Passing bind parameters with an arel AST is forbidden. " \
|
21
21
|
"The values must be stored on the AST directly"
|
22
22
|
end
|
23
|
-
|
24
|
-
|
23
|
+
|
24
|
+
if prepared_statements
|
25
|
+
sql, binds = visitor.compile(arel_or_sql_string.ast, collector)
|
26
|
+
|
27
|
+
if binds.length > bind_params_length
|
28
|
+
unprepared_statement do
|
29
|
+
sql, binds = to_sql_and_binds(arel_or_sql_string)
|
30
|
+
visitor.preparable = false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
else
|
34
|
+
sql = visitor.compile(arel_or_sql_string.ast, collector)
|
35
|
+
end
|
36
|
+
[sql.freeze, binds]
|
25
37
|
else
|
38
|
+
visitor.preparable = false if prepared_statements
|
26
39
|
[arel_or_sql_string.dup.freeze, binds]
|
27
40
|
end
|
28
41
|
end
|
@@ -32,11 +45,11 @@ module ActiveRecord
|
|
32
45
|
# can be used to query the database repeatedly.
|
33
46
|
def cacheable_query(klass, arel) # :nodoc:
|
34
47
|
if prepared_statements
|
35
|
-
sql, binds = visitor.
|
48
|
+
sql, binds = visitor.compile(arel.ast, collector)
|
36
49
|
query = klass.query(sql)
|
37
50
|
else
|
38
|
-
collector =
|
39
|
-
parts, binds = visitor.
|
51
|
+
collector = klass.partial_query_collector
|
52
|
+
parts, binds = visitor.compile(arel.ast, collector)
|
40
53
|
query = klass.partial_query(parts)
|
41
54
|
end
|
42
55
|
[query, binds]
|
@@ -46,11 +59,11 @@ module ActiveRecord
|
|
46
59
|
def select_all(arel, name = nil, binds = [], preparable: nil)
|
47
60
|
arel = arel_from_relation(arel)
|
48
61
|
sql, binds = to_sql_and_binds(arel, binds)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
preparable = visitor.preparable
|
62
|
+
|
63
|
+
if preparable.nil?
|
64
|
+
preparable = prepared_statements ? visitor.preparable : false
|
53
65
|
end
|
66
|
+
|
54
67
|
if prepared_statements && preparable
|
55
68
|
select_prepared(sql, name, binds)
|
56
69
|
else
|
@@ -93,6 +106,11 @@ module ActiveRecord
|
|
93
106
|
exec_query(sql, name).rows
|
94
107
|
end
|
95
108
|
|
109
|
+
# Determines whether the SQL statement is a write query.
|
110
|
+
def write_query?(sql)
|
111
|
+
raise NotImplementedError
|
112
|
+
end
|
113
|
+
|
96
114
|
# Executes the SQL statement in the context of this connection and returns
|
97
115
|
# the raw result from the connection adapter.
|
98
116
|
# Note: depending on your database connector, the result returned by this
|
@@ -113,7 +131,7 @@ module ActiveRecord
|
|
113
131
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
114
132
|
# the executed +sql+ statement.
|
115
133
|
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
|
116
|
-
sql, binds = sql_for_insert(sql, pk,
|
134
|
+
sql, binds = sql_for_insert(sql, pk, binds)
|
117
135
|
exec_query(sql, name, binds)
|
118
136
|
end
|
119
137
|
|
@@ -124,11 +142,6 @@ module ActiveRecord
|
|
124
142
|
exec_query(sql, name, binds)
|
125
143
|
end
|
126
144
|
|
127
|
-
# Executes the truncate statement.
|
128
|
-
def truncate(table_name, name = nil)
|
129
|
-
raise NotImplementedError
|
130
|
-
end
|
131
|
-
|
132
145
|
# Executes update +sql+ statement in the context of this connection using
|
133
146
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
134
147
|
# the executed +sql+ statement.
|
@@ -136,6 +149,10 @@ module ActiveRecord
|
|
136
149
|
exec_query(sql, name, binds)
|
137
150
|
end
|
138
151
|
|
152
|
+
def exec_insert_all(sql, name) # :nodoc:
|
153
|
+
exec_query(sql, name)
|
154
|
+
end
|
155
|
+
|
139
156
|
# Executes an INSERT query and returns the new record's ID
|
140
157
|
#
|
141
158
|
# +id_value+ will be returned unless the value is +nil+, in
|
@@ -163,12 +180,22 @@ module ActiveRecord
|
|
163
180
|
exec_delete(sql, name, binds)
|
164
181
|
end
|
165
182
|
|
166
|
-
#
|
167
|
-
|
168
|
-
|
169
|
-
|
183
|
+
# Executes the truncate statement.
|
184
|
+
def truncate(table_name, name = nil)
|
185
|
+
execute(build_truncate_statements(table_name), name)
|
186
|
+
end
|
187
|
+
|
188
|
+
def truncate_tables(*table_names) # :nodoc:
|
189
|
+
return if table_names.empty?
|
190
|
+
|
191
|
+
with_multi_statements do
|
192
|
+
disable_referential_integrity do
|
193
|
+
Array(build_truncate_statements(*table_names)).each do |sql|
|
194
|
+
execute_batch(sql, "Truncate Tables")
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
170
198
|
end
|
171
|
-
deprecate :supports_statement_cache?
|
172
199
|
|
173
200
|
# Runs the given block in a database transaction, and returns the result
|
174
201
|
# of the block.
|
@@ -259,7 +286,9 @@ module ActiveRecord
|
|
259
286
|
|
260
287
|
attr_reader :transaction_manager #:nodoc:
|
261
288
|
|
262
|
-
delegate :within_new_transaction, :open_transactions, :current_transaction, :begin_transaction,
|
289
|
+
delegate :within_new_transaction, :open_transactions, :current_transaction, :begin_transaction,
|
290
|
+
:commit_transaction, :rollback_transaction, :materialize_transactions,
|
291
|
+
:disable_lazy_transactions!, :enable_lazy_transactions!, to: :transaction_manager
|
263
292
|
|
264
293
|
def transaction_open?
|
265
294
|
current_transaction.open?
|
@@ -324,68 +353,30 @@ module ActiveRecord
|
|
324
353
|
|
325
354
|
# Inserts the given fixture into the table. Overridden in adapters that require
|
326
355
|
# something beyond a simple insert (eg. Oracle).
|
327
|
-
# Most of adapters should implement `
|
356
|
+
# Most of adapters should implement `insert_fixtures_set` that leverages bulk SQL insert.
|
328
357
|
# We keep this method to provide fallback
|
329
358
|
# for databases like sqlite that do not support bulk inserts.
|
330
359
|
def insert_fixture(fixture, table_name)
|
331
|
-
fixture
|
332
|
-
|
333
|
-
columns = schema_cache.columns_hash(table_name)
|
334
|
-
binds = fixture.map do |name, value|
|
335
|
-
if column = columns[name]
|
336
|
-
type = lookup_cast_type_from_column(column)
|
337
|
-
Relation::QueryAttribute.new(name, value, type)
|
338
|
-
else
|
339
|
-
raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
|
340
|
-
end
|
341
|
-
end
|
342
|
-
|
343
|
-
table = Arel::Table.new(table_name)
|
344
|
-
|
345
|
-
values = binds.map do |bind|
|
346
|
-
value = with_yaml_fallback(bind.value_for_database)
|
347
|
-
[table[bind.name], value]
|
348
|
-
end
|
349
|
-
|
350
|
-
manager = Arel::InsertManager.new
|
351
|
-
manager.into(table)
|
352
|
-
manager.insert(values)
|
353
|
-
execute manager.to_sql, "Fixture Insert"
|
354
|
-
end
|
355
|
-
|
356
|
-
# Inserts a set of fixtures into the table. Overridden in adapters that require
|
357
|
-
# something beyond a simple insert (eg. Oracle).
|
358
|
-
def insert_fixtures(fixtures, table_name)
|
359
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
360
|
-
`insert_fixtures` is deprecated and will be removed in the next version of Rails.
|
361
|
-
Consider using `insert_fixtures_set` for performance improvement.
|
362
|
-
MSG
|
363
|
-
return if fixtures.empty?
|
364
|
-
|
365
|
-
execute(build_fixture_sql(fixtures, table_name), "Fixtures Insert")
|
360
|
+
execute(build_fixture_sql(Array.wrap(fixture), table_name), "Fixture Insert")
|
366
361
|
end
|
367
362
|
|
368
363
|
def insert_fixtures_set(fixture_set, tables_to_delete = [])
|
369
|
-
fixture_inserts = fixture_set
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
transaction(requires_new: true) do
|
380
|
-
total_sql.each do |sql|
|
381
|
-
execute sql, "Fixtures Load"
|
382
|
-
yield if block_given?
|
364
|
+
fixture_inserts = build_fixture_statements(fixture_set)
|
365
|
+
table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name(table)}" }
|
366
|
+
total_sql = Array(combine_multi_statements(table_deletes + fixture_inserts))
|
367
|
+
|
368
|
+
with_multi_statements do
|
369
|
+
disable_referential_integrity do
|
370
|
+
transaction(requires_new: true) do
|
371
|
+
total_sql.each do |sql|
|
372
|
+
execute_batch(sql, "Fixtures Load")
|
373
|
+
end
|
383
374
|
end
|
384
375
|
end
|
385
376
|
end
|
386
377
|
end
|
387
378
|
|
388
|
-
def empty_insert_statement_value
|
379
|
+
def empty_insert_statement_value(primary_key = nil)
|
389
380
|
"DEFAULT VALUES"
|
390
381
|
end
|
391
382
|
|
@@ -403,25 +394,33 @@ module ActiveRecord
|
|
403
394
|
end
|
404
395
|
end
|
405
396
|
|
406
|
-
#
|
407
|
-
#
|
408
|
-
#
|
409
|
-
def
|
410
|
-
|
411
|
-
|
412
|
-
|
397
|
+
# Fixture value is quoted by Arel, however scalar values
|
398
|
+
# are not quotable. In this case we want to convert
|
399
|
+
# the column value to YAML.
|
400
|
+
def with_yaml_fallback(value) # :nodoc:
|
401
|
+
if value.is_a?(Hash) || value.is_a?(Array)
|
402
|
+
YAML.dump(value)
|
403
|
+
else
|
404
|
+
value
|
405
|
+
end
|
413
406
|
end
|
414
|
-
alias join_to_delete join_to_update
|
415
407
|
|
416
408
|
private
|
409
|
+
def execute_batch(sql, name = nil)
|
410
|
+
execute(sql, name)
|
411
|
+
end
|
412
|
+
|
413
|
+
DEFAULT_INSERT_VALUE = Arel.sql("DEFAULT").freeze
|
414
|
+
private_constant :DEFAULT_INSERT_VALUE
|
415
|
+
|
417
416
|
def default_insert_value(column)
|
418
|
-
|
417
|
+
DEFAULT_INSERT_VALUE
|
419
418
|
end
|
420
419
|
|
421
420
|
def build_fixture_sql(fixtures, table_name)
|
422
421
|
columns = schema_cache.columns_hash(table_name)
|
423
422
|
|
424
|
-
|
423
|
+
values_list = fixtures.map do |fixture|
|
425
424
|
fixture = fixture.stringify_keys
|
426
425
|
|
427
426
|
unknown_columns = fixture.keys - columns.keys
|
@@ -432,8 +431,7 @@ module ActiveRecord
|
|
432
431
|
columns.map do |name, column|
|
433
432
|
if fixture.key?(name)
|
434
433
|
type = lookup_cast_type_from_column(column)
|
435
|
-
|
436
|
-
with_yaml_fallback(bind.value_for_database)
|
434
|
+
with_yaml_fallback(type.serialize(fixture[name]))
|
437
435
|
else
|
438
436
|
default_insert_value(column)
|
439
437
|
end
|
@@ -443,21 +441,45 @@ module ActiveRecord
|
|
443
441
|
table = Arel::Table.new(table_name)
|
444
442
|
manager = Arel::InsertManager.new
|
445
443
|
manager.into(table)
|
446
|
-
columns.each_key { |column| manager.columns << table[column] }
|
447
|
-
manager.values = manager.create_values_list(values)
|
448
444
|
|
445
|
+
if values_list.size == 1
|
446
|
+
values = values_list.shift
|
447
|
+
new_values = []
|
448
|
+
columns.each_key.with_index { |column, i|
|
449
|
+
unless values[i].equal?(DEFAULT_INSERT_VALUE)
|
450
|
+
new_values << values[i]
|
451
|
+
manager.columns << table[column]
|
452
|
+
end
|
453
|
+
}
|
454
|
+
values_list << new_values
|
455
|
+
else
|
456
|
+
columns.each_key { |column| manager.columns << table[column] }
|
457
|
+
end
|
458
|
+
|
459
|
+
manager.values = manager.create_values_list(values_list)
|
449
460
|
manager.to_sql
|
450
461
|
end
|
451
462
|
|
452
|
-
def
|
453
|
-
|
463
|
+
def build_fixture_statements(fixture_set)
|
464
|
+
fixture_set.map do |table_name, fixtures|
|
465
|
+
next if fixtures.empty?
|
466
|
+
build_fixture_sql(fixtures, table_name)
|
467
|
+
end.compact
|
468
|
+
end
|
469
|
+
|
470
|
+
def build_truncate_statements(*table_names)
|
471
|
+
truncate_tables = table_names.map do |table_name|
|
472
|
+
"TRUNCATE TABLE #{quote_table_name(table_name)}"
|
473
|
+
end
|
474
|
+
combine_multi_statements(truncate_tables)
|
454
475
|
end
|
455
476
|
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
477
|
+
def with_multi_statements
|
478
|
+
yield
|
479
|
+
end
|
480
|
+
|
481
|
+
def combine_multi_statements(total_sql)
|
482
|
+
total_sql.join(";\n")
|
461
483
|
end
|
462
484
|
|
463
485
|
# Returns an ActiveRecord::Result instance.
|
@@ -469,7 +491,7 @@ module ActiveRecord
|
|
469
491
|
exec_query(sql, name, binds, prepare: true)
|
470
492
|
end
|
471
493
|
|
472
|
-
def sql_for_insert(sql, pk,
|
494
|
+
def sql_for_insert(sql, pk, binds)
|
473
495
|
[sql, binds]
|
474
496
|
end
|
475
497
|
|
@@ -489,39 +511,6 @@ module ActiveRecord
|
|
489
511
|
relation
|
490
512
|
end
|
491
513
|
end
|
492
|
-
|
493
|
-
# Fixture value is quoted by Arel, however scalar values
|
494
|
-
# are not quotable. In this case we want to convert
|
495
|
-
# the column value to YAML.
|
496
|
-
def with_yaml_fallback(value)
|
497
|
-
if value.is_a?(Hash) || value.is_a?(Array)
|
498
|
-
YAML.dump(value)
|
499
|
-
else
|
500
|
-
value
|
501
|
-
end
|
502
|
-
end
|
503
|
-
|
504
|
-
class PartialQueryCollector
|
505
|
-
def initialize
|
506
|
-
@parts = []
|
507
|
-
@binds = []
|
508
|
-
end
|
509
|
-
|
510
|
-
def <<(str)
|
511
|
-
@parts << str
|
512
|
-
self
|
513
|
-
end
|
514
|
-
|
515
|
-
def add_bind(obj)
|
516
|
-
@binds << obj
|
517
|
-
@parts << Arel::Nodes::BindParam.new(1)
|
518
|
-
self
|
519
|
-
end
|
520
|
-
|
521
|
-
def value
|
522
|
-
[@parts, @binds]
|
523
|
-
end
|
524
|
-
end
|
525
514
|
end
|
526
515
|
end
|
527
516
|
end
|