activerecord 4.1.0 → 4.2.0
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 +776 -1330
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/association_relation.rb +4 -0
- data/lib/active_record/associations/alias_tracker.rb +14 -13
- data/lib/active_record/associations/association.rb +2 -2
- data/lib/active_record/associations/association_scope.rb +83 -43
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- 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/has_and_belongs_to_many.rb +9 -6
- 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 +66 -29
- data/lib/active_record/associations/collection_proxy.rb +22 -26
- data/lib/active_record/associations/has_many_association.rb +65 -18
- data/lib/active_record/associations/has_many_through_association.rb +55 -27
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +20 -12
- data/lib/active_record/associations/preloader/association.rb +34 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +49 -59
- data/lib/active_record/associations/singular_association.rb +25 -4
- data/lib/active_record/associations/through_association.rb +23 -14
- data/lib/active_record/associations.rb +171 -42
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +18 -10
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +98 -44
- data/lib/active_record/attribute_methods/primary_key.rb +14 -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 +37 -147
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
- data/lib/active_record/attribute_methods/write.rb +14 -21
- data/lib/active_record/attribute_methods.rb +67 -94
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +45 -38
- data/lib/active_record/base.rb +10 -20
- data/lib/active_record/callbacks.rb +7 -7
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
- data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
- data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
- data/lib/active_record/connection_adapters/column.rb +28 -239
- data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
- data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
- 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 +39 -27
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -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 +14 -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 +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -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 +15 -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 +97 -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 -374
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
- 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 +127 -38
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
- data/lib/active_record/connection_handling.rb +3 -3
- data/lib/active_record/core.rb +143 -32
- data/lib/active_record/counter_cache.rb +60 -7
- data/lib/active_record/enum.rb +10 -11
- data/lib/active_record/errors.rb +49 -27
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +56 -70
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +35 -17
- data/lib/active_record/log_subscriber.rb +1 -1
- 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 +52 -49
- data/lib/active_record/model_schema.rb +49 -57
- data/lib/active_record/nested_attributes.rb +7 -7
- data/lib/active_record/null_relation.rb +19 -5
- data/lib/active_record/persistence.rb +50 -31
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +14 -11
- data/lib/active_record/railties/databases.rake +56 -54
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +286 -102
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +39 -31
- data/lib/active_record/relation/delegation.rb +2 -2
- data/lib/active_record/relation/finder_methods.rb +80 -36
- data/lib/active_record/relation/merger.rb +25 -30
- data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +11 -10
- data/lib/active_record/relation/query_methods.rb +141 -55
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +69 -30
- 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 +58 -26
- data/lib/active_record/schema_migration.rb +11 -0
- data/lib/active_record/scoping/default.rb +8 -7
- 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 +19 -10
- data/lib/active_record/tasks/database_tasks.rb +73 -7
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +11 -9
- data/lib/active_record/transactions.rb +37 -21
- 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 +30 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -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 +17 -0
- data/lib/active_record/type/integer.rb +55 -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 +56 -0
- data/lib/active_record/type/string.rb +36 -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 +101 -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 +6 -4
- data/lib/active_record/validations/uniqueness.rb +11 -17
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +3 -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 +4 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +65 -10
- 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
|
@@ -236,8 +235,7 @@ module ActiveRecord
|
|
236
235
|
|
237
236
|
@spec = spec
|
238
237
|
|
239
|
-
@checkout_timeout = spec.config[:checkout_timeout] || 5
|
240
|
-
@dead_connection_timeout = spec.config[:dead_connection_timeout] || 5
|
238
|
+
@checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
|
241
239
|
@reaper = Reaper.new self, spec.config[:reaping_frequency]
|
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
|
368
|
+
release 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.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,15 @@ 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(
|
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(owner)
|
430
|
+
thread_id = owner.object_id
|
430
431
|
|
431
|
-
@reserved_connections.delete thread_id
|
432
|
+
@reserved_connections.delete thread_id
|
432
433
|
end
|
433
434
|
|
434
435
|
def new_connection
|
@@ -449,7 +450,7 @@ module ActiveRecord
|
|
449
450
|
end
|
450
451
|
|
451
452
|
def checkout_and_verify(c)
|
452
|
-
c.
|
453
|
+
c._run_checkout_callbacks do
|
453
454
|
c.verify!
|
454
455
|
end
|
455
456
|
c
|
@@ -462,23 +463,44 @@ module ActiveRecord
|
|
462
463
|
#
|
463
464
|
# For example, suppose that you have 5 models, with the following hierarchy:
|
464
465
|
#
|
465
|
-
#
|
466
|
-
#
|
467
|
-
# | |
|
468
|
-
# | +-- ScaryBook
|
469
|
-
# | +-- GoodBook
|
470
|
-
# +-- Author
|
471
|
-
# +-- BankAccount
|
466
|
+
# class Author < ActiveRecord::Base
|
467
|
+
# end
|
472
468
|
#
|
473
|
-
#
|
474
|
-
#
|
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.
|
469
|
+
# class BankAccount < ActiveRecord::Base
|
470
|
+
# end
|
478
471
|
#
|
479
|
-
#
|
480
|
-
#
|
481
|
-
#
|
472
|
+
# class Book < ActiveRecord::Base
|
473
|
+
# establish_connection "library_db"
|
474
|
+
# end
|
475
|
+
#
|
476
|
+
# class ScaryBook < Book
|
477
|
+
# end
|
478
|
+
#
|
479
|
+
# class GoodBook < Book
|
480
|
+
# end
|
481
|
+
#
|
482
|
+
# And a database.yml that looked like this:
|
483
|
+
#
|
484
|
+
# development:
|
485
|
+
# database: my_application
|
486
|
+
# host: localhost
|
487
|
+
#
|
488
|
+
# library_db:
|
489
|
+
# database: library
|
490
|
+
# host: some.library.org
|
491
|
+
#
|
492
|
+
# Your primary database in the development environment is "my_application"
|
493
|
+
# but the Book model connects to a separate database called "library_db"
|
494
|
+
# (this can even be a database on a different machine).
|
495
|
+
#
|
496
|
+
# Book, ScaryBook and GoodBook will all use the same connection pool to
|
497
|
+
# "library_db" while Author, BankAccount, and any other models you create
|
498
|
+
# will use the default connection pool to "my_application".
|
499
|
+
#
|
500
|
+
# The various connection pools are managed by a single instance of
|
501
|
+
# ConnectionHandler accessible via ActiveRecord::Base.connection_handler.
|
502
|
+
# All Active Record models use this handler to determine the connection pool that they
|
503
|
+
# should use.
|
482
504
|
class ConnectionHandler
|
483
505
|
def initialize
|
484
506
|
# These caches are keyed by klass.name, NOT klass. Keying them by klass
|
@@ -497,10 +519,11 @@ module ActiveRecord
|
|
497
519
|
end
|
498
520
|
|
499
521
|
def connection_pools
|
500
|
-
ActiveSupport::Deprecation.warn(
|
501
|
-
|
502
|
-
|
503
|
-
|
522
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
523
|
+
In the next release, this will return the same as `#connection_pool_list`.
|
524
|
+
(An array of pools, rather than a hash mapping specs to pools.)
|
525
|
+
MSG
|
526
|
+
|
504
527
|
Hash[connection_pool_list.map { |pool| [pool.spec, pool] }]
|
505
528
|
end
|
506
529
|
|
@@ -538,7 +561,10 @@ module ActiveRecord
|
|
538
561
|
# for (not necessarily the current class).
|
539
562
|
def retrieve_connection(klass) #:nodoc:
|
540
563
|
pool = retrieve_connection_pool(klass)
|
541
|
-
|
564
|
+
raise ConnectionNotEstablished, "No connection pool for #{klass}" unless pool
|
565
|
+
conn = pool.connection
|
566
|
+
raise ConnectionNotEstablished, "No connection for #{klass} in connection pool" unless conn
|
567
|
+
conn
|
542
568
|
end
|
543
569
|
|
544
570
|
# Returns true if a connection that's accessible to this class has
|
@@ -616,7 +642,7 @@ module ActiveRecord
|
|
616
642
|
end
|
617
643
|
|
618
644
|
def call(env)
|
619
|
-
testing = env
|
645
|
+
testing = env['rack.test']
|
620
646
|
|
621
647
|
response = @app.call(env)
|
622
648
|
response[2] = ::Rack::BodyProxy.new(response[2]) do
|
@@ -9,25 +9,26 @@ 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
|
|
21
|
-
#
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
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
|
29
26
|
end
|
27
|
+
end
|
30
28
|
|
29
|
+
# Returns an ActiveRecord::Result instance.
|
30
|
+
def select_all(arel, name = nil, binds = [])
|
31
|
+
arel, binds = binds_from_relation arel, binds
|
31
32
|
select(to_sql(arel, binds), name, binds)
|
32
33
|
end
|
33
34
|
|
@@ -47,10 +48,7 @@ module ActiveRecord
|
|
47
48
|
# Returns an array of the values of the first column in a select:
|
48
49
|
# select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
|
49
50
|
def select_values(arel, name = nil)
|
50
|
-
binds = []
|
51
|
-
if arel.is_a?(Relation)
|
52
|
-
arel, binds = arel.arel, arel.bind_values
|
53
|
-
end
|
51
|
+
arel, binds = binds_from_relation arel, []
|
54
52
|
select_rows(to_sql(arel, binds), name, binds).map(&:first)
|
55
53
|
end
|
56
54
|
|
@@ -85,6 +83,11 @@ module ActiveRecord
|
|
85
83
|
exec_query(sql, name, binds)
|
86
84
|
end
|
87
85
|
|
86
|
+
# Executes the truncate statement.
|
87
|
+
def truncate(table_name, name = nil)
|
88
|
+
raise NotImplementedError
|
89
|
+
end
|
90
|
+
|
88
91
|
# Executes update +sql+ statement in the context of this connection using
|
89
92
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
90
93
|
# the executed +sql+ statement.
|
@@ -195,7 +198,7 @@ module ActiveRecord
|
|
195
198
|
# * You are creating a nested (savepoint) transaction
|
196
199
|
#
|
197
200
|
# The mysql, mysql2 and postgresql adapters support setting the transaction
|
198
|
-
# isolation level. However, support is disabled for
|
201
|
+
# isolation level. However, support is disabled for MySQL versions below 5,
|
199
202
|
# because they are affected by a bug[http://bugs.mysql.com/bug.php?id=39170]
|
200
203
|
# which means the isolation level gets persisted outside the transaction.
|
201
204
|
def transaction(options = {})
|
@@ -205,58 +208,30 @@ module ActiveRecord
|
|
205
208
|
if options[:isolation]
|
206
209
|
raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
|
207
210
|
end
|
208
|
-
|
209
211
|
yield
|
210
212
|
else
|
211
|
-
within_new_transaction(options) { yield }
|
213
|
+
transaction_manager.within_new_transaction(options) { yield }
|
212
214
|
end
|
213
215
|
rescue ActiveRecord::Rollback
|
214
216
|
# rollbacks are silently swallowed
|
215
217
|
end
|
216
218
|
|
217
|
-
|
218
|
-
transaction = begin_transaction(options)
|
219
|
-
yield
|
220
|
-
rescue Exception => error
|
221
|
-
rollback_transaction if transaction
|
222
|
-
raise
|
223
|
-
ensure
|
224
|
-
begin
|
225
|
-
commit_transaction unless error
|
226
|
-
rescue Exception
|
227
|
-
rollback_transaction
|
228
|
-
raise
|
229
|
-
end
|
230
|
-
end
|
219
|
+
attr_reader :transaction_manager #:nodoc:
|
231
220
|
|
232
|
-
|
233
|
-
@transaction
|
234
|
-
end
|
221
|
+
delegate :within_new_transaction, :open_transactions, :current_transaction, :begin_transaction, :commit_transaction, :rollback_transaction, to: :transaction_manager
|
235
222
|
|
236
223
|
def transaction_open?
|
237
|
-
|
238
|
-
end
|
239
|
-
|
240
|
-
def begin_transaction(options = {}) #:nodoc:
|
241
|
-
@transaction = @transaction.begin(options)
|
242
|
-
end
|
243
|
-
|
244
|
-
def commit_transaction #:nodoc:
|
245
|
-
@transaction = @transaction.commit
|
246
|
-
end
|
247
|
-
|
248
|
-
def rollback_transaction #:nodoc:
|
249
|
-
@transaction = @transaction.rollback
|
224
|
+
current_transaction.open?
|
250
225
|
end
|
251
226
|
|
252
227
|
def reset_transaction #:nodoc:
|
253
|
-
@
|
228
|
+
@transaction_manager = TransactionManager.new(self)
|
254
229
|
end
|
255
230
|
|
256
231
|
# Register a record with the current transaction so that its after_commit and after_rollback callbacks
|
257
232
|
# can be called.
|
258
233
|
def add_transaction_record(record)
|
259
|
-
|
234
|
+
current_transaction.add_record(record)
|
260
235
|
end
|
261
236
|
|
262
237
|
# Begins the transaction (and turns off auto-committing).
|
@@ -312,10 +287,6 @@ module ActiveRecord
|
|
312
287
|
"DEFAULT VALUES"
|
313
288
|
end
|
314
289
|
|
315
|
-
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
|
316
|
-
"WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
|
317
|
-
end
|
318
|
-
|
319
290
|
# Sanitizes the given LIMIT parameter in order to prevent SQL injection.
|
320
291
|
#
|
321
292
|
# The +limit+ may be anything that can evaluate to a string via #to_s. It
|
@@ -328,7 +299,7 @@ module ActiveRecord
|
|
328
299
|
def sanitize_limit(limit)
|
329
300
|
if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
|
330
301
|
limit
|
331
|
-
elsif limit.to_s
|
302
|
+
elsif limit.to_s.include?(',')
|
332
303
|
Arel.sql limit.to_s.split(',').map{ |i| Integer(i) }.join(',')
|
333
304
|
else
|
334
305
|
Integer(limit)
|
@@ -336,8 +307,8 @@ module ActiveRecord
|
|
336
307
|
end
|
337
308
|
|
338
309
|
# The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
|
339
|
-
# on
|
340
|
-
# an UPDATE statement, so in the
|
310
|
+
# on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
|
311
|
+
# an UPDATE statement, so in the MySQL adapters we redefine this to do that.
|
341
312
|
def join_to_update(update, select) #:nodoc:
|
342
313
|
key = update.key
|
343
314
|
subselect = subquery_for(key, select)
|
@@ -362,8 +333,9 @@ module ActiveRecord
|
|
362
333
|
|
363
334
|
# Returns an ActiveRecord::Result instance.
|
364
335
|
def select(sql, name = nil, binds = [])
|
336
|
+
exec_query(sql, name, binds)
|
365
337
|
end
|
366
|
-
|
338
|
+
|
367
339
|
|
368
340
|
# Returns the last auto-generated ID from the affected table.
|
369
341
|
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
@@ -389,6 +361,13 @@ module ActiveRecord
|
|
389
361
|
row = result.rows.first
|
390
362
|
row && row.first
|
391
363
|
end
|
364
|
+
|
365
|
+
def binds_from_relation(relation, binds)
|
366
|
+
if relation.is_a?(Relation) && binds.empty?
|
367
|
+
relation, binds = relation.arel, relation.bind_values
|
368
|
+
end
|
369
|
+
[relation, binds]
|
370
|
+
end
|
392
371
|
end
|
393
372
|
end
|
394
373
|
end
|
@@ -9,34 +9,11 @@ module ActiveRecord
|
|
9
9
|
# records are quoted as their primary key
|
10
10
|
return value.quoted_id if value.respond_to?(:quoted_id)
|
11
11
|
|
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 then value.to_s
|
34
|
-
when Date, Time then "'#{quoted_date(value)}'"
|
35
|
-
when Symbol then "'#{quote_string(value.to_s)}'"
|
36
|
-
when Class then "'#{value.to_s}'"
|
37
|
-
else
|
38
|
-
"'#{quote_string(YAML.dump(value))}'"
|
12
|
+
if column
|
13
|
+
value = column.cast_type.type_cast_for_database(value)
|
39
14
|
end
|
15
|
+
|
16
|
+
_quote(value)
|
40
17
|
end
|
41
18
|
|
42
19
|
# Cast a +value+ to a type that the database understands. For example,
|
@@ -47,34 +24,14 @@ module ActiveRecord
|
|
47
24
|
return value.id
|
48
25
|
end
|
49
26
|
|
50
|
-
|
51
|
-
|
52
|
-
value = value.to_s
|
53
|
-
return value unless column
|
54
|
-
|
55
|
-
case column.type
|
56
|
-
when :integer then value.to_i
|
57
|
-
when :float then value.to_f
|
58
|
-
else
|
59
|
-
value
|
60
|
-
end
|
61
|
-
|
62
|
-
when true, false
|
63
|
-
if column && column.type == :integer
|
64
|
-
value ? 1 : 0
|
65
|
-
else
|
66
|
-
value ? 't' : 'f'
|
67
|
-
end
|
68
|
-
# BigDecimals need to be put in a non-normalized form and quoted.
|
69
|
-
when nil then nil
|
70
|
-
when BigDecimal then value.to_s('F')
|
71
|
-
when Numeric then value
|
72
|
-
when Date, Time then quoted_date(value)
|
73
|
-
when Symbol then value.to_s
|
74
|
-
else
|
75
|
-
to_type = column ? " to #{column.type}" : ""
|
76
|
-
raise TypeError, "can't cast #{value.class}#{to_type}"
|
27
|
+
if column
|
28
|
+
value = column.cast_type.type_cast_for_database(value)
|
77
29
|
end
|
30
|
+
|
31
|
+
_type_cast(value)
|
32
|
+
rescue TypeError
|
33
|
+
to_type = column ? " to #{column.type}" : ""
|
34
|
+
raise TypeError, "can't cast #{value.class}#{to_type}"
|
78
35
|
end
|
79
36
|
|
80
37
|
# Quotes a string, escaping any ' (single quote) and \ (backslash)
|
@@ -99,7 +56,7 @@ module ActiveRecord
|
|
99
56
|
# This works for mysql and mysql2 where table.column can be used to
|
100
57
|
# resolve ambiguity.
|
101
58
|
#
|
102
|
-
# We override this in the
|
59
|
+
# We override this in the sqlite3 and postgresql adapters to use only
|
103
60
|
# the column name (as per syntax requirements).
|
104
61
|
def quote_table_name_for_assignment(table, attr)
|
105
62
|
quote_table_name("#{table}.#{attr}")
|
@@ -109,10 +66,18 @@ module ActiveRecord
|
|
109
66
|
"'t'"
|
110
67
|
end
|
111
68
|
|
69
|
+
def unquoted_true
|
70
|
+
't'
|
71
|
+
end
|
72
|
+
|
112
73
|
def quoted_false
|
113
74
|
"'f'"
|
114
75
|
end
|
115
76
|
|
77
|
+
def unquoted_false
|
78
|
+
'f'
|
79
|
+
end
|
80
|
+
|
116
81
|
def quoted_date(value)
|
117
82
|
if value.acts_like?(:time)
|
118
83
|
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
|
@@ -124,6 +89,45 @@ module ActiveRecord
|
|
124
89
|
|
125
90
|
value.to_s(:db)
|
126
91
|
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def types_which_need_no_typecasting
|
96
|
+
[nil, Numeric, String]
|
97
|
+
end
|
98
|
+
|
99
|
+
def _quote(value)
|
100
|
+
case value
|
101
|
+
when String, ActiveSupport::Multibyte::Chars, Type::Binary::Data
|
102
|
+
"'#{quote_string(value.to_s)}'"
|
103
|
+
when true then quoted_true
|
104
|
+
when false then quoted_false
|
105
|
+
when nil then "NULL"
|
106
|
+
# BigDecimals need to be put in a non-normalized form and quoted.
|
107
|
+
when BigDecimal then value.to_s('F')
|
108
|
+
when Numeric, ActiveSupport::Duration then value.to_s
|
109
|
+
when Date, Time then "'#{quoted_date(value)}'"
|
110
|
+
when Symbol then "'#{quote_string(value.to_s)}'"
|
111
|
+
when Class then "'#{value}'"
|
112
|
+
else
|
113
|
+
"'#{quote_string(YAML.dump(value))}'"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def _type_cast(value)
|
118
|
+
case value
|
119
|
+
when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
|
120
|
+
value.to_s
|
121
|
+
when true then unquoted_true
|
122
|
+
when false then unquoted_false
|
123
|
+
# BigDecimals need to be put in a non-normalized form and quoted.
|
124
|
+
when BigDecimal then value.to_s('F')
|
125
|
+
when Date, Time then quoted_date(value)
|
126
|
+
when *types_which_need_no_typecasting
|
127
|
+
value
|
128
|
+
else raise TypeError
|
129
|
+
end
|
130
|
+
end
|
127
131
|
end
|
128
132
|
end
|
129
133
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'active_support/core_ext/string/strip'
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
class AbstractAdapter
|
@@ -13,9 +15,7 @@ module ActiveRecord
|
|
13
15
|
end
|
14
16
|
|
15
17
|
def visit_AddColumn(o)
|
16
|
-
|
17
|
-
sql = "ADD #{quote_column_name(o.name)} #{sql_type}"
|
18
|
-
add_column_options!(sql, column_options(o))
|
18
|
+
"ADD #{accept(o)}"
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
@@ -23,10 +23,12 @@ module ActiveRecord
|
|
23
23
|
def visit_AlterTable(o)
|
24
24
|
sql = "ALTER TABLE #{quote_table_name(o.name)} "
|
25
25
|
sql << o.adds.map { |col| visit_AddColumn col }.join(' ')
|
26
|
+
sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(' ')
|
27
|
+
sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(' ')
|
26
28
|
end
|
27
29
|
|
28
30
|
def visit_ColumnDefinition(o)
|
29
|
-
sql_type = type_to_sql(o.type
|
31
|
+
sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale)
|
30
32
|
column_sql = "#{quote_column_name(o.name)} #{sql_type}"
|
31
33
|
add_column_options!(column_sql, column_options(o)) unless o.primary_key?
|
32
34
|
column_sql
|
@@ -41,6 +43,21 @@ module ActiveRecord
|
|
41
43
|
create_sql
|
42
44
|
end
|
43
45
|
|
46
|
+
def visit_AddForeignKey(o)
|
47
|
+
sql = <<-SQL.strip_heredoc
|
48
|
+
ADD CONSTRAINT #{quote_column_name(o.name)}
|
49
|
+
FOREIGN KEY (#{quote_column_name(o.column)})
|
50
|
+
REFERENCES #{quote_table_name(o.to_table)} (#{quote_column_name(o.primary_key)})
|
51
|
+
SQL
|
52
|
+
sql << " #{action_sql('DELETE', o.on_delete)}" if o.on_delete
|
53
|
+
sql << " #{action_sql('UPDATE', o.on_update)}" if o.on_update
|
54
|
+
sql
|
55
|
+
end
|
56
|
+
|
57
|
+
def visit_DropForeignKey(name)
|
58
|
+
"DROP CONSTRAINT #{quote_column_name(name)}"
|
59
|
+
end
|
60
|
+
|
44
61
|
def column_options(o)
|
45
62
|
column_options = {}
|
46
63
|
column_options[:null] = o.null unless o.null.nil?
|
@@ -64,7 +81,7 @@ module ActiveRecord
|
|
64
81
|
end
|
65
82
|
|
66
83
|
def add_column_options!(sql, options)
|
67
|
-
sql << " DEFAULT #{
|
84
|
+
sql << " DEFAULT #{quote_value(options[:default], options[:column])}" if options_include_default?(options)
|
68
85
|
# must explicitly check for :null to allow change_column to work on migrations
|
69
86
|
if options[:null] == false
|
70
87
|
sql << " NOT NULL"
|
@@ -75,9 +92,33 @@ module ActiveRecord
|
|
75
92
|
sql
|
76
93
|
end
|
77
94
|
|
95
|
+
def quote_value(value, column)
|
96
|
+
column.sql_type ||= type_to_sql(column.type, column.limit, column.precision, column.scale)
|
97
|
+
column.cast_type ||= type_for_column(column)
|
98
|
+
|
99
|
+
@conn.quote(value, column)
|
100
|
+
end
|
101
|
+
|
78
102
|
def options_include_default?(options)
|
79
103
|
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
|
80
104
|
end
|
105
|
+
|
106
|
+
def action_sql(action, dependency)
|
107
|
+
case dependency
|
108
|
+
when :nullify then "ON #{action} SET NULL"
|
109
|
+
when :cascade then "ON #{action} CASCADE"
|
110
|
+
when :restrict then "ON #{action} RESTRICT"
|
111
|
+
else
|
112
|
+
raise ArgumentError, <<-MSG.strip_heredoc
|
113
|
+
'#{dependency}' is not supported for :on_update or :on_delete.
|
114
|
+
Supported values are: :nullify, :cascade, :restrict
|
115
|
+
MSG
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def type_for_column(column)
|
120
|
+
@conn.lookup_cast_type(column.sql_type)
|
121
|
+
end
|
81
122
|
end
|
82
123
|
end
|
83
124
|
end
|