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.
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