activerecord 4.1.16 → 4.2.11.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

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