activerecord 3.2.22.5 → 5.2.8

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 (275) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -621
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +41 -46
  5. data/examples/performance.rb +55 -42
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +264 -236
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +127 -75
  11. data/lib/active_record/associations/association_scope.rb +126 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +78 -27
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -4
  14. data/lib/active_record/associations/builder/association.rb +117 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +135 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -54
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +120 -42
  18. data/lib/active_record/associations/builder/has_many.rb +10 -64
  19. data/lib/active_record/associations/builder/has_one.rb +19 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +28 -18
  21. data/lib/active_record/associations/collection_association.rb +226 -293
  22. data/lib/active_record/associations/collection_proxy.rb +1067 -69
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +83 -47
  25. data/lib/active_record/associations/has_many_through_association.rb +98 -65
  26. data/lib/active_record/associations/has_one_association.rb +57 -20
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +48 -126
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +212 -164
  32. data/lib/active_record/associations/preloader/association.rb +95 -89
  33. data/lib/active_record/associations/preloader/through_association.rb +84 -44
  34. data/lib/active_record/associations/preloader.rb +123 -111
  35. data/lib/active_record/associations/singular_association.rb +33 -24
  36. data/lib/active_record/associations/through_association.rb +60 -26
  37. data/lib/active_record/associations.rb +1759 -1506
  38. data/lib/active_record/attribute_assignment.rb +60 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +55 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +113 -74
  42. data/lib/active_record/attribute_methods/primary_key.rb +106 -77
  43. data/lib/active_record/attribute_methods/query.rb +8 -5
  44. data/lib/active_record/attribute_methods/read.rb +63 -114
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -90
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -43
  47. data/lib/active_record/attribute_methods/write.rb +43 -45
  48. data/lib/active_record/attribute_methods.rb +366 -149
  49. data/lib/active_record/attributes.rb +266 -0
  50. data/lib/active_record/autosave_association.rb +312 -225
  51. data/lib/active_record/base.rb +114 -505
  52. data/lib/active_record/callbacks.rb +145 -67
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +883 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +16 -2
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +350 -200
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +150 -65
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +477 -284
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1100 -310
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +450 -118
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +657 -446
  69. data/lib/active_record/connection_adapters/column.rb +50 -255
  70. data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -210
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +620 -1080
  117. data/lib/active_record/connection_adapters/schema_cache.rb +85 -36
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +545 -27
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +145 -0
  128. data/lib/active_record/core.rb +559 -0
  129. data/lib/active_record/counter_cache.rb +200 -105
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +107 -69
  132. data/lib/active_record/enum.rb +244 -0
  133. data/lib/active_record/errors.rb +245 -60
  134. data/lib/active_record/explain.rb +35 -71
  135. data/lib/active_record/explain_registry.rb +32 -0
  136. data/lib/active_record/explain_subscriber.rb +18 -9
  137. data/lib/active_record/fixture_set/file.rb +82 -0
  138. data/lib/active_record/fixtures.rb +418 -275
  139. data/lib/active_record/gem_version.rb +17 -0
  140. data/lib/active_record/inheritance.rb +209 -100
  141. data/lib/active_record/integration.rb +116 -21
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  144. data/lib/active_record/locale/en.yml +9 -1
  145. data/lib/active_record/locking/optimistic.rb +107 -94
  146. data/lib/active_record/locking/pessimistic.rb +20 -8
  147. data/lib/active_record/log_subscriber.rb +99 -34
  148. data/lib/active_record/migration/command_recorder.rb +199 -64
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +17 -0
  151. data/lib/active_record/migration.rb +893 -296
  152. data/lib/active_record/model_schema.rb +328 -175
  153. data/lib/active_record/nested_attributes.rb +338 -242
  154. data/lib/active_record/no_touching.rb +58 -0
  155. data/lib/active_record/null_relation.rb +68 -0
  156. data/lib/active_record/persistence.rb +557 -170
  157. data/lib/active_record/query_cache.rb +14 -43
  158. data/lib/active_record/querying.rb +36 -24
  159. data/lib/active_record/railtie.rb +147 -52
  160. data/lib/active_record/railties/console_sandbox.rb +5 -4
  161. data/lib/active_record/railties/controller_runtime.rb +13 -6
  162. data/lib/active_record/railties/databases.rake +206 -488
  163. data/lib/active_record/readonly_attributes.rb +4 -6
  164. data/lib/active_record/reflection.rb +734 -228
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +249 -52
  167. data/lib/active_record/relation/calculations.rb +330 -284
  168. data/lib/active_record/relation/delegation.rb +135 -37
  169. data/lib/active_record/relation/finder_methods.rb +450 -287
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +193 -0
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  179. data/lib/active_record/relation/predicate_builder.rb +132 -43
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +1037 -221
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +48 -151
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +451 -359
  187. data/lib/active_record/result.rb +129 -20
  188. data/lib/active_record/runtime_registry.rb +24 -0
  189. data/lib/active_record/sanitization.rb +164 -136
  190. data/lib/active_record/schema.rb +31 -19
  191. data/lib/active_record/schema_dumper.rb +154 -107
  192. data/lib/active_record/schema_migration.rb +56 -0
  193. data/lib/active_record/scoping/default.rb +108 -98
  194. data/lib/active_record/scoping/named.rb +125 -112
  195. data/lib/active_record/scoping.rb +77 -123
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +10 -6
  198. data/lib/active_record/statement_cache.rb +121 -0
  199. data/lib/active_record/store.rb +175 -16
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +337 -0
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
  206. data/lib/active_record/timestamp.rb +80 -41
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +240 -119
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +9 -0
  212. data/lib/active_record/type/date_time.rb +9 -0
  213. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  214. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +71 -0
  218. data/lib/active_record/type/text.rb +11 -0
  219. data/lib/active_record/type/time.rb +21 -0
  220. data/lib/active_record/type/type_map.rb +62 -0
  221. data/lib/active_record/type/unsigned_integer.rb +17 -0
  222. data/lib/active_record/type.rb +79 -0
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +35 -18
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +68 -0
  230. data/lib/active_record/validations/uniqueness.rb +133 -75
  231. data/lib/active_record/validations.rb +53 -43
  232. data/lib/active_record/version.rb +7 -7
  233. data/lib/active_record.rb +89 -57
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +61 -8
  237. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  238. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
  239. data/lib/rails/generators/active_record/migration.rb +28 -8
  240. data/lib/rails/generators/active_record/model/model_generator.rb +23 -22
  241. data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  243. data/lib/rails/generators/active_record.rb +10 -16
  244. metadata +141 -62
  245. data/examples/associations.png +0 -0
  246. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  247. data/lib/active_record/associations/join_helper.rb +0 -55
  248. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  249. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  250. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  251. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  252. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  253. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  254. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  255. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  256. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  257. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  258. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  259. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  260. data/lib/active_record/dynamic_finder_match.rb +0 -68
  261. data/lib/active_record/dynamic_scope_match.rb +0 -23
  262. data/lib/active_record/fixtures/file.rb +0 -65
  263. data/lib/active_record/identity_map.rb +0 -162
  264. data/lib/active_record/observer.rb +0 -121
  265. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  266. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  267. data/lib/active_record/session_store.rb +0 -360
  268. data/lib/active_record/test_case.rb +0 -73
  269. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  270. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  271. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  272. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  273. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  274. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  275. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,125 +1,187 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters # :nodoc:
3
5
  module DatabaseStatements
6
+ def initialize
7
+ super
8
+ reset_transaction
9
+ end
10
+
4
11
  # Converts an arel AST to SQL
5
- def to_sql(arel, binds = [])
6
- if arel.respond_to?(:ast)
7
- visitor.accept(arel.ast) do
8
- quote(*binds.shift.reverse)
12
+ def to_sql(arel_or_sql_string, binds = [])
13
+ sql, _ = to_sql_and_binds(arel_or_sql_string, binds)
14
+ sql
15
+ end
16
+
17
+ def to_sql_and_binds(arel_or_sql_string, binds = []) # :nodoc:
18
+ if arel_or_sql_string.respond_to?(:ast)
19
+ unless binds.empty?
20
+ raise "Passing bind parameters with an arel AST is forbidden. " \
21
+ "The values must be stored on the AST directly"
22
+ end
23
+
24
+ if prepared_statements
25
+ sql, binds = visitor.accept(arel_or_sql_string.ast, collector).value
26
+
27
+ if binds.length > bind_params_length
28
+ unprepared_statement do
29
+ sql, binds = to_sql_and_binds(arel_or_sql_string)
30
+ visitor.preparable = false
31
+ end
32
+ end
33
+ else
34
+ sql = visitor.accept(arel_or_sql_string.ast, collector).value
9
35
  end
36
+ [sql.freeze, binds]
37
+ else
38
+ visitor.preparable = false if prepared_statements
39
+ [arel_or_sql_string.dup.freeze, binds]
40
+ end
41
+ end
42
+ private :to_sql_and_binds
43
+
44
+ # This is used in the StatementCache object. It returns an object that
45
+ # can be used to query the database repeatedly.
46
+ def cacheable_query(klass, arel) # :nodoc:
47
+ if prepared_statements
48
+ sql, binds = visitor.accept(arel.ast, collector).value
49
+ query = klass.query(sql)
10
50
  else
11
- arel
51
+ collector = PartialQueryCollector.new
52
+ parts, binds = visitor.accept(arel.ast, collector).value
53
+ query = klass.partial_query(parts)
12
54
  end
55
+ [query, binds]
13
56
  end
14
57
 
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)
58
+ # Returns an ActiveRecord::Result instance.
59
+ def select_all(arel, name = nil, binds = [], preparable: nil)
60
+ arel = arel_from_relation(arel)
61
+ sql, binds = to_sql_and_binds(arel, binds)
62
+
63
+ if preparable.nil?
64
+ preparable = prepared_statements ? visitor.preparable : false
65
+ end
66
+
67
+ if prepared_statements && preparable
68
+ select_prepared(sql, name, binds)
69
+ else
70
+ select(sql, name, binds)
71
+ end
19
72
  end
20
73
 
21
74
  # Returns a record hash with the column names as keys and column values
22
75
  # as values.
23
- def select_one(arel, name = nil)
24
- result = select_all(arel, name)
25
- result.first if result
76
+ def select_one(arel, name = nil, binds = [])
77
+ select_all(arel, name, binds).first
26
78
  end
27
79
 
28
80
  # 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
32
- end
81
+ def select_value(arel, name = nil, binds = [])
82
+ single_value_from_rows(select_rows(arel, name, binds))
33
83
  end
34
84
 
35
85
  # Returns an array of the values of the first column in a select:
36
86
  # 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] }
87
+ def select_values(arel, name = nil, binds = [])
88
+ select_rows(arel, name, binds).map(&:first)
40
89
  end
41
90
 
42
91
  # Returns an array of arrays containing the field values.
43
92
  # Order is the same as that returned by +columns+.
44
- def select_rows(sql, name = nil)
93
+ def select_rows(arel, name = nil, binds = [])
94
+ select_all(arel, name, binds).rows
95
+ end
96
+
97
+ def query_value(sql, name = nil) # :nodoc:
98
+ single_value_from_rows(query(sql, name))
99
+ end
100
+
101
+ def query_values(sql, name = nil) # :nodoc:
102
+ query(sql, name).map(&:first)
45
103
  end
46
- undef_method :select_rows
47
104
 
48
- # Executes the SQL statement in the context of this connection.
105
+ def query(sql, name = nil) # :nodoc:
106
+ exec_query(sql, name).rows
107
+ end
108
+
109
+ # Executes the SQL statement in the context of this connection and returns
110
+ # the raw result from the connection adapter.
111
+ # Note: depending on your database connector, the result returned by this
112
+ # method may be manually memory managed. Consider using the exec_query
113
+ # wrapper instead.
49
114
  def execute(sql, name = nil)
115
+ raise NotImplementedError
50
116
  end
51
- undef_method :execute
52
117
 
53
118
  # Executes +sql+ statement in the context of this connection using
54
119
  # +binds+ as the bind substitutes. +name+ is logged along with
55
120
  # the executed +sql+ statement.
56
- def exec_query(sql, name = 'SQL', binds = [])
121
+ def exec_query(sql, name = "SQL", binds = [], prepare: false)
122
+ raise NotImplementedError
57
123
  end
58
124
 
59
125
  # Executes insert +sql+ statement in the context of this connection using
60
- # +binds+ as the bind substitutes. +name+ is the logged along with
126
+ # +binds+ as the bind substitutes. +name+ is logged along with
61
127
  # the executed +sql+ statement.
62
- def exec_insert(sql, name, binds)
128
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
129
+ sql, binds = sql_for_insert(sql, pk, nil, sequence_name, binds)
63
130
  exec_query(sql, name, binds)
64
131
  end
65
132
 
66
133
  # Executes delete +sql+ statement in the context of this connection using
67
- # +binds+ as the bind substitutes. +name+ is the logged along with
134
+ # +binds+ as the bind substitutes. +name+ is logged along with
68
135
  # the executed +sql+ statement.
69
- def exec_delete(sql, name, binds)
136
+ def exec_delete(sql, name = nil, binds = [])
70
137
  exec_query(sql, name, binds)
71
138
  end
72
139
 
140
+ # Executes the truncate statement.
141
+ def truncate(table_name, name = nil)
142
+ raise NotImplementedError
143
+ end
144
+
73
145
  # Executes update +sql+ statement in the context of this connection using
74
- # +binds+ as the bind substitutes. +name+ is the logged along with
146
+ # +binds+ as the bind substitutes. +name+ is logged along with
75
147
  # the executed +sql+ statement.
76
- def exec_update(sql, name, binds)
148
+ def exec_update(sql, name = nil, binds = [])
77
149
  exec_query(sql, name, binds)
78
150
  end
79
151
 
80
- # Returns the last auto-generated ID from the affected table.
152
+ # Executes an INSERT query and returns the new record's ID
81
153
  #
82
- # +id_value+ will be returned unless the value is nil, in
154
+ # +id_value+ will be returned unless the value is +nil+, in
83
155
  # which case the database will attempt to calculate the last inserted
84
156
  # id and return that value.
85
157
  #
86
158
  # If the next id was calculated in advance (as in Oracle), it should be
87
159
  # passed in as +id_value+.
88
160
  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)
161
+ sql, binds = to_sql_and_binds(arel, binds)
162
+ value = exec_insert(sql, name, binds, pk, sequence_name)
91
163
  id_value || last_inserted_id(value)
92
164
  end
165
+ alias create insert
93
166
 
94
167
  # Executes the update statement and returns the number of rows affected.
95
168
  def update(arel, name = nil, binds = [])
96
- exec_update(to_sql(arel, binds), name, binds)
169
+ sql, binds = to_sql_and_binds(arel, binds)
170
+ exec_update(sql, name, binds)
97
171
  end
98
172
 
99
173
  # Executes the delete statement and returns the number of rows affected.
100
174
  def delete(arel, name = nil, binds = [])
101
- exec_delete(to_sql(arel, binds), name, binds)
102
- 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
175
+ sql, binds = to_sql_and_binds(arel, binds)
176
+ exec_delete(sql, name, binds)
116
177
  end
117
178
 
118
179
  # Returns +true+ when the connection adapter supports prepared statement
119
180
  # caching, otherwise returns +false+
120
- def supports_statement_cache?
121
- false
181
+ def supports_statement_cache? # :nodoc:
182
+ true
122
183
  end
184
+ deprecate :supports_statement_cache?
123
185
 
124
186
  # Runs the given block in a database transaction, and returns the result
125
187
  # of the block.
@@ -132,8 +194,9 @@ module ActiveRecord
132
194
  #
133
195
  # In order to get around this problem, #transaction will emulate the effect
134
196
  # 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.
197
+ # https://dev.mysql.com/doc/refman/5.7/en/savepoint.html
198
+ # Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
199
+ # supports savepoints.
137
200
  #
138
201
  # It is safe to call this method if a database transaction is already open,
139
202
  # i.e. if #transaction is called within another #transaction block. In case
@@ -158,101 +221,110 @@ module ActiveRecord
158
221
  # already-automatically-released savepoints:
159
222
  #
160
223
  # Model.connection.transaction do # BEGIN
161
- # Model.connection.transaction(:requires_new => true) do # CREATE SAVEPOINT active_record_1
224
+ # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
162
225
  # Model.connection.create_table(...)
163
226
  # # active_record_1 now automatically released
164
227
  # end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
165
228
  # 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
205
- end
206
- raise unless database_transaction_rollback.is_a?(ActiveRecord::Rollback)
207
- end
208
- ensure
209
- @transaction_joinable = last_transaction_joinable
210
-
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
229
+ #
230
+ # == Transaction isolation
231
+ #
232
+ # If your database supports setting the isolation level for a transaction, you can set
233
+ # it like so:
234
+ #
235
+ # Post.transaction(isolation: :serializable) do
236
+ # # ...
237
+ # end
238
+ #
239
+ # Valid isolation levels are:
240
+ #
241
+ # * <tt>:read_uncommitted</tt>
242
+ # * <tt>:read_committed</tt>
243
+ # * <tt>:repeatable_read</tt>
244
+ # * <tt>:serializable</tt>
245
+ #
246
+ # You should consult the documentation for your database to understand the
247
+ # semantics of these different levels:
248
+ #
249
+ # * https://www.postgresql.org/docs/current/static/transaction-iso.html
250
+ # * https://dev.mysql.com/doc/refman/5.7/en/set-transaction.html
251
+ #
252
+ # An ActiveRecord::TransactionIsolationError will be raised if:
253
+ #
254
+ # * The adapter does not support setting the isolation level
255
+ # * You are joining an existing open transaction
256
+ # * You are creating a nested (savepoint) transaction
257
+ #
258
+ # The mysql2 and postgresql adapters support setting the transaction
259
+ # isolation level.
260
+ def transaction(requires_new: nil, isolation: nil, joinable: true)
261
+ if !requires_new && current_transaction.joinable?
262
+ if isolation
263
+ raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
236
264
  end
265
+ yield
266
+ else
267
+ transaction_manager.within_new_transaction(isolation: isolation, joinable: joinable) { yield }
237
268
  end
269
+ rescue ActiveRecord::Rollback
270
+ # rollbacks are silently swallowed
271
+ end
272
+
273
+ attr_reader :transaction_manager #:nodoc:
274
+
275
+ delegate :within_new_transaction, :open_transactions, :current_transaction, :begin_transaction, :commit_transaction, :rollback_transaction, to: :transaction_manager
276
+
277
+ def transaction_open?
278
+ current_transaction.open?
279
+ end
280
+
281
+ def reset_transaction #:nodoc:
282
+ @transaction_manager = ConnectionAdapters::TransactionManager.new(self)
238
283
  end
239
284
 
240
285
  # Register a record with the current transaction so that its after_commit and after_rollback callbacks
241
286
  # can be called.
242
287
  def add_transaction_record(record)
243
- last_batch = @_current_transaction_records.last
244
- last_batch << record if last_batch
288
+ current_transaction.add_record(record)
289
+ end
290
+
291
+ def transaction_state
292
+ current_transaction.state
245
293
  end
246
294
 
247
295
  # Begins the transaction (and turns off auto-committing).
248
296
  def begin_db_transaction() end
249
297
 
298
+ def transaction_isolation_levels
299
+ {
300
+ read_uncommitted: "READ UNCOMMITTED",
301
+ read_committed: "READ COMMITTED",
302
+ repeatable_read: "REPEATABLE READ",
303
+ serializable: "SERIALIZABLE"
304
+ }
305
+ end
306
+
307
+ # Begins the transaction with the isolation level set. Raises an error by
308
+ # default; adapters that support setting the isolation level should implement
309
+ # this method.
310
+ def begin_isolated_db_transaction(isolation)
311
+ raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation"
312
+ end
313
+
250
314
  # Commits the transaction (and turns on auto-committing).
251
315
  def commit_db_transaction() end
252
316
 
253
317
  # Rolls back the transaction (and turns on auto-committing). Must be
254
318
  # done if the transaction block raises an exception or returns false.
255
- def rollback_db_transaction() end
319
+ def rollback_db_transaction
320
+ exec_rollback_db_transaction
321
+ end
322
+
323
+ def exec_rollback_db_transaction() end #:nodoc:
324
+
325
+ def rollback_to_savepoint(name = nil)
326
+ exec_rollback_to_savepoint(name)
327
+ end
256
328
 
257
329
  def default_sequence_name(table, column)
258
330
  nil
@@ -265,126 +337,204 @@ module ActiveRecord
265
337
 
266
338
  # Inserts the given fixture into the table. Overridden in adapters that require
267
339
  # something beyond a simple insert (eg. Oracle).
340
+ # Most of adapters should implement `insert_fixtures` that leverages bulk SQL insert.
341
+ # We keep this method to provide fallback
342
+ # for databases like sqlite that do not support bulk inserts.
268
343
  def insert_fixture(fixture, table_name)
344
+ fixture = fixture.stringify_keys
345
+
269
346
  columns = schema_cache.columns_hash(table_name)
347
+ binds = fixture.map do |name, value|
348
+ if column = columns[name]
349
+ type = lookup_cast_type_from_column(column)
350
+ Relation::QueryAttribute.new(name, value, type)
351
+ else
352
+ raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
353
+ end
354
+ end
270
355
 
271
- key_list = []
272
- value_list = fixture.map do |name, value|
273
- key_list << quote_column_name(name)
274
- quote(value, columns[name])
356
+ table = Arel::Table.new(table_name)
357
+
358
+ values = binds.map do |bind|
359
+ value = with_yaml_fallback(bind.value_for_database)
360
+ [table[bind.name], value]
275
361
  end
276
362
 
277
- execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
363
+ manager = Arel::InsertManager.new
364
+ manager.into(table)
365
+ manager.insert(values)
366
+ execute manager.to_sql, "Fixture Insert"
278
367
  end
279
368
 
280
- def empty_insert_statement_value
281
- "VALUES(DEFAULT)"
369
+ # Inserts a set of fixtures into the table. Overridden in adapters that require
370
+ # something beyond a simple insert (eg. Oracle).
371
+ def insert_fixtures(fixtures, table_name)
372
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
373
+ `insert_fixtures` is deprecated and will be removed in the next version of Rails.
374
+ Consider using `insert_fixtures_set` for performance improvement.
375
+ MSG
376
+ return if fixtures.empty?
377
+
378
+ execute(build_fixture_sql(fixtures, table_name), "Fixtures Insert")
282
379
  end
283
380
 
284
- def case_sensitive_equality_operator
285
- "="
381
+ def insert_fixtures_set(fixture_set, tables_to_delete = [])
382
+ fixture_inserts = fixture_set.map do |table_name, fixtures|
383
+ next if fixtures.empty?
384
+
385
+ build_fixture_sql(fixtures, table_name)
386
+ end.compact
387
+
388
+ table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name table}".dup }
389
+ total_sql = Array.wrap(combine_multi_statements(table_deletes + fixture_inserts))
390
+
391
+ disable_referential_integrity do
392
+ transaction(requires_new: true) do
393
+ total_sql.each do |sql|
394
+ execute sql, "Fixtures Load"
395
+ yield if block_given?
396
+ end
397
+ end
398
+ end
286
399
  end
287
400
 
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})"
401
+ def empty_insert_statement_value
402
+ "DEFAULT VALUES"
290
403
  end
291
404
 
292
405
  # Sanitizes the given LIMIT parameter in order to prevent SQL injection.
293
406
  #
294
407
  # The +limit+ may be anything that can evaluate to a string via #to_s. It
295
- # should look like an integer, or a comma-delimited list of integers, or
296
- # an Arel SQL literal.
408
+ # should look like an integer, or an Arel SQL literal.
297
409
  #
298
410
  # Returns Integer and Arel::Nodes::SqlLiteral limits as is.
299
- # Returns the sanitized limit parameter, either as an integer, or as a
300
- # string which contains a comma-delimited list of integers.
301
411
  def sanitize_limit(limit)
302
412
  if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
303
413
  limit
304
- elsif limit.to_s =~ /,/
305
- Arel.sql limit.to_s.split(',').map{ |i| Integer(i) }.join(',')
306
414
  else
307
415
  Integer(limit)
308
416
  end
309
417
  end
310
418
 
311
419
  # 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]
420
+ # on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
421
+ # an UPDATE statement, so in the MySQL adapters we redefine this to do that.
422
+ def join_to_update(update, select, key) # :nodoc:
423
+ subselect = subquery_for(key, select)
317
424
 
318
- update.where update.key.in(subselect)
425
+ update.where key.in(subselect)
319
426
  end
427
+ alias join_to_delete join_to_update
428
+
429
+ private
430
+ def default_insert_value(column)
431
+ Arel.sql("DEFAULT")
432
+ end
433
+
434
+ def build_fixture_sql(fixtures, table_name)
435
+ columns = schema_cache.columns_hash(table_name)
320
436
 
321
- protected
322
- # Returns an array of record hashes with the column names as keys and
323
- # column values as values.
437
+ values = fixtures.map do |fixture|
438
+ fixture = fixture.stringify_keys
439
+
440
+ unknown_columns = fixture.keys - columns.keys
441
+ if unknown_columns.any?
442
+ raise Fixture::FixtureError, %(table "#{table_name}" has no columns named #{unknown_columns.map(&:inspect).join(', ')}.)
443
+ end
444
+
445
+ columns.map do |name, column|
446
+ if fixture.key?(name)
447
+ type = lookup_cast_type_from_column(column)
448
+ bind = Relation::QueryAttribute.new(name, fixture[name], type)
449
+ with_yaml_fallback(bind.value_for_database)
450
+ else
451
+ default_insert_value(column)
452
+ end
453
+ end
454
+ end
455
+
456
+ table = Arel::Table.new(table_name)
457
+ manager = Arel::InsertManager.new
458
+ manager.into(table)
459
+ columns.each_key { |column| manager.columns << table[column] }
460
+ manager.values = manager.create_values_list(values)
461
+
462
+ manager.to_sql
463
+ end
464
+
465
+ def combine_multi_statements(total_sql)
466
+ total_sql.join(";\n")
467
+ end
468
+
469
+ # Returns a subquery for the given key using the join information.
470
+ def subquery_for(key, select)
471
+ subselect = select.clone
472
+ subselect.projections = [key]
473
+ subselect
474
+ end
475
+
476
+ # Returns an ActiveRecord::Result instance.
324
477
  def select(sql, name = nil, binds = [])
478
+ exec_query(sql, name, binds, prepare: false)
479
+ end
480
+
481
+ def select_prepared(sql, name = nil, binds = [])
482
+ exec_query(sql, name, binds, prepare: true)
325
483
  end
326
- undef_method :select
327
484
 
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
485
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
486
+ [sql, binds]
332
487
  end
333
488
 
334
- # Executes the update statement and returns the number of rows affected.
335
- def update_sql(sql, name = nil)
336
- execute(sql, name)
489
+ def last_inserted_id(result)
490
+ single_value_from_rows(result.rows)
337
491
  end
338
492
 
339
- # Executes the delete statement and returns the number of rows affected.
340
- def delete_sql(sql, name = nil)
341
- update_sql(sql, name)
493
+ def single_value_from_rows(rows)
494
+ row = rows.first
495
+ row && row.first
342
496
  end
343
497
 
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
498
+ def arel_from_relation(relation)
499
+ if relation.is_a?(Relation)
500
+ relation.arel
350
501
  else
351
- records = @_current_transaction_records.pop
502
+ relation
352
503
  end
504
+ end
353
505
 
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
506
+ # Fixture value is quoted by Arel, however scalar values
507
+ # are not quotable. In this case we want to convert
508
+ # the column value to YAML.
509
+ def with_yaml_fallback(value)
510
+ if value.is_a?(Hash) || value.is_a?(Array)
511
+ YAML.dump(value)
512
+ else
513
+ value
362
514
  end
363
515
  end
364
516
 
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
517
+ class PartialQueryCollector
518
+ def initialize
519
+ @parts = []
520
+ @binds = []
377
521
  end
378
- end
379
522
 
380
- def sql_for_insert(sql, pk, id_value, sequence_name, binds)
381
- [sql, binds]
382
- end
523
+ def <<(str)
524
+ @parts << str
525
+ self
526
+ end
383
527
 
384
- def last_inserted_id(result)
385
- row = result.rows.first
386
- row && row.first
387
- end
528
+ def add_bind(obj)
529
+ @binds << obj
530
+ @parts << Arel::Nodes::BindParam.new(1)
531
+ self
532
+ end
533
+
534
+ def value
535
+ [@parts, @binds]
536
+ end
537
+ end
388
538
  end
389
539
  end
390
540
  end