activerecord 4.2.9 → 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 (274) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +614 -1572
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -11
  5. data/examples/performance.rb +32 -31
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +263 -249
  8. data/lib/active_record/association_relation.rb +11 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +77 -43
  11. data/lib/active_record/associations/association_scope.rb +106 -133
  12. data/lib/active_record/associations/belongs_to_association.rb +52 -41
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +29 -38
  15. data/lib/active_record/associations/builder/belongs_to.rb +77 -30
  16. data/lib/active_record/associations/builder/collection_association.rb +9 -22
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
  18. data/lib/active_record/associations/builder/has_many.rb +6 -4
  19. data/lib/active_record/associations/builder/has_one.rb +13 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +15 -11
  21. data/lib/active_record/associations/collection_association.rb +139 -280
  22. data/lib/active_record/associations/collection_proxy.rb +231 -133
  23. data/lib/active_record/associations/foreign_association.rb +3 -1
  24. data/lib/active_record/associations/has_many_association.rb +34 -89
  25. data/lib/active_record/associations/has_many_through_association.rb +49 -76
  26. data/lib/active_record/associations/has_one_association.rb +38 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +40 -89
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +133 -159
  32. data/lib/active_record/associations/preloader/association.rb +85 -120
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +81 -91
  35. data/lib/active_record/associations/singular_association.rb +27 -34
  36. data/lib/active_record/associations/through_association.rb +38 -18
  37. data/lib/active_record/associations.rb +1732 -1597
  38. data/lib/active_record/attribute_assignment.rb +58 -182
  39. data/lib/active_record/attribute_decorators.rb +39 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +10 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -135
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -71
  43. data/lib/active_record/attribute_methods/query.rb +4 -2
  44. data/lib/active_record/attribute_methods/read.rb +45 -63
  45. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  47. data/lib/active_record/attribute_methods/write.rb +30 -45
  48. data/lib/active_record/attribute_methods.rb +166 -109
  49. data/lib/active_record/attributes.rb +201 -82
  50. data/lib/active_record/autosave_association.rb +94 -36
  51. data/lib/active_record/base.rb +57 -44
  52. data/lib/active_record/callbacks.rb +97 -57
  53. data/lib/active_record/coders/json.rb +3 -1
  54. data/lib/active_record/coders/yaml_column.rb +24 -12
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -593
  69. data/lib/active_record/connection_adapters/column.rb +50 -41
  70. data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
  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 +41 -188
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
  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 +50 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  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 +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -284
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +432 -323
  117. data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
  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 +269 -308
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +40 -27
  128. data/lib/active_record/core.rb +178 -198
  129. data/lib/active_record/counter_cache.rb +79 -36
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +87 -105
  132. data/lib/active_record/enum.rb +135 -88
  133. data/lib/active_record/errors.rb +179 -52
  134. data/lib/active_record/explain.rb +23 -11
  135. data/lib/active_record/explain_registry.rb +4 -2
  136. data/lib/active_record/explain_subscriber.rb +10 -5
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +188 -132
  139. data/lib/active_record/gem_version.rb +4 -2
  140. data/lib/active_record/inheritance.rb +148 -112
  141. data/lib/active_record/integration.rb +70 -28
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +88 -96
  146. data/lib/active_record/locking/pessimistic.rb +15 -3
  147. data/lib/active_record/log_subscriber.rb +95 -33
  148. data/lib/active_record/migration/command_recorder.rb +133 -90
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +8 -6
  151. data/lib/active_record/migration.rb +581 -282
  152. data/lib/active_record/model_schema.rb +290 -111
  153. data/lib/active_record/nested_attributes.rb +264 -222
  154. data/lib/active_record/no_touching.rb +7 -1
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +347 -119
  157. data/lib/active_record/query_cache.rb +13 -24
  158. data/lib/active_record/querying.rb +19 -17
  159. data/lib/active_record/railtie.rb +94 -32
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +9 -3
  162. data/lib/active_record/railties/databases.rake +149 -156
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +414 -267
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +204 -55
  167. data/lib/active_record/relation/calculations.rb +256 -248
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +288 -239
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +86 -86
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
  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 +7 -1
  179. data/lib/active_record/relation/predicate_builder.rb +116 -119
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +448 -393
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +11 -13
  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 +287 -340
  187. data/lib/active_record/result.rb +54 -36
  188. data/lib/active_record/runtime_registry.rb +6 -4
  189. data/lib/active_record/sanitization.rb +155 -124
  190. data/lib/active_record/schema.rb +30 -24
  191. data/lib/active_record/schema_dumper.rb +91 -87
  192. data/lib/active_record/schema_migration.rb +19 -16
  193. data/lib/active_record/scoping/default.rb +102 -85
  194. data/lib/active_record/scoping/named.rb +81 -32
  195. data/lib/active_record/scoping.rb +45 -26
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +5 -5
  198. data/lib/active_record/statement_cache.rb +45 -35
  199. data/lib/active_record/store.rb +42 -36
  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 +134 -96
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
  206. data/lib/active_record/timestamp.rb +70 -38
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +199 -124
  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 +4 -45
  212. data/lib/active_record/type/date_time.rb +4 -49
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  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 +24 -15
  218. data/lib/active_record/type/text.rb +2 -2
  219. data/lib/active_record/type/time.rb +11 -16
  220. data/lib/active_record/type/type_map.rb +15 -17
  221. data/lib/active_record/type/unsigned_integer.rb +9 -7
  222. data/lib/active_record/type.rb +79 -23
  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 +13 -4
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +14 -13
  230. data/lib/active_record/validations/uniqueness.rb +40 -41
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +34 -22
  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 +43 -35
  237. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -3
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
  239. data/lib/rails/generators/active_record/migration.rb +18 -1
  240. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  243. data/lib/rails/generators/active_record.rb +7 -5
  244. metadata +72 -50
  245. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  246. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  247. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  248. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  249. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  250. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  251. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  252. data/lib/active_record/attribute.rb +0 -163
  253. data/lib/active_record/attribute_set/builder.rb +0 -106
  254. data/lib/active_record/attribute_set.rb +0 -81
  255. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  256. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  257. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  258. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  259. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  260. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  261. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  262. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  263. data/lib/active_record/type/big_integer.rb +0 -13
  264. data/lib/active_record/type/binary.rb +0 -50
  265. data/lib/active_record/type/boolean.rb +0 -31
  266. data/lib/active_record/type/decimal.rb +0 -64
  267. data/lib/active_record/type/decorator.rb +0 -14
  268. data/lib/active_record/type/float.rb +0 -19
  269. data/lib/active_record/type/integer.rb +0 -59
  270. data/lib/active_record/type/mutable.rb +0 -16
  271. data/lib/active_record/type/numeric.rb +0 -36
  272. data/lib/active_record/type/string.rb +0 -40
  273. data/lib/active_record/type/time_value.rb +0 -38
  274. data/lib/active_record/type/value.rb +0 -110
@@ -1,9 +1,16 @@
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
9
  def included(base) #:nodoc:
6
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)
@@ -18,11 +25,32 @@ module ActiveRecord
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
 
23
51
  def initialize(*)
24
52
  super
25
- @query_cache = Hash.new { |h,sql| h[sql] = {} }
53
+ @query_cache = Hash.new { |h, sql| h[sql] = {} }
26
54
  @query_cache_enabled = false
27
55
  end
28
56
 
@@ -41,6 +69,7 @@ module ActiveRecord
41
69
 
42
70
  def disable_query_cache!
43
71
  @query_cache_enabled = false
72
+ clear_query_cache
44
73
  end
45
74
 
46
75
  # Disable the query cache within the block.
@@ -58,14 +87,21 @@ module ActiveRecord
58
87
  # the same SQL query and repeatedly return the same result each time, silently
59
88
  # undermining the randomness you were expecting.
60
89
  def clear_query_cache
61
- @query_cache.clear
90
+ @lock.synchronize do
91
+ @query_cache.clear
92
+ end
62
93
  end
63
94
 
64
- def select_all(arel, name = nil, binds = [])
95
+ def select_all(arel, name = nil, binds = [], preparable: nil)
65
96
  if @query_cache_enabled && !locked?(arel)
66
- arel, binds = binds_from_relation arel, binds
67
- sql = to_sql(arel, binds)
68
- 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) }
69
105
  else
70
106
  super
71
107
  end
@@ -73,23 +109,37 @@ module ActiveRecord
73
109
 
74
110
  private
75
111
 
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]
82
- else
83
- @query_cache[sql][binds] = yield
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
84
130
  end
85
- result.dup
86
- end
131
+ end
87
132
 
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
133
+ # If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
134
+ # queries should not be cached.
135
+ def locked?(arel)
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
142
+ end
93
143
  end
94
144
  end
95
145
  end
@@ -1,17 +1,18 @@
1
- require 'active_support/core_ext/big_decimal/conversions'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/big_decimal/conversions"
2
4
  require "active_support/multibyte/chars"
3
5
 
4
6
  module ActiveRecord
5
7
  module ConnectionAdapters # :nodoc:
6
8
  module Quoting
7
9
  # Quotes the column value to help prevent
8
- # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
9
- def quote(value, column = nil)
10
- # records are quoted as their primary key
11
- return value.quoted_id if value.respond_to?(:quoted_id)
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)
12
13
 
13
- if column
14
- value = column.cast_type.type_cast_for_database(value)
14
+ if value.respond_to?(:value_for_database)
15
+ value = value.value_for_database
15
16
  end
16
17
 
17
18
  _quote(value)
@@ -20,13 +21,11 @@ module ActiveRecord
20
21
  # Cast a +value+ to a type that the database understands. For example,
21
22
  # SQLite does not understand dates, so this method will convert a Date
22
23
  # to a String.
23
- def type_cast(value, column)
24
- if value.respond_to?(:quoted_id) && value.respond_to?(:id)
25
- return value.id
26
- end
24
+ def type_cast(value, column = nil)
25
+ value = id_value_for_database(value) if value.is_a?(Base)
27
26
 
28
27
  if column
29
- value = column.cast_type.type_cast_for_database(value)
28
+ value = type_cast_from_column(column, value)
30
29
  end
31
30
 
32
31
  _type_cast(value)
@@ -35,15 +34,38 @@ module ActiveRecord
35
34
  raise TypeError, "can't cast #{value.class}#{to_type}"
36
35
  end
37
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)
50
+ else
51
+ value
52
+ end
53
+ end
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
+
38
60
  # Quotes a string, escaping any ' (single quote) and \ (backslash)
39
61
  # characters.
40
62
  def quote_string(s)
41
- s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
63
+ s.gsub('\\'.freeze, '\&\&'.freeze).gsub("'".freeze, "''".freeze) # ' (for ruby-mode)
42
64
  end
43
65
 
44
66
  # Quotes the column name. Defaults to no quoting.
45
67
  def quote_column_name(column_name)
46
- column_name
68
+ column_name.to_s
47
69
  end
48
70
 
49
71
  # Quotes the table name. Defaults to column name quoting.
@@ -54,7 +76,7 @@ module ActiveRecord
54
76
  # Override to return the quoted table name for assignment. Defaults to
55
77
  # table quoting.
56
78
  #
57
- # This works for mysql and mysql2 where table.column can be used to
79
+ # This works for mysql2 where table.column can be used to
58
80
  # resolve ambiguity.
59
81
  #
60
82
  # We override this in the sqlite3 and postgresql adapters to use only
@@ -63,22 +85,33 @@ module ActiveRecord
63
85
  quote_table_name("#{table}.#{attr}")
64
86
  end
65
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
+
66
97
  def quoted_true
67
- "'t'"
98
+ "TRUE".freeze
68
99
  end
69
100
 
70
101
  def unquoted_true
71
- 't'
102
+ true
72
103
  end
73
104
 
74
105
  def quoted_false
75
- "'f'"
106
+ "FALSE".freeze
76
107
  end
77
108
 
78
109
  def unquoted_false
79
- 'f'
110
+ false
80
111
  end
81
112
 
113
+ # Quote date/time values for use in SQL input. Includes microseconds
114
+ # if the value is a Time responding to usec.
82
115
  def quoted_date(value)
83
116
  if value.acts_like?(:time)
84
117
  zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
@@ -88,47 +121,80 @@ module ActiveRecord
88
121
  end
89
122
  end
90
123
 
91
- 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
92
130
  end
93
131
 
94
- private
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 /, "")
135
+ end
136
+
137
+ def quoted_binary(value) # :nodoc:
138
+ "'#{quote_string(value.to_s)}'"
139
+ end
95
140
 
96
- def types_which_need_no_typecasting
97
- [nil, Numeric, String]
98
- end
99
-
100
- def _quote(value)
101
- case value
102
- when String, ActiveSupport::Multibyte::Chars, Type::Binary::Data
103
- "'#{quote_string(value.to_s)}'"
104
- when true then quoted_true
105
- when false then quoted_false
106
- when nil then "NULL"
107
- # BigDecimals need to be put in a non-normalized form and quoted.
108
- when BigDecimal then value.to_s('F')
109
- when Numeric, ActiveSupport::Duration then value.to_s
110
- when Date, Time then "'#{quoted_date(value)}'"
111
- when Symbol then "'#{quote_string(value.to_s)}'"
112
- when Class then "'#{value}'"
141
+ def type_casted_binds(binds) # :nodoc:
142
+ if binds.first.is_a?(Array)
143
+ binds.map { |column, value| type_cast(value, column) }
113
144
  else
114
- "'#{quote_string(YAML.dump(value))}'"
145
+ binds.map { |attr| type_cast(attr.value_for_database) }
115
146
  end
116
147
  end
117
148
 
118
- def _type_cast(value)
119
- case value
120
- when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
121
- value.to_s
122
- when true then unquoted_true
123
- when false then unquoted_false
124
- # BigDecimals need to be put in a non-normalized form and quoted.
125
- when BigDecimal then value.to_s('F')
126
- when Date, Time then quoted_date(value)
127
- when *types_which_need_no_typecasting
128
- value
129
- else raise TypeError
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
130
197
  end
131
- end
132
198
  end
133
199
  end
134
200
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
- module Savepoints #:nodoc:
4
- def supports_savepoints?
5
- true
5
+ module Savepoints
6
+ def current_savepoint_name
7
+ current_transaction.savepoint_name
6
8
  end
7
9
 
8
10
  def create_savepoint(name = current_savepoint_name)
@@ -1,4 +1,6 @@
1
- require 'active_support/core_ext/string/strip'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/strip"
2
4
 
3
5
  module ActiveRecord
4
6
  module ConnectionAdapters
@@ -14,38 +16,58 @@ module ActiveRecord
14
16
  send m, o
15
17
  end
16
18
 
17
- def visit_AddColumn(o)
18
- "ADD #{accept(o)}"
19
- end
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
20
23
 
21
24
  private
22
25
 
23
26
  def visit_AlterTable(o)
24
- sql = "ALTER TABLE #{quote_table_name(o.name)} "
25
- sql << o.adds.map { |col| visit_AddColumn col }.join(' ')
26
- sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(' ')
27
- sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(' ')
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(" ")
28
31
  end
29
32
 
30
33
  def visit_ColumnDefinition(o)
31
- sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale)
32
- column_sql = "#{quote_column_name(o.name)} #{sql_type}"
33
- add_column_options!(column_sql, column_options(o)) unless o.primary_key?
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
34
37
  column_sql
35
38
  end
36
39
 
40
+ def visit_AddColumnDefinition(o)
41
+ "ADD #{accept(o.column)}".dup
42
+ end
43
+
37
44
  def visit_TableDefinition(o)
38
- create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE "
39
- create_sql << "#{quote_table_name(o.name)} "
40
- create_sql << "(#{o.columns.map { |c| accept c }.join(', ')}) " unless o.as
41
- create_sql << "#{o.options}"
42
- create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
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
43
61
  create_sql
44
62
  end
45
63
 
46
- def visit_AddForeignKey(o)
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)
47
69
  sql = <<-SQL.strip_heredoc
48
- ADD CONSTRAINT #{quote_column_name(o.name)}
70
+ CONSTRAINT #{quote_column_name(o.name)}
49
71
  FOREIGN KEY (#{quote_column_name(o.column)})
50
72
  REFERENCES #{quote_table_name(o.to_table)} (#{quote_column_name(o.primary_key)})
51
73
  SQL
@@ -54,34 +76,34 @@ module ActiveRecord
54
76
  sql
55
77
  end
56
78
 
57
- def visit_DropForeignKey(name)
58
- "DROP CONSTRAINT #{quote_column_name(name)}"
79
+ def visit_AddForeignKey(o)
80
+ "ADD #{accept(o)}"
59
81
  end
60
82
 
61
- def column_options(o)
62
- column_options = {}
63
- column_options[:null] = o.null unless o.null.nil?
64
- column_options[:default] = o.default unless o.default.nil?
65
- column_options[:column] = o
66
- column_options[:first] = o.first
67
- column_options[:after] = o.after
68
- column_options
83
+ def visit_DropForeignKey(name)
84
+ "DROP CONSTRAINT #{quote_column_name(name)}"
69
85
  end
70
86
 
71
- def quote_column_name(name)
72
- @conn.quote_column_name name
87
+ def table_options(o)
88
+ table_options = {}
89
+ table_options[:comment] = o.comment
90
+ table_options[:options] = o.options
91
+ table_options
73
92
  end
74
93
 
75
- def quote_table_name(name)
76
- @conn.quote_table_name name
94
+ def add_table_options!(create_sql, options)
95
+ if options_sql = options[:options]
96
+ create_sql << " #{options_sql}"
97
+ end
98
+ create_sql
77
99
  end
78
100
 
79
- def type_to_sql(type, limit, precision, scale)
80
- @conn.type_to_sql type.to_sym, limit, precision, scale
101
+ def column_options(o)
102
+ o.options.merge(column: o)
81
103
  end
82
104
 
83
105
  def add_column_options!(sql, options)
84
- sql << " DEFAULT #{quote_value(options[:default], options[:column])}" if options_include_default?(options)
106
+ sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
85
107
  # must explicitly check for :null to allow change_column to work on migrations
86
108
  if options[:null] == false
87
109
  sql << " NOT NULL"
@@ -89,18 +111,20 @@ module ActiveRecord
89
111
  if options[:auto_increment] == true
90
112
  sql << " AUTO_INCREMENT"
91
113
  end
114
+ if options[:primary_key] == true
115
+ sql << " PRIMARY KEY"
116
+ end
92
117
  sql
93
118
  end
94
119
 
95
- def quote_value(value, column)
96
- column.sql_type ||= type_to_sql(column.type, column.limit, column.precision, column.scale)
97
- column.cast_type ||= type_for_column(column)
98
-
99
- @conn.quote(value, column)
120
+ def to_sql(sql)
121
+ sql = sql.to_sql if sql.respond_to?(:to_sql)
122
+ sql
100
123
  end
101
124
 
102
- def options_include_default?(options)
103
- options.include?(:default) && !(options[:null] == false && options[:default].nil?)
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)
104
128
  end
105
129
 
106
130
  def action_sql(action, dependency)
@@ -115,11 +139,8 @@ module ActiveRecord
115
139
  MSG
116
140
  end
117
141
  end
118
-
119
- def type_for_column(column)
120
- @conn.lookup_cast_type(column.sql_type)
121
- end
122
142
  end
123
143
  end
144
+ SchemaCreation = AbstractAdapter::SchemaCreation # :nodoc:
124
145
  end
125
146
  end