Skip to content

Commit d4ff6fc

Browse files
committed
OverlappingFieldsCanBeMergedRule: Fix performance degradation
Replicates graphql/graphql-js@8f4c64e
1 parent cb6ba11 commit d4ff6fc

File tree

2 files changed

+56
-11
lines changed

2 files changed

+56
-11
lines changed

src/graphql/validation/rules/overlapping_fields_can_be_merged.py

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33

44
from ...error import GraphQLError
55
from ...language import (
6+
DirectiveNode,
67
FieldNode,
78
FragmentDefinitionNode,
89
FragmentSpreadNode,
910
InlineFragmentNode,
10-
ObjectFieldNode,
11-
ObjectValueNode,
1211
SelectionSetNode,
12+
ValueNode,
1313
print_ast,
1414
)
1515
from ...type import (
@@ -551,7 +551,7 @@ def find_conflict(
551551
)
552552

553553
# Two field calls must have the same arguments.
554-
if stringify_arguments(node1) != stringify_arguments(node2):
554+
if not same_arguments(node1, node2):
555555
return (response_name, "they have differing arguments"), [node1], [node2]
556556

557557
if type1 and type2 and do_types_conflict(type1, type2):
@@ -582,14 +582,34 @@ def find_conflict(
582582
return None # no conflict
583583

584584

585-
def stringify_arguments(field_node: FieldNode) -> str:
586-
input_object_with_args = ObjectValueNode(
587-
fields=tuple(
588-
ObjectFieldNode(name=arg_node.name, value=arg_node.value)
589-
for arg_node in field_node.arguments
590-
)
591-
)
592-
return print_ast(sort_value_node(input_object_with_args))
585+
def same_arguments(
586+
node1: Union[FieldNode, DirectiveNode], node2: Union[FieldNode, DirectiveNode]
587+
) -> bool:
588+
args1 = node1.arguments
589+
args2 = node2.arguments
590+
591+
if not args1:
592+
return not args2
593+
594+
if not args2:
595+
return False
596+
597+
if len(args1) != len(args2):
598+
return False # pragma: no cover
599+
600+
values2 = {arg.name.value: arg.value for arg in args2}
601+
602+
for arg1 in args1:
603+
value1 = arg1.value
604+
value2 = values2.get(arg1.name.value)
605+
if value2 is None or stringify_value(value1) != stringify_value(value2):
606+
return False
607+
608+
return True
609+
610+
611+
def stringify_value(value: ValueNode) -> str:
612+
return print_ast(sort_value_node(value))
593613

594614

595615
def do_types_conflict(type1: GraphQLOutputType, type2: GraphQLOutputType) -> bool:
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from graphql import (
2+
GraphQLField,
3+
GraphQLObjectType,
4+
GraphQLSchema,
5+
GraphQLString,
6+
graphql_sync,
7+
)
8+
9+
schema = GraphQLSchema(
10+
query=GraphQLObjectType(
11+
name="Query",
12+
fields={
13+
"hello": GraphQLField(
14+
GraphQLString,
15+
resolve=lambda _obj, _info: "world",
16+
)
17+
},
18+
)
19+
)
20+
source = f"{{ {'hello ' * 250}}}"
21+
22+
23+
def test_many_repeated_fields(benchmark):
24+
result = benchmark(lambda: graphql_sync(schema, source))
25+
assert result == ({"hello": "world"}, None)

0 commit comments

Comments
 (0)