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,32 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/map"
4
+
1
5
  module ActiveRecord
2
6
  module ConnectionAdapters # :nodoc:
3
7
  module QueryCache
4
8
  class << self
5
- def included(base)
6
- dirties_query_cache base, :insert, :update, :delete
9
+ def included(base) #:nodoc:
10
+ dirties_query_cache base, :insert, :update, :delete, :rollback_to_savepoint, :rollback_db_transaction
11
+
12
+ base.set_callback :checkout, :after, :configure_query_cache!
13
+ base.set_callback :checkin, :after, :disable_query_cache!
7
14
  end
8
15
 
9
16
  def dirties_query_cache(base, *method_names)
10
17
  method_names.each do |method_name|
11
18
  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
19
+ def #{method_name}(*)
20
+ clear_query_cache if @query_cache_enabled
21
+ super
22
+ end
16
23
  end_code
17
24
  end
18
25
  end
19
26
  end
20
27
 
28
+ module ConnectionPoolConfiguration
29
+ def initialize(*)
30
+ super
31
+ @query_cache_enabled = Concurrent::Map.new { false }
32
+ end
33
+
34
+ def enable_query_cache!
35
+ @query_cache_enabled[connection_cache_key(current_thread)] = true
36
+ connection.enable_query_cache! if active_connection?
37
+ end
38
+
39
+ def disable_query_cache!
40
+ @query_cache_enabled.delete connection_cache_key(current_thread)
41
+ connection.disable_query_cache! if active_connection?
42
+ end
43
+
44
+ def query_cache_enabled
45
+ @query_cache_enabled[connection_cache_key(current_thread)]
46
+ end
47
+ end
48
+
21
49
  attr_reader :query_cache, :query_cache_enabled
22
50
 
51
+ def initialize(*)
52
+ super
53
+ @query_cache = Hash.new { |h, sql| h[sql] = {} }
54
+ @query_cache_enabled = false
55
+ end
56
+
23
57
  # Enable the query cache within the block.
24
58
  def cache
25
59
  old, @query_cache_enabled = @query_cache_enabled, true
26
60
  yield
27
61
  ensure
28
- clear_query_cache
29
62
  @query_cache_enabled = old
63
+ clear_query_cache unless @query_cache_enabled
30
64
  end
31
65
 
32
66
  def enable_query_cache!
@@ -35,6 +69,7 @@ module ActiveRecord
35
69
 
36
70
  def disable_query_cache!
37
71
  @query_cache_enabled = false
72
+ clear_query_cache
38
73
  end
39
74
 
40
75
  # Disable the query cache within the block.
@@ -52,38 +87,58 @@ module ActiveRecord
52
87
  # the same SQL query and repeatedly return the same result each time, silently
53
88
  # undermining the randomness you were expecting.
54
89
  def clear_query_cache
55
- @query_cache.clear
90
+ @lock.synchronize do
91
+ @query_cache.clear
92
+ end
56
93
  end
57
94
 
58
- def select_all(arel, name = nil, binds = [])
95
+ def select_all(arel, name = nil, binds = [], preparable: nil)
59
96
  if @query_cache_enabled && !locked?(arel)
60
- sql = to_sql(arel, binds)
61
- cache_sql(sql, binds) { super(sql, name, binds) }
97
+ arel = arel_from_relation(arel)
98
+ sql, binds = to_sql_and_binds(arel, binds)
99
+
100
+ if preparable.nil?
101
+ preparable = prepared_statements ? visitor.preparable : false
102
+ end
103
+
104
+ cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable) }
62
105
  else
63
106
  super
64
107
  end
65
108
  end
66
109
 
67
110
  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 }
111
+
112
+ def cache_sql(sql, name, binds)
113
+ @lock.synchronize do
114
+ result =
115
+ if @query_cache[sql].key?(binds)
116
+ ActiveSupport::Notifications.instrument(
117
+ "sql.active_record",
118
+ sql: sql,
119
+ binds: binds,
120
+ type_casted_binds: -> { type_casted_binds(binds) },
121
+ name: name,
122
+ connection_id: object_id,
123
+ cached: true,
124
+ )
125
+ @query_cache[sql][binds]
126
+ else
127
+ @query_cache[sql][binds] = yield
128
+ end
129
+ result.dup
130
+ end
79
131
  end
80
132
 
133
+ # If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
134
+ # queries should not be cached.
81
135
  def locked?(arel)
82
- if arel.respond_to?(:locked)
83
- arel.locked
84
- else
85
- false
86
- end
136
+ arel = arel.arel if arel.is_a?(Relation)
137
+ arel.respond_to?(:locked) && arel.locked
138
+ end
139
+
140
+ def configure_query_cache!
141
+ enable_query_cache! if pool.query_cache_enabled
87
142
  end
88
143
  end
89
144
  end
@@ -1,89 +1,71 @@
1
- require 'active_support/core_ext/big_decimal/conversions'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/big_decimal/conversions"
4
+ require "active_support/multibyte/chars"
2
5
 
3
6
  module ActiveRecord
4
7
  module ConnectionAdapters # :nodoc:
5
8
  module Quoting
6
9
  # 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
10
+ # {SQL injection attacks}[https://en.wikipedia.org/wiki/SQL_injection].
11
+ def quote(value)
12
+ value = id_value_for_database(value) if value.is_a?(Base)
24
13
 
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 then value.to_s
35
- when Date, Time then "'#{quoted_date(value)}'"
36
- when Symbol then "'#{quote_string(value.to_s)}'"
37
- else
38
- "'#{quote_string(YAML.dump(value))}'"
14
+ if value.respond_to?(:value_for_database)
15
+ value = value.value_for_database
39
16
  end
17
+
18
+ _quote(value)
40
19
  end
41
20
 
42
21
  # Cast a +value+ to a type that the database understands. For example,
43
22
  # SQLite does not understand dates, so this method will convert a Date
44
23
  # to a String.
45
- def type_cast(value, column)
46
- return value.id if value.respond_to?(:quoted_id)
47
-
48
- case value
49
- when String, ActiveSupport::Multibyte::Chars
50
- value = value.to_s
51
- return value unless column
52
-
53
- case column.type
54
- when :binary then value
55
- when :integer then value.to_i
56
- when :float then value.to_f
57
- else
58
- value
59
- end
24
+ def type_cast(value, column = nil)
25
+ value = id_value_for_database(value) if value.is_a?(Base)
60
26
 
61
- when true, false
62
- if column && column.type == :integer
63
- value ? 1 : 0
64
- else
65
- value ? 't' : 'f'
66
- end
67
- # BigDecimals need to be put in a non-normalized form and quoted.
68
- when nil then nil
69
- when BigDecimal then value.to_s('F')
70
- when Numeric then value
71
- when Date, Time then quoted_date(value)
72
- when Symbol then value.to_s
27
+ if column
28
+ value = type_cast_from_column(column, value)
29
+ end
30
+
31
+ _type_cast(value)
32
+ rescue TypeError
33
+ to_type = column ? " to #{column.type}" : ""
34
+ raise TypeError, "can't cast #{value.class}#{to_type}"
35
+ end
36
+
37
+ # If you are having to call this function, you are likely doing something
38
+ # wrong. The column does not have sufficient type information if the user
39
+ # provided a custom type on the class level either explicitly (via
40
+ # Attributes::ClassMethods#attribute) or implicitly (via
41
+ # AttributeMethods::Serialization::ClassMethods#serialize, +time_zone_aware_attributes+).
42
+ # In almost all cases, the sql type should only be used to change quoting behavior, when the primitive to
43
+ # represent the type doesn't sufficiently reflect the differences
44
+ # (varchar vs binary) for example. The type used to get this primitive
45
+ # should have been provided before reaching the connection adapter.
46
+ def type_cast_from_column(column, value) # :nodoc:
47
+ if column
48
+ type = lookup_cast_type_from_column(column)
49
+ type.serialize(value)
73
50
  else
74
- YAML.dump(value)
51
+ value
75
52
  end
76
53
  end
77
54
 
55
+ # See docs for #type_cast_from_column
56
+ def lookup_cast_type_from_column(column) # :nodoc:
57
+ lookup_cast_type(column.sql_type)
58
+ end
59
+
78
60
  # Quotes a string, escaping any ' (single quote) and \ (backslash)
79
61
  # characters.
80
62
  def quote_string(s)
81
- s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
63
+ s.gsub('\\'.freeze, '\&\&'.freeze).gsub("'".freeze, "''".freeze) # ' (for ruby-mode)
82
64
  end
83
65
 
84
66
  # Quotes the column name. Defaults to no quoting.
85
67
  def quote_column_name(column_name)
86
- column_name
68
+ column_name.to_s
87
69
  end
88
70
 
89
71
  # Quotes the table name. Defaults to column name quoting.
@@ -91,14 +73,45 @@ module ActiveRecord
91
73
  quote_column_name(table_name)
92
74
  end
93
75
 
76
+ # Override to return the quoted table name for assignment. Defaults to
77
+ # table quoting.
78
+ #
79
+ # This works for mysql2 where table.column can be used to
80
+ # resolve ambiguity.
81
+ #
82
+ # We override this in the sqlite3 and postgresql adapters to use only
83
+ # the column name (as per syntax requirements).
84
+ def quote_table_name_for_assignment(table, attr)
85
+ quote_table_name("#{table}.#{attr}")
86
+ end
87
+
88
+ def quote_default_expression(value, column) # :nodoc:
89
+ if value.is_a?(Proc)
90
+ value.call
91
+ else
92
+ value = lookup_cast_type(column.sql_type).serialize(value)
93
+ quote(value)
94
+ end
95
+ end
96
+
94
97
  def quoted_true
95
- "'t'"
98
+ "TRUE".freeze
99
+ end
100
+
101
+ def unquoted_true
102
+ true
96
103
  end
97
104
 
98
105
  def quoted_false
99
- "'f'"
106
+ "FALSE".freeze
107
+ end
108
+
109
+ def unquoted_false
110
+ false
100
111
  end
101
112
 
113
+ # Quote date/time values for use in SQL input. Includes microseconds
114
+ # if the value is a Time responding to usec.
102
115
  def quoted_date(value)
103
116
  if value.acts_like?(:time)
104
117
  zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
@@ -108,8 +121,80 @@ module ActiveRecord
108
121
  end
109
122
  end
110
123
 
111
- value.to_s(:db)
124
+ result = value.to_s(:db)
125
+ if value.respond_to?(:usec) && value.usec > 0
126
+ "#{result}.#{sprintf("%06d", value.usec)}"
127
+ else
128
+ result
129
+ end
130
+ end
131
+
132
+ def quoted_time(value) # :nodoc:
133
+ value = value.change(year: 2000, month: 1, day: 1)
134
+ quoted_date(value).sub(/\A\d\d\d\d-\d\d-\d\d /, "")
112
135
  end
136
+
137
+ def quoted_binary(value) # :nodoc:
138
+ "'#{quote_string(value.to_s)}'"
139
+ end
140
+
141
+ def type_casted_binds(binds) # :nodoc:
142
+ if binds.first.is_a?(Array)
143
+ binds.map { |column, value| type_cast(value, column) }
144
+ else
145
+ binds.map { |attr| type_cast(attr.value_for_database) }
146
+ end
147
+ end
148
+
149
+ private
150
+ def lookup_cast_type(sql_type)
151
+ type_map.lookup(sql_type)
152
+ end
153
+
154
+ def id_value_for_database(value)
155
+ if primary_key = value.class.primary_key
156
+ value.instance_variable_get(:@attributes)[primary_key].value_for_database
157
+ end
158
+ end
159
+
160
+ def types_which_need_no_typecasting
161
+ [nil, Numeric, String]
162
+ end
163
+
164
+ def _quote(value)
165
+ case value
166
+ when String, ActiveSupport::Multibyte::Chars
167
+ "'#{quote_string(value.to_s)}'"
168
+ when true then quoted_true
169
+ when false then quoted_false
170
+ when nil then "NULL"
171
+ # BigDecimals need to be put in a non-normalized form and quoted.
172
+ when BigDecimal then value.to_s("F")
173
+ when Numeric, ActiveSupport::Duration then value.to_s
174
+ when Type::Binary::Data then quoted_binary(value)
175
+ when Type::Time::Value then "'#{quoted_time(value)}'"
176
+ when Date, Time then "'#{quoted_date(value)}'"
177
+ when Symbol then "'#{quote_string(value.to_s)}'"
178
+ when Class then "'#{value}'"
179
+ else raise TypeError, "can't quote #{value.class.name}"
180
+ end
181
+ end
182
+
183
+ def _type_cast(value)
184
+ case value
185
+ when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
186
+ value.to_s
187
+ when true then unquoted_true
188
+ when false then unquoted_false
189
+ # BigDecimals need to be put in a non-normalized form and quoted.
190
+ when BigDecimal then value.to_s("F")
191
+ when Type::Time::Value then quoted_time(value)
192
+ when Date, Time then quoted_date(value)
193
+ when *types_which_need_no_typecasting
194
+ value
195
+ else raise TypeError
196
+ end
197
+ end
113
198
  end
114
199
  end
115
200
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module Savepoints
6
+ def current_savepoint_name
7
+ current_transaction.savepoint_name
8
+ end
9
+
10
+ def create_savepoint(name = current_savepoint_name)
11
+ execute("SAVEPOINT #{name}")
12
+ end
13
+
14
+ def exec_rollback_to_savepoint(name = current_savepoint_name)
15
+ execute("ROLLBACK TO SAVEPOINT #{name}")
16
+ end
17
+
18
+ def release_savepoint(name = current_savepoint_name)
19
+ execute("RELEASE SAVEPOINT #{name}")
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/strip"
4
+
5
+ module ActiveRecord
6
+ module ConnectionAdapters
7
+ class AbstractAdapter
8
+ class SchemaCreation # :nodoc:
9
+ def initialize(conn)
10
+ @conn = conn
11
+ @cache = {}
12
+ end
13
+
14
+ def accept(o)
15
+ m = @cache[o.class] ||= "visit_#{o.class.name.split('::').last}"
16
+ send m, o
17
+ end
18
+
19
+ delegate :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
20
+ :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys_in_create?, :foreign_key_options, to: :@conn
21
+ private :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
22
+ :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys_in_create?, :foreign_key_options
23
+
24
+ private
25
+
26
+ def visit_AlterTable(o)
27
+ sql = "ALTER TABLE #{quote_table_name(o.name)} ".dup
28
+ sql << o.adds.map { |col| accept col }.join(" ")
29
+ sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(" ")
30
+ sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
31
+ end
32
+
33
+ def visit_ColumnDefinition(o)
34
+ o.sql_type = type_to_sql(o.type, o.options)
35
+ column_sql = "#{quote_column_name(o.name)} #{o.sql_type}".dup
36
+ add_column_options!(column_sql, column_options(o)) unless o.type == :primary_key
37
+ column_sql
38
+ end
39
+
40
+ def visit_AddColumnDefinition(o)
41
+ "ADD #{accept(o.column)}".dup
42
+ end
43
+
44
+ def visit_TableDefinition(o)
45
+ create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(o.name)} ".dup
46
+
47
+ statements = o.columns.map { |c| accept c }
48
+ statements << accept(o.primary_keys) if o.primary_keys
49
+
50
+ if supports_indexes_in_create?
51
+ statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
52
+ end
53
+
54
+ if supports_foreign_keys_in_create?
55
+ statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
56
+ end
57
+
58
+ create_sql << "(#{statements.join(', ')})" if statements.present?
59
+ add_table_options!(create_sql, table_options(o))
60
+ create_sql << " AS #{to_sql(o.as)}" if o.as
61
+ create_sql
62
+ end
63
+
64
+ def visit_PrimaryKeyDefinition(o)
65
+ "PRIMARY KEY (#{o.name.map { |name| quote_column_name(name) }.join(', ')})"
66
+ end
67
+
68
+ def visit_ForeignKeyDefinition(o)
69
+ sql = <<-SQL.strip_heredoc
70
+ CONSTRAINT #{quote_column_name(o.name)}
71
+ FOREIGN KEY (#{quote_column_name(o.column)})
72
+ REFERENCES #{quote_table_name(o.to_table)} (#{quote_column_name(o.primary_key)})
73
+ SQL
74
+ sql << " #{action_sql('DELETE', o.on_delete)}" if o.on_delete
75
+ sql << " #{action_sql('UPDATE', o.on_update)}" if o.on_update
76
+ sql
77
+ end
78
+
79
+ def visit_AddForeignKey(o)
80
+ "ADD #{accept(o)}"
81
+ end
82
+
83
+ def visit_DropForeignKey(name)
84
+ "DROP CONSTRAINT #{quote_column_name(name)}"
85
+ end
86
+
87
+ def table_options(o)
88
+ table_options = {}
89
+ table_options[:comment] = o.comment
90
+ table_options[:options] = o.options
91
+ table_options
92
+ end
93
+
94
+ def add_table_options!(create_sql, options)
95
+ if options_sql = options[:options]
96
+ create_sql << " #{options_sql}"
97
+ end
98
+ create_sql
99
+ end
100
+
101
+ def column_options(o)
102
+ o.options.merge(column: o)
103
+ end
104
+
105
+ def add_column_options!(sql, options)
106
+ sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
107
+ # must explicitly check for :null to allow change_column to work on migrations
108
+ if options[:null] == false
109
+ sql << " NOT NULL"
110
+ end
111
+ if options[:auto_increment] == true
112
+ sql << " AUTO_INCREMENT"
113
+ end
114
+ if options[:primary_key] == true
115
+ sql << " PRIMARY KEY"
116
+ end
117
+ sql
118
+ end
119
+
120
+ def to_sql(sql)
121
+ sql = sql.to_sql if sql.respond_to?(:to_sql)
122
+ sql
123
+ end
124
+
125
+ def foreign_key_in_create(from_table, to_table, options)
126
+ options = foreign_key_options(from_table, to_table, options)
127
+ accept ForeignKeyDefinition.new(from_table, to_table, options)
128
+ end
129
+
130
+ def action_sql(action, dependency)
131
+ case dependency
132
+ when :nullify then "ON #{action} SET NULL"
133
+ when :cascade then "ON #{action} CASCADE"
134
+ when :restrict then "ON #{action} RESTRICT"
135
+ else
136
+ raise ArgumentError, <<-MSG.strip_heredoc
137
+ '#{dependency}' is not supported for :on_update or :on_delete.
138
+ Supported values are: :nullify, :cascade, :restrict
139
+ MSG
140
+ end
141
+ end
142
+ end
143
+ end
144
+ SchemaCreation = AbstractAdapter::SchemaCreation # :nodoc:
145
+ end
146
+ end