Skip to content

Commit 5f07ebe

Browse files
committed
fixed a bug in can_merge function
Also changed the comparison algorithm to O(n + m) from O(nm)
1 parent 8c0acce commit 5f07ebe

File tree

4 files changed

+90
-4
lines changed

4 files changed

+90
-4
lines changed

src/builder.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::sql_types::*;
55
use graphql_parser::query::*;
66
use serde::Serialize;
77
use std::collections::HashMap;
8+
use std::hash::Hash;
89
use std::ops::Deref;
910
use std::str::FromStr;
1011
use std::sync::Arc;
@@ -264,6 +265,7 @@ pub fn to_insert_builder<'a, T>(
264265
) -> Result<InsertBuilder, String>
265266
where
266267
T: Text<'a> + Eq + AsRef<str> + Clone,
268+
T::Value: Hash,
267269
{
268270
let type_ = field.type_().unmodified_type();
269271
let type_name = type_
@@ -429,6 +431,7 @@ pub fn to_update_builder<'a, T>(
429431
) -> Result<UpdateBuilder, String>
430432
where
431433
T: Text<'a> + Eq + AsRef<str> + Clone,
434+
T::Value: Hash,
432435
{
433436
let type_ = field.type_().unmodified_type();
434437
let type_name = type_
@@ -534,6 +537,7 @@ pub fn to_delete_builder<'a, T>(
534537
) -> Result<DeleteBuilder, String>
535538
where
536539
T: Text<'a> + Eq + AsRef<str> + Clone,
540+
T::Value: Hash,
537541
{
538542
let type_ = field.type_().unmodified_type();
539543
let type_name = type_
@@ -643,6 +647,7 @@ pub fn to_function_call_builder<'a, T>(
643647
) -> Result<FunctionCallBuilder, String>
644648
where
645649
T: Text<'a> + Eq + AsRef<str> + Clone,
650+
T::Value: Hash,
646651
{
647652
let type_ = field.type_().unmodified_type();
648653
let alias = alias_or_name(query_field);
@@ -1337,6 +1342,7 @@ pub fn to_connection_builder<'a, T>(
13371342
) -> Result<ConnectionBuilder, String>
13381343
where
13391344
T: Text<'a> + Eq + AsRef<str> + Clone,
1345+
T::Value: Hash,
13401346
{
13411347
let type_ = field.type_().unmodified_type();
13421348
let type_ = type_.return_type();
@@ -1510,6 +1516,7 @@ fn to_page_info_builder<'a, T>(
15101516
) -> Result<PageInfoBuilder, String>
15111517
where
15121518
T: Text<'a> + Eq + AsRef<str> + Clone,
1519+
T::Value: Hash,
15131520
{
15141521
let type_ = field.type_().unmodified_type();
15151522
let type_name = type_.name().ok_or(format!(
@@ -1572,6 +1579,7 @@ fn to_edge_builder<'a, T>(
15721579
) -> Result<EdgeBuilder, String>
15731580
where
15741581
T: Text<'a> + Eq + AsRef<str> + Clone,
1582+
T::Value: Hash,
15751583
{
15761584
let type_ = field.type_().unmodified_type();
15771585
let type_name = type_.name().ok_or(format!(
@@ -1639,6 +1647,7 @@ pub fn to_node_builder<'a, T>(
16391647
) -> Result<NodeBuilder, String>
16401648
where
16411649
T: Text<'a> + Eq + AsRef<str> + Clone,
1650+
T::Value: Hash,
16421651
{
16431652
let type_ = field.type_().unmodified_type();
16441653

@@ -1982,6 +1991,7 @@ impl __Schema {
19821991
) -> Result<__EnumValueBuilder, String>
19831992
where
19841993
T: Text<'a> + Eq + AsRef<str> + Clone,
1994+
T::Value: Hash,
19851995
{
19861996
let selection_fields = normalize_selection_set(
19871997
&query_field.selection_set,
@@ -2034,6 +2044,7 @@ impl __Schema {
20342044
) -> Result<__InputValueBuilder, String>
20352045
where
20362046
T: Text<'a> + Eq + AsRef<str> + Clone,
2047+
T::Value: Hash,
20372048
{
20382049
let selection_fields = normalize_selection_set(
20392050
&query_field.selection_set,
@@ -2099,6 +2110,7 @@ impl __Schema {
20992110
) -> Result<__FieldBuilder, String>
21002111
where
21012112
T: Text<'a> + Eq + AsRef<str> + Clone,
2113+
T::Value: Hash,
21022114
{
21032115
let selection_fields = normalize_selection_set(
21042116
&query_field.selection_set,
@@ -2175,6 +2187,7 @@ impl __Schema {
21752187
) -> Result<Option<__TypeBuilder>, String>
21762188
where
21772189
T: Text<'a> + Eq + AsRef<str> + Clone,
2190+
T::Value: Hash,
21782191
{
21792192
if field.type_.unmodified_type() != __Type::__Type(__TypeType {}) {
21802193
return Err("can not build query for non-__type type".to_string());
@@ -2227,6 +2240,7 @@ impl __Schema {
22272240
) -> Result<__TypeBuilder, String>
22282241
where
22292242
T: Text<'a> + Eq + AsRef<str> + Clone,
2243+
T::Value: Hash,
22302244
{
22312245
let field_map = field_map(&__Type::__Type(__TypeType {}));
22322246

@@ -2421,6 +2435,7 @@ impl __Schema {
24212435
) -> Result<__DirectiveBuilder, String>
24222436
where
24232437
T: Text<'a> + Eq + AsRef<str> + Clone,
2438+
T::Value: Hash,
24242439
{
24252440
let selection_fields = normalize_selection_set(
24262441
&query_field.selection_set,
@@ -2490,6 +2505,7 @@ impl __Schema {
24902505
) -> Result<__SchemaBuilder, String>
24912506
where
24922507
T: Text<'a> + Eq + AsRef<str> + Clone,
2508+
T::Value: Hash,
24932509
{
24942510
let type_ = field.type_.unmodified_type();
24952511
let type_name = type_

src/merge.rs

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1+
use std::{collections::HashMap, hash::Hash};
2+
13
use graphql_parser::query::{Field, Text, Value};
24
use indexmap::IndexMap;
35

46
use crate::parser_util::alias_or_name;
57

68
/// Merges duplicates in a vector of fields. The fields in the vector are added to a
79
/// map from field name to field. If a field with the same name already exists in the
8-
/// map, the existing and new fields' children are combined into the existing field's
10+
/// map, the existing and new field's children are combined into the existing field's
911
/// children. These children will be merged later when they are normalized.
1012
///
1113
/// The map is an `IndexMap` to ensure iteration order of the fields is preserved.
1214
/// This prevents tests from being flaky due to field order changing between test runs.
1315
pub fn merge<'a, 'b, T>(fields: Vec<Field<'a, T>>) -> Result<Vec<Field<'a, T>>, String>
1416
where
1517
T: Text<'a> + Eq + AsRef<str>,
18+
T::Value: Hash,
1619
{
1720
let mut merged: IndexMap<String, Field<'a, T>> = IndexMap::new();
1821

@@ -41,6 +44,7 @@ where
4144
fn can_merge<'a, T>(field_a: &Field<'a, T>, field_b: &Field<'a, T>) -> Result<bool, String>
4245
where
4346
T: Text<'a> + Eq + AsRef<str>,
47+
T::Value: Hash,
4448
{
4549
if field_a.name != field_b.name {
4650
return Err(format!(
@@ -59,24 +63,81 @@ where
5963
Ok(true)
6064
}
6165

66+
/// Compares two sets of arguments and returns true only if
67+
/// both are the same. `arguments_a` should not have two
68+
/// arguments with the same name. Similarly `arguments_b`
69+
/// should not have duplicates. It is assumed that [Argument
70+
/// Uniqueness] validation has already been run by the time
71+
/// this function is called.
72+
///
73+
/// [Argument Uniqueness]: https://spec.graphql.org/October2021/#sec-Argument-Uniqueness
6274
fn same_arguments<'a, 'b, T>(
6375
arguments_a: &[(T::Value, Value<'a, T>)],
6476
arguments_b: &[(T::Value, Value<'a, T>)],
6577
) -> bool
6678
where
6779
T: Text<'a> + Eq + AsRef<str>,
80+
T::Value: Hash,
6881
{
6982
if arguments_a.len() != arguments_b.len() {
7083
return false;
7184
}
7285

86+
let mut arguments_a_map = HashMap::new();
7387
for (arg_a_name, arg_a_val) in arguments_a {
74-
for (arg_b_name, arg_b_val) in arguments_b {
75-
if arg_a_name == arg_b_name && arg_a_val != arg_b_val {
76-
return false;
88+
arguments_a_map.insert(arg_a_name, arg_a_val);
89+
}
90+
91+
for (arg_b_name, arg_b_val) in arguments_b {
92+
match arguments_a_map.get(arg_b_name) {
93+
Some(arg_a_val) => {
94+
if *arg_a_val != arg_b_val {
95+
return false;
96+
}
7797
}
98+
None => return false,
7899
}
79100
}
80101

81102
true
82103
}
104+
105+
#[cfg(test)]
106+
mod tests {
107+
108+
use super::same_arguments;
109+
use graphql_parser::query::Value;
110+
111+
#[test]
112+
fn same_args_test() {
113+
let arguments_a = vec![("a", Value::Int(1.into()))];
114+
let arguments_b = vec![("a", Value::Int(1.into()))];
115+
let result = same_arguments::<&str>(&arguments_a, &arguments_b);
116+
assert!(result);
117+
118+
let arguments_a = vec![("a", Value::Int(1.into())), ("b", Value::Int(2.into()))];
119+
let arguments_b = vec![("a", Value::Int(1.into())), ("b", Value::Int(2.into()))];
120+
let result = same_arguments::<&str>(&arguments_a, &arguments_b);
121+
assert!(result);
122+
123+
let arguments_a = vec![("a", Value::Int(1.into())), ("b", Value::Int(2.into()))];
124+
let arguments_b = vec![("b", Value::Int(2.into())), ("a", Value::Int(1.into()))];
125+
let result = same_arguments::<&str>(&arguments_a, &arguments_b);
126+
assert!(result);
127+
128+
let arguments_a = vec![("a", Value::Int(1.into()))];
129+
let arguments_b = vec![("a", Value::Int(2.into()))];
130+
let result = same_arguments::<&str>(&arguments_a, &arguments_b);
131+
assert!(!result);
132+
133+
let arguments_a = vec![("a", Value::Int(1.into())), ("b", Value::Int(1.into()))];
134+
let arguments_b = vec![("a", Value::Int(1.into()))];
135+
let result = same_arguments::<&str>(&arguments_a, &arguments_b);
136+
assert!(!result);
137+
138+
let arguments_a = vec![("a", Value::Int(1.into()))];
139+
let arguments_b = vec![("b", Value::Int(1.into()))];
140+
let result = same_arguments::<&str>(&arguments_a, &arguments_b);
141+
assert!(!result);
142+
}
143+
}

src/parser_util.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::graphql::{EnumSource, __InputValue, __Type, ___Type};
22
use crate::{gson, merge::merge};
33
use graphql_parser::query::*;
44
use std::collections::HashMap;
5+
use std::hash::Hash;
56

67
pub fn alias_or_name<'a, T>(query_field: &graphql_parser::query::Field<'a, T>) -> String
78
where
@@ -22,6 +23,7 @@ pub fn normalize_selection_set<'a, 'b, T>(
2223
) -> Result<Vec<Field<'a, T>>, String>
2324
where
2425
T: Text<'a> + Eq + AsRef<str> + Clone,
26+
T::Value: Hash,
2527
{
2628
let mut selections: Vec<Field<'a, T>> = vec![];
2729

@@ -139,6 +141,7 @@ pub fn normalize_selection<'a, 'b, T>(
139141
) -> Result<Vec<Field<'a, T>>, String>
140142
where
141143
T: Text<'a> + Eq + AsRef<str> + Clone,
144+
T::Value: Hash,
142145
{
143146
let mut selections: Vec<Field<'a, T>> = vec![];
144147

src/resolve.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::collections::HashSet;
2+
use std::hash::Hash;
23

34
use crate::builder::*;
45
use crate::graphql::*;
@@ -23,6 +24,7 @@ pub fn resolve_inner<'a, T>(
2324
) -> GraphQLResponse
2425
where
2526
T: Text<'a> + Eq + AsRef<str> + Clone,
27+
T::Value: Hash,
2628
{
2729
match variables {
2830
serde_json::Value::Object(_) => (),
@@ -131,6 +133,7 @@ fn resolve_query<'a, 'b, T>(
131133
) -> GraphQLResponse
132134
where
133135
T: Text<'a> + Eq + AsRef<str> + Clone,
136+
T::Value: Hash,
134137
{
135138
let variable_definitions = &query.variable_definitions;
136139
resolve_selection_set(
@@ -151,6 +154,7 @@ fn resolve_selection_set<'a, 'b, T>(
151154
) -> GraphQLResponse
152155
where
153156
T: Text<'a> + Eq + AsRef<str> + Clone,
157+
T::Value: Hash,
154158
{
155159
use crate::graphql::*;
156160

@@ -338,6 +342,7 @@ fn resolve_mutation<'a, 'b, T>(
338342
) -> GraphQLResponse
339343
where
340344
T: Text<'a> + Eq + AsRef<str> + Clone,
345+
T::Value: Hash,
341346
{
342347
let variable_definitions = &query.variable_definitions;
343348
resolve_mutation_selection_set(
@@ -358,6 +363,7 @@ fn resolve_mutation_selection_set<'a, 'b, T>(
358363
) -> GraphQLResponse
359364
where
360365
T: Text<'a> + Eq + AsRef<str> + Clone,
366+
T::Value: Hash,
361367
{
362368
use crate::graphql::*;
363369

0 commit comments

Comments
 (0)