activerecord 3.2.19 → 5.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 (264) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1715 -604
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +40 -45
  5. data/examples/performance.rb +33 -22
  6. data/examples/simple.rb +3 -4
  7. data/lib/active_record/aggregations.rb +76 -51
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +54 -40
  10. data/lib/active_record/associations/association.rb +76 -56
  11. data/lib/active_record/associations/association_scope.rb +125 -93
  12. data/lib/active_record/associations/belongs_to_association.rb +57 -28
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  14. data/lib/active_record/associations/builder/association.rb +120 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +115 -62
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -53
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +117 -43
  18. data/lib/active_record/associations/builder/has_many.rb +9 -65
  19. data/lib/active_record/associations/builder/has_one.rb +18 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +18 -19
  21. data/lib/active_record/associations/collection_association.rb +268 -186
  22. data/lib/active_record/associations/collection_proxy.rb +1003 -63
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +81 -41
  25. data/lib/active_record/associations/has_many_through_association.rb +76 -55
  26. data/lib/active_record/associations/has_one_association.rb +51 -21
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +83 -108
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +239 -155
  32. data/lib/active_record/associations/preloader/association.rb +97 -62
  33. data/lib/active_record/associations/preloader/collection_association.rb +2 -8
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +75 -33
  38. data/lib/active_record/associations/preloader.rb +111 -79
  39. data/lib/active_record/associations/singular_association.rb +35 -13
  40. data/lib/active_record/associations/through_association.rb +41 -19
  41. data/lib/active_record/associations.rb +727 -501
  42. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  43. data/lib/active_record/attribute.rb +213 -0
  44. data/lib/active_record/attribute_assignment.rb +32 -162
  45. data/lib/active_record/attribute_decorators.rb +67 -0
  46. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  47. data/lib/active_record/attribute_methods/dirty.rb +101 -61
  48. data/lib/active_record/attribute_methods/primary_key.rb +50 -36
  49. data/lib/active_record/attribute_methods/query.rb +7 -6
  50. data/lib/active_record/attribute_methods/read.rb +56 -117
  51. data/lib/active_record/attribute_methods/serialization.rb +43 -96
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +93 -42
  53. data/lib/active_record/attribute_methods/write.rb +34 -45
  54. data/lib/active_record/attribute_methods.rb +333 -144
  55. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  56. data/lib/active_record/attribute_set/builder.rb +108 -0
  57. data/lib/active_record/attribute_set.rb +108 -0
  58. data/lib/active_record/attributes.rb +265 -0
  59. data/lib/active_record/autosave_association.rb +285 -223
  60. data/lib/active_record/base.rb +95 -490
  61. data/lib/active_record/callbacks.rb +95 -61
  62. data/lib/active_record/coders/json.rb +13 -0
  63. data/lib/active_record/coders/yaml_column.rb +28 -19
  64. data/lib/active_record/collection_cache_key.rb +40 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +724 -277
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -192
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -26
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +140 -57
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +147 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +419 -276
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +105 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +963 -276
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +232 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +397 -106
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +643 -342
  78. data/lib/active_record/connection_adapters/column.rb +30 -259
  79. data/lib/active_record/connection_adapters/connection_specification.rb +263 -0
  80. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  81. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  82. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  83. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  84. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  86. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  87. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  88. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  89. data/lib/active_record/connection_adapters/mysql2_adapter.rb +47 -196
  90. data/lib/active_record/connection_adapters/postgresql/column.rb +15 -0
  91. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +170 -0
  92. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +70 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +48 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +21 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +10 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +39 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +93 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  112. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  113. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  114. data/lib/active_record/connection_adapters/postgresql/oid.rb +31 -0
  115. data/lib/active_record/connection_adapters/postgresql/quoting.rb +116 -0
  116. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +49 -0
  117. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +180 -0
  118. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  119. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +682 -0
  120. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  121. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  122. data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -1039
  123. data/lib/active_record/connection_adapters/schema_cache.rb +74 -36
  124. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  125. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +538 -24
  129. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  130. data/lib/active_record/connection_handling.rb +155 -0
  131. data/lib/active_record/core.rb +561 -0
  132. data/lib/active_record/counter_cache.rb +146 -105
  133. data/lib/active_record/dynamic_matchers.rb +101 -64
  134. data/lib/active_record/enum.rb +234 -0
  135. data/lib/active_record/errors.rb +153 -56
  136. data/lib/active_record/explain.rb +15 -63
  137. data/lib/active_record/explain_registry.rb +30 -0
  138. data/lib/active_record/explain_subscriber.rb +10 -6
  139. data/lib/active_record/fixture_set/file.rb +77 -0
  140. data/lib/active_record/fixtures.rb +355 -232
  141. data/lib/active_record/gem_version.rb +15 -0
  142. data/lib/active_record/inheritance.rb +144 -79
  143. data/lib/active_record/integration.rb +66 -13
  144. data/lib/active_record/internal_metadata.rb +56 -0
  145. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  146. data/lib/active_record/locale/en.yml +9 -1
  147. data/lib/active_record/locking/optimistic.rb +77 -56
  148. data/lib/active_record/locking/pessimistic.rb +6 -6
  149. data/lib/active_record/log_subscriber.rb +53 -28
  150. data/lib/active_record/migration/command_recorder.rb +166 -33
  151. data/lib/active_record/migration/compatibility.rb +126 -0
  152. data/lib/active_record/migration/join_table.rb +15 -0
  153. data/lib/active_record/migration.rb +792 -264
  154. data/lib/active_record/model_schema.rb +192 -130
  155. data/lib/active_record/nested_attributes.rb +238 -145
  156. data/lib/active_record/no_touching.rb +52 -0
  157. data/lib/active_record/null_relation.rb +89 -0
  158. data/lib/active_record/persistence.rb +357 -157
  159. data/lib/active_record/query_cache.rb +22 -43
  160. data/lib/active_record/querying.rb +34 -23
  161. data/lib/active_record/railtie.rb +88 -48
  162. data/lib/active_record/railties/console_sandbox.rb +3 -4
  163. data/lib/active_record/railties/controller_runtime.rb +5 -4
  164. data/lib/active_record/railties/databases.rake +170 -422
  165. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  166. data/lib/active_record/readonly_attributes.rb +2 -5
  167. data/lib/active_record/reflection.rb +715 -189
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  169. data/lib/active_record/relation/batches.rb +203 -50
  170. data/lib/active_record/relation/calculations.rb +203 -194
  171. data/lib/active_record/relation/delegation.rb +103 -25
  172. data/lib/active_record/relation/finder_methods.rb +457 -261
  173. data/lib/active_record/relation/from_clause.rb +32 -0
  174. data/lib/active_record/relation/merger.rb +167 -0
  175. data/lib/active_record/relation/predicate_builder/array_handler.rb +43 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  177. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  179. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  180. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  181. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  182. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  183. data/lib/active_record/relation/predicate_builder.rb +153 -48
  184. data/lib/active_record/relation/query_attribute.rb +19 -0
  185. data/lib/active_record/relation/query_methods.rb +1019 -194
  186. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  187. data/lib/active_record/relation/spawn_methods.rb +46 -150
  188. data/lib/active_record/relation/where_clause.rb +174 -0
  189. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  190. data/lib/active_record/relation.rb +450 -245
  191. data/lib/active_record/result.rb +104 -12
  192. data/lib/active_record/runtime_registry.rb +22 -0
  193. data/lib/active_record/sanitization.rb +120 -94
  194. data/lib/active_record/schema.rb +28 -18
  195. data/lib/active_record/schema_dumper.rb +141 -74
  196. data/lib/active_record/schema_migration.rb +50 -0
  197. data/lib/active_record/scoping/default.rb +64 -57
  198. data/lib/active_record/scoping/named.rb +93 -108
  199. data/lib/active_record/scoping.rb +73 -121
  200. data/lib/active_record/secure_token.rb +38 -0
  201. data/lib/active_record/serialization.rb +7 -5
  202. data/lib/active_record/statement_cache.rb +113 -0
  203. data/lib/active_record/store.rb +173 -15
  204. data/lib/active_record/suppressor.rb +58 -0
  205. data/lib/active_record/table_metadata.rb +68 -0
  206. data/lib/active_record/tasks/database_tasks.rb +313 -0
  207. data/lib/active_record/tasks/mysql_database_tasks.rb +151 -0
  208. data/lib/active_record/tasks/postgresql_database_tasks.rb +110 -0
  209. data/lib/active_record/tasks/sqlite_database_tasks.rb +59 -0
  210. data/lib/active_record/timestamp.rb +42 -24
  211. data/lib/active_record/touch_later.rb +58 -0
  212. data/lib/active_record/transactions.rb +233 -105
  213. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  214. data/lib/active_record/type/date.rb +7 -0
  215. data/lib/active_record/type/date_time.rb +7 -0
  216. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  217. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  218. data/lib/active_record/type/internal/timezone.rb +15 -0
  219. data/lib/active_record/type/serialized.rb +63 -0
  220. data/lib/active_record/type/time.rb +20 -0
  221. data/lib/active_record/type/type_map.rb +64 -0
  222. data/lib/active_record/type.rb +72 -0
  223. data/lib/active_record/type_caster/connection.rb +29 -0
  224. data/lib/active_record/type_caster/map.rb +19 -0
  225. data/lib/active_record/type_caster.rb +7 -0
  226. data/lib/active_record/validations/absence.rb +23 -0
  227. data/lib/active_record/validations/associated.rb +33 -18
  228. data/lib/active_record/validations/length.rb +24 -0
  229. data/lib/active_record/validations/presence.rb +66 -0
  230. data/lib/active_record/validations/uniqueness.rb +128 -68
  231. data/lib/active_record/validations.rb +48 -40
  232. data/lib/active_record/version.rb +5 -7
  233. data/lib/active_record.rb +71 -47
  234. data/lib/rails/generators/active_record/migration/migration_generator.rb +56 -8
  235. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +24 -0
  236. data/lib/rails/generators/active_record/migration/templates/migration.rb +28 -16
  237. data/lib/rails/generators/active_record/migration.rb +18 -8
  238. data/lib/rails/generators/active_record/model/model_generator.rb +38 -16
  239. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  240. data/lib/rails/generators/active_record/model/templates/model.rb +7 -6
  241. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  242. data/lib/rails/generators/active_record.rb +3 -11
  243. metadata +188 -134
  244. data/examples/associations.png +0 -0
  245. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  246. data/lib/active_record/associations/join_helper.rb +0 -55
  247. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  248. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  249. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  250. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  251. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  252. data/lib/active_record/dynamic_finder_match.rb +0 -68
  253. data/lib/active_record/dynamic_scope_match.rb +0 -23
  254. data/lib/active_record/fixtures/file.rb +0 -65
  255. data/lib/active_record/identity_map.rb +0 -162
  256. data/lib/active_record/observer.rb +0 -121
  257. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  258. data/lib/active_record/session_store.rb +0 -360
  259. data/lib/active_record/test_case.rb +0 -73
  260. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  261. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  262. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  263. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  264. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -17,6 +17,15 @@ module ActiveRecord
17
17
  64
18
18
  end
19
19
 
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
+ # #index_name_length. The gap between
23
+ # #index_name_length 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
+
20
29
  # Returns the maximum length of an index name.
21
30
  def index_name_length
22
31
  64
@@ -1,83 +1,117 @@
1
1
  module ActiveRecord
2
2
  module ConnectionAdapters # :nodoc:
3
3
  module DatabaseStatements
4
+ def initialize
5
+ super
6
+ reset_transaction
7
+ end
8
+
4
9
  # Converts an arel AST to SQL
5
10
  def to_sql(arel, binds = [])
6
11
  if arel.respond_to?(:ast)
7
- visitor.accept(arel.ast) do
8
- quote(*binds.shift.reverse)
9
- end
12
+ collected = visitor.accept(arel.ast, collector)
13
+ collected.compile(binds.dup, self)
10
14
  else
11
15
  arel
12
16
  end
13
17
  end
14
18
 
15
- # Returns an array of record hashes with the column names as keys and
16
- # column values as values.
17
- def select_all(arel, name = nil, binds = [])
18
- select(to_sql(arel, binds), name, binds)
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
+
29
+ # Returns an ActiveRecord::Result instance.
30
+ def select_all(arel, name = nil, binds = [], preparable: nil)
31
+ arel, binds = binds_from_relation arel, binds
32
+ sql = to_sql(arel, binds)
33
+ if !prepared_statements || (arel.is_a?(String) && preparable.nil?)
34
+ preparable = false
35
+ else
36
+ preparable = visitor.preparable
37
+ end
38
+ if prepared_statements && preparable
39
+ select_prepared(sql, name, binds)
40
+ else
41
+ select(sql, name, binds)
42
+ end
19
43
  end
20
44
 
21
45
  # Returns a record hash with the column names as keys and column values
22
46
  # as values.
23
- def select_one(arel, name = nil)
24
- result = select_all(arel, name)
25
- result.first if result
47
+ def select_one(arel, name = nil, binds = [])
48
+ select_all(arel, name, binds).first
26
49
  end
27
50
 
28
51
  # Returns a single value from a record
29
- def select_value(arel, name = nil)
30
- if result = select_one(arel, name)
31
- result.values.first
52
+ def select_value(arel, name = nil, binds = [])
53
+ arel, binds = binds_from_relation arel, binds
54
+ if result = select_rows(to_sql(arel, binds), name, binds).first
55
+ result.first
32
56
  end
33
57
  end
34
58
 
35
59
  # Returns an array of the values of the first column in a select:
36
60
  # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
37
- def select_values(arel, name = nil)
38
- result = select_rows(to_sql(arel, []), name)
39
- result.map { |v| v[0] }
61
+ def select_values(arel, name = nil, binds = [])
62
+ arel, binds = binds_from_relation arel, binds
63
+ select_rows(to_sql(arel, binds), name, binds).map(&:first)
40
64
  end
41
65
 
42
66
  # Returns an array of arrays containing the field values.
43
67
  # Order is the same as that returned by +columns+.
44
- def select_rows(sql, name = nil)
68
+ def select_rows(sql, name = nil, binds = [])
69
+ exec_query(sql, name, binds).rows
45
70
  end
46
- undef_method :select_rows
47
71
 
48
- # Executes the SQL statement in the context of this connection.
72
+ # Executes the SQL statement in the context of this connection and returns
73
+ # the raw result from the connection adapter.
74
+ # Note: depending on your database connector, the result returned by this
75
+ # method may be manually memory managed. Consider using the exec_query
76
+ # wrapper instead.
49
77
  def execute(sql, name = nil)
78
+ raise NotImplementedError
50
79
  end
51
- undef_method :execute
52
80
 
53
81
  # Executes +sql+ statement in the context of this connection using
54
82
  # +binds+ as the bind substitutes. +name+ is logged along with
55
83
  # the executed +sql+ statement.
56
- def exec_query(sql, name = 'SQL', binds = [])
84
+ def exec_query(sql, name = 'SQL', binds = [], prepare: false)
85
+ raise NotImplementedError
57
86
  end
58
87
 
59
88
  # Executes insert +sql+ statement in the context of this connection using
60
- # +binds+ as the bind substitutes. +name+ is the logged along with
89
+ # +binds+ as the bind substitutes. +name+ is logged along with
61
90
  # the executed +sql+ statement.
62
- def exec_insert(sql, name, binds)
91
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
63
92
  exec_query(sql, name, binds)
64
93
  end
65
94
 
66
95
  # Executes delete +sql+ statement in the context of this connection using
67
- # +binds+ as the bind substitutes. +name+ is the logged along with
96
+ # +binds+ as the bind substitutes. +name+ is logged along with
68
97
  # the executed +sql+ statement.
69
98
  def exec_delete(sql, name, binds)
70
99
  exec_query(sql, name, binds)
71
100
  end
72
101
 
102
+ # Executes the truncate statement.
103
+ def truncate(table_name, name = nil)
104
+ raise NotImplementedError
105
+ end
106
+
73
107
  # Executes update +sql+ statement in the context of this connection using
74
- # +binds+ as the bind substitutes. +name+ is the logged along with
108
+ # +binds+ as the bind substitutes. +name+ is logged along with
75
109
  # the executed +sql+ statement.
76
110
  def exec_update(sql, name, binds)
77
111
  exec_query(sql, name, binds)
78
112
  end
79
113
 
80
- # Returns the last auto-generated ID from the affected table.
114
+ # Executes an INSERT query and returns the new record's ID
81
115
  #
82
116
  # +id_value+ will be returned unless the value is nil, in
83
117
  # which case the database will attempt to calculate the last inserted
@@ -86,34 +120,27 @@ module ActiveRecord
86
120
  # If the next id was calculated in advance (as in Oracle), it should be
87
121
  # passed in as +id_value+.
88
122
  def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
89
- sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
90
- value = exec_insert(sql, name, binds)
123
+ sql, binds, pk, sequence_name = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
124
+ value = exec_insert(sql, name, binds, pk, sequence_name)
91
125
  id_value || last_inserted_id(value)
92
126
  end
127
+ alias create insert
128
+ alias insert_sql insert
129
+ deprecate insert_sql: :insert
93
130
 
94
131
  # Executes the update statement and returns the number of rows affected.
95
132
  def update(arel, name = nil, binds = [])
96
133
  exec_update(to_sql(arel, binds), name, binds)
97
134
  end
135
+ alias update_sql update
136
+ deprecate update_sql: :update
98
137
 
99
138
  # Executes the delete statement and returns the number of rows affected.
100
139
  def delete(arel, name = nil, binds = [])
101
140
  exec_delete(to_sql(arel, binds), name, binds)
102
141
  end
103
-
104
- # Checks whether there is currently no transaction active. This is done
105
- # by querying the database driver, and does not use the transaction
106
- # house-keeping information recorded by #increment_open_transactions and
107
- # friends.
108
- #
109
- # Returns true if there is no transaction active, false if there is a
110
- # transaction active, and nil if this information is unknown.
111
- #
112
- # Not all adapters supports transaction state introspection. Currently,
113
- # only the PostgreSQL adapter supports this.
114
- def outside_transaction?
115
- nil
116
- end
142
+ alias delete_sql delete
143
+ deprecate delete_sql: :delete
117
144
 
118
145
  # Returns +true+ when the connection adapter supports prepared statement
119
146
  # caching, otherwise returns +false+
@@ -132,8 +159,9 @@ module ActiveRecord
132
159
  #
133
160
  # In order to get around this problem, #transaction will emulate the effect
134
161
  # of nested transactions, by using savepoints:
135
- # http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
136
- # Savepoints are supported by MySQL and PostgreSQL, but not SQLite3.
162
+ # http://dev.mysql.com/doc/refman/5.7/en/savepoint.html
163
+ # Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
164
+ # supports savepoints.
137
165
  #
138
166
  # It is safe to call this method if a database transaction is already open,
139
167
  # i.e. if #transaction is called within another #transaction block. In case
@@ -158,101 +186,110 @@ module ActiveRecord
158
186
  # already-automatically-released savepoints:
159
187
  #
160
188
  # Model.connection.transaction do # BEGIN
161
- # Model.connection.transaction(:requires_new => true) do # CREATE SAVEPOINT active_record_1
189
+ # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
162
190
  # Model.connection.create_table(...)
163
191
  # # active_record_1 now automatically released
164
192
  # end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
165
193
  # end
166
- def transaction(options = {})
167
- options.assert_valid_keys :requires_new, :joinable
168
-
169
- last_transaction_joinable = defined?(@transaction_joinable) ? @transaction_joinable : nil
170
- if options.has_key?(:joinable)
171
- @transaction_joinable = options[:joinable]
172
- else
173
- @transaction_joinable = true
174
- end
175
- requires_new = options[:requires_new] || !last_transaction_joinable
176
-
177
- transaction_open = false
178
- @_current_transaction_records ||= []
179
-
180
- begin
181
- if block_given?
182
- if requires_new || open_transactions == 0
183
- if open_transactions == 0
184
- begin_db_transaction
185
- elsif requires_new
186
- create_savepoint
187
- end
188
- increment_open_transactions
189
- transaction_open = true
190
- @_current_transaction_records.push([])
191
- end
192
- yield
193
- end
194
- rescue Exception => database_transaction_rollback
195
- if transaction_open && !outside_transaction?
196
- transaction_open = false
197
- decrement_open_transactions
198
- if open_transactions == 0
199
- rollback_db_transaction
200
- rollback_transaction_records(true)
201
- else
202
- rollback_to_savepoint
203
- rollback_transaction_records(false)
204
- end
194
+ #
195
+ # == Transaction isolation
196
+ #
197
+ # If your database supports setting the isolation level for a transaction, you can set
198
+ # it like so:
199
+ #
200
+ # Post.transaction(isolation: :serializable) do
201
+ # # ...
202
+ # end
203
+ #
204
+ # Valid isolation levels are:
205
+ #
206
+ # * <tt>:read_uncommitted</tt>
207
+ # * <tt>:read_committed</tt>
208
+ # * <tt>:repeatable_read</tt>
209
+ # * <tt>:serializable</tt>
210
+ #
211
+ # You should consult the documentation for your database to understand the
212
+ # semantics of these different levels:
213
+ #
214
+ # * http://www.postgresql.org/docs/current/static/transaction-iso.html
215
+ # * https://dev.mysql.com/doc/refman/5.7/en/set-transaction.html
216
+ #
217
+ # An ActiveRecord::TransactionIsolationError will be raised if:
218
+ #
219
+ # * The adapter does not support setting the isolation level
220
+ # * You are joining an existing open transaction
221
+ # * You are creating a nested (savepoint) transaction
222
+ #
223
+ # The mysql2 and postgresql adapters support setting the transaction
224
+ # isolation level.
225
+ def transaction(requires_new: nil, isolation: nil, joinable: true)
226
+ if !requires_new && current_transaction.joinable?
227
+ if isolation
228
+ raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
205
229
  end
206
- raise unless database_transaction_rollback.is_a?(ActiveRecord::Rollback)
230
+ yield
231
+ else
232
+ transaction_manager.within_new_transaction(isolation: isolation, joinable: joinable) { yield }
207
233
  end
208
- ensure
209
- @transaction_joinable = last_transaction_joinable
234
+ rescue ActiveRecord::Rollback
235
+ # rollbacks are silently swallowed
236
+ end
210
237
 
211
- if outside_transaction?
212
- @open_transactions = 0
213
- elsif transaction_open
214
- decrement_open_transactions
215
- begin
216
- if open_transactions == 0
217
- commit_db_transaction
218
- commit_transaction_records
219
- else
220
- release_savepoint
221
- save_point_records = @_current_transaction_records.pop
222
- unless save_point_records.blank?
223
- @_current_transaction_records.push([]) if @_current_transaction_records.empty?
224
- @_current_transaction_records.last.concat(save_point_records)
225
- end
226
- end
227
- rescue Exception => database_transaction_rollback
228
- if open_transactions == 0
229
- rollback_db_transaction
230
- rollback_transaction_records(true)
231
- else
232
- rollback_to_savepoint
233
- rollback_transaction_records(false)
234
- end
235
- raise
236
- end
237
- end
238
+ attr_reader :transaction_manager #:nodoc:
239
+
240
+ delegate :within_new_transaction, :open_transactions, :current_transaction, :begin_transaction, :commit_transaction, :rollback_transaction, to: :transaction_manager
241
+
242
+ def transaction_open?
243
+ current_transaction.open?
244
+ end
245
+
246
+ def reset_transaction #:nodoc:
247
+ @transaction_manager = TransactionManager.new(self)
238
248
  end
239
249
 
240
250
  # Register a record with the current transaction so that its after_commit and after_rollback callbacks
241
251
  # can be called.
242
252
  def add_transaction_record(record)
243
- last_batch = @_current_transaction_records.last
244
- last_batch << record if last_batch
253
+ current_transaction.add_record(record)
254
+ end
255
+
256
+ def transaction_state
257
+ current_transaction.state
245
258
  end
246
259
 
247
260
  # Begins the transaction (and turns off auto-committing).
248
261
  def begin_db_transaction() end
249
262
 
263
+ def transaction_isolation_levels
264
+ {
265
+ read_uncommitted: "READ UNCOMMITTED",
266
+ read_committed: "READ COMMITTED",
267
+ repeatable_read: "REPEATABLE READ",
268
+ serializable: "SERIALIZABLE"
269
+ }
270
+ end
271
+
272
+ # Begins the transaction with the isolation level set. Raises an error by
273
+ # default; adapters that support setting the isolation level should implement
274
+ # this method.
275
+ def begin_isolated_db_transaction(isolation)
276
+ raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation"
277
+ end
278
+
250
279
  # Commits the transaction (and turns on auto-committing).
251
280
  def commit_db_transaction() end
252
281
 
253
282
  # Rolls back the transaction (and turns on auto-committing). Must be
254
283
  # done if the transaction block raises an exception or returns false.
255
- def rollback_db_transaction() end
284
+ def rollback_db_transaction
285
+ exec_rollback_db_transaction
286
+ end
287
+
288
+ def exec_rollback_db_transaction() end #:nodoc:
289
+
290
+ def rollback_to_savepoint(name = nil)
291
+ exec_rollback_to_savepoint(name)
292
+ end
256
293
 
257
294
  def default_sequence_name(table, column)
258
295
  nil
@@ -266,27 +303,31 @@ module ActiveRecord
266
303
  # Inserts the given fixture into the table. Overridden in adapters that require
267
304
  # something beyond a simple insert (eg. Oracle).
268
305
  def insert_fixture(fixture, table_name)
269
- columns = schema_cache.columns_hash(table_name)
306
+ fixture = fixture.stringify_keys
270
307
 
271
- key_list = []
272
- value_list = fixture.map do |name, value|
273
- key_list << quote_column_name(name)
274
- quote(value, columns[name])
308
+ columns = schema_cache.columns_hash(table_name)
309
+ binds = fixture.map do |name, value|
310
+ if column = columns[name]
311
+ type = lookup_cast_type_from_column(column)
312
+ Relation::QueryAttribute.new(name, value, type)
313
+ else
314
+ raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
315
+ end
316
+ end
317
+ key_list = fixture.keys.map { |name| quote_column_name(name) }
318
+ value_list = prepare_binds_for_database(binds).map do |value|
319
+ begin
320
+ quote(value)
321
+ rescue TypeError
322
+ quote(YAML.dump(value))
323
+ end
275
324
  end
276
325
 
277
326
  execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
278
327
  end
279
328
 
280
329
  def empty_insert_statement_value
281
- "VALUES(DEFAULT)"
282
- end
283
-
284
- def case_sensitive_equality_operator
285
- "="
286
- end
287
-
288
- def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
289
- "WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
330
+ "DEFAULT VALUES"
290
331
  end
291
332
 
292
333
  # Sanitizes the given LIMIT parameter in order to prevent SQL injection.
@@ -301,7 +342,7 @@ module ActiveRecord
301
342
  def sanitize_limit(limit)
302
343
  if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
303
344
  limit
304
- elsif limit.to_s =~ /,/
345
+ elsif limit.to_s.include?(',')
305
346
  Arel.sql limit.to_s.split(',').map{ |i| Integer(i) }.join(',')
306
347
  else
307
348
  Integer(limit)
@@ -309,82 +350,48 @@ module ActiveRecord
309
350
  end
310
351
 
311
352
  # The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
312
- # on mysql (even when aliasing the tables), but mysql allows using JOIN directly in
313
- # an UPDATE statement, so in the mysql adapters we redefine this to do that.
314
- def join_to_update(update, select) #:nodoc:
315
- subselect = select.clone
316
- subselect.projections = [update.key]
353
+ # on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
354
+ # an UPDATE statement, so in the MySQL adapters we redefine this to do that.
355
+ def join_to_update(update, select, key) # :nodoc:
356
+ subselect = subquery_for(key, select)
317
357
 
318
- update.where update.key.in(subselect)
358
+ update.where key.in(subselect)
319
359
  end
360
+ alias join_to_delete join_to_update
320
361
 
321
362
  protected
322
- # Returns an array of record hashes with the column names as keys and
323
- # column values as values.
324
- def select(sql, name = nil, binds = [])
325
- end
326
- undef_method :select
327
363
 
328
- # Returns the last auto-generated ID from the affected table.
329
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
330
- execute(sql, name)
331
- id_value
364
+ # Returns a subquery for the given key using the join information.
365
+ def subquery_for(key, select)
366
+ subselect = select.clone
367
+ subselect.projections = [key]
368
+ subselect
332
369
  end
333
370
 
334
- # Executes the update statement and returns the number of rows affected.
335
- def update_sql(sql, name = nil)
336
- execute(sql, name)
371
+ # Returns an ActiveRecord::Result instance.
372
+ def select(sql, name = nil, binds = [])
373
+ exec_query(sql, name, binds, prepare: false)
337
374
  end
338
375
 
339
- # Executes the delete statement and returns the number of rows affected.
340
- def delete_sql(sql, name = nil)
341
- update_sql(sql, name)
376
+ def select_prepared(sql, name = nil, binds = [])
377
+ exec_query(sql, name, binds, prepare: true)
342
378
  end
343
379
 
344
- # Send a rollback message to all records after they have been rolled back. If rollback
345
- # is false, only rollback records since the last save point.
346
- def rollback_transaction_records(rollback)
347
- if rollback
348
- records = @_current_transaction_records.flatten
349
- @_current_transaction_records.clear
350
- else
351
- records = @_current_transaction_records.pop
352
- end
380
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
381
+ [sql, binds, pk, sequence_name]
382
+ end
353
383
 
354
- unless records.blank?
355
- records.uniq.each do |record|
356
- begin
357
- record.rolledback!(rollback)
358
- rescue Exception => e
359
- record.logger.error(e) if record.respond_to?(:logger) && record.logger
360
- end
361
- end
362
- end
384
+ def last_inserted_id(result)
385
+ row = result.rows.first
386
+ row && row.first
363
387
  end
364
388
 
365
- # Send a commit message to all records after they have been committed.
366
- def commit_transaction_records
367
- records = @_current_transaction_records.flatten
368
- @_current_transaction_records.clear
369
- unless records.blank?
370
- records.uniq.each do |record|
371
- begin
372
- record.committed!
373
- rescue Exception => e
374
- record.logger.error(e) if record.respond_to?(:logger) && record.logger
375
- end
376
- end
389
+ def binds_from_relation(relation, binds)
390
+ if relation.is_a?(Relation) && binds.empty?
391
+ relation, binds = relation.arel, relation.bound_attributes
377
392
  end
393
+ [relation, binds]
378
394
  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
395
  end
389
396
  end
390
397
  end
@@ -2,17 +2,17 @@ module ActiveRecord
2
2
  module ConnectionAdapters # :nodoc:
3
3
  module QueryCache
4
4
  class << self
5
- def included(base)
6
- dirties_query_cache base, :insert, :update, :delete
5
+ def included(base) #:nodoc:
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)
10
10
  method_names.each do |method_name|
11
11
  base.class_eval <<-end_code, __FILE__, __LINE__ + 1
12
- def #{method_name}(*) # def update_with_query_dirty(*args)
13
- clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled
14
- super # update_without_query_dirty(*args)
15
- end # end
12
+ def #{method_name}(*)
13
+ clear_query_cache if @query_cache_enabled
14
+ super
15
+ end
16
16
  end_code
17
17
  end
18
18
  end
@@ -20,13 +20,19 @@ module ActiveRecord
20
20
 
21
21
  attr_reader :query_cache, :query_cache_enabled
22
22
 
23
+ def initialize(*)
24
+ super
25
+ @query_cache = Hash.new { |h,sql| h[sql] = {} }
26
+ @query_cache_enabled = false
27
+ end
28
+
23
29
  # Enable the query cache within the block.
24
30
  def cache
25
31
  old, @query_cache_enabled = @query_cache_enabled, true
26
32
  yield
27
33
  ensure
28
- clear_query_cache
29
34
  @query_cache_enabled = old
35
+ clear_query_cache unless @query_cache_enabled
30
36
  end
31
37
 
32
38
  def enable_query_cache!
@@ -55,36 +61,35 @@ module ActiveRecord
55
61
  @query_cache.clear
56
62
  end
57
63
 
58
- def select_all(arel, name = nil, binds = [])
64
+ def select_all(arel, name = nil, binds = [], preparable: nil)
59
65
  if @query_cache_enabled && !locked?(arel)
66
+ arel, binds = binds_from_relation arel, binds
60
67
  sql = to_sql(arel, binds)
61
- cache_sql(sql, binds) { super(sql, name, binds) }
68
+ cache_sql(sql, binds) { super(sql, name, binds, preparable: preparable) }
62
69
  else
63
70
  super
64
71
  end
65
72
  end
66
73
 
67
74
  private
68
- def cache_sql(sql, binds)
69
- result =
70
- if @query_cache[sql].key?(binds)
71
- ActiveSupport::Notifications.instrument("sql.active_record",
72
- :sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id)
73
- @query_cache[sql][binds]
74
- else
75
- @query_cache[sql][binds] = yield
76
- end
77
-
78
- result.collect { |row| row.dup }
79
- end
80
75
 
81
- def locked?(arel)
82
- if arel.respond_to?(:locked)
83
- arel.locked
76
+ def cache_sql(sql, binds)
77
+ result =
78
+ if @query_cache[sql].key?(binds)
79
+ ActiveSupport::Notifications.instrument("sql.active_record",
80
+ :sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id)
81
+ @query_cache[sql][binds]
84
82
  else
85
- false
83
+ @query_cache[sql][binds] = yield
86
84
  end
87
- end
85
+ result.dup
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
88
93
  end
89
94
  end
90
95
  end