activerecord 4.2.0 → 5.2.8.1

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 +640 -928
  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 +264 -247
  8. data/lib/active_record/association_relation.rb +24 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +87 -41
  11. data/lib/active_record/associations/association_scope.rb +106 -132
  12. data/lib/active_record/associations/belongs_to_association.rb +55 -36
  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 +14 -23
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
  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 +145 -266
  22. data/lib/active_record/associations/collection_proxy.rb +242 -138
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +35 -75
  25. data/lib/active_record/associations/has_many_through_association.rb +51 -69
  26. data/lib/active_record/associations/has_one_association.rb +39 -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 -81
  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 +134 -154
  32. data/lib/active_record/associations/preloader/association.rb +85 -116
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +83 -93
  35. data/lib/active_record/associations/singular_association.rb +27 -40
  36. data/lib/active_record/associations/through_association.rb +48 -23
  37. data/lib/active_record/associations.rb +1732 -1596
  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 +12 -5
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -125
  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 +62 -36
  47. data/lib/active_record/attribute_methods/write.rb +31 -46
  48. data/lib/active_record/attribute_methods.rb +170 -117
  49. data/lib/active_record/attributes.rb +201 -74
  50. data/lib/active_record/autosave_association.rb +118 -45
  51. data/lib/active_record/base.rb +60 -48
  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 +37 -13
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -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 +617 -212
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
  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 +42 -195
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
  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 -57
  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 +5 -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 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -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 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
  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 +466 -280
  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 +439 -330
  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 -324
  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 +205 -202
  129. data/lib/active_record/counter_cache.rb +80 -37
  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 +136 -90
  133. data/lib/active_record/errors.rb +180 -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 +11 -6
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +193 -135
  139. data/lib/active_record/gem_version.rb +5 -3
  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 +48 -0
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +92 -98
  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 +594 -267
  152. data/lib/active_record/model_schema.rb +292 -111
  153. data/lib/active_record/nested_attributes.rb +266 -214
  154. data/lib/active_record/no_touching.rb +8 -2
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +350 -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 +117 -35
  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 +160 -174
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +447 -288
  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 +259 -244
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +290 -253
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +91 -68
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
  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 +118 -92
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +446 -389
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +18 -16
  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 -339
  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 -19
  193. data/lib/active_record/scoping/default.rb +102 -84
  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 +136 -95
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
  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 +208 -123
  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 -41
  212. data/lib/active_record/type/date_time.rb +4 -38
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
  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 +30 -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 +41 -32
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +36 -21
  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 -6
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
  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.rb +7 -5
  243. metadata +77 -53
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  251. data/lib/active_record/attribute.rb +0 -149
  252. data/lib/active_record/attribute_set/builder.rb +0 -86
  253. data/lib/active_record/attribute_set.rb +0 -77
  254. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  255. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  256. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  257. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  258. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  259. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  260. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  261. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  262. data/lib/active_record/type/big_integer.rb +0 -13
  263. data/lib/active_record/type/binary.rb +0 -50
  264. data/lib/active_record/type/boolean.rb +0 -30
  265. data/lib/active_record/type/decimal.rb +0 -40
  266. data/lib/active_record/type/decorator.rb +0 -14
  267. data/lib/active_record/type/float.rb +0 -19
  268. data/lib/active_record/type/integer.rb +0 -55
  269. data/lib/active_record/type/mutable.rb +0 -16
  270. data/lib/active_record/type/numeric.rb +0 -36
  271. data/lib/active_record/type/string.rb +0 -36
  272. data/lib/active_record/type/time_value.rb +0 -38
  273. data/lib/active_record/type/value.rb +0 -101
  274. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -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
- dirties_query_cache base, :insert, :update, :delete
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,16 +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"
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)
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)
11
13
 
12
- if column
13
- value = column.cast_type.type_cast_for_database(value)
14
+ if value.respond_to?(:value_for_database)
15
+ value = value.value_for_database
14
16
  end
15
17
 
16
18
  _quote(value)
@@ -19,13 +21,11 @@ module ActiveRecord
19
21
  # Cast a +value+ to a type that the database understands. For example,
20
22
  # SQLite does not understand dates, so this method will convert a Date
21
23
  # to a String.
22
- def type_cast(value, column)
23
- if value.respond_to?(:quoted_id) && value.respond_to?(:id)
24
- return value.id
25
- end
24
+ def type_cast(value, column = nil)
25
+ value = id_value_for_database(value) if value.is_a?(Base)
26
26
 
27
27
  if column
28
- value = column.cast_type.type_cast_for_database(value)
28
+ value = type_cast_from_column(column, value)
29
29
  end
30
30
 
31
31
  _type_cast(value)
@@ -34,15 +34,38 @@ module ActiveRecord
34
34
  raise TypeError, "can't cast #{value.class}#{to_type}"
35
35
  end
36
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
+
37
60
  # Quotes a string, escaping any ' (single quote) and \ (backslash)
38
61
  # characters.
39
62
  def quote_string(s)
40
- s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
63
+ s.gsub('\\'.freeze, '\&\&'.freeze).gsub("'".freeze, "''".freeze) # ' (for ruby-mode)
41
64
  end
42
65
 
43
66
  # Quotes the column name. Defaults to no quoting.
44
67
  def quote_column_name(column_name)
45
- column_name
68
+ column_name.to_s
46
69
  end
47
70
 
48
71
  # Quotes the table name. Defaults to column name quoting.
@@ -53,7 +76,7 @@ module ActiveRecord
53
76
  # Override to return the quoted table name for assignment. Defaults to
54
77
  # table quoting.
55
78
  #
56
- # 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
57
80
  # resolve ambiguity.
58
81
  #
59
82
  # We override this in the sqlite3 and postgresql adapters to use only
@@ -62,22 +85,33 @@ module ActiveRecord
62
85
  quote_table_name("#{table}.#{attr}")
63
86
  end
64
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
+
65
97
  def quoted_true
66
- "'t'"
98
+ "TRUE".freeze
67
99
  end
68
100
 
69
101
  def unquoted_true
70
- 't'
102
+ true
71
103
  end
72
104
 
73
105
  def quoted_false
74
- "'f'"
106
+ "FALSE".freeze
75
107
  end
76
108
 
77
109
  def unquoted_false
78
- 'f'
110
+ false
79
111
  end
80
112
 
113
+ # Quote date/time values for use in SQL input. Includes microseconds
114
+ # if the value is a Time responding to usec.
81
115
  def quoted_date(value)
82
116
  if value.acts_like?(:time)
83
117
  zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
@@ -87,47 +121,80 @@ module ActiveRecord
87
121
  end
88
122
  end
89
123
 
90
- 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
91
130
  end
92
131
 
93
- 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
94
140
 
95
- def types_which_need_no_typecasting
96
- [nil, Numeric, String]
97
- end
98
-
99
- def _quote(value)
100
- case value
101
- when String, ActiveSupport::Multibyte::Chars, Type::Binary::Data
102
- "'#{quote_string(value.to_s)}'"
103
- when true then quoted_true
104
- when false then quoted_false
105
- when nil then "NULL"
106
- # BigDecimals need to be put in a non-normalized form and quoted.
107
- when BigDecimal then value.to_s('F')
108
- when Numeric, ActiveSupport::Duration then value.to_s
109
- when Date, Time then "'#{quoted_date(value)}'"
110
- when Symbol then "'#{quote_string(value.to_s)}'"
111
- 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) }
112
144
  else
113
- "'#{quote_string(YAML.dump(value))}'"
145
+ binds.map { |attr| type_cast(attr.value_for_database) }
114
146
  end
115
147
  end
116
148
 
117
- def _type_cast(value)
118
- case value
119
- when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
120
- value.to_s
121
- when true then unquoted_true
122
- when false then unquoted_false
123
- # BigDecimals need to be put in a non-normalized form and quoted.
124
- when BigDecimal then value.to_s('F')
125
- when Date, Time then quoted_date(value)
126
- when *types_which_need_no_typecasting
127
- value
128
- 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
129
197
  end
130
- end
131
198
  end
132
199
  end
133
200
  end
@@ -1,15 +1,17 @@
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)
9
11
  execute("SAVEPOINT #{name}")
10
12
  end
11
13
 
12
- def rollback_to_savepoint(name = current_savepoint_name)
14
+ def exec_rollback_to_savepoint(name = current_savepoint_name)
13
15
  execute("ROLLBACK TO SAVEPOINT #{name}")
14
16
  end
15
17
 
@@ -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