From e5c9ec05e2fd13bf3b762a91dbf3f28e19682aa8 Mon Sep 17 00:00:00 2001 From: ydah Date: Thu, 16 Jul 2026 01:24:25 +0900 Subject: [PATCH 1/2] Avoid duplicate selection step enqueueing --- lib/graphql/execution/selections_step.rb | 2 +- .../graphql/execution/selections_step_spec.rb | 52 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 spec/graphql/execution/selections_step_spec.rb diff --git a/lib/graphql/execution/selections_step.rb b/lib/graphql/execution/selections_step.rb index 4999593818..7408894b32 100644 --- a/lib/graphql/execution/selections_step.rb +++ b/lib/graphql/execution/selections_step.rb @@ -30,10 +30,10 @@ def graphql_objects def call @all_selections = [{}, (prototype_result = {})] @runner.gather_selections(@parent_type, @selections, self, self.query, @all_selections, @all_selections[1], into: @all_selections[0]) - continue_selections = [] i = 0 l = @all_selections.length while i < l + continue_selections = [] grouped_selections = @all_selections[i] selections_prototype_result = @all_selections[i + 1] if (directives_owner = grouped_selections.delete(:__node)) diff --git a/spec/graphql/execution/selections_step_spec.rb b/spec/graphql/execution/selections_step_spec.rb new file mode 100644 index 0000000000..7174b789e9 --- /dev/null +++ b/spec/graphql/execution/selections_step_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true +require "spec_helper" + +describe GraphQL::Execution::SelectionsStep do + class SelectionsStepRunner + attr_reader :steps + + def initialize(groups) + @groups = groups + @steps = [] + end + + def gather_selections(_parent_type, _selections, _step, _query, all_selections, _prototype_result, into:) + all_selections.replace(@groups) + end + + def runtime_directives + GraphQL::EmptyObjects::EMPTY_HASH + end + + def add_step(step) + @steps << step + end + end + + it "enqueues each field step once across selection groups" do + first_step = Object.new + second_step = Object.new + inline_fragment = GraphQL.parse("{ ... @include(if: true) { __typename } }").definitions.first.selections.first + runner = SelectionsStepRunner.new([ + { "first" => first_step }, + { "first" => nil }, + { __node: inline_fragment, "second" => second_step }, + { "second" => nil }, + ]) + step = GraphQL::Execution::SelectionsStep.new( + parent_type: nil, + field_resolve_step: nil, + selections: [], + objects: [], + results: [{}], + runner: runner, + query: Object.new, + path: [], + clobber: false, + ) + + step.call + + assert_equal [first_step, second_step], runner.steps + end +end From a824a8a5541e556af3a03a96a54d50de360b9242 Mon Sep 17 00:00:00 2001 From: ydah Date: Wed, 22 Jul 2026 09:53:32 +0900 Subject: [PATCH 2/2] Defer selection step enqueueing --- lib/graphql/execution/selections_step.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/graphql/execution/selections_step.rb b/lib/graphql/execution/selections_step.rb index 7408894b32..11f4a438fd 100644 --- a/lib/graphql/execution/selections_step.rb +++ b/lib/graphql/execution/selections_step.rb @@ -30,10 +30,10 @@ def graphql_objects def call @all_selections = [{}, (prototype_result = {})] @runner.gather_selections(@parent_type, @selections, self, self.query, @all_selections, @all_selections[1], into: @all_selections[0]) + continue_selections = [] i = 0 l = @all_selections.length while i < l - continue_selections = [] grouped_selections = @all_selections[i] selections_prototype_result = @all_selections[i + 1] if (directives_owner = grouped_selections.delete(:__node)) @@ -83,12 +83,12 @@ def call end end - continue_selections.each do |frs| - @runner.add_step(frs) - end - i += 2 end + + continue_selections.each do |frs| + @runner.add_step(frs) + end end end end