activerecord 5.2.3

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 (244) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +937 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +217 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record.rb +188 -0
  8. data/lib/active_record/aggregations.rb +283 -0
  9. data/lib/active_record/association_relation.rb +40 -0
  10. data/lib/active_record/associations.rb +1860 -0
  11. data/lib/active_record/associations/alias_tracker.rb +81 -0
  12. data/lib/active_record/associations/association.rb +299 -0
  13. data/lib/active_record/associations/association_scope.rb +168 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +130 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +140 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +163 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +82 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +135 -0
  20. data/lib/active_record/associations/builder/has_many.rb +17 -0
  21. data/lib/active_record/associations/builder/has_one.rb +30 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +42 -0
  23. data/lib/active_record/associations/collection_association.rb +513 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1131 -0
  25. data/lib/active_record/associations/foreign_association.rb +13 -0
  26. data/lib/active_record/associations/has_many_association.rb +144 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +227 -0
  28. data/lib/active_record/associations/has_one_association.rb +120 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency.rb +262 -0
  31. data/lib/active_record/associations/join_dependency/join_association.rb +60 -0
  32. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  33. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  34. data/lib/active_record/associations/preloader.rb +193 -0
  35. data/lib/active_record/associations/preloader/association.rb +131 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +107 -0
  37. data/lib/active_record/associations/singular_association.rb +73 -0
  38. data/lib/active_record/associations/through_association.rb +121 -0
  39. data/lib/active_record/attribute_assignment.rb +88 -0
  40. data/lib/active_record/attribute_decorators.rb +90 -0
  41. data/lib/active_record/attribute_methods.rb +492 -0
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +78 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +150 -0
  44. data/lib/active_record/attribute_methods/primary_key.rb +143 -0
  45. data/lib/active_record/attribute_methods/query.rb +42 -0
  46. data/lib/active_record/attribute_methods/read.rb +85 -0
  47. data/lib/active_record/attribute_methods/serialization.rb +90 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +91 -0
  49. data/lib/active_record/attribute_methods/write.rb +68 -0
  50. data/lib/active_record/attributes.rb +266 -0
  51. data/lib/active_record/autosave_association.rb +498 -0
  52. data/lib/active_record/base.rb +329 -0
  53. data/lib/active_record/callbacks.rb +353 -0
  54. data/lib/active_record/coders/json.rb +15 -0
  55. data/lib/active_record/coders/yaml_column.rb +50 -0
  56. data/lib/active_record/collection_cache_key.rb +53 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1068 -0
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +72 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +540 -0
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +145 -0
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +200 -0
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +685 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1396 -0
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +628 -0
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +887 -0
  70. data/lib/active_record/connection_adapters/column.rb +91 -0
  71. data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  73. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  81. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  82. data/lib/active_record/connection_adapters/mysql2_adapter.rb +129 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
  85. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  109. data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
  110. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
  115. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  116. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  117. data/lib/active_record/connection_adapters/postgresql_adapter.rb +863 -0
  118. data/lib/active_record/connection_adapters/schema_cache.rb +118 -0
  119. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +573 -0
  127. data/lib/active_record/connection_adapters/statement_pool.rb +61 -0
  128. data/lib/active_record/connection_handling.rb +145 -0
  129. data/lib/active_record/core.rb +559 -0
  130. data/lib/active_record/counter_cache.rb +218 -0
  131. data/lib/active_record/define_callbacks.rb +22 -0
  132. data/lib/active_record/dynamic_matchers.rb +122 -0
  133. data/lib/active_record/enum.rb +244 -0
  134. data/lib/active_record/errors.rb +380 -0
  135. data/lib/active_record/explain.rb +50 -0
  136. data/lib/active_record/explain_registry.rb +32 -0
  137. data/lib/active_record/explain_subscriber.rb +34 -0
  138. data/lib/active_record/fixture_set/file.rb +82 -0
  139. data/lib/active_record/fixtures.rb +1065 -0
  140. data/lib/active_record/gem_version.rb +17 -0
  141. data/lib/active_record/inheritance.rb +283 -0
  142. data/lib/active_record/integration.rb +155 -0
  143. data/lib/active_record/internal_metadata.rb +45 -0
  144. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  145. data/lib/active_record/locale/en.yml +48 -0
  146. data/lib/active_record/locking/optimistic.rb +198 -0
  147. data/lib/active_record/locking/pessimistic.rb +89 -0
  148. data/lib/active_record/log_subscriber.rb +137 -0
  149. data/lib/active_record/migration.rb +1378 -0
  150. data/lib/active_record/migration/command_recorder.rb +240 -0
  151. data/lib/active_record/migration/compatibility.rb +217 -0
  152. data/lib/active_record/migration/join_table.rb +17 -0
  153. data/lib/active_record/model_schema.rb +521 -0
  154. data/lib/active_record/nested_attributes.rb +600 -0
  155. data/lib/active_record/no_touching.rb +58 -0
  156. data/lib/active_record/null_relation.rb +68 -0
  157. data/lib/active_record/persistence.rb +763 -0
  158. data/lib/active_record/query_cache.rb +45 -0
  159. data/lib/active_record/querying.rb +70 -0
  160. data/lib/active_record/railtie.rb +226 -0
  161. data/lib/active_record/railties/console_sandbox.rb +7 -0
  162. data/lib/active_record/railties/controller_runtime.rb +56 -0
  163. data/lib/active_record/railties/databases.rake +377 -0
  164. data/lib/active_record/readonly_attributes.rb +24 -0
  165. data/lib/active_record/reflection.rb +1044 -0
  166. data/lib/active_record/relation.rb +629 -0
  167. data/lib/active_record/relation/batches.rb +287 -0
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  169. data/lib/active_record/relation/calculations.rb +417 -0
  170. data/lib/active_record/relation/delegation.rb +147 -0
  171. data/lib/active_record/relation/finder_methods.rb +565 -0
  172. data/lib/active_record/relation/from_clause.rb +26 -0
  173. data/lib/active_record/relation/merger.rb +193 -0
  174. data/lib/active_record/relation/predicate_builder.rb +152 -0
  175. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  177. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  179. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  180. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  181. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  182. data/lib/active_record/relation/query_attribute.rb +45 -0
  183. data/lib/active_record/relation/query_methods.rb +1231 -0
  184. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  185. data/lib/active_record/relation/spawn_methods.rb +77 -0
  186. data/lib/active_record/relation/where_clause.rb +186 -0
  187. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  188. data/lib/active_record/result.rb +149 -0
  189. data/lib/active_record/runtime_registry.rb +24 -0
  190. data/lib/active_record/sanitization.rb +222 -0
  191. data/lib/active_record/schema.rb +70 -0
  192. data/lib/active_record/schema_dumper.rb +255 -0
  193. data/lib/active_record/schema_migration.rb +56 -0
  194. data/lib/active_record/scoping.rb +106 -0
  195. data/lib/active_record/scoping/default.rb +152 -0
  196. data/lib/active_record/scoping/named.rb +213 -0
  197. data/lib/active_record/secure_token.rb +40 -0
  198. data/lib/active_record/serialization.rb +22 -0
  199. data/lib/active_record/statement_cache.rb +121 -0
  200. data/lib/active_record/store.rb +211 -0
  201. data/lib/active_record/suppressor.rb +61 -0
  202. data/lib/active_record/table_metadata.rb +82 -0
  203. data/lib/active_record/tasks/database_tasks.rb +337 -0
  204. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  205. data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
  206. data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
  207. data/lib/active_record/timestamp.rb +153 -0
  208. data/lib/active_record/touch_later.rb +64 -0
  209. data/lib/active_record/transactions.rb +502 -0
  210. data/lib/active_record/translation.rb +24 -0
  211. data/lib/active_record/type.rb +79 -0
  212. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  213. data/lib/active_record/type/date.rb +9 -0
  214. data/lib/active_record/type/date_time.rb +9 -0
  215. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  216. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  217. data/lib/active_record/type/internal/timezone.rb +17 -0
  218. data/lib/active_record/type/json.rb +30 -0
  219. data/lib/active_record/type/serialized.rb +71 -0
  220. data/lib/active_record/type/text.rb +11 -0
  221. data/lib/active_record/type/time.rb +21 -0
  222. data/lib/active_record/type/type_map.rb +62 -0
  223. data/lib/active_record/type/unsigned_integer.rb +17 -0
  224. data/lib/active_record/type_caster.rb +9 -0
  225. data/lib/active_record/type_caster/connection.rb +33 -0
  226. data/lib/active_record/type_caster/map.rb +23 -0
  227. data/lib/active_record/validations.rb +93 -0
  228. data/lib/active_record/validations/absence.rb +25 -0
  229. data/lib/active_record/validations/associated.rb +60 -0
  230. data/lib/active_record/validations/length.rb +26 -0
  231. data/lib/active_record/validations/presence.rb +68 -0
  232. data/lib/active_record/validations/uniqueness.rb +238 -0
  233. data/lib/active_record/version.rb +10 -0
  234. data/lib/rails/generators/active_record.rb +19 -0
  235. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  236. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  237. data/lib/rails/generators/active_record/migration.rb +35 -0
  238. data/lib/rails/generators/active_record/migration/migration_generator.rb +78 -0
  239. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  240. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
  241. data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
  242. data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
  243. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  244. metadata +333 -0
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class Relation
5
+ module RecordFetchWarning
6
+ # When this module is prepended to ActiveRecord::Relation and
7
+ # +config.active_record.warn_on_records_fetched_greater_than+ is
8
+ # set to an integer, if the number of records a query returns is
9
+ # greater than the value of +warn_on_records_fetched_greater_than+,
10
+ # a warning is logged. This allows for the detection of queries that
11
+ # return a large number of records, which could cause memory bloat.
12
+ #
13
+ # In most cases, fetching large number of records can be performed
14
+ # efficiently using the ActiveRecord::Batches methods.
15
+ # See ActiveRecord::Batches for more information.
16
+ def exec_queries
17
+ QueryRegistry.reset
18
+
19
+ super.tap do
20
+ if logger && warn_on_records_fetched_greater_than
21
+ if @records.length > warn_on_records_fetched_greater_than
22
+ logger.warn "Query fetched #{@records.size} #{@klass} records: #{QueryRegistry.queries.join(";")}"
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ # :stopdoc:
29
+ ActiveSupport::Notifications.subscribe("sql.active_record") do |*, payload|
30
+ QueryRegistry.queries << payload[:sql]
31
+ end
32
+ # :startdoc:
33
+
34
+ class QueryRegistry # :nodoc:
35
+ extend ActiveSupport::PerThreadRegistry
36
+
37
+ attr_reader :queries
38
+
39
+ def initialize
40
+ @queries = []
41
+ end
42
+
43
+ def reset
44
+ @queries.clear
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ ActiveRecord::Relation.prepend ActiveRecord::Relation::RecordFetchWarning
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/except"
4
+ require "active_support/core_ext/hash/slice"
5
+ require "active_record/relation/merger"
6
+
7
+ module ActiveRecord
8
+ module SpawnMethods
9
+ # This is overridden by Associations::CollectionProxy
10
+ def spawn #:nodoc:
11
+ @delegate_to_klass ? klass.all : clone
12
+ end
13
+
14
+ # Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation.
15
+ # Returns an array representing the intersection of the resulting records with <tt>other</tt>, if <tt>other</tt> is an array.
16
+ #
17
+ # Post.where(published: true).joins(:comments).merge( Comment.where(spam: false) )
18
+ # # Performs a single join query with both where conditions.
19
+ #
20
+ # recent_posts = Post.order('created_at DESC').first(5)
21
+ # Post.where(published: true).merge(recent_posts)
22
+ # # Returns the intersection of all published posts with the 5 most recently created posts.
23
+ # # (This is just an example. You'd probably want to do this with a single query!)
24
+ #
25
+ # Procs will be evaluated by merge:
26
+ #
27
+ # Post.where(published: true).merge(-> { joins(:comments) })
28
+ # # => Post.where(published: true).joins(:comments)
29
+ #
30
+ # This is mainly intended for sharing common conditions between multiple associations.
31
+ def merge(other)
32
+ if other.is_a?(Array)
33
+ records & other
34
+ elsif other
35
+ spawn.merge!(other)
36
+ else
37
+ raise ArgumentError, "invalid argument: #{other.inspect}."
38
+ end
39
+ end
40
+
41
+ def merge!(other) # :nodoc:
42
+ if other.is_a?(Hash)
43
+ Relation::HashMerger.new(self, other).merge
44
+ elsif other.is_a?(Relation)
45
+ Relation::Merger.new(self, other).merge
46
+ elsif other.respond_to?(:to_proc)
47
+ instance_exec(&other)
48
+ else
49
+ raise ArgumentError, "#{other.inspect} is not an ActiveRecord::Relation"
50
+ end
51
+ end
52
+
53
+ # Removes from the query the condition(s) specified in +skips+.
54
+ #
55
+ # Post.order('id asc').except(:order) # discards the order condition
56
+ # Post.where('id > 10').order('id asc').except(:where) # discards the where condition but keeps the order
57
+ def except(*skips)
58
+ relation_with values.except(*skips)
59
+ end
60
+
61
+ # Removes any condition from the query other than the one(s) specified in +onlies+.
62
+ #
63
+ # Post.order('id asc').only(:where) # discards the order condition
64
+ # Post.order('id asc').only(:where, :order) # uses the specified order
65
+ def only(*onlies)
66
+ relation_with values.slice(*onlies)
67
+ end
68
+
69
+ private
70
+
71
+ def relation_with(values)
72
+ result = Relation.create(klass, values: values)
73
+ result.extend(*extending_values) if extending_values.any?
74
+ result
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class Relation
5
+ class WhereClause # :nodoc:
6
+ delegate :any?, :empty?, to: :predicates
7
+
8
+ def initialize(predicates)
9
+ @predicates = predicates
10
+ end
11
+
12
+ def +(other)
13
+ WhereClause.new(
14
+ predicates + other.predicates,
15
+ )
16
+ end
17
+
18
+ def -(other)
19
+ WhereClause.new(
20
+ predicates - other.predicates,
21
+ )
22
+ end
23
+
24
+ def merge(other)
25
+ WhereClause.new(
26
+ predicates_unreferenced_by(other) + other.predicates,
27
+ )
28
+ end
29
+
30
+ def except(*columns)
31
+ WhereClause.new(except_predicates(columns))
32
+ end
33
+
34
+ def or(other)
35
+ left = self - other
36
+ common = self - left
37
+ right = other - common
38
+
39
+ if left.empty? || right.empty?
40
+ common
41
+ else
42
+ or_clause = WhereClause.new(
43
+ [left.ast.or(right.ast)],
44
+ )
45
+ common + or_clause
46
+ end
47
+ end
48
+
49
+ def to_h(table_name = nil)
50
+ equalities = equalities(predicates)
51
+ if table_name
52
+ equalities = equalities.select do |node|
53
+ node.left.relation.name == table_name
54
+ end
55
+ end
56
+
57
+ equalities.map { |node|
58
+ name = node.left.name.to_s
59
+ value = extract_node_value(node.right)
60
+ [name, value]
61
+ }.to_h
62
+ end
63
+
64
+ def ast
65
+ Arel::Nodes::And.new(predicates_with_wrapped_sql_literals)
66
+ end
67
+
68
+ def ==(other)
69
+ other.is_a?(WhereClause) &&
70
+ predicates == other.predicates
71
+ end
72
+
73
+ def invert
74
+ WhereClause.new(inverted_predicates)
75
+ end
76
+
77
+ def self.empty
78
+ @empty ||= new([])
79
+ end
80
+
81
+ protected
82
+
83
+ attr_reader :predicates
84
+
85
+ def referenced_columns
86
+ @referenced_columns ||= begin
87
+ equality_nodes = predicates.select { |n| equality_node?(n) }
88
+ Set.new(equality_nodes, &:left)
89
+ end
90
+ end
91
+
92
+ private
93
+ def equalities(predicates)
94
+ equalities = []
95
+
96
+ predicates.each do |node|
97
+ case node
98
+ when Arel::Nodes::Equality
99
+ equalities << node
100
+ when Arel::Nodes::And
101
+ equalities.concat equalities(node.children)
102
+ end
103
+ end
104
+
105
+ equalities
106
+ end
107
+
108
+ def predicates_unreferenced_by(other)
109
+ predicates.reject do |n|
110
+ equality_node?(n) && other.referenced_columns.include?(n.left)
111
+ end
112
+ end
113
+
114
+ def equality_node?(node)
115
+ node.respond_to?(:operator) && node.operator == :==
116
+ end
117
+
118
+ def inverted_predicates
119
+ predicates.map { |node| invert_predicate(node) }
120
+ end
121
+
122
+ def invert_predicate(node)
123
+ case node
124
+ when NilClass
125
+ raise ArgumentError, "Invalid argument for .where.not(), got nil."
126
+ when Arel::Nodes::In
127
+ Arel::Nodes::NotIn.new(node.left, node.right)
128
+ when Arel::Nodes::Equality
129
+ Arel::Nodes::NotEqual.new(node.left, node.right)
130
+ when String
131
+ Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(node))
132
+ else
133
+ Arel::Nodes::Not.new(node)
134
+ end
135
+ end
136
+
137
+ def except_predicates(columns)
138
+ predicates.reject do |node|
139
+ case node
140
+ when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
141
+ subrelation = (node.left.kind_of?(Arel::Attributes::Attribute) ? node.left : node.right)
142
+ columns.include?(subrelation.name.to_s)
143
+ end
144
+ end
145
+ end
146
+
147
+ def predicates_with_wrapped_sql_literals
148
+ non_empty_predicates.map do |node|
149
+ case node
150
+ when Arel::Nodes::SqlLiteral, ::String
151
+ wrap_sql_literal(node)
152
+ else node
153
+ end
154
+ end
155
+ end
156
+
157
+ ARRAY_WITH_EMPTY_STRING = [""]
158
+ def non_empty_predicates
159
+ predicates - ARRAY_WITH_EMPTY_STRING
160
+ end
161
+
162
+ def wrap_sql_literal(node)
163
+ if ::String === node
164
+ node = Arel.sql(node)
165
+ end
166
+ Arel::Nodes::Grouping.new(node)
167
+ end
168
+
169
+ def extract_node_value(node)
170
+ case node
171
+ when Array
172
+ node.map { |v| extract_node_value(v) }
173
+ when Arel::Nodes::Casted, Arel::Nodes::Quoted
174
+ node.val
175
+ when Arel::Nodes::BindParam
176
+ value = node.value
177
+ if value.respond_to?(:value_before_type_cast)
178
+ value.value_before_type_cast
179
+ else
180
+ value
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class Relation
5
+ class WhereClauseFactory # :nodoc:
6
+ def initialize(klass, predicate_builder)
7
+ @klass = klass
8
+ @predicate_builder = predicate_builder
9
+ end
10
+
11
+ def build(opts, other)
12
+ case opts
13
+ when String, Array
14
+ parts = [klass.sanitize_sql(other.empty? ? opts : ([opts] + other))]
15
+ when Hash
16
+ attributes = predicate_builder.resolve_column_aliases(opts)
17
+ attributes.stringify_keys!
18
+
19
+ parts = predicate_builder.build_from_hash(attributes)
20
+ when Arel::Nodes::Node
21
+ parts = [opts]
22
+ else
23
+ raise ArgumentError, "Unsupported argument type: #{opts} (#{opts.class})"
24
+ end
25
+
26
+ WhereClause.new(parts)
27
+ end
28
+
29
+ protected
30
+
31
+ attr_reader :klass, :predicate_builder
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ ###
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
+ # ]
22
+ #
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
34
+ class Result
35
+ include Enumerable
36
+
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
45
+
46
+ # Returns the number of elements in the rows array.
47
+ def length
48
+ @rows.length
49
+ end
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.
55
+ def each
56
+ if block_given?
57
+ hash_rows.each { |row| yield row }
58
+ else
59
+ hash_rows.to_enum { @rows.size }
60
+ end
61
+ end
62
+
63
+ # Returns an array of hashes representing each row record.
64
+ def to_hash
65
+ hash_rows
66
+ end
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
+
115
+ private
116
+
117
+ def column_type(name, type_overrides = {})
118
+ type_overrides.fetch(name) do
119
+ column_types.fetch(name, Type.default_value)
120
+ 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
148
+ end
149
+ end