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,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  ###
3
- # This class encapsulates a Result returned from calling +exec_query+ on any
4
- # database connection adapter. For example:
5
+ # This class encapsulates a result returned from calling
6
+ # {#exec_query}[rdoc-ref:ConnectionAdapters::DatabaseStatements#exec_query]
7
+ # on any database connection adapter. For example:
5
8
  #
6
9
  # result = ActiveRecord::Base.connection.exec_query('SELECT id, title, body FROM posts')
7
10
  # result # => #<ActiveRecord::Result:0xdeadbeef>
@@ -31,8 +34,6 @@ module ActiveRecord
31
34
  class Result
32
35
  include Enumerable
33
36
 
34
- IDENTITY_TYPE = Type::Value.new # :nodoc:
35
-
36
37
  attr_reader :columns, :rows, :column_types
37
38
 
38
39
  def initialize(columns, rows, column_types = {})
@@ -42,10 +43,15 @@ module ActiveRecord
42
43
  @column_types = column_types
43
44
  end
44
45
 
46
+ # Returns the number of elements in the rows array.
45
47
  def length
46
48
  @rows.length
47
49
  end
48
50
 
51
+ # Calls the given block once for each element in row collection, passing
52
+ # row as parameter.
53
+ #
54
+ # Returns an +Enumerator+ if no block is given.
49
55
  def each
50
56
  if block_given?
51
57
  hash_rows.each { |row| yield row }
@@ -54,6 +60,7 @@ module ActiveRecord
54
60
  end
55
61
  end
56
62
 
63
+ # Returns an array of hashes representing each row record.
57
64
  def to_hash
58
65
  hash_rows
59
66
  end
@@ -61,11 +68,12 @@ module ActiveRecord
61
68
  alias :map! :map
62
69
  alias :collect! :map
63
70
 
64
- # Returns true if there are no records.
71
+ # Returns true if there are no records, otherwise false.
65
72
  def empty?
66
73
  rows.empty?
67
74
  end
68
75
 
76
+ # Returns an array of hashes representing each row record.
69
77
  def to_ary
70
78
  hash_rows
71
79
  end
@@ -74,14 +82,24 @@ module ActiveRecord
74
82
  hash_rows[idx]
75
83
  end
76
84
 
85
+ # Returns the first record from the rows collection.
86
+ # If the rows collection is empty, returns +nil+.
87
+ def first
88
+ return nil if @rows.empty?
89
+ Hash[@columns.zip(@rows.first)]
90
+ end
91
+
92
+ # Returns the last record from the rows collection.
93
+ # If the rows collection is empty, returns +nil+.
77
94
  def last
78
- hash_rows.last
95
+ return nil if @rows.empty?
96
+ Hash[@columns.zip(@rows.last)]
79
97
  end
80
98
 
81
99
  def cast_values(type_overrides = {}) # :nodoc:
82
100
  types = columns.map { |name| column_type(name, type_overrides) }
83
101
  result = rows.map do |values|
84
- types.zip(values).map { |type, value| type.type_cast_from_database(value) }
102
+ types.zip(values).map { |type, value| type.deserialize(value) }
85
103
  end
86
104
 
87
105
  columns.one? ? result.map!(&:first) : result
@@ -96,36 +114,36 @@ module ActiveRecord
96
114
 
97
115
  private
98
116
 
99
- def column_type(name, type_overrides = {})
100
- type_overrides.fetch(name) do
101
- column_types.fetch(name, IDENTITY_TYPE)
117
+ def column_type(name, type_overrides = {})
118
+ type_overrides.fetch(name) do
119
+ column_types.fetch(name, Type.default_value)
120
+ end
102
121
  end
103
- end
104
122
 
105
- def hash_rows
106
- @hash_rows ||=
107
- begin
108
- # We freeze the strings to prevent them getting duped when
109
- # used as keys in ActiveRecord::Base's @attributes hash
110
- columns = @columns.map { |c| c.dup.freeze }
111
- @rows.map { |row|
112
- # In the past we used Hash[columns.zip(row)]
113
- # though elegant, the verbose way is much more efficient
114
- # both time and memory wise cause it avoids a big array allocation
115
- # this method is called a lot and needs to be micro optimised
116
- hash = {}
117
-
118
- index = 0
119
- length = columns.length
120
-
121
- while index < length
122
- hash[columns[index]] = row[index]
123
- index += 1
124
- end
125
-
126
- hash
127
- }
128
- end
129
- end
123
+ def hash_rows
124
+ @hash_rows ||=
125
+ begin
126
+ # We freeze the strings to prevent them getting duped when
127
+ # used as keys in ActiveRecord::Base's @attributes hash
128
+ columns = @columns.map { |c| c.dup.freeze }
129
+ @rows.map { |row|
130
+ # In the past we used Hash[columns.zip(row)]
131
+ # though elegant, the verbose way is much more efficient
132
+ # both time and memory wise cause it avoids a big array allocation
133
+ # this method is called a lot and needs to be micro optimised
134
+ hash = {}
135
+
136
+ index = 0
137
+ length = columns.length
138
+
139
+ while index < length
140
+ hash[columns[index]] = row[index]
141
+ index += 1
142
+ end
143
+
144
+ hash
145
+ }
146
+ end
147
+ end
130
148
  end
131
149
  end
@@ -1,4 +1,6 @@
1
- require 'active_support/per_thread_registry'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/per_thread_registry"
2
4
 
3
5
  module ActiveRecord
4
6
  # This is a thread locals registry for Active Record. For example:
@@ -7,14 +9,14 @@ module ActiveRecord
7
9
  #
8
10
  # returns the connection handler local to the current thread.
9
11
  #
10
- # See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
12
+ # See the documentation of ActiveSupport::PerThreadRegistry
11
13
  # for further details.
12
14
  class RuntimeRegistry # :nodoc:
13
15
  extend ActiveSupport::PerThreadRegistry
14
16
 
15
- attr_accessor :connection_handler, :sql_runtime, :connection_id
17
+ attr_accessor :connection_handler, :sql_runtime
16
18
 
17
- [:connection_handler, :sql_runtime, :connection_id].each do |val|
19
+ [:connection_handler, :sql_runtime].each do |val|
18
20
  class_eval %{ def self.#{val}; instance.#{val}; end }, __FILE__, __LINE__
19
21
  class_eval %{ def self.#{val}=(x); instance.#{val}=x; end }, __FILE__, __LINE__
20
22
  end
@@ -1,40 +1,49 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Sanitization
3
5
  extend ActiveSupport::Concern
4
6
 
5
7
  module ClassMethods
6
- def quote_value(value, column) #:nodoc:
7
- connection.quote(value, column)
8
- end
9
-
10
- # Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
11
- def sanitize(object) #:nodoc:
12
- connection.quote(object)
13
- end
14
-
15
- protected
16
-
17
- # Accepts an array, hash, or string of SQL conditions and sanitizes
8
+ # Accepts an array or string of SQL conditions and sanitizes
18
9
  # them into a valid SQL fragment for a WHERE clause.
19
- # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
20
- # { name: "foo'bar", group_id: 4 } returns "name='foo''bar' and group_id='4'"
21
- # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
22
- def sanitize_sql_for_conditions(condition, table_name = self.table_name)
10
+ #
11
+ # sanitize_sql_for_conditions(["name=? and group_id=?", "foo'bar", 4])
12
+ # # => "name='foo''bar' and group_id=4"
13
+ #
14
+ # sanitize_sql_for_conditions(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
15
+ # # => "name='foo''bar' and group_id='4'"
16
+ #
17
+ # sanitize_sql_for_conditions(["name='%s' and group_id='%s'", "foo'bar", 4])
18
+ # # => "name='foo''bar' and group_id='4'"
19
+ #
20
+ # sanitize_sql_for_conditions("name='foo''bar' and group_id='4'")
21
+ # # => "name='foo''bar' and group_id='4'"
22
+ def sanitize_sql_for_conditions(condition)
23
23
  return nil if condition.blank?
24
24
 
25
25
  case condition
26
26
  when Array; sanitize_sql_array(condition)
27
- when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
28
27
  else condition
29
28
  end
30
29
  end
31
- alias_method :sanitize_sql, :sanitize_sql_for_conditions
32
- alias_method :sanitize_conditions, :sanitize_sql
30
+ alias :sanitize_sql :sanitize_sql_for_conditions
33
31
 
34
32
  # Accepts an array, hash, or string of SQL conditions and sanitizes
35
33
  # them into a valid SQL fragment for a SET clause.
36
- # { name: nil, group_id: 4 } returns "name = NULL , group_id='4'"
37
- def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name)
34
+ #
35
+ # sanitize_sql_for_assignment(["name=? and group_id=?", nil, 4])
36
+ # # => "name=NULL and group_id=4"
37
+ #
38
+ # sanitize_sql_for_assignment(["name=:name and group_id=:group_id", name: nil, group_id: 4])
39
+ # # => "name=NULL and group_id=4"
40
+ #
41
+ # Post.sanitize_sql_for_assignment({ name: nil, group_id: 4 })
42
+ # # => "`posts`.`name` = NULL, `posts`.`group_id` = 4"
43
+ #
44
+ # sanitize_sql_for_assignment("name=NULL and group_id='4'")
45
+ # # => "name=NULL and group_id='4'"
46
+ def sanitize_sql_for_assignment(assignments, default_table_name = table_name)
38
47
  case assignments
39
48
  when Array; sanitize_sql_array(assignments)
40
49
  when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
@@ -42,76 +51,59 @@ module ActiveRecord
42
51
  end
43
52
  end
44
53
 
45
- # Accepts a hash of SQL conditions and replaces those attributes
46
- # that correspond to a +composed_of+ relationship with their expanded
47
- # aggregate attribute values.
48
- # Given:
49
- # class Person < ActiveRecord::Base
50
- # composed_of :address, class_name: "Address",
51
- # mapping: [%w(address_street street), %w(address_city city)]
52
- # end
53
- # Then:
54
- # { address: Address.new("813 abc st.", "chicago") }
55
- # # => { address_street: "813 abc st.", address_city: "chicago" }
56
- def expand_hash_conditions_for_aggregates(attrs)
57
- expanded_attrs = {}
58
- attrs.each do |attr, value|
59
- if aggregation = reflect_on_aggregation(attr.to_sym)
60
- mapping = aggregation.mapping
61
- mapping.each do |field_attr, aggregate_attr|
62
- if mapping.size == 1 && !value.respond_to?(aggregate_attr)
63
- expanded_attrs[field_attr] = value
64
- else
65
- expanded_attrs[field_attr] = value.send(aggregate_attr)
66
- end
67
- end
68
- else
69
- expanded_attrs[attr] = value
54
+ # Accepts an array, or string of SQL conditions and sanitizes
55
+ # them into a valid SQL fragment for an ORDER clause.
56
+ #
57
+ # sanitize_sql_for_order(["field(id, ?)", [1,3,2]])
58
+ # # => "field(id, 1,3,2)"
59
+ #
60
+ # sanitize_sql_for_order("id ASC")
61
+ # # => "id ASC"
62
+ def sanitize_sql_for_order(condition)
63
+ if condition.is_a?(Array) && condition.first.to_s.include?("?")
64
+ enforce_raw_sql_whitelist([condition.first],
65
+ whitelist: AttributeMethods::ClassMethods::COLUMN_NAME_ORDER_WHITELIST
66
+ )
67
+
68
+ # Ensure we aren't dealing with a subclass of String that might
69
+ # override methods we use (eg. Arel::Nodes::SqlLiteral).
70
+ if condition.first.kind_of?(String) && !condition.first.instance_of?(String)
71
+ condition = [String.new(condition.first), *condition[1..-1]]
70
72
  end
71
- end
72
- expanded_attrs
73
- end
74
73
 
75
- # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
76
- # { name: "foo'bar", group_id: 4 }
77
- # # => "name='foo''bar' and group_id= 4"
78
- # { status: nil, group_id: [1,2,3] }
79
- # # => "status IS NULL and group_id IN (1,2,3)"
80
- # { age: 13..18 }
81
- # # => "age BETWEEN 13 AND 18"
82
- # { 'other_records.id' => 7 }
83
- # # => "`other_records`.`id` = 7"
84
- # { other_records: { id: 7 } }
85
- # # => "`other_records`.`id` = 7"
86
- # And for value objects on a composed_of relationship:
87
- # { address: Address.new("123 abc st.", "chicago") }
88
- # # => "address_street='123 abc st.' and address_city='chicago'"
89
- def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
90
- ActiveSupport::Deprecation.warn(<<-EOWARN)
91
- sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
92
- EOWARN
93
- attrs = PredicateBuilder.resolve_column_aliases self, attrs
94
- attrs = expand_hash_conditions_for_aggregates(attrs)
95
-
96
- table = Arel::Table.new(table_name, arel_engine).alias(default_table_name)
97
- PredicateBuilder.build_from_hash(self, attrs, table).map { |b|
98
- connection.visitor.compile b
99
- }.join(' AND ')
74
+ Arel.sql(sanitize_sql_array(condition))
75
+ else
76
+ condition
77
+ end
100
78
  end
101
- alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
102
79
 
103
80
  # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
104
- # { status: nil, group_id: 1 }
105
- # # => "status = NULL , group_id = 1"
81
+ #
82
+ # sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
83
+ # # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
106
84
  def sanitize_sql_hash_for_assignment(attrs, table)
107
85
  c = connection
108
86
  attrs.map do |attr, value|
109
- "#{c.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value, c, columns_hash[attr.to_s])}"
110
- end.join(', ')
87
+ type = type_for_attribute(attr)
88
+ value = type.serialize(type.cast(value))
89
+ "#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}"
90
+ end.join(", ")
111
91
  end
112
92
 
113
93
  # Sanitizes a +string+ so that it is safe to use within an SQL
114
- # LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%"
94
+ # LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%".
95
+ #
96
+ # sanitize_sql_like("100%")
97
+ # # => "100\\%"
98
+ #
99
+ # sanitize_sql_like("snake_cased_string")
100
+ # # => "snake\\_cased\\_string"
101
+ #
102
+ # sanitize_sql_like("100%", "!")
103
+ # # => "100!%"
104
+ #
105
+ # sanitize_sql_like("snake_cased_string", "!")
106
+ # # => "snake!_cased!_string"
115
107
  def sanitize_sql_like(string, escape_character = "\\")
116
108
  pattern = Regexp.union(escape_character, "%", "_")
117
109
  string.gsub(pattern) { |x| [escape_character, x].join }
@@ -119,12 +111,20 @@ sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
119
111
 
120
112
  # Accepts an array of conditions. The array has each value
121
113
  # sanitized and interpolated into the SQL statement.
122
- # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
114
+ #
115
+ # sanitize_sql_array(["name=? and group_id=?", "foo'bar", 4])
116
+ # # => "name='foo''bar' and group_id=4"
117
+ #
118
+ # sanitize_sql_array(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
119
+ # # => "name='foo''bar' and group_id=4"
120
+ #
121
+ # sanitize_sql_array(["name='%s' and group_id='%s'", "foo'bar", 4])
122
+ # # => "name='foo''bar' and group_id='4'"
123
123
  def sanitize_sql_array(ary)
124
124
  statement, *values = ary
125
- if values.first.is_a?(Hash) && statement =~ /:\w+/
125
+ if values.first.is_a?(Hash) && /:\w+/.match?(statement)
126
126
  replace_named_bind_variables(statement, values.first)
127
- elsif statement.include?('?')
127
+ elsif statement.include?("?")
128
128
  replace_bind_variables(statement, values)
129
129
  elsif statement.blank?
130
130
  statement
@@ -133,59 +133,90 @@ sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
133
133
  end
134
134
  end
135
135
 
136
- def replace_bind_variables(statement, values) #:nodoc:
137
- raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
138
- bound = values.dup
139
- c = connection
140
- statement.gsub(/\?/) do
141
- replace_bind_variable(bound.shift, c)
136
+ private
137
+ # Accepts a hash of SQL conditions and replaces those attributes
138
+ # that correspond to a {#composed_of}[rdoc-ref:Aggregations::ClassMethods#composed_of]
139
+ # relationship with their expanded aggregate attribute values.
140
+ #
141
+ # Given:
142
+ #
143
+ # class Person < ActiveRecord::Base
144
+ # composed_of :address, class_name: "Address",
145
+ # mapping: [%w(address_street street), %w(address_city city)]
146
+ # end
147
+ #
148
+ # Then:
149
+ #
150
+ # { address: Address.new("813 abc st.", "chicago") }
151
+ # # => { address_street: "813 abc st.", address_city: "chicago" }
152
+ def expand_hash_conditions_for_aggregates(attrs) # :doc:
153
+ expanded_attrs = {}
154
+ attrs.each do |attr, value|
155
+ if aggregation = reflect_on_aggregation(attr.to_sym)
156
+ mapping = aggregation.mapping
157
+ mapping.each do |field_attr, aggregate_attr|
158
+ expanded_attrs[field_attr] = if value.is_a?(Array)
159
+ value.map { |it| it.send(aggregate_attr) }
160
+ elsif mapping.size == 1 && !value.respond_to?(aggregate_attr)
161
+ value
162
+ else
163
+ value.send(aggregate_attr)
164
+ end
165
+ end
166
+ else
167
+ expanded_attrs[attr] = value
168
+ end
169
+ end
170
+ expanded_attrs
142
171
  end
143
- end
144
-
145
- def replace_bind_variable(value, c = connection) #:nodoc:
146
- if ActiveRecord::Relation === value
147
- value.to_sql
148
- else
149
- quote_bound_value(value, c)
172
+ deprecate :expand_hash_conditions_for_aggregates
173
+
174
+ def replace_bind_variables(statement, values)
175
+ raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size)
176
+ bound = values.dup
177
+ c = connection
178
+ statement.gsub(/\?/) do
179
+ replace_bind_variable(bound.shift, c)
180
+ end
150
181
  end
151
- end
152
182
 
153
- def replace_named_bind_variables(statement, bind_vars) #:nodoc:
154
- statement.gsub(/(:?):([a-zA-Z]\w*)/) do
155
- if $1 == ':' # skip postgresql casts
156
- $& # return the whole match
157
- elsif bind_vars.include?(match = $2.to_sym)
158
- replace_bind_variable(bind_vars[match])
183
+ def replace_bind_variable(value, c = connection)
184
+ if ActiveRecord::Relation === value
185
+ value.to_sql
159
186
  else
160
- raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
187
+ quote_bound_value(value, c)
161
188
  end
162
189
  end
163
- end
164
190
 
165
- def quote_bound_value(value, c = connection, column = nil) #:nodoc:
166
- if column
167
- c.quote(value, column)
168
- elsif value.respond_to?(:map) && !value.acts_like?(:string)
169
- if value.respond_to?(:empty?) && value.empty?
170
- c.quote(nil)
171
- else
172
- value.map { |v| c.quote(v) }.join(',')
191
+ def replace_named_bind_variables(statement, bind_vars)
192
+ statement.gsub(/(:?):([a-zA-Z]\w*)/) do |match|
193
+ if $1 == ":" # skip postgresql casts
194
+ match # return the whole match
195
+ elsif bind_vars.include?(match = $2.to_sym)
196
+ replace_bind_variable(bind_vars[match])
197
+ else
198
+ raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
199
+ end
173
200
  end
174
- else
175
- c.quote(value)
176
201
  end
177
- end
178
202
 
179
- def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
180
- unless expected == provided
181
- raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
203
+ def quote_bound_value(value, c = connection)
204
+ if value.respond_to?(:map) && !value.acts_like?(:string)
205
+ if value.respond_to?(:empty?) && value.empty?
206
+ c.quote(nil)
207
+ else
208
+ value.map { |v| c.quote(v) }.join(",")
209
+ end
210
+ else
211
+ c.quote(value)
212
+ end
182
213
  end
183
- end
184
- end
185
214
 
186
- # TODO: Deprecate this
187
- def quoted_id
188
- self.class.quote_value(id, column_for_attribute(self.class.primary_key))
215
+ def raise_if_bind_arity_mismatch(statement, expected, provided)
216
+ unless expected == provided
217
+ raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
218
+ end
219
+ end
189
220
  end
190
221
  end
191
222
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- # = Active Record Schema
4
+ # = Active Record \Schema
3
5
  #
4
6
  # Allows programmers to programmatically define a schema in a portable
5
7
  # DSL. This means you can define tables, indexes, etc. without using SQL
@@ -27,38 +29,42 @@ module ActiveRecord
27
29
  #
28
30
  # ActiveRecord::Schema is only supported by database adapters that also
29
31
  # support migrations, the two features being very similar.
30
- class Schema < Migration
31
-
32
- # Returns the migrations paths.
33
- #
34
- # ActiveRecord::Schema.new.migrations_paths
35
- # # => ["db/migrate"] # Rails migration path by default.
36
- def migrations_paths
37
- ActiveRecord::Migrator.migrations_paths
38
- end
39
-
40
- def define(info, &block) # :nodoc:
41
- instance_eval(&block)
42
-
43
- unless info[:version].blank?
44
- initialize_schema_migrations_table
45
- connection.assume_migrated_upto_version(info[:version], migrations_paths)
46
- end
47
- end
48
-
32
+ class Schema < Migration::Current
49
33
  # Eval the given block. All methods available to the current connection
50
34
  # adapter are available within the block, so you can easily use the
51
- # database definition DSL to build up your schema (+create_table+,
52
- # +add_index+, etc.).
35
+ # database definition DSL to build up your schema (
36
+ # {create_table}[rdoc-ref:ConnectionAdapters::SchemaStatements#create_table],
37
+ # {add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index], etc.).
53
38
  #
54
39
  # The +info+ hash is optional, and if given is used to define metadata
55
40
  # about the current schema (currently, only the schema's version):
56
41
  #
57
- # ActiveRecord::Schema.define(version: 20380119000001) do
42
+ # ActiveRecord::Schema.define(version: 2038_01_19_000001) do
58
43
  # ...
59
44
  # end
60
- def self.define(info={}, &block)
45
+ def self.define(info = {}, &block)
61
46
  new.define(info, &block)
62
47
  end
48
+
49
+ def define(info, &block) # :nodoc:
50
+ instance_eval(&block)
51
+
52
+ if info[:version].present?
53
+ ActiveRecord::SchemaMigration.create_table
54
+ connection.assume_migrated_upto_version(info[:version], migrations_paths)
55
+ end
56
+
57
+ ActiveRecord::InternalMetadata.create_table
58
+ ActiveRecord::InternalMetadata[:environment] = connection.migration_context.current_environment
59
+ end
60
+
61
+ private
62
+ # Returns the migrations paths.
63
+ #
64
+ # ActiveRecord::Schema.new.migrations_paths
65
+ # # => ["db/migrate"] # Rails migration path by default.
66
+ def migrations_paths
67
+ ActiveRecord::Migrator.migrations_paths
68
+ end
63
69
  end
64
70
  end