diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_gt.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_gt.graphql-parsed.ron new file mode 100644 index 00000000..f9e81768 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_gt.graphql-parsed.ron @@ -0,0 +1,88 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(30), + "min": Int64(30), + }, + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + coerced_to: Some("Composite"), + connections: [ + (FieldConnection( + position: Pos( + line: 5, + column: 13, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 5, + column: 13, + ), + name: "value", + output: [ + OutputDirective(), + ], + )), + (FieldConnection( + position: Pos( + line: 7, + column: 13, + ), + name: "primeFactor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + transform: TransformDirective( + kind: Count, + ), + filter: [ + FilterDirective( + operation: GreaterThan((), VariableRef("two")), + ), + FilterDirective( + operation: GreaterThan((), VariableRef("three")), + ), + ], + )), + )), + ), FieldNode( + position: Pos( + line: 7, + column: 13, + ), + name: "primeFactor", + transform_group: Some(TransformGroup( + transform: TransformDirective( + kind: Count, + ), + filter: [ + FilterDirective( + operation: GreaterThan((), VariableRef("two")), + ), + FilterDirective( + operation: GreaterThan((), VariableRef("three")), + ), + ], + )), + )), + ], + ), + ), + arguments: { + "three": Uint64(3), + "two": Uint64(2), + }, +)) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_gt.graphql.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_gt.graphql.ron new file mode 100644 index 00000000..952cc258 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_gt.graphql.ron @@ -0,0 +1,28 @@ +TestGraphQLQuery ( + // Ensure that we properly handle two of the same type of filters + // but one of the filters dominates the other. In this case filtering + // by greater than two is dominated by filtering for greater than three. + // + // This test guards against an accidental min/max mixup in the implementation + // of the optimization that allows us to stop expanding folds early. + // We should only stop when the dominating filter is satisfied, + // not when the dominated filter is satisfied. If such a bug were to happen, + // this query would return `{ "value": 30 }` instead of having no results. + schema_name: "numbers", + query: r#" +{ + Number(min: 30, max: 30) { + ... on Composite { + value @output + + primeFactor @fold @transform(op: "count") + @filter(op: ">", value: ["$two"]) + @filter(op: ">", value: ["$three"]) + } + } +}"#, + arguments: { + "two": Uint64(2), + "three": Uint64(3), + }, +) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_gt.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_gt.ir.ron new file mode 100644 index 00000000..858a25d2 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_gt.ir.ron @@ -0,0 +1,64 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(30), + "min": Int64(30), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Composite", + coerced_from_type: Some("Number"), + ), + }, + folds: { + Eid(1): IRFold( + eid: Eid(1), + from_vid: Vid(1), + to_vid: Vid(2), + edge_name: "primeFactor", + component: IRQueryComponent( + root: Vid(2), + vertices: { + Vid(2): IRVertex( + vid: Vid(2), + type_name: "Prime", + ), + }, + ), + post_filters: [ + GreaterThan(Count, Variable(VariableRef( + variable_name: "two", + variable_type: "Int!", + ))), + GreaterThan(Count, Variable(VariableRef( + variable_name: "three", + variable_type: "Int!", + ))), + ], + ), + }, + outputs: { + "value": ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + ), + }, + ), + variables: { + "three": "Int!", + "two": "Int!", + }, + ), + arguments: { + "three": Uint64(3), + "two": Uint64(2), + }, +)) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_gt.output.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_gt.output.ron new file mode 100644 index 00000000..413fc4d0 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_gt.output.ron @@ -0,0 +1,11 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "value": Output( + name: "value", + value_type: "Int", + vid: Vid(1), + ), + }, + results: [], +) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_gt.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_gt.trace.ron new file mode 100644 index 00000000..02d70a85 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_gt.trace.ron @@ -0,0 +1,237 @@ +TestInterpreterOutputTrace( + schema_name: "numbers", + trace: Trace( + ops: { + Opid(1): TraceOp( + opid: Opid(1), + parent_opid: None, + content: Call(ResolveStartingVertices(Vid(1))), + ), + Opid(2): TraceOp( + opid: Opid(2), + parent_opid: None, + content: Call(ResolveCoercion(Vid(1), "Number", "Composite")), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveNeighbors(Vid(1), "Composite", Eid(1))), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Composite", "value")), + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ])))), + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + vertices: {}, + )), + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveCoercion(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + vertices: {}, + ), true)), + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + }, + )), + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + }, + ))), + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: Some(Opid(12)), + content: YieldFrom(ResolveNeighborsInner(0, Prime(PrimeNumber(2)))), + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(12)), + content: YieldFrom(ResolveNeighborsInner(1, Prime(PrimeNumber(3)))), + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(12)), + content: YieldFrom(ResolveNeighborsInner(2, Prime(PrimeNumber(5)))), + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(12)), + content: OutputIteratorExhausted, + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(19): TraceOp( + opid: Opid(19), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(23): TraceOp( + opid: Opid(23), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + Opid(24): TraceOp( + opid: Opid(24), + parent_opid: Some(Opid(4)), + content: InputIteratorExhausted, + ), + Opid(25): TraceOp( + opid: Opid(25), + parent_opid: Some(Opid(4)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(30), + "min": Int64(30), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Composite", + coerced_from_type: Some("Number"), + ), + }, + folds: { + Eid(1): IRFold( + eid: Eid(1), + from_vid: Vid(1), + to_vid: Vid(2), + edge_name: "primeFactor", + component: IRQueryComponent( + root: Vid(2), + vertices: { + Vid(2): IRVertex( + vid: Vid(2), + type_name: "Prime", + ), + }, + ), + post_filters: [ + GreaterThan(Count, Variable(VariableRef( + variable_name: "two", + variable_type: "Int!", + ))), + GreaterThan(Count, Variable(VariableRef( + variable_name: "three", + variable_type: "Int!", + ))), + ], + ), + }, + outputs: { + "value": ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + ), + }, + ), + variables: { + "three": "Int!", + "two": "Int!", + }, + ), + arguments: { + "three": Uint64(3), + "two": Uint64(2), + }, + ), +) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_lt.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_lt.graphql-parsed.ron new file mode 100644 index 00000000..fa1064d5 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_lt.graphql-parsed.ron @@ -0,0 +1,88 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(30), + "min": Int64(30), + }, + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + coerced_to: Some("Composite"), + connections: [ + (FieldConnection( + position: Pos( + line: 5, + column: 13, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 5, + column: 13, + ), + name: "value", + output: [ + OutputDirective(), + ], + )), + (FieldConnection( + position: Pos( + line: 7, + column: 13, + ), + name: "primeFactor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + transform: TransformDirective( + kind: Count, + ), + filter: [ + FilterDirective( + operation: LessThan((), VariableRef("four")), + ), + FilterDirective( + operation: LessThan((), VariableRef("two")), + ), + ], + )), + )), + ), FieldNode( + position: Pos( + line: 7, + column: 13, + ), + name: "primeFactor", + transform_group: Some(TransformGroup( + transform: TransformDirective( + kind: Count, + ), + filter: [ + FilterDirective( + operation: LessThan((), VariableRef("four")), + ), + FilterDirective( + operation: LessThan((), VariableRef("two")), + ), + ], + )), + )), + ], + ), + ), + arguments: { + "four": Uint64(4), + "two": Uint64(2), + }, +)) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_lt.graphql.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_lt.graphql.ron new file mode 100644 index 00000000..9033d57b --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_lt.graphql.ron @@ -0,0 +1,28 @@ +TestGraphQLQuery ( + // Ensure that we properly handle two of the same type of filters + // but one of the filters dominates the other. In this case filtering + // by less than three is dominated by filtering for less than two. + // + // This test guards against an accidental min/max mixup in the implementation + // of the optimization that allows us to stop expanding folds early. + // We should only stop when the dominating filter is satisfied, + // not when the dominated filter is satisfied. If such a bug were to happen, + // this query would return `{ "value": 30 }` instead of having no results. + schema_name: "numbers", + query: r#" +{ + Number(min: 30, max: 30) { + ... on Composite { + value @output + + primeFactor @fold @transform(op: "count") + @filter(op: "<", value: ["$four"]) + @filter(op: "<", value: ["$two"]) + } + } +}"#, + arguments: { + "four": Uint64(4), + "two": Uint64(2), + }, +) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_lt.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_lt.ir.ron new file mode 100644 index 00000000..816bfe87 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_lt.ir.ron @@ -0,0 +1,64 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(30), + "min": Int64(30), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Composite", + coerced_from_type: Some("Number"), + ), + }, + folds: { + Eid(1): IRFold( + eid: Eid(1), + from_vid: Vid(1), + to_vid: Vid(2), + edge_name: "primeFactor", + component: IRQueryComponent( + root: Vid(2), + vertices: { + Vid(2): IRVertex( + vid: Vid(2), + type_name: "Prime", + ), + }, + ), + post_filters: [ + LessThan(Count, Variable(VariableRef( + variable_name: "four", + variable_type: "Int!", + ))), + LessThan(Count, Variable(VariableRef( + variable_name: "two", + variable_type: "Int!", + ))), + ], + ), + }, + outputs: { + "value": ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + ), + }, + ), + variables: { + "four": "Int!", + "two": "Int!", + }, + ), + arguments: { + "four": Uint64(4), + "two": Uint64(2), + }, +)) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_lt.output.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_lt.output.ron new file mode 100644 index 00000000..413fc4d0 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_lt.output.ron @@ -0,0 +1,11 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "value": Output( + name: "value", + value_type: "Int", + vid: Vid(1), + ), + }, + results: [], +) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_lt.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_lt.trace.ron new file mode 100644 index 00000000..b45a8fe1 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_dominated_filter_lt.trace.ron @@ -0,0 +1,227 @@ +TestInterpreterOutputTrace( + schema_name: "numbers", + trace: Trace( + ops: { + Opid(1): TraceOp( + opid: Opid(1), + parent_opid: None, + content: Call(ResolveStartingVertices(Vid(1))), + ), + Opid(2): TraceOp( + opid: Opid(2), + parent_opid: None, + content: Call(ResolveCoercion(Vid(1), "Number", "Composite")), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveNeighbors(Vid(1), "Composite", Eid(1))), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Composite", "value")), + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ])))), + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + vertices: {}, + )), + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveCoercion(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + vertices: {}, + ), true)), + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + }, + )), + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + }, + ))), + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: Some(Opid(12)), + content: YieldFrom(ResolveNeighborsInner(0, Prime(PrimeNumber(2)))), + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(12)), + content: YieldFrom(ResolveNeighborsInner(1, Prime(PrimeNumber(3)))), + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(19): TraceOp( + opid: Opid(19), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: Some(Opid(4)), + content: InputIteratorExhausted, + ), + Opid(23): TraceOp( + opid: Opid(23), + parent_opid: Some(Opid(4)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(30), + "min": Int64(30), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Composite", + coerced_from_type: Some("Number"), + ), + }, + folds: { + Eid(1): IRFold( + eid: Eid(1), + from_vid: Vid(1), + to_vid: Vid(2), + edge_name: "primeFactor", + component: IRQueryComponent( + root: Vid(2), + vertices: { + Vid(2): IRVertex( + vid: Vid(2), + type_name: "Prime", + ), + }, + ), + post_filters: [ + LessThan(Count, Variable(VariableRef( + variable_name: "four", + variable_type: "Int!", + ))), + LessThan(Count, Variable(VariableRef( + variable_name: "two", + variable_type: "Int!", + ))), + ], + ), + }, + outputs: { + "value": ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + ), + }, + ), + variables: { + "four": "Int!", + "two": "Int!", + }, + ), + arguments: { + "four": Uint64(4), + "two": Uint64(2), + }, + ), +) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_impossible_filter.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_impossible_filter.graphql-parsed.ron new file mode 100644 index 00000000..6474c3e6 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_impossible_filter.graphql-parsed.ron @@ -0,0 +1,88 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(30), + "min": Int64(30), + }, + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + coerced_to: Some("Composite"), + connections: [ + (FieldConnection( + position: Pos( + line: 5, + column: 13, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 5, + column: 13, + ), + name: "value", + output: [ + OutputDirective(), + ], + )), + (FieldConnection( + position: Pos( + line: 7, + column: 13, + ), + name: "primeFactor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + transform: TransformDirective( + kind: Count, + ), + filter: [ + FilterDirective( + operation: GreaterThan((), VariableRef("six")), + ), + FilterDirective( + operation: LessThan((), VariableRef("five")), + ), + ], + )), + )), + ), FieldNode( + position: Pos( + line: 7, + column: 13, + ), + name: "primeFactor", + transform_group: Some(TransformGroup( + transform: TransformDirective( + kind: Count, + ), + filter: [ + FilterDirective( + operation: GreaterThan((), VariableRef("six")), + ), + FilterDirective( + operation: LessThan((), VariableRef("five")), + ), + ], + )), + )), + ], + ), + ), + arguments: { + "five": Uint64(5), + "six": Uint64(6), + }, +)) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_impossible_filter.graphql.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_impossible_filter.graphql.ron new file mode 100644 index 00000000..bee04394 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_impossible_filter.graphql.ron @@ -0,0 +1,21 @@ +TestGraphQLQuery ( + // Ensure that we properly handle two filters that together are impossible + // on the count of a folded edge. + schema_name: "numbers", + query: r#" +{ + Number(min: 30, max: 30) { + ... on Composite { + value @output + + primeFactor @fold @transform(op: "count") + @filter(op: ">", value: ["$six"]) + @filter(op: "<", value: ["$five"]) + } + } +}"#, + arguments: { + "five": Uint64(5), + "six": Uint64(6), + }, +) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_impossible_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_impossible_filter.ir.ron new file mode 100644 index 00000000..5310a310 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_impossible_filter.ir.ron @@ -0,0 +1,64 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(30), + "min": Int64(30), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Composite", + coerced_from_type: Some("Number"), + ), + }, + folds: { + Eid(1): IRFold( + eid: Eid(1), + from_vid: Vid(1), + to_vid: Vid(2), + edge_name: "primeFactor", + component: IRQueryComponent( + root: Vid(2), + vertices: { + Vid(2): IRVertex( + vid: Vid(2), + type_name: "Prime", + ), + }, + ), + post_filters: [ + GreaterThan(Count, Variable(VariableRef( + variable_name: "six", + variable_type: "Int!", + ))), + LessThan(Count, Variable(VariableRef( + variable_name: "five", + variable_type: "Int!", + ))), + ], + ), + }, + outputs: { + "value": ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + ), + }, + ), + variables: { + "five": "Int!", + "six": "Int!", + }, + ), + arguments: { + "five": Uint64(5), + "six": Uint64(6), + }, +)) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_impossible_filter.output.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_impossible_filter.output.ron new file mode 100644 index 00000000..413fc4d0 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_impossible_filter.output.ron @@ -0,0 +1,11 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "value": Output( + name: "value", + value_type: "Int", + vid: Vid(1), + ), + }, + results: [], +) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_impossible_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_impossible_filter.trace.ron new file mode 100644 index 00000000..e4535137 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_impossible_filter.trace.ron @@ -0,0 +1,237 @@ +TestInterpreterOutputTrace( + schema_name: "numbers", + trace: Trace( + ops: { + Opid(1): TraceOp( + opid: Opid(1), + parent_opid: None, + content: Call(ResolveStartingVertices(Vid(1))), + ), + Opid(2): TraceOp( + opid: Opid(2), + parent_opid: None, + content: Call(ResolveCoercion(Vid(1), "Number", "Composite")), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveNeighbors(Vid(1), "Composite", Eid(1))), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Composite", "value")), + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ])))), + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + vertices: {}, + )), + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveCoercion(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + vertices: {}, + ), true)), + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + }, + )), + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + }, + ))), + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: Some(Opid(12)), + content: YieldFrom(ResolveNeighborsInner(0, Prime(PrimeNumber(2)))), + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(12)), + content: YieldFrom(ResolveNeighborsInner(1, Prime(PrimeNumber(3)))), + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(12)), + content: YieldFrom(ResolveNeighborsInner(2, Prime(PrimeNumber(5)))), + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(12)), + content: OutputIteratorExhausted, + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(19): TraceOp( + opid: Opid(19), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(23): TraceOp( + opid: Opid(23), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + Opid(24): TraceOp( + opid: Opid(24), + parent_opid: Some(Opid(4)), + content: InputIteratorExhausted, + ), + Opid(25): TraceOp( + opid: Opid(25), + parent_opid: Some(Opid(4)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(30), + "min": Int64(30), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Composite", + coerced_from_type: Some("Number"), + ), + }, + folds: { + Eid(1): IRFold( + eid: Eid(1), + from_vid: Vid(1), + to_vid: Vid(2), + edge_name: "primeFactor", + component: IRQueryComponent( + root: Vid(2), + vertices: { + Vid(2): IRVertex( + vid: Vid(2), + type_name: "Prime", + ), + }, + ), + post_filters: [ + GreaterThan(Count, Variable(VariableRef( + variable_name: "six", + variable_type: "Int!", + ))), + LessThan(Count, Variable(VariableRef( + variable_name: "five", + variable_type: "Int!", + ))), + ], + ), + }, + outputs: { + "value": ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + ), + }, + ), + variables: { + "five": "Int!", + "six": "Int!", + }, + ), + arguments: { + "five": Uint64(5), + "six": Uint64(6), + }, + ), +)