activerecord 3.0.0 → 4.0.0

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

Potentially problematic release.


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

Files changed (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +35 -44
  5. data/examples/performance.rb +110 -100
  6. data/lib/active_record/aggregations.rb +59 -75
  7. data/lib/active_record/associations/alias_tracker.rb +76 -0
  8. data/lib/active_record/associations/association.rb +248 -0
  9. data/lib/active_record/associations/association_scope.rb +135 -0
  10. data/lib/active_record/associations/belongs_to_association.rb +60 -59
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -59
  12. data/lib/active_record/associations/builder/association.rb +108 -0
  13. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  14. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  16. data/lib/active_record/associations/builder/has_many.rb +15 -0
  17. data/lib/active_record/associations/builder/has_one.rb +25 -0
  18. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  19. data/lib/active_record/associations/collection_association.rb +608 -0
  20. data/lib/active_record/associations/collection_proxy.rb +986 -0
  21. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +40 -112
  22. data/lib/active_record/associations/has_many_association.rb +83 -76
  23. data/lib/active_record/associations/has_many_through_association.rb +147 -66
  24. data/lib/active_record/associations/has_one_association.rb +67 -108
  25. data/lib/active_record/associations/has_one_through_association.rb +21 -25
  26. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  27. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  28. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  29. data/lib/active_record/associations/join_dependency.rb +235 -0
  30. data/lib/active_record/associations/join_helper.rb +45 -0
  31. data/lib/active_record/associations/preloader/association.rb +121 -0
  32. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  33. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  35. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  36. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  37. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  38. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  39. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  40. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  41. data/lib/active_record/associations/preloader.rb +178 -0
  42. data/lib/active_record/associations/singular_association.rb +64 -0
  43. data/lib/active_record/associations/through_association.rb +87 -0
  44. data/lib/active_record/associations.rb +512 -1224
  45. data/lib/active_record/attribute_assignment.rb +201 -0
  46. data/lib/active_record/attribute_methods/before_type_cast.rb +49 -12
  47. data/lib/active_record/attribute_methods/dirty.rb +51 -28
  48. data/lib/active_record/attribute_methods/primary_key.rb +94 -22
  49. data/lib/active_record/attribute_methods/query.rb +5 -4
  50. data/lib/active_record/attribute_methods/read.rb +63 -72
  51. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -41
  53. data/lib/active_record/attribute_methods/write.rb +39 -13
  54. data/lib/active_record/attribute_methods.rb +362 -29
  55. data/lib/active_record/autosave_association.rb +132 -75
  56. data/lib/active_record/base.rb +83 -1627
  57. data/lib/active_record/callbacks.rb +69 -47
  58. data/lib/active_record/coders/yaml_column.rb +38 -0
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +411 -138
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +21 -11
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -173
  62. data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -22
  63. data/lib/active_record/connection_adapters/abstract/quoting.rb +82 -25
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +176 -414
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +562 -232
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +281 -53
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  70. data/lib/active_record/connection_adapters/column.rb +318 -0
  71. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  73. data/lib/active_record/connection_adapters/mysql_adapter.rb +365 -450
  74. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  75. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  80. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  81. data/lib/active_record/connection_adapters/postgresql_adapter.rb +672 -752
  82. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  83. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +588 -17
  84. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  85. data/lib/active_record/connection_handling.rb +98 -0
  86. data/lib/active_record/core.rb +463 -0
  87. data/lib/active_record/counter_cache.rb +108 -101
  88. data/lib/active_record/dynamic_matchers.rb +131 -0
  89. data/lib/active_record/errors.rb +54 -13
  90. data/lib/active_record/explain.rb +38 -0
  91. data/lib/active_record/explain_registry.rb +30 -0
  92. data/lib/active_record/explain_subscriber.rb +29 -0
  93. data/lib/active_record/fixture_set/file.rb +55 -0
  94. data/lib/active_record/fixtures.rb +703 -785
  95. data/lib/active_record/inheritance.rb +200 -0
  96. data/lib/active_record/integration.rb +60 -0
  97. data/lib/active_record/locale/en.yml +8 -1
  98. data/lib/active_record/locking/optimistic.rb +69 -60
  99. data/lib/active_record/locking/pessimistic.rb +34 -12
  100. data/lib/active_record/log_subscriber.rb +40 -6
  101. data/lib/active_record/migration/command_recorder.rb +164 -0
  102. data/lib/active_record/migration/join_table.rb +15 -0
  103. data/lib/active_record/migration.rb +614 -216
  104. data/lib/active_record/model_schema.rb +345 -0
  105. data/lib/active_record/nested_attributes.rb +248 -119
  106. data/lib/active_record/null_relation.rb +65 -0
  107. data/lib/active_record/persistence.rb +275 -57
  108. data/lib/active_record/query_cache.rb +29 -9
  109. data/lib/active_record/querying.rb +62 -0
  110. data/lib/active_record/railtie.rb +135 -21
  111. data/lib/active_record/railties/console_sandbox.rb +5 -0
  112. data/lib/active_record/railties/controller_runtime.rb +17 -5
  113. data/lib/active_record/railties/databases.rake +249 -359
  114. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  115. data/lib/active_record/readonly_attributes.rb +30 -0
  116. data/lib/active_record/reflection.rb +283 -103
  117. data/lib/active_record/relation/batches.rb +38 -34
  118. data/lib/active_record/relation/calculations.rb +252 -139
  119. data/lib/active_record/relation/delegation.rb +125 -0
  120. data/lib/active_record/relation/finder_methods.rb +182 -188
  121. data/lib/active_record/relation/merger.rb +161 -0
  122. data/lib/active_record/relation/predicate_builder.rb +86 -21
  123. data/lib/active_record/relation/query_methods.rb +917 -134
  124. data/lib/active_record/relation/spawn_methods.rb +53 -92
  125. data/lib/active_record/relation.rb +405 -143
  126. data/lib/active_record/result.rb +67 -0
  127. data/lib/active_record/runtime_registry.rb +17 -0
  128. data/lib/active_record/sanitization.rb +168 -0
  129. data/lib/active_record/schema.rb +20 -14
  130. data/lib/active_record/schema_dumper.rb +55 -46
  131. data/lib/active_record/schema_migration.rb +39 -0
  132. data/lib/active_record/scoping/default.rb +146 -0
  133. data/lib/active_record/scoping/named.rb +175 -0
  134. data/lib/active_record/scoping.rb +82 -0
  135. data/lib/active_record/serialization.rb +8 -46
  136. data/lib/active_record/serializers/xml_serializer.rb +21 -68
  137. data/lib/active_record/statement_cache.rb +26 -0
  138. data/lib/active_record/store.rb +156 -0
  139. data/lib/active_record/tasks/database_tasks.rb +203 -0
  140. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  141. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  142. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  143. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  144. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  145. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  146. data/lib/active_record/test_case.rb +57 -28
  147. data/lib/active_record/timestamp.rb +49 -18
  148. data/lib/active_record/transactions.rb +106 -63
  149. data/lib/active_record/translation.rb +22 -0
  150. data/lib/active_record/validations/associated.rb +25 -24
  151. data/lib/active_record/validations/presence.rb +65 -0
  152. data/lib/active_record/validations/uniqueness.rb +123 -83
  153. data/lib/active_record/validations.rb +29 -29
  154. data/lib/active_record/version.rb +7 -5
  155. data/lib/active_record.rb +83 -34
  156. data/lib/rails/generators/active_record/migration/migration_generator.rb +46 -9
  157. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  158. data/lib/rails/generators/active_record/migration/templates/migration.rb +30 -8
  159. data/lib/rails/generators/active_record/model/model_generator.rb +15 -5
  160. data/lib/rails/generators/active_record/model/templates/model.rb +7 -2
  161. data/lib/rails/generators/active_record/model/templates/module.rb +3 -1
  162. data/lib/rails/generators/active_record.rb +4 -8
  163. metadata +163 -121
  164. data/CHANGELOG +0 -6023
  165. data/examples/associations.png +0 -0
  166. data/lib/active_record/association_preload.rb +0 -403
  167. data/lib/active_record/associations/association_collection.rb +0 -562
  168. data/lib/active_record/associations/association_proxy.rb +0 -295
  169. data/lib/active_record/associations/through_association_scope.rb +0 -154
  170. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -113
  171. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -401
  172. data/lib/active_record/dynamic_finder_match.rb +0 -53
  173. data/lib/active_record/dynamic_scope_match.rb +0 -32
  174. data/lib/active_record/named_scope.rb +0 -138
  175. data/lib/active_record/observer.rb +0 -140
  176. data/lib/active_record/session_store.rb +0 -340
  177. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -16
  178. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  179. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -2
  180. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -24
  181. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -2,52 +2,62 @@ module ActiveRecord
2
2
  module ConnectionAdapters # :nodoc:
3
3
  module DatabaseLimits
4
4
 
5
- # the maximum length of a table alias
5
+ # Returns the maximum length of a table alias.
6
6
  def table_alias_length
7
7
  255
8
8
  end
9
9
 
10
- # the maximum length of a column name
10
+ # Returns the maximum length of a column name.
11
11
  def column_name_length
12
12
  64
13
13
  end
14
14
 
15
- # the maximum length of a table name
15
+ # Returns the maximum length of a table name.
16
16
  def table_name_length
17
17
  64
18
18
  end
19
19
 
20
- # the maximum length of an index name
20
+ # Returns the maximum allowed length for an index name. This
21
+ # limit is enforced by rails and Is less than or equal to
22
+ # <tt>index_name_length</tt>. The gap between
23
+ # <tt>index_name_length</tt> is to allow internal rails
24
+ # operations to use prefixes in temporary operations.
25
+ def allowed_index_name_length
26
+ index_name_length
27
+ end
28
+
29
+ # Returns the maximum length of an index name.
21
30
  def index_name_length
22
31
  64
23
32
  end
24
33
 
25
- # the maximum number of columns per table
34
+ # Returns the maximum number of columns per table.
26
35
  def columns_per_table
27
36
  1024
28
37
  end
29
38
 
30
- # the maximum number of indexes per table
39
+ # Returns the maximum number of indexes per table.
31
40
  def indexes_per_table
32
41
  16
33
42
  end
34
43
 
35
- # the maximum number of columns in a multicolumn index
44
+ # Returns the maximum number of columns in a multicolumn index.
36
45
  def columns_per_multicolumn_index
37
46
  16
38
47
  end
39
48
 
40
- # the maximum number of elements in an IN (x,y,z) clause
49
+ # Returns the maximum number of elements in an IN (x,y,z) clause.
50
+ # nil means no limit.
41
51
  def in_clause_length
42
- 65535
52
+ nil
43
53
  end
44
54
 
45
- # the maximum length of an SQL query
55
+ # Returns the maximum length of an SQL query.
46
56
  def sql_query_length
47
57
  1048575
48
58
  end
49
59
 
50
- # maximum number of joins in a single query
60
+ # Returns maximum number of joins in a single query.
51
61
  def joins_per_query
52
62
  256
53
63
  end
@@ -1,30 +1,47 @@
1
1
  module ActiveRecord
2
2
  module ConnectionAdapters # :nodoc:
3
3
  module DatabaseStatements
4
+ def initialize
5
+ super
6
+ reset_transaction
7
+ end
8
+
9
+ # Converts an arel AST to SQL
10
+ def to_sql(arel, binds = [])
11
+ if arel.respond_to?(:ast)
12
+ binds = binds.dup
13
+ visitor.accept(arel.ast) do
14
+ quote(*binds.shift.reverse)
15
+ end
16
+ else
17
+ arel
18
+ end
19
+ end
20
+
4
21
  # Returns an array of record hashes with the column names as keys and
5
22
  # column values as values.
6
- def select_all(sql, name = nil)
7
- select(sql, name)
23
+ def select_all(arel, name = nil, binds = [])
24
+ select(to_sql(arel, binds), name, binds)
8
25
  end
9
26
 
10
27
  # Returns a record hash with the column names as keys and column values
11
28
  # as values.
12
- def select_one(sql, name = nil)
13
- result = select_all(sql, name)
29
+ def select_one(arel, name = nil, binds = [])
30
+ result = select_all(arel, name, binds)
14
31
  result.first if result
15
32
  end
16
33
 
17
34
  # Returns a single value from a record
18
- def select_value(sql, name = nil)
19
- if result = select_one(sql, name)
35
+ def select_value(arel, name = nil, binds = [])
36
+ if result = select_one(arel, name, binds)
20
37
  result.values.first
21
38
  end
22
39
  end
23
40
 
24
41
  # Returns an array of the values of the first column in a select:
25
42
  # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
26
- def select_values(sql, name = nil)
27
- result = select_rows(sql, name)
43
+ def select_values(arel, name = nil)
44
+ result = select_rows(to_sql(arel, []), name)
28
45
  result.map { |v| v[0] }
29
46
  end
30
47
 
@@ -35,37 +52,65 @@ module ActiveRecord
35
52
  undef_method :select_rows
36
53
 
37
54
  # Executes the SQL statement in the context of this connection.
38
- def execute(sql, name = nil, skip_logging = false)
55
+ def execute(sql, name = nil)
39
56
  end
40
57
  undef_method :execute
41
58
 
59
+ # Executes +sql+ statement in the context of this connection using
60
+ # +binds+ as the bind substitutes. +name+ is logged along with
61
+ # the executed +sql+ statement.
62
+ def exec_query(sql, name = 'SQL', binds = [])
63
+ end
64
+
65
+ # Executes insert +sql+ statement in the context of this connection using
66
+ # +binds+ as the bind substitutes. +name+ is logged along with
67
+ # the executed +sql+ statement.
68
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
69
+ exec_query(sql, name, binds)
70
+ end
71
+
72
+ # Executes delete +sql+ statement in the context of this connection using
73
+ # +binds+ as the bind substitutes. +name+ is logged along with
74
+ # the executed +sql+ statement.
75
+ def exec_delete(sql, name, binds)
76
+ exec_query(sql, name, binds)
77
+ end
78
+
79
+ # Executes update +sql+ statement in the context of this connection using
80
+ # +binds+ as the bind substitutes. +name+ is logged along with
81
+ # the executed +sql+ statement.
82
+ def exec_update(sql, name, binds)
83
+ exec_query(sql, name, binds)
84
+ end
85
+
42
86
  # Returns the last auto-generated ID from the affected table.
43
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
44
- insert_sql(sql, name, pk, id_value, sequence_name)
87
+ #
88
+ # +id_value+ will be returned unless the value is nil, in
89
+ # which case the database will attempt to calculate the last inserted
90
+ # id and return that value.
91
+ #
92
+ # If the next id was calculated in advance (as in Oracle), it should be
93
+ # passed in as +id_value+.
94
+ def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
95
+ sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
96
+ value = exec_insert(sql, name, binds, pk, sequence_name)
97
+ id_value || last_inserted_id(value)
45
98
  end
46
99
 
47
100
  # Executes the update statement and returns the number of rows affected.
48
- def update(sql, name = nil)
49
- update_sql(sql, name)
101
+ def update(arel, name = nil, binds = [])
102
+ exec_update(to_sql(arel, binds), name, binds)
50
103
  end
51
104
 
52
105
  # Executes the delete statement and returns the number of rows affected.
53
- def delete(sql, name = nil)
54
- delete_sql(sql, name)
106
+ def delete(arel, name = nil, binds = [])
107
+ exec_delete(to_sql(arel, binds), name, binds)
55
108
  end
56
109
 
57
- # Checks whether there is currently no transaction active. This is done
58
- # by querying the database driver, and does not use the transaction
59
- # house-keeping information recorded by #increment_open_transactions and
60
- # friends.
61
- #
62
- # Returns true if there is no transaction active, false if there is a
63
- # transaction active, and nil if this information is unknown.
64
- #
65
- # Not all adapters supports transaction state introspection. Currently,
66
- # only the PostgreSQL adapter supports this.
67
- def outside_transaction?
68
- nil
110
+ # Returns +true+ when the connection adapter supports prepared statement
111
+ # caching, otherwise returns +false+
112
+ def supports_statement_cache?
113
+ false
69
114
  end
70
115
 
71
116
  # Runs the given block in a database transaction, and returns the result
@@ -79,8 +124,9 @@ module ActiveRecord
79
124
  #
80
125
  # In order to get around this problem, #transaction will emulate the effect
81
126
  # of nested transactions, by using savepoints:
82
- # http://dev.mysql.com/doc/refman/5.0/en/savepoints.html
83
- # Savepoints are supported by MySQL and PostgreSQL, but not SQLite3.
127
+ # http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
128
+ # Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
129
+ # supports savepoints.
84
130
  #
85
131
  # It is safe to call this method if a database transaction is already open,
86
132
  # i.e. if #transaction is called within another #transaction block. In case
@@ -105,95 +151,124 @@ module ActiveRecord
105
151
  # already-automatically-released savepoints:
106
152
  #
107
153
  # Model.connection.transaction do # BEGIN
108
- # Model.connection.transaction(:requires_new => true) do # CREATE SAVEPOINT active_record_1
154
+ # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
109
155
  # Model.connection.create_table(...)
110
156
  # # active_record_1 now automatically released
111
157
  # end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
112
158
  # end
159
+ #
160
+ # == Transaction isolation
161
+ #
162
+ # If your database supports setting the isolation level for a transaction, you can set
163
+ # it like so:
164
+ #
165
+ # Post.transaction(isolation: :serializable) do
166
+ # # ...
167
+ # end
168
+ #
169
+ # Valid isolation levels are:
170
+ #
171
+ # * <tt>:read_uncommitted</tt>
172
+ # * <tt>:read_committed</tt>
173
+ # * <tt>:repeatable_read</tt>
174
+ # * <tt>:serializable</tt>
175
+ #
176
+ # You should consult the documentation for your database to understand the
177
+ # semantics of these different levels:
178
+ #
179
+ # * http://www.postgresql.org/docs/9.1/static/transaction-iso.html
180
+ # * https://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
181
+ #
182
+ # An <tt>ActiveRecord::TransactionIsolationError</tt> will be raised if:
183
+ #
184
+ # * The adapter does not support setting the isolation level
185
+ # * You are joining an existing open transaction
186
+ # * You are creating a nested (savepoint) transaction
187
+ #
188
+ # The mysql, mysql2 and postgresql adapters support setting the transaction
189
+ # isolation level. However, support is disabled for mysql versions below 5,
190
+ # because they are affected by a bug[http://bugs.mysql.com/bug.php?id=39170]
191
+ # which means the isolation level gets persisted outside the transaction.
113
192
  def transaction(options = {})
114
- options.assert_valid_keys :requires_new, :joinable
193
+ options.assert_valid_keys :requires_new, :joinable, :isolation
194
+
195
+ if !options[:requires_new] && current_transaction.joinable?
196
+ if options[:isolation]
197
+ raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
198
+ end
115
199
 
116
- last_transaction_joinable = defined?(@transaction_joinable) ? @transaction_joinable : nil
117
- if options.has_key?(:joinable)
118
- @transaction_joinable = options[:joinable]
200
+ yield
119
201
  else
120
- @transaction_joinable = true
202
+ within_new_transaction(options) { yield }
121
203
  end
122
- requires_new = options[:requires_new] || !last_transaction_joinable
123
-
124
- transaction_open = false
125
- @_current_transaction_records ||= []
204
+ rescue ActiveRecord::Rollback
205
+ # rollbacks are silently swallowed
206
+ end
126
207
 
127
- begin
128
- if block_given?
129
- if requires_new || open_transactions == 0
130
- if open_transactions == 0
131
- begin_db_transaction
132
- elsif requires_new
133
- create_savepoint
134
- end
135
- increment_open_transactions
136
- transaction_open = true
137
- @_current_transaction_records.push([])
138
- end
139
- yield
140
- end
141
- rescue Exception => database_transaction_rollback
142
- if transaction_open && !outside_transaction?
143
- transaction_open = false
144
- decrement_open_transactions
145
- if open_transactions == 0
146
- rollback_db_transaction
147
- rollback_transaction_records(true)
148
- else
149
- rollback_to_savepoint
150
- rollback_transaction_records(false)
151
- end
152
- end
153
- raise unless database_transaction_rollback.is_a?(ActiveRecord::Rollback)
154
- end
208
+ def within_new_transaction(options = {}) #:nodoc:
209
+ transaction = begin_transaction(options)
210
+ yield
211
+ rescue Exception => error
212
+ rollback_transaction if transaction
213
+ raise
155
214
  ensure
156
- @transaction_joinable = last_transaction_joinable
157
-
158
- if outside_transaction?
159
- @open_transactions = 0
160
- elsif transaction_open
161
- decrement_open_transactions
162
- begin
163
- if open_transactions == 0
164
- commit_db_transaction
165
- commit_transaction_records
166
- else
167
- release_savepoint
168
- save_point_records = @_current_transaction_records.pop
169
- unless save_point_records.blank?
170
- @_current_transaction_records.push([]) if @_current_transaction_records.empty?
171
- @_current_transaction_records.last.concat(save_point_records)
172
- end
173
- end
174
- rescue Exception => database_transaction_rollback
175
- if open_transactions == 0
176
- rollback_db_transaction
177
- rollback_transaction_records(true)
178
- else
179
- rollback_to_savepoint
180
- rollback_transaction_records(false)
181
- end
182
- raise
183
- end
215
+ begin
216
+ commit_transaction unless error
217
+ rescue Exception
218
+ rollback_transaction
219
+ raise
184
220
  end
185
221
  end
186
222
 
223
+ def current_transaction #:nodoc:
224
+ @transaction
225
+ end
226
+
227
+ def transaction_open?
228
+ @transaction.open?
229
+ end
230
+
231
+ def begin_transaction(options = {}) #:nodoc:
232
+ @transaction = @transaction.begin(options)
233
+ end
234
+
235
+ def commit_transaction #:nodoc:
236
+ @transaction = @transaction.commit
237
+ end
238
+
239
+ def rollback_transaction #:nodoc:
240
+ @transaction = @transaction.rollback
241
+ end
242
+
243
+ def reset_transaction #:nodoc:
244
+ @transaction = ClosedTransaction.new(self)
245
+ end
246
+
187
247
  # Register a record with the current transaction so that its after_commit and after_rollback callbacks
188
248
  # can be called.
189
249
  def add_transaction_record(record)
190
- last_batch = @_current_transaction_records.last
191
- last_batch << record if last_batch
250
+ @transaction.add_record(record)
192
251
  end
193
252
 
194
253
  # Begins the transaction (and turns off auto-committing).
195
254
  def begin_db_transaction() end
196
255
 
256
+ def transaction_isolation_levels
257
+ {
258
+ read_uncommitted: "READ UNCOMMITTED",
259
+ read_committed: "READ COMMITTED",
260
+ repeatable_read: "REPEATABLE READ",
261
+ serializable: "SERIALIZABLE"
262
+ }
263
+ end
264
+
265
+ # Begins the transaction with the isolation level set. Raises an error by
266
+ # default; adapters that support setting the isolation level should implement
267
+ # this method.
268
+ def begin_isolated_db_transaction(isolation)
269
+ raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation"
270
+ end
271
+
197
272
  # Commits the transaction (and turns on auto-committing).
198
273
  def commit_db_transaction() end
199
274
 
@@ -201,46 +276,31 @@ module ActiveRecord
201
276
  # done if the transaction block raises an exception or returns false.
202
277
  def rollback_db_transaction() end
203
278
 
204
- # Appends +LIMIT+ and +OFFSET+ options to an SQL statement, or some SQL
205
- # fragment that has the same semantics as LIMIT and OFFSET.
206
- #
207
- # +options+ must be a Hash which contains a +:limit+ option
208
- # and an +:offset+ option.
209
- #
210
- # This method *modifies* the +sql+ parameter.
211
- #
212
- # ===== Examples
213
- # add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50})
214
- # generates
215
- # SELECT * FROM suppliers LIMIT 10 OFFSET 50
216
-
217
- def add_limit_offset!(sql, options)
218
- if limit = options[:limit]
219
- sql << " LIMIT #{sanitize_limit(limit)}"
220
- end
221
- if offset = options[:offset]
222
- sql << " OFFSET #{offset.to_i}"
223
- end
224
- sql
225
- end
226
-
227
279
  def default_sequence_name(table, column)
228
280
  nil
229
281
  end
230
282
 
231
283
  # Set the sequence to the max value of the table's column.
232
284
  def reset_sequence!(table, column, sequence = nil)
233
- # Do nothing by default. Implement for PostgreSQL, Oracle, ...
285
+ # Do nothing by default. Implement for PostgreSQL, Oracle, ...
234
286
  end
235
287
 
236
288
  # Inserts the given fixture into the table. Overridden in adapters that require
237
289
  # something beyond a simple insert (eg. Oracle).
238
290
  def insert_fixture(fixture, table_name)
239
- execute "INSERT INTO #{quote_table_name(table_name)} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert'
291
+ columns = schema_cache.columns_hash(table_name)
292
+
293
+ key_list = []
294
+ value_list = fixture.map do |name, value|
295
+ key_list << quote_column_name(name)
296
+ quote(value, columns[name])
297
+ end
298
+
299
+ execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
240
300
  end
241
301
 
242
302
  def empty_insert_statement_value
243
- "VALUES(DEFAULT)"
303
+ "DEFAULT VALUES"
244
304
  end
245
305
 
246
306
  def case_sensitive_equality_operator
@@ -251,10 +311,53 @@ module ActiveRecord
251
311
  "WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
252
312
  end
253
313
 
314
+ # Sanitizes the given LIMIT parameter in order to prevent SQL injection.
315
+ #
316
+ # The +limit+ may be anything that can evaluate to a string via #to_s. It
317
+ # should look like an integer, or a comma-delimited list of integers, or
318
+ # an Arel SQL literal.
319
+ #
320
+ # Returns Integer and Arel::Nodes::SqlLiteral limits as is.
321
+ # Returns the sanitized limit parameter, either as an integer, or as a
322
+ # string which contains a comma-delimited list of integers.
323
+ def sanitize_limit(limit)
324
+ if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
325
+ limit
326
+ elsif limit.to_s =~ /,/
327
+ Arel.sql limit.to_s.split(',').map{ |i| Integer(i) }.join(',')
328
+ else
329
+ Integer(limit)
330
+ end
331
+ end
332
+
333
+ # The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
334
+ # on mysql (even when aliasing the tables), but mysql allows using JOIN directly in
335
+ # an UPDATE statement, so in the mysql adapters we redefine this to do that.
336
+ def join_to_update(update, select) #:nodoc:
337
+ key = update.key
338
+ subselect = subquery_for(key, select)
339
+
340
+ update.where key.in(subselect)
341
+ end
342
+
343
+ def join_to_delete(delete, select, key) #:nodoc:
344
+ subselect = subquery_for(key, select)
345
+
346
+ delete.where key.in(subselect)
347
+ end
348
+
254
349
  protected
350
+
351
+ # Return a subquery for the given key using the join information.
352
+ def subquery_for(key, select)
353
+ subselect = select.clone
354
+ subselect.projections = [key]
355
+ subselect
356
+ end
357
+
255
358
  # Returns an array of record hashes with the column names as keys and
256
359
  # column values as values.
257
- def select(sql, name = nil)
360
+ def select(sql, name = nil, binds = [])
258
361
  end
259
362
  undef_method :select
260
363
 
@@ -274,56 +377,14 @@ module ActiveRecord
274
377
  update_sql(sql, name)
275
378
  end
276
379
 
277
- # Sanitizes the given LIMIT parameter in order to prevent SQL injection.
278
- #
279
- # +limit+ may be anything that can evaluate to a string via #to_s. It
280
- # should look like an integer, or a comma-delimited list of integers.
281
- #
282
- # Returns the sanitized limit parameter, either as an integer, or as a
283
- # string which contains a comma-delimited list of integers.
284
- def sanitize_limit(limit)
285
- if limit.to_s =~ /,/
286
- limit.to_s.split(',').map{ |i| i.to_i }.join(',')
287
- else
288
- limit.to_i
289
- end
290
- end
291
-
292
- # Send a rollback message to all records after they have been rolled back. If rollback
293
- # is false, only rollback records since the last save point.
294
- def rollback_transaction_records(rollback) #:nodoc
295
- if rollback
296
- records = @_current_transaction_records.flatten
297
- @_current_transaction_records.clear
298
- else
299
- records = @_current_transaction_records.pop
300
- end
301
-
302
- unless records.blank?
303
- records.uniq.each do |record|
304
- begin
305
- record.rolledback!(rollback)
306
- rescue Exception => e
307
- record.logger.error(e) if record.respond_to?(:logger) && record.logger
308
- end
309
- end
310
- end
311
- end
380
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
381
+ [sql, binds]
382
+ end
312
383
 
313
- # Send a commit message to all records after they have been committed.
314
- def commit_transaction_records #:nodoc
315
- records = @_current_transaction_records.flatten
316
- @_current_transaction_records.clear
317
- unless records.blank?
318
- records.uniq.each do |record|
319
- begin
320
- record.committed!
321
- rescue Exception => e
322
- record.logger.error(e) if record.respond_to?(:logger) && record.logger
323
- end
324
- end
325
- end
326
- end
384
+ def last_inserted_id(result)
385
+ row = result.rows.first
386
+ row && row.first
387
+ end
327
388
  end
328
389
  end
329
390
  end
@@ -1,19 +1,17 @@
1
- require 'active_support/core_ext/object/duplicable'
2
-
3
1
  module ActiveRecord
4
2
  module ConnectionAdapters # :nodoc:
5
3
  module QueryCache
6
4
  class << self
7
- def included(base)
5
+ def included(base) #:nodoc:
8
6
  dirties_query_cache base, :insert, :update, :delete
9
7
  end
10
8
 
11
9
  def dirties_query_cache(base, *method_names)
12
10
  method_names.each do |method_name|
13
11
  base.class_eval <<-end_code, __FILE__, __LINE__ + 1
14
- def #{method_name}(*) # def update_with_query_dirty(*args)
12
+ def #{method_name}(*) # def update_with_query_dirty(*)
15
13
  clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled
16
- super # update_without_query_dirty(*args)
14
+ super # super
17
15
  end # end
18
16
  end_code
19
17
  end
@@ -31,6 +29,14 @@ module ActiveRecord
31
29
  @query_cache_enabled = old
32
30
  end
33
31
 
32
+ def enable_query_cache!
33
+ @query_cache_enabled = true
34
+ end
35
+
36
+ def disable_query_cache!
37
+ @query_cache_enabled = false
38
+ end
39
+
34
40
  # Disable the query cache within the block.
35
41
  def uncached
36
42
  old, @query_cache_enabled = @query_cache_enabled, false
@@ -49,33 +55,41 @@ module ActiveRecord
49
55
  @query_cache.clear
50
56
  end
51
57
 
52
- def select_all(*args)
53
- if @query_cache_enabled
54
- cache_sql(args.first) { super }
58
+ def select_all(arel, name = nil, binds = [])
59
+ if @query_cache_enabled && !locked?(arel)
60
+ sql = to_sql(arel, binds)
61
+ cache_sql(sql, binds) { super(sql, name, binds) }
55
62
  else
56
63
  super
57
64
  end
58
65
  end
59
66
 
60
67
  private
61
- def cache_sql(sql)
62
- result =
63
- if @query_cache.has_key?(sql)
64
- ActiveSupport::Notifications.instrument("sql.active_record",
65
- :sql => sql, :name => "CACHE", :connection_id => self.object_id)
66
- @query_cache[sql]
67
- else
68
- @query_cache[sql] = yield
69
- end
70
68
 
71
- if Array === result
72
- result.collect { |row| row.dup }
69
+ def cache_sql(sql, binds)
70
+ result =
71
+ if @query_cache[sql].key?(binds)
72
+ ActiveSupport::Notifications.instrument("sql.active_record",
73
+ :sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id)
74
+ @query_cache[sql][binds]
73
75
  else
74
- result.duplicable? ? result.dup : result
76
+ @query_cache[sql][binds] = yield
75
77
  end
76
- rescue TypeError
77
- result
78
+
79
+ # FIXME: we should guarantee that all cached items are Result
80
+ # objects. Then we can avoid this conditional
81
+ if ActiveRecord::Result === result
82
+ result.dup
83
+ else
84
+ result.collect { |row| row.dup }
78
85
  end
86
+ end
87
+
88
+ # If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
89
+ # queries should not be cached.
90
+ def locked?(arel)
91
+ arel.respond_to?(:locked) && arel.locked
92
+ end
79
93
  end
80
94
  end
81
95
  end