@@ -111,14 +111,16 @@ pub async fn introspect(
111
111
. instrument ( info_span ! ( "Run introspection query" ) )
112
112
. await ?;
113
113
114
- let ( tables, aggregate_functions, comparison_operators) = async {
114
+ let ( tables, aggregate_functions, comparison_operators, composite_types ) = async {
115
115
let tables: metadata:: TablesInfo = serde_json:: from_value ( row. get ( 0 ) ) ?;
116
116
117
117
let aggregate_functions: metadata:: AggregateFunctions = serde_json:: from_value ( row. get ( 1 ) ) ?;
118
118
119
119
let metadata:: ComparisonOperators ( mut comparison_operators) : metadata:: ComparisonOperators =
120
120
serde_json:: from_value ( row. get ( 2 ) ) ?;
121
121
122
+ let composite_types: metadata:: CompositeTypes = serde_json:: from_value ( row. get ( 3 ) ) ?;
123
+
122
124
// We need to include `in` as a comparison operator in the schema, and since it is syntax, it is not introspectable.
123
125
// Instead, we will check if the scalar type defines an equals operator and if yes, we will insert the `_in` operator
124
126
// as well.
@@ -146,17 +148,24 @@ pub async fn introspect(
146
148
tables,
147
149
aggregate_functions,
148
150
metadata:: ComparisonOperators ( comparison_operators) ,
151
+ composite_types,
149
152
) )
150
153
}
151
154
. instrument ( info_span ! ( "Decode introspection result" ) )
152
155
. await ?;
153
156
154
- let scalar_types = occurring_scalar_types (
155
- & tables,
156
- & args. metadata . native_queries ,
157
- & args. metadata . aggregate_functions ,
157
+ let ( scalar_types, composite_types) = transitively_occurring_types (
158
+ occurring_scalar_types (
159
+ & tables,
160
+ & args. metadata . native_queries ,
161
+ & args. metadata . aggregate_functions ,
162
+ ) ,
163
+ occurring_composite_types ( & tables, & args. metadata . native_queries ) ,
164
+ composite_types,
158
165
) ;
159
166
167
+ // We filter our comparison operators and aggregate functions to only include those relevant to
168
+ // types that may actually occur in the schema.
160
169
let relevant_comparison_operators =
161
170
filter_comparison_operators ( & scalar_types, comparison_operators) ;
162
171
let relevant_aggregate_functions =
@@ -170,14 +179,103 @@ pub async fn introspect(
170
179
native_queries : args. metadata . native_queries ,
171
180
aggregate_functions : relevant_aggregate_functions,
172
181
comparison_operators : relevant_comparison_operators,
173
- composite_types : args . metadata . composite_types ,
182
+ composite_types,
174
183
} ,
175
184
introspection_options : args. introspection_options ,
176
185
mutations_version : args. mutations_version ,
177
186
} )
178
187
}
179
188
180
- /// Collect all the types that can occur in the metadata. This is a bit circumstantial. A better
189
+ /// Collect all the composite types that can occur in the metadata.
190
+ pub fn occurring_composite_types (
191
+ tables : & metadata:: TablesInfo ,
192
+ native_queries : & metadata:: NativeQueries ,
193
+ ) -> BTreeSet < String > {
194
+ let tables_column_types = tables
195
+ . 0
196
+ . values ( )
197
+ . flat_map ( |v| v. columns . values ( ) . map ( |c| & c. r#type ) ) ;
198
+ let native_queries_column_types = native_queries
199
+ . 0
200
+ . values ( )
201
+ . flat_map ( |v| v. columns . values ( ) . map ( |c| & c. r#type ) ) ;
202
+ let native_queries_arguments_types = native_queries
203
+ . 0
204
+ . values ( )
205
+ . flat_map ( |v| v. arguments . values ( ) . map ( |c| & c. r#type ) ) ;
206
+
207
+ tables_column_types
208
+ . chain ( native_queries_column_types)
209
+ . chain ( native_queries_arguments_types)
210
+ . filter_map ( |t| match t {
211
+ metadata:: Type :: CompositeType ( ref t) => Some ( t. clone ( ) ) ,
212
+ metadata:: Type :: ArrayType ( t) => match * * t {
213
+ metadata:: Type :: CompositeType ( ref t) => Some ( t. clone ( ) ) ,
214
+ metadata:: Type :: ArrayType ( _) | metadata:: Type :: ScalarType ( _) => None ,
215
+ } ,
216
+ metadata:: Type :: ScalarType ( _) => None ,
217
+ } )
218
+ . collect :: < BTreeSet < String > > ( )
219
+ }
220
+
221
+ // Since array types and composite types may refer to other types we have to transitively discover
222
+ // the full set of types that are relevant to the schema.
223
+ pub fn transitively_occurring_types (
224
+ mut occurring_scalar_types : BTreeSet < metadata:: ScalarType > ,
225
+ occurring_type_names : BTreeSet < String > ,
226
+ mut composite_types : metadata:: CompositeTypes ,
227
+ ) -> ( BTreeSet < metadata:: ScalarType > , metadata:: CompositeTypes ) {
228
+ let mut discovered_type_names = occurring_type_names. clone ( ) ;
229
+
230
+ for t in & occurring_type_names {
231
+ match composite_types. 0 . get ( t) {
232
+ None => ( ) ,
233
+ Some ( ct) => {
234
+ for f in ct. fields . values ( ) {
235
+ match & f. r#type {
236
+ metadata:: Type :: CompositeType ( ct2) => {
237
+ discovered_type_names. insert ( ct2. to_string ( ) ) ;
238
+ }
239
+ metadata:: Type :: ScalarType ( t) => {
240
+ occurring_scalar_types. insert ( t. clone ( ) ) ;
241
+ }
242
+ metadata:: Type :: ArrayType ( arr_ty) => match * * arr_ty {
243
+ metadata:: Type :: CompositeType ( ref ct2) => {
244
+ discovered_type_names. insert ( ct2. to_string ( ) ) ;
245
+ }
246
+ metadata:: Type :: ScalarType ( ref t) => {
247
+ occurring_scalar_types. insert ( t. clone ( ) ) ;
248
+ }
249
+ metadata:: Type :: ArrayType ( _) => {
250
+ // This case is impossible, because we do not support nested arrays
251
+ }
252
+ } ,
253
+ }
254
+ }
255
+ }
256
+ }
257
+ }
258
+
259
+ // Since 'discovered_type_names' only grows monotonically starting from 'occurring_type_names'
260
+ // we just have to compare the number of elements to know if new types have been discovered.
261
+ if discovered_type_names. len ( ) == occurring_type_names. len ( ) {
262
+ // Iterating over occurring types discovered no new types
263
+ composite_types
264
+ . 0
265
+ . retain ( |t, _| occurring_type_names. contains ( t) ) ;
266
+ ( occurring_scalar_types, composite_types)
267
+ } else {
268
+ // Iterating over occurring types did discover new types,
269
+ // so we keep on going.
270
+ transitively_occurring_types (
271
+ occurring_scalar_types,
272
+ discovered_type_names,
273
+ composite_types,
274
+ )
275
+ }
276
+ }
277
+
278
+ /// Collect all the scalar types that can occur in the metadata. This is a bit circumstantial. A better
181
279
/// approach is likely to record scalar type names directly in the metadata via version2.sql.
182
280
pub fn occurring_scalar_types (
183
281
tables : & metadata:: TablesInfo ,
0 commit comments