activerecord 4.1.16 → 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.

Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +634 -2185
  3. data/README.rdoc +15 -10
  4. data/lib/active_record.rb +2 -1
  5. data/lib/active_record/aggregations.rb +12 -8
  6. data/lib/active_record/associations.rb +58 -33
  7. data/lib/active_record/associations/association.rb +1 -1
  8. data/lib/active_record/associations/association_scope.rb +53 -21
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  10. data/lib/active_record/associations/builder/association.rb +16 -5
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
  13. data/lib/active_record/associations/builder/has_one.rb +2 -2
  14. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  15. data/lib/active_record/associations/collection_association.rb +32 -44
  16. data/lib/active_record/associations/collection_proxy.rb +1 -10
  17. data/lib/active_record/associations/has_many_association.rb +60 -14
  18. data/lib/active_record/associations/has_many_through_association.rb +34 -23
  19. data/lib/active_record/associations/has_one_association.rb +0 -1
  20. data/lib/active_record/associations/join_dependency.rb +7 -9
  21. data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
  22. data/lib/active_record/associations/preloader.rb +2 -2
  23. data/lib/active_record/associations/preloader/association.rb +9 -5
  24. data/lib/active_record/associations/preloader/through_association.rb +3 -3
  25. data/lib/active_record/associations/singular_association.rb +16 -1
  26. data/lib/active_record/associations/through_association.rb +6 -22
  27. data/lib/active_record/attribute.rb +131 -0
  28. data/lib/active_record/attribute_assignment.rb +19 -11
  29. data/lib/active_record/attribute_decorators.rb +66 -0
  30. data/lib/active_record/attribute_methods.rb +53 -90
  31. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  32. data/lib/active_record/attribute_methods/dirty.rb +85 -42
  33. data/lib/active_record/attribute_methods/primary_key.rb +6 -8
  34. data/lib/active_record/attribute_methods/read.rb +14 -57
  35. data/lib/active_record/attribute_methods/serialization.rb +12 -146
  36. data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
  37. data/lib/active_record/attribute_methods/write.rb +8 -23
  38. data/lib/active_record/attribute_set.rb +77 -0
  39. data/lib/active_record/attribute_set/builder.rb +32 -0
  40. data/lib/active_record/attributes.rb +122 -0
  41. data/lib/active_record/autosave_association.rb +11 -21
  42. data/lib/active_record/base.rb +9 -19
  43. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
  44. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
  53. data/lib/active_record/connection_adapters/column.rb +13 -244
  54. data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
  55. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
  56. data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
  57. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  58. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  59. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
  60. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  61. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -0
  62. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  64. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  66. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  67. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +76 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +85 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  86. data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
  87. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  88. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
  89. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
  90. data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
  91. data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
  92. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
  94. data/lib/active_record/connection_handling.rb +1 -1
  95. data/lib/active_record/core.rb +119 -22
  96. data/lib/active_record/counter_cache.rb +60 -6
  97. data/lib/active_record/enum.rb +9 -10
  98. data/lib/active_record/errors.rb +27 -26
  99. data/lib/active_record/explain.rb +1 -1
  100. data/lib/active_record/fixtures.rb +52 -45
  101. data/lib/active_record/gem_version.rb +3 -3
  102. data/lib/active_record/inheritance.rb +33 -8
  103. data/lib/active_record/integration.rb +4 -4
  104. data/lib/active_record/locking/optimistic.rb +34 -16
  105. data/lib/active_record/migration.rb +22 -32
  106. data/lib/active_record/migration/command_recorder.rb +19 -2
  107. data/lib/active_record/migration/join_table.rb +1 -1
  108. data/lib/active_record/model_schema.rb +39 -48
  109. data/lib/active_record/nested_attributes.rb +8 -18
  110. data/lib/active_record/persistence.rb +39 -22
  111. data/lib/active_record/query_cache.rb +3 -3
  112. data/lib/active_record/querying.rb +1 -8
  113. data/lib/active_record/railtie.rb +17 -10
  114. data/lib/active_record/railties/databases.rake +47 -42
  115. data/lib/active_record/readonly_attributes.rb +0 -1
  116. data/lib/active_record/reflection.rb +225 -92
  117. data/lib/active_record/relation.rb +35 -11
  118. data/lib/active_record/relation/batches.rb +0 -2
  119. data/lib/active_record/relation/calculations.rb +28 -32
  120. data/lib/active_record/relation/delegation.rb +1 -1
  121. data/lib/active_record/relation/finder_methods.rb +42 -20
  122. data/lib/active_record/relation/merger.rb +0 -1
  123. data/lib/active_record/relation/predicate_builder.rb +1 -22
  124. data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
  125. data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
  126. data/lib/active_record/relation/query_methods.rb +98 -62
  127. data/lib/active_record/relation/spawn_methods.rb +6 -7
  128. data/lib/active_record/result.rb +16 -9
  129. data/lib/active_record/sanitization.rb +8 -1
  130. data/lib/active_record/schema.rb +0 -1
  131. data/lib/active_record/schema_dumper.rb +51 -9
  132. data/lib/active_record/schema_migration.rb +4 -0
  133. data/lib/active_record/scoping/default.rb +5 -4
  134. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  135. data/lib/active_record/statement_cache.rb +79 -5
  136. data/lib/active_record/store.rb +5 -5
  137. data/lib/active_record/tasks/database_tasks.rb +37 -5
  138. data/lib/active_record/tasks/mysql_database_tasks.rb +10 -16
  139. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
  140. data/lib/active_record/timestamp.rb +9 -7
  141. data/lib/active_record/transactions.rb +35 -21
  142. data/lib/active_record/type.rb +20 -0
  143. data/lib/active_record/type/binary.rb +40 -0
  144. data/lib/active_record/type/boolean.rb +19 -0
  145. data/lib/active_record/type/date.rb +46 -0
  146. data/lib/active_record/type/date_time.rb +43 -0
  147. data/lib/active_record/type/decimal.rb +40 -0
  148. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  149. data/lib/active_record/type/float.rb +19 -0
  150. data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
  151. data/lib/active_record/type/integer.rb +23 -0
  152. data/lib/active_record/type/mutable.rb +16 -0
  153. data/lib/active_record/type/numeric.rb +36 -0
  154. data/lib/active_record/type/serialized.rb +51 -0
  155. data/lib/active_record/type/string.rb +36 -0
  156. data/lib/active_record/type/text.rb +11 -0
  157. data/lib/active_record/type/time.rb +26 -0
  158. data/lib/active_record/type/time_value.rb +38 -0
  159. data/lib/active_record/type/type_map.rb +48 -0
  160. data/lib/active_record/type/value.rb +101 -0
  161. data/lib/active_record/validations.rb +21 -16
  162. data/lib/active_record/validations/uniqueness.rb +9 -23
  163. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  164. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  165. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  166. metadata +71 -14
  167. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -9,16 +9,19 @@ require 'active_support/core_ext/class/delegating_attributes'
9
9
  require 'active_support/core_ext/array/extract_options'
10
10
  require 'active_support/core_ext/hash/deep_merge'
11
11
  require 'active_support/core_ext/hash/slice'
12
+ require 'active_support/core_ext/hash/transform_values'
12
13
  require 'active_support/core_ext/string/behavior'
13
14
  require 'active_support/core_ext/kernel/singleton_class'
14
15
  require 'active_support/core_ext/module/introspection'
15
16
  require 'active_support/core_ext/object/duplicable'
16
17
  require 'active_support/core_ext/class/subclasses'
17
18
  require 'arel'
19
+ require 'active_record/attribute_decorators'
18
20
  require 'active_record/errors'
19
21
  require 'active_record/log_subscriber'
20
22
  require 'active_record/explain_subscriber'
21
23
  require 'active_record/relation/delegation'
24
+ require 'active_record/attributes'
22
25
 
23
26
  module ActiveRecord #:nodoc:
24
27
  # = Active Record
@@ -138,6 +141,7 @@ module ActiveRecord #:nodoc:
138
141
  #
139
142
  # In addition to the basic accessors, query methods are also automatically available on the Active Record object.
140
143
  # Query methods allow you to test whether an attribute value is present.
144
+ # For numeric values, present is defined as non-zero.
141
145
  #
142
146
  # For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call
143
147
  # to determine whether the user has a name:
@@ -217,25 +221,9 @@ module ActiveRecord #:nodoc:
217
221
  #
218
222
  # == Single table inheritance
219
223
  #
220
- # Active Record allows inheritance by storing the name of the class in a column that by
221
- # default is named "type" (can be changed by overwriting <tt>Base.inheritance_column</tt>).
222
- # This means that an inheritance looking like this:
223
- #
224
- # class Company < ActiveRecord::Base; end
225
- # class Firm < Company; end
226
- # class Client < Company; end
227
- # class PriorityClient < Client; end
228
- #
229
- # When you do <tt>Firm.create(name: "37signals")</tt>, this record will be saved in
230
- # the companies table with type = "Firm". You can then fetch this row again using
231
- # <tt>Company.where(name: '37signals').first</tt> and it will return a Firm object.
232
- #
233
- # If you don't have a type column defined in your table, single-table inheritance won't
234
- # be triggered. In that case, it'll work just like normal subclasses with no special magic
235
- # for differentiating between them or reloading the right type with find.
236
- #
237
- # Note, all the attributes for all the cases are kept in the same table. Read more:
238
- # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
224
+ # Active Record allows inheritance by storing the name of the class in a
225
+ # column that is named "type" by default. See ActiveRecord::Inheritance for
226
+ # more details.
239
227
  #
240
228
  # == Connection to multiple databases in different models
241
229
  #
@@ -306,6 +294,8 @@ module ActiveRecord #:nodoc:
306
294
  include Integration
307
295
  include Validations
308
296
  include CounterCache
297
+ include Attributes
298
+ include AttributeDecorators
309
299
  include Locking::Optimistic
310
300
  include Locking::Pessimistic
311
301
  include AttributeMethods
@@ -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 close dead connections, which can
62
- # occur if a programmer forgets to close a connection at the end of a
63
- # thread or a thread dies unexpectedly. (Default nil, which means don't
64
- # run the Reaper).
65
- # * +dead_connection_timeout+: number of seconds from last checkout
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, :dead_connection_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
- @dead_connection_timeout = (spec.config[:dead_connection_timeout] && spec.config[:dead_connection_timeout].to_f) || 5
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 conn
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
- # FIXME: we might want to store the key on the connection so that removing
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
- # Removes dead connections from the pool. A dead connection can occur
390
- # if a programmer forgets to close a connection at the end of a thread
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
- stale = Time.now - @dead_connection_timeout
395
- connections.dup.each do |conn|
396
- if conn.in_use? && stale > conn.last_use && !conn.active_threadsafe?
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(conn)
423
- thread_id = if @reserved_connections[current_connection_id] == conn
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 if 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
- # +-- Book
467
- # | |
468
- # | +-- ScaryBook
469
- # | +-- GoodBook
470
- # +-- Author
471
- # +-- BankAccount
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
- # Suppose that Book is to connect to a separate database (i.e. one other
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.
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
- # Normally there is only a single ConnectionHandler instance, accessible via
480
- # ActiveRecord::Base.connection_handler. Active Record models use this to
481
- # determine the connection pool that they should use.
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
- (pool && pool.connection) or raise ConnectionNotEstablished
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
- binds = binds.dup
13
- visitor.accept(arel.ast) do
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 mysql versions below 5,
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
- def within_new_transaction(options = {}) #:nodoc:
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
- def current_transaction #:nodoc:
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
- @transaction.open?
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
- @transaction = ClosedTransaction.new(self)
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
- @transaction.add_record(record)
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 mysql (even when aliasing the tables), but mysql allows using JOIN directly in
330
- # an UPDATE statement, so in the mysql adapters we redefine this to do that.
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.blank?
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
- case value
13
- when String, ActiveSupport::Multibyte::Chars
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
- case value
56
- when String, ActiveSupport::Multibyte::Chars
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 sqlite and postgresql adapters to use only
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