diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-18 11:17:02 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-18 11:17:02 +0300 |
commit | b39512ed755239198a9c294b6a45e65c05900235 (patch) | |
tree | d234a3efade1de67c46b9e5a38ce813627726aa7 /lib/unnested_in_filters | |
parent | d31474cf3b17ece37939d20082b07f6657cc79a9 (diff) |
Add latest changes from gitlab-org/gitlab@15-3-stable-eev15.3.0-rc42
Diffstat (limited to 'lib/unnested_in_filters')
-rw-r--r-- | lib/unnested_in_filters/rewriter.rb | 47 |
1 files changed, 42 insertions, 5 deletions
diff --git a/lib/unnested_in_filters/rewriter.rb b/lib/unnested_in_filters/rewriter.rb index cba002a5632..bf7be177a0d 100644 --- a/lib/unnested_in_filters/rewriter.rb +++ b/lib/unnested_in_filters/rewriter.rb @@ -25,7 +25,7 @@ module UnnestedInFilters attr_reader :model, :attribute, :values delegate :connection, :columns, :attribute_types, to: :model, private: true - delegate :quote, :quote_table_name, :quote_column_name, to: :connection + delegate :quote, :quote_table_name, :quote_column_name, :visitor, to: :connection def table_name quote_table_name(attribute.pluralize) @@ -36,8 +36,17 @@ module UnnestedInFilters end def serialized_values - array_type.serialize(values) - .then { |array| quote(array) } + values.is_a?(Arel::Nodes::SelectStatement) ? "ARRAY(#{serialized_arel_value})" : serialized_array_values + end + + def serialized_arel_value + visitor.compile(values, unprepared_statement_collector) + end + + def serialized_array_values + values.map(&:value) + .then { array_type.serialize(_1) } + .then { |array| quote(array) } end def array_type @@ -51,6 +60,13 @@ module UnnestedInFilters def column columns.find { _1.name == attribute } end + + def unprepared_statement_collector + Arel::Collectors::SubstituteBinds.new( + connection, + Arel::Collectors::SQLString.new + ) + end end def initialize(relation) @@ -125,7 +141,7 @@ module UnnestedInFilters attr_reader :relation - delegate :model, :order_values, :limit_value, :where_values_hash, to: :relation, private: true + delegate :model, :order_values, :limit_value, :where_values_hash, :where_clause, to: :relation, private: true def log_rewrite ::Gitlab::AppLogger.info(message: 'Query is being rewritten by `UnnestedInFilters`', model: model.name) @@ -150,7 +166,28 @@ module UnnestedInFilters end def in_filters - @in_filters ||= where_values_hash.select { _2.is_a?(Array) } + @in_filters ||= arel_in_nodes.each_with_object({}) { |node, memo| memo[node.left.name] = node.right } + end + + def arel_in_nodes + where_clause_arel_nodes.select(&method(:in_predicate?)) + end + + # `ActiveRecord::WhereClause#ast` is returning a single node when there is only one + # predicate but returning an `Arel::Nodes::And` node if there are more than one predicates. + # This is why we are checking the returned object responds to `children` or not. + def where_clause_arel_nodes + return [where_clause_ast] unless where_clause_ast.respond_to?(:children) + + where_clause_ast.children + end + + def where_clause_ast + @where_clause_ast ||= where_clause.ast + end + + def in_predicate?(arel_node) + arel_node.is_a?(Arel::Nodes::HomogeneousIn) || arel_node.is_a?(Arel::Nodes::In) end def has_index_coverage? |