Skip to content

Commit f28cd14

Browse files
authored
Merge pull request #497 from supabase/merge_fields_fix
Fix slow introspection queries
2 parents df4ac14 + 90e3509 commit f28cd14

10 files changed

+119
-639
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "pg_graphql"
3-
version = "1.5.0"
3+
version = "1.5.1-mergeless"
44
edition = "2021"
55

66
[lib]

src/builder.rs

Lines changed: 75 additions & 96 deletions
Large diffs are not rendered by default.

src/parser_util.rs

Lines changed: 31 additions & 222 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use crate::graphql::*;
1+
use crate::graphql::{EnumSource, __InputValue, __Type, ___Type};
22
use crate::gson;
33
use graphql_parser::query::*;
4-
use graphql_parser::Pos;
54
use std::collections::HashMap;
65

76
pub fn alias_or_name<'a, T>(query_field: &graphql_parser::query::Field<'a, T>) -> String
@@ -15,221 +14,34 @@ where
1514
.unwrap_or_else(|| query_field.name.as_ref().to_string())
1615
}
1716

18-
pub fn merge_fields<'a, T, I>(
19-
target_fields: &mut Vec<Field<'a, T>>,
20-
next_fields: I,
21-
type_name: &str,
22-
field_map: &HashMap<String, __Field>,
23-
) -> Result<(), String>
24-
where
25-
T: Text<'a> + Eq + AsRef<str> + std::fmt::Debug + Clone,
26-
I: IntoIterator<Item = Field<'a, T>>,
27-
{
28-
for field in next_fields {
29-
merge_field(target_fields, field, type_name, field_map)?
30-
}
31-
Ok(())
32-
}
33-
34-
pub fn merge_field<'a, T>(
35-
target_fields: &mut Vec<Field<'a, T>>,
36-
mut field: Field<'a, T>,
37-
type_name: &str,
38-
field_map: &HashMap<String, __Field>,
39-
) -> Result<(), String>
40-
where
41-
T: Text<'a> + Eq + AsRef<str> + std::fmt::Debug + Clone,
42-
{
43-
let Some((matching_idx, matching_field)) = target_fields
44-
.iter()
45-
.enumerate()
46-
.find(|(_, target)| alias_or_name(target) == alias_or_name(&field))
47-
else {
48-
target_fields.push(field);
49-
return Ok(());
50-
};
51-
52-
can_fields_merge(matching_field, &field, type_name, field_map)?;
53-
54-
field.position = field.position.min(matching_field.position);
55-
56-
field.selection_set.span =
57-
min_encapsulating_span(field.selection_set.span, matching_field.selection_set.span);
58-
59-
// Subfields will be normalized and properly merged on a later pass.
60-
field
61-
.selection_set
62-
.items
63-
.extend(matching_field.selection_set.items.clone());
64-
65-
target_fields[matching_idx] = field;
66-
67-
Ok(())
68-
}
69-
70-
pub fn can_fields_merge<'a, T>(
71-
field_a: &Field<'a, T>,
72-
field_b: &Field<'a, T>,
73-
type_name: &str,
74-
field_map: &HashMap<String, __Field>,
75-
) -> Result<(), String>
76-
where
77-
T: Text<'a> + Eq + AsRef<str> + std::fmt::Debug + Clone,
78-
{
79-
let Some(_field_a) = field_map.get(field_a.name.as_ref()) else {
80-
return Err(format!(
81-
"Unknown field '{}' on type '{}'",
82-
field_a.name.as_ref(),
83-
type_name
84-
));
85-
};
86-
let Some(_field_b) = field_map.get(field_b.name.as_ref()) else {
87-
return Err(format!(
88-
"Unknown field '{}' on type '{}'",
89-
field_b.name.as_ref(),
90-
type_name
91-
));
92-
};
93-
94-
has_same_type_shape(
95-
&alias_or_name(field_a),
96-
type_name,
97-
&_field_a.type_,
98-
&_field_b.type_,
99-
)?;
100-
101-
if field_a.name != field_b.name {
102-
return Err(format!(
103-
"Fields '{}' on type '{}' conflict because '{}' and '{}' are different fields",
104-
alias_or_name(field_a),
105-
type_name,
106-
field_a.name.as_ref(),
107-
field_b.name.as_ref(),
108-
));
109-
}
110-
111-
for (arg_a_name, arg_a_value) in field_a.arguments.iter() {
112-
let arg_b_value = field_b.arguments.iter().find_map(|(name, value)| {
113-
if name == arg_a_name {
114-
Some(value)
115-
} else {
116-
None
117-
}
118-
});
119-
let args_match = match arg_b_value {
120-
None => false,
121-
Some(arg_b_value) => arg_b_value == arg_a_value,
122-
};
123-
if !args_match {
124-
return Err(format!(
125-
"Fields '{}' on type '{}' conflict because they have differing arguments",
126-
alias_or_name(field_a),
127-
type_name,
128-
));
129-
}
130-
}
131-
132-
Ok(())
133-
}
134-
135-
pub fn has_same_type_shape(
136-
field_name: &str,
137-
type_name: &str,
138-
type_a: &__Type,
139-
type_b: &__Type,
140-
) -> Result<(), String> {
141-
let mut type_a = type_a;
142-
let mut type_b = type_b;
143-
144-
if matches!(type_a, __Type::NonNull(_)) || matches!(type_b, __Type::NonNull(_)) {
145-
if let (__Type::NonNull(nullable_type_a), __Type::NonNull(nullable_type_b)) =
146-
(type_a, type_b)
147-
{
148-
type_a = nullable_type_a.type_.as_ref();
149-
type_b = nullable_type_b.type_.as_ref();
150-
} else {
151-
return Err(format!(
152-
"Fields '{}' on type '{}' conflict because only one is non nullable",
153-
field_name, type_name,
154-
));
155-
}
156-
}
157-
158-
if matches!(type_a, __Type::List(_)) || matches!(type_b, __Type::List(_)) {
159-
if let (__Type::List(list_type_a), __Type::List(list_type_b)) = (type_a, type_b) {
160-
type_a = list_type_a.type_.as_ref();
161-
type_b = list_type_b.type_.as_ref();
162-
} else {
163-
return Err(format!(
164-
"Fields '{}' on type '{}' conflict because only one is a list type",
165-
field_name, type_name,
166-
));
167-
}
168-
169-
return has_same_type_shape(field_name, type_name, type_a, type_b);
170-
}
171-
172-
if matches!(type_a, __Type::Enum(_))
173-
|| matches!(type_b, __Type::Enum(_))
174-
|| matches!(type_a, __Type::Scalar(_))
175-
|| matches!(type_b, __Type::Scalar(_))
176-
{
177-
return if type_a == type_b {
178-
Ok(())
179-
} else {
180-
Err(format!(
181-
"Fields '{}' on type '{}' conflict due to mismatched types",
182-
field_name, type_name,
183-
))
184-
};
185-
}
186-
187-
// TODO handle composite types?
188-
189-
// Subfield type shapes will be checked on a later pass.
190-
Ok(())
191-
}
192-
193-
pub fn min_encapsulating_span(a: (Pos, Pos), b: (Pos, Pos)) -> (Pos, Pos) {
194-
(a.0.min(b.0), a.1.max(b.1))
195-
}
196-
197-
pub fn normalize_selection_set<'a, T>(
198-
selection_set: &SelectionSet<'a, T>,
199-
fragment_definitions: &Vec<FragmentDefinition<'a, T>>,
17+
pub fn normalize_selection_set<'a, 'b, T>(
18+
selection_set: &'b SelectionSet<'a, T>,
19+
fragment_definitions: &'b Vec<FragmentDefinition<'a, T>>,
20020
type_name: &String, // for inline fragments
20121
variables: &serde_json::Value, // for directives
202-
field_type: &__Type,
203-
) -> Result<Vec<Field<'a, T>>, String>
22+
) -> Result<Vec<&'b Field<'a, T>>, String>
20423
where
205-
T: Text<'a> + Eq + AsRef<str> + std::fmt::Debug + Clone,
24+
T: Text<'a> + Eq + AsRef<str>,
20625
{
207-
let mut normalized_fields: Vec<Field<'a, T>> = vec![];
208-
209-
let field_map = field_map(&field_type.unmodified_type());
26+
let mut selections: Vec<&'b Field<'a, T>> = vec![];
21027

21128
for selection in &selection_set.items {
212-
match normalize_selection(
213-
selection,
214-
fragment_definitions,
215-
type_name,
216-
variables,
217-
field_type,
218-
) {
219-
Ok(fields) => merge_fields(&mut normalized_fields, fields, type_name, &field_map)?,
29+
let sel = selection;
30+
match normalize_selection(sel, fragment_definitions, type_name, variables) {
31+
Ok(sels) => selections.extend(sels),
22032
Err(err) => return Err(err),
22133
}
22234
}
223-
Ok(normalized_fields)
35+
Ok(selections)
22436
}
22537

22638
/// Combines @skip and @include
227-
pub fn selection_is_skipped<'a, T>(
228-
query_selection: &Selection<'a, T>,
39+
pub fn selection_is_skipped<'a, 'b, T>(
40+
query_selection: &'b Selection<'a, T>,
22941
variables: &serde_json::Value,
23042
) -> Result<bool, String>
23143
where
232-
T: Text<'a> + Eq + AsRef<str> + std::fmt::Debug,
44+
T: Text<'a> + Eq + AsRef<str>,
23345
{
23446
let directives = match query_selection {
23547
Selection::Field(x) => &x.directives,
@@ -318,27 +130,24 @@ where
318130
}
319131

320132
/// Normalizes literal selections, fragment spreads, and inline fragments
321-
pub fn normalize_selection<'a, T>(
322-
query_selection: &Selection<'a, T>,
323-
fragment_definitions: &Vec<FragmentDefinition<'a, T>>,
133+
pub fn normalize_selection<'a, 'b, T>(
134+
query_selection: &'b Selection<'a, T>,
135+
fragment_definitions: &'b Vec<FragmentDefinition<'a, T>>,
324136
type_name: &String, // for inline fragments
325137
variables: &serde_json::Value, // for directives
326-
field_type: &__Type, // for field merging shape check
327-
) -> Result<Vec<Field<'a, T>>, String>
138+
) -> Result<Vec<&'b Field<'a, T>>, String>
328139
where
329-
T: Text<'a> + Eq + AsRef<str> + std::fmt::Debug + Clone,
140+
T: Text<'a> + Eq + AsRef<str>,
330141
{
331-
let mut normalized_fields: Vec<Field<'a, T>> = vec![];
142+
let mut selections: Vec<&Field<'a, T>> = vec![];
332143

333144
if selection_is_skipped(query_selection, variables)? {
334-
return Ok(normalized_fields);
145+
return Ok(selections);
335146
}
336147

337-
let field_map = field_map(&field_type.unmodified_type());
338-
339148
match query_selection {
340149
Selection::Field(field) => {
341-
merge_field(&mut normalized_fields, field.clone(), type_name, &field_map)?;
150+
selections.push(field);
342151
}
343152
Selection::FragmentSpread(fragment_spread) => {
344153
let frag_name = &fragment_spread.fragment_name;
@@ -364,15 +173,14 @@ where
364173
};
365174

366175
// TODO handle directives?
367-
let frag_fields = normalize_selection_set(
176+
let frag_selections = normalize_selection_set(
368177
&frag_def.selection_set,
369178
fragment_definitions,
370179
type_name,
371180
variables,
372-
field_type,
373181
);
374-
match frag_fields {
375-
Ok(fields) => merge_fields(&mut normalized_fields, fields, type_name, &field_map)?,
182+
match frag_selections {
183+
Ok(sels) => selections.extend(sels.iter()),
376184
Err(err) => return Err(err),
377185
};
378186
}
@@ -385,19 +193,18 @@ where
385193
};
386194

387195
if inline_fragment_applies {
388-
let infrag_fields = normalize_selection_set(
196+
let infrag_selections = normalize_selection_set(
389197
&inline_fragment.selection_set,
390198
fragment_definitions,
391199
type_name,
392200
variables,
393-
field_type,
394201
)?;
395-
merge_fields(&mut normalized_fields, infrag_fields, type_name, &field_map)?;
202+
selections.extend(infrag_selections.iter());
396203
}
397204
}
398205
}
399206

400-
Ok(normalized_fields)
207+
Ok(selections)
401208
}
402209

403210
pub fn to_gson<'a, T>(
@@ -406,7 +213,7 @@ pub fn to_gson<'a, T>(
406213
variable_definitions: &Vec<VariableDefinition<'a, T>>,
407214
) -> Result<gson::Value, String>
408215
where
409-
T: Text<'a> + AsRef<str> + std::fmt::Debug,
216+
T: Text<'a> + AsRef<str>,
410217
{
411218
let result = match graphql_value {
412219
Value::Null => gson::Value::Null,
@@ -466,6 +273,7 @@ where
466273
}
467274

468275
pub fn validate_arg_from_type(type_: &__Type, value: &gson::Value) -> Result<gson::Value, String> {
276+
use crate::graphql::Scalar;
469277
use crate::gson::Number as GsonNumber;
470278
use crate::gson::Value as GsonValue;
471279

@@ -676,6 +484,7 @@ pub fn validate_arg_from_input_object(
676484
input_type: &__Type,
677485
value: &gson::Value,
678486
) -> Result<gson::Value, String> {
487+
use crate::graphql::__TypeKind;
679488
use crate::gson::Value as GsonValue;
680489

681490
let input_type_name = input_type.name().unwrap_or_default();

0 commit comments

Comments
 (0)