activerecord 6.0.0.beta1 → 6.0.1.rc1
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 +529 -10
- data/README.rdoc +3 -1
- data/lib/active_record.rb +7 -1
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +4 -3
- data/lib/active_record/associations/association.rb +27 -2
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +5 -2
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
- 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 +5 -6
- data/lib/active_record/associations/collection_proxy.rb +13 -42
- data/lib/active_record/associations/has_many_association.rb +1 -9
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/join_dependency.rb +14 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +21 -7
- data/lib/active_record/associations/preloader.rb +12 -7
- data/lib/active_record/associations/preloader/association.rb +37 -34
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/attribute_methods.rb +3 -53
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +47 -14
- data/lib/active_record/attribute_methods/primary_key.rb +7 -15
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +3 -9
- data/lib/active_record/attribute_methods/write.rb +6 -12
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +21 -7
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/callbacks.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +134 -23
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +105 -70
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
- data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
- data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -35
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +106 -138
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- 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 +66 -5
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +95 -24
- data/lib/active_record/connection_adapters/schema_cache.rb +32 -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 +38 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +73 -118
- data/lib/active_record/connection_handling.rb +40 -17
- data/lib/active_record/core.rb +35 -24
- data/lib/active_record/database_configurations.rb +99 -50
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +21 -16
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +15 -0
- data/lib/active_record/errors.rb +18 -13
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +13 -1
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +3 -4
- data/lib/active_record/log_subscriber.rb +1 -1
- 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 +62 -44
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +72 -63
- data/lib/active_record/model_schema.rb +3 -0
- data/lib/active_record/persistence.rb +212 -19
- data/lib/active_record/querying.rb +18 -14
- data/lib/active_record/railtie.rb +9 -1
- data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
- data/lib/active_record/railties/databases.rake +124 -25
- data/lib/active_record/reflection.rb +18 -32
- data/lib/active_record/relation.rb +185 -35
- data/lib/active_record/relation/calculations.rb +40 -44
- data/lib/active_record/relation/delegation.rb +23 -31
- data/lib/active_record/relation/finder_methods.rb +23 -14
- data/lib/active_record/relation/merger.rb +11 -16
- data/lib/active_record/relation/query_attribute.rb +5 -3
- data/lib/active_record/relation/query_methods.rb +230 -69
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +10 -10
- data/lib/active_record/sanitization.rb +33 -4
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +10 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping.rb +6 -7
- data/lib/active_record/scoping/default.rb +7 -15
- data/lib/active_record/scoping/named.rb +10 -2
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +9 -13
- data/lib/active_record/tasks/database_tasks.rb +109 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
- data/lib/active_record/test_databases.rb +1 -16
- data/lib/active_record/test_fixtures.rb +2 -2
- data/lib/active_record/timestamp.rb +35 -19
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +56 -46
- data/lib/active_record/type_caster/connection.rb +16 -10
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/arel.rb +18 -4
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/nodes/and.rb +1 -1
- data/lib/arel/nodes/case.rb +1 -1
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/select_core.rb +16 -12
- data/lib/arel/nodes/unary.rb +1 -0
- data/lib/arel/nodes/values_list.rb +2 -17
- data/lib/arel/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +7 -2
- data/lib/arel/visitors/dot.rb +7 -2
- data/lib/arel/visitors/ibm_db.rb +13 -0
- data/lib/arel/visitors/informix.rb +6 -0
- data/lib/arel/visitors/mssql.rb +15 -1
- data/lib/arel/visitors/oracle12.rb +4 -5
- data/lib/arel/visitors/postgresql.rb +4 -10
- data/lib/arel/visitors/to_sql.rb +107 -131
- data/lib/arel/visitors/visitor.rb +9 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- 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 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +19 -12
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
data/lib/active_record/base.rb
CHANGED
@@ -95,7 +95,7 @@ module ActiveRecord
|
|
95
95
|
#
|
96
96
|
# private
|
97
97
|
# def delete_parents
|
98
|
-
# self.class.
|
98
|
+
# self.class.delete_by(parent_id: id)
|
99
99
|
# end
|
100
100
|
# end
|
101
101
|
#
|
@@ -324,7 +324,7 @@ module ActiveRecord
|
|
324
324
|
|
325
325
|
private
|
326
326
|
|
327
|
-
def create_or_update(
|
327
|
+
def create_or_update(**)
|
328
328
|
_run_save_callbacks { super }
|
329
329
|
end
|
330
330
|
|
@@ -332,7 +332,7 @@ module ActiveRecord
|
|
332
332
|
_run_create_callbacks { super }
|
333
333
|
end
|
334
334
|
|
335
|
-
def _update_record
|
335
|
+
def _update_record
|
336
336
|
_run_update_callbacks { super }
|
337
337
|
end
|
338
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,7 +206,7 @@ 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
|
@@ -194,7 +215,7 @@ module ActiveRecord
|
|
194
215
|
|
195
216
|
return remove if any?
|
196
217
|
|
197
|
-
elapsed =
|
218
|
+
elapsed = Concurrent.monotonic_time - t0
|
198
219
|
if elapsed >= timeout
|
199
220
|
msg = "could not obtain a connection from the pool within %0.3f seconds (waited %0.3f seconds); all pooled connections were in use" %
|
200
221
|
[timeout, elapsed]
|
@@ -294,23 +315,59 @@ module ActiveRecord
|
|
294
315
|
@frequency = frequency
|
295
316
|
end
|
296
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
|
+
|
297
359
|
def run
|
298
360
|
return unless frequency && frequency > 0
|
299
|
-
|
300
|
-
loop do
|
301
|
-
sleep t
|
302
|
-
p.reap
|
303
|
-
p.flush
|
304
|
-
end
|
305
|
-
}
|
361
|
+
self.class.register_pool(pool, frequency)
|
306
362
|
end
|
307
363
|
end
|
308
364
|
|
309
365
|
include MonitorMixin
|
310
366
|
include QueryCache::ConnectionPoolConfiguration
|
367
|
+
include ConnectionAdapters::AbstractPool
|
311
368
|
|
312
369
|
attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
|
313
|
-
attr_reader :spec, :
|
370
|
+
attr_reader :spec, :size, :reaper
|
314
371
|
|
315
372
|
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
|
316
373
|
# object which describes database connection information (e.g. adapter,
|
@@ -379,7 +436,7 @@ module ActiveRecord
|
|
379
436
|
# #connection can be called any number of times; the connection is
|
380
437
|
# held in a cache keyed by a thread.
|
381
438
|
def connection
|
382
|
-
@thread_cached_conns[connection_cache_key(
|
439
|
+
@thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
|
383
440
|
end
|
384
441
|
|
385
442
|
# Returns true if there is an open connection being used for the current thread.
|
@@ -388,7 +445,7 @@ module ActiveRecord
|
|
388
445
|
# #connection or #with_connection methods. Connections obtained through
|
389
446
|
# #checkout will not be detected by #active_connection?
|
390
447
|
def active_connection?
|
391
|
-
@thread_cached_conns[connection_cache_key(
|
448
|
+
@thread_cached_conns[connection_cache_key(current_thread)]
|
392
449
|
end
|
393
450
|
|
394
451
|
# Signal that the thread is finished with the current connection.
|
@@ -423,6 +480,21 @@ module ActiveRecord
|
|
423
480
|
synchronize { @connections.any? }
|
424
481
|
end
|
425
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
|
+
|
426
498
|
# Disconnects all connections in the pool, and clears the pool.
|
427
499
|
#
|
428
500
|
# Raises:
|
@@ -578,6 +650,7 @@ module ActiveRecord
|
|
578
650
|
# or a thread dies unexpectedly.
|
579
651
|
def reap
|
580
652
|
stale_connections = synchronize do
|
653
|
+
return unless @connections
|
581
654
|
@connections.select do |conn|
|
582
655
|
conn.in_use? && !conn.owner.alive?
|
583
656
|
end.each do |conn|
|
@@ -602,6 +675,7 @@ module ActiveRecord
|
|
602
675
|
return if minimum_idle.nil?
|
603
676
|
|
604
677
|
idle_connections = synchronize do
|
678
|
+
return unless @connections
|
605
679
|
@connections.select do |conn|
|
606
680
|
!conn.in_use? && conn.seconds_idle >= minimum_idle
|
607
681
|
end.each do |conn|
|
@@ -668,6 +742,10 @@ module ActiveRecord
|
|
668
742
|
thread
|
669
743
|
end
|
670
744
|
|
745
|
+
def current_thread
|
746
|
+
@lock_thread || Thread.current
|
747
|
+
end
|
748
|
+
|
671
749
|
# Take control of all existing connections so a "group" action such as
|
672
750
|
# reload/disconnect can be performed safely. It is no longer enough to
|
673
751
|
# wrap it in +synchronize+ because some pool's actions are allowed
|
@@ -686,13 +764,13 @@ module ActiveRecord
|
|
686
764
|
end
|
687
765
|
|
688
766
|
newly_checked_out = []
|
689
|
-
timeout_time =
|
767
|
+
timeout_time = Concurrent.monotonic_time + (@checkout_timeout * 2)
|
690
768
|
|
691
769
|
@available.with_a_bias_for(Thread.current) do
|
692
770
|
loop do
|
693
771
|
synchronize do
|
694
772
|
return if collected_conns.size == @connections.size && @now_connecting == 0
|
695
|
-
remaining_timeout = timeout_time -
|
773
|
+
remaining_timeout = timeout_time - Concurrent.monotonic_time
|
696
774
|
remaining_timeout = 0 if remaining_timeout < 0
|
697
775
|
conn = checkout_for_exclusive_access(remaining_timeout)
|
698
776
|
collected_conns << conn
|
@@ -809,7 +887,7 @@ module ActiveRecord
|
|
809
887
|
|
810
888
|
def new_connection
|
811
889
|
Base.send(spec.adapter_method, spec.config).tap do |conn|
|
812
|
-
conn.
|
890
|
+
conn.check_version
|
813
891
|
end
|
814
892
|
end
|
815
893
|
|
@@ -915,6 +993,16 @@ module ActiveRecord
|
|
915
993
|
# about the model. The model needs to pass a specification name to the handler,
|
916
994
|
# in order to look up the correct connection pool.
|
917
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
|
+
|
918
1006
|
def self.unowned_pool_finalizer(pid_map) # :nodoc:
|
919
1007
|
lambda do |_|
|
920
1008
|
discard_unowned_pools(pid_map)
|
@@ -929,19 +1017,33 @@ module ActiveRecord
|
|
929
1017
|
|
930
1018
|
def initialize
|
931
1019
|
# These caches are keyed by spec.name (ConnectionSpecification#name).
|
932
|
-
@owner_to_pool =
|
933
|
-
# Discard the parent's connection pools immediately; we have no need
|
934
|
-
# of them
|
935
|
-
ConnectionHandler.discard_unowned_pools(h)
|
936
|
-
|
937
|
-
h[k] = Concurrent::Map.new(initial_capacity: 2)
|
938
|
-
end
|
1020
|
+
@owner_to_pool = ConnectionHandler.create_owner_to_pool
|
939
1021
|
|
940
1022
|
# Backup finalizer: if the forked child never needed a pool, the above
|
941
1023
|
# early discard has not occurred
|
942
1024
|
ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
|
943
1025
|
end
|
944
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
|
+
|
945
1047
|
def connection_pool_list
|
946
1048
|
owner_to_pool.values.compact
|
947
1049
|
end
|
@@ -1006,7 +1108,16 @@ module ActiveRecord
|
|
1006
1108
|
# for (not necessarily the current class).
|
1007
1109
|
def retrieve_connection(spec_name) #:nodoc:
|
1008
1110
|
pool = retrieve_connection_pool(spec_name)
|
1009
|
-
|
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
|
+
|
1010
1121
|
pool.connection
|
1011
1122
|
end
|
1012
1123
|
|
@@ -5,20 +5,24 @@ require "active_support/deprecation"
|
|
5
5
|
module ActiveRecord
|
6
6
|
module ConnectionAdapters # :nodoc:
|
7
7
|
module DatabaseLimits
|
8
|
+
def max_identifier_length # :nodoc:
|
9
|
+
64
|
10
|
+
end
|
11
|
+
|
8
12
|
# Returns the maximum length of a table alias.
|
9
13
|
def table_alias_length
|
10
|
-
|
14
|
+
max_identifier_length
|
11
15
|
end
|
12
16
|
|
13
17
|
# Returns the maximum length of a column name.
|
14
18
|
def column_name_length
|
15
|
-
|
19
|
+
max_identifier_length
|
16
20
|
end
|
17
21
|
deprecate :column_name_length
|
18
22
|
|
19
23
|
# Returns the maximum length of a table name.
|
20
24
|
def table_name_length
|
21
|
-
|
25
|
+
max_identifier_length
|
22
26
|
end
|
23
27
|
deprecate :table_name_length
|
24
28
|
|
@@ -33,7 +37,7 @@ module ActiveRecord
|
|
33
37
|
|
34
38
|
# Returns the maximum length of an index name.
|
35
39
|
def index_name_length
|
36
|
-
|
40
|
+
max_identifier_length
|
37
41
|
end
|
38
42
|
|
39
43
|
# Returns the maximum number of columns per table.
|
@@ -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
|
@@ -47,13 +60,8 @@ module ActiveRecord
|
|
47
60
|
arel = arel_from_relation(arel)
|
48
61
|
sql, binds = to_sql_and_binds(arel, binds)
|
49
62
|
|
50
|
-
if
|
51
|
-
preparable = false
|
52
|
-
elsif binds.length > bind_params_length
|
53
|
-
sql, binds = unprepared_statement { to_sql_and_binds(arel) }
|
54
|
-
preparable = false
|
55
|
-
else
|
56
|
-
preparable = visitor.preparable
|
63
|
+
if preparable.nil?
|
64
|
+
preparable = prepared_statements ? visitor.preparable : false
|
57
65
|
end
|
58
66
|
|
59
67
|
if prepared_statements && preparable
|
@@ -123,7 +131,7 @@ module ActiveRecord
|
|
123
131
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
124
132
|
# the executed +sql+ statement.
|
125
133
|
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
|
126
|
-
sql, binds = sql_for_insert(sql, pk,
|
134
|
+
sql, binds = sql_for_insert(sql, pk, binds)
|
127
135
|
exec_query(sql, name, binds)
|
128
136
|
end
|
129
137
|
|
@@ -134,11 +142,6 @@ module ActiveRecord
|
|
134
142
|
exec_query(sql, name, binds)
|
135
143
|
end
|
136
144
|
|
137
|
-
# Executes the truncate statement.
|
138
|
-
def truncate(table_name, name = nil)
|
139
|
-
raise NotImplementedError
|
140
|
-
end
|
141
|
-
|
142
145
|
# Executes update +sql+ statement in the context of this connection using
|
143
146
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
144
147
|
# the executed +sql+ statement.
|
@@ -146,6 +149,10 @@ module ActiveRecord
|
|
146
149
|
exec_query(sql, name, binds)
|
147
150
|
end
|
148
151
|
|
152
|
+
def exec_insert_all(sql, name) # :nodoc:
|
153
|
+
exec_query(sql, name)
|
154
|
+
end
|
155
|
+
|
149
156
|
# Executes an INSERT query and returns the new record's ID
|
150
157
|
#
|
151
158
|
# +id_value+ will be returned unless the value is +nil+, in
|
@@ -173,6 +180,23 @@ module ActiveRecord
|
|
173
180
|
exec_delete(sql, name, binds)
|
174
181
|
end
|
175
182
|
|
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
|
198
|
+
end
|
199
|
+
|
176
200
|
# Runs the given block in a database transaction, and returns the result
|
177
201
|
# of the block.
|
178
202
|
#
|
@@ -333,46 +357,20 @@ module ActiveRecord
|
|
333
357
|
# We keep this method to provide fallback
|
334
358
|
# for databases like sqlite that do not support bulk inserts.
|
335
359
|
def insert_fixture(fixture, table_name)
|
336
|
-
fixture
|
337
|
-
|
338
|
-
columns = schema_cache.columns_hash(table_name)
|
339
|
-
binds = fixture.map do |name, value|
|
340
|
-
if column = columns[name]
|
341
|
-
type = lookup_cast_type_from_column(column)
|
342
|
-
Relation::QueryAttribute.new(name, value, type)
|
343
|
-
else
|
344
|
-
raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
table = Arel::Table.new(table_name)
|
349
|
-
|
350
|
-
values = binds.map do |bind|
|
351
|
-
value = with_yaml_fallback(bind.value_for_database)
|
352
|
-
[table[bind.name], value]
|
353
|
-
end
|
354
|
-
|
355
|
-
manager = Arel::InsertManager.new
|
356
|
-
manager.into(table)
|
357
|
-
manager.insert(values)
|
358
|
-
execute manager.to_sql, "Fixture Insert"
|
360
|
+
execute(build_fixture_sql(Array.wrap(fixture), table_name), "Fixture Insert")
|
359
361
|
end
|
360
362
|
|
361
363
|
def insert_fixtures_set(fixture_set, tables_to_delete = [])
|
362
|
-
fixture_inserts = fixture_set
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
transaction(requires_new: true) do
|
373
|
-
total_sql.each do |sql|
|
374
|
-
execute sql, "Fixtures Load"
|
375
|
-
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
|
376
374
|
end
|
377
375
|
end
|
378
376
|
end
|
@@ -396,15 +394,33 @@ module ActiveRecord
|
|
396
394
|
end
|
397
395
|
end
|
398
396
|
|
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
|
406
|
+
end
|
407
|
+
|
399
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
|
+
|
400
416
|
def default_insert_value(column)
|
401
|
-
|
417
|
+
DEFAULT_INSERT_VALUE
|
402
418
|
end
|
403
419
|
|
404
420
|
def build_fixture_sql(fixtures, table_name)
|
405
421
|
columns = schema_cache.columns_hash(table_name)
|
406
422
|
|
407
|
-
|
423
|
+
values_list = fixtures.map do |fixture|
|
408
424
|
fixture = fixture.stringify_keys
|
409
425
|
|
410
426
|
unknown_columns = fixture.keys - columns.keys
|
@@ -415,8 +431,7 @@ module ActiveRecord
|
|
415
431
|
columns.map do |name, column|
|
416
432
|
if fixture.key?(name)
|
417
433
|
type = lookup_cast_type_from_column(column)
|
418
|
-
|
419
|
-
with_yaml_fallback(bind.value_for_database)
|
434
|
+
with_yaml_fallback(type.serialize(fixture[name]))
|
420
435
|
else
|
421
436
|
default_insert_value(column)
|
422
437
|
end
|
@@ -426,12 +441,43 @@ module ActiveRecord
|
|
426
441
|
table = Arel::Table.new(table_name)
|
427
442
|
manager = Arel::InsertManager.new
|
428
443
|
manager.into(table)
|
429
|
-
columns.each_key { |column| manager.columns << table[column] }
|
430
|
-
manager.values = manager.create_values_list(values)
|
431
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)
|
432
460
|
manager.to_sql
|
433
461
|
end
|
434
462
|
|
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)
|
475
|
+
end
|
476
|
+
|
477
|
+
def with_multi_statements
|
478
|
+
yield
|
479
|
+
end
|
480
|
+
|
435
481
|
def combine_multi_statements(total_sql)
|
436
482
|
total_sql.join(";\n")
|
437
483
|
end
|
@@ -445,7 +491,7 @@ module ActiveRecord
|
|
445
491
|
exec_query(sql, name, binds, prepare: true)
|
446
492
|
end
|
447
493
|
|
448
|
-
def sql_for_insert(sql, pk,
|
494
|
+
def sql_for_insert(sql, pk, binds)
|
449
495
|
[sql, binds]
|
450
496
|
end
|
451
497
|
|
@@ -465,17 +511,6 @@ module ActiveRecord
|
|
465
511
|
relation
|
466
512
|
end
|
467
513
|
end
|
468
|
-
|
469
|
-
# Fixture value is quoted by Arel, however scalar values
|
470
|
-
# are not quotable. In this case we want to convert
|
471
|
-
# the column value to YAML.
|
472
|
-
def with_yaml_fallback(value)
|
473
|
-
if value.is_a?(Hash) || value.is_a?(Array)
|
474
|
-
YAML.dump(value)
|
475
|
-
else
|
476
|
-
value
|
477
|
-
end
|
478
|
-
end
|
479
514
|
end
|
480
515
|
end
|
481
516
|
end
|