activerecord 1.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 (255) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +213 -0
  5. data/examples/performance.rb +172 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record/aggregations.rb +180 -84
  8. data/lib/active_record/associations/alias_tracker.rb +76 -0
  9. data/lib/active_record/associations/association.rb +248 -0
  10. data/lib/active_record/associations/association_scope.rb +135 -0
  11. data/lib/active_record/associations/belongs_to_association.rb +92 -0
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +35 -0
  13. data/lib/active_record/associations/builder/association.rb +108 -0
  14. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  15. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  17. data/lib/active_record/associations/builder/has_many.rb +15 -0
  18. data/lib/active_record/associations/builder/has_one.rb +25 -0
  19. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  20. data/lib/active_record/associations/collection_association.rb +608 -0
  21. data/lib/active_record/associations/collection_proxy.rb +986 -0
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +58 -39
  23. data/lib/active_record/associations/has_many_association.rb +116 -85
  24. data/lib/active_record/associations/has_many_through_association.rb +197 -0
  25. data/lib/active_record/associations/has_one_association.rb +102 -0
  26. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  27. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_dependency.rb +235 -0
  31. data/lib/active_record/associations/join_helper.rb +45 -0
  32. data/lib/active_record/associations/preloader/association.rb +121 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  42. data/lib/active_record/associations/preloader.rb +178 -0
  43. data/lib/active_record/associations/singular_association.rb +64 -0
  44. data/lib/active_record/associations/through_association.rb +87 -0
  45. data/lib/active_record/associations.rb +1437 -431
  46. data/lib/active_record/attribute_assignment.rb +201 -0
  47. data/lib/active_record/attribute_methods/before_type_cast.rb +70 -0
  48. data/lib/active_record/attribute_methods/dirty.rb +118 -0
  49. data/lib/active_record/attribute_methods/primary_key.rb +122 -0
  50. data/lib/active_record/attribute_methods/query.rb +40 -0
  51. data/lib/active_record/attribute_methods/read.rb +107 -0
  52. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  53. data/lib/active_record/attribute_methods/time_zone_conversion.rb +59 -0
  54. data/lib/active_record/attribute_methods/write.rb +63 -0
  55. data/lib/active_record/attribute_methods.rb +393 -0
  56. data/lib/active_record/autosave_association.rb +426 -0
  57. data/lib/active_record/base.rb +268 -930
  58. data/lib/active_record/callbacks.rb +203 -230
  59. data/lib/active_record/coders/yaml_column.rb +38 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +638 -0
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +390 -0
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +129 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +501 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +873 -0
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +389 -275
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  71. data/lib/active_record/connection_adapters/column.rb +318 -0
  72. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  73. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  74. data/lib/active_record/connection_adapters/mysql_adapter.rb +517 -90
  75. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  76. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  77. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  79. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  80. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  81. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  82. data/lib/active_record/connection_adapters/postgresql_adapter.rb +911 -138
  83. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  84. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +624 -0
  85. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  86. data/lib/active_record/connection_handling.rb +98 -0
  87. data/lib/active_record/core.rb +463 -0
  88. data/lib/active_record/counter_cache.rb +122 -0
  89. data/lib/active_record/dynamic_matchers.rb +131 -0
  90. data/lib/active_record/errors.rb +213 -0
  91. data/lib/active_record/explain.rb +38 -0
  92. data/lib/active_record/explain_registry.rb +30 -0
  93. data/lib/active_record/explain_subscriber.rb +29 -0
  94. data/lib/active_record/fixture_set/file.rb +55 -0
  95. data/lib/active_record/fixtures.rb +892 -138
  96. data/lib/active_record/inheritance.rb +200 -0
  97. data/lib/active_record/integration.rb +60 -0
  98. data/lib/active_record/locale/en.yml +47 -0
  99. data/lib/active_record/locking/optimistic.rb +181 -0
  100. data/lib/active_record/locking/pessimistic.rb +77 -0
  101. data/lib/active_record/log_subscriber.rb +82 -0
  102. data/lib/active_record/migration/command_recorder.rb +164 -0
  103. data/lib/active_record/migration/join_table.rb +15 -0
  104. data/lib/active_record/migration.rb +1015 -0
  105. data/lib/active_record/model_schema.rb +345 -0
  106. data/lib/active_record/nested_attributes.rb +546 -0
  107. data/lib/active_record/null_relation.rb +65 -0
  108. data/lib/active_record/persistence.rb +509 -0
  109. data/lib/active_record/query_cache.rb +56 -0
  110. data/lib/active_record/querying.rb +62 -0
  111. data/lib/active_record/railtie.rb +205 -0
  112. data/lib/active_record/railties/console_sandbox.rb +5 -0
  113. data/lib/active_record/railties/controller_runtime.rb +50 -0
  114. data/lib/active_record/railties/databases.rake +402 -0
  115. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  116. data/lib/active_record/readonly_attributes.rb +30 -0
  117. data/lib/active_record/reflection.rb +544 -87
  118. data/lib/active_record/relation/batches.rb +93 -0
  119. data/lib/active_record/relation/calculations.rb +399 -0
  120. data/lib/active_record/relation/delegation.rb +125 -0
  121. data/lib/active_record/relation/finder_methods.rb +349 -0
  122. data/lib/active_record/relation/merger.rb +161 -0
  123. data/lib/active_record/relation/predicate_builder.rb +106 -0
  124. data/lib/active_record/relation/query_methods.rb +1044 -0
  125. data/lib/active_record/relation/spawn_methods.rb +73 -0
  126. data/lib/active_record/relation.rb +655 -0
  127. data/lib/active_record/result.rb +67 -0
  128. data/lib/active_record/runtime_registry.rb +17 -0
  129. data/lib/active_record/sanitization.rb +168 -0
  130. data/lib/active_record/schema.rb +65 -0
  131. data/lib/active_record/schema_dumper.rb +204 -0
  132. data/lib/active_record/schema_migration.rb +39 -0
  133. data/lib/active_record/scoping/default.rb +146 -0
  134. data/lib/active_record/scoping/named.rb +175 -0
  135. data/lib/active_record/scoping.rb +82 -0
  136. data/lib/active_record/serialization.rb +22 -0
  137. data/lib/active_record/serializers/xml_serializer.rb +197 -0
  138. data/lib/active_record/statement_cache.rb +26 -0
  139. data/lib/active_record/store.rb +156 -0
  140. data/lib/active_record/tasks/database_tasks.rb +203 -0
  141. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  142. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  143. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  144. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  145. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  146. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  147. data/lib/active_record/test_case.rb +96 -0
  148. data/lib/active_record/timestamp.rb +119 -0
  149. data/lib/active_record/transactions.rb +366 -69
  150. data/lib/active_record/translation.rb +22 -0
  151. data/lib/active_record/validations/associated.rb +49 -0
  152. data/lib/active_record/validations/presence.rb +65 -0
  153. data/lib/active_record/validations/uniqueness.rb +225 -0
  154. data/lib/active_record/validations.rb +64 -185
  155. data/lib/active_record/version.rb +11 -0
  156. data/lib/active_record.rb +149 -24
  157. data/lib/rails/generators/active_record/migration/migration_generator.rb +62 -0
  158. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  159. data/lib/rails/generators/active_record/migration/templates/migration.rb +39 -0
  160. data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
  161. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  162. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  163. data/lib/rails/generators/active_record.rb +23 -0
  164. metadata +261 -161
  165. data/CHANGELOG +0 -581
  166. data/README +0 -361
  167. data/RUNNING_UNIT_TESTS +0 -36
  168. data/dev-utils/eval_debugger.rb +0 -9
  169. data/examples/associations.png +0 -0
  170. data/examples/associations.rb +0 -87
  171. data/examples/shared_setup.rb +0 -15
  172. data/examples/validation.rb +0 -88
  173. data/install.rb +0 -60
  174. data/lib/active_record/associations/association_collection.rb +0 -70
  175. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -107
  176. data/lib/active_record/deprecated_associations.rb +0 -70
  177. data/lib/active_record/observer.rb +0 -71
  178. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  179. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  180. data/lib/active_record/support/clean_logger.rb +0 -10
  181. data/lib/active_record/support/inflector.rb +0 -70
  182. data/lib/active_record/vendor/mysql.rb +0 -1117
  183. data/lib/active_record/vendor/simple.rb +0 -702
  184. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  185. data/lib/active_record/wrappings.rb +0 -59
  186. data/rakefile +0 -122
  187. data/test/abstract_unit.rb +0 -16
  188. data/test/aggregations_test.rb +0 -34
  189. data/test/all.sh +0 -8
  190. data/test/associations_test.rb +0 -477
  191. data/test/base_test.rb +0 -513
  192. data/test/class_inheritable_attributes_test.rb +0 -33
  193. data/test/connections/native_mysql/connection.rb +0 -24
  194. data/test/connections/native_postgresql/connection.rb +0 -24
  195. data/test/connections/native_sqlite/connection.rb +0 -24
  196. data/test/deprecated_associations_test.rb +0 -336
  197. data/test/finder_test.rb +0 -67
  198. data/test/fixtures/accounts/signals37 +0 -3
  199. data/test/fixtures/accounts/unknown +0 -2
  200. data/test/fixtures/auto_id.rb +0 -4
  201. data/test/fixtures/column_name.rb +0 -3
  202. data/test/fixtures/companies/first_client +0 -6
  203. data/test/fixtures/companies/first_firm +0 -4
  204. data/test/fixtures/companies/second_client +0 -6
  205. data/test/fixtures/company.rb +0 -37
  206. data/test/fixtures/company_in_module.rb +0 -33
  207. data/test/fixtures/course.rb +0 -3
  208. data/test/fixtures/courses/java +0 -2
  209. data/test/fixtures/courses/ruby +0 -2
  210. data/test/fixtures/customer.rb +0 -30
  211. data/test/fixtures/customers/david +0 -6
  212. data/test/fixtures/db_definitions/mysql.sql +0 -96
  213. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  214. data/test/fixtures/db_definitions/postgresql.sql +0 -113
  215. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  216. data/test/fixtures/db_definitions/sqlite.sql +0 -85
  217. data/test/fixtures/db_definitions/sqlite2.sql +0 -4
  218. data/test/fixtures/default.rb +0 -2
  219. data/test/fixtures/developer.rb +0 -8
  220. data/test/fixtures/developers/david +0 -2
  221. data/test/fixtures/developers/jamis +0 -2
  222. data/test/fixtures/developers_projects/david_action_controller +0 -2
  223. data/test/fixtures/developers_projects/david_active_record +0 -2
  224. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  225. data/test/fixtures/entrant.rb +0 -3
  226. data/test/fixtures/entrants/first +0 -3
  227. data/test/fixtures/entrants/second +0 -3
  228. data/test/fixtures/entrants/third +0 -3
  229. data/test/fixtures/fixture_database.sqlite +0 -0
  230. data/test/fixtures/fixture_database_2.sqlite +0 -0
  231. data/test/fixtures/movie.rb +0 -5
  232. data/test/fixtures/movies/first +0 -2
  233. data/test/fixtures/movies/second +0 -2
  234. data/test/fixtures/project.rb +0 -3
  235. data/test/fixtures/projects/action_controller +0 -2
  236. data/test/fixtures/projects/active_record +0 -2
  237. data/test/fixtures/reply.rb +0 -21
  238. data/test/fixtures/subscriber.rb +0 -5
  239. data/test/fixtures/subscribers/first +0 -2
  240. data/test/fixtures/subscribers/second +0 -2
  241. data/test/fixtures/topic.rb +0 -20
  242. data/test/fixtures/topics/first +0 -9
  243. data/test/fixtures/topics/second +0 -8
  244. data/test/fixtures_test.rb +0 -20
  245. data/test/inflector_test.rb +0 -104
  246. data/test/inheritance_test.rb +0 -125
  247. data/test/lifecycle_test.rb +0 -110
  248. data/test/modules_test.rb +0 -21
  249. data/test/multiple_db_test.rb +0 -46
  250. data/test/pk_test.rb +0 -57
  251. data/test/reflection_test.rb +0 -78
  252. data/test/thread_safety_test.rb +0 -33
  253. data/test/transactions_test.rb +0 -83
  254. data/test/unconnected_test.rb +0 -24
  255. data/test/validations_test.rb +0 -126
@@ -0,0 +1,390 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters # :nodoc:
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
+
21
+ # Returns an array of record hashes with the column names as keys and
22
+ # column values as values.
23
+ def select_all(arel, name = nil, binds = [])
24
+ select(to_sql(arel, binds), name, binds)
25
+ end
26
+
27
+ # Returns a record hash with the column names as keys and column values
28
+ # as values.
29
+ def select_one(arel, name = nil, binds = [])
30
+ result = select_all(arel, name, binds)
31
+ result.first if result
32
+ end
33
+
34
+ # Returns a single value from a record
35
+ def select_value(arel, name = nil, binds = [])
36
+ if result = select_one(arel, name, binds)
37
+ result.values.first
38
+ end
39
+ end
40
+
41
+ # Returns an array of the values of the first column in a select:
42
+ # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
43
+ def select_values(arel, name = nil)
44
+ result = select_rows(to_sql(arel, []), name)
45
+ result.map { |v| v[0] }
46
+ end
47
+
48
+ # Returns an array of arrays containing the field values.
49
+ # Order is the same as that returned by +columns+.
50
+ def select_rows(sql, name = nil)
51
+ end
52
+ undef_method :select_rows
53
+
54
+ # Executes the SQL statement in the context of this connection.
55
+ def execute(sql, name = nil)
56
+ end
57
+ undef_method :execute
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
+
86
+ # Returns the last auto-generated ID from the affected table.
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)
98
+ end
99
+
100
+ # Executes the update statement and returns the number of rows affected.
101
+ def update(arel, name = nil, binds = [])
102
+ exec_update(to_sql(arel, binds), name, binds)
103
+ end
104
+
105
+ # Executes the delete statement and returns the number of rows affected.
106
+ def delete(arel, name = nil, binds = [])
107
+ exec_delete(to_sql(arel, binds), name, binds)
108
+ end
109
+
110
+ # Returns +true+ when the connection adapter supports prepared statement
111
+ # caching, otherwise returns +false+
112
+ def supports_statement_cache?
113
+ false
114
+ end
115
+
116
+ # Runs the given block in a database transaction, and returns the result
117
+ # of the block.
118
+ #
119
+ # == Nested transactions support
120
+ #
121
+ # Most databases don't support true nested transactions. At the time of
122
+ # writing, the only database that supports true nested transactions that
123
+ # we're aware of, is MS-SQL.
124
+ #
125
+ # In order to get around this problem, #transaction will emulate the effect
126
+ # of nested transactions, by using savepoints:
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.
130
+ #
131
+ # It is safe to call this method if a database transaction is already open,
132
+ # i.e. if #transaction is called within another #transaction block. In case
133
+ # of a nested call, #transaction will behave as follows:
134
+ #
135
+ # - The block will be run without doing anything. All database statements
136
+ # that happen within the block are effectively appended to the already
137
+ # open database transaction.
138
+ # - However, if +:requires_new+ is set, the block will be wrapped in a
139
+ # database savepoint acting as a sub-transaction.
140
+ #
141
+ # === Caveats
142
+ #
143
+ # MySQL doesn't support DDL transactions. If you perform a DDL operation,
144
+ # then any created savepoints will be automatically released. For example,
145
+ # if you've created a savepoint, then you execute a CREATE TABLE statement,
146
+ # then the savepoint that was created will be automatically released.
147
+ #
148
+ # This means that, on MySQL, you shouldn't execute DDL operations inside
149
+ # a #transaction call that you know might create a savepoint. Otherwise,
150
+ # #transaction will raise exceptions when it tries to release the
151
+ # already-automatically-released savepoints:
152
+ #
153
+ # Model.connection.transaction do # BEGIN
154
+ # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
155
+ # Model.connection.create_table(...)
156
+ # # active_record_1 now automatically released
157
+ # end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
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.
192
+ def transaction(options = {})
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
199
+
200
+ yield
201
+ else
202
+ within_new_transaction(options) { yield }
203
+ end
204
+ rescue ActiveRecord::Rollback
205
+ # rollbacks are silently swallowed
206
+ end
207
+
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
214
+ ensure
215
+ begin
216
+ commit_transaction unless error
217
+ rescue Exception
218
+ rollback_transaction
219
+ raise
220
+ end
221
+ end
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
+
247
+ # Register a record with the current transaction so that its after_commit and after_rollback callbacks
248
+ # can be called.
249
+ def add_transaction_record(record)
250
+ @transaction.add_record(record)
251
+ end
252
+
253
+ # Begins the transaction (and turns off auto-committing).
254
+ def begin_db_transaction() end
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
+
272
+ # Commits the transaction (and turns on auto-committing).
273
+ def commit_db_transaction() end
274
+
275
+ # Rolls back the transaction (and turns on auto-committing). Must be
276
+ # done if the transaction block raises an exception or returns false.
277
+ def rollback_db_transaction() end
278
+
279
+ def default_sequence_name(table, column)
280
+ nil
281
+ end
282
+
283
+ # Set the sequence to the max value of the table's column.
284
+ def reset_sequence!(table, column, sequence = nil)
285
+ # Do nothing by default. Implement for PostgreSQL, Oracle, ...
286
+ end
287
+
288
+ # Inserts the given fixture into the table. Overridden in adapters that require
289
+ # something beyond a simple insert (eg. Oracle).
290
+ def insert_fixture(fixture, table_name)
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'
300
+ end
301
+
302
+ def empty_insert_statement_value
303
+ "DEFAULT VALUES"
304
+ end
305
+
306
+ def case_sensitive_equality_operator
307
+ "="
308
+ end
309
+
310
+ def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
311
+ "WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
312
+ end
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
+
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
+
358
+ # Returns an array of record hashes with the column names as keys and
359
+ # column values as values.
360
+ def select(sql, name = nil, binds = [])
361
+ end
362
+ undef_method :select
363
+
364
+ # Returns the last auto-generated ID from the affected table.
365
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
366
+ execute(sql, name)
367
+ id_value
368
+ end
369
+
370
+ # Executes the update statement and returns the number of rows affected.
371
+ def update_sql(sql, name = nil)
372
+ execute(sql, name)
373
+ end
374
+
375
+ # Executes the delete statement and returns the number of rows affected.
376
+ def delete_sql(sql, name = nil)
377
+ update_sql(sql, name)
378
+ end
379
+
380
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
381
+ [sql, binds]
382
+ end
383
+
384
+ def last_inserted_id(result)
385
+ row = result.rows.first
386
+ row && row.first
387
+ end
388
+ end
389
+ end
390
+ end
@@ -0,0 +1,95 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module QueryCache
4
+ class << self
5
+ def included(base) #:nodoc:
6
+ dirties_query_cache base, :insert, :update, :delete
7
+ end
8
+
9
+ def dirties_query_cache(base, *method_names)
10
+ method_names.each do |method_name|
11
+ base.class_eval <<-end_code, __FILE__, __LINE__ + 1
12
+ def #{method_name}(*) # def update_with_query_dirty(*)
13
+ clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled
14
+ super # super
15
+ end # end
16
+ end_code
17
+ end
18
+ end
19
+ end
20
+
21
+ attr_reader :query_cache, :query_cache_enabled
22
+
23
+ # Enable the query cache within the block.
24
+ def cache
25
+ old, @query_cache_enabled = @query_cache_enabled, true
26
+ yield
27
+ ensure
28
+ clear_query_cache
29
+ @query_cache_enabled = old
30
+ end
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
+
40
+ # Disable the query cache within the block.
41
+ def uncached
42
+ old, @query_cache_enabled = @query_cache_enabled, false
43
+ yield
44
+ ensure
45
+ @query_cache_enabled = old
46
+ end
47
+
48
+ # Clears the query cache.
49
+ #
50
+ # One reason you may wish to call this method explicitly is between queries
51
+ # that ask the database to randomize results. Otherwise the cache would see
52
+ # the same SQL query and repeatedly return the same result each time, silently
53
+ # undermining the randomness you were expecting.
54
+ def clear_query_cache
55
+ @query_cache.clear
56
+ end
57
+
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) }
62
+ else
63
+ super
64
+ end
65
+ end
66
+
67
+ private
68
+
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]
75
+ else
76
+ @query_cache[sql][binds] = yield
77
+ end
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 }
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
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,129 @@
1
+ require 'active_support/core_ext/big_decimal/conversions'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters # :nodoc:
5
+ module Quoting
6
+ # Quotes the column value to help prevent
7
+ # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
8
+ def quote(value, column = nil)
9
+ # records are quoted as their primary key
10
+ return value.quoted_id if value.respond_to?(:quoted_id)
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 :binary then "'#{quote_string(column.string_to_binary(value))}'"
19
+ when :integer then value.to_i.to_s
20
+ when :float then value.to_f.to_s
21
+ else
22
+ "'#{quote_string(value)}'"
23
+ end
24
+
25
+ when true, false
26
+ if column && column.type == :integer
27
+ value ? '1' : '0'
28
+ else
29
+ value ? quoted_true : quoted_false
30
+ end
31
+ # BigDecimals need to be put in a non-normalized form and quoted.
32
+ when nil then "NULL"
33
+ when BigDecimal then value.to_s('F')
34
+ when Numeric, ActiveSupport::Duration then value.to_s
35
+ when Date, Time then "'#{quoted_date(value)}'"
36
+ when Symbol then "'#{quote_string(value.to_s)}'"
37
+ when Class then "'#{value.to_s}'"
38
+ else
39
+ "'#{quote_string(YAML.dump(value))}'"
40
+ end
41
+ end
42
+
43
+ # Cast a +value+ to a type that the database understands. For example,
44
+ # SQLite does not understand dates, so this method will convert a Date
45
+ # to a String.
46
+ def type_cast(value, column)
47
+ return value.id if value.respond_to?(:quoted_id)
48
+
49
+ case value
50
+ when String, ActiveSupport::Multibyte::Chars
51
+ value = value.to_s
52
+ return value unless column
53
+
54
+ case column.type
55
+ when :binary then value
56
+ when :integer then value.to_i
57
+ when :float then value.to_f
58
+ else
59
+ value
60
+ end
61
+
62
+ when true, false
63
+ if column && column.type == :integer
64
+ value ? 1 : 0
65
+ else
66
+ value ? 't' : 'f'
67
+ end
68
+ # BigDecimals need to be put in a non-normalized form and quoted.
69
+ when nil then nil
70
+ when BigDecimal then value.to_s('F')
71
+ when Numeric then value
72
+ when Date, Time then quoted_date(value)
73
+ when Symbol then value.to_s
74
+ else
75
+ to_type = column ? " to #{column.type}" : ""
76
+ raise TypeError, "can't cast #{value.class}#{to_type}"
77
+ end
78
+ end
79
+
80
+ # Quotes a string, escaping any ' (single quote) and \ (backslash)
81
+ # characters.
82
+ def quote_string(s)
83
+ s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
84
+ end
85
+
86
+ # Quotes the column name. Defaults to no quoting.
87
+ def quote_column_name(column_name)
88
+ column_name
89
+ end
90
+
91
+ # Quotes the table name. Defaults to column name quoting.
92
+ def quote_table_name(table_name)
93
+ quote_column_name(table_name)
94
+ end
95
+
96
+ # Override to return the quoted table name for assignment. Defaults to
97
+ # table quoting.
98
+ #
99
+ # This works for mysql and mysql2 where table.column can be used to
100
+ # resolve ambiguity.
101
+ #
102
+ # We override this in the sqlite and postgresql adapters to use only
103
+ # the column name (as per syntax requirements).
104
+ def quote_table_name_for_assignment(table, attr)
105
+ quote_table_name("#{table}.#{attr}")
106
+ end
107
+
108
+ def quoted_true
109
+ "'t'"
110
+ end
111
+
112
+ def quoted_false
113
+ "'f'"
114
+ end
115
+
116
+ def quoted_date(value)
117
+ if value.acts_like?(:time)
118
+ zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
119
+
120
+ if value.respond_to?(zone_conversion_method)
121
+ value = value.send(zone_conversion_method)
122
+ end
123
+ end
124
+
125
+ value.to_s(:db)
126
+ end
127
+ end
128
+ end
129
+ end