activerecord 4.1.16 → 4.2.11.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1162 -1801
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +83 -38
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +63 -27
- data/lib/active_record/associations/collection_proxy.rb +29 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +26 -13
- data/lib/active_record/associations/preloader/association.rb +14 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +36 -26
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +5 -12
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_methods.rb +56 -94
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +19 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -39
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -11
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +55 -69
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +71 -46
- data/lib/active_record/model_schema.rb +52 -58
- data/lib/active_record/nested_attributes.rb +5 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +46 -26
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +18 -11
- data/lib/active_record/railties/databases.rake +50 -51
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +273 -114
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/finder_methods.rb +70 -47
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
- data/lib/active_record/relation/predicate_builder.rb +16 -8
- data/lib/active_record/relation/query_methods.rb +114 -65
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +57 -25
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +20 -11
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +53 -27
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +62 -0
- data/lib/active_record/type/string.rb +40 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +5 -3
- data/lib/active_record/validations/uniqueness.rb +25 -29
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +4 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +66 -11
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -2,6 +2,7 @@ require 'thread'
|
|
2
2
|
require 'thread_safe'
|
3
3
|
require 'monitor'
|
4
4
|
require 'set'
|
5
|
+
require 'active_support/core_ext/string/filters'
|
5
6
|
|
6
7
|
module ActiveRecord
|
7
8
|
# Raised when a connection could not be obtained within the connection
|
@@ -58,13 +59,11 @@ module ActiveRecord
|
|
58
59
|
# * +checkout_timeout+: number of seconds to block and wait for a connection
|
59
60
|
# before giving up and raising a timeout error (default 5 seconds).
|
60
61
|
# * +reaping_frequency+: frequency in seconds to periodically run the
|
61
|
-
# Reaper, which attempts to find and
|
62
|
-
# occur if a programmer forgets to close a
|
63
|
-
# thread or a thread dies unexpectedly.
|
64
|
-
#
|
65
|
-
#
|
66
|
-
# after which the Reaper will consider a connection reapable. (default
|
67
|
-
# 5 seconds).
|
62
|
+
# Reaper, which attempts to find and recover connections from dead
|
63
|
+
# threads, which can occur if a programmer forgets to close a
|
64
|
+
# connection at the end of a thread or a thread dies unexpectedly.
|
65
|
+
# Regardless of this setting, the Reaper will be invoked before every
|
66
|
+
# blocking wait. (Default nil, which means don't schedule the Reaper).
|
68
67
|
class ConnectionPool
|
69
68
|
# Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool
|
70
69
|
# with which it shares a Monitor. But could be a generic Queue.
|
@@ -222,7 +221,7 @@ module ActiveRecord
|
|
222
221
|
|
223
222
|
include MonitorMixin
|
224
223
|
|
225
|
-
attr_accessor :automatic_reconnect, :checkout_timeout
|
224
|
+
attr_accessor :automatic_reconnect, :checkout_timeout
|
226
225
|
attr_reader :spec, :connections, :size, :reaper
|
227
226
|
|
228
227
|
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
|
@@ -237,7 +236,6 @@ module ActiveRecord
|
|
237
236
|
@spec = spec
|
238
237
|
|
239
238
|
@checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
|
240
|
-
@dead_connection_timeout = (spec.config[:dead_connection_timeout] && spec.config[:dead_connection_timeout].to_f) || 5
|
241
239
|
@reaper = Reaper.new(self, (spec.config[:reaping_frequency] && spec.config[:reaping_frequency].to_f))
|
242
240
|
@reaper.run
|
243
241
|
|
@@ -361,11 +359,13 @@ module ActiveRecord
|
|
361
359
|
# calling +checkout+ on this pool.
|
362
360
|
def checkin(conn)
|
363
361
|
synchronize do
|
364
|
-
conn.
|
362
|
+
owner = conn.owner
|
363
|
+
|
364
|
+
conn._run_checkin_callbacks do
|
365
365
|
conn.expire
|
366
366
|
end
|
367
367
|
|
368
|
-
release conn
|
368
|
+
release conn, owner
|
369
369
|
|
370
370
|
@available.add conn
|
371
371
|
end
|
@@ -378,22 +378,28 @@ module ActiveRecord
|
|
378
378
|
@connections.delete conn
|
379
379
|
@available.delete conn
|
380
380
|
|
381
|
-
|
382
|
-
# from the reserved hash will be a little easier.
|
383
|
-
release conn
|
381
|
+
release conn, conn.owner
|
384
382
|
|
385
383
|
@available.add checkout_new_connection if @available.any_waiting?
|
386
384
|
end
|
387
385
|
end
|
388
386
|
|
389
|
-
#
|
390
|
-
#
|
387
|
+
# Recover lost connections for the pool. A lost connection can occur if
|
388
|
+
# a programmer forgets to checkin a connection at the end of a thread
|
391
389
|
# or a thread dies unexpectedly.
|
392
390
|
def reap
|
393
|
-
synchronize do
|
394
|
-
|
395
|
-
|
396
|
-
|
391
|
+
stale_connections = synchronize do
|
392
|
+
@connections.select do |conn|
|
393
|
+
conn.in_use? && !conn.owner.alive?
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
stale_connections.each do |conn|
|
398
|
+
synchronize do
|
399
|
+
if conn.active?
|
400
|
+
conn.reset!
|
401
|
+
checkin conn
|
402
|
+
else
|
397
403
|
remove conn
|
398
404
|
end
|
399
405
|
end
|
@@ -415,20 +421,17 @@ module ActiveRecord
|
|
415
421
|
elsif @connections.size < @size
|
416
422
|
checkout_new_connection
|
417
423
|
else
|
424
|
+
reap
|
418
425
|
@available.poll(@checkout_timeout)
|
419
426
|
end
|
420
427
|
end
|
421
428
|
|
422
|
-
def release(conn)
|
423
|
-
thread_id =
|
424
|
-
current_connection_id
|
425
|
-
else
|
426
|
-
@reserved_connections.keys.find { |k|
|
427
|
-
@reserved_connections[k] == conn
|
428
|
-
}
|
429
|
-
end
|
429
|
+
def release(conn, owner)
|
430
|
+
thread_id = owner.object_id
|
430
431
|
|
431
|
-
@reserved_connections
|
432
|
+
if @reserved_connections[thread_id] == conn
|
433
|
+
@reserved_connections.delete thread_id
|
434
|
+
end
|
432
435
|
end
|
433
436
|
|
434
437
|
def new_connection
|
@@ -449,10 +452,14 @@ module ActiveRecord
|
|
449
452
|
end
|
450
453
|
|
451
454
|
def checkout_and_verify(c)
|
452
|
-
c.
|
455
|
+
c._run_checkout_callbacks do
|
453
456
|
c.verify!
|
454
457
|
end
|
455
458
|
c
|
459
|
+
rescue
|
460
|
+
remove c
|
461
|
+
c.disconnect!
|
462
|
+
raise
|
456
463
|
end
|
457
464
|
end
|
458
465
|
|
@@ -462,23 +469,44 @@ module ActiveRecord
|
|
462
469
|
#
|
463
470
|
# For example, suppose that you have 5 models, with the following hierarchy:
|
464
471
|
#
|
465
|
-
#
|
466
|
-
#
|
467
|
-
#
|
468
|
-
#
|
469
|
-
#
|
470
|
-
#
|
471
|
-
#
|
472
|
+
# class Author < ActiveRecord::Base
|
473
|
+
# end
|
474
|
+
#
|
475
|
+
# class BankAccount < ActiveRecord::Base
|
476
|
+
# end
|
477
|
+
#
|
478
|
+
# class Book < ActiveRecord::Base
|
479
|
+
# establish_connection "library_db"
|
480
|
+
# end
|
481
|
+
#
|
482
|
+
# class ScaryBook < Book
|
483
|
+
# end
|
484
|
+
#
|
485
|
+
# class GoodBook < Book
|
486
|
+
# end
|
472
487
|
#
|
473
|
-
#
|
474
|
-
# than the default database). Then Book, ScaryBook and GoodBook will all use
|
475
|
-
# the same connection pool. Likewise, Author and BankAccount will use the
|
476
|
-
# same connection pool. However, the connection pool used by Author/BankAccount
|
477
|
-
# is not the same as the one used by Book/ScaryBook/GoodBook.
|
488
|
+
# And a database.yml that looked like this:
|
478
489
|
#
|
479
|
-
#
|
480
|
-
#
|
481
|
-
#
|
490
|
+
# development:
|
491
|
+
# database: my_application
|
492
|
+
# host: localhost
|
493
|
+
#
|
494
|
+
# library_db:
|
495
|
+
# database: library
|
496
|
+
# host: some.library.org
|
497
|
+
#
|
498
|
+
# Your primary database in the development environment is "my_application"
|
499
|
+
# but the Book model connects to a separate database called "library_db"
|
500
|
+
# (this can even be a database on a different machine).
|
501
|
+
#
|
502
|
+
# Book, ScaryBook and GoodBook will all use the same connection pool to
|
503
|
+
# "library_db" while Author, BankAccount, and any other models you create
|
504
|
+
# will use the default connection pool to "my_application".
|
505
|
+
#
|
506
|
+
# The various connection pools are managed by a single instance of
|
507
|
+
# ConnectionHandler accessible via ActiveRecord::Base.connection_handler.
|
508
|
+
# All Active Record models use this handler to determine the connection pool that they
|
509
|
+
# should use.
|
482
510
|
class ConnectionHandler
|
483
511
|
def initialize
|
484
512
|
# These caches are keyed by klass.name, NOT klass. Keying them by klass
|
@@ -497,10 +525,11 @@ module ActiveRecord
|
|
497
525
|
end
|
498
526
|
|
499
527
|
def connection_pools
|
500
|
-
ActiveSupport::Deprecation.warn(
|
501
|
-
|
502
|
-
|
503
|
-
|
528
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
529
|
+
In the next release, this will return the same as `#connection_pool_list`.
|
530
|
+
(An array of pools, rather than a hash mapping specs to pools.)
|
531
|
+
MSG
|
532
|
+
|
504
533
|
Hash[connection_pool_list.map { |pool| [pool.spec, pool] }]
|
505
534
|
end
|
506
535
|
|
@@ -538,7 +567,10 @@ module ActiveRecord
|
|
538
567
|
# for (not necessarily the current class).
|
539
568
|
def retrieve_connection(klass) #:nodoc:
|
540
569
|
pool = retrieve_connection_pool(klass)
|
541
|
-
|
570
|
+
raise ConnectionNotEstablished, "No connection pool for #{klass}" unless pool
|
571
|
+
conn = pool.connection
|
572
|
+
raise ConnectionNotEstablished, "No connection for #{klass} in connection pool" unless conn
|
573
|
+
conn
|
542
574
|
end
|
543
575
|
|
544
576
|
# Returns true if a connection that's accessible to this class has
|
@@ -605,7 +637,7 @@ module ActiveRecord
|
|
605
637
|
end
|
606
638
|
|
607
639
|
def pool_from_any_process_for(owner)
|
608
|
-
owner_to_pool = @owner_to_pool.values.find { |v| v[owner.name] }
|
640
|
+
owner_to_pool = @owner_to_pool.values.reverse.find { |v| v[owner.name] }
|
609
641
|
owner_to_pool && owner_to_pool[owner.name]
|
610
642
|
end
|
611
643
|
end
|
@@ -616,7 +648,7 @@ module ActiveRecord
|
|
616
648
|
end
|
617
649
|
|
618
650
|
def call(env)
|
619
|
-
testing = env
|
651
|
+
testing = env['rack.test']
|
620
652
|
|
621
653
|
response = @app.call(env)
|
622
654
|
response[2] = ::Rack::BodyProxy.new(response[2]) do
|
@@ -9,15 +9,23 @@ module ActiveRecord
|
|
9
9
|
# Converts an arel AST to SQL
|
10
10
|
def to_sql(arel, binds = [])
|
11
11
|
if arel.respond_to?(:ast)
|
12
|
-
|
13
|
-
|
14
|
-
quote(*binds.shift.reverse)
|
15
|
-
end
|
12
|
+
collected = visitor.accept(arel.ast, collector)
|
13
|
+
collected.compile(binds.dup, self)
|
16
14
|
else
|
17
15
|
arel
|
18
16
|
end
|
19
17
|
end
|
20
18
|
|
19
|
+
# This is used in the StatementCache object. It returns an object that
|
20
|
+
# can be used to query the database repeatedly.
|
21
|
+
def cacheable_query(arel) # :nodoc:
|
22
|
+
if prepared_statements
|
23
|
+
ActiveRecord::StatementCache.query visitor, arel.ast
|
24
|
+
else
|
25
|
+
ActiveRecord::StatementCache.partial_query visitor, arel.ast, collector
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
21
29
|
# Returns an ActiveRecord::Result instance.
|
22
30
|
def select_all(arel, name = nil, binds = [])
|
23
31
|
arel, binds = binds_from_relation arel, binds
|
@@ -75,6 +83,11 @@ module ActiveRecord
|
|
75
83
|
exec_query(sql, name, binds)
|
76
84
|
end
|
77
85
|
|
86
|
+
# Executes the truncate statement.
|
87
|
+
def truncate(table_name, name = nil)
|
88
|
+
raise NotImplementedError
|
89
|
+
end
|
90
|
+
|
78
91
|
# Executes update +sql+ statement in the context of this connection using
|
79
92
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
80
93
|
# the executed +sql+ statement.
|
@@ -185,7 +198,7 @@ module ActiveRecord
|
|
185
198
|
# * You are creating a nested (savepoint) transaction
|
186
199
|
#
|
187
200
|
# The mysql, mysql2 and postgresql adapters support setting the transaction
|
188
|
-
# isolation level. However, support is disabled for
|
201
|
+
# isolation level. However, support is disabled for MySQL versions below 5,
|
189
202
|
# because they are affected by a bug[http://bugs.mysql.com/bug.php?id=39170]
|
190
203
|
# which means the isolation level gets persisted outside the transaction.
|
191
204
|
def transaction(options = {})
|
@@ -195,58 +208,34 @@ module ActiveRecord
|
|
195
208
|
if options[:isolation]
|
196
209
|
raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
|
197
210
|
end
|
198
|
-
|
199
211
|
yield
|
200
212
|
else
|
201
|
-
within_new_transaction(options) { yield }
|
213
|
+
transaction_manager.within_new_transaction(options) { yield }
|
202
214
|
end
|
203
215
|
rescue ActiveRecord::Rollback
|
204
216
|
# rollbacks are silently swallowed
|
205
217
|
end
|
206
218
|
|
207
|
-
|
208
|
-
transaction = begin_transaction(options)
|
209
|
-
yield
|
210
|
-
rescue Exception => error
|
211
|
-
rollback_transaction if transaction
|
212
|
-
raise
|
213
|
-
ensure
|
214
|
-
begin
|
215
|
-
commit_transaction unless error
|
216
|
-
rescue Exception
|
217
|
-
rollback_transaction
|
218
|
-
raise
|
219
|
-
end
|
220
|
-
end
|
219
|
+
attr_reader :transaction_manager #:nodoc:
|
221
220
|
|
222
|
-
|
223
|
-
@transaction
|
224
|
-
end
|
221
|
+
delegate :within_new_transaction, :open_transactions, :current_transaction, :begin_transaction, :commit_transaction, :rollback_transaction, to: :transaction_manager
|
225
222
|
|
226
223
|
def transaction_open?
|
227
|
-
|
228
|
-
end
|
229
|
-
|
230
|
-
def begin_transaction(options = {}) #:nodoc:
|
231
|
-
@transaction = @transaction.begin(options)
|
232
|
-
end
|
233
|
-
|
234
|
-
def commit_transaction #:nodoc:
|
235
|
-
@transaction = @transaction.commit
|
236
|
-
end
|
237
|
-
|
238
|
-
def rollback_transaction #:nodoc:
|
239
|
-
@transaction = @transaction.rollback
|
224
|
+
current_transaction.open?
|
240
225
|
end
|
241
226
|
|
242
227
|
def reset_transaction #:nodoc:
|
243
|
-
@
|
228
|
+
@transaction_manager = TransactionManager.new(self)
|
244
229
|
end
|
245
230
|
|
246
231
|
# Register a record with the current transaction so that its after_commit and after_rollback callbacks
|
247
232
|
# can be called.
|
248
233
|
def add_transaction_record(record)
|
249
|
-
|
234
|
+
current_transaction.add_record(record)
|
235
|
+
end
|
236
|
+
|
237
|
+
def transaction_state
|
238
|
+
current_transaction.state
|
250
239
|
end
|
251
240
|
|
252
241
|
# Begins the transaction (and turns off auto-committing).
|
@@ -273,7 +262,18 @@ module ActiveRecord
|
|
273
262
|
|
274
263
|
# Rolls back the transaction (and turns on auto-committing). Must be
|
275
264
|
# done if the transaction block raises an exception or returns false.
|
276
|
-
def rollback_db_transaction
|
265
|
+
def rollback_db_transaction
|
266
|
+
exec_rollback_db_transaction
|
267
|
+
end
|
268
|
+
|
269
|
+
def exec_rollback_db_transaction() end #:nodoc:
|
270
|
+
|
271
|
+
def rollback_to_savepoint(name = nil)
|
272
|
+
exec_rollback_to_savepoint(name)
|
273
|
+
end
|
274
|
+
|
275
|
+
def exec_rollback_to_savepoint(name = nil) #:nodoc:
|
276
|
+
end
|
277
277
|
|
278
278
|
def default_sequence_name(table, column)
|
279
279
|
nil
|
@@ -287,12 +287,17 @@ module ActiveRecord
|
|
287
287
|
# Inserts the given fixture into the table. Overridden in adapters that require
|
288
288
|
# something beyond a simple insert (eg. Oracle).
|
289
289
|
def insert_fixture(fixture, table_name)
|
290
|
+
fixture = fixture.stringify_keys
|
290
291
|
columns = schema_cache.columns_hash(table_name)
|
291
292
|
|
292
293
|
key_list = []
|
293
294
|
value_list = fixture.map do |name, value|
|
294
|
-
|
295
|
-
|
295
|
+
if column = columns[name]
|
296
|
+
key_list << quote_column_name(name)
|
297
|
+
quote(value, column)
|
298
|
+
else
|
299
|
+
raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
|
300
|
+
end
|
296
301
|
end
|
297
302
|
|
298
303
|
execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
|
@@ -302,10 +307,6 @@ module ActiveRecord
|
|
302
307
|
"DEFAULT VALUES"
|
303
308
|
end
|
304
309
|
|
305
|
-
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
|
306
|
-
"WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
|
307
|
-
end
|
308
|
-
|
309
310
|
# Sanitizes the given LIMIT parameter in order to prevent SQL injection.
|
310
311
|
#
|
311
312
|
# The +limit+ may be anything that can evaluate to a string via #to_s. It
|
@@ -326,8 +327,8 @@ module ActiveRecord
|
|
326
327
|
end
|
327
328
|
|
328
329
|
# The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
|
329
|
-
# on
|
330
|
-
# an UPDATE statement, so in the
|
330
|
+
# on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
|
331
|
+
# an UPDATE statement, so in the MySQL adapters we redefine this to do that.
|
331
332
|
def join_to_update(update, select) #:nodoc:
|
332
333
|
key = update.key
|
333
334
|
subselect = subquery_for(key, select)
|
@@ -352,8 +353,9 @@ module ActiveRecord
|
|
352
353
|
|
353
354
|
# Returns an ActiveRecord::Result instance.
|
354
355
|
def select(sql, name = nil, binds = [])
|
356
|
+
exec_query(sql, name, binds)
|
355
357
|
end
|
356
|
-
|
358
|
+
|
357
359
|
|
358
360
|
# Returns the last auto-generated ID from the affected table.
|
359
361
|
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
@@ -381,7 +383,7 @@ module ActiveRecord
|
|
381
383
|
end
|
382
384
|
|
383
385
|
def binds_from_relation(relation, binds)
|
384
|
-
if relation.is_a?(Relation) && binds.
|
386
|
+
if relation.is_a?(Relation) && binds.empty?
|
385
387
|
relation, binds = relation.arel, relation.bind_values
|
386
388
|
end
|
387
389
|
[relation, binds]
|
@@ -3,7 +3,7 @@ module ActiveRecord
|
|
3
3
|
module QueryCache
|
4
4
|
class << self
|
5
5
|
def included(base) #:nodoc:
|
6
|
-
dirties_query_cache base, :insert, :update, :delete
|
6
|
+
dirties_query_cache base, :insert, :update, :delete, :rollback_to_savepoint, :rollback_db_transaction
|
7
7
|
end
|
8
8
|
|
9
9
|
def dirties_query_cache(base, *method_names)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_support/core_ext/big_decimal/conversions'
|
2
|
+
require "active_support/multibyte/chars"
|
2
3
|
|
3
4
|
module ActiveRecord
|
4
5
|
module ConnectionAdapters # :nodoc:
|
@@ -9,39 +10,11 @@ module ActiveRecord
|
|
9
10
|
# records are quoted as their primary key
|
10
11
|
return value.quoted_id if value.respond_to?(:quoted_id)
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
value = value.to_s
|
15
|
-
return "'#{quote_string(value)}'" unless column
|
16
|
-
|
17
|
-
case column.type
|
18
|
-
when :integer then value.to_i.to_s
|
19
|
-
when :float then value.to_f.to_s
|
20
|
-
else
|
21
|
-
"'#{quote_string(value)}'"
|
22
|
-
end
|
23
|
-
|
24
|
-
when true, false
|
25
|
-
if column && column.type == :integer
|
26
|
-
value ? '1' : '0'
|
27
|
-
else
|
28
|
-
value ? quoted_true : quoted_false
|
29
|
-
end
|
30
|
-
# BigDecimals need to be put in a non-normalized form and quoted.
|
31
|
-
when nil then "NULL"
|
32
|
-
when BigDecimal then value.to_s('F')
|
33
|
-
when Numeric, ActiveSupport::Duration
|
34
|
-
if column.try(:type) == :string
|
35
|
-
quote(value.to_s, column)
|
36
|
-
else
|
37
|
-
value.to_s
|
38
|
-
end
|
39
|
-
when Date, Time then "'#{quoted_date(value)}'"
|
40
|
-
when Symbol then "'#{quote_string(value.to_s)}'"
|
41
|
-
when Class then "'#{value.to_s}'"
|
42
|
-
else
|
43
|
-
"'#{quote_string(YAML.dump(value))}'"
|
13
|
+
if column
|
14
|
+
value = column.cast_type.type_cast_for_database(value)
|
44
15
|
end
|
16
|
+
|
17
|
+
_quote(value)
|
45
18
|
end
|
46
19
|
|
47
20
|
# Cast a +value+ to a type that the database understands. For example,
|
@@ -52,34 +25,14 @@ module ActiveRecord
|
|
52
25
|
return value.id
|
53
26
|
end
|
54
27
|
|
55
|
-
|
56
|
-
|
57
|
-
value = value.to_s
|
58
|
-
return value unless column
|
59
|
-
|
60
|
-
case column.type
|
61
|
-
when :integer then value.to_i
|
62
|
-
when :float then value.to_f
|
63
|
-
else
|
64
|
-
value
|
65
|
-
end
|
66
|
-
|
67
|
-
when true, false
|
68
|
-
if column && column.type == :integer
|
69
|
-
value ? 1 : 0
|
70
|
-
else
|
71
|
-
value ? 't' : 'f'
|
72
|
-
end
|
73
|
-
# BigDecimals need to be put in a non-normalized form and quoted.
|
74
|
-
when nil then nil
|
75
|
-
when BigDecimal then value.to_s('F')
|
76
|
-
when Numeric then value
|
77
|
-
when Date, Time then quoted_date(value)
|
78
|
-
when Symbol then value.to_s
|
79
|
-
else
|
80
|
-
to_type = column ? " to #{column.type}" : ""
|
81
|
-
raise TypeError, "can't cast #{value.class}#{to_type}"
|
28
|
+
if column
|
29
|
+
value = column.cast_type.type_cast_for_database(value)
|
82
30
|
end
|
31
|
+
|
32
|
+
_type_cast(value)
|
33
|
+
rescue TypeError
|
34
|
+
to_type = column ? " to #{column.type}" : ""
|
35
|
+
raise TypeError, "can't cast #{value.class}#{to_type}"
|
83
36
|
end
|
84
37
|
|
85
38
|
# Quotes a string, escaping any ' (single quote) and \ (backslash)
|
@@ -104,7 +57,7 @@ module ActiveRecord
|
|
104
57
|
# This works for mysql and mysql2 where table.column can be used to
|
105
58
|
# resolve ambiguity.
|
106
59
|
#
|
107
|
-
# We override this in the
|
60
|
+
# We override this in the sqlite3 and postgresql adapters to use only
|
108
61
|
# the column name (as per syntax requirements).
|
109
62
|
def quote_table_name_for_assignment(table, attr)
|
110
63
|
quote_table_name("#{table}.#{attr}")
|
@@ -114,10 +67,18 @@ module ActiveRecord
|
|
114
67
|
"'t'"
|
115
68
|
end
|
116
69
|
|
70
|
+
def unquoted_true
|
71
|
+
't'
|
72
|
+
end
|
73
|
+
|
117
74
|
def quoted_false
|
118
75
|
"'f'"
|
119
76
|
end
|
120
77
|
|
78
|
+
def unquoted_false
|
79
|
+
'f'
|
80
|
+
end
|
81
|
+
|
121
82
|
def quoted_date(value)
|
122
83
|
if value.acts_like?(:time)
|
123
84
|
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
|
@@ -129,6 +90,45 @@ module ActiveRecord
|
|
129
90
|
|
130
91
|
value.to_s(:db)
|
131
92
|
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def types_which_need_no_typecasting
|
97
|
+
[nil, Numeric, String]
|
98
|
+
end
|
99
|
+
|
100
|
+
def _quote(value)
|
101
|
+
case value
|
102
|
+
when String, ActiveSupport::Multibyte::Chars, Type::Binary::Data
|
103
|
+
"'#{quote_string(value.to_s)}'"
|
104
|
+
when true then quoted_true
|
105
|
+
when false then quoted_false
|
106
|
+
when nil then "NULL"
|
107
|
+
# BigDecimals need to be put in a non-normalized form and quoted.
|
108
|
+
when BigDecimal then value.to_s('F')
|
109
|
+
when Numeric, ActiveSupport::Duration then value.to_s
|
110
|
+
when Date, Time then "'#{quoted_date(value)}'"
|
111
|
+
when Symbol then "'#{quote_string(value.to_s)}'"
|
112
|
+
when Class then "'#{value}'"
|
113
|
+
else
|
114
|
+
"'#{quote_string(YAML.dump(value))}'"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def _type_cast(value)
|
119
|
+
case value
|
120
|
+
when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
|
121
|
+
value.to_s
|
122
|
+
when true then unquoted_true
|
123
|
+
when false then unquoted_false
|
124
|
+
# BigDecimals need to be put in a non-normalized form and quoted.
|
125
|
+
when BigDecimal then value.to_s('F')
|
126
|
+
when Date, Time then quoted_date(value)
|
127
|
+
when *types_which_need_no_typecasting
|
128
|
+
value
|
129
|
+
else raise TypeError
|
130
|
+
end
|
131
|
+
end
|
132
132
|
end
|
133
133
|
end
|
134
134
|
end
|