activerecord 3.2.22.5 → 5.2.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (275) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -621
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +41 -46
  5. data/examples/performance.rb +55 -42
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +264 -236
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +127 -75
  11. data/lib/active_record/associations/association_scope.rb +126 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +78 -27
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -4
  14. data/lib/active_record/associations/builder/association.rb +117 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +135 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -54
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +120 -42
  18. data/lib/active_record/associations/builder/has_many.rb +10 -64
  19. data/lib/active_record/associations/builder/has_one.rb +19 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +28 -18
  21. data/lib/active_record/associations/collection_association.rb +226 -293
  22. data/lib/active_record/associations/collection_proxy.rb +1067 -69
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +83 -47
  25. data/lib/active_record/associations/has_many_through_association.rb +98 -65
  26. data/lib/active_record/associations/has_one_association.rb +57 -20
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +48 -126
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +212 -164
  32. data/lib/active_record/associations/preloader/association.rb +95 -89
  33. data/lib/active_record/associations/preloader/through_association.rb +84 -44
  34. data/lib/active_record/associations/preloader.rb +123 -111
  35. data/lib/active_record/associations/singular_association.rb +33 -24
  36. data/lib/active_record/associations/through_association.rb +60 -26
  37. data/lib/active_record/associations.rb +1759 -1506
  38. data/lib/active_record/attribute_assignment.rb +60 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +55 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +113 -74
  42. data/lib/active_record/attribute_methods/primary_key.rb +106 -77
  43. data/lib/active_record/attribute_methods/query.rb +8 -5
  44. data/lib/active_record/attribute_methods/read.rb +63 -114
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -90
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -43
  47. data/lib/active_record/attribute_methods/write.rb +43 -45
  48. data/lib/active_record/attribute_methods.rb +366 -149
  49. data/lib/active_record/attributes.rb +266 -0
  50. data/lib/active_record/autosave_association.rb +312 -225
  51. data/lib/active_record/base.rb +114 -505
  52. data/lib/active_record/callbacks.rb +145 -67
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +883 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +16 -2
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +350 -200
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +150 -65
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +477 -284
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1100 -310
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +450 -118
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +657 -446
  69. data/lib/active_record/connection_adapters/column.rb +50 -255
  70. data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -210
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +620 -1080
  117. data/lib/active_record/connection_adapters/schema_cache.rb +85 -36
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +545 -27
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +145 -0
  128. data/lib/active_record/core.rb +559 -0
  129. data/lib/active_record/counter_cache.rb +200 -105
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +107 -69
  132. data/lib/active_record/enum.rb +244 -0
  133. data/lib/active_record/errors.rb +245 -60
  134. data/lib/active_record/explain.rb +35 -71
  135. data/lib/active_record/explain_registry.rb +32 -0
  136. data/lib/active_record/explain_subscriber.rb +18 -9
  137. data/lib/active_record/fixture_set/file.rb +82 -0
  138. data/lib/active_record/fixtures.rb +418 -275
  139. data/lib/active_record/gem_version.rb +17 -0
  140. data/lib/active_record/inheritance.rb +209 -100
  141. data/lib/active_record/integration.rb +116 -21
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  144. data/lib/active_record/locale/en.yml +9 -1
  145. data/lib/active_record/locking/optimistic.rb +107 -94
  146. data/lib/active_record/locking/pessimistic.rb +20 -8
  147. data/lib/active_record/log_subscriber.rb +99 -34
  148. data/lib/active_record/migration/command_recorder.rb +199 -64
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +17 -0
  151. data/lib/active_record/migration.rb +893 -296
  152. data/lib/active_record/model_schema.rb +328 -175
  153. data/lib/active_record/nested_attributes.rb +338 -242
  154. data/lib/active_record/no_touching.rb +58 -0
  155. data/lib/active_record/null_relation.rb +68 -0
  156. data/lib/active_record/persistence.rb +557 -170
  157. data/lib/active_record/query_cache.rb +14 -43
  158. data/lib/active_record/querying.rb +36 -24
  159. data/lib/active_record/railtie.rb +147 -52
  160. data/lib/active_record/railties/console_sandbox.rb +5 -4
  161. data/lib/active_record/railties/controller_runtime.rb +13 -6
  162. data/lib/active_record/railties/databases.rake +206 -488
  163. data/lib/active_record/readonly_attributes.rb +4 -6
  164. data/lib/active_record/reflection.rb +734 -228
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +249 -52
  167. data/lib/active_record/relation/calculations.rb +330 -284
  168. data/lib/active_record/relation/delegation.rb +135 -37
  169. data/lib/active_record/relation/finder_methods.rb +450 -287
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +193 -0
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  179. data/lib/active_record/relation/predicate_builder.rb +132 -43
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +1037 -221
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +48 -151
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +451 -359
  187. data/lib/active_record/result.rb +129 -20
  188. data/lib/active_record/runtime_registry.rb +24 -0
  189. data/lib/active_record/sanitization.rb +164 -136
  190. data/lib/active_record/schema.rb +31 -19
  191. data/lib/active_record/schema_dumper.rb +154 -107
  192. data/lib/active_record/schema_migration.rb +56 -0
  193. data/lib/active_record/scoping/default.rb +108 -98
  194. data/lib/active_record/scoping/named.rb +125 -112
  195. data/lib/active_record/scoping.rb +77 -123
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +10 -6
  198. data/lib/active_record/statement_cache.rb +121 -0
  199. data/lib/active_record/store.rb +175 -16
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +337 -0
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
  206. data/lib/active_record/timestamp.rb +80 -41
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +240 -119
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +9 -0
  212. data/lib/active_record/type/date_time.rb +9 -0
  213. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  214. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +71 -0
  218. data/lib/active_record/type/text.rb +11 -0
  219. data/lib/active_record/type/time.rb +21 -0
  220. data/lib/active_record/type/type_map.rb +62 -0
  221. data/lib/active_record/type/unsigned_integer.rb +17 -0
  222. data/lib/active_record/type.rb +79 -0
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +35 -18
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +68 -0
  230. data/lib/active_record/validations/uniqueness.rb +133 -75
  231. data/lib/active_record/validations.rb +53 -43
  232. data/lib/active_record/version.rb +7 -7
  233. data/lib/active_record.rb +89 -57
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +61 -8
  237. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  238. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
  239. data/lib/rails/generators/active_record/migration.rb +28 -8
  240. data/lib/rails/generators/active_record/model/model_generator.rb +23 -22
  241. data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  243. data/lib/rails/generators/active_record.rb +10 -16
  244. metadata +141 -62
  245. data/examples/associations.png +0 -0
  246. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  247. data/lib/active_record/associations/join_helper.rb +0 -55
  248. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  249. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  250. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  251. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  252. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  253. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  254. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  255. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  256. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  257. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  258. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  259. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  260. data/lib/active_record/dynamic_finder_match.rb +0 -68
  261. data/lib/active_record/dynamic_scope_match.rb +0 -23
  262. data/lib/active_record/fixtures/file.rb +0 -65
  263. data/lib/active_record/identity_map.rb +0 -162
  264. data/lib/active_record/observer.rb +0 -121
  265. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  266. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  267. data/lib/active_record/session_store.rb +0 -360
  268. data/lib/active_record/test_case.rb +0 -73
  269. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  270. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  271. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  272. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  273. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  274. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  275. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,40 +1,149 @@
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:
8
+ #
9
+ # result = ActiveRecord::Base.connection.exec_query('SELECT id, title, body FROM posts')
10
+ # result # => #<ActiveRecord::Result:0xdeadbeef>
11
+ #
12
+ # # Get the column names of the result:
13
+ # result.columns
14
+ # # => ["id", "title", "body"]
15
+ #
16
+ # # Get the record values of the result:
17
+ # result.rows
18
+ # # => [[1, "title_1", "body_1"],
19
+ # [2, "title_2", "body_2"],
20
+ # ...
21
+ # ]
5
22
  #
6
- # x = ActiveRecord::Base.connection.exec_query('SELECT * FROM foo')
7
- # x # => #<ActiveRecord::Result:0xdeadbeef>
23
+ # # Get an array of hashes representing the result (column => value):
24
+ # result.to_hash
25
+ # # => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
26
+ # {"id" => 2, "title" => "title_2", "body" => "body_2"},
27
+ # ...
28
+ # ]
29
+ #
30
+ # # ActiveRecord::Result also includes Enumerable.
31
+ # result.each do |row|
32
+ # puts row['title'] + " " + row['body']
33
+ # end
8
34
  class Result
9
35
  include Enumerable
10
36
 
11
- attr_reader :columns, :rows
37
+ attr_reader :columns, :rows, :column_types
38
+
39
+ def initialize(columns, rows, column_types = {})
40
+ @columns = columns
41
+ @rows = rows
42
+ @hash_rows = nil
43
+ @column_types = column_types
44
+ end
12
45
 
13
- def initialize(columns, rows)
14
- @columns = columns
15
- @rows = rows
16
- @hash_rows = nil
46
+ # Returns the number of elements in the rows array.
47
+ def length
48
+ @rows.length
17
49
  end
18
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.
19
55
  def each
20
- hash_rows.each { |row| yield row }
56
+ if block_given?
57
+ hash_rows.each { |row| yield row }
58
+ else
59
+ hash_rows.to_enum { @rows.size }
60
+ end
21
61
  end
22
62
 
63
+ # Returns an array of hashes representing each row record.
23
64
  def to_hash
24
65
  hash_rows
25
66
  end
26
67
 
68
+ alias :map! :map
69
+ alias :collect! :map
70
+
71
+ # Returns true if there are no records, otherwise false.
72
+ def empty?
73
+ rows.empty?
74
+ end
75
+
76
+ # Returns an array of hashes representing each row record.
77
+ def to_ary
78
+ hash_rows
79
+ end
80
+
81
+ def [](idx)
82
+ hash_rows[idx]
83
+ end
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+.
94
+ def last
95
+ return nil if @rows.empty?
96
+ Hash[@columns.zip(@rows.last)]
97
+ end
98
+
99
+ def cast_values(type_overrides = {}) # :nodoc:
100
+ types = columns.map { |name| column_type(name, type_overrides) }
101
+ result = rows.map do |values|
102
+ types.zip(values).map { |type, value| type.deserialize(value) }
103
+ end
104
+
105
+ columns.one? ? result.map!(&:first) : result
106
+ end
107
+
108
+ def initialize_copy(other)
109
+ @columns = columns.dup
110
+ @rows = rows.dup
111
+ @column_types = column_types.dup
112
+ @hash_rows = nil
113
+ end
114
+
27
115
  private
28
- def hash_rows
29
- @hash_rows ||=
30
- begin
31
- # We freeze the strings to prevent them getting duped when
32
- # used as keys in ActiveRecord::Model's @attributes hash
33
- columns = @columns.map { |c| c.dup.freeze }
34
- @rows.map { |row|
35
- Hash[columns.zip(row)]
36
- }
116
+
117
+ def column_type(name, type_overrides = {})
118
+ type_overrides.fetch(name) do
119
+ column_types.fetch(name, Type.default_value)
37
120
  end
38
- end
121
+ end
122
+
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
39
148
  end
40
149
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/per_thread_registry"
4
+
5
+ module ActiveRecord
6
+ # This is a thread locals registry for Active Record. For example:
7
+ #
8
+ # ActiveRecord::RuntimeRegistry.connection_handler
9
+ #
10
+ # returns the connection handler local to the current thread.
11
+ #
12
+ # See the documentation of ActiveSupport::PerThreadRegistry
13
+ # for further details.
14
+ class RuntimeRegistry # :nodoc:
15
+ extend ActiveSupport::PerThreadRegistry
16
+
17
+ attr_accessor :connection_handler, :sql_runtime
18
+
19
+ [:connection_handler, :sql_runtime].each do |val|
20
+ class_eval %{ def self.#{val}; instance.#{val}; end }, __FILE__, __LINE__
21
+ class_eval %{ def self.#{val}=(x); instance.#{val}=x; end }, __FILE__, __LINE__
22
+ end
23
+ end
24
+ end
@@ -1,119 +1,130 @@
1
- require 'active_support/concern'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
4
  module Sanitization
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  module ClassMethods
8
- def quote_value(value, column = nil) #:nodoc:
9
- connection.quote(value,column)
10
- end
11
-
12
- # Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
13
- def sanitize(object) #:nodoc:
14
- connection.quote(object)
15
- end
16
-
17
- protected
18
-
19
- # Accepts an array, hash, or string of SQL conditions and sanitizes
8
+ # Accepts an array or string of SQL conditions and sanitizes
20
9
  # them into a valid SQL fragment for a WHERE clause.
21
- # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
22
- # { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'"
23
- # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
24
- 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)
25
23
  return nil if condition.blank?
26
24
 
27
25
  case condition
28
26
  when Array; sanitize_sql_array(condition)
29
- when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
30
27
  else condition
31
28
  end
32
29
  end
33
- alias_method :sanitize_sql, :sanitize_sql_for_conditions
30
+ alias :sanitize_sql :sanitize_sql_for_conditions
34
31
 
35
32
  # Accepts an array, hash, or string of SQL conditions and sanitizes
36
33
  # them into a valid SQL fragment for a SET clause.
37
- # { :name => nil, :group_id => 4 } returns "name = NULL , group_id='4'"
38
- def sanitize_sql_for_assignment(assignments)
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)
39
47
  case assignments
40
- when Array; sanitize_sql_array(assignments)
41
- when Hash; sanitize_sql_hash_for_assignment(assignments)
42
- else assignments
48
+ when Array; sanitize_sql_array(assignments)
49
+ when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
50
+ else assignments
43
51
  end
44
52
  end
45
53
 
46
- # Accepts a hash of SQL conditions and replaces those attributes
47
- # that correspond to a +composed_of+ relationship with their expanded
48
- # aggregate attribute values.
49
- # Given:
50
- # class Person < ActiveRecord::Base
51
- # composed_of :address, :class_name => "Address",
52
- # :mapping => [%w(address_street street), %w(address_city city)]
53
- # end
54
- # Then:
55
- # { :address => Address.new("813 abc st.", "chicago") }
56
- # # => { :address_street => "813 abc st.", :address_city => "chicago" }
57
- def expand_hash_conditions_for_aggregates(attrs)
58
- expanded_attrs = {}
59
- attrs.each do |attr, value|
60
- unless (aggregation = reflect_on_aggregation(attr.to_sym)).nil?
61
- mapping = aggregate_mapping(aggregation)
62
- mapping.each do |field_attr, aggregate_attr|
63
- if mapping.size == 1 && !value.respond_to?(aggregate_attr)
64
- expanded_attrs[field_attr] = value
65
- else
66
- expanded_attrs[field_attr] = value.send(aggregate_attr)
67
- end
68
- end
69
- else
70
- 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]]
71
72
  end
72
- end
73
- expanded_attrs
74
- end
75
73
 
76
- # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
77
- # { :name => "foo'bar", :group_id => 4 }
78
- # # => "name='foo''bar' and group_id= 4"
79
- # { :status => nil, :group_id => [1,2,3] }
80
- # # => "status IS NULL and group_id IN (1,2,3)"
81
- # { :age => 13..18 }
82
- # # => "age BETWEEN 13 AND 18"
83
- # { 'other_records.id' => 7 }
84
- # # => "`other_records`.`id` = 7"
85
- # { :other_records => { :id => 7 } }
86
- # # => "`other_records`.`id` = 7"
87
- # And for value objects on a composed_of relationship:
88
- # { :address => Address.new("123 abc st.", "chicago") }
89
- # # => "address_street='123 abc st.' and address_city='chicago'"
90
- def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
91
- attrs = expand_hash_conditions_for_aggregates(attrs)
92
-
93
- table = Arel::Table.new(table_name).alias(default_table_name)
94
- PredicateBuilder.build_from_hash(arel_engine, attrs, table).map { |b|
95
- connection.visitor.accept b
96
- }.join(' AND ')
74
+ Arel.sql(sanitize_sql_array(condition))
75
+ else
76
+ condition
77
+ end
97
78
  end
98
- alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
99
79
 
100
80
  # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
101
- # { :status => nil, :group_id => 1 }
102
- # # => "status = NULL , group_id = 1"
103
- def sanitize_sql_hash_for_assignment(attrs)
81
+ #
82
+ # sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
83
+ # # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
84
+ def sanitize_sql_hash_for_assignment(attrs, table)
85
+ c = connection
104
86
  attrs.map do |attr, value|
105
- "#{connection.quote_column_name(attr)} = #{quote_bound_value(value)}"
106
- 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(", ")
91
+ end
92
+
93
+ # Sanitizes a +string+ so that it is safe to use within an SQL
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"
107
+ def sanitize_sql_like(string, escape_character = "\\")
108
+ pattern = Regexp.union(escape_character, "%", "_")
109
+ string.gsub(pattern) { |x| [escape_character, x].join }
107
110
  end
108
111
 
109
112
  # Accepts an array of conditions. The array has each value
110
113
  # sanitized and interpolated into the SQL statement.
111
- # ["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'"
112
123
  def sanitize_sql_array(ary)
113
124
  statement, *values = ary
114
- if values.first.is_a?(Hash) && statement =~ /:\w+/
125
+ if values.first.is_a?(Hash) && /:\w+/.match?(statement)
115
126
  replace_named_bind_variables(statement, values.first)
116
- elsif statement.include?('?')
127
+ elsif statement.include?("?")
117
128
  replace_bind_variables(statement, values)
118
129
  elsif statement.blank?
119
130
  statement
@@ -122,73 +133,90 @@ module ActiveRecord
122
133
  end
123
134
  end
124
135
 
125
- alias_method :sanitize_conditions, :sanitize_sql
126
-
127
- def replace_bind_variables(statement, values) #:nodoc:
128
- raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
129
- bound = values.dup
130
- c = connection
131
- statement.gsub('?') { quote_bound_value(bound.shift, c) }
132
- end
133
-
134
- def replace_named_bind_variables(statement, bind_vars) #:nodoc:
135
- statement.gsub(/(:?):([a-zA-Z]\w*)/) do
136
- if $1 == ':' # skip postgresql casts
137
- $& # return the whole match
138
- elsif bind_vars.include?(match = $2.to_sym)
139
- quote_bound_value(bind_vars[match])
140
- else
141
- raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
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
171
+ end
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)
142
180
  end
143
181
  end
144
- end
145
-
146
- def expand_range_bind_variables(bind_vars) #:nodoc:
147
- expanded = []
148
-
149
- bind_vars.each do |var|
150
- next if var.is_a?(Hash)
151
182
 
152
- if var.is_a?(Range)
153
- expanded << var.first
154
- expanded << var.last
183
+ def replace_bind_variable(value, c = connection)
184
+ if ActiveRecord::Relation === value
185
+ value.to_sql
155
186
  else
156
- expanded << var
187
+ quote_bound_value(value, c)
157
188
  end
158
189
  end
159
190
 
160
- expanded
161
- end
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
200
+ end
201
+ end
162
202
 
163
- def quote_bound_value(value, c = connection) #:nodoc:
164
- if value.respond_to?(:map) && !value.acts_like?(:string)
165
- if value.respond_to?(:empty?) && value.empty?
166
- c.quote(nil)
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
167
210
  else
168
- value.map { |v| c.quote(v) }.join(',')
211
+ c.quote(value)
169
212
  end
170
- else
171
- c.quote(value)
172
213
  end
173
- end
174
214
 
175
- def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
176
- unless expected == provided
177
- raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
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
178
219
  end
179
- end
180
- end
181
-
182
- # TODO: Deprecate this
183
- def quoted_id #:nodoc:
184
- quote_value(id, column_for_attribute(self.class.primary_key))
185
- end
186
-
187
- private
188
-
189
- # Quote strings appropriately for SQL statements.
190
- def quote_value(value, column = nil)
191
- self.class.connection.quote(value, column)
192
220
  end
193
221
  end
194
222
  end
@@ -1,7 +1,7 @@
1
- require 'active_support/core_ext/object/blank'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
- # = Active Record Schema
4
+ # = Active Record \Schema
5
5
  #
6
6
  # Allows programmers to programmatically define a schema in a portable
7
7
  # DSL. This means you can define tables, indexes, etc. without using SQL
@@ -12,16 +12,16 @@ module ActiveRecord
12
12
  #
13
13
  # ActiveRecord::Schema.define do
14
14
  # create_table :authors do |t|
15
- # t.string :name, :null => false
15
+ # t.string :name, null: false
16
16
  # end
17
17
  #
18
18
  # add_index :authors, :name, :unique
19
19
  #
20
20
  # create_table :posts do |t|
21
- # t.integer :author_id, :null => false
21
+ # t.integer :author_id, null: false
22
22
  # t.string :subject
23
23
  # t.text :body
24
- # t.boolean :private, :default => false
24
+ # t.boolean :private, default: false
25
25
  # end
26
26
  #
27
27
  # add_index :posts, :author_id
@@ -29,30 +29,42 @@ module ActiveRecord
29
29
  #
30
30
  # ActiveRecord::Schema is only supported by database adapters that also
31
31
  # support migrations, the two features being very similar.
32
- class Schema < Migration
33
- def migrations_paths
34
- ActiveRecord::Migrator.migrations_paths
35
- end
36
-
32
+ class Schema < Migration::Current
37
33
  # Eval the given block. All methods available to the current connection
38
34
  # adapter are available within the block, so you can easily use the
39
- # database definition DSL to build up your schema (+create_table+,
40
- # +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.).
41
38
  #
42
39
  # The +info+ hash is optional, and if given is used to define metadata
43
40
  # about the current schema (currently, only the schema's version):
44
41
  #
45
- # ActiveRecord::Schema.define(:version => 20380119000001) do
42
+ # ActiveRecord::Schema.define(version: 2038_01_19_000001) do
46
43
  # ...
47
44
  # end
48
- def self.define(info={}, &block)
49
- schema = new
50
- schema.instance_eval(&block)
45
+ def self.define(info = {}, &block)
46
+ new.define(info, &block)
47
+ end
48
+
49
+ def define(info, &block) # :nodoc:
50
+ instance_eval(&block)
51
51
 
52
- unless info[:version].blank?
53
- initialize_schema_migrations_table
54
- assume_migrated_upto_version(info[:version], schema.migrations_paths)
52
+ if info[:version].present?
53
+ ActiveRecord::SchemaMigration.create_table
54
+ connection.assume_migrated_upto_version(info[:version], migrations_paths)
55
55
  end
56
+
57
+ ActiveRecord::InternalMetadata.create_table
58
+ ActiveRecord::InternalMetadata[:environment] = connection.migration_context.current_environment
56
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
57
69
  end
58
70
  end