activerecord 4.1.15 → 4.2.0.beta1
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 +634 -2176
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/association_scope.rb +53 -21
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
- 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 +32 -44
- data/lib/active_record/associations/collection_proxy.rb +1 -10
- data/lib/active_record/associations/has_many_association.rb +60 -14
- data/lib/active_record/associations/has_many_through_association.rb +34 -23
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
- data/lib/active_record/associations/join_dependency.rb +7 -9
- data/lib/active_record/associations/preloader/association.rb +9 -5
- data/lib/active_record/associations/preloader/through_association.rb +3 -3
- data/lib/active_record/associations/preloader.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +16 -1
- data/lib/active_record/associations/through_association.rb +6 -22
- data/lib/active_record/associations.rb +58 -33
- data/lib/active_record/attribute.rb +131 -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 +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +85 -42
- data/lib/active_record/attribute_methods/primary_key.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +14 -57
- data/lib/active_record/attribute_methods/serialization.rb +12 -146
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
- data/lib/active_record/attribute_methods/write.rb +8 -23
- data/lib/active_record/attribute_methods.rb +53 -90
- data/lib/active_record/attribute_set/builder.rb +32 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +122 -0
- data/lib/active_record/autosave_association.rb +11 -21
- data/lib/active_record/base.rb +9 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
- data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
- data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
- data/lib/active_record/connection_adapters/column.rb +13 -244
- data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
- data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
- 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 -20
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -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 +76 -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 +85 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -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 +42 -122
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
- data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +119 -22
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -10
- data/lib/active_record/errors.rb +27 -26
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +52 -45
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +33 -8
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +34 -16
- 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 +22 -32
- data/lib/active_record/model_schema.rb +39 -48
- data/lib/active_record/nested_attributes.rb +8 -18
- data/lib/active_record/persistence.rb +39 -22
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +1 -8
- data/lib/active_record/railtie.rb +17 -10
- data/lib/active_record/railties/databases.rake +47 -42
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +225 -92
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +28 -32
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +42 -20
- data/lib/active_record/relation/merger.rb +0 -1
- data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
- data/lib/active_record/relation/predicate_builder.rb +1 -22
- data/lib/active_record/relation/query_methods.rb +98 -62
- data/lib/active_record/relation/spawn_methods.rb +6 -7
- data/lib/active_record/relation.rb +35 -11
- data/lib/active_record/result.rb +16 -9
- data/lib/active_record/sanitization.rb +8 -1
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +51 -9
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping/default.rb +5 -4
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +79 -5
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +37 -5
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +35 -21
- data/lib/active_record/type/binary.rb +40 -0
- data/lib/active_record/type/boolean.rb +19 -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/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
- data/lib/active_record/type/integer.rb +23 -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 +51 -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 +48 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/type.rb +20 -0
- data/lib/active_record/validations/uniqueness.rb +9 -23
- data/lib/active_record/validations.rb +21 -16
- data/lib/active_record.rb +2 -1
- 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 +71 -14
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -58,13 +58,11 @@ module ActiveRecord
|
|
58
58
|
# * +checkout_timeout+: number of seconds to block and wait for a connection
|
59
59
|
# before giving up and raising a timeout error (default 5 seconds).
|
60
60
|
# * +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).
|
61
|
+
# Reaper, which attempts to find and recover connections from dead
|
62
|
+
# threads, which can occur if a programmer forgets to close a
|
63
|
+
# connection at the end of a thread or a thread dies unexpectedly.
|
64
|
+
# Regardless of this setting, the Reaper will be invoked before every
|
65
|
+
# blocking wait. (Default nil, which means don't schedule the Reaper).
|
68
66
|
class ConnectionPool
|
69
67
|
# Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool
|
70
68
|
# with which it shares a Monitor. But could be a generic Queue.
|
@@ -222,7 +220,7 @@ module ActiveRecord
|
|
222
220
|
|
223
221
|
include MonitorMixin
|
224
222
|
|
225
|
-
attr_accessor :automatic_reconnect, :checkout_timeout
|
223
|
+
attr_accessor :automatic_reconnect, :checkout_timeout
|
226
224
|
attr_reader :spec, :connections, :size, :reaper
|
227
225
|
|
228
226
|
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
|
@@ -237,8 +235,7 @@ module ActiveRecord
|
|
237
235
|
@spec = spec
|
238
236
|
|
239
237
|
@checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
|
240
|
-
@
|
241
|
-
@reaper = Reaper.new(self, (spec.config[:reaping_frequency] && spec.config[:reaping_frequency].to_f))
|
238
|
+
@reaper = Reaper.new self, spec.config[:reaping_frequency]
|
242
239
|
@reaper.run
|
243
240
|
|
244
241
|
# default max pool size to 5
|
@@ -361,11 +358,13 @@ module ActiveRecord
|
|
361
358
|
# calling +checkout+ on this pool.
|
362
359
|
def checkin(conn)
|
363
360
|
synchronize do
|
361
|
+
owner = conn.owner
|
362
|
+
|
364
363
|
conn.run_callbacks :checkin do
|
365
364
|
conn.expire
|
366
365
|
end
|
367
366
|
|
368
|
-
release
|
367
|
+
release owner
|
369
368
|
|
370
369
|
@available.add conn
|
371
370
|
end
|
@@ -378,22 +377,28 @@ module ActiveRecord
|
|
378
377
|
@connections.delete conn
|
379
378
|
@available.delete conn
|
380
379
|
|
381
|
-
|
382
|
-
# from the reserved hash will be a little easier.
|
383
|
-
release conn
|
380
|
+
release conn.owner
|
384
381
|
|
385
382
|
@available.add checkout_new_connection if @available.any_waiting?
|
386
383
|
end
|
387
384
|
end
|
388
385
|
|
389
|
-
#
|
390
|
-
#
|
386
|
+
# Recover lost connections for the pool. A lost connection can occur if
|
387
|
+
# a programmer forgets to checkin a connection at the end of a thread
|
391
388
|
# or a thread dies unexpectedly.
|
392
389
|
def reap
|
393
|
-
synchronize do
|
394
|
-
|
395
|
-
|
396
|
-
|
390
|
+
stale_connections = synchronize do
|
391
|
+
@connections.select do |conn|
|
392
|
+
conn.in_use? && !conn.owner.alive?
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
stale_connections.each do |conn|
|
397
|
+
synchronize do
|
398
|
+
if conn.active?
|
399
|
+
conn.reset!
|
400
|
+
checkin conn
|
401
|
+
else
|
397
402
|
remove conn
|
398
403
|
end
|
399
404
|
end
|
@@ -415,20 +420,15 @@ module ActiveRecord
|
|
415
420
|
elsif @connections.size < @size
|
416
421
|
checkout_new_connection
|
417
422
|
else
|
423
|
+
reap
|
418
424
|
@available.poll(@checkout_timeout)
|
419
425
|
end
|
420
426
|
end
|
421
427
|
|
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
|
428
|
+
def release(owner)
|
429
|
+
thread_id = owner.object_id
|
430
430
|
|
431
|
-
@reserved_connections.delete thread_id
|
431
|
+
@reserved_connections.delete thread_id
|
432
432
|
end
|
433
433
|
|
434
434
|
def new_connection
|
@@ -462,23 +462,44 @@ module ActiveRecord
|
|
462
462
|
#
|
463
463
|
# For example, suppose that you have 5 models, with the following hierarchy:
|
464
464
|
#
|
465
|
-
#
|
466
|
-
#
|
467
|
-
#
|
468
|
-
#
|
469
|
-
#
|
470
|
-
#
|
471
|
-
#
|
465
|
+
# class Author < ActiveRecord::Base
|
466
|
+
# end
|
467
|
+
#
|
468
|
+
# class BankAccount < ActiveRecord::Base
|
469
|
+
# end
|
470
|
+
#
|
471
|
+
# class Book < ActiveRecord::Base
|
472
|
+
# establish_connection "library_db"
|
473
|
+
# end
|
474
|
+
#
|
475
|
+
# class ScaryBook < Book
|
476
|
+
# end
|
477
|
+
#
|
478
|
+
# class GoodBook < Book
|
479
|
+
# end
|
480
|
+
#
|
481
|
+
# And a database.yml that looked like this:
|
482
|
+
#
|
483
|
+
# development:
|
484
|
+
# database: my_application
|
485
|
+
# host: localhost
|
486
|
+
#
|
487
|
+
# library_db:
|
488
|
+
# database: library
|
489
|
+
# host: some.library.org
|
490
|
+
#
|
491
|
+
# Your primary database in the development environment is "my_application"
|
492
|
+
# but the Book model connects to a separate database called "library_db"
|
493
|
+
# (this can even be a database on a different machine).
|
472
494
|
#
|
473
|
-
#
|
474
|
-
#
|
475
|
-
# 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.
|
495
|
+
# Book, ScaryBook and GoodBook will all use the same connection pool to
|
496
|
+
# "library_db" while Author, BankAccount, and any other models you create
|
497
|
+
# will use the default connection pool to "my_application".
|
478
498
|
#
|
479
|
-
#
|
480
|
-
# ActiveRecord::Base.connection_handler.
|
481
|
-
# determine the connection pool that they
|
499
|
+
# The various connection pools are managed by a single instance of
|
500
|
+
# ConnectionHandler accessible via ActiveRecord::Base.connection_handler.
|
501
|
+
# All Active Record models use this handler to determine the connection pool that they
|
502
|
+
# should use.
|
482
503
|
class ConnectionHandler
|
483
504
|
def initialize
|
484
505
|
# These caches are keyed by klass.name, NOT klass. Keying them by klass
|
@@ -538,7 +559,10 @@ module ActiveRecord
|
|
538
559
|
# for (not necessarily the current class).
|
539
560
|
def retrieve_connection(klass) #:nodoc:
|
540
561
|
pool = retrieve_connection_pool(klass)
|
541
|
-
|
562
|
+
raise ConnectionNotEstablished, "No connection pool for #{klass}" unless pool
|
563
|
+
conn = pool.connection
|
564
|
+
raise ConnectionNotEstablished, "No connection for #{klass} in connection pool" unless conn
|
565
|
+
conn
|
542
566
|
end
|
543
567
|
|
544
568
|
# Returns true if a connection that's accessible to this class has
|
@@ -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
|
@@ -185,7 +193,7 @@ module ActiveRecord
|
|
185
193
|
# * You are creating a nested (savepoint) transaction
|
186
194
|
#
|
187
195
|
# The mysql, mysql2 and postgresql adapters support setting the transaction
|
188
|
-
# isolation level. However, support is disabled for
|
196
|
+
# isolation level. However, support is disabled for MySQL versions below 5,
|
189
197
|
# because they are affected by a bug[http://bugs.mysql.com/bug.php?id=39170]
|
190
198
|
# which means the isolation level gets persisted outside the transaction.
|
191
199
|
def transaction(options = {})
|
@@ -195,58 +203,30 @@ module ActiveRecord
|
|
195
203
|
if options[:isolation]
|
196
204
|
raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
|
197
205
|
end
|
198
|
-
|
199
206
|
yield
|
200
207
|
else
|
201
|
-
within_new_transaction(options) { yield }
|
208
|
+
transaction_manager.within_new_transaction(options) { yield }
|
202
209
|
end
|
203
210
|
rescue ActiveRecord::Rollback
|
204
211
|
# rollbacks are silently swallowed
|
205
212
|
end
|
206
213
|
|
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
|
214
|
+
attr_reader :transaction_manager #:nodoc:
|
221
215
|
|
222
|
-
|
223
|
-
@transaction
|
224
|
-
end
|
216
|
+
delegate :within_new_transaction, :open_transactions, :current_transaction, :begin_transaction, :commit_transaction, :rollback_transaction, to: :transaction_manager
|
225
217
|
|
226
218
|
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
|
219
|
+
current_transaction.open?
|
240
220
|
end
|
241
221
|
|
242
222
|
def reset_transaction #:nodoc:
|
243
|
-
@
|
223
|
+
@transaction_manager = TransactionManager.new(self)
|
244
224
|
end
|
245
225
|
|
246
226
|
# Register a record with the current transaction so that its after_commit and after_rollback callbacks
|
247
227
|
# can be called.
|
248
228
|
def add_transaction_record(record)
|
249
|
-
|
229
|
+
current_transaction.add_record(record)
|
250
230
|
end
|
251
231
|
|
252
232
|
# Begins the transaction (and turns off auto-committing).
|
@@ -326,8 +306,8 @@ module ActiveRecord
|
|
326
306
|
end
|
327
307
|
|
328
308
|
# 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
|
309
|
+
# on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
|
310
|
+
# an UPDATE statement, so in the MySQL adapters we redefine this to do that.
|
331
311
|
def join_to_update(update, select) #:nodoc:
|
332
312
|
key = update.key
|
333
313
|
subselect = subquery_for(key, select)
|
@@ -381,7 +361,7 @@ module ActiveRecord
|
|
381
361
|
end
|
382
362
|
|
383
363
|
def binds_from_relation(relation, binds)
|
384
|
-
if relation.is_a?(Relation) && binds.
|
364
|
+
if relation.is_a?(Relation) && binds.empty?
|
385
365
|
relation, binds = relation.arel, relation.bind_values
|
386
366
|
end
|
387
367
|
[relation, binds]
|
@@ -9,39 +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
|
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))}'"
|
12
|
+
if column
|
13
|
+
value = column.cast_type.type_cast_for_database(value)
|
44
14
|
end
|
15
|
+
|
16
|
+
_quote(value)
|
45
17
|
end
|
46
18
|
|
47
19
|
# Cast a +value+ to a type that the database understands. For example,
|
@@ -52,34 +24,14 @@ module ActiveRecord
|
|
52
24
|
return value.id
|
53
25
|
end
|
54
26
|
|
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}"
|
27
|
+
if column
|
28
|
+
value = column.cast_type.type_cast_for_database(value)
|
82
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}"
|
83
35
|
end
|
84
36
|
|
85
37
|
# Quotes a string, escaping any ' (single quote) and \ (backslash)
|
@@ -104,7 +56,7 @@ module ActiveRecord
|
|
104
56
|
# This works for mysql and mysql2 where table.column can be used to
|
105
57
|
# resolve ambiguity.
|
106
58
|
#
|
107
|
-
# We override this in the
|
59
|
+
# We override this in the sqlite3 and postgresql adapters to use only
|
108
60
|
# the column name (as per syntax requirements).
|
109
61
|
def quote_table_name_for_assignment(table, attr)
|
110
62
|
quote_table_name("#{table}.#{attr}")
|
@@ -114,10 +66,18 @@ module ActiveRecord
|
|
114
66
|
"'t'"
|
115
67
|
end
|
116
68
|
|
69
|
+
def unquoted_true
|
70
|
+
't'
|
71
|
+
end
|
72
|
+
|
117
73
|
def quoted_false
|
118
74
|
"'f'"
|
119
75
|
end
|
120
76
|
|
77
|
+
def unquoted_false
|
78
|
+
'f'
|
79
|
+
end
|
80
|
+
|
121
81
|
def quoted_date(value)
|
122
82
|
if value.acts_like?(:time)
|
123
83
|
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
|
@@ -129,6 +89,45 @@ module ActiveRecord
|
|
129
89
|
|
130
90
|
value.to_s(:db)
|
131
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.to_s}'"
|
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
|
132
131
|
end
|
133
132
|
end
|
134
133
|
end
|
@@ -13,7 +13,7 @@ module ActiveRecord
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def visit_AddColumn(o)
|
16
|
-
sql_type = type_to_sql(o.type
|
16
|
+
sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale)
|
17
17
|
sql = "ADD #{quote_column_name(o.name)} #{sql_type}"
|
18
18
|
add_column_options!(sql, column_options(o))
|
19
19
|
end
|
@@ -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?
|
@@ -77,6 +94,7 @@ module ActiveRecord
|
|
77
94
|
|
78
95
|
def quote_value(value, column)
|
79
96
|
column.sql_type ||= type_to_sql(column.type, column.limit, column.precision, column.scale)
|
97
|
+
column.cast_type ||= type_for_column(column)
|
80
98
|
|
81
99
|
@conn.quote(value, column)
|
82
100
|
end
|
@@ -84,6 +102,23 @@ module ActiveRecord
|
|
84
102
|
def options_include_default?(options)
|
85
103
|
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
|
86
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
|
87
122
|
end
|
88
123
|
end
|
89
124
|
end
|