From 0f83d6754ba826360c00c3371fd2a6dc74e40ed2 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> Date: Tue, 11 Jun 2024 15:47:26 -0400 Subject: [PATCH 01/30] Allow query outputs to refer to a `FieldRef`. (#615) --- trustfall_core/src/frontend/mod.rs | 5 +- trustfall_core/src/interpreter/execution.rs | 97 +++++++++++-------- .../src/interpreter/helpers/correctness.rs | 16 +-- .../src/interpreter/hints/vertex_info.rs | 4 +- trustfall_core/src/ir/indexed.rs | 4 +- trustfall_core/src/ir/mod.rs | 9 +- .../both_missing_and_unused.ir.ron | 4 +- .../invalid_argument_types.ir.ron | 4 +- .../invalid_null_argument_values.ir.ron | 4 +- .../execution_errors/missing_argument.ir.ron | 4 +- ...issing_unused_and_invalid_arguments.ir.ron | 4 +- ...ng_prevents_list_with_null_argument.ir.ron | 8 +- ...pe_narrowing_prevents_null_argument.ir.ron | 8 +- .../execution_errors/unused_argument.ir.ron | 8 +- .../alias_and_prefix_output_names.ir.ron | 8 +- .../alias_and_prefix_output_names.trace.ron | 8 +- .../alias_driven_output_names.ir.ron | 8 +- .../alias_driven_output_names.trace.ron | 8 +- .../alias_driven_tag_and_output_names.ir.ron | 8 +- ...lias_driven_tag_and_output_names.trace.ron | 8 +- ...utput_name_conflict_with_alias_name.ir.ron | 8 +- ...ut_name_conflict_with_alias_name.trace.ron | 8 +- .../coercion_allows_additional_edge.ir.ron | 8 +- .../coercion_allows_additional_edge.trace.ron | 8 +- .../valid_queries/coercion_on_edge.ir.ron | 8 +- .../valid_queries/coercion_on_edge.trace.ron | 8 +- .../coercion_on_folded_edge.ir.ron | 8 +- .../coercion_on_folded_edge.trace.ron | 8 +- .../coercion_on_optional_edge.ir.ron | 8 +- .../coercion_on_optional_edge.trace.ron | 8 +- .../coercion_on_recursed_edge.ir.ron | 8 +- .../coercion_on_recursed_edge.trace.ron | 8 +- .../tests/valid_queries/deep_optional.ir.ron | 12 +-- .../valid_queries/deep_optional.trace.ron | 12 +-- .../valid_queries/duplicated_edge.ir.ron | 8 +- .../valid_queries/duplicated_edge.trace.ron | 8 +- .../tests/valid_queries/edge_parameter.ir.ron | 4 +- .../valid_queries/edge_parameter.trace.ron | 4 +- .../edge_parameters_on_non_root.ir.ron | 8 +- .../edge_parameters_on_non_root.trace.ron | 8 +- .../valid_queries/empty_fold_output.ir.ron | 8 +- .../valid_queries/empty_fold_output.trace.ron | 8 +- .../execute_fold_before_traverse.ir.ron | 8 +- .../execute_fold_before_traverse.trace.ron | 8 +- .../explicit_output_overrides_alias.ir.ron | 12 +-- .../explicit_output_overrides_alias.trace.ron | 12 +-- .../filter_in_fold_using_external_tag.ir.ron | 4 +- ...ilter_in_fold_using_external_tag.trace.ron | 4 +- ...r_in_nested_fold_using_external_tag.ir.ron | 8 +- ...n_nested_fold_using_external_tag.trace.ron | 8 +- .../valid_queries/filter_op_contains.ir.ron | 8 +- .../filter_op_contains.trace.ron | 8 +- .../filter_op_greater_or_equal.ir.ron | 8 +- .../filter_op_greater_or_equal.trace.ron | 8 +- .../filter_op_greater_than.ir.ron | 8 +- .../filter_op_greater_than.trace.ron | 8 +- .../valid_queries/filter_op_has_prefix.ir.ron | 4 +- .../filter_op_has_prefix.trace.ron | 4 +- .../filter_op_has_substring.ir.ron | 4 +- .../filter_op_has_substring.trace.ron | 4 +- .../valid_queries/filter_op_has_suffix.ir.ron | 4 +- .../filter_op_has_suffix.trace.ron | 4 +- .../filter_op_less_or_equal.ir.ron | 4 +- .../filter_op_less_or_equal.trace.ron | 4 +- .../valid_queries/filter_op_less_than.ir.ron | 4 +- .../filter_op_less_than.trace.ron | 4 +- .../valid_queries/filter_op_not_equal.ir.ron | 4 +- .../filter_op_not_equal.trace.ron | 4 +- .../valid_queries/filter_op_one_of.ir.ron | 4 +- .../valid_queries/filter_op_one_of.trace.ron | 4 +- .../valid_queries/filter_op_regex.ir.ron | 4 +- .../valid_queries/filter_op_regex.trace.ron | 4 +- .../filter_with_omitted_value_arg.ir.ron | 8 +- .../filter_with_omitted_value_arg.trace.ron | 8 +- .../valid_queries/filter_within_fold.ir.ron | 4 +- .../filter_within_fold.trace.ron | 4 +- .../valid_queries/fold_count_filter.ir.ron | 8 +- .../valid_queries/fold_count_filter.trace.ron | 8 +- ...er_early_prune_max_fold_size_equals.ir.ron | 8 +- ...early_prune_max_fold_size_equals.trace.ron | 8 +- ...lter_early_prune_max_fold_size_less.ir.ron | 8 +- ...r_early_prune_max_fold_size_less.trace.ron | 8 +- ...rly_prune_max_fold_size_less_equals.ir.ron | 8 +- ..._prune_max_fold_size_less_equals.trace.ron | 8 +- ...er_early_prune_max_fold_size_one_of.ir.ron | 8 +- ...early_prune_max_fold_size_one_of.trace.ron | 8 +- ...d_count_filter_eq_with_negative_arg.ir.ron | 8 +- ...ount_filter_eq_with_negative_arg.trace.ron | 8 +- ...d_count_filter_gt_with_negative_arg.ir.ron | 8 +- ...ount_filter_gt_with_negative_arg.trace.ron | 8 +- ..._count_filter_gte_with_negative_arg.ir.ron | 8 +- ...unt_filter_gte_with_negative_arg.trace.ron | 8 +- .../fold_count_filter_lt_over_i64_max.ir.ron | 8 +- ...old_count_filter_lt_over_i64_max.trace.ron | 8 +- ...d_count_filter_lt_with_negative_arg.ir.ron | 8 +- ...ount_filter_lt_with_negative_arg.trace.ron | 8 +- ..._count_filter_lte_with_negative_arg.ir.ron | 8 +- ...unt_filter_lte_with_negative_arg.trace.ron | 8 +- ...unt_filter_not_eq_with_negative_arg.ir.ron | 8 +- ..._filter_not_eq_with_negative_arg.trace.ron | 8 +- ...filter_not_one_of_with_negative_arg.ir.ron | 8 +- ...ter_not_one_of_with_negative_arg.trace.ron | 8 +- .../fold_count_filter_on_a_tag.ir.ron | 4 +- .../fold_count_filter_on_a_tag.trace.ron | 4 +- ...unt_filter_one_of_with_negative_arg.ir.ron | 8 +- ..._filter_one_of_with_negative_arg.trace.ron | 8 +- ...unt_filter_with_dominated_filter_gt.ir.ron | 4 +- ..._filter_with_dominated_filter_gt.trace.ron | 4 +- ...unt_filter_with_dominated_filter_lt.ir.ron | 4 +- ..._filter_with_dominated_filter_lt.trace.ron | 4 +- ...count_filter_with_impossible_filter.ir.ron | 4 +- ...nt_filter_with_impossible_filter.trace.ron | 4 +- ...fold_count_filter_with_inner_filter.ir.ron | 8 +- ...d_count_filter_with_inner_filter.trace.ron | 8 +- .../fold_count_prefixed_output_name.ir.ron | 4 +- .../fold_count_prefixed_output_name.trace.ron | 4 +- .../fold_count_tag_explicitly_named.ir.ron | 4 +- .../fold_count_tag_explicitly_named.trace.ron | 4 +- ...d_count_tag_on_nonexistent_optional.ir.ron | 8 +- ...ount_tag_on_nonexistent_optional.trace.ron | 8 +- ..._count_tag_used_inside_sibling_fold.ir.ron | 4 +- ...unt_tag_used_inside_sibling_fold.trace.ron | 4 +- .../tests/valid_queries/fold_directive.ir.ron | 4 +- .../valid_queries/fold_directive.trace.ron | 4 +- ..._and_nested_filter_dependent_on_tag.ir.ron | 8 +- ...d_nested_filter_dependent_on_tag.trace.ron | 8 +- ...with_count_filter_and_nested_filter.ir.ron | 8 +- ...h_count_filter_and_nested_filter.trace.ron | 8 +- ...t_filter_and_nested_filter_with_tag.ir.ron | 8 +- ...ilter_and_nested_filter_with_tag.trace.ron | 8 +- .../fold_with_nested_filter.ir.ron | 4 +- .../fold_with_nested_filter.trace.ron | 4 +- .../fold_with_nested_filter_and_tag.ir.ron | 8 +- .../fold_with_nested_filter_and_tag.trace.ron | 8 +- ...h_no_inner_output_with_count_output.ir.ron | 4 +- ...o_inner_output_with_count_output.trace.ron | 4 +- ...with_no_inner_output_with_count_tag.ir.ron | 8 +- ...h_no_inner_output_with_count_tag.trace.ron | 8 +- .../valid_queries/fold_with_no_outputs.ir.ron | 4 +- .../fold_with_no_outputs.trace.ron | 4 +- .../fold_with_no_outputs_elided_braces.ir.ron | 4 +- ...ld_with_no_outputs_elided_braces.trace.ron | 4 +- ...s_transform_and_filter_greater_than.ir.ron | 4 +- ...ransform_and_filter_greater_than.trace.ron | 4 +- ...puts_transform_and_filter_less_than.ir.ron | 4 +- ...s_transform_and_filter_less_than.trace.ron | 4 +- ...outputs_transform_and_filter_one_of.ir.ron | 4 +- ...puts_transform_and_filter_one_of.trace.ron | 4 +- .../fold_with_nonexistent_optional.ir.ron | 8 +- .../fold_with_nonexistent_optional.trace.ron | 8 +- ...s_transform_and_filter_greater_than.ir.ron | 8 +- ...ransform_and_filter_greater_than.trace.ron | 8 +- ...puts_transform_and_filter_less_than.ir.ron | 8 +- ...s_transform_and_filter_less_than.trace.ron | 8 +- ...d_filter_in_fold_using_external_tag.ir.ron | 8 +- ...ilter_in_fold_using_external_tag.trace.ron | 8 +- ...it_coercion_in_recurse_to_supertype.ir.ron | 8 +- ...coercion_in_recurse_to_supertype.trace.ron | 8 +- ...it_null_for_nullable_edge_parameter.ir.ron | 4 +- ...null_for_nullable_edge_parameter.trace.ron | 4 +- .../implicit_tag_and_output_names.ir.ron | 8 +- .../implicit_tag_and_output_names.trace.ron | 8 +- .../multiple_copies_of_field.ir.ron | 4 +- .../multiple_copies_of_field.trace.ron | 4 +- .../multiple_fold_count_outputs.ir.ron | 12 +-- .../multiple_fold_count_outputs.trace.ron | 12 +-- .../multiple_fold_outputs.ir.ron | 8 +- .../multiple_fold_outputs.trace.ron | 8 +- .../tests/valid_queries/multiple_folds.ir.ron | 12 +-- .../valid_queries/multiple_folds.trace.ron | 12 +-- .../tests/valid_queries/nested_fold.ir.ron | 12 +-- .../tests/valid_queries/nested_fold.trace.ron | 12 +-- .../nested_fold_count_output.ir.ron | 12 +-- .../nested_fold_count_output.trace.ron | 12 +-- .../nested_fold_with_no_outputs.ir.ron | 4 +- .../nested_fold_with_no_outputs.trace.ron | 4 +- .../tests/valid_queries/nested_query.ir.ron | 8 +- .../valid_queries/nested_query.trace.ron | 8 +- .../tests/valid_queries/no_op_fragment.ir.ron | 4 +- .../valid_queries/no_op_fragment.trace.ron | 4 +- ...nexistent_optional_with_nested_fold.ir.ron | 12 +-- ...istent_optional_with_nested_fold.trace.ron | 12 +-- ...stent_optional_with_nested_optional.ir.ron | 12 +-- ...nt_optional_with_nested_optional.trace.ron | 12 +-- .../valid_queries/optional_directive.ir.ron | 8 +- .../optional_directive.trace.ron | 8 +- ...ptional_with_nested_edge_and_filter.ir.ron | 4 +- ...onal_with_nested_edge_and_filter.trace.ron | 4 +- ...ith_nested_edge_with_filter_and_tag.ir.ron | 4 +- ..._nested_edge_with_filter_and_tag.trace.ron | 4 +- ...tional_with_nested_filter_semantics.ir.ron | 8 +- ...nal_with_nested_filter_semantics.trace.ron | 8 +- ...th_nested_filter_with_tag_semantics.ir.ron | 8 +- ...nested_filter_with_tag_semantics.trace.ron | 8 +- ...with_nested_required_edge_semantics.ir.ron | 4 +- ...h_nested_required_edge_semantics.trace.ron | 4 +- .../valid_queries/output_fold_count.ir.ron | 8 +- .../valid_queries/output_fold_count.trace.ron | 8 +- .../output_fold_count_multiple.ir.ron | 8 +- .../output_fold_count_multiple.trace.ron | 8 +- ...utputs_both_inside_and_outside_fold.ir.ron | 8 +- ...uts_both_inside_and_outside_fold.trace.ron | 8 +- .../valid_queries/recurse_and_traverse.ir.ron | 12 +-- .../recurse_and_traverse.trace.ron | 12 +-- .../valid_queries/recurse_directive.ir.ron | 8 +- .../valid_queries/recurse_directive.trace.ron | 8 +- .../recurse_on_parameterized_edge.ir.ron | 4 +- .../recurse_on_parameterized_edge.trace.ron | 4 +- .../recurse_then_filter_depth_one.ir.ron | 4 +- .../recurse_then_filter_depth_one.trace.ron | 4 +- .../recurse_then_filter_depth_two.ir.ron | 4 +- .../recurse_then_filter_depth_two.trace.ron | 4 +- .../recurse_then_filter_intermediate.ir.ron | 4 +- ...recurse_then_filter_intermediate.trace.ron | 4 +- .../recurse_then_filter_leaves.ir.ron | 4 +- .../recurse_then_filter_leaves.trace.ron | 4 +- ...ecurse_then_filter_on_tag_depth_one.ir.ron | 4 +- ...rse_then_filter_on_tag_depth_one.trace.ron | 4 +- ...ecurse_then_filter_on_tag_depth_two.ir.ron | 4 +- ...rse_then_filter_on_tag_depth_two.trace.ron | 4 +- ...then_nested_required_edge_depth_two.ir.ron | 4 +- ...n_nested_required_edge_depth_two.trace.ron | 4 +- ...ecurse_then_required_edge_depth_one.ir.ron | 4 +- ...rse_then_required_edge_depth_one.trace.ron | 4 +- .../valid_queries/required_properties.ir.ron | 8 +- .../required_properties.trace.ron | 8 +- ...quired_properties_filter_and_output.ir.ron | 8 +- ...red_properties_filter_and_output.trace.ron | 8 +- .../tests/valid_queries/root_coercion.ir.ron | 4 +- .../valid_queries/root_coercion.trace.ron | 4 +- .../tests/valid_queries/simple_filter.ir.ron | 4 +- .../valid_queries/simple_filter.trace.ron | 4 +- .../tests/valid_queries/simple_query.ir.ron | 4 +- .../valid_queries/simple_query.trace.ron | 4 +- .../static_and_dynamic_filter.ir.ron | 4 +- .../static_and_dynamic_filter.trace.ron | 4 +- .../tag_and_filter_directives.ir.ron | 8 +- .../tag_and_filter_directives.trace.ron | 8 +- .../tag_before_filter_in_same_scope.ir.ron | 4 +- .../tag_before_filter_in_same_scope.trace.ron | 4 +- .../tag_name_in_prefixed_vertex.ir.ron | 8 +- .../tag_name_in_prefixed_vertex.trace.ron | 8 +- .../tag_within_non_existent_optional.ir.ron | 12 +-- ...tag_within_non_existent_optional.trace.ron | 12 +-- ..._non_existent_optional_used_in_fold.ir.ron | 12 +-- ...n_existent_optional_used_in_fold.trace.ron | 12 +-- .../valid_queries/typename_filter.ir.ron | 4 +- .../valid_queries/typename_filter.trace.ron | 4 +- .../valid_queries/typename_output.ir.ron | 4 +- .../valid_queries/typename_output.trace.ron | 4 +- .../typename_output_on_final_type.ir.ron | 4 +- .../typename_output_on_final_type.trace.ron | 4 +- .../typename_tag_and_filter.ir.ron | 4 +- .../typename_tag_and_filter.trace.ron | 4 +- ...ement_fold_with_doubly_nested_folds.ir.ron | 16 +-- ...nt_fold_with_doubly_nested_folds.trace.ron | 16 +-- .../zero_element_fold_with_nested_edge.ir.ron | 12 +-- ...ro_element_fold_with_nested_edge.trace.ron | 12 +-- .../zero_element_fold_with_nested_fold.ir.ron | 12 +-- ...ro_element_fold_with_nested_fold.trace.ron | 12 +-- ...ment_fold_with_nested_optional_edge.ir.ron | 12 +-- ...t_fold_with_nested_optional_edge.trace.ron | 12 +-- 262 files changed, 952 insertions(+), 931 deletions(-) diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index 5fb42c90..5869dfc6 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -599,10 +599,7 @@ where // TODO: fixme, temporary hack to avoid changing the IRQueryComponent struct let hacked_outputs = component_outputs .into_iter() - .filter_map(|(k, v)| match v { - FieldRef::ContextField(c) => Some((k, c)), - FieldRef::FoldSpecificField(_) => None, - }) + .filter(|(k, v)| !matches!(&v, FieldRef::FoldSpecificField(..))) .collect(); Ok(IRQueryComponent { diff --git a/trustfall_core/src/interpreter/execution.rs b/trustfall_core/src/interpreter/execution.rs index 73a87c87..5a4b9715 100644 --- a/trustfall_core/src/interpreter/execution.rs +++ b/trustfall_core/src/interpreter/execution.rs @@ -193,29 +193,37 @@ fn construct_outputs<'query, AdapterT: Adapter<'query>>( let mut output_iterator = iterator; for output_name in output_names.iter() { - let context_field = &root_component.outputs[output_name]; - let vertex_id = context_field.vertex_id; + let field_ref = &root_component.outputs[output_name]; - let moved_iterator = Box::new(output_iterator.map(move |context| { - let new_vertex = context.vertices[&vertex_id].clone(); - context.move_to_vertex(new_vertex) - })); + match field_ref { + FieldRef::ContextField(context_field) => { + let vertex_id = context_field.vertex_id; - let resolve_info = ResolveInfo::new(query, vertex_id, true); + let moved_iterator = Box::new(output_iterator.map(move |context| { + let new_vertex = context.vertices[&vertex_id].clone(); + context.move_to_vertex(new_vertex) + })); - let type_name = &root_component.vertices[&vertex_id].type_name; - let field_data_iterator = adapter.resolve_property( - moved_iterator, - type_name, - &context_field.field_name, - &resolve_info, - ); - query = resolve_info.into_inner(); + let resolve_info = ResolveInfo::new(query, vertex_id, true); - output_iterator = Box::new(field_data_iterator.map(|(mut context, value)| { - context.values.push(value); - context - })); + let type_name = &root_component.vertices[&vertex_id].type_name; + let field_data_iterator = adapter.resolve_property( + moved_iterator, + type_name, + &context_field.field_name, + &resolve_info, + ); + query = resolve_info.into_inner(); + + output_iterator = Box::new(field_data_iterator.map(|(mut context, value)| { + context.values.push(value); + context + })); + } + FieldRef::FoldSpecificField(_) => { + unreachable!("found fold-specific field in component outputs: {root_component:#?}") + } + } } let expected_output_names: BTreeSet<_> = query.indexed_query.outputs.keys().cloned().collect(); carrier.query = Some(query); @@ -648,27 +656,36 @@ mismatch on whether the fold below {expanding_from_vid:?} was inside an `@option // This is a slimmed-down version of computing a context field: // - it does not restore the prior active vertex after getting each value // - it already knows that the context field is guaranteed to exist - let context_field = &fold.component.outputs[output_name.as_ref()]; - let vertex_id = context_field.vertex_id; - let moved_iterator = Box::new(output_iterator.map(move |context| { - let new_vertex = context.vertices[&vertex_id].clone(); - context.move_to_vertex(new_vertex) - })); - - let query = cloned_carrier.query.take().expect("query was not returned"); - let resolve_info = ResolveInfo::new(query, vertex_id, true); - let field_data_iterator = cloned_adapter.resolve_property( - moved_iterator, - &fold.component.vertices[&vertex_id].type_name, - &context_field.field_name, - &resolve_info, - ); - cloned_carrier.query = Some(resolve_info.into_inner()); - - output_iterator = Box::new(field_data_iterator.map(|(mut context, value)| { - context.values.push(value); - context - })); + let field_ref = &fold.component.outputs[output_name.as_ref()]; + + match field_ref { + FieldRef::ContextField(context_field) => { + let vertex_id = context_field.vertex_id; + let moved_iterator = Box::new(output_iterator.map(move |context| { + let new_vertex = context.vertices[&vertex_id].clone(); + context.move_to_vertex(new_vertex) + })); + + let query = cloned_carrier.query.take().expect("query was not returned"); + let resolve_info = ResolveInfo::new(query, vertex_id, true); + let field_data_iterator = cloned_adapter.resolve_property( + moved_iterator, + &fold.component.vertices[&vertex_id].type_name, + &context_field.field_name, + &resolve_info, + ); + cloned_carrier.query = Some(resolve_info.into_inner()); + + output_iterator = + Box::new(field_data_iterator.map(|(mut context, value)| { + context.values.push(value); + context + })); + } + FieldRef::FoldSpecificField(_) => { + unreachable!("found fold-specific field in component outputs: {fold:#?}") + } + } } for mut folded_context in output_iterator { diff --git a/trustfall_core/src/interpreter/helpers/correctness.rs b/trustfall_core/src/interpreter/helpers/correctness.rs index cde76ee6..214e8e51 100644 --- a/trustfall_core/src/interpreter/helpers/correctness.rs +++ b/trustfall_core/src/interpreter/helpers/correctness.rs @@ -3,8 +3,8 @@ use std::{collections::BTreeMap, fmt::Debug, num::NonZeroUsize, sync::Arc}; use crate::{ interpreter::{Adapter, DataContext, InterpretedQuery, ResolveEdgeInfo, ResolveInfo}, ir::{ - ContextField, EdgeParameters, Eid, FieldValue, IREdge, IRQuery, IRQueryComponent, IRVertex, - TransparentValue, Type, Vid, + ContextField, EdgeParameters, Eid, FieldRef, FieldValue, IREdge, IRQuery, IRQueryComponent, + IRVertex, TransparentValue, Type, Vid, }, schema::{Schema, SchemaAdapter}, TryIntoStruct, @@ -166,11 +166,11 @@ fn make_resolve_info_for_property_check( edges: Default::default(), folds: Default::default(), outputs: btreemap! { - property_name.clone() => ContextField { + property_name.clone() => FieldRef::ContextField(ContextField { vertex_id: vid, field_name: property_name.clone(), field_type: Type::parse(property_type).expect("not a valid type"), - } + }) }, }), variables: Default::default(), @@ -324,11 +324,11 @@ fn make_resolve_edge_info_for_edge_check( }, folds: Default::default(), outputs: btreemap! { - property_name.clone() => ContextField { + property_name.clone() => FieldRef::ContextField(ContextField { vertex_id: vid, field_name: property_name, field_type: Type::parse("String!").expect("not a valid type"), - } + }) }, }), variables: Default::default(), @@ -493,11 +493,11 @@ fn make_resolve_info_for_type_coercion( edges: Default::default(), folds: Default::default(), outputs: btreemap! { - typename_property.clone() => ContextField { + typename_property.clone() => FieldRef::ContextField(ContextField { vertex_id: vid, field_name: typename_property.clone(), field_type: Type::parse("String!").expect("not a valid type"), - } + }) }, }), variables: Default::default(), diff --git a/trustfall_core/src/interpreter/hints/vertex_info.rs b/trustfall_core/src/interpreter/hints/vertex_info.rs index 60ab67e1..8d49e13e 100644 --- a/trustfall_core/src/interpreter/hints/vertex_info.rs +++ b/trustfall_core/src/interpreter/hints/vertex_info.rs @@ -166,8 +166,8 @@ impl VertexInfo for T { let properties = current_component .outputs .values() - .filter(|c| c.vertex_id == current_vertex.vid) - .map(|c| RequiredProperty::new(c.field_name.clone())); + .filter(|c| c.defined_at() == current_vertex.vid) + .map(|c| RequiredProperty::new(c.field_name_arc())); let properties = properties.chain( current_vertex diff --git a/trustfall_core/src/ir/indexed.rs b/trustfall_core/src/ir/indexed.rs index 7c302cee..2592cd25 100644 --- a/trustfall_core/src/ir/indexed.rs +++ b/trustfall_core/src/ir/indexed.rs @@ -154,7 +154,7 @@ fn add_data_from_component( } for (output_name, field) in component.outputs.iter() { - let output_vid = field.vertex_id; + let output_vid = field.defined_at(); // the output must be from a vertex in this component let output_component = @@ -166,7 +166,7 @@ fn add_data_from_component( let output_name = output_name.clone(); let output_type = get_output_type( output_vid, - &field.field_type, + field.field_type(), &component_optional_vertices, are_folds_optional, ); diff --git a/trustfall_core/src/ir/mod.rs b/trustfall_core/src/ir/mod.rs index a3843e2e..9fbd3fad 100644 --- a/trustfall_core/src/ir/mod.rs +++ b/trustfall_core/src/ir/mod.rs @@ -117,7 +117,7 @@ pub struct IRQueryComponent { pub folds: BTreeMap>, #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub outputs: BTreeMap, ContextField>, + pub outputs: BTreeMap, FieldRef>, } /// Intermediate representation of a query @@ -318,6 +318,13 @@ impl FieldRef { } } + pub fn field_name_arc(&self) -> Arc { + match self { + FieldRef::ContextField(c) => c.field_name.clone(), + FieldRef::FoldSpecificField(f) => f.kind.field_name().into(), + } + } + /// The vertex ID at which this reference is considered defined. pub fn defined_at(&self) -> Vid { match self { diff --git a/trustfall_core/test_data/tests/execution_errors/both_missing_and_unused.ir.ron b/trustfall_core/test_data/tests/execution_errors/both_missing_and_unused.ir.ron index a9835aee..a5ba3ff8 100644 --- a/trustfall_core/test_data/tests/execution_errors/both_missing_and_unused.ir.ron +++ b/trustfall_core/test_data/tests/execution_errors/both_missing_and_unused.ir.ron @@ -26,11 +26,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/execution_errors/invalid_argument_types.ir.ron b/trustfall_core/test_data/tests/execution_errors/invalid_argument_types.ir.ron index abc573c7..dc851cf5 100644 --- a/trustfall_core/test_data/tests/execution_errors/invalid_argument_types.ir.ron +++ b/trustfall_core/test_data/tests/execution_errors/invalid_argument_types.ir.ron @@ -33,11 +33,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/execution_errors/invalid_null_argument_values.ir.ron b/trustfall_core/test_data/tests/execution_errors/invalid_null_argument_values.ir.ron index 17bd69b5..77bde985 100644 --- a/trustfall_core/test_data/tests/execution_errors/invalid_null_argument_values.ir.ron +++ b/trustfall_core/test_data/tests/execution_errors/invalid_null_argument_values.ir.ron @@ -33,11 +33,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/execution_errors/missing_argument.ir.ron b/trustfall_core/test_data/tests/execution_errors/missing_argument.ir.ron index 3210a59d..e8f9dd69 100644 --- a/trustfall_core/test_data/tests/execution_errors/missing_argument.ir.ron +++ b/trustfall_core/test_data/tests/execution_errors/missing_argument.ir.ron @@ -26,11 +26,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/execution_errors/missing_unused_and_invalid_arguments.ir.ron b/trustfall_core/test_data/tests/execution_errors/missing_unused_and_invalid_arguments.ir.ron index 3ff6b1ab..ae529c19 100644 --- a/trustfall_core/test_data/tests/execution_errors/missing_unused_and_invalid_arguments.ir.ron +++ b/trustfall_core/test_data/tests/execution_errors/missing_unused_and_invalid_arguments.ir.ron @@ -40,11 +40,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/execution_errors/type_narrowing_prevents_list_with_null_argument.ir.ron b/trustfall_core/test_data/tests/execution_errors/type_narrowing_prevents_list_with_null_argument.ir.ron index 6cd32a77..37785f3f 100644 --- a/trustfall_core/test_data/tests/execution_errors/type_narrowing_prevents_list_with_null_argument.ir.ron +++ b/trustfall_core/test_data/tests/execution_errors/type_narrowing_prevents_list_with_null_argument.ir.ron @@ -27,16 +27,16 @@ Ok(TestIRQuery( ), }, outputs: { - "intNonNullList": ContextField( + "intNonNullList": ContextField(ContextField( vertex_id: Vid(1), field_name: "intNonNullList", field_type: "[Int]!", - ), - "nonNullIntList": ContextField( + )), + "nonNullIntList": ContextField(ContextField( vertex_id: Vid(1), field_name: "nonNullIntList", field_type: "[Int!]", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/execution_errors/type_narrowing_prevents_null_argument.ir.ron b/trustfall_core/test_data/tests/execution_errors/type_narrowing_prevents_null_argument.ir.ron index d59b404d..cb1b1f32 100644 --- a/trustfall_core/test_data/tests/execution_errors/type_narrowing_prevents_null_argument.ir.ron +++ b/trustfall_core/test_data/tests/execution_errors/type_narrowing_prevents_null_argument.ir.ron @@ -27,16 +27,16 @@ Ok(TestIRQuery( ), }, outputs: { - "intNonNullList": ContextField( + "intNonNullList": ContextField(ContextField( vertex_id: Vid(1), field_name: "intNonNullList", field_type: "[Int]!", - ), - "nonNullIntList": ContextField( + )), + "nonNullIntList": ContextField(ContextField( vertex_id: Vid(1), field_name: "nonNullIntList", field_type: "[Int!]", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/execution_errors/unused_argument.ir.ron b/trustfall_core/test_data/tests/execution_errors/unused_argument.ir.ron index 23fae1a6..7b54f650 100644 --- a/trustfall_core/test_data/tests/execution_errors/unused_argument.ir.ron +++ b/trustfall_core/test_data/tests/execution_errors/unused_argument.ir.ron @@ -17,16 +17,16 @@ Ok(TestIRQuery( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/alias_and_prefix_output_names.ir.ron b/trustfall_core/test_data/tests/valid_queries/alias_and_prefix_output_names.ir.ron index 13466295..01017c09 100644 --- a/trustfall_core/test_data/tests/valid_queries/alias_and_prefix_output_names.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/alias_and_prefix_output_names.ir.ron @@ -23,16 +23,16 @@ Ok(TestIRQuery( ), }, outputs: { - "succ_is_named": ContextField( + "succ_is_named": ContextField(ContextField( vertex_id: Vid(2), field_name: "name", field_type: "String", - ), - "zero": ContextField( + )), + "zero": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/alias_and_prefix_output_names.trace.ron b/trustfall_core/test_data/tests/valid_queries/alias_and_prefix_output_names.trace.ron index cf7868cc..b6a7f3c1 100644 --- a/trustfall_core/test_data/tests/valid_queries/alias_and_prefix_output_names.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/alias_and_prefix_output_names.trace.ron @@ -204,16 +204,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "succ_is_named": ContextField( + "succ_is_named": ContextField(ContextField( vertex_id: Vid(2), field_name: "name", field_type: "String", - ), - "zero": ContextField( + )), + "zero": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/alias_driven_output_names.ir.ron b/trustfall_core/test_data/tests/valid_queries/alias_driven_output_names.ir.ron index 05b3a359..b6fdeb5b 100644 --- a/trustfall_core/test_data/tests/valid_queries/alias_driven_output_names.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/alias_driven_output_names.ir.ron @@ -23,16 +23,16 @@ Ok(TestIRQuery( ), }, outputs: { - "succ_value": ContextField( + "succ_value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "zero": ContextField( + )), + "zero": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/alias_driven_output_names.trace.ron b/trustfall_core/test_data/tests/valid_queries/alias_driven_output_names.trace.ron index c492dd97..3e9a19b3 100644 --- a/trustfall_core/test_data/tests/valid_queries/alias_driven_output_names.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/alias_driven_output_names.trace.ron @@ -204,16 +204,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "succ_value": ContextField( + "succ_value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "zero": ContextField( + )), + "zero": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/alias_driven_tag_and_output_names.ir.ron b/trustfall_core/test_data/tests/valid_queries/alias_driven_tag_and_output_names.ir.ron index a5e10890..5831bee6 100644 --- a/trustfall_core/test_data/tests/valid_queries/alias_driven_tag_and_output_names.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/alias_driven_tag_and_output_names.ir.ron @@ -33,16 +33,16 @@ Ok(TestIRQuery( ), }, outputs: { - "one": ContextField( + "one": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "successor": ContextField( + )), + "successor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/alias_driven_tag_and_output_names.trace.ron b/trustfall_core/test_data/tests/valid_queries/alias_driven_tag_and_output_names.trace.ron index 71ba400f..6a6da00d 100644 --- a/trustfall_core/test_data/tests/valid_queries/alias_driven_tag_and_output_names.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/alias_driven_tag_and_output_names.trace.ron @@ -316,16 +316,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "one": ContextField( + "one": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "successor": ContextField( + )), + "successor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/avoid_output_name_conflict_with_alias_name.ir.ron b/trustfall_core/test_data/tests/valid_queries/avoid_output_name_conflict_with_alias_name.ir.ron index 2e943749..975d8f8e 100644 --- a/trustfall_core/test_data/tests/valid_queries/avoid_output_name_conflict_with_alias_name.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/avoid_output_name_conflict_with_alias_name.ir.ron @@ -23,16 +23,16 @@ Ok(TestIRQuery( ), }, outputs: { - "successor": ContextField( + "successor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/avoid_output_name_conflict_with_alias_name.trace.ron b/trustfall_core/test_data/tests/valid_queries/avoid_output_name_conflict_with_alias_name.trace.ron index 2eac0bce..ac695a60 100644 --- a/trustfall_core/test_data/tests/valid_queries/avoid_output_name_conflict_with_alias_name.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/avoid_output_name_conflict_with_alias_name.trace.ron @@ -204,16 +204,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "successor": ContextField( + "successor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/coercion_allows_additional_edge.ir.ron b/trustfall_core/test_data/tests/valid_queries/coercion_allows_additional_edge.ir.ron index c8cae80d..f1fdf8bc 100644 --- a/trustfall_core/test_data/tests/valid_queries/coercion_allows_additional_edge.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/coercion_allows_additional_edge.ir.ron @@ -32,21 +32,21 @@ Ok(TestIRQuery( ), }, outputs: { - "prime": ContextField( + "prime": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/coercion_allows_additional_edge.trace.ron b/trustfall_core/test_data/tests/valid_queries/coercion_allows_additional_edge.trace.ron index 6602e441..7103140b 100644 --- a/trustfall_core/test_data/tests/valid_queries/coercion_allows_additional_edge.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/coercion_allows_additional_edge.trace.ron @@ -1896,21 +1896,21 @@ TestInterpreterOutputTrace( ), }, outputs: { - "prime": ContextField( + "prime": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/coercion_on_edge.ir.ron b/trustfall_core/test_data/tests/valid_queries/coercion_on_edge.ir.ron index 6ef03d15..7a72a6e4 100644 --- a/trustfall_core/test_data/tests/valid_queries/coercion_on_edge.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/coercion_on_edge.ir.ron @@ -30,16 +30,16 @@ Ok(TestIRQuery( ), }, outputs: { - "prime": ContextField( + "prime": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/coercion_on_edge.trace.ron b/trustfall_core/test_data/tests/valid_queries/coercion_on_edge.trace.ron index 77a6b491..6acadfed 100644 --- a/trustfall_core/test_data/tests/valid_queries/coercion_on_edge.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/coercion_on_edge.trace.ron @@ -1346,16 +1346,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "prime": ContextField( + "prime": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/coercion_on_folded_edge.ir.ron b/trustfall_core/test_data/tests/valid_queries/coercion_on_folded_edge.ir.ron index f6d39db3..8e0ad8e2 100644 --- a/trustfall_core/test_data/tests/valid_queries/coercion_on_folded_edge.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/coercion_on_folded_edge.ir.ron @@ -32,21 +32,21 @@ Ok(TestIRQuery( ), }, outputs: { - "prime": ContextField( + "prime": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/coercion_on_folded_edge.trace.ron b/trustfall_core/test_data/tests/valid_queries/coercion_on_folded_edge.trace.ron index ec51aea7..77b661de 100644 --- a/trustfall_core/test_data/tests/valid_queries/coercion_on_folded_edge.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/coercion_on_folded_edge.trace.ron @@ -1187,21 +1187,21 @@ TestInterpreterOutputTrace( ), }, outputs: { - "prime": ContextField( + "prime": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/coercion_on_optional_edge.ir.ron b/trustfall_core/test_data/tests/valid_queries/coercion_on_optional_edge.ir.ron index 417e6ea3..f3251f05 100644 --- a/trustfall_core/test_data/tests/valid_queries/coercion_on_optional_edge.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/coercion_on_optional_edge.ir.ron @@ -31,16 +31,16 @@ Ok(TestIRQuery( ), }, outputs: { - "prime": ContextField( + "prime": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/coercion_on_optional_edge.trace.ron b/trustfall_core/test_data/tests/valid_queries/coercion_on_optional_edge.trace.ron index 0fe1c99c..f906d199 100644 --- a/trustfall_core/test_data/tests/valid_queries/coercion_on_optional_edge.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/coercion_on_optional_edge.trace.ron @@ -849,16 +849,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "prime": ContextField( + "prime": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/coercion_on_recursed_edge.ir.ron b/trustfall_core/test_data/tests/valid_queries/coercion_on_recursed_edge.ir.ron index 078d0a24..dc76ccc9 100644 --- a/trustfall_core/test_data/tests/valid_queries/coercion_on_recursed_edge.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/coercion_on_recursed_edge.ir.ron @@ -33,16 +33,16 @@ Ok(TestIRQuery( ), }, outputs: { - "prime": ContextField( + "prime": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/coercion_on_recursed_edge.trace.ron b/trustfall_core/test_data/tests/valid_queries/coercion_on_recursed_edge.trace.ron index e90af64b..11865136 100644 --- a/trustfall_core/test_data/tests/valid_queries/coercion_on_recursed_edge.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/coercion_on_recursed_edge.trace.ron @@ -2430,16 +2430,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "prime": ContextField( + "prime": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/deep_optional.ir.ron b/trustfall_core/test_data/tests/valid_queries/deep_optional.ir.ron index 449387e7..efa51a1d 100644 --- a/trustfall_core/test_data/tests/valid_queries/deep_optional.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/deep_optional.ir.ron @@ -65,21 +65,21 @@ Ok(TestIRQuery( ), }, outputs: { - "top_bottom_mult_value": ContextField( + "top_bottom_mult_value": ContextField(ContextField( vertex_id: Vid(5), field_name: "value", field_type: "Int", - ), - "top_bottom_value": ContextField( + )), + "top_bottom_value": ContextField(ContextField( vertex_id: Vid(4), field_name: "value", field_type: "Int", - ), - "top_value": ContextField( + )), + "top_value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/deep_optional.trace.ron b/trustfall_core/test_data/tests/valid_queries/deep_optional.trace.ron index e5355bab..61ac724d 100644 --- a/trustfall_core/test_data/tests/valid_queries/deep_optional.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/deep_optional.trace.ron @@ -1979,21 +1979,21 @@ TestInterpreterOutputTrace( ), }, outputs: { - "top_bottom_mult_value": ContextField( + "top_bottom_mult_value": ContextField(ContextField( vertex_id: Vid(5), field_name: "value", field_type: "Int", - ), - "top_bottom_value": ContextField( + )), + "top_bottom_value": ContextField(ContextField( vertex_id: Vid(4), field_name: "value", field_type: "Int", - ), - "top_value": ContextField( + )), + "top_value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/duplicated_edge.ir.ron b/trustfall_core/test_data/tests/valid_queries/duplicated_edge.ir.ron index cb3a2602..b2c609bb 100644 --- a/trustfall_core/test_data/tests/valid_queries/duplicated_edge.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/duplicated_edge.ir.ron @@ -33,16 +33,16 @@ Ok(TestIRQuery( ), }, outputs: { - "one": ContextField( + "one": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "one2": ContextField( + )), + "one2": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/duplicated_edge.trace.ron b/trustfall_core/test_data/tests/valid_queries/duplicated_edge.trace.ron index 46a04a27..0c0c8098 100644 --- a/trustfall_core/test_data/tests/valid_queries/duplicated_edge.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/duplicated_edge.trace.ron @@ -275,16 +275,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "one": ContextField( + "one": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "one2": ContextField( + )), + "one2": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/edge_parameter.ir.ron b/trustfall_core/test_data/tests/valid_queries/edge_parameter.ir.ron index ca94d0cc..cd03f297 100644 --- a/trustfall_core/test_data/tests/valid_queries/edge_parameter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/edge_parameter.ir.ron @@ -17,11 +17,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/edge_parameter.trace.ron b/trustfall_core/test_data/tests/valid_queries/edge_parameter.trace.ron index dbca526d..aaaa1d29 100644 --- a/trustfall_core/test_data/tests/valid_queries/edge_parameter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/edge_parameter.trace.ron @@ -517,11 +517,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/edge_parameters_on_non_root.ir.ron b/trustfall_core/test_data/tests/valid_queries/edge_parameters_on_non_root.ir.ron index a60c6229..5f1bcc1f 100644 --- a/trustfall_core/test_data/tests/valid_queries/edge_parameters_on_non_root.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/edge_parameters_on_non_root.ir.ron @@ -34,16 +34,16 @@ Ok(TestIRQuery( ), }, outputs: { - "multiple": ContextField( + "multiple": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/edge_parameters_on_non_root.trace.ron b/trustfall_core/test_data/tests/valid_queries/edge_parameters_on_non_root.trace.ron index 8599e5e4..0bacacd5 100644 --- a/trustfall_core/test_data/tests/valid_queries/edge_parameters_on_non_root.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/edge_parameters_on_non_root.trace.ron @@ -609,16 +609,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "multiple": ContextField( + "multiple": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/empty_fold_output.ir.ron b/trustfall_core/test_data/tests/valid_queries/empty_fold_output.ir.ron index fc2570f2..12d80c8a 100644 --- a/trustfall_core/test_data/tests/valid_queries/empty_fold_output.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/empty_fold_output.ir.ron @@ -30,21 +30,21 @@ Ok(TestIRQuery( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/empty_fold_output.trace.ron b/trustfall_core/test_data/tests/valid_queries/empty_fold_output.trace.ron index 16da43b9..07bb7657 100644 --- a/trustfall_core/test_data/tests/valid_queries/empty_fold_output.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/empty_fold_output.trace.ron @@ -163,21 +163,21 @@ TestInterpreterOutputTrace( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/execute_fold_before_traverse.ir.ron b/trustfall_core/test_data/tests/valid_queries/execute_fold_before_traverse.ir.ron index 56246f7e..1a811235 100644 --- a/trustfall_core/test_data/tests/valid_queries/execute_fold_before_traverse.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/execute_fold_before_traverse.ir.ron @@ -52,21 +52,21 @@ Ok(TestIRQuery( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "succ": ContextField( + "succ": ContextField(ContextField( vertex_id: Vid(4), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/execute_fold_before_traverse.trace.ron b/trustfall_core/test_data/tests/valid_queries/execute_fold_before_traverse.trace.ron index b4add80f..bed150bf 100644 --- a/trustfall_core/test_data/tests/valid_queries/execute_fold_before_traverse.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/execute_fold_before_traverse.trace.ron @@ -527,21 +527,21 @@ TestInterpreterOutputTrace( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "succ": ContextField( + "succ": ContextField(ContextField( vertex_id: Vid(4), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/explicit_output_overrides_alias.ir.ron b/trustfall_core/test_data/tests/valid_queries/explicit_output_overrides_alias.ir.ron index fb78b57c..75c6be8f 100644 --- a/trustfall_core/test_data/tests/valid_queries/explicit_output_overrides_alias.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/explicit_output_overrides_alias.ir.ron @@ -23,21 +23,21 @@ Ok(TestIRQuery( ), }, outputs: { - "one": ContextField( + "one": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "succ_name": ContextField( + )), + "succ_name": ContextField(ContextField( vertex_id: Vid(2), field_name: "name", field_type: "String", - ), - "two": ContextField( + )), + "two": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/explicit_output_overrides_alias.trace.ron b/trustfall_core/test_data/tests/valid_queries/explicit_output_overrides_alias.trace.ron index f01f7efe..28ee4d4b 100644 --- a/trustfall_core/test_data/tests/valid_queries/explicit_output_overrides_alias.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/explicit_output_overrides_alias.trace.ron @@ -260,21 +260,21 @@ TestInterpreterOutputTrace( ), }, outputs: { - "one": ContextField( + "one": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "succ_name": ContextField( + )), + "succ_name": ContextField(ContextField( vertex_id: Vid(2), field_name: "name", field_type: "String", - ), - "two": ContextField( + )), + "two": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_in_fold_using_external_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_in_fold_using_external_tag.ir.ron index 6e68b7ca..4b4ec07b 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_in_fold_using_external_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_in_fold_using_external_tag.ir.ron @@ -40,11 +40,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), imported_tags: [ diff --git a/trustfall_core/test_data/tests/valid_queries/filter_in_fold_using_external_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_in_fold_using_external_tag.trace.ron index 3a28ec71..387115a8 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_in_fold_using_external_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_in_fold_using_external_tag.trace.ron @@ -410,11 +410,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), imported_tags: [ diff --git a/trustfall_core/test_data/tests/valid_queries/filter_in_nested_fold_using_external_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_in_nested_fold_using_external_tag.ir.ron index a8bf174d..09e655a5 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_in_nested_fold_using_external_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_in_nested_fold_using_external_tag.ir.ron @@ -59,21 +59,21 @@ Ok(TestIRQuery( ), }, outputs: { - "second": ContextField( + "second": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "first": ContextField( + "first": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), imported_tags: [ diff --git a/trustfall_core/test_data/tests/valid_queries/filter_in_nested_fold_using_external_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_in_nested_fold_using_external_tag.trace.ron index 31c72c4d..cc8de21e 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_in_nested_fold_using_external_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_in_nested_fold_using_external_tag.trace.ron @@ -661,21 +661,21 @@ TestInterpreterOutputTrace( ), }, outputs: { - "second": ContextField( + "second": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "first": ContextField( + "first": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), imported_tags: [ diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_contains.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_contains.ir.ron index 87b3fa57..bc3324b7 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_contains.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_contains.ir.ron @@ -26,16 +26,16 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "vowelsInName": ContextField( + )), + "vowelsInName": ContextField(ContextField( vertex_id: Vid(1), field_name: "vowelsInName", field_type: "[String]", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_contains.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_contains.trace.ron index 2d7025c1..336291db 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_contains.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_contains.trace.ron @@ -408,16 +408,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "vowelsInName": ContextField( + )), + "vowelsInName": ContextField(ContextField( vertex_id: Vid(1), field_name: "vowelsInName", field_type: "[String]", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_greater_or_equal.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_greater_or_equal.ir.ron index 899392d2..ad621326 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_greater_or_equal.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_greater_or_equal.ir.ron @@ -43,16 +43,16 @@ Ok(TestIRQuery( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_greater_or_equal.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_greater_or_equal.trace.ron index dedaac92..8f60eeea 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_greater_or_equal.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_greater_or_equal.trace.ron @@ -1382,16 +1382,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_greater_than.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_greater_than.ir.ron index 7628d68a..f0a349c0 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_greater_than.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_greater_than.ir.ron @@ -43,16 +43,16 @@ Ok(TestIRQuery( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_greater_than.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_greater_than.trace.ron index a637f2c8..8a22475b 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_greater_than.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_greater_than.trace.ron @@ -1284,16 +1284,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_has_prefix.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_has_prefix.ir.ron index 2abfde3e..47ec5336 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_has_prefix.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_has_prefix.ir.ron @@ -26,11 +26,11 @@ Ok(TestIRQuery( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_has_prefix.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_has_prefix.trace.ron index 88a6787e..97a16d15 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_has_prefix.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_has_prefix.trace.ron @@ -503,11 +503,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_has_substring.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_has_substring.ir.ron index 29b1505c..61b83273 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_has_substring.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_has_substring.ir.ron @@ -26,11 +26,11 @@ Ok(TestIRQuery( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_has_substring.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_has_substring.trace.ron index 00ce1b5e..c029fc7c 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_has_substring.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_has_substring.trace.ron @@ -277,11 +277,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_has_suffix.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_has_suffix.ir.ron index 142a4d0f..cb51c33e 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_has_suffix.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_has_suffix.ir.ron @@ -26,11 +26,11 @@ Ok(TestIRQuery( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_has_suffix.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_has_suffix.trace.ron index 2e910ea7..4cdd6fbe 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_has_suffix.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_has_suffix.trace.ron @@ -632,11 +632,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_less_or_equal.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_less_or_equal.ir.ron index 8bb30f6c..54a97a0c 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_less_or_equal.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_less_or_equal.ir.ron @@ -26,11 +26,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_less_or_equal.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_less_or_equal.trace.ron index 22c34469..31046e5f 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_less_or_equal.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_less_or_equal.trace.ron @@ -183,11 +183,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_less_than.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_less_than.ir.ron index f0d46bdc..e98f3acb 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_less_than.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_less_than.ir.ron @@ -26,11 +26,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_less_than.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_less_than.trace.ron index a9201eb9..6409219f 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_less_than.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_less_than.trace.ron @@ -183,11 +183,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_not_equal.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_not_equal.ir.ron index 79e81837..ff19af8e 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_not_equal.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_not_equal.ir.ron @@ -26,11 +26,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_not_equal.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_not_equal.trace.ron index 126f9362..a8ea925b 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_not_equal.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_not_equal.trace.ron @@ -183,11 +183,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_one_of.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_one_of.ir.ron index 36fd2a3c..c3f85b6c 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_one_of.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_one_of.ir.ron @@ -26,11 +26,11 @@ Ok(TestIRQuery( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_one_of.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_one_of.trace.ron index 0b6e2767..cf60b3b6 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_one_of.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_one_of.trace.ron @@ -330,11 +330,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_regex.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_regex.ir.ron index 57b38834..c1519e86 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_regex.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_regex.ir.ron @@ -26,11 +26,11 @@ Ok(TestIRQuery( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_regex.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_regex.trace.ron index 3d80006a..807bfe28 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_regex.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_regex.trace.ron @@ -330,11 +330,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/filter_with_omitted_value_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_with_omitted_value_arg.ir.ron index 763a3e23..dd549605 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_with_omitted_value_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_with_omitted_value_arg.ir.ron @@ -23,16 +23,16 @@ Ok(TestIRQuery( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_with_omitted_value_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_with_omitted_value_arg.trace.ron index 057b9c59..a6c28088 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_with_omitted_value_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_with_omitted_value_arg.trace.ron @@ -1903,16 +1903,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_within_fold.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_within_fold.ir.ron index 5bcbf18c..b70926b9 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_within_fold.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_within_fold.ir.ron @@ -39,11 +39,11 @@ Ok(TestIRQuery( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(2), field_name: "name", field_type: "String", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_within_fold.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_within_fold.trace.ron index d4829144..23e9591d 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_within_fold.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_within_fold.trace.ron @@ -326,11 +326,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(2), field_name: "name", field_type: "String", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter.ir.ron index 545731ca..57cc9983 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -48,11 +48,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter.trace.ron index 1339bb9a..1b169725 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter.trace.ron @@ -455,11 +455,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -471,11 +471,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_equals.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_equals.ir.ron index b5fff1ca..497d422d 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_equals.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_equals.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -48,11 +48,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_equals.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_equals.trace.ron index 11ed1113..e71eeb5d 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_equals.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_equals.trace.ron @@ -413,11 +413,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -429,11 +429,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less.ir.ron index d87e31e0..8ce0e2a4 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -48,11 +48,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less.trace.ron index f6eb6036..1c108c3e 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less.trace.ron @@ -413,11 +413,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -429,11 +429,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less_equals.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less_equals.ir.ron index 39abf837..3260a6f1 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less_equals.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less_equals.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -48,11 +48,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less_equals.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less_equals.trace.ron index ca00ae11..2515967f 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less_equals.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less_equals.trace.ron @@ -413,11 +413,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -429,11 +429,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_one_of.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_one_of.ir.ron index a930864d..77538dbc 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_one_of.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_one_of.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -48,11 +48,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_one_of.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_one_of.trace.ron index 54f4cf51..7fcba8ca 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_one_of.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_one_of.trace.ron @@ -413,11 +413,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -429,11 +429,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_eq_with_negative_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_eq_with_negative_arg.ir.ron index 611625ad..0a3fecbd 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_eq_with_negative_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_eq_with_negative_arg.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -48,11 +48,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_eq_with_negative_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_eq_with_negative_arg.trace.ron index 356fc81c..97491cd7 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_eq_with_negative_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_eq_with_negative_arg.trace.ron @@ -278,11 +278,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -294,11 +294,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gt_with_negative_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gt_with_negative_arg.ir.ron index 9d078be6..3f3982cd 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gt_with_negative_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gt_with_negative_arg.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -48,11 +48,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gt_with_negative_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gt_with_negative_arg.trace.ron index feb38ede..0557b657 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gt_with_negative_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gt_with_negative_arg.trace.ron @@ -573,11 +573,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -589,11 +589,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gte_with_negative_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gte_with_negative_arg.ir.ron index 8e61f5bb..bfec2fff 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gte_with_negative_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gte_with_negative_arg.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -48,11 +48,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gte_with_negative_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gte_with_negative_arg.trace.ron index 5f3a98da..10512569 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gte_with_negative_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gte_with_negative_arg.trace.ron @@ -573,11 +573,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -589,11 +589,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_over_i64_max.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_over_i64_max.ir.ron index 88ed0e2d..aaf6460b 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_over_i64_max.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_over_i64_max.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -48,11 +48,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_over_i64_max.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_over_i64_max.trace.ron index 364104a0..657e6270 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_over_i64_max.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_over_i64_max.trace.ron @@ -573,11 +573,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -589,11 +589,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_with_negative_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_with_negative_arg.ir.ron index 439033a0..451e52eb 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_with_negative_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_with_negative_arg.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -48,11 +48,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_with_negative_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_with_negative_arg.trace.ron index 7bd07023..67944649 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_with_negative_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_with_negative_arg.trace.ron @@ -278,11 +278,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -294,11 +294,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lte_with_negative_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lte_with_negative_arg.ir.ron index 1efb52a2..a2dc184a 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lte_with_negative_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lte_with_negative_arg.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -48,11 +48,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lte_with_negative_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lte_with_negative_arg.trace.ron index 0b1f943b..126ed95e 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lte_with_negative_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lte_with_negative_arg.trace.ron @@ -278,11 +278,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -294,11 +294,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_eq_with_negative_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_eq_with_negative_arg.ir.ron index 1c15be91..e2f58522 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_eq_with_negative_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_eq_with_negative_arg.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -48,11 +48,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_eq_with_negative_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_eq_with_negative_arg.trace.ron index 7c2f9fb9..a5e56d4a 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_eq_with_negative_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_eq_with_negative_arg.trace.ron @@ -573,11 +573,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -589,11 +589,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_one_of_with_negative_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_one_of_with_negative_arg.ir.ron index e78bc045..5339e776 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_one_of_with_negative_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_one_of_with_negative_arg.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -48,11 +48,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_one_of_with_negative_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_one_of_with_negative_arg.trace.ron index 2ccc1899..73cb787b 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_one_of_with_negative_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_one_of_with_negative_arg.trace.ron @@ -573,11 +573,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -589,11 +589,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.ir.ron index 9bbf8635..ae8a35c6 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.ir.ron @@ -65,11 +65,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.trace.ron index 967f7124..5cd3d96f 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.trace.ron @@ -425,11 +425,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_one_of_with_negative_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_one_of_with_negative_arg.ir.ron index 9b3af45c..17a40573 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_one_of_with_negative_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_one_of_with_negative_arg.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -48,11 +48,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_one_of_with_negative_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_one_of_with_negative_arg.trace.ron index ad6d8632..f162545d 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_one_of_with_negative_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_one_of_with_negative_arg.trace.ron @@ -278,11 +278,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -294,11 +294,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { 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 index 858a25d2..185ac597 100644 --- 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 @@ -45,11 +45,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { 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 index 02d70a85..3ade64f8 100644 --- 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 @@ -217,11 +217,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { 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 index 816bfe87..1c079c08 100644 --- 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 @@ -45,11 +45,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { 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 index b45a8fe1..a9aa144e 100644 --- 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 @@ -207,11 +207,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { 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 index 5310a310..952e5fdf 100644 --- 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 @@ -45,11 +45,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { 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 index e4535137..d2ce4669 100644 --- 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 @@ -217,11 +217,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.ir.ron index 87261b7b..fd975460 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.ir.ron @@ -41,11 +41,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -60,11 +60,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.trace.ron index e6fb0f8d..dd14a7c4 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.trace.ron @@ -465,11 +465,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -484,11 +484,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_prefixed_output_name.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_prefixed_output_name.ir.ron index 552a5722..cb5c69e7 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_prefixed_output_name.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_prefixed_output_name.ir.ron @@ -25,11 +25,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factor_numbers": ContextField( + "factor_numbers": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_prefixed_output_name.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_prefixed_output_name.trace.ron index 8a0bb7c7..7c8965ba 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_prefixed_output_name.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_prefixed_output_name.trace.ron @@ -163,11 +163,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factor_numbers": ContextField( + "factor_numbers": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_explicitly_named.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_explicitly_named.ir.ron index 513f48d8..f9edb197 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_explicitly_named.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_explicitly_named.ir.ron @@ -50,11 +50,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_explicitly_named.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_explicitly_named.trace.ron index 4c65f274..0b74f4b3 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_explicitly_named.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_explicitly_named.trace.ron @@ -337,11 +337,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.ir.ron index 36b11e12..f8e7e901 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.ir.ron @@ -79,16 +79,16 @@ Ok(TestIRQuery( ), }, outputs: { - "start": ContextField( + "start": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "succ": ContextField( + )), + "succ": ContextField(ContextField( vertex_id: Vid(5), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.trace.ron index d8eeeb07..4140717f 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.trace.ron @@ -526,16 +526,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "start": ContextField( + "start": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "succ": ContextField( + )), + "succ": ContextField(ContextField( vertex_id: Vid(5), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_used_inside_sibling_fold.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_used_inside_sibling_fold.ir.ron index e02043ee..b82b265a 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_used_inside_sibling_fold.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_used_inside_sibling_fold.ir.ron @@ -60,11 +60,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_used_inside_sibling_fold.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_used_inside_sibling_fold.trace.ron index f8f5c0be..b91be272 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_used_inside_sibling_fold.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_used_inside_sibling_fold.trace.ron @@ -379,11 +379,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_directive.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_directive.ir.ron index 6f7e73cc..3a682e10 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_directive.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_directive.ir.ron @@ -30,11 +30,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_directive.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_directive.trace.ron index 45437ef9..58465b60 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_directive.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_directive.trace.ron @@ -213,11 +213,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.ir.ron index a987218d..e803eb34 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.ir.ron @@ -47,11 +47,11 @@ Ok(TestIRQuery( ), }, outputs: { - "inner": ContextField( + "inner": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), imported_tags: [ @@ -71,11 +71,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.trace.ron index 0c9d5ab5..6c10a18b 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.trace.ron @@ -546,11 +546,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "inner": ContextField( + "inner": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), imported_tags: [ @@ -570,11 +570,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.ir.ron index 50a243fa..c15997ad 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.ir.ron @@ -46,11 +46,11 @@ Ok(TestIRQuery( ), }, outputs: { - "inner": ContextField( + "inner": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -62,11 +62,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.trace.ron index 271526b9..131220c1 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.trace.ron @@ -349,11 +349,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "inner": ContextField( + "inner": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -365,11 +365,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.ir.ron index 0141af2e..196c0263 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.ir.ron @@ -47,11 +47,11 @@ Ok(TestIRQuery( ), }, outputs: { - "inner": ContextField( + "inner": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), imported_tags: [ @@ -70,11 +70,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.trace.ron index df949697..6486625c 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.trace.ron @@ -453,11 +453,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "inner": ContextField( + "inner": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), imported_tags: [ @@ -476,11 +476,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter.ir.ron index 4eba295d..5146f9ad 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter.ir.ron @@ -46,11 +46,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter.trace.ron index f1dda1b4..0ecc1d9b 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter.trace.ron @@ -271,11 +271,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter_and_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter_and_tag.ir.ron index 5e3ab52a..42b6384f 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter_and_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter_and_tag.ir.ron @@ -47,11 +47,11 @@ Ok(TestIRQuery( ), }, outputs: { - "inner": ContextField( + "inner": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), imported_tags: [ @@ -64,11 +64,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter_and_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter_and_tag.trace.ron index aa3988d0..09083f32 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter_and_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter_and_tag.trace.ron @@ -453,11 +453,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "inner": ContextField( + "inner": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), imported_tags: [ @@ -470,11 +470,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.ir.ron index 46386800..83fb88e2 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.ir.ron @@ -44,11 +44,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.trace.ron index ab17ae1a..cf18fb99 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.trace.ron @@ -315,11 +315,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.ir.ron index 94bb473e..f608c07e 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.ir.ron @@ -63,16 +63,16 @@ Ok(TestIRQuery( ), }, outputs: { - "composite_value": ContextField( + "composite_value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "prime_factors": ContextField( + )), + "prime_factors": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.trace.ron index da69396c..a4ca19a6 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.trace.ron @@ -1166,16 +1166,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "composite_value": ContextField( + "composite_value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "prime_factors": ContextField( + )), + "prime_factors": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs.ir.ron index 394a706e..55f345dd 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs.ir.ron @@ -41,11 +41,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs.trace.ron index 2c56bda8..ad4f66eb 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs.trace.ron @@ -283,11 +283,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_elided_braces.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_elided_braces.ir.ron index 394a706e..55f345dd 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_elided_braces.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_elided_braces.ir.ron @@ -41,11 +41,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_elided_braces.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_elided_braces.trace.ron index 2c56bda8..ad4f66eb 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_elided_braces.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_elided_braces.trace.ron @@ -283,11 +283,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_greater_than.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_greater_than.ir.ron index b1659963..8cee37a0 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_greater_than.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_greater_than.ir.ron @@ -41,11 +41,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_greater_than.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_greater_than.trace.ron index 22223938..a311def1 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_greater_than.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_greater_than.trace.ron @@ -283,11 +283,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_less_than.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_less_than.ir.ron index 938dadc1..613889d0 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_less_than.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_less_than.ir.ron @@ -41,11 +41,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_less_than.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_less_than.trace.ron index 17ca7270..be52bb09 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_less_than.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_less_than.trace.ron @@ -364,11 +364,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_one_of.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_one_of.ir.ron index 22732dc4..a8a0d075 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_one_of.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_one_of.ir.ron @@ -41,11 +41,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_one_of.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_one_of.trace.ron index d6204a1a..2c3ae9b1 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_one_of.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_one_of.trace.ron @@ -203,11 +203,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_nonexistent_optional.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_nonexistent_optional.ir.ron index 57e26b6c..943139c4 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_nonexistent_optional.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_nonexistent_optional.ir.ron @@ -38,16 +38,16 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), - "zero": ContextField( + )), + "zero": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_nonexistent_optional.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_nonexistent_optional.trace.ron index ee086a87..a7f7d7e3 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_nonexistent_optional.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_nonexistent_optional.trace.ron @@ -273,16 +273,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), - "zero": ContextField( + )), + "zero": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_greater_than.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_greater_than.ir.ron index 24942dca..8e59a212 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_greater_than.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_greater_than.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "prime": ContextField( + "prime": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -48,11 +48,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_greater_than.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_greater_than.trace.ron index bb1cf001..87946321 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_greater_than.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_greater_than.trace.ron @@ -410,11 +410,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "prime": ContextField( + "prime": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -426,11 +426,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_less_than.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_less_than.ir.ron index 57405b25..a6925573 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_less_than.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_less_than.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "prime": ContextField( + "prime": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -48,11 +48,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_less_than.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_less_than.trace.ron index ba793b62..1ae89fad 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_less_than.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_less_than.trace.ron @@ -194,11 +194,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "prime": ContextField( + "prime": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), post_filters: [ @@ -210,11 +210,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/folded_filter_in_fold_using_external_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/folded_filter_in_fold_using_external_tag.ir.ron index 9b936fec..854bc967 100644 --- a/trustfall_core/test_data/tests/valid_queries/folded_filter_in_fold_using_external_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/folded_filter_in_fold_using_external_tag.ir.ron @@ -59,11 +59,11 @@ Ok(TestIRQuery( ), }, outputs: { - "second": ContextField( + "second": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), imported_tags: [ @@ -76,11 +76,11 @@ Ok(TestIRQuery( ), }, outputs: { - "first": ContextField( + "first": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/folded_filter_in_fold_using_external_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/folded_filter_in_fold_using_external_tag.trace.ron index 43b4f781..25bca7cb 100644 --- a/trustfall_core/test_data/tests/valid_queries/folded_filter_in_fold_using_external_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/folded_filter_in_fold_using_external_tag.trace.ron @@ -557,11 +557,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "second": ContextField( + "second": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), imported_tags: [ @@ -574,11 +574,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "first": ContextField( + "first": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/implicit_coercion_in_recurse_to_supertype.ir.ron b/trustfall_core/test_data/tests/valid_queries/implicit_coercion_in_recurse_to_supertype.ir.ron index f34cb5d1..d68a1a6e 100644 --- a/trustfall_core/test_data/tests/valid_queries/implicit_coercion_in_recurse_to_supertype.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/implicit_coercion_in_recurse_to_supertype.ir.ron @@ -34,16 +34,16 @@ Ok(TestIRQuery( ), }, outputs: { - "base": ContextField( + "base": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/implicit_coercion_in_recurse_to_supertype.trace.ron b/trustfall_core/test_data/tests/valid_queries/implicit_coercion_in_recurse_to_supertype.trace.ron index d6a09a59..b53413a0 100644 --- a/trustfall_core/test_data/tests/valid_queries/implicit_coercion_in_recurse_to_supertype.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/implicit_coercion_in_recurse_to_supertype.trace.ron @@ -2501,16 +2501,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "base": ContextField( + "base": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/implicit_null_for_nullable_edge_parameter.ir.ron b/trustfall_core/test_data/tests/valid_queries/implicit_null_for_nullable_edge_parameter.ir.ron index ba3f9a89..b1006684 100644 --- a/trustfall_core/test_data/tests/valid_queries/implicit_null_for_nullable_edge_parameter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/implicit_null_for_nullable_edge_parameter.ir.ron @@ -17,11 +17,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/implicit_null_for_nullable_edge_parameter.trace.ron b/trustfall_core/test_data/tests/valid_queries/implicit_null_for_nullable_edge_parameter.trace.ron index 79253635..9cd28ba9 100644 --- a/trustfall_core/test_data/tests/valid_queries/implicit_null_for_nullable_edge_parameter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/implicit_null_for_nullable_edge_parameter.trace.ron @@ -124,11 +124,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/implicit_tag_and_output_names.ir.ron b/trustfall_core/test_data/tests/valid_queries/implicit_tag_and_output_names.ir.ron index bf72ece4..9ba8c79e 100644 --- a/trustfall_core/test_data/tests/valid_queries/implicit_tag_and_output_names.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/implicit_tag_and_output_names.ir.ron @@ -33,16 +33,16 @@ Ok(TestIRQuery( ), }, outputs: { - "successor_value": ContextField( + "successor_value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/implicit_tag_and_output_names.trace.ron b/trustfall_core/test_data/tests/valid_queries/implicit_tag_and_output_names.trace.ron index 1e0ae743..5a169cf9 100644 --- a/trustfall_core/test_data/tests/valid_queries/implicit_tag_and_output_names.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/implicit_tag_and_output_names.trace.ron @@ -316,16 +316,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "successor_value": ContextField( + "successor_value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/multiple_copies_of_field.ir.ron b/trustfall_core/test_data/tests/valid_queries/multiple_copies_of_field.ir.ron index 45026a65..7d1738df 100644 --- a/trustfall_core/test_data/tests/valid_queries/multiple_copies_of_field.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/multiple_copies_of_field.ir.ron @@ -37,11 +37,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/multiple_copies_of_field.trace.ron b/trustfall_core/test_data/tests/valid_queries/multiple_copies_of_field.trace.ron index e089ed4a..307c7dad 100644 --- a/trustfall_core/test_data/tests/valid_queries/multiple_copies_of_field.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/multiple_copies_of_field.trace.ron @@ -266,11 +266,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/multiple_fold_count_outputs.ir.ron b/trustfall_core/test_data/tests/valid_queries/multiple_fold_count_outputs.ir.ron index be72a741..e38c52fd 100644 --- a/trustfall_core/test_data/tests/valid_queries/multiple_fold_count_outputs.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/multiple_fold_count_outputs.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -62,11 +62,11 @@ Ok(TestIRQuery( ), }, outputs: { - "multiples": ContextField( + "multiples": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -75,11 +75,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/multiple_fold_count_outputs.trace.ron b/trustfall_core/test_data/tests/valid_queries/multiple_fold_count_outputs.trace.ron index e261b2c8..f9822420 100644 --- a/trustfall_core/test_data/tests/valid_queries/multiple_fold_count_outputs.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/multiple_fold_count_outputs.trace.ron @@ -714,11 +714,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -744,11 +744,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "multiples": ContextField( + "multiples": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -757,11 +757,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/multiple_fold_outputs.ir.ron b/trustfall_core/test_data/tests/valid_queries/multiple_fold_outputs.ir.ron index dbc3e61f..850c79a5 100644 --- a/trustfall_core/test_data/tests/valid_queries/multiple_fold_outputs.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/multiple_fold_outputs.ir.ron @@ -30,16 +30,16 @@ Ok(TestIRQuery( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(2), field_name: "name", field_type: "String", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/multiple_fold_outputs.trace.ron b/trustfall_core/test_data/tests/valid_queries/multiple_fold_outputs.trace.ron index 98d8ea4d..3579bb51 100644 --- a/trustfall_core/test_data/tests/valid_queries/multiple_fold_outputs.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/multiple_fold_outputs.trace.ron @@ -580,16 +580,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(2), field_name: "name", field_type: "String", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/multiple_folds.ir.ron b/trustfall_core/test_data/tests/valid_queries/multiple_folds.ir.ron index e097f49e..04b5511d 100644 --- a/trustfall_core/test_data/tests/valid_queries/multiple_folds.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/multiple_folds.ir.ron @@ -30,11 +30,11 @@ Ok(TestIRQuery( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), @@ -52,21 +52,21 @@ Ok(TestIRQuery( ), }, outputs: { - "div": ContextField( + "div": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/multiple_folds.trace.ron b/trustfall_core/test_data/tests/valid_queries/multiple_folds.trace.ron index 8e228e5d..fe824e1f 100644 --- a/trustfall_core/test_data/tests/valid_queries/multiple_folds.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/multiple_folds.trace.ron @@ -570,11 +570,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), @@ -592,21 +592,21 @@ TestInterpreterOutputTrace( ), }, outputs: { - "div": ContextField( + "div": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/nested_fold.ir.ron b/trustfall_core/test_data/tests/valid_queries/nested_fold.ir.ron index 0f4f6fdd..fe48daaf 100644 --- a/trustfall_core/test_data/tests/valid_queries/nested_fold.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/nested_fold.ir.ron @@ -55,31 +55,31 @@ Ok(TestIRQuery( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "base": ContextField( + "base": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/nested_fold.trace.ron b/trustfall_core/test_data/tests/valid_queries/nested_fold.trace.ron index cafc8186..f9480774 100644 --- a/trustfall_core/test_data/tests/valid_queries/nested_fold.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/nested_fold.trace.ron @@ -3884,31 +3884,31 @@ TestInterpreterOutputTrace( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "base": ContextField( + "base": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/nested_fold_count_output.ir.ron b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_output.ir.ron index d8446dda..c2c77620 100644 --- a/trustfall_core/test_data/tests/valid_queries/nested_fold_count_output.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_output.ir.ron @@ -51,11 +51,11 @@ Ok(TestIRQuery( ), }, outputs: { - "multiples": ContextField( + "multiples": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -64,11 +64,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -77,11 +77,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/nested_fold_count_output.trace.ron b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_output.trace.ron index ee57c119..3ca0e131 100644 --- a/trustfall_core/test_data/tests/valid_queries/nested_fold_count_output.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_output.trace.ron @@ -972,11 +972,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "multiples": ContextField( + "multiples": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -985,11 +985,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -998,11 +998,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/nested_fold_with_no_outputs.ir.ron b/trustfall_core/test_data/tests/valid_queries/nested_fold_with_no_outputs.ir.ron index 2980e9ac..2173ae02 100644 --- a/trustfall_core/test_data/tests/valid_queries/nested_fold_with_no_outputs.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/nested_fold_with_no_outputs.ir.ron @@ -65,11 +65,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/nested_fold_with_no_outputs.trace.ron b/trustfall_core/test_data/tests/valid_queries/nested_fold_with_no_outputs.trace.ron index 2c9246d1..00fcc6c9 100644 --- a/trustfall_core/test_data/tests/valid_queries/nested_fold_with_no_outputs.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/nested_fold_with_no_outputs.trace.ron @@ -567,11 +567,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/nested_query.ir.ron b/trustfall_core/test_data/tests/valid_queries/nested_query.ir.ron index 65cb28ed..b5012c32 100644 --- a/trustfall_core/test_data/tests/valid_queries/nested_query.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/nested_query.ir.ron @@ -29,16 +29,16 @@ Ok(TestIRQuery( ), }, outputs: { - "successor": ContextField( + "successor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/nested_query.trace.ron b/trustfall_core/test_data/tests/valid_queries/nested_query.trace.ron index f4e4cdd6..2c55b385 100644 --- a/trustfall_core/test_data/tests/valid_queries/nested_query.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/nested_query.trace.ron @@ -548,16 +548,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "successor": ContextField( + "successor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/no_op_fragment.ir.ron b/trustfall_core/test_data/tests/valid_queries/no_op_fragment.ir.ron index 1bfa85c7..43455141 100644 --- a/trustfall_core/test_data/tests/valid_queries/no_op_fragment.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/no_op_fragment.ir.ron @@ -23,11 +23,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/no_op_fragment.trace.ron b/trustfall_core/test_data/tests/valid_queries/no_op_fragment.trace.ron index 0ebf5789..f9c82b54 100644 --- a/trustfall_core/test_data/tests/valid_queries/no_op_fragment.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/no_op_fragment.trace.ron @@ -150,11 +150,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_fold.ir.ron b/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_fold.ir.ron index 17b9906a..f7d46bae 100644 --- a/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_fold.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_fold.ir.ron @@ -38,11 +38,11 @@ Ok(TestIRQuery( ), }, outputs: { - "successors": ContextField( + "successors": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -51,16 +51,16 @@ Ok(TestIRQuery( ), }, outputs: { - "predecessor": ContextField( + "predecessor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "zero": ContextField( + )), + "zero": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_fold.trace.ron b/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_fold.trace.ron index 515550dc..a8d2d15b 100644 --- a/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_fold.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_fold.trace.ron @@ -291,11 +291,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "successors": ContextField( + "successors": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -304,16 +304,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "predecessor": ContextField( + "predecessor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "zero": ContextField( + )), + "zero": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_optional.ir.ron b/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_optional.ir.ron index 73fd0d32..8d37c6a8 100644 --- a/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_optional.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_optional.ir.ron @@ -35,21 +35,21 @@ Ok(TestIRQuery( ), }, outputs: { - "predecessor": ContextField( + "predecessor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "successors": ContextField( + )), + "successors": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), - "zero": ContextField( + )), + "zero": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_optional.trace.ron b/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_optional.trace.ron index 21a156cb..4e7a499c 100644 --- a/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_optional.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_optional.trace.ron @@ -325,21 +325,21 @@ TestInterpreterOutputTrace( ), }, outputs: { - "predecessor": ContextField( + "predecessor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "successors": ContextField( + )), + "successors": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), - "zero": ContextField( + )), + "zero": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/optional_directive.ir.ron b/trustfall_core/test_data/tests/valid_queries/optional_directive.ir.ron index fdbd62f6..bf2ffae6 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_directive.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_directive.ir.ron @@ -35,16 +35,16 @@ Ok(TestIRQuery( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/optional_directive.trace.ron b/trustfall_core/test_data/tests/valid_queries/optional_directive.trace.ron index ff16a04a..446e4579 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_directive.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_directive.trace.ron @@ -1095,16 +1095,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_and_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_and_filter.ir.ron index 9afd1cd9..d8695e7f 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_and_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_and_filter.ir.ron @@ -43,11 +43,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_and_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_and_filter.trace.ron index 49b634b5..3d60d378 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_and_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_and_filter.trace.ron @@ -240,11 +240,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_with_filter_and_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_with_filter_and_tag.ir.ron index 6603c248..6003538c 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_with_filter_and_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_with_filter_and_tag.ir.ron @@ -44,11 +44,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_with_filter_and_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_with_filter_and_tag.trace.ron index 93642d23..6d53a8a4 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_with_filter_and_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_with_filter_and_tag.trace.ron @@ -300,11 +300,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_semantics.ir.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_semantics.ir.ron index 8dab8804..f524fb38 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_semantics.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_semantics.ir.ron @@ -33,16 +33,16 @@ Ok(TestIRQuery( ), }, outputs: { - "predecessor": ContextField( + "predecessor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_semantics.trace.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_semantics.trace.ron index f4282d6a..b33170d9 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_semantics.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_semantics.trace.ron @@ -191,16 +191,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "predecessor": ContextField( + "predecessor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_with_tag_semantics.ir.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_with_tag_semantics.ir.ron index 90d81b64..63fffc12 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_with_tag_semantics.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_with_tag_semantics.ir.ron @@ -34,16 +34,16 @@ Ok(TestIRQuery( ), }, outputs: { - "predecessor": ContextField( + "predecessor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_with_tag_semantics.trace.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_with_tag_semantics.trace.ron index 5e72d04e..4eefb794 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_with_tag_semantics.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_with_tag_semantics.trace.ron @@ -249,16 +249,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "predecessor": ContextField( + "predecessor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_required_edge_semantics.ir.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_required_edge_semantics.ir.ron index 9c6fec8e..22cd3876 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_required_edge_semantics.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_required_edge_semantics.ir.ron @@ -34,11 +34,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_required_edge_semantics.trace.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_required_edge_semantics.trace.ron index e0344636..599f1aeb 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_required_edge_semantics.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_required_edge_semantics.trace.ron @@ -179,11 +179,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/output_fold_count.ir.ron b/trustfall_core/test_data/tests/valid_queries/output_fold_count.ir.ron index b9420919..d84dfbef 100644 --- a/trustfall_core/test_data/tests/valid_queries/output_fold_count.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/output_fold_count.ir.ron @@ -25,11 +25,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -38,11 +38,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/output_fold_count.trace.ron b/trustfall_core/test_data/tests/valid_queries/output_fold_count.trace.ron index 924cea93..900641e6 100644 --- a/trustfall_core/test_data/tests/valid_queries/output_fold_count.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/output_fold_count.trace.ron @@ -249,11 +249,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -262,11 +262,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/output_fold_count_multiple.ir.ron b/trustfall_core/test_data/tests/valid_queries/output_fold_count_multiple.ir.ron index 5ed41887..ea14bcbb 100644 --- a/trustfall_core/test_data/tests/valid_queries/output_fold_count_multiple.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/output_fold_count_multiple.ir.ron @@ -32,11 +32,11 @@ Ok(TestIRQuery( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -45,11 +45,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/output_fold_count_multiple.trace.ron b/trustfall_core/test_data/tests/valid_queries/output_fold_count_multiple.trace.ron index c9f0f1c1..6409ca6e 100644 --- a/trustfall_core/test_data/tests/valid_queries/output_fold_count_multiple.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/output_fold_count_multiple.trace.ron @@ -913,11 +913,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "factors": ContextField( + "factors": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -926,11 +926,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/outputs_both_inside_and_outside_fold.ir.ron b/trustfall_core/test_data/tests/valid_queries/outputs_both_inside_and_outside_fold.ir.ron index 21d4d4ed..39e9a463 100644 --- a/trustfall_core/test_data/tests/valid_queries/outputs_both_inside_and_outside_fold.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/outputs_both_inside_and_outside_fold.ir.ron @@ -36,21 +36,21 @@ Ok(TestIRQuery( ), }, outputs: { - "multiple": ContextField( + "multiple": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/outputs_both_inside_and_outside_fold.trace.ron b/trustfall_core/test_data/tests/valid_queries/outputs_both_inside_and_outside_fold.trace.ron index 8d1ce1cd..8caea71e 100644 --- a/trustfall_core/test_data/tests/valid_queries/outputs_both_inside_and_outside_fold.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/outputs_both_inside_and_outside_fold.trace.ron @@ -649,21 +649,21 @@ TestInterpreterOutputTrace( ), }, outputs: { - "multiple": ContextField( + "multiple": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_and_traverse.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_and_traverse.ir.ron index b810af81..7badd3c3 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_and_traverse.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_and_traverse.ir.ron @@ -47,21 +47,21 @@ Ok(TestIRQuery( ), }, outputs: { - "base": ContextField( + "base": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "mult": ContextField( + )), + "mult": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_and_traverse.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_and_traverse.trace.ron index 9fa858f2..38d60fb1 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_and_traverse.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_and_traverse.trace.ron @@ -4132,21 +4132,21 @@ TestInterpreterOutputTrace( ), }, outputs: { - "base": ContextField( + "base": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "mult": ContextField( + )), + "mult": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_directive.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_directive.ir.ron index ff02a351..1a981003 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_directive.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_directive.ir.ron @@ -32,16 +32,16 @@ Ok(TestIRQuery( ), }, outputs: { - "next": ContextField( + "next": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_directive.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_directive.trace.ron index 282e4a3a..22c7609f 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_directive.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_directive.trace.ron @@ -2012,16 +2012,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "next": ContextField( + "next": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "value": ContextField( + )), + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_on_parameterized_edge.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_on_parameterized_edge.ir.ron index 30d859dc..ce24f8fe 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_on_parameterized_edge.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_on_parameterized_edge.ir.ron @@ -31,11 +31,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_on_parameterized_edge.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_on_parameterized_edge.trace.ron index 7ab1f839..02688fad 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_on_parameterized_edge.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_on_parameterized_edge.trace.ron @@ -1305,11 +1305,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_one.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_one.ir.ron index 821f63a4..4c903374 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_one.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_one.ir.ron @@ -41,11 +41,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_one.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_one.trace.ron index c66f3606..5f8980c9 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_one.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_one.trace.ron @@ -737,11 +737,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_two.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_two.ir.ron index cc3386b8..5d98c004 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_two.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_two.ir.ron @@ -41,11 +41,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_two.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_two.trace.ron index 28fdff7d..7c923c10 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_two.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_two.trace.ron @@ -1340,11 +1340,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_intermediate.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_intermediate.ir.ron index 8c3fa748..bb92bffd 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_intermediate.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_intermediate.ir.ron @@ -41,11 +41,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_intermediate.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_intermediate.trace.ron index d80cff00..979a7826 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_intermediate.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_intermediate.trace.ron @@ -2091,11 +2091,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_leaves.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_leaves.ir.ron index 4a1301ba..73fd0e7b 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_leaves.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_leaves.ir.ron @@ -41,11 +41,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_leaves.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_leaves.trace.ron index 5daa0460..64b03d69 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_leaves.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_leaves.trace.ron @@ -1991,11 +1991,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_one.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_one.ir.ron index 4d314f9e..9b68d8d7 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_one.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_one.ir.ron @@ -42,11 +42,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_one.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_one.trace.ron index a074486d..50ce2a73 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_one.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_one.trace.ron @@ -1414,11 +1414,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_two.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_two.ir.ron index 8e8b2879..8d397edb 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_two.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_two.ir.ron @@ -42,11 +42,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_two.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_two.trace.ron index 46dca3f4..a2743355 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_two.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_two.trace.ron @@ -2435,11 +2435,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_nested_required_edge_depth_two.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_nested_required_edge_depth_two.ir.ron index 75fdea17..c16b388a 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_nested_required_edge_depth_two.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_nested_required_edge_depth_two.ir.ron @@ -46,11 +46,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(4), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_nested_required_edge_depth_two.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_nested_required_edge_depth_two.trace.ron index 5e734933..c3d9783c 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_nested_required_edge_depth_two.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_nested_required_edge_depth_two.trace.ron @@ -616,11 +616,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(4), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_required_edge_depth_one.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_required_edge_depth_one.ir.ron index 08f1c1af..e9040760 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_required_edge_depth_one.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_required_edge_depth_one.ir.ron @@ -36,11 +36,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_required_edge_depth_one.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_required_edge_depth_one.trace.ron index 2caf33ab..b1d15df8 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_required_edge_depth_one.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_required_edge_depth_one.trace.ron @@ -295,11 +295,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/required_properties.ir.ron b/trustfall_core/test_data/tests/valid_queries/required_properties.ir.ron index 00e48bd1..f730ac8b 100644 --- a/trustfall_core/test_data/tests/valid_queries/required_properties.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/required_properties.ir.ron @@ -67,21 +67,21 @@ Ok(TestIRQuery( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(2), field_name: "name", field_type: "String", - ), + )), }, ), ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/required_properties.trace.ron b/trustfall_core/test_data/tests/valid_queries/required_properties.trace.ron index 5369b197..019b6b19 100644 --- a/trustfall_core/test_data/tests/valid_queries/required_properties.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/required_properties.trace.ron @@ -950,21 +950,21 @@ TestInterpreterOutputTrace( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(2), field_name: "name", field_type: "String", - ), + )), }, ), ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/required_properties_filter_and_output.ir.ron b/trustfall_core/test_data/tests/valid_queries/required_properties_filter_and_output.ir.ron index a0840d39..1985e90e 100644 --- a/trustfall_core/test_data/tests/valid_queries/required_properties_filter_and_output.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/required_properties_filter_and_output.ir.ron @@ -33,16 +33,16 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "vowelsInName": ContextField( + )), + "vowelsInName": ContextField(ContextField( vertex_id: Vid(1), field_name: "vowelsInName", field_type: "[String]", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/required_properties_filter_and_output.trace.ron b/trustfall_core/test_data/tests/valid_queries/required_properties_filter_and_output.trace.ron index b218c001..d34153dc 100644 --- a/trustfall_core/test_data/tests/valid_queries/required_properties_filter_and_output.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/required_properties_filter_and_output.trace.ron @@ -199,16 +199,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "vowelsInName": ContextField( + )), + "vowelsInName": ContextField(ContextField( vertex_id: Vid(1), field_name: "vowelsInName", field_type: "[String]", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/root_coercion.ir.ron b/trustfall_core/test_data/tests/valid_queries/root_coercion.ir.ron index cddbd900..37d5593c 100644 --- a/trustfall_core/test_data/tests/valid_queries/root_coercion.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/root_coercion.ir.ron @@ -18,11 +18,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/root_coercion.trace.ron b/trustfall_core/test_data/tests/valid_queries/root_coercion.trace.ron index ef4bcc4e..1cb5ed46 100644 --- a/trustfall_core/test_data/tests/valid_queries/root_coercion.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/root_coercion.trace.ron @@ -521,11 +521,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/simple_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/simple_filter.ir.ron index 9e73c709..8aed2d46 100644 --- a/trustfall_core/test_data/tests/valid_queries/simple_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/simple_filter.ir.ron @@ -26,11 +26,11 @@ Ok(TestIRQuery( ), }, outputs: { - "number_name": ContextField( + "number_name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/simple_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/simple_filter.trace.ron index 686ba03d..3f3b4169 100644 --- a/trustfall_core/test_data/tests/valid_queries/simple_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/simple_filter.trace.ron @@ -215,11 +215,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "number_name": ContextField( + "number_name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/simple_query.ir.ron b/trustfall_core/test_data/tests/valid_queries/simple_query.ir.ron index 811259d0..39131796 100644 --- a/trustfall_core/test_data/tests/valid_queries/simple_query.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/simple_query.ir.ron @@ -17,11 +17,11 @@ Ok(TestIRQuery( ), }, outputs: { - "number_name": ContextField( + "number_name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/simple_query.trace.ron b/trustfall_core/test_data/tests/valid_queries/simple_query.trace.ron index ea42ef5f..bcbf3e3d 100644 --- a/trustfall_core/test_data/tests/valid_queries/simple_query.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/simple_query.trace.ron @@ -198,11 +198,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "number_name": ContextField( + "number_name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/static_and_dynamic_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/static_and_dynamic_filter.ir.ron index 9631a7ef..bf43bce9 100644 --- a/trustfall_core/test_data/tests/valid_queries/static_and_dynamic_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/static_and_dynamic_filter.ir.ron @@ -46,11 +46,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/static_and_dynamic_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/static_and_dynamic_filter.trace.ron index 2b41c34d..55993630 100644 --- a/trustfall_core/test_data/tests/valid_queries/static_and_dynamic_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/static_and_dynamic_filter.trace.ron @@ -533,11 +533,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/tag_and_filter_directives.ir.ron b/trustfall_core/test_data/tests/valid_queries/tag_and_filter_directives.ir.ron index 9683910c..f4b5570b 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_and_filter_directives.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_and_filter_directives.ir.ron @@ -64,16 +64,16 @@ Ok(TestIRQuery( ), }, outputs: { - "start": ContextField( + "start": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "target": ContextField( + )), + "target": ContextField(ContextField( vertex_id: Vid(4), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/tag_and_filter_directives.trace.ron b/trustfall_core/test_data/tests/valid_queries/tag_and_filter_directives.trace.ron index 38405be6..dec823eb 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_and_filter_directives.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_and_filter_directives.trace.ron @@ -1119,16 +1119,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "start": ContextField( + "start": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "target": ContextField( + )), + "target": ContextField(ContextField( vertex_id: Vid(4), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/tag_before_filter_in_same_scope.ir.ron b/trustfall_core/test_data/tests/valid_queries/tag_before_filter_in_same_scope.ir.ron index 3d57a840..d1d6e6f2 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_before_filter_in_same_scope.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_before_filter_in_same_scope.ir.ron @@ -21,11 +21,11 @@ Ok(TestIRQuery( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/tag_before_filter_in_same_scope.trace.ron b/trustfall_core/test_data/tests/valid_queries/tag_before_filter_in_same_scope.trace.ron index 38b736db..73ed08d1 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_before_filter_in_same_scope.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_before_filter_in_same_scope.trace.ron @@ -156,11 +156,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "name": ContextField( + "name": ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/tag_name_in_prefixed_vertex.ir.ron b/trustfall_core/test_data/tests/valid_queries/tag_name_in_prefixed_vertex.ir.ron index f29088f0..fb545301 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_name_in_prefixed_vertex.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_name_in_prefixed_vertex.ir.ron @@ -59,16 +59,16 @@ Ok(TestIRQuery( ), }, outputs: { - "m1_value": ContextField( + "m1_value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "m2_value": ContextField( + )), + "m2_value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/tag_name_in_prefixed_vertex.trace.ron b/trustfall_core/test_data/tests/valid_queries/tag_name_in_prefixed_vertex.trace.ron index 9c299deb..d0f4233b 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_name_in_prefixed_vertex.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_name_in_prefixed_vertex.trace.ron @@ -1636,16 +1636,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "m1_value": ContextField( + "m1_value": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "m2_value": ContextField( + )), + "m2_value": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional.ir.ron b/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional.ir.ron index 2c96a8a4..55faa0b7 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional.ir.ron @@ -104,21 +104,21 @@ Ok(TestIRQuery( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "start": ContextField( + )), + "start": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "succ": ContextField( + )), + "succ": ContextField(ContextField( vertex_id: Vid(7), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional.trace.ron b/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional.trace.ron index 5274b433..9d064e15 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional.trace.ron @@ -2207,21 +2207,21 @@ TestInterpreterOutputTrace( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "start": ContextField( + )), + "start": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), - "succ": ContextField( + )), + "succ": ContextField(ContextField( vertex_id: Vid(7), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional_used_in_fold.ir.ron b/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional_used_in_fold.ir.ron index 2a89476e..48f07923 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional_used_in_fold.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional_used_in_fold.ir.ron @@ -108,11 +108,11 @@ Ok(TestIRQuery( ), }, outputs: { - "succ": ContextField( + "succ": ContextField(ContextField( vertex_id: Vid(7), field_name: "value", field_type: "Int", - ), + )), }, ), imported_tags: [ @@ -125,16 +125,16 @@ Ok(TestIRQuery( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "start": ContextField( + )), + "start": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional_used_in_fold.trace.ron b/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional_used_in_fold.trace.ron index 4bc2eb55..6d71524e 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional_used_in_fold.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional_used_in_fold.trace.ron @@ -2509,11 +2509,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "succ": ContextField( + "succ": ContextField(ContextField( vertex_id: Vid(7), field_name: "value", field_type: "Int", - ), + )), }, ), imported_tags: [ @@ -2526,16 +2526,16 @@ TestInterpreterOutputTrace( ), }, outputs: { - "mult": ContextField( + "mult": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "start": ContextField( + )), + "start": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/typename_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/typename_filter.ir.ron index 0b1213fc..d811978b 100644 --- a/trustfall_core/test_data/tests/valid_queries/typename_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/typename_filter.ir.ron @@ -26,11 +26,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/typename_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/typename_filter.trace.ron index 49b2518d..5415aefa 100644 --- a/trustfall_core/test_data/tests/valid_queries/typename_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/typename_filter.trace.ron @@ -163,11 +163,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), variables: { diff --git a/trustfall_core/test_data/tests/valid_queries/typename_output.ir.ron b/trustfall_core/test_data/tests/valid_queries/typename_output.ir.ron index f14affc9..c96ea4df 100644 --- a/trustfall_core/test_data/tests/valid_queries/typename_output.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/typename_output.ir.ron @@ -11,11 +11,11 @@ Ok(TestIRQuery( ), }, outputs: { - "__typename": ContextField( + "__typename": ContextField(ContextField( vertex_id: Vid(1), field_name: "__typename", field_type: "String!", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/typename_output.trace.ron b/trustfall_core/test_data/tests/valid_queries/typename_output.trace.ron index 661b7061..19c2b681 100644 --- a/trustfall_core/test_data/tests/valid_queries/typename_output.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/typename_output.trace.ron @@ -81,11 +81,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "__typename": ContextField( + "__typename": ContextField(ContextField( vertex_id: Vid(1), field_name: "__typename", field_type: "String!", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/typename_output_on_final_type.ir.ron b/trustfall_core/test_data/tests/valid_queries/typename_output_on_final_type.ir.ron index 0772dc72..e18f1b8b 100644 --- a/trustfall_core/test_data/tests/valid_queries/typename_output_on_final_type.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/typename_output_on_final_type.ir.ron @@ -11,11 +11,11 @@ Ok(TestIRQuery( ), }, outputs: { - "__typename": ContextField( + "__typename": ContextField(ContextField( vertex_id: Vid(1), field_name: "__typename", field_type: "String!", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/typename_output_on_final_type.trace.ron b/trustfall_core/test_data/tests/valid_queries/typename_output_on_final_type.trace.ron index f44bd7ea..fc76e41d 100644 --- a/trustfall_core/test_data/tests/valid_queries/typename_output_on_final_type.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/typename_output_on_final_type.trace.ron @@ -81,11 +81,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "__typename": ContextField( + "__typename": ContextField(ContextField( vertex_id: Vid(1), field_name: "__typename", field_type: "String!", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/typename_tag_and_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/typename_tag_and_filter.ir.ron index fb431edc..2b6fc733 100644 --- a/trustfall_core/test_data/tests/valid_queries/typename_tag_and_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/typename_tag_and_filter.ir.ron @@ -39,11 +39,11 @@ Ok(TestIRQuery( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/typename_tag_and_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/typename_tag_and_filter.trace.ron index 3a10ea8e..f5c9ef62 100644 --- a/trustfall_core/test_data/tests/valid_queries/typename_tag_and_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/typename_tag_and_filter.trace.ron @@ -370,11 +370,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "value": ContextField( + "value": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_doubly_nested_folds.ir.ron b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_doubly_nested_folds.ir.ron index 339908f1..3b44d4f5 100644 --- a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_doubly_nested_folds.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_doubly_nested_folds.ir.ron @@ -53,11 +53,11 @@ Ok(TestIRQuery( ), }, outputs: { - "next_successors": ContextField( + "next_successors": ContextField(ContextField( vertex_id: Vid(4), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -66,11 +66,11 @@ Ok(TestIRQuery( ), }, outputs: { - "successors": ContextField( + "successors": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -79,21 +79,21 @@ Ok(TestIRQuery( ), }, outputs: { - "predecessor": ContextField( + "predecessor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "zero": ContextField( + "zero": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_doubly_nested_folds.trace.ron b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_doubly_nested_folds.trace.ron index c101e17b..f3af8d71 100644 --- a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_doubly_nested_folds.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_doubly_nested_folds.trace.ron @@ -218,11 +218,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "next_successors": ContextField( + "next_successors": ContextField(ContextField( vertex_id: Vid(4), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -231,11 +231,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "successors": ContextField( + "successors": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -244,21 +244,21 @@ TestInterpreterOutputTrace( ), }, outputs: { - "predecessor": ContextField( + "predecessor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "zero": ContextField( + "zero": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_edge.ir.ron b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_edge.ir.ron index a83f62c7..e6efee4c 100644 --- a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_edge.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_edge.ir.ron @@ -37,26 +37,26 @@ Ok(TestIRQuery( ), }, outputs: { - "predecessor": ContextField( + "predecessor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "successors": ContextField( + )), + "successors": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "zero": ContextField( + "zero": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_edge.trace.ron b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_edge.trace.ron index ebb5aeb4..0d6788c9 100644 --- a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_edge.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_edge.trace.ron @@ -193,26 +193,26 @@ TestInterpreterOutputTrace( ), }, outputs: { - "predecessor": ContextField( + "predecessor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "successors": ContextField( + )), + "successors": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "zero": ContextField( + "zero": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_fold.ir.ron b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_fold.ir.ron index 2b965582..8cd34a31 100644 --- a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_fold.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_fold.ir.ron @@ -39,11 +39,11 @@ Ok(TestIRQuery( ), }, outputs: { - "successors": ContextField( + "successors": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -52,21 +52,21 @@ Ok(TestIRQuery( ), }, outputs: { - "predecessor": ContextField( + "predecessor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "zero": ContextField( + "zero": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_fold.trace.ron b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_fold.trace.ron index a74bb5d9..5e53dbae 100644 --- a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_fold.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_fold.trace.ron @@ -198,11 +198,11 @@ TestInterpreterOutputTrace( ), }, outputs: { - "successors": ContextField( + "successors": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), fold_specific_outputs: { @@ -211,21 +211,21 @@ TestInterpreterOutputTrace( ), }, outputs: { - "predecessor": ContextField( + "predecessor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "zero": ContextField( + "zero": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_optional_edge.ir.ron b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_optional_edge.ir.ron index f501b04a..47e4345b 100644 --- a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_optional_edge.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_optional_edge.ir.ron @@ -38,26 +38,26 @@ Ok(TestIRQuery( ), }, outputs: { - "predecessor": ContextField( + "predecessor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "successors": ContextField( + )), + "successors": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "zero": ContextField( + "zero": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), diff --git a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_optional_edge.trace.ron b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_optional_edge.trace.ron index 0b5341ea..c5c3ec30 100644 --- a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_optional_edge.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_optional_edge.trace.ron @@ -194,26 +194,26 @@ TestInterpreterOutputTrace( ), }, outputs: { - "predecessor": ContextField( + "predecessor": ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", - ), - "successors": ContextField( + )), + "successors": ContextField(ContextField( vertex_id: Vid(3), field_name: "value", field_type: "Int", - ), + )), }, ), ), }, outputs: { - "zero": ContextField( + "zero": ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", - ), + )), }, ), ), From c5e1bdf1d8211d5832a05056df1cda4063ceff91 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> Date: Tue, 11 Jun 2024 16:58:01 -0400 Subject: [PATCH 02/30] Accept `FieldRef` for fold-specific field outputs in IR. (#616) This lays the groundwork for allowing us to transform those values before outputting them. --- trustfall_core/src/frontend/mod.rs | 6 +- trustfall_core/src/interpreter/execution.rs | 70 +++++++++++-------- trustfall_core/src/ir/mod.rs | 13 +++- ...fold_count_filter_with_inner_filter.ir.ron | 6 +- ...d_count_filter_with_inner_filter.trace.ron | 6 +- .../fold_count_prefixed_output_name.ir.ron | 6 +- .../fold_count_prefixed_output_name.trace.ron | 6 +- ...d_count_tag_on_nonexistent_optional.ir.ron | 6 +- ...ount_tag_on_nonexistent_optional.trace.ron | 6 +- .../fold_count_tag_then_filter_on_fold.ir.ron | 6 +- ...ld_count_tag_then_filter_on_fold.trace.ron | 6 +- ...h_no_inner_output_with_count_output.ir.ron | 6 +- ...o_inner_output_with_count_output.trace.ron | 6 +- .../multiple_fold_count_outputs.ir.ron | 12 +++- .../multiple_fold_count_outputs.trace.ron | 12 +++- .../nested_fold_count_output.ir.ron | 12 +++- .../nested_fold_count_output.trace.ron | 12 +++- ...nexistent_optional_with_nested_fold.ir.ron | 6 +- ...istent_optional_with_nested_fold.trace.ron | 6 +- .../valid_queries/output_fold_count.ir.ron | 6 +- .../valid_queries/output_fold_count.trace.ron | 6 +- .../output_fold_count_multiple.ir.ron | 6 +- .../output_fold_count_multiple.trace.ron | 6 +- ...ement_fold_with_doubly_nested_folds.ir.ron | 12 +++- ...nt_fold_with_doubly_nested_folds.trace.ron | 12 +++- .../zero_element_fold_with_nested_fold.ir.ron | 6 +- ...ro_element_fold_with_nested_fold.trace.ron | 6 +- 27 files changed, 206 insertions(+), 63 deletions(-) diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index 5869dfc6..bb1d6ed9 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -1174,7 +1174,7 @@ where }; let prior_output_by_that_name = - fold_specific_outputs.insert(final_output_name.clone(), fold_specific_field.kind); + fold_specific_outputs.insert(final_output_name.clone(), field_ref.clone()); if let Some(prior_output_kind) = prior_output_by_that_name { errors.push(FrontendError::MultipleOutputsWithSameName(DuplicatedNamesConflict { duplicates: btreemap! { @@ -1189,9 +1189,7 @@ where for tag_directive in &transform_group.tag { let tag_name = tag_directive.name.as_ref().map(|x| x.as_ref()); if let Some(tag_name) = tag_name { - let field = FieldRef::FoldSpecificField(fold_specific_field.clone()); - - if let Err(e) = tags.register_tag(tag_name, field, component_path) { + if let Err(e) = tags.register_tag(tag_name, field_ref.clone(), component_path) { errors.push(FrontendError::MultipleTagsWithSameName(tag_name.to_string())); } } else { diff --git a/trustfall_core/src/interpreter/execution.rs b/trustfall_core/src/interpreter/execution.rs index 5a4b9715..7ef77a29 100644 --- a/trustfall_core/src/interpreter/execution.rs +++ b/trustfall_core/src/interpreter/execution.rs @@ -508,33 +508,42 @@ fn compute_fold<'query, AdapterT: Adapter<'query> + 'query>( // // For example, if `@filter(op: ">", value: ["$ten"])` is our only filter on the count // of the fold, we can stop computing the rest of the fold after seeing we have 11 elements. - let min_fold_size = - if let Some(min_fold_size) = get_min_fold_count_limit(carrier, fold.as_ref()) { - let no_outputs_in_fold = fold.component.outputs.is_empty(); - let has_output_on_fold_count = - fold.fold_specific_outputs.values().any(|x| *x == FoldSpecificFieldKind::Count); - let has_tag_on_fold_count = parent_component.vertices.values().any(|vertex| { - vertex.filters.iter().any(|filter| { - let Some(Argument::Tag(FieldRef::FoldSpecificField(tagged_fold_count))) = - filter.right() - else { - return false; - }; + let min_fold_size = if let Some(min_fold_size) = + get_min_fold_count_limit(carrier, fold.as_ref()) + { + let no_outputs_in_fold = fold.component.outputs.is_empty(); + let has_output_on_fold_count = fold.fold_specific_outputs.values().any(|x| { + x.refers_to_fold_specific_field() + .is_some_and(|f| f.kind == FoldSpecificFieldKind::Count) + }); + let has_tag_on_fold_count = parent_component.vertices.values().any(|vertex| { + // TODO: If we allow referencing the tagged values located inside a fold in filters + // outside that fold, then this logic will be buggy (not conservative enough). + // It currently assumes that use of tags on `@fold @transform(op: "count")` + // can only happen within the direct parent component of that fold. + vertex.filters.iter().any(|filter| { + let Some(Argument::Tag(field_ref)) = filter.right() else { + return false; + }; - tagged_fold_count.fold_root_vid == fold.to_vid - && tagged_fold_count.fold_eid == fold.eid - && tagged_fold_count.kind == FoldSpecificFieldKind::Count - }) - }); + let Some(fold_specific_field) = field_ref.refers_to_fold_specific_field() else { + return false; + }; - if no_outputs_in_fold && !has_output_on_fold_count && !has_tag_on_fold_count { - Some(min_fold_size) - } else { - None - } + fold_specific_field.fold_root_vid == fold.to_vid + && fold_specific_field.fold_eid == fold.eid + && fold_specific_field.kind == FoldSpecificFieldKind::Count + }) + }); + + if no_outputs_in_fold && !has_output_on_fold_count && !has_tag_on_fold_count { + Some(min_fold_size) } else { None - }; + } + } else { + None + }; let moved_fold = fold.clone(); let folded_iterator = edge_iterator.filter_map(move |(mut context, neighbors)| { @@ -607,14 +616,19 @@ mismatch on whether the fold below {expanding_from_vid:?} was inside an `@option ); // Add any fold-specific field outputs to the context's folded values. - for (output_name, fold_specific_field) in &fold.fold_specific_outputs { + for (output_name, field_ref) in &fold.fold_specific_outputs { // If the @fold is inside an @optional that doesn't exist, // its outputs should be `null` rather than empty lists (the usual for empty folds). // Transformed outputs should also be `null` rather than their usual transformed defaults. - let value = fold_elements.as_ref().map(|elements| match fold_specific_field { - FoldSpecificFieldKind::Count => { - ValueOrVec::Value(FieldValue::Uint64(elements.len() as u64)) - } + let value = fold_elements.as_ref().map(|elements| match field_ref { + FieldRef::FoldSpecificField(field) => match field.kind { + FoldSpecificFieldKind::Count => { + ValueOrVec::Value(FieldValue::Uint64(elements.len() as u64)) + } + }, + FieldRef::ContextField(_) => unreachable!( + "found ContextField inside a fold's fold-specific outputs: {fold:#?}" + ), }); ctx.folded_values .insert_or_error((fold_eid, output_name.clone()), value) diff --git a/trustfall_core/src/ir/mod.rs b/trustfall_core/src/ir/mod.rs index 9fbd3fad..a561a19b 100644 --- a/trustfall_core/src/ir/mod.rs +++ b/trustfall_core/src/ir/mod.rs @@ -209,8 +209,12 @@ pub struct IRFold { #[serde(default, skip_serializing_if = "Vec::is_empty")] pub imported_tags: Vec, + /// Outputs from this fold that are derived from fold-specific fields. + /// + /// All [`FieldRef`] values in the map are guaranteed to have + /// `[FieldRef].refers_to_fold_specific_field().is_some() == true`. #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub fold_specific_outputs: BTreeMap, FoldSpecificFieldKind>, + pub fold_specific_outputs: BTreeMap, FieldRef>, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub post_filters: Vec>, @@ -332,6 +336,13 @@ impl FieldRef { FieldRef::FoldSpecificField(f) => f.fold_root_vid, } } + + pub fn refers_to_fold_specific_field(&self) -> Option<&FoldSpecificField> { + match self { + FieldRef::ContextField(_) => None, + FieldRef::FoldSpecificField(fold_specific) => Some(fold_specific), + } + } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.ir.ron index fd975460..bcf5981e 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.ir.ron @@ -49,7 +49,11 @@ Ok(TestIRQuery( }, ), fold_specific_outputs: { - "primeFactorcount": Count, + "primeFactorcount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), }, post_filters: [ Equals(Count, Variable(VariableRef( diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.trace.ron index dd14a7c4..03ae4db7 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.trace.ron @@ -473,7 +473,11 @@ TestInterpreterOutputTrace( }, ), fold_specific_outputs: { - "primeFactorcount": Count, + "primeFactorcount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), }, post_filters: [ Equals(Count, Variable(VariableRef( diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_prefixed_output_name.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_prefixed_output_name.ir.ron index cb5c69e7..c5c092d6 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_prefixed_output_name.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_prefixed_output_name.ir.ron @@ -33,7 +33,11 @@ Ok(TestIRQuery( }, ), fold_specific_outputs: { - "factor_count": Count, + "factor_count": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_prefixed_output_name.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_prefixed_output_name.trace.ron index 7c8965ba..c106fc17 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_prefixed_output_name.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_prefixed_output_name.trace.ron @@ -171,7 +171,11 @@ TestInterpreterOutputTrace( }, ), fold_specific_outputs: { - "factor_count": Count, + "factor_count": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.ir.ron index f8e7e901..a245eef1 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.ir.ron @@ -74,7 +74,11 @@ Ok(TestIRQuery( }, ), fold_specific_outputs: { - "count": Count, + "count": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(3), + fold_root_vid: Vid(4), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.trace.ron index 4140717f..e2767d8e 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.trace.ron @@ -521,7 +521,11 @@ TestInterpreterOutputTrace( }, ), fold_specific_outputs: { - "count": Count, + "count": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(3), + fold_root_vid: Vid(4), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_then_filter_on_fold.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_then_filter_on_fold.ir.ron index a44d85cd..2d540a5e 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_then_filter_on_fold.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_then_filter_on_fold.ir.ron @@ -41,7 +41,11 @@ Ok(TestIRQuery( }, ), fold_specific_outputs: { - "predecessorcount": Count, + "predecessorcount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), }, post_filters: [ Equals(Count, Tag(FoldSpecificField(FoldSpecificField( diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_then_filter_on_fold.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_then_filter_on_fold.trace.ron index 3a0407be..0763dde6 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_then_filter_on_fold.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_then_filter_on_fold.trace.ron @@ -196,7 +196,11 @@ TestInterpreterOutputTrace( }, ), fold_specific_outputs: { - "predecessorcount": Count, + "predecessorcount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), }, post_filters: [ Equals(Count, Tag(FoldSpecificField(FoldSpecificField( diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.ir.ron index 83fb88e2..c4f88a26 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.ir.ron @@ -33,7 +33,11 @@ Ok(TestIRQuery( }, ), fold_specific_outputs: { - "primeFactorcount": Count, + "primeFactorcount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), }, post_filters: [ GreaterThanOrEqual(Count, Variable(VariableRef( diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.trace.ron index cf18fb99..6082ce2a 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.trace.ron @@ -304,7 +304,11 @@ TestInterpreterOutputTrace( }, ), fold_specific_outputs: { - "primeFactorcount": Count, + "primeFactorcount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), }, post_filters: [ GreaterThanOrEqual(Count, Variable(VariableRef( diff --git a/trustfall_core/test_data/tests/valid_queries/multiple_fold_count_outputs.ir.ron b/trustfall_core/test_data/tests/valid_queries/multiple_fold_count_outputs.ir.ron index e38c52fd..b99236bc 100644 --- a/trustfall_core/test_data/tests/valid_queries/multiple_fold_count_outputs.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/multiple_fold_count_outputs.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), fold_specific_outputs: { - "primeFactorcount": Count, + "primeFactorcount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), }, ), Eid(2): IRFold( @@ -70,7 +74,11 @@ Ok(TestIRQuery( }, ), fold_specific_outputs: { - "multiplecount": Count, + "multiplecount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/multiple_fold_count_outputs.trace.ron b/trustfall_core/test_data/tests/valid_queries/multiple_fold_count_outputs.trace.ron index f9822420..42ebfcff 100644 --- a/trustfall_core/test_data/tests/valid_queries/multiple_fold_count_outputs.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/multiple_fold_count_outputs.trace.ron @@ -722,7 +722,11 @@ TestInterpreterOutputTrace( }, ), fold_specific_outputs: { - "primeFactorcount": Count, + "primeFactorcount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), }, ), Eid(2): IRFold( @@ -752,7 +756,11 @@ TestInterpreterOutputTrace( }, ), fold_specific_outputs: { - "multiplecount": Count, + "multiplecount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/nested_fold_count_output.ir.ron b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_output.ir.ron index c2c77620..815f2ed6 100644 --- a/trustfall_core/test_data/tests/valid_queries/nested_fold_count_output.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_output.ir.ron @@ -59,7 +59,11 @@ Ok(TestIRQuery( }, ), fold_specific_outputs: { - "multiplecount": Count, + "multiplecount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), }, ), }, @@ -72,7 +76,11 @@ Ok(TestIRQuery( }, ), fold_specific_outputs: { - "primeFactorcount": Count, + "primeFactorcount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/nested_fold_count_output.trace.ron b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_output.trace.ron index 3ca0e131..f6045dab 100644 --- a/trustfall_core/test_data/tests/valid_queries/nested_fold_count_output.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_output.trace.ron @@ -980,7 +980,11 @@ TestInterpreterOutputTrace( }, ), fold_specific_outputs: { - "multiplecount": Count, + "multiplecount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), }, ), }, @@ -993,7 +997,11 @@ TestInterpreterOutputTrace( }, ), fold_specific_outputs: { - "primeFactorcount": Count, + "primeFactorcount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_fold.ir.ron b/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_fold.ir.ron index f7d46bae..9e592736 100644 --- a/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_fold.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_fold.ir.ron @@ -46,7 +46,11 @@ Ok(TestIRQuery( }, ), fold_specific_outputs: { - "successor_counts": Count, + "successor_counts": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_fold.trace.ron b/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_fold.trace.ron index a8d2d15b..dfffc329 100644 --- a/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_fold.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/nonexistent_optional_with_nested_fold.trace.ron @@ -299,7 +299,11 @@ TestInterpreterOutputTrace( }, ), fold_specific_outputs: { - "successor_counts": Count, + "successor_counts": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/output_fold_count.ir.ron b/trustfall_core/test_data/tests/valid_queries/output_fold_count.ir.ron index d84dfbef..10cb5f2d 100644 --- a/trustfall_core/test_data/tests/valid_queries/output_fold_count.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/output_fold_count.ir.ron @@ -33,7 +33,11 @@ Ok(TestIRQuery( }, ), fold_specific_outputs: { - "primeFactorcount": Count, + "primeFactorcount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/output_fold_count.trace.ron b/trustfall_core/test_data/tests/valid_queries/output_fold_count.trace.ron index 900641e6..a17fd90a 100644 --- a/trustfall_core/test_data/tests/valid_queries/output_fold_count.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/output_fold_count.trace.ron @@ -257,7 +257,11 @@ TestInterpreterOutputTrace( }, ), fold_specific_outputs: { - "primeFactorcount": Count, + "primeFactorcount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/output_fold_count_multiple.ir.ron b/trustfall_core/test_data/tests/valid_queries/output_fold_count_multiple.ir.ron index ea14bcbb..52e0b833 100644 --- a/trustfall_core/test_data/tests/valid_queries/output_fold_count_multiple.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/output_fold_count_multiple.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), fold_specific_outputs: { - "primeFactorcount": Count, + "primeFactorcount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/output_fold_count_multiple.trace.ron b/trustfall_core/test_data/tests/valid_queries/output_fold_count_multiple.trace.ron index 6409ca6e..54c54f33 100644 --- a/trustfall_core/test_data/tests/valid_queries/output_fold_count_multiple.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/output_fold_count_multiple.trace.ron @@ -921,7 +921,11 @@ TestInterpreterOutputTrace( }, ), fold_specific_outputs: { - "primeFactorcount": Count, + "primeFactorcount": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_doubly_nested_folds.ir.ron b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_doubly_nested_folds.ir.ron index 3b44d4f5..fe3a9086 100644 --- a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_doubly_nested_folds.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_doubly_nested_folds.ir.ron @@ -61,7 +61,11 @@ Ok(TestIRQuery( }, ), fold_specific_outputs: { - "next_successor_counts": Count, + "next_successor_counts": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(3), + fold_root_vid: Vid(4), + kind: Count, + )), }, ), }, @@ -74,7 +78,11 @@ Ok(TestIRQuery( }, ), fold_specific_outputs: { - "successor_counts": Count, + "successor_counts": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_doubly_nested_folds.trace.ron b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_doubly_nested_folds.trace.ron index f3af8d71..5d7b0553 100644 --- a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_doubly_nested_folds.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_doubly_nested_folds.trace.ron @@ -226,7 +226,11 @@ TestInterpreterOutputTrace( }, ), fold_specific_outputs: { - "next_successor_counts": Count, + "next_successor_counts": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(3), + fold_root_vid: Vid(4), + kind: Count, + )), }, ), }, @@ -239,7 +243,11 @@ TestInterpreterOutputTrace( }, ), fold_specific_outputs: { - "successor_counts": Count, + "successor_counts": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_fold.ir.ron b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_fold.ir.ron index 8cd34a31..9670444a 100644 --- a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_fold.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_fold.ir.ron @@ -47,7 +47,11 @@ Ok(TestIRQuery( }, ), fold_specific_outputs: { - "successor_counts": Count, + "successor_counts": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), }, ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_fold.trace.ron b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_fold.trace.ron index 5e53dbae..8ffeeb6f 100644 --- a/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_fold.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/zero_element_fold_with_nested_fold.trace.ron @@ -206,7 +206,11 @@ TestInterpreterOutputTrace( }, ), fold_specific_outputs: { - "successor_counts": Count, + "successor_counts": FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), }, ), }, From e9be0eab0641f3731b5bef13a1f44bb077c48280 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> Date: Tue, 11 Jun 2024 21:53:14 -0400 Subject: [PATCH 03/30] Allow `FieldRef` in fold post-filtering operations. (#618) This lays the groundwork for `@fold @transform(op: "count")` followed by more `@transform` operations before a subsequent `@filter`. --- trustfall_core/src/frontend/mod.rs | 2 +- trustfall_core/src/interpreter/execution.rs | 95 +++++++++++++------ .../src/interpreter/hints/filters.rs | 9 +- trustfall_core/src/ir/mod.rs | 10 +- .../valid_queries/fold_count_filter.ir.ron | 6 +- .../valid_queries/fold_count_filter.trace.ron | 6 +- ...er_early_prune_max_fold_size_equals.ir.ron | 6 +- ...early_prune_max_fold_size_equals.trace.ron | 6 +- ...lter_early_prune_max_fold_size_less.ir.ron | 6 +- ...r_early_prune_max_fold_size_less.trace.ron | 6 +- ...rly_prune_max_fold_size_less_equals.ir.ron | 6 +- ..._prune_max_fold_size_less_equals.trace.ron | 6 +- ...er_early_prune_max_fold_size_one_of.ir.ron | 6 +- ...early_prune_max_fold_size_one_of.trace.ron | 6 +- ...d_count_filter_eq_with_negative_arg.ir.ron | 6 +- ...ount_filter_eq_with_negative_arg.trace.ron | 6 +- ...d_count_filter_gt_with_negative_arg.ir.ron | 6 +- ...ount_filter_gt_with_negative_arg.trace.ron | 6 +- ..._count_filter_gte_with_negative_arg.ir.ron | 6 +- ...unt_filter_gte_with_negative_arg.trace.ron | 6 +- .../fold_count_filter_lt_over_i64_max.ir.ron | 6 +- ...old_count_filter_lt_over_i64_max.trace.ron | 6 +- ...d_count_filter_lt_with_negative_arg.ir.ron | 6 +- ...ount_filter_lt_with_negative_arg.trace.ron | 6 +- ..._count_filter_lte_with_negative_arg.ir.ron | 6 +- ...unt_filter_lte_with_negative_arg.trace.ron | 6 +- ...unt_filter_not_eq_with_negative_arg.ir.ron | 6 +- ..._filter_not_eq_with_negative_arg.trace.ron | 6 +- ...filter_not_one_of_with_negative_arg.ir.ron | 6 +- ...ter_not_one_of_with_negative_arg.trace.ron | 6 +- .../fold_count_filter_on_a_tag.ir.ron | 12 ++- .../fold_count_filter_on_a_tag.trace.ron | 12 ++- ...unt_filter_one_of_with_negative_arg.ir.ron | 6 +- ..._filter_one_of_with_negative_arg.trace.ron | 6 +- ...unt_filter_with_dominated_filter_gt.ir.ron | 12 ++- ..._filter_with_dominated_filter_gt.trace.ron | 12 ++- ...unt_filter_with_dominated_filter_lt.ir.ron | 12 ++- ..._filter_with_dominated_filter_lt.trace.ron | 12 ++- ...count_filter_with_impossible_filter.ir.ron | 12 ++- ...nt_filter_with_impossible_filter.trace.ron | 12 ++- ...fold_count_filter_with_inner_filter.ir.ron | 6 +- ...d_count_filter_with_inner_filter.trace.ron | 6 +- .../fold_count_tag_then_filter_on_fold.ir.ron | 6 +- ...ld_count_tag_then_filter_on_fold.trace.ron | 6 +- ..._and_nested_filter_dependent_on_tag.ir.ron | 6 +- ...d_nested_filter_dependent_on_tag.trace.ron | 6 +- ...with_count_filter_and_nested_filter.ir.ron | 6 +- ...h_count_filter_and_nested_filter.trace.ron | 6 +- ...t_filter_and_nested_filter_with_tag.ir.ron | 6 +- ...ilter_and_nested_filter_with_tag.trace.ron | 6 +- ...h_no_inner_output_with_count_output.ir.ron | 6 +- ...o_inner_output_with_count_output.trace.ron | 6 +- ...with_no_inner_output_with_count_tag.ir.ron | 6 +- ...h_no_inner_output_with_count_tag.trace.ron | 6 +- .../valid_queries/fold_with_no_outputs.ir.ron | 6 +- .../fold_with_no_outputs.trace.ron | 6 +- .../fold_with_no_outputs_elided_braces.ir.ron | 6 +- ...ld_with_no_outputs_elided_braces.trace.ron | 6 +- ...s_transform_and_filter_greater_than.ir.ron | 6 +- ...ransform_and_filter_greater_than.trace.ron | 6 +- ...puts_transform_and_filter_less_than.ir.ron | 6 +- ...s_transform_and_filter_less_than.trace.ron | 6 +- ...outputs_transform_and_filter_one_of.ir.ron | 6 +- ...puts_transform_and_filter_one_of.trace.ron | 6 +- ...s_transform_and_filter_greater_than.ir.ron | 6 +- ...ransform_and_filter_greater_than.trace.ron | 6 +- ...puts_transform_and_filter_less_than.ir.ron | 6 +- ...s_transform_and_filter_less_than.trace.ron | 6 +- .../nested_fold_with_no_outputs.ir.ron | 12 ++- .../nested_fold_with_no_outputs.trace.ron | 12 ++- 70 files changed, 463 insertions(+), 109 deletions(-) diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index bb1d6ed9..ec11a460 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -1141,7 +1141,7 @@ where component_path, tags, starting_vid, - fold_specific_field.kind, + field_ref.clone(), filter_directive, ) { Ok(filter) => post_filters.push(filter), diff --git a/trustfall_core/src/interpreter/execution.rs b/trustfall_core/src/interpreter/execution.rs index 7ef77a29..34ed29a7 100644 --- a/trustfall_core/src/interpreter/execution.rs +++ b/trustfall_core/src/interpreter/execution.rs @@ -270,26 +270,42 @@ fn usize_from_field_value(field_value: &FieldValue) -> Option { } } -/// If this IRFold has a filter on the folded element count, and that filter imposes +/// If this [`IRFold`] has a filter on the folded element count, and that filter imposes /// a max size that can be statically determined, return that max size so it can -/// be used for further optimizations. Otherwise, return None. +/// be used for further optimizations. Otherwise, return `None`. fn get_max_fold_count_limit(carrier: &mut QueryCarrier, fold: &IRFold) -> Option { let mut result: Option = None; let query_arguments = &carrier.query.as_ref().expect("query was not returned").arguments; for post_fold_filter in fold.post_filters.iter() { + let left = post_fold_filter.left(); + if !left + .refers_to_fold_specific_field() + .is_some_and(|f| f.kind == FoldSpecificFieldKind::Count) + { + // This filter is not using the count of the fold. + continue; + } + + if !matches!(left, FieldRef::FoldSpecificField(f) if f.kind == FoldSpecificFieldKind::Count) + { + // The filter expression is doing something more complex than we can currently analyze. + // Conservatively return `None` to disable optimizations here. + // + // TODO: Once `@transform` may be applied to property-like values, update the analysis + // here to be able to optimize in such cases as well. + return None; + } + let next_limit = match post_fold_filter { - Operation::Equals(FoldSpecificFieldKind::Count, Argument::Variable(var_ref)) - | Operation::LessThanOrEqual( - FoldSpecificFieldKind::Count, - Argument::Variable(var_ref), - ) => { + Operation::Equals(_, Argument::Variable(var_ref)) + | Operation::LessThanOrEqual(_, Argument::Variable(var_ref)) => { let variable_value = usize_from_field_value(&query_arguments[&var_ref.variable_name]) .expect("for field value to be coercible to usize"); Some(variable_value) } - Operation::LessThan(FoldSpecificFieldKind::Count, Argument::Variable(var_ref)) => { + Operation::LessThan(_, Argument::Variable(var_ref)) => { let variable_value = usize_from_field_value(&query_arguments[&var_ref.variable_name]) .expect("for field value to be coercible to usize"); @@ -300,7 +316,7 @@ fn get_max_fold_count_limit(carrier: &mut QueryCarrier, fold: &IRFold) -> Option // The later full application of filters ensures correctness. Some(variable_value.saturating_sub(1)) } - Operation::OneOf(FoldSpecificFieldKind::Count, Argument::Variable(var_ref)) => { + Operation::OneOf(_, Argument::Variable(var_ref)) => { match &query_arguments[&var_ref.variable_name] { FieldValue::List(v) => v .iter() @@ -325,25 +341,41 @@ fn get_max_fold_count_limit(carrier: &mut QueryCarrier, fold: &IRFold) -> Option result } -/// If this IRFold has a filter on the folded element count, and that filter imposes +/// If this [`IRFold`] has a filter on the folded element count, and that filter imposes /// a min size that can be statically determined, return that min size so it can -/// be used for further optimizations. Otherwise, return None. +/// be used for further optimizations. Otherwise, return `None`. fn get_min_fold_count_limit(carrier: &mut QueryCarrier, fold: &IRFold) -> Option { let mut result: Option = None; let query_arguments = &carrier.query.as_ref().expect("query was not returned").arguments; for post_fold_filter in fold.post_filters.iter() { + let left = post_fold_filter.left(); + if !left + .refers_to_fold_specific_field() + .is_some_and(|f| f.kind == FoldSpecificFieldKind::Count) + { + // This filter is not using the count of the fold. + continue; + } + + if !matches!(left, FieldRef::FoldSpecificField(f) if f.kind == FoldSpecificFieldKind::Count) + { + // The filter expression is doing something more complex than we can currently analyze. + // Conservatively return `None` to disable optimizations here. + // + // TODO: Once `@transform` may be applied to property-like values, update the analysis + // here to be able to optimize in such cases as well. + return None; + } + let next_limit = match post_fold_filter { - Operation::GreaterThanOrEqual( - FoldSpecificFieldKind::Count, - Argument::Variable(var_ref), - ) => { + Operation::GreaterThanOrEqual(_, Argument::Variable(var_ref)) => { let variable_value = usize_from_field_value(&query_arguments[&var_ref.variable_name]) .expect("for field value to be coercible to usize"); Some(variable_value) } - Operation::GreaterThan(FoldSpecificFieldKind::Count, Argument::Variable(var_ref)) => { + Operation::GreaterThan(_, Argument::Variable(var_ref)) => { let variable_value = usize_from_field_value(&query_arguments[&var_ref.variable_name]) .expect("for field value to be coercible to usize"); @@ -587,15 +619,24 @@ fn compute_fold<'query, AdapterT: Adapter<'query> + 'query>( let mut post_filtered_iterator: ContextIterator<'query, AdapterT::Vertex> = Box::new(folded_iterator); for post_fold_filter in fold.post_filters.iter() { - post_filtered_iterator = apply_fold_specific_filter( - adapter.as_ref(), - carrier, - parent_component, - fold.as_ref(), - expanding_from.vid, - post_fold_filter, - post_filtered_iterator, - ); + let left = post_fold_filter.left(); + match left { + FieldRef::ContextField(_) => { + unreachable!("unexpectedly found a fold post-filtering step that references a ContextField: {fold:#?}"); + } + FieldRef::FoldSpecificField(fold_specific_field) => { + let remapped_operation = post_fold_filter.map(|_| fold_specific_field.kind, |x| x); + post_filtered_iterator = apply_fold_specific_filter( + adapter.as_ref(), + carrier, + parent_component, + fold.as_ref(), + expanding_from.vid, + &remapped_operation, + post_filtered_iterator, + ); + } + } } // Compute the outputs from this fold. @@ -772,7 +813,7 @@ fn apply_fold_specific_filter<'query, AdapterT: Adapter<'query>>( component: &IRQueryComponent, fold: &IRFold, current_vid: Vid, - filter: &Operation, + filter: &Operation, iterator: ContextIterator<'query, AdapterT::Vertex>, ) -> ContextIterator<'query, AdapterT::Vertex> { let fold_specific_field = filter.left(); @@ -792,7 +833,7 @@ fn apply_fold_specific_filter<'query, AdapterT: Adapter<'query>>( carrier, component, current_vid, - &filter.map(|_| (), |r| r), + &filter.map(|_| (), |r| *r), field_iterator, ) } diff --git a/trustfall_core/src/interpreter/hints/filters.rs b/trustfall_core/src/interpreter/hints/filters.rs index a80f5bf2..ae7a028e 100644 --- a/trustfall_core/src/interpreter/hints/filters.rs +++ b/trustfall_core/src/interpreter/hints/filters.rs @@ -2,7 +2,7 @@ use std::{borrow::Cow, collections::BTreeMap, fmt::Debug, ops::Bound, sync::Arc} use itertools::Itertools; -use crate::ir::{Argument, FieldValue, FoldSpecificFieldKind, IRFold, Operation}; +use crate::ir::{Argument, FieldRef, FieldValue, FoldSpecificFieldKind, IRFold, Operation}; use super::{candidates::NullableValue, CandidateValue, Range}; @@ -114,8 +114,11 @@ pub(super) fn fold_requires_at_least_one_element( query_variables: &BTreeMap, FieldValue>, fold: &IRFold, ) -> bool { - let relevant_filters = - fold.post_filters.iter().filter(|op| matches!(op.left(), FoldSpecificFieldKind::Count)); + // TODO: When we support applying `@transform` to property-like values, we can update this logic + // to be smarter and less conservative. + let relevant_filters = fold.post_filters.iter().filter(|op| { + matches!(op.left(), FieldRef::FoldSpecificField(f) if f.kind == FoldSpecificFieldKind::Count) + }); let is_subject_field_nullable = false; // the "count" value can't be null candidate_from_statically_evaluated_filters( relevant_filters, diff --git a/trustfall_core/src/ir/mod.rs b/trustfall_core/src/ir/mod.rs index a561a19b..2e698911 100644 --- a/trustfall_core/src/ir/mod.rs +++ b/trustfall_core/src/ir/mod.rs @@ -212,12 +212,18 @@ pub struct IRFold { /// Outputs from this fold that are derived from fold-specific fields. /// /// All [`FieldRef`] values in the map are guaranteed to have - /// `[FieldRef].refers_to_fold_specific_field().is_some() == true`. + /// `FieldRef.refers_to_fold_specific_field().is_some() == true`. #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] pub fold_specific_outputs: BTreeMap, FieldRef>, + /// Filters that are applied on the fold as a whole. + /// + /// For example, as in `@fold @transform(op: "count") @filter(op: "=", value: ["$zero"])`. + /// + /// All [`FieldRef`] values inside each [`Operation`] within the `Vec` are guaranteed to have + /// `FieldRef.refers_to_fold_specific_field().is_some() == true`. #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub post_filters: Vec>, + pub post_filters: Vec>, } #[non_exhaustive] diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter.ir.ron index 57cc9983..8252f415 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - Equals(Count, Variable(VariableRef( + Equals(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter.trace.ron index 1b169725..a277c408 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter.trace.ron @@ -463,7 +463,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - Equals(Count, Variable(VariableRef( + Equals(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_equals.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_equals.ir.ron index 497d422d..4d53a242 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_equals.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_equals.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - Equals(Count, Variable(VariableRef( + Equals(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "one", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_equals.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_equals.trace.ron index e71eeb5d..238c0f0f 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_equals.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_equals.trace.ron @@ -421,7 +421,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - Equals(Count, Variable(VariableRef( + Equals(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "one", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less.ir.ron index 8ce0e2a4..ebd26520 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - LessThan(Count, Variable(VariableRef( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less.trace.ron index 1c108c3e..d7a28653 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less.trace.ron @@ -421,7 +421,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - LessThan(Count, Variable(VariableRef( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less_equals.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less_equals.ir.ron index 3260a6f1..83351975 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less_equals.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less_equals.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - LessThanOrEqual(Count, Variable(VariableRef( + LessThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "one", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less_equals.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less_equals.trace.ron index 2515967f..7bdc47c9 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less_equals.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_less_equals.trace.ron @@ -421,7 +421,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - LessThanOrEqual(Count, Variable(VariableRef( + LessThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "one", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_one_of.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_one_of.ir.ron index 77538dbc..8890f345 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_one_of.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_one_of.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - OneOf(Count, Variable(VariableRef( + OneOf(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "counts", variable_type: "[Int!]!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_one_of.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_one_of.trace.ron index 7fcba8ca..4b37df69 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_one_of.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_early_prune_max_fold_size_one_of.trace.ron @@ -421,7 +421,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - OneOf(Count, Variable(VariableRef( + OneOf(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "counts", variable_type: "[Int!]!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_eq_with_negative_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_eq_with_negative_arg.ir.ron index 0a3fecbd..0ae125ab 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_eq_with_negative_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_eq_with_negative_arg.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - Equals(Count, Variable(VariableRef( + Equals(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "neg_two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_eq_with_negative_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_eq_with_negative_arg.trace.ron index 97491cd7..bd44ca83 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_eq_with_negative_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_eq_with_negative_arg.trace.ron @@ -286,7 +286,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - Equals(Count, Variable(VariableRef( + Equals(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "neg_two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gt_with_negative_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gt_with_negative_arg.ir.ron index 3f3982cd..45d152cd 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gt_with_negative_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gt_with_negative_arg.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - GreaterThan(Count, Variable(VariableRef( + GreaterThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "neg_two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gt_with_negative_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gt_with_negative_arg.trace.ron index 0557b657..2b562342 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gt_with_negative_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gt_with_negative_arg.trace.ron @@ -581,7 +581,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - GreaterThan(Count, Variable(VariableRef( + GreaterThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "neg_two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gte_with_negative_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gte_with_negative_arg.ir.ron index bfec2fff..2b382dda 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gte_with_negative_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gte_with_negative_arg.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "neg_two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gte_with_negative_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gte_with_negative_arg.trace.ron index 10512569..a31b9fe6 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gte_with_negative_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_gte_with_negative_arg.trace.ron @@ -581,7 +581,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "neg_two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_over_i64_max.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_over_i64_max.ir.ron index aaf6460b..cc87fbf3 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_over_i64_max.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_over_i64_max.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - LessThan(Count, Variable(VariableRef( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "over_i64_max", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_over_i64_max.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_over_i64_max.trace.ron index 657e6270..22113669 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_over_i64_max.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_over_i64_max.trace.ron @@ -581,7 +581,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - LessThan(Count, Variable(VariableRef( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "over_i64_max", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_with_negative_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_with_negative_arg.ir.ron index 451e52eb..cd306485 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_with_negative_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_with_negative_arg.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - LessThan(Count, Variable(VariableRef( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "neg_two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_with_negative_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_with_negative_arg.trace.ron index 67944649..ea93f6ec 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_with_negative_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lt_with_negative_arg.trace.ron @@ -286,7 +286,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - LessThan(Count, Variable(VariableRef( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "neg_two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lte_with_negative_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lte_with_negative_arg.ir.ron index a2dc184a..9c08a38b 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lte_with_negative_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lte_with_negative_arg.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - LessThanOrEqual(Count, Variable(VariableRef( + LessThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "neg_two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lte_with_negative_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lte_with_negative_arg.trace.ron index 126ed95e..40ee3f14 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lte_with_negative_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_lte_with_negative_arg.trace.ron @@ -286,7 +286,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - LessThanOrEqual(Count, Variable(VariableRef( + LessThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "neg_two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_eq_with_negative_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_eq_with_negative_arg.ir.ron index e2f58522..4ad045c2 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_eq_with_negative_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_eq_with_negative_arg.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - NotEquals(Count, Variable(VariableRef( + NotEquals(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "neg_two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_eq_with_negative_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_eq_with_negative_arg.trace.ron index a5e56d4a..f4f89dd4 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_eq_with_negative_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_eq_with_negative_arg.trace.ron @@ -581,7 +581,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - NotEquals(Count, Variable(VariableRef( + NotEquals(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "neg_two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_one_of_with_negative_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_one_of_with_negative_arg.ir.ron index 5339e776..91c7dd38 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_one_of_with_negative_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_one_of_with_negative_arg.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - NotOneOf(Count, Variable(VariableRef( + NotOneOf(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "neg_two", variable_type: "[Int!]!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_one_of_with_negative_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_one_of_with_negative_arg.trace.ron index 73cb787b..12311a16 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_one_of_with_negative_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_not_one_of_with_negative_arg.trace.ron @@ -581,7 +581,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - NotOneOf(Count, Variable(VariableRef( + NotOneOf(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "neg_two", variable_type: "[Int!]!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.ir.ron index ae8a35c6..ae307bc2 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.ir.ron @@ -52,11 +52,19 @@ Ok(TestIRQuery( }, ), post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), Variable(VariableRef( variable_name: "zero", variable_type: "Int!", ))), - LessThan(Count, Tag(ContextField(ContextField( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.trace.ron index 5cd3d96f..a5cc8663 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.trace.ron @@ -412,11 +412,19 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), Variable(VariableRef( variable_name: "zero", variable_type: "Int!", ))), - LessThan(Count, Tag(ContextField(ContextField( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_one_of_with_negative_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_one_of_with_negative_arg.ir.ron index 17a40573..a18df128 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_one_of_with_negative_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_one_of_with_negative_arg.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - OneOf(Count, Variable(VariableRef( + OneOf(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "neg_two", variable_type: "[Int!]!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_one_of_with_negative_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_one_of_with_negative_arg.trace.ron index f162545d..056373ba 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_one_of_with_negative_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_one_of_with_negative_arg.trace.ron @@ -286,7 +286,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - OneOf(Count, Variable(VariableRef( + OneOf(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "neg_two", variable_type: "[Int!]!", ))), 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 index 185ac597..0d3611a4 100644 --- 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 @@ -33,11 +33,19 @@ Ok(TestIRQuery( }, ), post_filters: [ - GreaterThan(Count, Variable(VariableRef( + GreaterThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), - GreaterThan(Count, Variable(VariableRef( + GreaterThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "three", variable_type: "Int!", ))), 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 index 3ade64f8..6816a157 100644 --- 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 @@ -205,11 +205,19 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - GreaterThan(Count, Variable(VariableRef( + GreaterThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), - GreaterThan(Count, Variable(VariableRef( + GreaterThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "three", variable_type: "Int!", ))), 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 index 1c079c08..9ede3050 100644 --- 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 @@ -33,11 +33,19 @@ Ok(TestIRQuery( }, ), post_filters: [ - LessThan(Count, Variable(VariableRef( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "four", variable_type: "Int!", ))), - LessThan(Count, Variable(VariableRef( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), 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 index a9aa144e..92e81bb7 100644 --- 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 @@ -195,11 +195,19 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - LessThan(Count, Variable(VariableRef( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "four", variable_type: "Int!", ))), - LessThan(Count, Variable(VariableRef( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), 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 index 952e5fdf..1ec3ed88 100644 --- 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 @@ -33,11 +33,19 @@ Ok(TestIRQuery( }, ), post_filters: [ - GreaterThan(Count, Variable(VariableRef( + GreaterThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "six", variable_type: "Int!", ))), - LessThan(Count, Variable(VariableRef( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "five", variable_type: "Int!", ))), 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 index d2ce4669..6ec231e6 100644 --- 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 @@ -205,11 +205,19 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - GreaterThan(Count, Variable(VariableRef( + GreaterThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "six", variable_type: "Int!", ))), - LessThan(Count, Variable(VariableRef( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "five", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.ir.ron index bcf5981e..d5f1c3c8 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.ir.ron @@ -56,7 +56,11 @@ Ok(TestIRQuery( )), }, post_filters: [ - Equals(Count, Variable(VariableRef( + Equals(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.trace.ron index 03ae4db7..4fa207b4 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.trace.ron @@ -480,7 +480,11 @@ TestInterpreterOutputTrace( )), }, post_filters: [ - Equals(Count, Variable(VariableRef( + Equals(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_then_filter_on_fold.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_then_filter_on_fold.ir.ron index 2d540a5e..40e2ef7b 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_then_filter_on_fold.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_then_filter_on_fold.ir.ron @@ -48,7 +48,11 @@ Ok(TestIRQuery( )), }, post_filters: [ - Equals(Count, Tag(FoldSpecificField(FoldSpecificField( + Equals(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), Tag(FoldSpecificField(FoldSpecificField( fold_eid: Eid(1), fold_root_vid: Vid(2), kind: Count, diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_then_filter_on_fold.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_then_filter_on_fold.trace.ron index 0763dde6..3f9ad3d4 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_then_filter_on_fold.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_then_filter_on_fold.trace.ron @@ -203,7 +203,11 @@ TestInterpreterOutputTrace( )), }, post_filters: [ - Equals(Count, Tag(FoldSpecificField(FoldSpecificField( + Equals(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), Tag(FoldSpecificField(FoldSpecificField( fold_eid: Eid(1), fold_root_vid: Vid(2), kind: Count, diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.ir.ron index e803eb34..787c7301 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.ir.ron @@ -62,7 +62,11 @@ Ok(TestIRQuery( )), ], post_filters: [ - GreaterThanOrEqual(Count, Tag(ContextField(ContextField( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.trace.ron index 6c10a18b..7ceef5eb 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.trace.ron @@ -561,7 +561,11 @@ TestInterpreterOutputTrace( )), ], post_filters: [ - GreaterThanOrEqual(Count, Tag(ContextField(ContextField( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.ir.ron index c15997ad..18ad04a6 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.ir.ron @@ -54,7 +54,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), Variable(VariableRef( variable_name: "one", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.trace.ron index 131220c1..78b4d5c9 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.trace.ron @@ -357,7 +357,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), Variable(VariableRef( variable_name: "one", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.ir.ron index 196c0263..8d281d85 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.ir.ron @@ -62,7 +62,11 @@ Ok(TestIRQuery( )), ], post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), Variable(VariableRef( variable_name: "one", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.trace.ron index 6486625c..18a829c2 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.trace.ron @@ -468,7 +468,11 @@ TestInterpreterOutputTrace( )), ], post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), Variable(VariableRef( variable_name: "one", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.ir.ron index c4f88a26..96879113 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( )), }, post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.trace.ron index 6082ce2a..7c6b347f 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_output.trace.ron @@ -311,7 +311,11 @@ TestInterpreterOutputTrace( )), }, post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.ir.ron index f608c07e..119fde3f 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.ir.ron @@ -55,7 +55,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.trace.ron index a4ca19a6..cc1298b5 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.trace.ron @@ -1158,7 +1158,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs.ir.ron index 55f345dd..c280ba6d 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs.ir.ron @@ -33,7 +33,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs.trace.ron index ad4f66eb..0123752c 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs.trace.ron @@ -275,7 +275,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_elided_braces.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_elided_braces.ir.ron index 55f345dd..c280ba6d 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_elided_braces.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_elided_braces.ir.ron @@ -33,7 +33,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_elided_braces.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_elided_braces.trace.ron index ad4f66eb..0123752c 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_elided_braces.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_elided_braces.trace.ron @@ -275,7 +275,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_greater_than.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_greater_than.ir.ron index 8cee37a0..f1396862 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_greater_than.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_greater_than.ir.ron @@ -33,7 +33,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - GreaterThan(Count, Variable(VariableRef( + GreaterThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_greater_than.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_greater_than.trace.ron index a311def1..9c80fbb3 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_greater_than.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_greater_than.trace.ron @@ -275,7 +275,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - GreaterThan(Count, Variable(VariableRef( + GreaterThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_less_than.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_less_than.ir.ron index 613889d0..8f55d796 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_less_than.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_less_than.ir.ron @@ -33,7 +33,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - LessThan(Count, Variable(VariableRef( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_less_than.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_less_than.trace.ron index be52bb09..770b751d 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_less_than.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_less_than.trace.ron @@ -356,7 +356,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - LessThan(Count, Variable(VariableRef( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_one_of.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_one_of.ir.ron index a8a0d075..b17b3db6 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_one_of.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_one_of.ir.ron @@ -33,7 +33,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - OneOf(Count, Variable(VariableRef( + OneOf(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes_count", variable_type: "[Int!]!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_one_of.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_one_of.trace.ron index 2c3ae9b1..d4912260 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_one_of.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_outputs_transform_and_filter_one_of.trace.ron @@ -195,7 +195,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - OneOf(Count, Variable(VariableRef( + OneOf(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes_count", variable_type: "[Int!]!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_greater_than.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_greater_than.ir.ron index 8e59a212..6fa65159 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_greater_than.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_greater_than.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - GreaterThan(Count, Variable(VariableRef( + GreaterThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_greater_than.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_greater_than.trace.ron index 87946321..8072de86 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_greater_than.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_greater_than.trace.ron @@ -418,7 +418,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - GreaterThan(Count, Variable(VariableRef( + GreaterThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_less_than.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_less_than.ir.ron index a6925573..96b21490 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_less_than.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_less_than.ir.ron @@ -40,7 +40,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - LessThan(Count, Variable(VariableRef( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_less_than.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_less_than.trace.ron index 1ae89fad..555a4753 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_less_than.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_outputs_transform_and_filter_less_than.trace.ron @@ -202,7 +202,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - LessThan(Count, Variable(VariableRef( + LessThan(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "min_primes", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/nested_fold_with_no_outputs.ir.ron b/trustfall_core/test_data/tests/valid_queries/nested_fold_with_no_outputs.ir.ron index 2173ae02..e8b65278 100644 --- a/trustfall_core/test_data/tests/valid_queries/nested_fold_with_no_outputs.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/nested_fold_with_no_outputs.ir.ron @@ -48,7 +48,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), @@ -57,7 +61,11 @@ Ok(TestIRQuery( }, ), post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/nested_fold_with_no_outputs.trace.ron b/trustfall_core/test_data/tests/valid_queries/nested_fold_with_no_outputs.trace.ron index 00fcc6c9..cce419d3 100644 --- a/trustfall_core/test_data/tests/valid_queries/nested_fold_with_no_outputs.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/nested_fold_with_no_outputs.trace.ron @@ -550,7 +550,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), @@ -559,7 +563,11 @@ TestInterpreterOutputTrace( }, ), post_filters: [ - GreaterThanOrEqual(Count, Variable(VariableRef( + GreaterThanOrEqual(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), From 0e749c883b9016e5e4c5f80176c91c27e992cfae Mon Sep 17 00:00:00 2001 From: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> Date: Thu, 13 Jun 2024 16:53:22 -0400 Subject: [PATCH 04/30] Refactor IR to allow transformed values on the LHS of filter operations. (#622) --- trustfall_core/src/frontend/mod.rs | 8 +- trustfall_core/src/interpreter/execution.rs | 43 +++++- .../src/interpreter/hints/vertex_info.rs | 127 ++++++++++++------ trustfall_core/src/ir/mod.rs | 94 ++++++++++++- trustfall_core/src/ir/types/named_typed.rs | 18 ++- .../both_missing_and_unused.ir.ron | 4 +- .../invalid_argument_types.ir.ron | 8 +- .../invalid_null_argument_values.ir.ron | 8 +- .../execution_errors/missing_argument.ir.ron | 4 +- ...issing_unused_and_invalid_arguments.ir.ron | 12 +- ...ng_prevents_list_with_null_argument.ir.ron | 8 +- ...pe_narrowing_prevents_null_argument.ir.ron | 8 +- .../alias_driven_tag_and_output_names.ir.ron | 4 +- ...lias_driven_tag_and_output_names.trace.ron | 4 +- .../filter_in_fold_using_external_tag.ir.ron | 4 +- ...ilter_in_fold_using_external_tag.trace.ron | 4 +- ...r_in_nested_fold_using_external_tag.ir.ron | 4 +- ...n_nested_fold_using_external_tag.trace.ron | 4 +- .../valid_queries/filter_op_contains.ir.ron | 4 +- .../filter_op_contains.trace.ron | 4 +- .../filter_op_greater_or_equal.ir.ron | 4 +- .../filter_op_greater_or_equal.trace.ron | 4 +- .../filter_op_greater_than.ir.ron | 4 +- .../filter_op_greater_than.trace.ron | 4 +- .../valid_queries/filter_op_has_prefix.ir.ron | 4 +- .../filter_op_has_prefix.trace.ron | 4 +- .../filter_op_has_substring.ir.ron | 4 +- .../filter_op_has_substring.trace.ron | 4 +- .../valid_queries/filter_op_has_suffix.ir.ron | 4 +- .../filter_op_has_suffix.trace.ron | 4 +- .../filter_op_less_or_equal.ir.ron | 4 +- .../filter_op_less_or_equal.trace.ron | 4 +- .../valid_queries/filter_op_less_than.ir.ron | 4 +- .../filter_op_less_than.trace.ron | 4 +- .../valid_queries/filter_op_not_equal.ir.ron | 4 +- .../filter_op_not_equal.trace.ron | 4 +- .../valid_queries/filter_op_one_of.ir.ron | 4 +- .../valid_queries/filter_op_one_of.trace.ron | 4 +- .../valid_queries/filter_op_regex.ir.ron | 4 +- .../valid_queries/filter_op_regex.trace.ron | 4 +- .../filter_with_omitted_value_arg.ir.ron | 4 +- .../filter_with_omitted_value_arg.trace.ron | 4 +- .../valid_queries/filter_within_fold.ir.ron | 4 +- .../filter_within_fold.trace.ron | 4 +- .../fold_count_filter_on_a_tag.ir.ron | 4 +- .../fold_count_filter_on_a_tag.trace.ron | 4 +- ...fold_count_filter_with_inner_filter.ir.ron | 4 +- ...d_count_filter_with_inner_filter.trace.ron | 4 +- .../fold_count_tag_explicitly_named.ir.ron | 4 +- .../fold_count_tag_explicitly_named.trace.ron | 4 +- ...d_count_tag_on_nonexistent_optional.ir.ron | 4 +- ...ount_tag_on_nonexistent_optional.trace.ron | 4 +- ..._count_tag_used_inside_sibling_fold.ir.ron | 4 +- ...unt_tag_used_inside_sibling_fold.trace.ron | 4 +- ..._and_nested_filter_dependent_on_tag.ir.ron | 4 +- ...d_nested_filter_dependent_on_tag.trace.ron | 4 +- ...with_count_filter_and_nested_filter.ir.ron | 4 +- ...h_count_filter_and_nested_filter.trace.ron | 4 +- ...t_filter_and_nested_filter_with_tag.ir.ron | 4 +- ...ilter_and_nested_filter_with_tag.trace.ron | 4 +- .../fold_with_nested_filter.ir.ron | 4 +- .../fold_with_nested_filter.trace.ron | 4 +- .../fold_with_nested_filter_and_tag.ir.ron | 4 +- .../fold_with_nested_filter_and_tag.trace.ron | 4 +- ...with_no_inner_output_with_count_tag.ir.ron | 4 +- ...h_no_inner_output_with_count_tag.trace.ron | 4 +- ...d_filter_in_fold_using_external_tag.ir.ron | 4 +- ...ilter_in_fold_using_external_tag.trace.ron | 4 +- .../implicit_tag_and_output_names.ir.ron | 4 +- .../implicit_tag_and_output_names.trace.ron | 4 +- .../multiple_copies_of_field.ir.ron | 4 +- .../multiple_copies_of_field.trace.ron | 4 +- ...ptional_with_nested_edge_and_filter.ir.ron | 4 +- ...onal_with_nested_edge_and_filter.trace.ron | 4 +- ...ith_nested_edge_with_filter_and_tag.ir.ron | 4 +- ..._nested_edge_with_filter_and_tag.trace.ron | 4 +- ...tional_with_nested_filter_semantics.ir.ron | 4 +- ...nal_with_nested_filter_semantics.trace.ron | 4 +- ...th_nested_filter_with_tag_semantics.ir.ron | 4 +- ...nested_filter_with_tag_semantics.trace.ron | 4 +- .../recurse_then_filter_depth_one.ir.ron | 4 +- .../recurse_then_filter_depth_one.trace.ron | 4 +- .../recurse_then_filter_depth_two.ir.ron | 4 +- .../recurse_then_filter_depth_two.trace.ron | 4 +- .../recurse_then_filter_intermediate.ir.ron | 4 +- ...recurse_then_filter_intermediate.trace.ron | 4 +- .../recurse_then_filter_leaves.ir.ron | 4 +- .../recurse_then_filter_leaves.trace.ron | 4 +- ...ecurse_then_filter_on_tag_depth_one.ir.ron | 4 +- ...rse_then_filter_on_tag_depth_one.trace.ron | 4 +- ...ecurse_then_filter_on_tag_depth_two.ir.ron | 4 +- ...rse_then_filter_on_tag_depth_two.trace.ron | 4 +- .../valid_queries/required_properties.ir.ron | 8 +- .../required_properties.trace.ron | 8 +- ...quired_properties_filter_and_output.ir.ron | 8 +- ...red_properties_filter_and_output.trace.ron | 8 +- .../tests/valid_queries/simple_filter.ir.ron | 4 +- .../valid_queries/simple_filter.trace.ron | 4 +- .../static_and_dynamic_filter.ir.ron | 8 +- .../static_and_dynamic_filter.trace.ron | 8 +- .../tag_and_filter_directives.ir.ron | 4 +- .../tag_and_filter_directives.trace.ron | 4 +- .../tag_before_filter_in_same_scope.ir.ron | 4 +- .../tag_before_filter_in_same_scope.trace.ron | 4 +- .../tag_name_in_prefixed_vertex.ir.ron | 4 +- .../tag_name_in_prefixed_vertex.trace.ron | 4 +- .../tag_within_non_existent_optional.ir.ron | 8 +- ...tag_within_non_existent_optional.trace.ron | 8 +- ..._non_existent_optional_used_in_fold.ir.ron | 8 +- ...n_existent_optional_used_in_fold.trace.ron | 8 +- .../valid_queries/typename_filter.ir.ron | 4 +- .../valid_queries/typename_filter.trace.ron | 4 +- .../typename_tag_and_filter.ir.ron | 4 +- .../typename_tag_and_filter.trace.ron | 4 +- 114 files changed, 483 insertions(+), 307 deletions(-) diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index ec11a460..5217d53b 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -17,8 +17,8 @@ use crate::{ ir::{ get_typename_meta_field, Argument, ContextField, EdgeParameters, Eid, FieldRef, FieldValue, FoldSpecificField, FoldSpecificFieldKind, IREdge, IRFold, IRQuery, IRQueryComponent, - IRVertex, IndexedQuery, LocalField, Operation, Recursive, TransformationKind, Type, Vid, - TYPENAME_META_FIELD, + IRVertex, IndexedQuery, LocalField, Operation, OperationSubject, Recursive, + TransformationKind, Type, Vid, TYPENAME_META_FIELD, }, schema::{get_builtin_scalars, FieldOrigin, Schema}, util::{BTreeMapTryInsertExt, TryCollectUniqueKey}, @@ -232,7 +232,7 @@ fn make_local_field_filter_expr( property_name: &Arc, property_type: &Type, filter_directive: &FilterDirective, -) -> Result, Vec> { +) -> Result, Vec> { let left = LocalField { field_name: property_name.clone(), field_type: property_type.clone() }; filters::make_filter_expr( @@ -240,7 +240,7 @@ fn make_local_field_filter_expr( component_path, tags, current_vertex_vid, - left, + OperationSubject::LocalField(left), filter_directive, ) } diff --git a/trustfall_core/src/interpreter/execution.rs b/trustfall_core/src/interpreter/execution.rs index 34ed29a7..fffc97f9 100644 --- a/trustfall_core/src/interpreter/execution.rs +++ b/trustfall_core/src/interpreter/execution.rs @@ -7,8 +7,8 @@ use std::{ use crate::{ ir::{ Argument, ContextField, EdgeParameters, Eid, FieldRef, FieldValue, FoldSpecificFieldKind, - IREdge, IRFold, IRQueryComponent, IRVertex, IndexedQuery, LocalField, Operation, Recursive, - Vid, + IREdge, IRFold, IRQueryComponent, IRVertex, IndexedQuery, LocalField, Operation, + OperationSubject, Recursive, Vid, }, util::BTreeMapTryInsertExt, }; @@ -105,7 +105,7 @@ fn compute_component<'query, AdapterT: Adapter<'query> + 'query>( iterator = coerce_if_needed(adapter.as_ref(), carrier, root_vertex, iterator); for filter_expr in &root_vertex.filters { - iterator = apply_local_field_filter( + iterator = apply_filter_with_arbitrary_subject( adapter.as_ref(), carrier, component, @@ -785,12 +785,35 @@ mismatch on whether the fold below {expanding_from_vid:?} was inside an `@option Box::new(final_iterator) } +fn apply_filter_with_arbitrary_subject<'query, AdapterT: Adapter<'query>>( + adapter: &AdapterT, + carrier: &mut QueryCarrier, + component: &IRQueryComponent, + current_vid: Vid, + filter: &Operation, + iterator: ContextIterator<'query, AdapterT::Vertex>, +) -> ContextIterator<'query, AdapterT::Vertex> { + let subject = filter.left(); + + match subject { + OperationSubject::LocalField(field) => apply_local_field_filter( + adapter, + carrier, + component, + current_vid, + filter.map_left(|_| field), + iterator, + ), + OperationSubject::TransformedField(_) => todo!(), + } +} + fn apply_local_field_filter<'query, AdapterT: Adapter<'query>>( adapter: &AdapterT, carrier: &mut QueryCarrier, component: &IRQueryComponent, current_vid: Vid, - filter: &Operation, + filter: Operation<&LocalField, &Argument>, iterator: ContextIterator<'query, AdapterT::Vertex>, ) -> ContextIterator<'query, AdapterT::Vertex> { let local_field = filter.left(); @@ -802,7 +825,7 @@ fn apply_local_field_filter<'query, AdapterT: Adapter<'query>>( carrier, component, current_vid, - &filter.map(|_| (), |r| r), + &filter.map(|_| (), |r| *r), field_iterator, ) } @@ -1123,8 +1146,14 @@ fn perform_entry_into_new_vertex<'query, AdapterT: Adapter<'query>>( let vertex_id = vertex.vid; let mut iterator = coerce_if_needed(adapter, carrier, vertex, iterator); for filter_expr in vertex.filters.iter() { - iterator = - apply_local_field_filter(adapter, carrier, component, vertex_id, filter_expr, iterator); + iterator = apply_filter_with_arbitrary_subject( + adapter, + carrier, + component, + vertex_id, + filter_expr, + iterator, + ); } Box::new(iterator.map(move |mut x| { x.record_vertex(vertex_id); diff --git a/trustfall_core/src/interpreter/hints/vertex_info.rs b/trustfall_core/src/interpreter/hints/vertex_info.rs index 8d49e13e..b05657bd 100644 --- a/trustfall_core/src/interpreter/hints/vertex_info.rs +++ b/trustfall_core/src/interpreter/hints/vertex_info.rs @@ -10,7 +10,7 @@ use crate::{ interpreter::InterpretedQuery, ir::{ Argument, FieldRef, FieldValue, IREdge, IRFold, IRQueryComponent, IRVertex, LocalField, - Operation, Vid, + Operation, OperationSubject, Vid, }, }; @@ -169,12 +169,12 @@ impl VertexInfo for T { .filter(|c| c.defined_at() == current_vertex.vid) .map(|c| RequiredProperty::new(c.field_name_arc())); - let properties = properties.chain( - current_vertex - .filters - .iter() - .map(|f| RequiredProperty::new(f.left().field_name.clone())), - ); + let properties = properties.chain(current_vertex.filters.iter().map(|f| { + RequiredProperty::new(match f.left() { + OperationSubject::LocalField(field) => field.field_name.clone(), + OperationSubject::TransformedField(_) => todo!(), + }) + })); let properties = properties.chain(current_component.vertices.values().flat_map(|v| { v.filters @@ -210,11 +210,12 @@ impl VertexInfo for T { } let query_variables = self.query_variables(); + let vertex = self.current_vertex(); // We only care about filtering operations that are both: // - on the requested property of this vertex, and // - statically-resolvable, i.e. do not depend on tagged arguments - let mut relevant_filters = filters_on_local_property(self.current_vertex(), property) + let mut relevant_filters = filters_on_local_property(vertex, property) .filter(|op| { // Either there's no "right-hand side" in the operator (as in "is_not_null"), // or the right-hand side is a variable. @@ -223,10 +224,24 @@ impl VertexInfo for T { .peekable(); // Early-return in case there are no filters that apply here. - let field = relevant_filters.peek()?.left(); + let first_filter = relevant_filters.peek()?; + + // We currently ignore filters applied to transformed values of local properties. + // This is sound because each filter we consider only *narrows* the set of possible values, + // meaning that non-evaluated filters are a lost optimization opportunity, + // not a correctness problem. + // + // If tweaking this logic, make sure to tweak the logic in `filters_on_local_property` + // as well -- their behaviors have to match! + // + // TODO: This is an optimization opportunity for the future. + let local_field = match first_filter.left() { + OperationSubject::LocalField(field) => field, + OperationSubject::TransformedField(_) => return None, + }; let candidate = - compute_statically_known_candidate(field, relevant_filters, query_variables) + compute_statically_known_candidate(local_field, relevant_filters, query_variables) .map(|x| x.into_owned()); debug_assert!( // Ensure we never return a range variant with a completely unrestricted range. @@ -288,8 +303,22 @@ impl VertexInfo for T { // Early-return in case there are no filters that apply here. let first_filter = relevant_filters.first()?; + // We currently ignore filters applied to transformed values of local properties. + // This is sound because each filter we consider only *narrows* the set of possible values, + // meaning that non-evaluated filters are a lost optimization opportunity, + // not a correctness problem. + // + // If tweaking this logic, make sure to tweak the logic in `filters_on_local_property` + // as well -- their behaviors have to match! + // + // TODO: This is an optimization opportunity for the future. + let local_field = match first_filter.left() { + OperationSubject::LocalField(field) => field, + OperationSubject::TransformedField(_) => return None, + }; + let initial_candidate = self.statically_required_property(property).unwrap_or_else(|| { - if first_filter.left().field_type.nullable() { + if local_field.field_type.nullable() { CandidateValue::All } else { CandidateValue::Range(Range::full_non_null()) @@ -390,13 +419,27 @@ impl VertexInfo for T { fn filters_on_local_property<'a: 'b, 'b>( vertex: &'a IRVertex, property_name: &'b str, -) -> impl Iterator> + 'b { - vertex.filters.iter().filter(move |op| op.left().field_name.as_ref() == property_name) +) -> impl Iterator> + 'b { + vertex.filters.iter().filter(move |op| { + let left = op.left(); + match left { + OperationSubject::LocalField(field) => field.field_name.as_ref() == property_name, + OperationSubject::TransformedField(..) => { + // We are currently ignoring field transformations for purposes of determining + // required property values. This is sound because every new filter we account for + // can only *reduce* the set of values that are allowed, so skipping filters + // means we lose out on optimization opportunities without harming correctness. + // + // TODO: This is an opportunity for further optimization. + false + } + } + }) } fn compute_statically_known_candidate<'a, 'b>( field: &'a LocalField, - relevant_filters: impl Iterator>, + relevant_filters: impl Iterator>, query_variables: &'b BTreeMap, FieldValue>, ) -> Option>> { let is_subject_field_nullable = field.field_type.nullable(); @@ -467,16 +510,16 @@ mod tests { // Both `= 1` and `!= 1` are impossible to satisfy simultaneously. ( vec![ - Operation::NotEquals(local_field.clone(), first_var.clone()), - Operation::Equals(local_field.clone(), first_var.clone()), + Operation::NotEquals(local_field.clone().into(), first_var.clone()), + Operation::Equals(local_field.clone().into(), first_var.clone()), ], Some(CandidateValue::Impossible), ), // `= 2` and `!= 1` means the value must be 2. ( vec![ - Operation::NotEquals(local_field.clone(), first_var.clone()), - Operation::Equals(local_field.clone(), second_var.clone()), + Operation::NotEquals(local_field.clone().into(), first_var.clone()), + Operation::Equals(local_field.clone().into(), second_var.clone()), ], Some(CandidateValue::Single(&variables["second"])), ), @@ -484,8 +527,8 @@ mod tests { // `one_of [1, 2]` and `!= 1` allows only `2`. ( vec![ - Operation::OneOf(local_field.clone(), list_var.clone()), - Operation::NotEquals(local_field.clone(), first_var.clone()), + Operation::OneOf(local_field.clone().into(), list_var.clone()), + Operation::NotEquals(local_field.clone().into(), first_var.clone()), ], Some(CandidateValue::Single(&variables["second"])), ), @@ -493,8 +536,8 @@ mod tests { // `one_of [1, 2, 3]` and `not_one_of [1, 2]` allows only `3`. ( vec![ - Operation::OneOf(local_field.clone(), longer_list_var.clone()), - Operation::NotOneOf(local_field.clone(), list_var.clone()), + Operation::OneOf(local_field.clone().into(), longer_list_var.clone()), + Operation::NotOneOf(local_field.clone().into(), list_var.clone()), ], Some(CandidateValue::Single(&variables["third"])), ), @@ -502,8 +545,8 @@ mod tests { // `>= 2` and `not_one_of [1, 2]` produces the exclusive > 2 range ( vec![ - Operation::GreaterThanOrEqual(local_field.clone(), second_var.clone()), - Operation::NotOneOf(local_field.clone(), list_var.clone()), + Operation::GreaterThanOrEqual(local_field.clone().into(), second_var.clone()), + Operation::NotOneOf(local_field.clone().into(), list_var.clone()), ], Some(CandidateValue::Range(Range::with_start( Bound::Excluded(&variables["second"]), @@ -514,9 +557,9 @@ mod tests { // `>= 2` and `is_not_null` and `not_one_of [1, 2]` produces the exclusive non-null > 2 range ( vec![ - Operation::GreaterThanOrEqual(local_field.clone(), second_var.clone()), - Operation::NotOneOf(local_field.clone(), list_var.clone()), - Operation::IsNotNull(local_field.clone()), + Operation::GreaterThanOrEqual(local_field.clone().into(), second_var.clone()), + Operation::NotOneOf(local_field.clone().into(), list_var.clone()), + Operation::IsNotNull(local_field.clone().into()), ], Some(CandidateValue::Range(Range::with_start( Bound::Excluded(&variables["second"]), @@ -527,8 +570,8 @@ mod tests { // `> 2` and `is_not_null` produces the exclusive non-null > 2 range ( vec![ - Operation::GreaterThan(local_field.clone(), second_var.clone()), - Operation::IsNotNull(local_field.clone()), + Operation::GreaterThan(local_field.clone().into(), second_var.clone()), + Operation::IsNotNull(local_field.clone().into()), ], Some(CandidateValue::Range(Range::with_start( Bound::Excluded(&variables["second"]), @@ -539,9 +582,9 @@ mod tests { // `<= 2` and `!= 2` and `is_not_null` produces the exclusive non-null < 2 range ( vec![ - Operation::LessThanOrEqual(local_field.clone(), second_var.clone()), - Operation::NotEquals(local_field.clone(), second_var.clone()), - Operation::IsNotNull(local_field.clone()), + Operation::LessThanOrEqual(local_field.clone().into(), second_var.clone()), + Operation::NotEquals(local_field.clone().into(), second_var.clone()), + Operation::IsNotNull(local_field.clone().into()), ], Some(CandidateValue::Range(Range::with_end( Bound::Excluded(&variables["second"]), @@ -552,8 +595,8 @@ mod tests { // `< 2` and `is_not_null` produces the exclusive non-null < 2 range ( vec![ - Operation::LessThan(local_field.clone(), second_var.clone()), - Operation::IsNotNull(local_field.clone()), + Operation::LessThan(local_field.clone().into(), second_var.clone()), + Operation::IsNotNull(local_field.clone().into()), ], Some(CandidateValue::Range(Range::with_end( Bound::Excluded(&variables["second"]), @@ -563,21 +606,21 @@ mod tests { // // `is_not_null` by itself only eliminates null ( - vec![Operation::IsNotNull(local_field.clone())], + vec![Operation::IsNotNull(local_field.clone().into())], Some(CandidateValue::Range(Range::full_non_null())), ), // // `!= null` also elminates null ( - vec![Operation::NotEquals(local_field.clone(), null_var.clone())], + vec![Operation::NotEquals(local_field.clone().into(), null_var.clone())], Some(CandidateValue::Range(Range::full_non_null())), ), // // `!= 1` by itself doesn't produce any candidates - (vec![Operation::NotEquals(local_field.clone(), first_var.clone())], None), + (vec![Operation::NotEquals(local_field.clone().into(), first_var.clone())], None), // // `not_one_of [1, 2]` by itself doesn't produce any candidates - (vec![Operation::NotEquals(local_field.clone(), list_var.clone())], None), + (vec![Operation::NotEquals(local_field.clone().into(), list_var.clone())], None), ]; for (filters, expected_output) in test_data { @@ -625,28 +668,28 @@ mod tests { // The local field is non-nullable. // When we apply a range bound on the field, the range must be non-nullable too. ( - vec![Operation::GreaterThanOrEqual(local_field.clone(), first_var.clone())], + vec![Operation::GreaterThanOrEqual(local_field.clone().into(), first_var.clone())], Some(CandidateValue::Range(Range::with_start( Bound::Included(&variables["first"]), false, ))), ), ( - vec![Operation::GreaterThan(local_field.clone(), first_var.clone())], + vec![Operation::GreaterThan(local_field.clone().into(), first_var.clone())], Some(CandidateValue::Range(Range::with_start( Bound::Excluded(&variables["first"]), false, ))), ), ( - vec![Operation::LessThan(local_field.clone(), first_var.clone())], + vec![Operation::LessThan(local_field.clone().into(), first_var.clone())], Some(CandidateValue::Range(Range::with_end( Bound::Excluded(&variables["first"]), false, ))), ), ( - vec![Operation::LessThanOrEqual(local_field.clone(), first_var.clone())], + vec![Operation::LessThanOrEqual(local_field.clone().into(), first_var.clone())], Some(CandidateValue::Range(Range::with_end( Bound::Included(&variables["first"]), false, diff --git a/trustfall_core/src/ir/mod.rs b/trustfall_core/src/ir/mod.rs index 2e698911..a920b276 100644 --- a/trustfall_core/src/ir/mod.rs +++ b/trustfall_core/src/ir/mod.rs @@ -50,6 +50,18 @@ impl Eid { } } +/// Unique ID of a value term in a Trustfall query, such as a vertex property +/// or a computed value like the number of elements in a `@fold`. +#[doc(alias("term"))] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Tid(pub(crate) NonZeroUsize); + +impl Tid { + pub fn new(id: NonZeroUsize) -> Tid { + Tid(id) + } +} + /// Parameter values for an edge expansion. /// /// Passed as an argument to the [`Adapter::resolve_starting_vertices`] and @@ -189,7 +201,7 @@ pub struct IRVertex { pub coerced_from_type: Option>, #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub filters: Vec>, + pub filters: Vec>, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -351,6 +363,16 @@ impl FieldRef { } } +/// The right-hand side of a Trustfall operation. +/// +/// In a Trustfall query, the `@filter` directive produces [`Operation`] values. +/// The right-hand side of [`Operation`] is usually [`Argument`]. +/// +/// For example: +/// ```graphql +/// name @filter(op: "=", value: ["$input"]) +/// ``` +/// produces a value like `Operation::Equals(..., Argument::Variable(...))`. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Argument { Tag(FieldRef), @@ -378,13 +400,42 @@ impl Argument { } } +/// The left-hand side of a Trustfall operation. +/// +/// In a Trustfall query, the `@filter` directive produces [`Operation`] values. +/// The left-hand side of [`Operation`] is usually [`OperationSubject`]. +/// +/// For example: +/// ```graphql +/// name @filter(op: "=", value: ["$input"]) +/// ``` +/// produces a value like `Operation::Equals(OperationSubject::LocalField(...), ...)`. +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum OperationSubject { + LocalField(LocalField), + TransformedField(TransformedField), +} + +impl From for OperationSubject { + fn from(value: LocalField) -> Self { + Self::LocalField(value) + } +} + +impl From for OperationSubject { + fn from(value: TransformedField) -> Self { + Self::TransformedField(value) + } +} + /// Operations that can be made in the graph. /// -/// In a Trustfall query, the `@filter` directive produces `Operation` values: +/// In a Trustfall query, the `@filter` directive produces [`Operation`] values: /// ```graphql /// name @filter(op: "=", value: ["$input"]) /// ``` -/// would produce the `Operation::Equals` variant, for example. +/// would produce the [`Operation::Equals`] variant, for example. #[non_exhaustive] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Operation @@ -494,6 +545,29 @@ where } } + pub(crate) fn map_left<'a, LeftF, LeftOutT>( + &'a self, + map_left: LeftF, + ) -> Operation + where + LeftOutT: Debug + Clone + PartialEq + Eq, + LeftF: FnOnce(&'a LeftT) -> LeftOutT, + { + self.map(map_left, |x| x) + } + + #[allow(dead_code)] + pub(crate) fn map_right<'a, RightF, RightOutT>( + &'a self, + map_right: RightF, + ) -> Operation<&LeftT, RightOutT> + where + RightOutT: Debug + Clone + PartialEq + Eq, + RightF: FnOnce(&'a RightT) -> RightOutT, + { + self.map(|x| x, map_right) + } + pub(crate) fn map<'a, LeftF, LeftOutT, RightF, RightOutT>( &'a self, map_left: LeftF, @@ -645,6 +719,20 @@ pub struct LocalField { pub field_type: Type, } +/// The outcome of a `@transform` operation applied to a vertex property or property-like value +/// such as the element count of a fold. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TransformedField { + /// Which vertex's field is this a transformation of. + pub vertex_id: Vid, + + /// The unique identifier of the transformation this represents. + pub tid: Tid, + + /// The resulting type of the value produced by this transformation. + pub field_type: Type, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct VariableRef { pub variable_name: Arc, diff --git a/trustfall_core/src/ir/types/named_typed.rs b/trustfall_core/src/ir/types/named_typed.rs index a56d2b0d..f309871e 100644 --- a/trustfall_core/src/ir/types/named_typed.rs +++ b/trustfall_core/src/ir/types/named_typed.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use super::{ super::{ Argument, ContextField, FieldRef, FoldSpecificField, FoldSpecificFieldKind, LocalField, - VariableRef, + OperationSubject, VariableRef, }, Type, }; @@ -14,6 +14,22 @@ pub trait NamedTypedValue: Debug + Clone + PartialEq + Eq { fn named(&self) -> &str; } +impl NamedTypedValue for OperationSubject { + fn typed(&self) -> &Type { + match self { + OperationSubject::LocalField(inner) => inner.typed(), + OperationSubject::TransformedField(_) => todo!(), + } + } + + fn named(&self) -> &str { + match self { + OperationSubject::LocalField(inner) => inner.named(), + OperationSubject::TransformedField(_) => todo!(), + } + } +} + impl NamedTypedValue for LocalField { fn typed(&self) -> &Type { &self.field_type diff --git a/trustfall_core/test_data/tests/execution_errors/both_missing_and_unused.ir.ron b/trustfall_core/test_data/tests/execution_errors/both_missing_and_unused.ir.ron index a5ba3ff8..0f0bb133 100644 --- a/trustfall_core/test_data/tests/execution_errors/both_missing_and_unused.ir.ron +++ b/trustfall_core/test_data/tests/execution_errors/both_missing_and_unused.ir.ron @@ -15,10 +15,10 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - HasSubstring(LocalField( + HasSubstring(LocalField(LocalField( field_name: "name", field_type: "String", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "substr", variable_type: "String!", ))), diff --git a/trustfall_core/test_data/tests/execution_errors/invalid_argument_types.ir.ron b/trustfall_core/test_data/tests/execution_errors/invalid_argument_types.ir.ron index dc851cf5..1671e12a 100644 --- a/trustfall_core/test_data/tests/execution_errors/invalid_argument_types.ir.ron +++ b/trustfall_core/test_data/tests/execution_errors/invalid_argument_types.ir.ron @@ -15,17 +15,17 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - HasSubstring(LocalField( + HasSubstring(LocalField(LocalField( field_name: "name", field_type: "String", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "substr", variable_type: "String!", ))), - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "num", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/execution_errors/invalid_null_argument_values.ir.ron b/trustfall_core/test_data/tests/execution_errors/invalid_null_argument_values.ir.ron index 77bde985..7dbd37fa 100644 --- a/trustfall_core/test_data/tests/execution_errors/invalid_null_argument_values.ir.ron +++ b/trustfall_core/test_data/tests/execution_errors/invalid_null_argument_values.ir.ron @@ -15,17 +15,17 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - HasSubstring(LocalField( + HasSubstring(LocalField(LocalField( field_name: "name", field_type: "String", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "substr", variable_type: "String!", ))), - OneOf(LocalField( + OneOf(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "numbers", variable_type: "[Int]!", ))), diff --git a/trustfall_core/test_data/tests/execution_errors/missing_argument.ir.ron b/trustfall_core/test_data/tests/execution_errors/missing_argument.ir.ron index e8f9dd69..0b4c7e61 100644 --- a/trustfall_core/test_data/tests/execution_errors/missing_argument.ir.ron +++ b/trustfall_core/test_data/tests/execution_errors/missing_argument.ir.ron @@ -15,10 +15,10 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - HasSubstring(LocalField( + HasSubstring(LocalField(LocalField( field_name: "name", field_type: "String", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "substr", variable_type: "String!", ))), diff --git a/trustfall_core/test_data/tests/execution_errors/missing_unused_and_invalid_arguments.ir.ron b/trustfall_core/test_data/tests/execution_errors/missing_unused_and_invalid_arguments.ir.ron index ae529c19..29cc6f35 100644 --- a/trustfall_core/test_data/tests/execution_errors/missing_unused_and_invalid_arguments.ir.ron +++ b/trustfall_core/test_data/tests/execution_errors/missing_unused_and_invalid_arguments.ir.ron @@ -15,24 +15,24 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - HasSubstring(LocalField( + HasSubstring(LocalField(LocalField( field_name: "name", field_type: "String", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "substr", variable_type: "String!", ))), - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "num", variable_type: "Int", ))), - LessThan(LocalField( + LessThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "max", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/execution_errors/type_narrowing_prevents_list_with_null_argument.ir.ron b/trustfall_core/test_data/tests/execution_errors/type_narrowing_prevents_list_with_null_argument.ir.ron index 37785f3f..e2823951 100644 --- a/trustfall_core/test_data/tests/execution_errors/type_narrowing_prevents_list_with_null_argument.ir.ron +++ b/trustfall_core/test_data/tests/execution_errors/type_narrowing_prevents_list_with_null_argument.ir.ron @@ -9,17 +9,17 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "MainType", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "nonNullIntList", field_type: "[Int!]", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "arg", variable_type: "[Int!]", ))), - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "intNonNullList", field_type: "[Int]!", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "arg", variable_type: "[Int]!", ))), diff --git a/trustfall_core/test_data/tests/execution_errors/type_narrowing_prevents_null_argument.ir.ron b/trustfall_core/test_data/tests/execution_errors/type_narrowing_prevents_null_argument.ir.ron index cb1b1f32..0c247a54 100644 --- a/trustfall_core/test_data/tests/execution_errors/type_narrowing_prevents_null_argument.ir.ron +++ b/trustfall_core/test_data/tests/execution_errors/type_narrowing_prevents_null_argument.ir.ron @@ -9,17 +9,17 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "MainType", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "nonNullIntList", field_type: "[Int!]", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "arg", variable_type: "[Int!]", ))), - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "intNonNullList", field_type: "[Int]!", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "arg", variable_type: "[Int]!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/alias_driven_tag_and_output_names.ir.ron b/trustfall_core/test_data/tests/valid_queries/alias_driven_tag_and_output_names.ir.ron index 5831bee6..8f810cd1 100644 --- a/trustfall_core/test_data/tests/valid_queries/alias_driven_tag_and_output_names.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/alias_driven_tag_and_output_names.ir.ron @@ -13,10 +13,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Number", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/alias_driven_tag_and_output_names.trace.ron b/trustfall_core/test_data/tests/valid_queries/alias_driven_tag_and_output_names.trace.ron index 6a6da00d..be6a4c0e 100644 --- a/trustfall_core/test_data/tests/valid_queries/alias_driven_tag_and_output_names.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/alias_driven_tag_and_output_names.trace.ron @@ -296,10 +296,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Number", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/filter_in_fold_using_external_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_in_fold_using_external_tag.ir.ron index 4b4ec07b..6765d009 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_in_fold_using_external_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_in_fold_using_external_tag.ir.ron @@ -28,10 +28,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Composite", filters: [ - LessThan(LocalField( + LessThan(LocalField(LocalField( field_name: "name", field_type: "String", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", diff --git a/trustfall_core/test_data/tests/valid_queries/filter_in_fold_using_external_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_in_fold_using_external_tag.trace.ron index 387115a8..bae5427f 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_in_fold_using_external_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_in_fold_using_external_tag.trace.ron @@ -398,10 +398,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Composite", filters: [ - LessThan(LocalField( + LessThan(LocalField(LocalField( field_name: "name", field_type: "String", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", diff --git a/trustfall_core/test_data/tests/valid_queries/filter_in_nested_fold_using_external_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_in_nested_fold_using_external_tag.ir.ron index 09e655a5..93dcd3b2 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_in_nested_fold_using_external_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_in_nested_fold_using_external_tag.ir.ron @@ -47,10 +47,10 @@ Ok(TestIRQuery( vid: Vid(3), type_name: "Composite", filters: [ - LessThan(LocalField( + LessThan(LocalField(LocalField( field_name: "name", field_type: "String", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", diff --git a/trustfall_core/test_data/tests/valid_queries/filter_in_nested_fold_using_external_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_in_nested_fold_using_external_tag.trace.ron index cc8de21e..d7c87f9d 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_in_nested_fold_using_external_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_in_nested_fold_using_external_tag.trace.ron @@ -649,10 +649,10 @@ TestInterpreterOutputTrace( vid: Vid(3), type_name: "Composite", filters: [ - LessThan(LocalField( + LessThan(LocalField(LocalField( field_name: "name", field_type: "String", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_contains.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_contains.ir.ron index bc3324b7..5ce4688f 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_contains.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_contains.ir.ron @@ -15,10 +15,10 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - Contains(LocalField( + Contains(LocalField(LocalField( field_name: "vowelsInName", field_type: "[String]", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "vowel", variable_type: "String", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_contains.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_contains.trace.ron index 336291db..6c8004d9 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_contains.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_contains.trace.ron @@ -397,10 +397,10 @@ TestInterpreterOutputTrace( vid: Vid(1), type_name: "Number", filters: [ - Contains(LocalField( + Contains(LocalField(LocalField( field_name: "vowelsInName", field_type: "[String]", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "vowel", variable_type: "String", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_greater_or_equal.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_greater_or_equal.ir.ron index ad621326..2973804a 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_greater_or_equal.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_greater_or_equal.ir.ron @@ -19,10 +19,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Composite", filters: [ - GreaterThanOrEqual(LocalField( + GreaterThanOrEqual(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "num", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_greater_or_equal.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_greater_or_equal.trace.ron index 8f60eeea..d1ad57e2 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_greater_or_equal.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_greater_or_equal.trace.ron @@ -1358,10 +1358,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Composite", filters: [ - GreaterThanOrEqual(LocalField( + GreaterThanOrEqual(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "num", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_greater_than.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_greater_than.ir.ron index f0a349c0..eda043da 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_greater_than.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_greater_than.ir.ron @@ -19,10 +19,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Composite", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "num", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_greater_than.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_greater_than.trace.ron index 8a22475b..25ff253c 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_greater_than.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_greater_than.trace.ron @@ -1260,10 +1260,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Composite", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "num", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_has_prefix.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_has_prefix.ir.ron index 47ec5336..9ea3b8c6 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_has_prefix.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_has_prefix.ir.ron @@ -15,10 +15,10 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - HasPrefix(LocalField( + HasPrefix(LocalField(LocalField( field_name: "name", field_type: "String", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "prefix", variable_type: "String!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_has_prefix.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_has_prefix.trace.ron index 97a16d15..28d442fc 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_has_prefix.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_has_prefix.trace.ron @@ -492,10 +492,10 @@ TestInterpreterOutputTrace( vid: Vid(1), type_name: "Number", filters: [ - HasPrefix(LocalField( + HasPrefix(LocalField(LocalField( field_name: "name", field_type: "String", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "prefix", variable_type: "String!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_has_substring.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_has_substring.ir.ron index 61b83273..6e91cf83 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_has_substring.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_has_substring.ir.ron @@ -15,10 +15,10 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - HasSubstring(LocalField( + HasSubstring(LocalField(LocalField( field_name: "name", field_type: "String", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "substr", variable_type: "String!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_has_substring.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_has_substring.trace.ron index c029fc7c..d1fde637 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_has_substring.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_has_substring.trace.ron @@ -266,10 +266,10 @@ TestInterpreterOutputTrace( vid: Vid(1), type_name: "Number", filters: [ - HasSubstring(LocalField( + HasSubstring(LocalField(LocalField( field_name: "name", field_type: "String", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "substr", variable_type: "String!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_has_suffix.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_has_suffix.ir.ron index cb51c33e..592ada18 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_has_suffix.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_has_suffix.ir.ron @@ -15,10 +15,10 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - HasSuffix(LocalField( + HasSuffix(LocalField(LocalField( field_name: "name", field_type: "String", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "suffix", variable_type: "String!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_has_suffix.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_has_suffix.trace.ron index 4cdd6fbe..d6883e41 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_has_suffix.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_has_suffix.trace.ron @@ -621,10 +621,10 @@ TestInterpreterOutputTrace( vid: Vid(1), type_name: "Number", filters: [ - HasSuffix(LocalField( + HasSuffix(LocalField(LocalField( field_name: "name", field_type: "String", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "suffix", variable_type: "String!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_less_or_equal.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_less_or_equal.ir.ron index 54a97a0c..e4b96b83 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_less_or_equal.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_less_or_equal.ir.ron @@ -15,10 +15,10 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - LessThanOrEqual(LocalField( + LessThanOrEqual(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "num", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_less_or_equal.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_less_or_equal.trace.ron index 31046e5f..6faabb82 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_less_or_equal.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_less_or_equal.trace.ron @@ -172,10 +172,10 @@ TestInterpreterOutputTrace( vid: Vid(1), type_name: "Number", filters: [ - LessThanOrEqual(LocalField( + LessThanOrEqual(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "num", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_less_than.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_less_than.ir.ron index e98f3acb..3f525dff 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_less_than.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_less_than.ir.ron @@ -15,10 +15,10 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - LessThan(LocalField( + LessThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "num", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_less_than.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_less_than.trace.ron index 6409219f..180d507b 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_less_than.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_less_than.trace.ron @@ -172,10 +172,10 @@ TestInterpreterOutputTrace( vid: Vid(1), type_name: "Number", filters: [ - LessThan(LocalField( + LessThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "num", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_not_equal.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_not_equal.ir.ron index ff19af8e..973a0ed9 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_not_equal.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_not_equal.ir.ron @@ -15,10 +15,10 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - NotEquals(LocalField( + NotEquals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "num", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_not_equal.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_not_equal.trace.ron index a8ea925b..01a7129c 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_not_equal.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_not_equal.trace.ron @@ -172,10 +172,10 @@ TestInterpreterOutputTrace( vid: Vid(1), type_name: "Number", filters: [ - NotEquals(LocalField( + NotEquals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "num", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_one_of.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_one_of.ir.ron index c3f85b6c..0e826ddf 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_one_of.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_one_of.ir.ron @@ -15,10 +15,10 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - OneOf(LocalField( + OneOf(LocalField(LocalField( field_name: "name", field_type: "String", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "options", variable_type: "[String]!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_one_of.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_one_of.trace.ron index cf60b3b6..fe534299 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_one_of.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_one_of.trace.ron @@ -319,10 +319,10 @@ TestInterpreterOutputTrace( vid: Vid(1), type_name: "Number", filters: [ - OneOf(LocalField( + OneOf(LocalField(LocalField( field_name: "name", field_type: "String", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "options", variable_type: "[String]!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_regex.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_regex.ir.ron index c1519e86..e0cd8f23 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_regex.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_regex.ir.ron @@ -15,10 +15,10 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - RegexMatches(LocalField( + RegexMatches(LocalField(LocalField( field_name: "name", field_type: "String", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "pattern", variable_type: "String!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_op_regex.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_op_regex.trace.ron index 807bfe28..f3115ba9 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_op_regex.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_op_regex.trace.ron @@ -319,10 +319,10 @@ TestInterpreterOutputTrace( vid: Vid(1), type_name: "Number", filters: [ - RegexMatches(LocalField( + RegexMatches(LocalField(LocalField( field_name: "name", field_type: "String", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "pattern", variable_type: "String!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_with_omitted_value_arg.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_with_omitted_value_arg.ir.ron index dd549605..00d70255 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_with_omitted_value_arg.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_with_omitted_value_arg.ir.ron @@ -15,10 +15,10 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - IsNotNull(LocalField( + IsNotNull(LocalField(LocalField( field_name: "name", field_type: "String", - )), + ))), ], ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/filter_with_omitted_value_arg.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_with_omitted_value_arg.trace.ron index a6c28088..7951c9ca 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_with_omitted_value_arg.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_with_omitted_value_arg.trace.ron @@ -1895,10 +1895,10 @@ TestInterpreterOutputTrace( vid: Vid(1), type_name: "Number", filters: [ - IsNotNull(LocalField( + IsNotNull(LocalField(LocalField( field_name: "name", field_type: "String", - )), + ))), ], ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/filter_within_fold.ir.ron b/trustfall_core/test_data/tests/valid_queries/filter_within_fold.ir.ron index b70926b9..937c4112 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_within_fold.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_within_fold.ir.ron @@ -28,10 +28,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Composite", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "bound", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/filter_within_fold.trace.ron b/trustfall_core/test_data/tests/valid_queries/filter_within_fold.trace.ron index 23e9591d..b68fe8db 100644 --- a/trustfall_core/test_data/tests/valid_queries/filter_within_fold.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/filter_within_fold.trace.ron @@ -315,10 +315,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Composite", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "bound", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.ir.ron index ae307bc2..dd7730e9 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.ir.ron @@ -13,10 +13,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Composite", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "six", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.trace.ron index a5cc8663..7376d7b3 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_on_a_tag.trace.ron @@ -373,10 +373,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Composite", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "six", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.ir.ron index d5f1c3c8..c66cb841 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.ir.ron @@ -30,10 +30,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Prime", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.trace.ron index 4fa207b4..5f0b9948 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_filter_with_inner_filter.trace.ron @@ -454,10 +454,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Prime", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_explicitly_named.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_explicitly_named.ir.ron index f9edb197..371ae21d 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_explicitly_named.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_explicitly_named.ir.ron @@ -13,10 +13,10 @@ Ok(TestIRQuery( vid: Vid(3), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(FoldSpecificField(FoldSpecificField( + )), Tag(FoldSpecificField(FoldSpecificField( fold_eid: Eid(1), fold_root_vid: Vid(2), kind: Count, diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_explicitly_named.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_explicitly_named.trace.ron index 0b74f4b3..8da067e6 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_explicitly_named.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_explicitly_named.trace.ron @@ -300,10 +300,10 @@ TestInterpreterOutputTrace( vid: Vid(3), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(FoldSpecificField(FoldSpecificField( + )), Tag(FoldSpecificField(FoldSpecificField( fold_eid: Eid(1), fold_root_vid: Vid(2), kind: Count, diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.ir.ron index a245eef1..b6893521 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.ir.ron @@ -21,10 +21,10 @@ Ok(TestIRQuery( vid: Vid(5), type_name: "Number", filters: [ - LessThan(LocalField( + LessThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(FoldSpecificField(FoldSpecificField( + )), Tag(FoldSpecificField(FoldSpecificField( fold_eid: Eid(3), fold_root_vid: Vid(4), kind: Count, diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.trace.ron index e2767d8e..7cb4fafe 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.trace.ron @@ -468,10 +468,10 @@ TestInterpreterOutputTrace( vid: Vid(5), type_name: "Number", filters: [ - LessThan(LocalField( + LessThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(FoldSpecificField(FoldSpecificField( + )), Tag(FoldSpecificField(FoldSpecificField( fold_eid: Eid(3), fold_root_vid: Vid(4), kind: Count, diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_used_inside_sibling_fold.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_used_inside_sibling_fold.ir.ron index b82b265a..46b6d9d5 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_used_inside_sibling_fold.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_used_inside_sibling_fold.ir.ron @@ -38,10 +38,10 @@ Ok(TestIRQuery( vid: Vid(3), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(FoldSpecificField(FoldSpecificField( + )), Tag(FoldSpecificField(FoldSpecificField( fold_eid: Eid(1), fold_root_vid: Vid(2), kind: Count, diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_used_inside_sibling_fold.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_used_inside_sibling_fold.trace.ron index b91be272..42d13881 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_used_inside_sibling_fold.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_used_inside_sibling_fold.trace.ron @@ -357,10 +357,10 @@ TestInterpreterOutputTrace( vid: Vid(3), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(FoldSpecificField(FoldSpecificField( + )), Tag(FoldSpecificField(FoldSpecificField( fold_eid: Eid(1), fold_root_vid: Vid(2), kind: Count, diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.ir.ron index 787c7301..b20daff1 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.ir.ron @@ -35,10 +35,10 @@ Ok(TestIRQuery( vid: Vid(3), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.trace.ron index 7ceef5eb..59363711 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_both_count_and_nested_filter_dependent_on_tag.trace.ron @@ -534,10 +534,10 @@ TestInterpreterOutputTrace( vid: Vid(3), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.ir.ron index 18ad04a6..ee83ae2e 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.ir.ron @@ -35,10 +35,10 @@ Ok(TestIRQuery( vid: Vid(3), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "one", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.trace.ron index 78b4d5c9..43029f5d 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter.trace.ron @@ -338,10 +338,10 @@ TestInterpreterOutputTrace( vid: Vid(3), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "one", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.ir.ron index 8d281d85..0c3725ee 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.ir.ron @@ -35,10 +35,10 @@ Ok(TestIRQuery( vid: Vid(3), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.trace.ron index 18a829c2..69088e76 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_count_filter_and_nested_filter_with_tag.trace.ron @@ -441,10 +441,10 @@ TestInterpreterOutputTrace( vid: Vid(3), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter.ir.ron index 5146f9ad..b9a63645 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter.ir.ron @@ -35,10 +35,10 @@ Ok(TestIRQuery( vid: Vid(3), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "one", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter.trace.ron index 0ecc1d9b..13cf1269 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter.trace.ron @@ -260,10 +260,10 @@ TestInterpreterOutputTrace( vid: Vid(3), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "one", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter_and_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter_and_tag.ir.ron index 42b6384f..63e79152 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter_and_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter_and_tag.ir.ron @@ -35,10 +35,10 @@ Ok(TestIRQuery( vid: Vid(3), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter_and_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter_and_tag.trace.ron index 09083f32..813ea528 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter_and_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_nested_filter_and_tag.trace.ron @@ -441,10 +441,10 @@ TestInterpreterOutputTrace( vid: Vid(3), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.ir.ron index 119fde3f..dd3f0c35 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.ir.ron @@ -20,10 +20,10 @@ Ok(TestIRQuery( vid: Vid(3), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(FoldSpecificField(FoldSpecificField( + )), Tag(FoldSpecificField(FoldSpecificField( fold_eid: Eid(1), fold_root_vid: Vid(2), kind: Count, diff --git a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.trace.ron index cc1298b5..90361844 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_with_no_inner_output_with_count_tag.trace.ron @@ -1123,10 +1123,10 @@ TestInterpreterOutputTrace( vid: Vid(3), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(FoldSpecificField(FoldSpecificField( + )), Tag(FoldSpecificField(FoldSpecificField( fold_eid: Eid(1), fold_root_vid: Vid(2), kind: Count, diff --git a/trustfall_core/test_data/tests/valid_queries/folded_filter_in_fold_using_external_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/folded_filter_in_fold_using_external_tag.ir.ron index 854bc967..bbc63b54 100644 --- a/trustfall_core/test_data/tests/valid_queries/folded_filter_in_fold_using_external_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/folded_filter_in_fold_using_external_tag.ir.ron @@ -47,10 +47,10 @@ Ok(TestIRQuery( vid: Vid(3), type_name: "Composite", filters: [ - LessThan(LocalField( + LessThan(LocalField(LocalField( field_name: "name", field_type: "String", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(2), field_name: "name", field_type: "String", diff --git a/trustfall_core/test_data/tests/valid_queries/folded_filter_in_fold_using_external_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/folded_filter_in_fold_using_external_tag.trace.ron index 25bca7cb..77175fa0 100644 --- a/trustfall_core/test_data/tests/valid_queries/folded_filter_in_fold_using_external_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/folded_filter_in_fold_using_external_tag.trace.ron @@ -545,10 +545,10 @@ TestInterpreterOutputTrace( vid: Vid(3), type_name: "Composite", filters: [ - LessThan(LocalField( + LessThan(LocalField(LocalField( field_name: "name", field_type: "String", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(2), field_name: "name", field_type: "String", diff --git a/trustfall_core/test_data/tests/valid_queries/implicit_tag_and_output_names.ir.ron b/trustfall_core/test_data/tests/valid_queries/implicit_tag_and_output_names.ir.ron index 9ba8c79e..a0d4a5f7 100644 --- a/trustfall_core/test_data/tests/valid_queries/implicit_tag_and_output_names.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/implicit_tag_and_output_names.ir.ron @@ -13,10 +13,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Number", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/implicit_tag_and_output_names.trace.ron b/trustfall_core/test_data/tests/valid_queries/implicit_tag_and_output_names.trace.ron index 5a169cf9..b4598664 100644 --- a/trustfall_core/test_data/tests/valid_queries/implicit_tag_and_output_names.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/implicit_tag_and_output_names.trace.ron @@ -296,10 +296,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Number", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/multiple_copies_of_field.ir.ron b/trustfall_core/test_data/tests/valid_queries/multiple_copies_of_field.ir.ron index 7d1738df..e29a7fda 100644 --- a/trustfall_core/test_data/tests/valid_queries/multiple_copies_of_field.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/multiple_copies_of_field.ir.ron @@ -13,10 +13,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Composite", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "min_value", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/multiple_copies_of_field.trace.ron b/trustfall_core/test_data/tests/valid_queries/multiple_copies_of_field.trace.ron index 307c7dad..a6d0b99c 100644 --- a/trustfall_core/test_data/tests/valid_queries/multiple_copies_of_field.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/multiple_copies_of_field.trace.ron @@ -242,10 +242,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Composite", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "min_value", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_and_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_and_filter.ir.ron index d8695e7f..f843bab7 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_and_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_and_filter.ir.ron @@ -17,10 +17,10 @@ Ok(TestIRQuery( vid: Vid(3), type_name: "Number", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "one", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_and_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_and_filter.trace.ron index 3d60d378..65e90262 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_and_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_and_filter.trace.ron @@ -214,10 +214,10 @@ TestInterpreterOutputTrace( vid: Vid(3), type_name: "Number", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "one", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_with_filter_and_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_with_filter_and_tag.ir.ron index 6003538c..fe50c238 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_with_filter_and_tag.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_with_filter_and_tag.ir.ron @@ -17,10 +17,10 @@ Ok(TestIRQuery( vid: Vid(3), type_name: "Number", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_with_filter_and_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_with_filter_and_tag.trace.ron index 6d53a8a4..58363fc5 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_with_filter_and_tag.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_edge_with_filter_and_tag.trace.ron @@ -273,10 +273,10 @@ TestInterpreterOutputTrace( vid: Vid(3), type_name: "Number", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_semantics.ir.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_semantics.ir.ron index f524fb38..5ae79550 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_semantics.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_semantics.ir.ron @@ -13,10 +13,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Number", filters: [ - GreaterThanOrEqual(LocalField( + GreaterThanOrEqual(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_semantics.trace.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_semantics.trace.ron index b33170d9..67607bd6 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_semantics.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_semantics.trace.ron @@ -171,10 +171,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Number", filters: [ - GreaterThanOrEqual(LocalField( + GreaterThanOrEqual(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "two", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_with_tag_semantics.ir.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_with_tag_semantics.ir.ron index 63fffc12..a2272505 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_with_tag_semantics.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_with_tag_semantics.ir.ron @@ -13,10 +13,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Number", filters: [ - GreaterThanOrEqual(LocalField( + GreaterThanOrEqual(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_with_tag_semantics.trace.ron b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_with_tag_semantics.trace.ron index 4eefb794..53d2cd11 100644 --- a/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_with_tag_semantics.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/optional_with_nested_filter_with_tag_semantics.trace.ron @@ -228,10 +228,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Number", filters: [ - GreaterThanOrEqual(LocalField( + GreaterThanOrEqual(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_one.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_one.ir.ron index 4c903374..2fd49089 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_one.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_one.ir.ron @@ -19,10 +19,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "bound", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_one.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_one.trace.ron index 5f8980c9..33622b99 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_one.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_one.trace.ron @@ -715,10 +715,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "bound", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_two.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_two.ir.ron index 5d98c004..b8becfaa 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_two.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_two.ir.ron @@ -19,10 +19,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "bound", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_two.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_two.trace.ron index 7c923c10..0939b130 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_two.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_depth_two.trace.ron @@ -1318,10 +1318,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "bound", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_intermediate.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_intermediate.ir.ron index bb92bffd..04f11d2a 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_intermediate.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_intermediate.ir.ron @@ -19,10 +19,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "bound", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_intermediate.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_intermediate.trace.ron index 979a7826..50b2968f 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_intermediate.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_intermediate.trace.ron @@ -2069,10 +2069,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "bound", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_leaves.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_leaves.ir.ron index 73fd0e7b..1c9ab748 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_leaves.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_leaves.ir.ron @@ -19,10 +19,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Number", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "bound", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_leaves.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_leaves.trace.ron index 64b03d69..d6dee800 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_leaves.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_leaves.trace.ron @@ -1969,10 +1969,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Number", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "bound", variable_type: "Int!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_one.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_one.ir.ron index 9b68d8d7..234d9eea 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_one.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_one.ir.ron @@ -19,10 +19,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Number", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_one.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_one.trace.ron index 50ce2a73..dfbc152b 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_one.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_one.trace.ron @@ -1391,10 +1391,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Number", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_two.ir.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_two.ir.ron index 8d397edb..fd92d17f 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_two.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_two.ir.ron @@ -19,10 +19,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Number", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_two.trace.ron b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_two.trace.ron index a2743355..14ed5986 100644 --- a/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_two.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/recurse_then_filter_on_tag_depth_two.trace.ron @@ -2412,10 +2412,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Number", filters: [ - GreaterThan(LocalField( + GreaterThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/required_properties.ir.ron b/trustfall_core/test_data/tests/valid_queries/required_properties.ir.ron index f730ac8b..bc5be614 100644 --- a/trustfall_core/test_data/tests/valid_queries/required_properties.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/required_properties.ir.ron @@ -15,10 +15,10 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "__typename", field_type: "String!", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "type", variable_type: "String!", ))), @@ -28,10 +28,10 @@ Ok(TestIRQuery( vid: Vid(3), type_name: "Composite", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "name", field_type: "String", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", diff --git a/trustfall_core/test_data/tests/valid_queries/required_properties.trace.ron b/trustfall_core/test_data/tests/valid_queries/required_properties.trace.ron index 019b6b19..d41115a2 100644 --- a/trustfall_core/test_data/tests/valid_queries/required_properties.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/required_properties.trace.ron @@ -898,10 +898,10 @@ TestInterpreterOutputTrace( vid: Vid(1), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "__typename", field_type: "String!", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "type", variable_type: "String!", ))), @@ -911,10 +911,10 @@ TestInterpreterOutputTrace( vid: Vid(3), type_name: "Composite", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "name", field_type: "String", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", diff --git a/trustfall_core/test_data/tests/valid_queries/required_properties_filter_and_output.ir.ron b/trustfall_core/test_data/tests/valid_queries/required_properties_filter_and_output.ir.ron index 1985e90e..ce947111 100644 --- a/trustfall_core/test_data/tests/valid_queries/required_properties_filter_and_output.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/required_properties_filter_and_output.ir.ron @@ -15,17 +15,17 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "__typename", field_type: "String!", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "type", variable_type: "String!", ))), - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "val", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/required_properties_filter_and_output.trace.ron b/trustfall_core/test_data/tests/valid_queries/required_properties_filter_and_output.trace.ron index d34153dc..a49977e5 100644 --- a/trustfall_core/test_data/tests/valid_queries/required_properties_filter_and_output.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/required_properties_filter_and_output.trace.ron @@ -181,17 +181,17 @@ TestInterpreterOutputTrace( vid: Vid(1), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "__typename", field_type: "String!", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "type", variable_type: "String!", ))), - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "val", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/simple_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/simple_filter.ir.ron index 8aed2d46..6f79bd1e 100644 --- a/trustfall_core/test_data/tests/valid_queries/simple_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/simple_filter.ir.ron @@ -15,10 +15,10 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "value", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/simple_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/simple_filter.trace.ron index 3f3b4169..c7ce2abe 100644 --- a/trustfall_core/test_data/tests/valid_queries/simple_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/simple_filter.trace.ron @@ -204,10 +204,10 @@ TestInterpreterOutputTrace( vid: Vid(1), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "value", variable_type: "Int", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/static_and_dynamic_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/static_and_dynamic_filter.ir.ron index bf43bce9..19e5bdc8 100644 --- a/trustfall_core/test_data/tests/valid_queries/static_and_dynamic_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/static_and_dynamic_filter.ir.ron @@ -19,17 +19,17 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Number", filters: [ - OneOf(LocalField( + OneOf(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "collection", variable_type: "[Int]!", ))), - GreaterThanOrEqual(LocalField( + GreaterThanOrEqual(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/static_and_dynamic_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/static_and_dynamic_filter.trace.ron index 55993630..a17f1d4a 100644 --- a/trustfall_core/test_data/tests/valid_queries/static_and_dynamic_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/static_and_dynamic_filter.trace.ron @@ -506,17 +506,17 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Number", filters: [ - OneOf(LocalField( + OneOf(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "collection", variable_type: "[Int]!", ))), - GreaterThanOrEqual(LocalField( + GreaterThanOrEqual(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/tag_and_filter_directives.ir.ron b/trustfall_core/test_data/tests/valid_queries/tag_and_filter_directives.ir.ron index f4b5570b..43d17907 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_and_filter_directives.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_and_filter_directives.ir.ron @@ -27,10 +27,10 @@ Ok(TestIRQuery( vid: Vid(4), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/tag_and_filter_directives.trace.ron b/trustfall_core/test_data/tests/valid_queries/tag_and_filter_directives.trace.ron index dec823eb..27b16889 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_and_filter_directives.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_and_filter_directives.trace.ron @@ -1082,10 +1082,10 @@ TestInterpreterOutputTrace( vid: Vid(4), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/tag_before_filter_in_same_scope.ir.ron b/trustfall_core/test_data/tests/valid_queries/tag_before_filter_in_same_scope.ir.ron index d1d6e6f2..b890b629 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_before_filter_in_same_scope.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_before_filter_in_same_scope.ir.ron @@ -9,10 +9,10 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - Contains(LocalField( + Contains(LocalField(LocalField( field_name: "vowelsInName", field_type: "[String]", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", diff --git a/trustfall_core/test_data/tests/valid_queries/tag_before_filter_in_same_scope.trace.ron b/trustfall_core/test_data/tests/valid_queries/tag_before_filter_in_same_scope.trace.ron index 73ed08d1..4377e2f4 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_before_filter_in_same_scope.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_before_filter_in_same_scope.trace.ron @@ -144,10 +144,10 @@ TestInterpreterOutputTrace( vid: Vid(1), type_name: "Number", filters: [ - Contains(LocalField( + Contains(LocalField(LocalField( field_name: "vowelsInName", field_type: "[String]", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "name", field_type: "String", diff --git a/trustfall_core/test_data/tests/valid_queries/tag_name_in_prefixed_vertex.ir.ron b/trustfall_core/test_data/tests/valid_queries/tag_name_in_prefixed_vertex.ir.ron index fb545301..ebf103b2 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_name_in_prefixed_vertex.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_name_in_prefixed_vertex.ir.ron @@ -23,10 +23,10 @@ Ok(TestIRQuery( vid: Vid(3), type_name: "Composite", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/tag_name_in_prefixed_vertex.trace.ron b/trustfall_core/test_data/tests/valid_queries/tag_name_in_prefixed_vertex.trace.ron index d0f4233b..298766b3 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_name_in_prefixed_vertex.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_name_in_prefixed_vertex.trace.ron @@ -1600,10 +1600,10 @@ TestInterpreterOutputTrace( vid: Vid(3), type_name: "Composite", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(2), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional.ir.ron b/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional.ir.ron index 55faa0b7..55ffe43e 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional.ir.ron @@ -23,10 +23,10 @@ Ok(TestIRQuery( vid: Vid(3), type_name: "Prime", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "prime", variable_type: "Int", ))), @@ -48,10 +48,10 @@ Ok(TestIRQuery( vid: Vid(7), type_name: "Number", filters: [ - LessThan(LocalField( + LessThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(6), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional.trace.ron b/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional.trace.ron index 9d064e15..a0615a8e 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional.trace.ron @@ -2126,10 +2126,10 @@ TestInterpreterOutputTrace( vid: Vid(3), type_name: "Prime", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "prime", variable_type: "Int", ))), @@ -2151,10 +2151,10 @@ TestInterpreterOutputTrace( vid: Vid(7), type_name: "Number", filters: [ - LessThan(LocalField( + LessThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(6), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional_used_in_fold.ir.ron b/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional_used_in_fold.ir.ron index 48f07923..78446750 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional_used_in_fold.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional_used_in_fold.ir.ron @@ -23,10 +23,10 @@ Ok(TestIRQuery( vid: Vid(3), type_name: "Prime", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "prime", variable_type: "Int", ))), @@ -96,10 +96,10 @@ Ok(TestIRQuery( vid: Vid(7), type_name: "Number", filters: [ - LessThan(LocalField( + LessThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(6), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional_used_in_fold.trace.ron b/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional_used_in_fold.trace.ron index 6d71524e..e774aa4a 100644 --- a/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional_used_in_fold.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/tag_within_non_existent_optional_used_in_fold.trace.ron @@ -2424,10 +2424,10 @@ TestInterpreterOutputTrace( vid: Vid(3), type_name: "Prime", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "prime", variable_type: "Int", ))), @@ -2497,10 +2497,10 @@ TestInterpreterOutputTrace( vid: Vid(7), type_name: "Number", filters: [ - LessThan(LocalField( + LessThan(LocalField(LocalField( field_name: "value", field_type: "Int", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(6), field_name: "value", field_type: "Int", diff --git a/trustfall_core/test_data/tests/valid_queries/typename_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/typename_filter.ir.ron index d811978b..b9f54b6a 100644 --- a/trustfall_core/test_data/tests/valid_queries/typename_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/typename_filter.ir.ron @@ -15,10 +15,10 @@ Ok(TestIRQuery( vid: Vid(1), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "__typename", field_type: "String!", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "type", variable_type: "String!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/typename_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/typename_filter.trace.ron index 5415aefa..a07b85ff 100644 --- a/trustfall_core/test_data/tests/valid_queries/typename_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/typename_filter.trace.ron @@ -152,10 +152,10 @@ TestInterpreterOutputTrace( vid: Vid(1), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "__typename", field_type: "String!", - ), Variable(VariableRef( + )), Variable(VariableRef( variable_name: "type", variable_type: "String!", ))), diff --git a/trustfall_core/test_data/tests/valid_queries/typename_tag_and_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/typename_tag_and_filter.ir.ron index 2b6fc733..5913f90b 100644 --- a/trustfall_core/test_data/tests/valid_queries/typename_tag_and_filter.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/typename_tag_and_filter.ir.ron @@ -19,10 +19,10 @@ Ok(TestIRQuery( vid: Vid(2), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "__typename", field_type: "String!", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "__typename", field_type: "String!", diff --git a/trustfall_core/test_data/tests/valid_queries/typename_tag_and_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/typename_tag_and_filter.trace.ron index f5c9ef62..a3b58a84 100644 --- a/trustfall_core/test_data/tests/valid_queries/typename_tag_and_filter.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/typename_tag_and_filter.trace.ron @@ -350,10 +350,10 @@ TestInterpreterOutputTrace( vid: Vid(2), type_name: "Number", filters: [ - Equals(LocalField( + Equals(LocalField(LocalField( field_name: "__typename", field_type: "String!", - ), Tag(ContextField(ContextField( + )), Tag(ContextField(ContextField( vertex_id: Vid(1), field_name: "__typename", field_type: "String!", From 0b5ea39e2069b7ef9370ca4965093015e609aff3 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> Date: Fri, 14 Jun 2024 20:28:46 -0400 Subject: [PATCH 05/30] Initial "transform on properties" data structures and updates to error infra. (#623) * Initial draft of the data structures for transforming values. * Update error message generation and tests. --- trustfall_core/src/frontend/error.rs | 192 +++++++++++--- trustfall_core/src/frontend/filters.rs | 239 +++++++++--------- trustfall_core/src/frontend/mod.rs | 7 +- trustfall_core/src/interpreter/execution.rs | 22 +- .../src/interpreter/hints/filters.rs | 4 +- .../src/interpreter/hints/vertex_info.rs | 24 +- trustfall_core/src/ir/mod.rs | 74 +++++- trustfall_core/src/ir/types/mod.rs | 2 - trustfall_core/src/ir/types/named_typed.rs | 113 --------- ...e_tag_in_separate_folds.frontend-error.ron | 2 +- ..._with_parent_after_fold.frontend-error.ron | 2 +- ..._tag_and_string_subject.frontend-error.ron | 1 + ..._tag_and_string_subject.graphql-parsed.ron | 106 ++++++++ ...d_count_tag_and_string_subject.graphql.ron | 18 ++ ..._and_fold_count_subject.frontend-error.ron | 1 + ..._and_fold_count_subject.graphql-parsed.ron | 76 ++++++ ...ing_tag_and_fold_count_subject.graphql.ron | 12 + ...ue_used_inside_own_fold.frontend-error.ron | 2 +- ...formed_fold_count_value.frontend-error.ron | 4 + ...ormed_fold_count_value.graphql-parsed.ron} | 0 ..._transformed_fold_count_value.graphql.ron} | 0 ...me_on_transformed_value.frontend-error.ron | 4 - ...lter_in_unrelated_folds.frontend-error.ron | 2 +- ...ter_in_different_scopes.frontend-error.ron | 2 +- .../tag_from_inside_fold.frontend-error.ron | 2 +- ..._fold_but_defined_after.frontend-error.ron | 2 +- .../undefined_tag.frontend-error.ron | 2 +- 27 files changed, 602 insertions(+), 313 deletions(-) delete mode 100644 trustfall_core/src/ir/types/named_typed.rs create mode 100644 trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_fold_count_tag_and_string_subject.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_fold_count_tag_and_string_subject.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_fold_count_tag_and_string_subject.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_string_tag_and_fold_count_subject.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_string_tag_and_fold_count_subject.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_string_tag_and_fold_count_subject.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/implicit_tag_name_on_transformed_fold_count_value.frontend-error.ron rename trustfall_core/test_data/tests/frontend_errors/{implicit_tag_name_on_transformed_value.graphql-parsed.ron => implicit_tag_name_on_transformed_fold_count_value.graphql-parsed.ron} (100%) rename trustfall_core/test_data/tests/frontend_errors/{implicit_tag_name_on_transformed_value.graphql.ron => implicit_tag_name_on_transformed_fold_count_value.graphql.ron} (100%) delete mode 100644 trustfall_core/test_data/tests/frontend_errors/implicit_tag_name_on_transformed_value.frontend-error.ron diff --git a/trustfall_core/src/frontend/error.rs b/trustfall_core/src/frontend/error.rs index a4f78d97..94e4e333 100644 --- a/trustfall_core/src/frontend/error.rs +++ b/trustfall_core/src/frontend/error.rs @@ -1,9 +1,12 @@ -use std::collections::BTreeMap; +use std::{collections::BTreeMap, fmt::Write}; use serde::{Deserialize, Serialize}; use crate::{ - ir::{FieldValue, Type}, + ir::{ + Argument, FieldValue, FoldSpecificField, OperationSubject, TransformBase, TransformedField, + Type, + }, util::DisplayVec, }; @@ -16,11 +19,11 @@ pub enum FrontendError { #[error("{0}")] ParseError(#[from] crate::graphql_query::error::ParseError), - #[error("Filter on property name \"{0}\" uses undefined tag: %{1}")] + #[error("Filter on {0} uses undefined tag: %{1}")] UndefinedTagInFilter(String, String), #[error( - "Filter on property name \"{0}\" uses tag \"{1}\" which is not yet defined at that point \ + "Filter on {0} uses tag \"{1}\" which is not yet defined at that point \ in the query. Please reorder the query components so that the @tag directive \ comes before all uses of its tagged value." )] @@ -28,7 +31,7 @@ pub enum FrontendError { #[error( "Tag \"{1}\" is defined within a @fold but is used outside that @fold in a filter on \ - property name \"{0}\". This is not supported; if possible, please consider reorganizing \ + {0}. This is not supported; if possible, please consider reorganizing \ the query so that the tagged values are captured outside the @fold and \ their use in @filter moves inside the @fold." )] @@ -48,7 +51,7 @@ pub enum FrontendError { #[error( "Tagged fields with an applied @transform must explicitly specify the tag name, like this: \ - @tag(name: \"some_name\"). Affected field: {0}" + @tag(name: \"some_name\"). Affected location: {0}" )] ExplicitTagNameRequired(String), @@ -119,6 +122,52 @@ pub enum FrontendError { OtherError(String), } +impl FrontendError { + #[inline] + fn represent_subject(subject: &OperationSubject) -> String { + match subject { + OperationSubject::LocalField(field) => { + let property_name = &field.field_name; + format!("property \"{property_name}\"") + } + OperationSubject::TransformedField(field) => { + let mut buf = String::with_capacity(32); + write_name_of_transformed_field(&mut buf, field); + buf + } + OperationSubject::FoldSpecificField(field) => { + let field_name = field.kind.field_name(); + format!("transformed field \"{field_name}\"") + } + } + } + + pub(super) fn undefined_tag_in_filter( + subject: &OperationSubject, + tag_name: impl Into, + ) -> Self { + Self::UndefinedTagInFilter(Self::represent_subject(subject), tag_name.into()) + } + + pub(super) fn tag_used_before_definition( + subject: &OperationSubject, + tag_name: impl Into, + ) -> Self { + Self::TagUsedBeforeDefinition(Self::represent_subject(subject), tag_name.into()) + } + + pub(super) fn tag_used_outside_its_folded_subquery( + subject: &OperationSubject, + tag_name: impl Into, + ) -> Self { + Self::TagUsedOutsideItsFoldedSubquery(Self::represent_subject(subject), tag_name.into()) + } + + pub(super) fn explicit_tag_name_required(subject: &OperationSubject) -> Self { + Self::ExplicitTagNameRequired(Self::represent_subject(subject)) + } +} + #[non_exhaustive] #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, thiserror::Error)] pub enum FilterTypeError { @@ -178,108 +227,169 @@ pub enum FilterTypeError { } impl FilterTypeError { + #[inline] fn represent_property_and_type(property_name: &str, property_type: &Type) -> String { format!("property \"{property_name}\" of type \"{property_type}\"") } + #[inline] fn represent_tag_name_and_type(tag_name: &str, tag_type: &Type) -> String { format!("tag \"{tag_name}\" of type \"{tag_type}\"") } - pub(crate) fn non_nullable_property_with_nullability_filter( + #[inline] + fn represent_variable_name_and_type(var_name: &str, var_type: &Type) -> String { + format!("variable \"{var_name}\" of type \"{var_type}\"") + } + + #[inline] + fn represent_fold_specific_field(field: &FoldSpecificField) -> String { + let field_name = field.kind.field_name(); + let field_type = field.kind.field_type(); + format!("transformed field \"{field_name}\" of type \"{field_type}\"") + } + + #[inline] + fn represent_transformed_field(field: &TransformedField) -> String { + let mut buf = String::with_capacity(64); + + write_name_of_transformed_field(&mut buf, field); + + let field_type = &field.field_type; + write!(buf, " of type \"{field_type}\"").expect("write failed"); + buf + } + + fn represent_subject(subject: &OperationSubject) -> String { + match subject { + OperationSubject::LocalField(field) => { + Self::represent_property_and_type(&field.field_name, &field.field_type) + } + OperationSubject::TransformedField(field) => Self::represent_transformed_field(field), + OperationSubject::FoldSpecificField(field) => { + Self::represent_fold_specific_field(field) + } + } + } + + /// Represent a filter argument as a human-readable string suitable for use in an error message. + /// Tag arguments don't carry a name inside the [`Argument`] type, so we look up and supply + /// the tag name separately if needed. + fn represent_argument(argument: &Argument, tag_name: Option<&str>) -> String { + match argument { + Argument::Tag(tag) => Self::represent_tag_name_and_type( + tag_name.expect("tag argument without a name"), + tag.field_type(), + ), + Argument::Variable(var) => { + Self::represent_variable_name_and_type(&var.variable_name, &var.variable_type) + } + } + } + + pub(crate) fn non_nullable_subject_with_nullability_filter( filter_operator: &str, - property_name: &str, - property_type: &Type, + subject: &OperationSubject, filter_outcome: bool, ) -> Self { Self::NonNullableTypeFilteredForNullability( filter_operator.to_string(), - Self::represent_property_and_type(property_name, property_type), + Self::represent_subject(subject), filter_outcome, ) } - pub(crate) fn type_mismatch_between_property_and_tag( + pub(crate) fn type_mismatch_between_subject_and_argument( filter_operator: &str, - property_name: &str, - property_type: &Type, - tag_name: &str, - tag_type: &Type, + subject: &OperationSubject, + argument: &Argument, + tag_name: Option<&str>, ) -> Self { Self::TypeMismatchBetweenFilterSubjectAndArgument( filter_operator.to_string(), - Self::represent_property_and_type(property_name, property_type), - Self::represent_tag_name_and_type(tag_name, tag_type), + Self::represent_subject(subject), + Self::represent_argument(argument, tag_name), ) } - pub(crate) fn non_orderable_property_with_ordering_filter( + pub(crate) fn non_orderable_subject_with_ordering_filter( filter_operator: &str, - property_name: &str, - property_type: &Type, + subject: &OperationSubject, ) -> Self { Self::OrderingFilterOperationOnNonOrderableSubject( filter_operator.to_string(), - Self::represent_property_and_type(property_name, property_type), + Self::represent_subject(subject), ) } - pub(crate) fn non_orderable_tag_argument_to_ordering_filter( + pub(crate) fn non_orderable_argument_to_ordering_filter( filter_operator: &str, - tag_name: &str, - tag_type: &Type, + argument: &Argument, + tag_name: Option<&str>, ) -> Self { Self::OrderingFilterOperationWithNonOrderableArgument( filter_operator.to_string(), - Self::represent_tag_name_and_type(tag_name, tag_type), + Self::represent_argument(argument, tag_name), ) } - pub(crate) fn non_string_property_with_string_filter( + pub(crate) fn non_string_subject_with_string_filter( filter_operator: &str, - property_name: &str, - property_type: &Type, + subject: &OperationSubject, ) -> Self { Self::StringFilterOperationOnNonStringSubject( filter_operator.to_string(), - Self::represent_property_and_type(property_name, property_type), + Self::represent_subject(subject), ) } - pub(crate) fn non_string_tag_argument_to_string_filter( + pub(crate) fn non_string_argument_to_string_filter( filter_operator: &str, - tag_name: &str, - tag_type: &Type, + argument: &Argument, + tag_name: Option<&str>, ) -> Self { Self::StringFilterOperationOnNonStringArgument( filter_operator.to_string(), - Self::represent_tag_name_and_type(tag_name, tag_type), + Self::represent_argument(argument, tag_name), ) } - pub(crate) fn non_list_property_with_list_filter( + pub(crate) fn non_list_subject_with_list_filter( filter_operator: &str, - property_name: &str, - property_type: &Type, + subject: &OperationSubject, ) -> Self { Self::ListFilterOperationOnNonListSubject( filter_operator.to_string(), - Self::represent_property_and_type(property_name, property_type), + Self::represent_subject(subject), ) } - pub(crate) fn non_list_tag_argument_to_list_filter( + pub(crate) fn non_list_argument_to_list_filter( filter_operator: &str, - tag_name: &str, - tag_type: &Type, + argument: &Argument, + tag_name: Option<&str>, ) -> Self { Self::ListFilterOperationOnNonListArgument( filter_operator.to_string(), - Self::represent_tag_name_and_type(tag_name, tag_type), + Self::represent_argument(argument, tag_name), ) } } +fn write_name_of_transformed_field(buf: &mut String, field: &TransformedField) { + buf.push_str("transformed field \""); + + buf.push_str(match &field.value.base { + TransformBase::ContextField(c) => &c.field_name, + TransformBase::FoldSpecificField(f) => f.kind.field_name(), + }); + for transform in &field.value.transforms { + buf.push('.'); + buf.push_str(transform.operation_name()); + } + buf.push('"'); +} + #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] pub struct DuplicatedNamesConflict { // duplicate output name -> vec (type name, field name) being output under that name diff --git a/trustfall_core/src/frontend/filters.rs b/trustfall_core/src/frontend/filters.rs index ec84f325..ffd7f0cb 100644 --- a/trustfall_core/src/frontend/filters.rs +++ b/trustfall_core/src/frontend/filters.rs @@ -1,6 +1,6 @@ use crate::{ graphql_query::directives::{FilterDirective, OperatorArgument}, - ir::{Argument, NamedTypedValue, Operation, Type, VariableRef, Vid}, + ir::{Argument, Operation, OperationSubject, Type, VariableRef, Vid}, schema::Schema, }; @@ -11,14 +11,14 @@ use super::{ }; #[allow(clippy::too_many_arguments)] -pub(super) fn make_filter_expr( +pub(super) fn make_filter_expr( schema: &Schema, component_path: &ComponentPath, tags: &mut TagHandler<'_>, current_vertex_vid: Vid, - left_operand: LeftT, + left_operand: OperationSubject, filter_directive: &FilterDirective, -) -> Result, Vec> { +) -> Result, Vec> { let filter_operation = filter_directive .operation .try_map( @@ -28,8 +28,7 @@ pub(super) fn make_filter_expr( OperatorArgument::VariableRef(var_name) => Argument::Variable(VariableRef { variable_name: var_name.clone(), variable_type: infer_variable_type( - left_operand.named(), - left_operand.typed().clone(), + &left_operand, &filter_directive.operation, ) .map_err(|e| *e)?, @@ -42,20 +41,20 @@ pub(super) fn make_filter_expr( ) { Ok(defined_tag) => defined_tag, Err(TagLookupError::UndefinedTag(tag_name)) => { - return Err(FrontendError::UndefinedTagInFilter( - left_operand.named().to_string(), + return Err(FrontendError::undefined_tag_in_filter( + &left_operand, tag_name, )); } Err(TagLookupError::TagDefinedInsideFold(tag_name)) => { - return Err(FrontendError::TagUsedOutsideItsFoldedSubquery( - left_operand.named().to_string(), + return Err(FrontendError::tag_used_outside_its_folded_subquery( + &left_operand, tag_name, )); } Err(TagLookupError::TagUsedBeforeDefinition(tag_name)) => { - return Err(FrontendError::TagUsedBeforeDefinition( - left_operand.named().to_string(), + return Err(FrontendError::tag_used_before_definition( + &left_operand, tag_name, )) } @@ -83,16 +82,16 @@ pub(super) fn make_filter_expr( } fn infer_variable_type( - property_name: &str, - property_type: Type, + subject: &OperationSubject, operation: &Operation<(), OperatorArgument>, ) -> Result> { + let left_type = subject.field_type(); match operation { Operation::Equals(..) | Operation::NotEquals(..) => { // Direct equality comparison. // If the field is nullable, then the input should be nullable too // so that the null valued fields can be matched. - Ok(property_type) + Ok(left_type.to_owned()) } Operation::LessThan(..) | Operation::LessThanOrEqual(..) @@ -105,18 +104,17 @@ fn infer_variable_type( // Using a "null" valued variable doesn't make sense as a comparison. // However, [[1], [2], null] is a valid value to use in the comparison, since // there are definitely values that it is smaller than or bigger than. - Ok(property_type.with_nullability(false)) + Ok(left_type.with_nullability(false)) } Operation::Contains(..) | Operation::NotContains(..) => { // To be able to check whether the property's value contains the operand, // the property needs to be a list. If it's not a list, this is a bad filter. - let inner_type = if let Some(list) = property_type.as_list() { + let inner_type = if let Some(list) = left_type.as_list() { list } else { - return Err(Box::new(FilterTypeError::non_list_property_with_list_filter( + return Err(Box::new(FilterTypeError::non_list_subject_with_list_filter( operation.operation_name(), - property_name, - &property_type, + subject, ))); }; @@ -128,7 +126,7 @@ fn infer_variable_type( // Whatever the property's type is, the argument must be a non-nullable list of // the same type, so that the elements of that list may be checked for equality // against that property's value. - Ok(Type::new_list_type(property_type, false)) + Ok(Type::new_list_type(left_type.to_owned(), false)) } Operation::HasPrefix(..) | Operation::NotHasPrefix(..) @@ -150,14 +148,13 @@ fn infer_variable_type( } } -fn operand_types_valid( - operation: &Operation, +fn operand_types_valid( + operation: &Operation, tag_name: Option<&str>, ) -> Result<(), Vec> { let left = operation.left(); let right = operation.right(); - let left_type = left.typed(); - let right_type = right.map(|x| x.typed()); + let left_type = left.field_type(); // Check the left and right operands match the operator's needs individually. // For example: @@ -204,93 +201,92 @@ fn operand_types_valid( mod validity { use crate::{ frontend::error::FilterTypeError, - ir::{Argument, NamedTypedValue, Operation}, + ir::{Argument, Operation, OperationSubject}, }; - pub(super) fn nullability_types_valid( - operation: &Operation, + pub(super) fn nullability_types_valid( + operation: &Operation, tag_name: Option<&str>, ) -> Result<(), Vec> { let left = operation.left(); - let left_type = left.typed(); + let left_type = left.field_type(); // Checking non-nullable types for null or non-null is pointless. if left_type.nullable() { Ok(()) } else { - Err(vec![FilterTypeError::non_nullable_property_with_nullability_filter( + Err(vec![FilterTypeError::non_nullable_subject_with_nullability_filter( operation.operation_name(), - left.named(), - left_type, + left, matches!(operation, Operation::IsNotNull(..)), )]) } } - pub(super) fn equality_types_valid( - operation: &Operation, + pub(super) fn equality_types_valid( + operation: &Operation, tag_name: Option<&str>, ) -> Result<(), Vec> { let left = operation.left(); let right = operation.right(); - let left_type = left.typed(); - let right_type = right.map(|x| x.typed()); + let argument = right.unwrap(); + let left_type = left.field_type(); + let right_type = argument.field_type(); // Individually, any operands are valid for equality operations. // // For the operands relative to each other, nullability doesn't matter, // but the rest of the type must be the same. - let right_type = right_type.unwrap(); if left_type.equal_ignoring_nullability(right_type) { Ok(()) } else { - // The right argument must be a tag at this point. If it is not a tag - // and the second .unwrap() below panics, then our type inference - // has inferred an incorrect type for the variable in the argument. - let tag = right.unwrap().as_tag().unwrap(); - - Err(vec![FilterTypeError::type_mismatch_between_property_and_tag( + let argument = right.unwrap(); + assert!( + argument.as_variable().is_none(), + "type inference for variable {argument:?} has failed to produce a valid type; \ + this is a bug since the issue should have been caught in an earlier stage" + ); + + Err(vec![FilterTypeError::type_mismatch_between_subject_and_argument( operation.operation_name(), - left.named(), - left_type, - tag_name.unwrap(), - tag.field_type(), + left, + argument, + tag_name, )]) } } - pub(super) fn ordering_types_valid( - operation: &Operation, + pub(super) fn ordering_types_valid( + operation: &Operation, tag_name: Option<&str>, ) -> Result<(), Vec> { let left = operation.left(); let right = operation.right(); - let left_type = left.typed(); - let right_type = right.map(|x| x.typed()); + let argument = right.unwrap(); + let left_type = left.field_type(); + let right_type = argument.field_type(); // Individually, the operands' types must be non-nullable or list, recursively, // versions of orderable types. - let right_type = right_type.unwrap(); - let mut errors = vec![]; if !left_type.is_orderable() { - errors.push(FilterTypeError::non_orderable_property_with_ordering_filter( + errors.push(FilterTypeError::non_orderable_subject_with_ordering_filter( operation.operation_name(), - left.named(), - left_type, + left, )); } if !right_type.is_orderable() { - // The right argument must be a tag at this point. If it is not a tag - // and the second .unwrap() below panics, then our type inference - // has inferred an incorrect type for the variable in the argument. - let tag = right.unwrap().as_tag().unwrap(); + assert!( + argument.as_variable().is_none(), + "type inference for variable {argument:?} has failed to produce a valid type; \ + this is a bug since the issue should have been caught in an earlier stage" + ); - errors.push(FilterTypeError::non_orderable_tag_argument_to_ordering_filter( + errors.push(FilterTypeError::non_orderable_argument_to_ordering_filter( operation.operation_name(), - tag_name.unwrap(), - tag.field_type(), + argument, + tag_name, )); } @@ -302,12 +298,11 @@ mod validity { // has inferred an incorrect type for the variable in the argument. let tag = right.unwrap().as_tag().unwrap(); - errors.push(FilterTypeError::type_mismatch_between_property_and_tag( + errors.push(FilterTypeError::type_mismatch_between_subject_and_argument( operation.operation_name(), - left.named(), - left_type, - tag_name.unwrap(), - tag.field_type(), + left, + argument, + tag_name, )); } @@ -318,71 +313,70 @@ mod validity { } } - pub(super) fn list_containment_types_valid( - operation: &Operation, + pub(super) fn list_containment_types_valid( + operation: &Operation, tag_name: Option<&str>, ) -> Result<(), Vec> { let left = operation.left(); let right = operation.right(); - let left_type = left.typed(); - let right_type = right.map(|x| x.typed()); + let argument = right.unwrap(); + let left_type = left.field_type(); + let right_type = argument.field_type(); // The left-hand operand needs to be a list, ignoring nullability. // The right-hand operand may be anything, if considered individually. let inner_type = left_type.as_list().ok_or_else(|| { - vec![FilterTypeError::non_list_property_with_list_filter( + vec![FilterTypeError::non_list_subject_with_list_filter( operation.operation_name(), - left.named(), - left_type, + left, )] })?; - let right_type = right_type.unwrap(); - // However, the type inside the left-hand list must be equal, // ignoring nullability, to the type of the right-hand operand. if inner_type.equal_ignoring_nullability(right_type) { Ok(()) } else { - // The right argument must be a tag at this point. If it is not a tag - // and the second .unwrap() below panics, then our type inference - // has inferred an incorrect type for the variable in the argument. - let tag = right.unwrap().as_tag().unwrap(); + assert!( + argument.as_variable().is_none(), + "type inference for variable {argument:?} has failed to produce a valid type; \ + this is a bug since the issue should have been caught in an earlier stage" + ); - Err(vec![FilterTypeError::type_mismatch_between_property_and_tag( + Err(vec![FilterTypeError::type_mismatch_between_subject_and_argument( operation.operation_name(), - left.named(), - left_type, - tag_name.unwrap(), - tag.field_type(), + left, + argument, + tag_name, )]) } } - pub(super) fn bulk_equality_types_valid( - operation: &Operation, + pub(super) fn bulk_equality_types_valid( + operation: &Operation, tag_name: Option<&str>, ) -> Result<(), Vec> { let left = operation.left(); let right = operation.right(); - let left_type = left.typed(); - let right_type = right.map(|x| x.typed()); + let argument = right.unwrap(); + let left_type = left.field_type(); + let right_type = argument.field_type(); // The right-hand operand needs to be a list, ignoring nullability. // The left-hand operand may be anything, if considered individually. - let right_type = right_type.unwrap(); let inner_type = if let Some(list) = right_type.as_list() { Ok(list) } else { - // The right argument must be a tag at this point. If it is not a tag - // and the second .unwrap() below panics, then our type inference - // has inferred an incorrect type for the variable in the argument. - let tag = right.unwrap().as_tag().unwrap(); + assert!( + argument.as_variable().is_none(), + "type inference for variable {argument:?} has failed to produce a valid type; \ + this is a bug since the issue should have been caught in an earlier stage" + ); - Err(vec![FilterTypeError::non_list_tag_argument_to_list_filter( + Err(vec![FilterTypeError::non_list_argument_to_list_filter( operation.operation_name(), - tag_name.unwrap(), - tag.field_type(), + argument, + tag_name, )]) }?; @@ -391,51 +385,52 @@ mod validity { if left_type.equal_ignoring_nullability(&inner_type) { Ok(()) } else { - // The right argument must be a tag at this point. If it is not a tag - // and the second .unwrap() below panics, then our type inference - // has inferred an incorrect type for the variable in the argument. - let tag = right.unwrap().as_tag().unwrap(); + assert!( + argument.as_variable().is_none(), + "type inference for variable {argument:?} has failed to produce a valid type; \ + this is a bug since the issue should have been caught in an earlier stage" + ); - Err(vec![FilterTypeError::type_mismatch_between_property_and_tag( + Err(vec![FilterTypeError::type_mismatch_between_subject_and_argument( operation.operation_name(), - left.named(), - left_type, - tag_name.unwrap(), - tag.field_type(), + left, + argument, + tag_name, )]) } } - pub(super) fn string_operation_types_valid( - operation: &Operation, + pub(super) fn string_operation_types_valid( + operation: &Operation, tag_name: Option<&str>, ) -> Result<(), Vec> { let left = operation.left(); let right = operation.right(); - let left_type = left.typed(); - let right_type = right.map(|x| x.typed()); + let argument = right.unwrap(); + let left_type = left.field_type(); + let right_type = argument.field_type(); let mut errors = vec![]; // Both operands need to be strings, ignoring nullability. if left_type.is_list() || left_type.base_type() != "String" { - errors.push(FilterTypeError::non_string_property_with_string_filter( + errors.push(FilterTypeError::non_string_subject_with_string_filter( operation.operation_name(), - left.named(), - left_type, + left, )); } - // The right argument must be a tag at this point. If it is not a tag - // and the second .unwrap() below panics, then our type inference - // has inferred an incorrect type for the variable in the argument. - let right_type = right_type.unwrap(); if right_type.is_list() || right_type.base_type() != "String" { - let tag = right.unwrap().as_tag().unwrap(); - errors.push(FilterTypeError::non_string_tag_argument_to_string_filter( + assert!( + argument.as_variable().is_none(), + "type inference for variable {argument:?} has failed to produce a valid type; \ + this is a bug since the issue should have been caught in an earlier stage" + ); + + errors.push(FilterTypeError::non_string_argument_to_string_filter( operation.operation_name(), - tag_name.unwrap(), - tag.field_type(), + argument, + tag_name, )); } diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index 5217d53b..63c42f86 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -1134,6 +1134,7 @@ where }, }; let field_ref = FieldRef::FoldSpecificField(fold_specific_field.clone()); + let subject = OperationSubject::FoldSpecificField(fold_specific_field.clone()); for filter_directive in &transform_group.filter { match make_filter_expr( @@ -1141,7 +1142,7 @@ where component_path, tags, starting_vid, - field_ref.clone(), + subject.clone(), filter_directive, ) { Ok(filter) => post_filters.push(filter), @@ -1193,9 +1194,7 @@ where errors.push(FrontendError::MultipleTagsWithSameName(tag_name.to_string())); } } else { - errors.push(FrontendError::ExplicitTagNameRequired( - starting_field.name.as_ref().to_owned(), - )) + errors.push(FrontendError::explicit_tag_name_required(&subject)) } } } diff --git a/trustfall_core/src/interpreter/execution.rs b/trustfall_core/src/interpreter/execution.rs index fffc97f9..2c1978b2 100644 --- a/trustfall_core/src/interpreter/execution.rs +++ b/trustfall_core/src/interpreter/execution.rs @@ -105,7 +105,7 @@ fn compute_component<'query, AdapterT: Adapter<'query> + 'query>( iterator = coerce_if_needed(adapter.as_ref(), carrier, root_vertex, iterator); for filter_expr in &root_vertex.filters { - iterator = apply_filter_with_arbitrary_subject( + iterator = apply_filter_with_non_folded_field_subject( adapter.as_ref(), carrier, component, @@ -287,7 +287,7 @@ fn get_max_fold_count_limit(carrier: &mut QueryCarrier, fold: &IRFold) -> Option continue; } - if !matches!(left, FieldRef::FoldSpecificField(f) if f.kind == FoldSpecificFieldKind::Count) + if !matches!(left, OperationSubject::FoldSpecificField(f) if f.kind == FoldSpecificFieldKind::Count) { // The filter expression is doing something more complex than we can currently analyze. // Conservatively return `None` to disable optimizations here. @@ -358,7 +358,7 @@ fn get_min_fold_count_limit(carrier: &mut QueryCarrier, fold: &IRFold) -> Option continue; } - if !matches!(left, FieldRef::FoldSpecificField(f) if f.kind == FoldSpecificFieldKind::Count) + if !matches!(left, OperationSubject::FoldSpecificField(f) if f.kind == FoldSpecificFieldKind::Count) { // The filter expression is doing something more complex than we can currently analyze. // Conservatively return `None` to disable optimizations here. @@ -621,10 +621,7 @@ fn compute_fold<'query, AdapterT: Adapter<'query> + 'query>( for post_fold_filter in fold.post_filters.iter() { let left = post_fold_filter.left(); match left { - FieldRef::ContextField(_) => { - unreachable!("unexpectedly found a fold post-filtering step that references a ContextField: {fold:#?}"); - } - FieldRef::FoldSpecificField(fold_specific_field) => { + OperationSubject::FoldSpecificField(fold_specific_field) => { let remapped_operation = post_fold_filter.map(|_| fold_specific_field.kind, |x| x); post_filtered_iterator = apply_fold_specific_filter( adapter.as_ref(), @@ -636,6 +633,10 @@ fn compute_fold<'query, AdapterT: Adapter<'query> + 'query>( post_filtered_iterator, ); } + OperationSubject::TransformedField(_) => todo!(), + OperationSubject::LocalField(_) => { + unreachable!("unexpectedly found a fold post-filtering step that references a LocalField: {fold:#?}"); + } } } @@ -785,7 +786,7 @@ mismatch on whether the fold below {expanding_from_vid:?} was inside an `@option Box::new(final_iterator) } -fn apply_filter_with_arbitrary_subject<'query, AdapterT: Adapter<'query>>( +fn apply_filter_with_non_folded_field_subject<'query, AdapterT: Adapter<'query>>( adapter: &AdapterT, carrier: &mut QueryCarrier, component: &IRQueryComponent, @@ -805,6 +806,9 @@ fn apply_filter_with_arbitrary_subject<'query, AdapterT: Adapter<'query>>( iterator, ), OperationSubject::TransformedField(_) => todo!(), + OperationSubject::FoldSpecificField(..) => unreachable!( + "illegal filter over fold-specific field passed to this function: {filter:?}" + ), } } @@ -1146,7 +1150,7 @@ fn perform_entry_into_new_vertex<'query, AdapterT: Adapter<'query>>( let vertex_id = vertex.vid; let mut iterator = coerce_if_needed(adapter, carrier, vertex, iterator); for filter_expr in vertex.filters.iter() { - iterator = apply_filter_with_arbitrary_subject( + iterator = apply_filter_with_non_folded_field_subject( adapter, carrier, component, diff --git a/trustfall_core/src/interpreter/hints/filters.rs b/trustfall_core/src/interpreter/hints/filters.rs index ae7a028e..7b8efd32 100644 --- a/trustfall_core/src/interpreter/hints/filters.rs +++ b/trustfall_core/src/interpreter/hints/filters.rs @@ -2,7 +2,7 @@ use std::{borrow::Cow, collections::BTreeMap, fmt::Debug, ops::Bound, sync::Arc} use itertools::Itertools; -use crate::ir::{Argument, FieldRef, FieldValue, FoldSpecificFieldKind, IRFold, Operation}; +use crate::ir::{Argument, FieldValue, FoldSpecificFieldKind, IRFold, Operation, OperationSubject}; use super::{candidates::NullableValue, CandidateValue, Range}; @@ -117,7 +117,7 @@ pub(super) fn fold_requires_at_least_one_element( // TODO: When we support applying `@transform` to property-like values, we can update this logic // to be smarter and less conservative. let relevant_filters = fold.post_filters.iter().filter(|op| { - matches!(op.left(), FieldRef::FoldSpecificField(f) if f.kind == FoldSpecificFieldKind::Count) + matches!(op.left(), OperationSubject::FoldSpecificField(f) if f.kind == FoldSpecificFieldKind::Count) }); let is_subject_field_nullable = false; // the "count" value can't be null candidate_from_statically_evaluated_filters( diff --git a/trustfall_core/src/interpreter/hints/vertex_info.rs b/trustfall_core/src/interpreter/hints/vertex_info.rs index b05657bd..06773569 100644 --- a/trustfall_core/src/interpreter/hints/vertex_info.rs +++ b/trustfall_core/src/interpreter/hints/vertex_info.rs @@ -6,6 +6,7 @@ use std::{ sync::Arc, }; +use crate::ir::TransformBase; use crate::{ interpreter::InterpretedQuery, ir::{ @@ -169,10 +170,16 @@ impl VertexInfo for T { .filter(|c| c.defined_at() == current_vertex.vid) .map(|c| RequiredProperty::new(c.field_name_arc())); - let properties = properties.chain(current_vertex.filters.iter().map(|f| { + let properties = properties.chain(current_vertex.filters.iter().map(move |f| { RequiredProperty::new(match f.left() { OperationSubject::LocalField(field) => field.field_name.clone(), - OperationSubject::TransformedField(_) => todo!(), + OperationSubject::TransformedField(transformed) => { + match &transformed.value.base { + TransformBase::ContextField(field) => field.field_name.clone(), + TransformBase::FoldSpecificField(_) => unreachable!("illegal transformed vertex in filter of current_vertex: {current_vertex:#?}"), + } + } + OperationSubject::FoldSpecificField(..) => unreachable!("found fold-specific field in vertex filters: {current_vertex:#?}"), }) })); @@ -238,6 +245,9 @@ impl VertexInfo for T { let local_field = match first_filter.left() { OperationSubject::LocalField(field) => field, OperationSubject::TransformedField(_) => return None, + OperationSubject::FoldSpecificField(..) => { + unreachable!("found fold-specific field in vertex filters: {vertex:#?}") + } }; let candidate = @@ -268,6 +278,8 @@ impl VertexInfo for T { return None; } + let current_vertex = self.current_vertex(); + // We only care about filtering operations that are all of the following: // - on the requested property of this vertex; // - dynamically-resolvable, i.e. depend on tagged arguments, @@ -275,7 +287,7 @@ impl VertexInfo for T { // at the time this call was made, and // - use a supported filtering operation using those tagged arguments. let resolved_range = (Bound::Unbounded, self.execution_frontier()); - let relevant_filters: Vec<_> = filters_on_local_property(self.current_vertex(), property) + let relevant_filters: Vec<_> = filters_on_local_property(current_vertex, property) .filter(|op| { matches!( op, @@ -315,6 +327,9 @@ impl VertexInfo for T { let local_field = match first_filter.left() { OperationSubject::LocalField(field) => field, OperationSubject::TransformedField(_) => return None, + OperationSubject::FoldSpecificField(..) => { + unreachable!("found fold-specific field in vertex filters: {current_vertex:#?}") + } }; let initial_candidate = self.statically_required_property(property).unwrap_or_else(|| { @@ -433,6 +448,9 @@ fn filters_on_local_property<'a: 'b, 'b>( // TODO: This is an opportunity for further optimization. false } + OperationSubject::FoldSpecificField(..) => { + unreachable!("found fold-specific field in vertex filters: {vertex:#?}") + } } }) } diff --git a/trustfall_core/src/ir/mod.rs b/trustfall_core/src/ir/mod.rs index a920b276..f3c39433 100644 --- a/trustfall_core/src/ir/mod.rs +++ b/trustfall_core/src/ir/mod.rs @@ -13,7 +13,7 @@ use std::{ use serde::{Deserialize, Serialize}; pub use self::indexed::{EdgeKind, IndexedQuery, InvalidIRQueryError, Output}; -pub use self::types::{NamedTypedValue, Type}; +pub use self::types::Type; pub use self::value::{FieldValue, TransparentValue}; mod indexed; @@ -235,7 +235,7 @@ pub struct IRFold { /// All [`FieldRef`] values inside each [`Operation`] within the `Vec` are guaranteed to have /// `FieldRef.refers_to_fold_specific_field().is_some() == true`. #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub post_filters: Vec>, + pub post_filters: Vec>, } #[non_exhaustive] @@ -380,6 +380,13 @@ pub enum Argument { } impl Argument { + pub(crate) fn as_variable(&self) -> Option<&VariableRef> { + match self { + Argument::Variable(var) => Some(var), + _ => None, + } + } + pub(crate) fn as_tag(&self) -> Option<&FieldRef> { match self { Argument::Tag(t) => Some(t), @@ -398,6 +405,13 @@ impl Argument { } } } + + pub fn field_type(&self) -> &Type { + match self { + Argument::Tag(tag) => tag.field_type(), + Argument::Variable(var) => &var.variable_type, + } + } } /// The left-hand side of a Trustfall operation. @@ -414,6 +428,7 @@ impl Argument { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum OperationSubject { LocalField(LocalField), + FoldSpecificField(FoldSpecificField), TransformedField(TransformedField), } @@ -429,6 +444,23 @@ impl From for OperationSubject { } } +impl OperationSubject { + pub fn refers_to_fold_specific_field(&self) -> Option<&FoldSpecificField> { + match self { + OperationSubject::FoldSpecificField(fold_specific) => Some(fold_specific), + _ => None, + } + } + + pub fn field_type(&self) -> &Type { + match self { + OperationSubject::LocalField(inner) => &inner.field_type, + OperationSubject::TransformedField(inner) => &inner.field_type, + OperationSubject::FoldSpecificField(inner) => inner.kind.field_type(), + } + } +} + /// Operations that can be made in the graph. /// /// In a Trustfall query, the `@filter` directive produces [`Operation`] values: @@ -719,20 +751,52 @@ pub struct LocalField { pub field_type: Type, } +#[non_exhaustive] /// The outcome of a `@transform` operation applied to a vertex property or property-like value /// such as the element count of a fold. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct TransformedField { - /// Which vertex's field is this a transformation of. - pub vertex_id: Vid, + pub value: Arc, - /// The unique identifier of the transformation this represents. + /// The unique identifier of the transformed value this struct represents. pub tid: Tid, /// The resulting type of the value produced by this transformation. pub field_type: Type, } +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TransformedValue { + pub base: TransformBase, + pub transforms: Vec, +} + +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum TransformBase { + ContextField(ContextField), + FoldSpecificField(FoldSpecificField), +} + +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum Transform { + Len, + Abs, + Add(FieldRef), +} + +impl Transform { + pub(crate) fn operation_name(&self) -> &str { + match self { + Self::Len => "len", + Self::Abs => "abs", + Self::Add(..) => "add", + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct VariableRef { pub variable_name: Arc, diff --git a/trustfall_core/src/ir/types/mod.rs b/trustfall_core/src/ir/types/mod.rs index e6cbc830..ed1611d4 100644 --- a/trustfall_core/src/ir/types/mod.rs +++ b/trustfall_core/src/ir/types/mod.rs @@ -1,5 +1,3 @@ mod base; -mod named_typed; pub use base::Type; -pub use named_typed::NamedTypedValue; diff --git a/trustfall_core/src/ir/types/named_typed.rs b/trustfall_core/src/ir/types/named_typed.rs deleted file mode 100644 index f309871e..00000000 --- a/trustfall_core/src/ir/types/named_typed.rs +++ /dev/null @@ -1,113 +0,0 @@ -use std::fmt::Debug; - -use super::{ - super::{ - Argument, ContextField, FieldRef, FoldSpecificField, FoldSpecificFieldKind, LocalField, - OperationSubject, VariableRef, - }, - Type, -}; - -pub trait NamedTypedValue: Debug + Clone + PartialEq + Eq { - fn typed(&self) -> &Type; - - fn named(&self) -> &str; -} - -impl NamedTypedValue for OperationSubject { - fn typed(&self) -> &Type { - match self { - OperationSubject::LocalField(inner) => inner.typed(), - OperationSubject::TransformedField(_) => todo!(), - } - } - - fn named(&self) -> &str { - match self { - OperationSubject::LocalField(inner) => inner.named(), - OperationSubject::TransformedField(_) => todo!(), - } - } -} - -impl NamedTypedValue for LocalField { - fn typed(&self) -> &Type { - &self.field_type - } - - fn named(&self) -> &str { - self.field_name.as_ref() - } -} - -impl NamedTypedValue for ContextField { - fn typed(&self) -> &Type { - &self.field_type - } - - fn named(&self) -> &str { - self.field_name.as_ref() - } -} - -impl NamedTypedValue for FoldSpecificField { - fn typed(&self) -> &Type { - self.kind.field_type() - } - - fn named(&self) -> &str { - self.kind.field_name() - } -} - -impl NamedTypedValue for FoldSpecificFieldKind { - fn typed(&self) -> &Type { - self.field_type() - } - - fn named(&self) -> &str { - self.field_name() - } -} - -impl NamedTypedValue for VariableRef { - fn typed(&self) -> &Type { - &self.variable_type - } - - fn named(&self) -> &str { - &self.variable_name - } -} - -impl NamedTypedValue for FieldRef { - fn typed(&self) -> &Type { - match self { - FieldRef::ContextField(c) => c.typed(), - FieldRef::FoldSpecificField(f) => f.kind.typed(), - } - } - - fn named(&self) -> &str { - match self { - FieldRef::ContextField(c) => c.named(), - FieldRef::FoldSpecificField(f) => f.kind.named(), - } - } -} - -impl NamedTypedValue for Argument { - fn typed(&self) -> &Type { - match self { - Argument::Tag(t) => t.typed(), - Argument::Variable(v) => v.typed(), - } - } - - fn named(&self) -> &str { - match self { - Argument::Tag(t) => t.named(), - Argument::Variable(v) => v.named(), - } - } -} diff --git a/trustfall_core/test_data/tests/frontend_errors/duplicate_tag_in_separate_folds.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/duplicate_tag_in_separate_folds.frontend-error.ron index fe2f9372..64fab002 100644 --- a/trustfall_core/test_data/tests/frontend_errors/duplicate_tag_in_separate_folds.frontend-error.ron +++ b/trustfall_core/test_data/tests/frontend_errors/duplicate_tag_in_separate_folds.frontend-error.ron @@ -1,4 +1,4 @@ Err(MultipleErrors(DisplayVec([ MultipleTagsWithSameName("tagged"), - TagUsedOutsideItsFoldedSubquery("value", "tagged"), + TagUsedOutsideItsFoldedSubquery("property \"value\"", "tagged"), ]))) diff --git a/trustfall_core/test_data/tests/frontend_errors/duplicate_tag_with_parent_after_fold.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/duplicate_tag_with_parent_after_fold.frontend-error.ron index fe2f9372..64fab002 100644 --- a/trustfall_core/test_data/tests/frontend_errors/duplicate_tag_with_parent_after_fold.frontend-error.ron +++ b/trustfall_core/test_data/tests/frontend_errors/duplicate_tag_with_parent_after_fold.frontend-error.ron @@ -1,4 +1,4 @@ Err(MultipleErrors(DisplayVec([ MultipleTagsWithSameName("tagged"), - TagUsedOutsideItsFoldedSubquery("value", "tagged"), + TagUsedOutsideItsFoldedSubquery("property \"value\"", "tagged"), ]))) diff --git a/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_fold_count_tag_and_string_subject.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_fold_count_tag_and_string_subject.frontend-error.ron new file mode 100644 index 00000000..3f0ff72f --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_fold_count_tag_and_string_subject.frontend-error.ron @@ -0,0 +1 @@ +Err(FilterTypeError(StringFilterOperationOnNonStringArgument("regex", "tag \"factors\" of type \"Int!\""))) diff --git a/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_fold_count_tag_and_string_subject.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_fold_count_tag_and_string_subject.graphql-parsed.ron new file mode 100644 index 00000000..f2f79788 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_fold_count_tag_and_string_subject.graphql-parsed.ron @@ -0,0 +1,106 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Four", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Four", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "primeFactor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + transform: TransformDirective( + kind: Count, + ), + tag: [ + TagDirective( + name: Some("factors"), + ), + ], + )), + )), + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "primeFactor", + transform_group: Some(TransformGroup( + transform: TransformDirective( + kind: Count, + ), + tag: [ + TagDirective( + name: Some("factors"), + ), + ], + )), + )), + (FieldConnection( + position: Pos( + line: 6, + column: 9, + ), + name: "successor", + ), FieldNode( + position: Pos( + line: 6, + column: 9, + ), + name: "successor", + connections: [ + (FieldConnection( + position: Pos( + line: 9, + column: 13, + ), + name: "name", + ), FieldNode( + position: Pos( + line: 9, + column: 13, + ), + name: "name", + filter: [ + FilterDirective( + operation: RegexMatches((), TagRef("factors")), + ), + ], + )), + (FieldConnection( + position: Pos( + line: 11, + column: 13, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 11, + column: 13, + ), + name: "value", + output: [ + OutputDirective(), + ], + )), + ], + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_fold_count_tag_and_string_subject.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_fold_count_tag_and_string_subject.graphql.ron new file mode 100644 index 00000000..baae3460 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_fold_count_tag_and_string_subject.graphql.ron @@ -0,0 +1,18 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Four { + primeFactor @fold @transform(op: "count") @tag(name: "factors") + + successor { + # This filter has nonsensical types: we're attempting to use + # a tag of type `Int!` as an argument to a string filter operator. + name @filter(op: "regex", value: ["%factors"]) + + value @output + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_string_tag_and_fold_count_subject.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_string_tag_and_fold_count_subject.frontend-error.ron new file mode 100644 index 00000000..77a2b0aa --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_string_tag_and_fold_count_subject.frontend-error.ron @@ -0,0 +1 @@ +Err(FilterTypeError(StringFilterOperationOnNonStringSubject("regex", "transformed field \"@fold.count\" of type \"Int!\""))) diff --git a/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_string_tag_and_fold_count_subject.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_string_tag_and_fold_count_subject.graphql-parsed.ron new file mode 100644 index 00000000..7edff169 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_string_tag_and_fold_count_subject.graphql-parsed.ron @@ -0,0 +1,76 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Four", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Four", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "name", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "name", + output: [ + OutputDirective(), + ], + tag: [ + TagDirective(), + ], + )), + (FieldConnection( + position: Pos( + line: 6, + column: 9, + ), + name: "primeFactor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + transform: TransformDirective( + kind: Count, + ), + filter: [ + FilterDirective( + operation: RegexMatches((), TagRef("name")), + ), + ], + )), + )), + ), FieldNode( + position: Pos( + line: 6, + column: 9, + ), + name: "primeFactor", + transform_group: Some(TransformGroup( + transform: TransformDirective( + kind: Count, + ), + filter: [ + FilterDirective( + operation: RegexMatches((), TagRef("name")), + ), + ], + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_string_tag_and_fold_count_subject.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_string_tag_and_fold_count_subject.graphql.ron new file mode 100644 index 00000000..4739d259 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/filter_tag_type_mismatch_string_tag_and_fold_count_subject.graphql.ron @@ -0,0 +1,12 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Four { + name @tag @output + + primeFactor @fold @transform(op: "count") @filter(op: "regex", value: ["%name"]) + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/fold_count_tag_value_used_inside_own_fold.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/fold_count_tag_value_used_inside_own_fold.frontend-error.ron index 2f2e1e83..c383c92b 100644 --- a/trustfall_core/test_data/tests/frontend_errors/fold_count_tag_value_used_inside_own_fold.frontend-error.ron +++ b/trustfall_core/test_data/tests/frontend_errors/fold_count_tag_value_used_inside_own_fold.frontend-error.ron @@ -1 +1 @@ -Err(UndefinedTagInFilter("value", "tagged_count")) +Err(UndefinedTagInFilter("property \"value\"", "tagged_count")) diff --git a/trustfall_core/test_data/tests/frontend_errors/implicit_tag_name_on_transformed_fold_count_value.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/implicit_tag_name_on_transformed_fold_count_value.frontend-error.ron new file mode 100644 index 00000000..9a289eb1 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/implicit_tag_name_on_transformed_fold_count_value.frontend-error.ron @@ -0,0 +1,4 @@ +Err(MultipleErrors(DisplayVec([ + ExplicitTagNameRequired("transformed field \"@fold.count\""), + UndefinedTagInFilter("property \"value\"", "successorcount"), +]))) diff --git a/trustfall_core/test_data/tests/frontend_errors/implicit_tag_name_on_transformed_value.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/implicit_tag_name_on_transformed_fold_count_value.graphql-parsed.ron similarity index 100% rename from trustfall_core/test_data/tests/frontend_errors/implicit_tag_name_on_transformed_value.graphql-parsed.ron rename to trustfall_core/test_data/tests/frontend_errors/implicit_tag_name_on_transformed_fold_count_value.graphql-parsed.ron diff --git a/trustfall_core/test_data/tests/frontend_errors/implicit_tag_name_on_transformed_value.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/implicit_tag_name_on_transformed_fold_count_value.graphql.ron similarity index 100% rename from trustfall_core/test_data/tests/frontend_errors/implicit_tag_name_on_transformed_value.graphql.ron rename to trustfall_core/test_data/tests/frontend_errors/implicit_tag_name_on_transformed_fold_count_value.graphql.ron diff --git a/trustfall_core/test_data/tests/frontend_errors/implicit_tag_name_on_transformed_value.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/implicit_tag_name_on_transformed_value.frontend-error.ron deleted file mode 100644 index d52bed85..00000000 --- a/trustfall_core/test_data/tests/frontend_errors/implicit_tag_name_on_transformed_value.frontend-error.ron +++ /dev/null @@ -1,4 +0,0 @@ -Err(MultipleErrors(DisplayVec([ - ExplicitTagNameRequired("successor"), - UndefinedTagInFilter("value", "successorcount"), -]))) diff --git a/trustfall_core/test_data/tests/frontend_errors/tag_and_filter_in_unrelated_folds.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/tag_and_filter_in_unrelated_folds.frontend-error.ron index f9bf2aa5..8a1f8d9a 100644 --- a/trustfall_core/test_data/tests/frontend_errors/tag_and_filter_in_unrelated_folds.frontend-error.ron +++ b/trustfall_core/test_data/tests/frontend_errors/tag_and_filter_in_unrelated_folds.frontend-error.ron @@ -1 +1 @@ -Err(TagUsedOutsideItsFoldedSubquery("value", "folded")) +Err(TagUsedOutsideItsFoldedSubquery("property \"value\"", "folded")) diff --git a/trustfall_core/test_data/tests/frontend_errors/tag_before_filter_in_different_scopes.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/tag_before_filter_in_different_scopes.frontend-error.ron index b9748c0a..1cd17482 100644 --- a/trustfall_core/test_data/tests/frontend_errors/tag_before_filter_in_different_scopes.frontend-error.ron +++ b/trustfall_core/test_data/tests/frontend_errors/tag_before_filter_in_different_scopes.frontend-error.ron @@ -1 +1 @@ -Err(TagUsedBeforeDefinition("name", "my_tag")) +Err(TagUsedBeforeDefinition("property \"name\"", "my_tag")) diff --git a/trustfall_core/test_data/tests/frontend_errors/tag_from_inside_fold.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/tag_from_inside_fold.frontend-error.ron index f9bf2aa5..8a1f8d9a 100644 --- a/trustfall_core/test_data/tests/frontend_errors/tag_from_inside_fold.frontend-error.ron +++ b/trustfall_core/test_data/tests/frontend_errors/tag_from_inside_fold.frontend-error.ron @@ -1 +1 @@ -Err(TagUsedOutsideItsFoldedSubquery("value", "folded")) +Err(TagUsedOutsideItsFoldedSubquery("property \"value\"", "folded")) diff --git a/trustfall_core/test_data/tests/frontend_errors/tag_used_in_fold_but_defined_after.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/tag_used_in_fold_but_defined_after.frontend-error.ron index 6edd5b65..ea06ba12 100644 --- a/trustfall_core/test_data/tests/frontend_errors/tag_used_in_fold_but_defined_after.frontend-error.ron +++ b/trustfall_core/test_data/tests/frontend_errors/tag_used_in_fold_but_defined_after.frontend-error.ron @@ -1 +1 @@ -Err(UndefinedTagInFilter("value", "my_tag")) +Err(UndefinedTagInFilter("property \"value\"", "my_tag")) diff --git a/trustfall_core/test_data/tests/frontend_errors/undefined_tag.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/undefined_tag.frontend-error.ron index 498fb53c..694c4d8e 100644 --- a/trustfall_core/test_data/tests/frontend_errors/undefined_tag.frontend-error.ron +++ b/trustfall_core/test_data/tests/frontend_errors/undefined_tag.frontend-error.ron @@ -1 +1 @@ -Err(UndefinedTagInFilter("name", "undefined_tag_name")) +Err(UndefinedTagInFilter("property \"name\"", "undefined_tag_name")) From 977d1a741dbeb29b32f40b481ad14aded6470d8a Mon Sep 17 00:00:00 2001 From: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> Date: Fri, 14 Jun 2024 22:50:54 -0400 Subject: [PATCH 06/30] Add `TransformedField` variant to `FieldRef` used for tags. (#625) --- trustfall_core/src/frontend/mod.rs | 1 + trustfall_core/src/interpreter/execution.rs | 4 +++ trustfall_core/src/interpreter/filtering.rs | 3 ++ .../src/interpreter/hints/dynamic.rs | 2 ++ trustfall_core/src/ir/mod.rs | 29 ++++++++++++++++++- 5 files changed, 38 insertions(+), 1 deletion(-) diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index 63c42f86..3146c816 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -421,6 +421,7 @@ fn make_duplicated_output_names_error( ), } } + FieldRef::TransformedField(field) => todo!(), }) .collect(); (k.to_string(), duplicate_values) diff --git a/trustfall_core/src/interpreter/execution.rs b/trustfall_core/src/interpreter/execution.rs index 2c1978b2..483dd63f 100644 --- a/trustfall_core/src/interpreter/execution.rs +++ b/trustfall_core/src/interpreter/execution.rs @@ -220,6 +220,7 @@ fn construct_outputs<'query, AdapterT: Adapter<'query>>( context })); } + FieldRef::TransformedField(_) => todo!(), FieldRef::FoldSpecificField(_) => { unreachable!("found fold-specific field in component outputs: {root_component:#?}") } @@ -505,6 +506,7 @@ fn compute_fold<'query, AdapterT: Adapter<'query> + 'query>( }), ); } + FieldRef::TransformedField(_) => todo!(), } } @@ -668,6 +670,7 @@ mismatch on whether the fold below {expanding_from_vid:?} was inside an `@option ValueOrVec::Value(FieldValue::Uint64(elements.len() as u64)) } }, + FieldRef::TransformedField(field) => todo!(), FieldRef::ContextField(_) => unreachable!( "found ContextField inside a fold's fold-specific outputs: {fold:#?}" ), @@ -738,6 +741,7 @@ mismatch on whether the fold below {expanding_from_vid:?} was inside an `@option context })); } + FieldRef::TransformedField(_) => todo!(), FieldRef::FoldSpecificField(_) => { unreachable!("found fold-specific field in component outputs: {fold:#?}") } diff --git a/trustfall_core/src/interpreter/filtering.rs b/trustfall_core/src/interpreter/filtering.rs index f6b1b87d..11b1e058 100644 --- a/trustfall_core/src/interpreter/filtering.rs +++ b/trustfall_core/src/interpreter/filtering.rs @@ -333,6 +333,9 @@ pub(super) fn apply_filter<'query, AdapterT: Adapter<'query>>( }; apply_filter_with_tagged_argument_value(filter, argument_value_iterator) } + Some(Argument::Tag(FieldRef::TransformedField(_))) => { + todo!() + } None => unreachable!( "no argument present for filter, but not handled in unary filters fn: {filter:?}" ), diff --git a/trustfall_core/src/interpreter/hints/dynamic.rs b/trustfall_core/src/interpreter/hints/dynamic.rs index 5ac27408..25747807 100644 --- a/trustfall_core/src/interpreter/hints/dynamic.rs +++ b/trustfall_core/src/interpreter/hints/dynamic.rs @@ -226,6 +226,7 @@ impl<'a> DynamicallyResolvedValue<'a> { self.resolve_fold_specific_field(fold_field, contexts) } } + FieldRef::TransformedField(_) => todo!(), } } @@ -302,6 +303,7 @@ impl<'a> DynamicallyResolvedValue<'a> { FieldRef::FoldSpecificField(f) => { (f.kind.field_name().into(), f.kind.field_type().clone()) } + FieldRef::TransformedField(_) => todo!(), }; compute_candidate_from_operation( &self.operation, diff --git a/trustfall_core/src/ir/mod.rs b/trustfall_core/src/ir/mod.rs index f3c39433..fc3debd5 100644 --- a/trustfall_core/src/ir/mod.rs +++ b/trustfall_core/src/ir/mod.rs @@ -289,6 +289,7 @@ pub enum TransformationKind { pub enum FieldRef { ContextField(ContextField), FoldSpecificField(FoldSpecificField), + TransformedField(TransformedField), } impl Ord for FieldRef { @@ -298,11 +299,20 @@ impl Ord for FieldRef { .vertex_id .cmp(&f2.vertex_id) .then(f1.field_name.as_ref().cmp(f2.field_name.as_ref())), - (FieldRef::ContextField(_), FieldRef::FoldSpecificField(_)) => Ordering::Less, + ( + FieldRef::ContextField(_), + FieldRef::FoldSpecificField(_) | FieldRef::TransformedField(..), + ) => Ordering::Less, (FieldRef::FoldSpecificField(_), FieldRef::ContextField(_)) => Ordering::Greater, + (FieldRef::FoldSpecificField(..), FieldRef::TransformedField(..)) => Ordering::Less, (FieldRef::FoldSpecificField(f1), FieldRef::FoldSpecificField(f2)) => { f1.fold_eid.cmp(&f2.fold_eid).then(f1.kind.cmp(&f2.kind)) } + ( + FieldRef::TransformedField(..), + FieldRef::ContextField(..) | FieldRef::FoldSpecificField(..), + ) => Ordering::Greater, + (FieldRef::TransformedField(f1), FieldRef::TransformedField(f2)) => f1.tid.cmp(&f2.tid), } } } @@ -325,11 +335,18 @@ impl From for FieldRef { } } +impl From for FieldRef { + fn from(f: TransformedField) -> Self { + Self::TransformedField(f) + } +} + impl FieldRef { pub fn field_type(&self) -> &Type { match self { FieldRef::ContextField(c) => &c.field_type, FieldRef::FoldSpecificField(f) => f.kind.field_type(), + FieldRef::TransformedField(f) => &f.field_type, } } @@ -337,6 +354,7 @@ impl FieldRef { match self { FieldRef::ContextField(c) => c.field_name.as_ref(), FieldRef::FoldSpecificField(f) => f.kind.field_name(), + FieldRef::TransformedField(..) => todo!(), } } @@ -344,6 +362,7 @@ impl FieldRef { match self { FieldRef::ContextField(c) => c.field_name.clone(), FieldRef::FoldSpecificField(f) => f.kind.field_name().into(), + FieldRef::TransformedField(..) => todo!(), } } @@ -352,6 +371,10 @@ impl FieldRef { match self { FieldRef::ContextField(c) => c.vertex_id, FieldRef::FoldSpecificField(f) => f.fold_root_vid, + FieldRef::TransformedField(f) => match &f.value.base { + TransformBase::ContextField(c) => c.vertex_id, + TransformBase::FoldSpecificField(f) => f.fold_root_vid, + }, } } @@ -359,6 +382,10 @@ impl FieldRef { match self { FieldRef::ContextField(_) => None, FieldRef::FoldSpecificField(fold_specific) => Some(fold_specific), + FieldRef::TransformedField(t) => match &t.value.base { + TransformBase::ContextField(_) => None, + TransformBase::FoldSpecificField(fold_specific) => Some(fold_specific), + }, } } } From 387c1ff856c184506710a6d9e9102eedb8b6d4f1 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> Date: Sat, 15 Jun 2024 13:43:05 -0400 Subject: [PATCH 07/30] Implement execution for `@transform` applied to filters' left-hand operand. (#626) --- trustfall_core/src/interpreter/execution.rs | 163 ++++++++++++---- trustfall_core/src/interpreter/filtering.rs | 71 ++----- trustfall_core/src/interpreter/mod.rs | 2 + trustfall_core/src/interpreter/tags.rs | 72 +++++++ .../src/interpreter/transformation.rs | 180 ++++++++++++++++++ trustfall_core/src/ir/mod.rs | 2 +- 6 files changed, 394 insertions(+), 96 deletions(-) create mode 100644 trustfall_core/src/interpreter/tags.rs create mode 100644 trustfall_core/src/interpreter/transformation.rs diff --git a/trustfall_core/src/interpreter/execution.rs b/trustfall_core/src/interpreter/execution.rs index 483dd63f..8835e807 100644 --- a/trustfall_core/src/interpreter/execution.rs +++ b/trustfall_core/src/interpreter/execution.rs @@ -8,15 +8,17 @@ use crate::{ ir::{ Argument, ContextField, EdgeParameters, Eid, FieldRef, FieldValue, FoldSpecificFieldKind, IREdge, IRFold, IRQueryComponent, IRVertex, IndexedQuery, LocalField, Operation, - OperationSubject, Recursive, Vid, + OperationSubject, Recursive, TransformBase, Vid, }, util::BTreeMapTryInsertExt, }; use super::{ - error::QueryArgumentsError, filtering::apply_filter, Adapter, AsVertex, ContextIterator, - ContextOutcomeIterator, DataContext, InterpretedQuery, ResolveEdgeInfo, ResolveInfo, - TaggedValue, ValueOrVec, VertexIterator, + error::QueryArgumentsError, + filtering::apply_filter, + transformation::{apply_transforms, push_transform_argument_tag_values_onto_stack}, + Adapter, AsVertex, ContextIterator, ContextOutcomeIterator, DataContext, InterpretedQuery, + ResolveEdgeInfo, ResolveInfo, TaggedValue, ValueOrVec, VertexIterator, }; #[derive(Debug, Clone)] @@ -621,25 +623,15 @@ fn compute_fold<'query, AdapterT: Adapter<'query> + 'query>( let mut post_filtered_iterator: ContextIterator<'query, AdapterT::Vertex> = Box::new(folded_iterator); for post_fold_filter in fold.post_filters.iter() { - let left = post_fold_filter.left(); - match left { - OperationSubject::FoldSpecificField(fold_specific_field) => { - let remapped_operation = post_fold_filter.map(|_| fold_specific_field.kind, |x| x); - post_filtered_iterator = apply_fold_specific_filter( - adapter.as_ref(), - carrier, - parent_component, - fold.as_ref(), - expanding_from.vid, - &remapped_operation, - post_filtered_iterator, - ); - } - OperationSubject::TransformedField(_) => todo!(), - OperationSubject::LocalField(_) => { - unreachable!("unexpectedly found a fold post-filtering step that references a LocalField: {fold:#?}"); - } - } + post_filtered_iterator = apply_fold_specific_filter( + adapter.as_ref(), + carrier, + parent_component, + fold.as_ref(), + expanding_from.vid, + post_fold_filter, + post_filtered_iterator, + ); } // Compute the outputs from this fold. @@ -809,7 +801,63 @@ fn apply_filter_with_non_folded_field_subject<'query, AdapterT: Adapter<'query>> filter.map_left(|_| field), iterator, ), - OperationSubject::TransformedField(_) => todo!(), + OperationSubject::TransformedField(transformed) => { + let prepped_iterator = push_transform_argument_tag_values_onto_stack( + adapter, + carrier, + component, + current_vid, + &transformed.value.transforms, + iterator, + ); + + let query_variables = + Arc::clone(&carrier.query.as_ref().expect("query was not returned").arguments); + let transform_data = Arc::clone(&transformed.value); + + match &transformed.value.base { + TransformBase::ContextField(field) => { + assert_eq!(current_vid, field.vertex_id, "filter left-hand side was a transformed field from a different vertex: {current_vid:?} {filter:?}"); + let local_field = LocalField { + field_name: field.field_name.clone(), + field_type: field.field_type.clone(), + }; + + let filter_input_iterator = Box::new( + compute_local_field_with_separate_value( + adapter, + carrier, + component, + current_vid, + &local_field, + prepped_iterator, + ) + .map(move |(mut ctx, mut value)| { + value = apply_transforms( + &transform_data, + &query_variables, + &mut ctx.values, + value, + ); + ctx.values.push(value); + ctx + }), + ); + + apply_filter( + adapter, + carrier, + component, + current_vid, + &filter.map(|_| (), |r| r), + filter_input_iterator, + ) + } + TransformBase::FoldSpecificField(..) => unreachable!( + "illegal filter over fold-specific field passed to this function: {filter:?}" + ), + } + } OperationSubject::FoldSpecificField(..) => unreachable!( "illegal filter over fold-specific field passed to this function: {filter:?}" ), @@ -844,27 +892,70 @@ fn apply_fold_specific_filter<'query, AdapterT: Adapter<'query>>( component: &IRQueryComponent, fold: &IRFold, current_vid: Vid, - filter: &Operation, + filter: &Operation, iterator: ContextIterator<'query, AdapterT::Vertex>, ) -> ContextIterator<'query, AdapterT::Vertex> { - let fold_specific_field = filter.left(); - let field_iterator = Box::new(compute_fold_specific_field_with_separate_value(fold.eid, fold_specific_field, iterator).map(|(mut ctx, tagged_value)| { - let value = match tagged_value { - TaggedValue::Some(value) => value, - TaggedValue::NonexistentOptional => { - unreachable!("while applying fold-specific filter, the @fold turned out to not exist: {ctx:?}") + let left = filter.left(); + let (fold_specific_field, transform_data) = match left { + OperationSubject::FoldSpecificField(field) => (field, None), + OperationSubject::TransformedField(transformed) => match &transformed.value.base { + TransformBase::FoldSpecificField(field) => (field, Some(&transformed.value)), + TransformBase::ContextField(_) => { + unreachable!("post-fold filter does not refer to a fold-specific field: {left:?}") } - }; - ctx.values.push(value); - ctx - })); + }, + OperationSubject::LocalField(_) => { + unreachable!("post-fold filter does not refer to a fold-specific field: {left:?}") + } + }; + + let field_iterator: ContextIterator<'query, AdapterT::Vertex> = if let Some(transform_data) = + transform_data + { + let prepped_iterator = push_transform_argument_tag_values_onto_stack( + adapter, + carrier, + component, + current_vid, + &transform_data.transforms, + iterator, + ); + + let query_variables = + Arc::clone(&carrier.query.as_ref().expect("query was not returned").arguments); + let transform_data = Arc::clone(transform_data); + Box::new(compute_fold_specific_field_with_separate_value(fold.eid, &fold_specific_field.kind, prepped_iterator).map(move |(mut ctx, tagged_value)| { + let mut value = match tagged_value { + TaggedValue::Some(value) => value, + TaggedValue::NonexistentOptional => { + unreachable!("while applying fold-specific filter, the @fold turned out to not exist: {ctx:?}") + } + }; + + value = apply_transforms(&transform_data, &query_variables, &mut ctx.values, value); + + ctx.values.push(value); + ctx + })) + } else { + Box::new(compute_fold_specific_field_with_separate_value(fold.eid, &fold_specific_field.kind, iterator).map(|(mut ctx, tagged_value)| { + let value = match tagged_value { + TaggedValue::Some(value) => value, + TaggedValue::NonexistentOptional => { + unreachable!("while applying fold-specific filter, the @fold turned out to not exist: {ctx:?}") + } + }; + ctx.values.push(value); + ctx + })) + }; apply_filter( adapter, carrier, component, current_vid, - &filter.map(|_| (), |r| *r), + &filter.map(|_| (), |r| r), field_iterator, ) } diff --git a/trustfall_core/src/interpreter/filtering.rs b/trustfall_core/src/interpreter/filtering.rs index 11b1e058..3c38996a 100644 --- a/trustfall_core/src/interpreter/filtering.rs +++ b/trustfall_core/src/interpreter/filtering.rs @@ -2,14 +2,11 @@ use std::{fmt::Debug, mem}; use regex::Regex; -use crate::ir::{Argument, FieldRef, FieldValue, IRQueryComponent, LocalField, Operation, Vid}; +use crate::ir::{Argument, FieldValue, IRQueryComponent, Operation, Vid}; use super::{ - execution::{ - compute_context_field_with_separate_value, compute_fold_specific_field_with_separate_value, - compute_local_field_with_separate_value, QueryCarrier, - }, - Adapter, ContextIterator, ContextOutcomeIterator, TaggedValue, + execution::QueryCarrier, tags::compute_tag_with_separate_value, Adapter, ContextIterator, + ContextOutcomeIterator, TaggedValue, }; #[inline(always)] @@ -281,61 +278,17 @@ pub(super) fn apply_filter<'query, AdapterT: Adapter<'query>>( let right_value = query_arguments[var.variable_name.as_ref()].to_owned(); apply_filter_with_static_argument_value(filter, right_value, iterator) } - Some(Argument::Tag(FieldRef::ContextField(context_field))) => { - // TODO: Benchmark if it would be faster to duplicate the filtering code to special-case - // the situation when the tag is always known to exist, so we don't have to unwrap - // a TaggedValue enum, because we know it would be TaggedValue::Some. - let argument_value_iterator = if context_field.vertex_id == current_vid { - // This tag is from the vertex we're currently filtering. That means the field - // whose value we want to get is actually local, so there's no need to compute it - // using the more expensive approach we use for non-local fields. - let local_equivalent_field = LocalField { - field_name: context_field.field_name.clone(), - field_type: context_field.field_type.clone(), - }; - Box::new( - compute_local_field_with_separate_value( - adapter, - carrier, - component, - current_vid, - &local_equivalent_field, - iterator, - ) - .map(|(ctx, value)| (ctx, TaggedValue::Some(value))), - ) - } else { - compute_context_field_with_separate_value( - adapter, - carrier, - component, - context_field, - iterator, - ) - }; - apply_filter_with_tagged_argument_value(filter, argument_value_iterator) - } - Some(Argument::Tag(field_ref @ FieldRef::FoldSpecificField(fold_field))) => { - let argument_value_iterator = if component.folds.contains_key(&fold_field.fold_eid) { - compute_fold_specific_field_with_separate_value( - fold_field.fold_eid, - &fold_field.kind, - iterator, - ) - } else { - // This value represents an imported tag value from an outer component. - // Grab its value from the context itself. - let cloned_ref = field_ref.clone(); - Box::new(iterator.map(move |ctx| { - let right_value = ctx.imported_tags[&cloned_ref].clone(); - (ctx, right_value) - })) - }; + Some(Argument::Tag(field_ref)) => { + let argument_value_iterator = compute_tag_with_separate_value( + adapter, + carrier, + component, + current_vid, + field_ref, + iterator, + ); apply_filter_with_tagged_argument_value(filter, argument_value_iterator) } - Some(Argument::Tag(FieldRef::TransformedField(_))) => { - todo!() - } None => unreachable!( "no argument present for filter, but not handled in unary filters fn: {filter:?}" ), diff --git a/trustfall_core/src/interpreter/mod.rs b/trustfall_core/src/interpreter/mod.rs index 0679af3c..7b4c94f3 100644 --- a/trustfall_core/src/interpreter/mod.rs +++ b/trustfall_core/src/interpreter/mod.rs @@ -17,7 +17,9 @@ mod filtering; pub mod helpers; mod hints; pub mod replay; +mod tags; pub mod trace; +mod transformation; pub use hints::{ CandidateValue, DynamicallyResolvedValue, EdgeInfo, NeighborInfo, QueryInfo, Range, diff --git a/trustfall_core/src/interpreter/tags.rs b/trustfall_core/src/interpreter/tags.rs new file mode 100644 index 00000000..dec68995 --- /dev/null +++ b/trustfall_core/src/interpreter/tags.rs @@ -0,0 +1,72 @@ +use crate::ir::{FieldRef, IRQueryComponent, LocalField, Vid}; + +use super::{ + execution::{ + compute_context_field_with_separate_value, compute_fold_specific_field_with_separate_value, + compute_local_field_with_separate_value, QueryCarrier, + }, + Adapter, ContextIterator, DataContext, TaggedValue, +}; + +pub(super) fn compute_tag_with_separate_value<'query, AdapterT: Adapter<'query>>( + adapter: &AdapterT, + carrier: &mut QueryCarrier, + component: &IRQueryComponent, + current_vid: Vid, + field_ref: &FieldRef, + iterator: ContextIterator<'query, AdapterT::Vertex>, +) -> Box, TaggedValue)> + 'query> { + match field_ref { + FieldRef::ContextField(context_field) => { + // TODO: Benchmark if it would be faster to duplicate the code to special-case + // the situation when the tag is always known to exist, so we don't have to unwrap + // a TaggedValue enum, because we know it would be TaggedValue::Some. + if context_field.vertex_id == current_vid { + // This tag is from the vertex we're currently evaluating. That means the field + // whose value we want to get is actually local, so there's no need to compute it + // using the more expensive approach we use for non-local fields. + let local_equivalent_field = LocalField { + field_name: context_field.field_name.clone(), + field_type: context_field.field_type.clone(), + }; + Box::new( + compute_local_field_with_separate_value( + adapter, + carrier, + component, + current_vid, + &local_equivalent_field, + iterator, + ) + .map(|(ctx, value)| (ctx, TaggedValue::Some(value))), + ) + } else { + compute_context_field_with_separate_value( + adapter, + carrier, + component, + context_field, + iterator, + ) + } + } + FieldRef::FoldSpecificField(fold_field) => { + if component.folds.contains_key(&fold_field.fold_eid) { + compute_fold_specific_field_with_separate_value( + fold_field.fold_eid, + &fold_field.kind, + iterator, + ) + } else { + // This value represents an imported tag value from an outer component. + // Grab its value from the context itself. + let cloned_ref = field_ref.clone(); + Box::new(iterator.map(move |ctx| { + let right_value = ctx.imported_tags[&cloned_ref].clone(); + (ctx, right_value) + })) + } + } + FieldRef::TransformedField(_) => todo!(), + } +} diff --git a/trustfall_core/src/interpreter/transformation.rs b/trustfall_core/src/interpreter/transformation.rs new file mode 100644 index 00000000..e11de1eb --- /dev/null +++ b/trustfall_core/src/interpreter/transformation.rs @@ -0,0 +1,180 @@ +use std::{collections::BTreeMap, sync::Arc}; + +use crate::ir::{Argument, FieldValue, IRQueryComponent, Transform, TransformedValue, Vid}; + +use super::{ + execution::QueryCarrier, tags::compute_tag_with_separate_value, Adapter, ContextIterator, + TaggedValue, +}; + +pub(super) fn push_transform_argument_tag_values_onto_stack<'query, AdapterT: Adapter<'query>>( + adapter: &AdapterT, + carrier: &mut QueryCarrier, + component: &IRQueryComponent, + current_vid: Vid, + transforms: &[Transform], + mut iterator: ContextIterator<'query, AdapterT::Vertex>, +) -> ContextIterator<'query, AdapterT::Vertex> { + // Ensure any non-immediate operands (like values coming from tags) are pushed + // onto the each context's stack before we evaluate the transform. + // We push them on the stack in reverse order, since the stack is LIFO. + for transform in transforms.iter().rev() { + match transform { + Transform::Add(op) => match op { + Argument::Tag(tag) => { + iterator = Box::new( + compute_tag_with_separate_value( + adapter, + carrier, + component, + current_vid, + tag, + iterator, + ) + .map(|(mut ctx, tag_value)| { + let value = match tag_value { + TaggedValue::NonexistentOptional => FieldValue::Null, + TaggedValue::Some(value) => value, + }; + ctx.values.push(value); + ctx + }), + ); + } + Argument::Variable(..) => {} + }, + Transform::Len | Transform::Abs => { + // No tag arguments here! + } + } + } + + iterator +} + +pub(super) fn apply_transforms( + transformed_value: &TransformedValue, + variables: &BTreeMap, FieldValue>, + stack: &mut Vec, + mut value: FieldValue, +) -> FieldValue { + for transform in &transformed_value.transforms { + value = apply_one_transform(transform, variables, stack, &value); + } + + value +} + +#[inline] +fn apply_one_transform( + transform: &Transform, + variables: &BTreeMap, FieldValue>, + stack: &mut Vec, + value: &FieldValue, +) -> FieldValue { + match transform { + Transform::Len => apply_len_transform(value), + Transform::Abs => apply_abs_transform(value), + Transform::Add(argument) => match argument { + Argument::Variable(var) => { + let operand = &variables[&var.variable_name]; + apply_add_transform(value, operand) + } + Argument::Tag(_) => { + let operand = stack.pop().expect( + "empty stack while attempting to resolve transform operand: {transform:?}", + ); + apply_add_transform(value, &operand) + } + }, + } +} + +#[inline] +fn apply_len_transform(value: &FieldValue) -> FieldValue { + match value { + FieldValue::Null => FieldValue::Null, + FieldValue::List(l) => FieldValue::Int64(l.len() as i64), + _ => unreachable!("{value:?}"), + } +} + +#[inline] +fn apply_abs_transform(value: &FieldValue) -> FieldValue { + match value { + FieldValue::Null => FieldValue::Null, + FieldValue::Int64(x) => FieldValue::Uint64(x.unsigned_abs()), + FieldValue::Uint64(x) => FieldValue::Uint64(*x), + FieldValue::Float64(x) => FieldValue::Float64(x.abs()), + _ => unreachable!("{value:?}"), + } +} + +#[inline] +fn apply_add_transform(value: &FieldValue, operand: &FieldValue) -> FieldValue { + match (value, operand) { + (FieldValue::Null, _) => FieldValue::Null, + (_, FieldValue::Null) => FieldValue::Null, + (FieldValue::Int64(x), FieldValue::Int64(y)) => FieldValue::Int64(x.saturating_add(*y)), + (FieldValue::Uint64(x), FieldValue::Uint64(y)) => FieldValue::Uint64(x.saturating_add(*y)), + (FieldValue::Int64(signed), FieldValue::Uint64(unsigned)) + | (FieldValue::Uint64(unsigned), FieldValue::Int64(signed)) => { + add_unlike_signedness_integers(*signed, *unsigned) + } + (FieldValue::Float64(x), FieldValue::Float64(y)) => FieldValue::Float64(x + y), + (FieldValue::Float64(x), FieldValue::Int64(y)) + | (FieldValue::Int64(y), FieldValue::Float64(x)) => FieldValue::Float64(x + (*y as f64)), + (FieldValue::Float64(x), FieldValue::Uint64(y)) + | (FieldValue::Uint64(y), FieldValue::Float64(x)) => FieldValue::Float64(x + (*y as f64)), + _ => unreachable!("{value:?} {operand:?}"), + } +} + +#[inline] +fn add_unlike_signedness_integers(signed: i64, unsigned: u64) -> FieldValue { + if (unsigned > i64::MAX as u64) || !signed.is_negative() { + return FieldValue::Uint64(unsigned.saturating_add_signed(signed)); + } + + FieldValue::Int64(signed.saturating_add_unsigned(unsigned)) +} + +#[cfg(test)] +mod tests { + use crate::ir::FieldValue; + + use super::add_unlike_signedness_integers; + + #[test] + fn test_add_unlike_signedness_integers() { + let test_data = [ + // Adding two non-negative numbers results in a u64. + (123i64, 456u64, FieldValue::Uint64(579)), + (i64::MAX, 0, FieldValue::Uint64(i64::MAX as u64)), + (i64::MAX, 1, FieldValue::Uint64(i64::MAX as u64 + 1)), + // Adding a negative and positive number far from the numeric bounds results in i64. + (-123, 122, FieldValue::Int64(-1)), + (-123, 123, FieldValue::Int64(0)), + (-123, 124, FieldValue::Int64(1)), + // Adding a small negative number to a u64 above the i64 numeric bound results in u64. + (-1, u64::MAX, FieldValue::Uint64(u64::MAX - 1)), + // Addition right up to the numeric bounds. + (i64::MAX, u64::MAX - (i64::MAX as u64), FieldValue::Uint64(u64::MAX)), + (i64::MIN, 0, FieldValue::Int64(i64::MIN)), + // Saturation at the numeric bounds instead of overflow or underflow. + (i64::MAX, u64::MAX, FieldValue::Uint64(u64::MAX)), + ]; + + for (signed, unsigned, expected) in test_data { + let actual = add_unlike_signedness_integers(signed, unsigned); + assert_eq!( + expected, actual, + "{signed} + {unsigned} => {actual:?} but expected {expected:?}" + ); + assert!( + expected.structural_eq(&actual), + "values compare equal but are structurally different: {expected:?} {actual:?}" + ); + } + } +} diff --git a/trustfall_core/src/ir/mod.rs b/trustfall_core/src/ir/mod.rs index fc3debd5..04344409 100644 --- a/trustfall_core/src/ir/mod.rs +++ b/trustfall_core/src/ir/mod.rs @@ -811,7 +811,7 @@ pub enum TransformBase { pub enum Transform { Len, Abs, - Add(FieldRef), + Add(Argument), } impl Transform { From 1bb0dea0445cb72b91aedbd18e154bf4d15dffb9 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Sun, 16 Jun 2024 01:13:18 +0000 Subject: [PATCH 08/30] Deduplicate transform groups in parser and assign `Tid`s to each. --- .../src/graphql_query/directives.rs | 4 +- trustfall_core/src/graphql_query/query.rs | 44 +++++++++++++------ ...xplicit_fold_count_name.graphql-parsed.ron | 12 +---- ...plicit_fold_count_names.graphql-parsed.ron | 10 +---- ..._tag_and_string_subject.graphql-parsed.ron | 11 +---- ..._and_fold_count_subject.graphql-parsed.ron | 11 +---- ...nsform_used_on_property.graphql-parsed.ron | 6 +-- ...ue_used_inside_own_fold.graphql-parsed.ron | 11 +---- ...formed_fold_count_value.graphql-parsed.ron | 9 +--- ...iple_fold_output_errors.graphql-parsed.ron | 12 +---- .../fold_count_filter.graphql-parsed.ron | 11 +---- ...ne_max_fold_size_equals.graphql-parsed.ron | 11 +---- ...rune_max_fold_size_less.graphql-parsed.ron | 11 +---- ...x_fold_size_less_equals.graphql-parsed.ron | 11 +---- ...ne_max_fold_size_one_of.graphql-parsed.ron | 11 +---- ...er_eq_with_negative_arg.graphql-parsed.ron | 11 +---- ...er_gt_with_negative_arg.graphql-parsed.ron | 11 +---- ...r_gte_with_negative_arg.graphql-parsed.ron | 11 +---- ..._filter_lt_over_i64_max.graphql-parsed.ron | 11 +---- ...er_lt_with_negative_arg.graphql-parsed.ron | 11 +---- ...r_lte_with_negative_arg.graphql-parsed.ron | 11 +---- ...ot_eq_with_negative_arg.graphql-parsed.ron | 11 +---- ...ne_of_with_negative_arg.graphql-parsed.ron | 11 +---- ...d_count_filter_on_a_tag.graphql-parsed.ron | 14 +----- ...ne_of_with_negative_arg.graphql-parsed.ron | 11 +---- ...ith_dominated_filter_gt.graphql-parsed.ron | 14 +----- ...ith_dominated_filter_lt.graphql-parsed.ron | 14 +----- ..._with_impossible_filter.graphql-parsed.ron | 14 +----- ...ilter_with_inner_filter.graphql-parsed.ron | 14 +----- ...nt_prefixed_output_name.graphql-parsed.ron | 9 +--- ...nt_tag_explicitly_named.graphql-parsed.ron | 11 +---- ...on_nonexistent_optional.graphql-parsed.ron | 16 +------ ...tag_then_filter_on_fold.graphql-parsed.ron | 25 +---------- ...sed_inside_sibling_fold.graphql-parsed.ron | 11 +---- ...filter_dependent_on_tag.graphql-parsed.ron | 11 +---- ...ilter_and_nested_filter.graphql-parsed.ron | 11 +---- ..._nested_filter_with_tag.graphql-parsed.ron | 11 +---- ...utput_with_count_output.graphql-parsed.ron | 14 +----- ...r_output_with_count_tag.graphql-parsed.ron | 16 +------ .../fold_with_no_outputs.graphql-parsed.ron | 11 +---- ...o_outputs_elided_braces.graphql-parsed.ron | 11 +---- ...and_filter_greater_than.graphql-parsed.ron | 11 +---- ...rm_and_filter_less_than.graphql-parsed.ron | 11 +---- ...sform_and_filter_one_of.graphql-parsed.ron | 11 +---- ...and_filter_greater_than.graphql-parsed.ron | 11 +---- ...rm_and_filter_less_than.graphql-parsed.ron | 11 +---- ...iple_fold_count_outputs.graphql-parsed.ron | 18 +------- ...ested_fold_count_output.graphql-parsed.ron | 18 +------- ...ed_fold_with_no_outputs.graphql-parsed.ron | 22 +--------- ...tional_with_nested_fold.graphql-parsed.ron | 11 +---- .../output_fold_count.graphql-parsed.ron | 9 +--- ...put_fold_count_multiple.graphql-parsed.ron | 9 +--- ...ith_doubly_nested_folds.graphql-parsed.ron | 22 +--------- ...t_fold_with_nested_fold.graphql-parsed.ron | 11 +---- 54 files changed, 91 insertions(+), 595 deletions(-) diff --git a/trustfall_core/src/graphql_query/directives.rs b/trustfall_core/src/graphql_query/directives.rs index cd54e219..ff83a0d3 100644 --- a/trustfall_core/src/graphql_query/directives.rs +++ b/trustfall_core/src/graphql_query/directives.rs @@ -7,7 +7,7 @@ use async_graphql_value::Value; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; -use crate::ir::{Operation, TransformationKind}; +use crate::ir::{Operation, Tid, TransformationKind}; use super::error::ParseError; @@ -531,6 +531,8 @@ impl TryFrom<&Positioned> for RecurseDirective { #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] pub(crate) struct TransformGroup { + pub tid: Tid, + pub transform: TransformDirective, #[serde(default, skip_serializing_if = "Vec::is_empty")] diff --git a/trustfall_core/src/graphql_query/query.rs b/trustfall_core/src/graphql_query/query.rs index 9230b2c6..63d3a766 100644 --- a/trustfall_core/src/graphql_query/query.rs +++ b/trustfall_core/src/graphql_query/query.rs @@ -1,5 +1,6 @@ use std::collections::BTreeMap; use std::fmt::Debug; +use std::num::NonZeroUsize; use std::sync::Arc; use async_graphql_parser::types::{Directive, OperationDefinition}; @@ -10,7 +11,7 @@ use async_graphql_parser::{ use serde::{Deserialize, Serialize}; use smallvec::SmallVec; -use crate::ir::FieldValue; +use crate::ir::{FieldValue, Tid}; use crate::util::BTreeMapTryInsertExt; use super::directives::{FoldGroup, TransformDirective, TransformGroup}; @@ -239,7 +240,10 @@ fn make_directives( Ok(parsed_directives) } -fn make_field_node(field: &Positioned) -> Result { +fn make_field_node( + field: &Positioned, + tid_generator: &mut impl Iterator, +) -> Result { let name = &field.node.name.node; let alias = field.node.alias.as_ref().map(|x| &x.node); @@ -303,9 +307,16 @@ fn make_field_node(field: &Positioned) -> Result { Some(ParsedDirective::Output(o, _)) => output.push(o), Some(ParsedDirective::Tag(t, _)) => tag.push(t), Some(ParsedDirective::Transform(t, _)) => break Some(t), + Some(ParsedDirective::Fold(..)) => { + // Any subsequent `@transform` directives apply to the `@fold`, which means either: + // - this is an edge, so we don't need to process its transform directives + // here -- we've already handled them in edge processing earlier, or + // - this query is invalid and will generate an error anyway, so we don't need to + // keep processing these directives either way. + break None; + } Some( ParsedDirective::Optional(..) - | ParsedDirective::Fold(..) | ParsedDirective::Recurse(..), ) => { // edge-specific directives, ignore them @@ -315,7 +326,7 @@ fn make_field_node(field: &Positioned) -> Result { }; let transform_group = if let Some(transform) = maybe_transform { - Some(make_transform_group(transform, &mut directives_iter)?) + Some(make_transform_group(transform, &mut directives_iter, tid_generator)?) } else { None }; @@ -333,8 +344,8 @@ fn make_field_node(field: &Positioned) -> Result { return Err(ParseError::NestedTypeCoercion(selection.pos)); } Selection::Field(f) => { - let edge = make_field_connection(f)?; - let vertex = make_field_node(f)?; + let edge = make_field_connection(f, tid_generator)?; + let vertex = make_field_node(f, tid_generator)?; connections.push((edge, vertex)); } } @@ -353,7 +364,10 @@ fn make_field_node(field: &Positioned) -> Result { }) } -fn make_field_connection(field: &Positioned) -> Result { +fn make_field_connection( + field: &Positioned, + tid_generator: &mut impl Iterator, +) -> Result { let arguments = field.node.arguments.iter().try_fold( BTreeMap::new(), |mut acc, (name, value)| -> Result, FieldValue>, ParseError> { @@ -426,7 +440,7 @@ fn make_field_connection(field: &Positioned) -> Result) -> Result, + tid_generator: &mut impl Iterator, ) -> Result { let transform_group = if let Some(directive) = directive_iter.next() { match directive { ParsedDirective::Transform(transform, _) => { - Some(make_transform_group(transform, directive_iter)?) + Some(make_transform_group(transform, directive_iter, tid_generator)?) } ParsedDirective::Fold(_, pos) => { return Err(ParseError::UnsupportedDuplicatedDirective("@fold".to_string(), pos)); @@ -472,10 +487,12 @@ fn make_fold_group( fn make_transform_group( transform: TransformDirective, directive_iter: &mut impl Iterator, + tid_generator: &mut impl Iterator, ) -> Result { let mut output = vec![]; let mut tag = vec![]; let mut filter = vec![]; + let tid = tid_generator.next().expect("failed to get next tid"); let retransform = loop { if let Some(directive) = directive_iter.next() { @@ -484,7 +501,7 @@ fn make_transform_group( ParsedDirective::Output(o, _) => output.push(o), ParsedDirective::Tag(t, _) => tag.push(t), ParsedDirective::Transform(xform, _) => { - break Some(Box::new(make_transform_group(xform, directive_iter)?)); + break Some(Box::new(make_transform_group(xform, directive_iter, tid_generator)?)); } ParsedDirective::Fold(..) | ParsedDirective::Optional(..) @@ -505,11 +522,12 @@ fn make_transform_group( // all other directives apply to the transformed value and are processed here. assert!(directive_iter.next().is_none()); - Ok(TransformGroup { transform, output, tag, filter, retransform }) + Ok(TransformGroup { tid, transform, output, tag, filter, retransform }) } /// Parses a query document. May fail if there is no query root. pub fn parse_document(document: &ExecutableDocument) -> Result { + let mut tid_generator = (1usize..).map(|x| Tid(NonZeroUsize::new(x).unwrap())); let query_root = try_get_query_root(document)?; if let Some(dir) = query_root.node.directives.first() { @@ -519,12 +537,12 @@ pub fn parse_document(document: &ExecutableDocument) -> Result Date: Sun, 16 Jun 2024 04:23:17 +0000 Subject: [PATCH 09/30] Partially implement `@transform` on properties in frontend. Only works for zero-arg transform operators, only on properties, and not tested currently. Required some noticeable refactors, and I needed an intermediate checkpoint. --- trustfall_core/src/frontend/error.rs | 5 +- trustfall_core/src/frontend/mod.rs | 267 ++++++++++++++---- trustfall_core/src/frontend/outputs.rs | 13 +- .../src/graphql_query/directives.rs | 27 +- trustfall_core/src/graphql_query/query.rs | 41 +-- .../src/interpreter/transformation.rs | 29 +- trustfall_core/src/ir/mod.rs | 13 + ..._count_on_unfolded_edge.frontend-error.ron | 1 + ..._count_on_unfolded_edge.graphql-parsed.ron | 84 ++++++ ...ansform_count_on_unfolded_edge.graphql.ron | 16 ++ ...dge_without_inner_scope.frontend-error.ron | 1 + ...dge_without_inner_scope.graphql-parsed.ron | 64 +++++ ...olded_edge_without_inner_scope.graphql.ron | 14 + .../fold_and_output.parse-error.ron | 2 +- .../parse_errors/tag_on_fold.parse-error.ron | 2 +- ...form_count_before_fold_on_edge.graphql.ron | 17 ++ ..._count_before_fold_on_edge.parse-error.ron | 4 + ...ld_on_edge_without_inner_scope.graphql.ron | 15 + ...n_edge_without_inner_scope.parse-error.ron | 4 + .../transform_first_directive.graphql.ron | 12 - .../transform_first_directive.parse-error.ron | 4 - 21 files changed, 527 insertions(+), 108 deletions(-) create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge_without_inner_scope.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge_without_inner_scope.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge_without_inner_scope.graphql.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/transform_count_before_fold_on_edge.graphql.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/transform_count_before_fold_on_edge.parse-error.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/transform_count_before_fold_on_edge_without_inner_scope.graphql.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/transform_count_before_fold_on_edge_without_inner_scope.parse-error.ron delete mode 100644 trustfall_core/test_data/tests/parse_errors/transform_first_directive.graphql.ron delete mode 100644 trustfall_core/test_data/tests/parse_errors/transform_first_directive.parse-error.ron diff --git a/trustfall_core/src/frontend/error.rs b/trustfall_core/src/frontend/error.rs index 94e4e333..d2f7307c 100644 --- a/trustfall_core/src/frontend/error.rs +++ b/trustfall_core/src/frontend/error.rs @@ -73,6 +73,9 @@ pub enum FrontendError { #[error("Found an unsupported {1} directive on an edge with @fold: {0}")] UnsupportedDirectiveOnFoldedEdge(String, String), + #[error("Found a @transform directive applied to edge \"{0}\" which is not marked @fold, and therefore cannot be transformed. Did you mean to apply @fold to the edge before the @transform directive?")] + CannotTransformEdgeWithoutFold(String), + #[error("Missing required edge parameter \"{0}\" on edge {1}")] MissingRequiredEdgeParameter(String, String), @@ -103,7 +106,7 @@ pub enum FrontendError { // This error type may or may not be reachable in practice. // At the time of writing, schemas containing fields with ambiguous origin are disallowed, // though they may be allowed in the future. If they are allowed, then this error is reachable. - #[error("Edge {0} has an ambiguous origin, and cannot be used for recursion.")] + #[error("Edge \"{0}\" has an ambiguous origin, and cannot be used for recursion.")] AmbiguousOriginEdgeRecursion(String), #[error( diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index 3146c816..5d7d4589 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -11,14 +11,17 @@ use smallvec::SmallVec; use crate::{ graphql_query::{ - directives::{FilterDirective, FoldGroup, RecurseDirective}, + directives::{ + FilterDirective, FoldGroup, OperatorArgument, RecurseDirective, TransformDirective, + TransformOp, + }, query::{parse_document, FieldConnection, FieldNode, Query}, }, ir::{ get_typename_meta_field, Argument, ContextField, EdgeParameters, Eid, FieldRef, FieldValue, FoldSpecificField, FoldSpecificFieldKind, IREdge, IRFold, IRQuery, IRQueryComponent, - IRVertex, IndexedQuery, LocalField, Operation, OperationSubject, Recursive, - TransformationKind, Type, Vid, TYPENAME_META_FIELD, + IRVertex, IndexedQuery, LocalField, Operation, OperationSubject, Recursive, Tid, Transform, + TransformBase, TransformedField, TransformedValue, Type, Vid, TYPENAME_META_FIELD, }, schema::{get_builtin_scalars, FieldOrigin, Schema}, util::{BTreeMapTryInsertExt, TryCollectUniqueKey}, @@ -874,6 +877,21 @@ where output_handler .begin_nested_scope(next_vid, subfield.alias.as_ref().map(|x| x.as_ref())); + // Ensure we don't have `@transform` applied to the edge directly, + // either completely without `@fold` or placed before it. + // Both cases are an error, though a different error for each for better UX. + if subfield.transform_group.is_some() { + if connection.fold.is_none() { + // `@transform` used without `@fold` on the edge at all. + errors.push(FrontendError::CannotTransformEdgeWithoutFold( + subfield.name.to_string(), + )); + } else { + // `@transform` placed before `@fold` on the edge. + unreachable!("@transform placed before @fold, but this error wasn't caught in the parser") + } + } + if let Some(fold_group) = &connection.fold { if connection.optional.is_some() { errors.push(FrontendError::UnsupportedDirectiveOnFoldedEdge( @@ -1002,58 +1020,113 @@ where (subfield_name, subfield_raw_type.clone(), SmallVec::from([subfield])) }); - for output_directive in &subfield.output { - // TODO: handle outputs of non-fold-related transformed fields here. - let field_ref = FieldRef::ContextField(ContextField { - vertex_id: current_vid, - field_name: subfield.name.clone(), - field_type: subfield_raw_type.clone(), - }); - - // The output's name can be either explicit or local (i.e. implicitly prefixed). - // Explicit names are given explicitly in the directive: - // @output(name: "foo") - // This would result in a "foo" output name, regardless of any prefixes. - // Local names use the field's alias, if present, falling back to the field's name - // otherwise. The local name is appended to any prefixes given as aliases - // applied to the edges whose scopes enclose the output. - if let Some(explicit_name) = output_directive.name.as_ref() { - output_handler - .register_explicitly_named_output(explicit_name.clone(), field_ref); - } else { - let local_name = subfield - .alias - .as_ref() - .map(|x| x.as_ref()) - .unwrap_or_else(|| subfield.name.as_ref()); - output_handler.register_locally_named_output(local_name, None, field_ref); - } - } + let mut transforms: Vec = vec![]; + let mut current_tid: Option = None; + let mut current_type = subfield_raw_type.clone(); + let mut output_directives = subfield.output.as_slice(); + let mut tag_directives = subfield.tag.as_slice(); + let mut next_transform_group = subfield.transform_group.as_ref(); + loop { + for output_directive in output_directives { + let context_field = ContextField { + vertex_id: current_vid, + field_name: subfield.name.clone(), + field_type: subfield_raw_type.clone(), + }; + let field_ref = if let Some(tid) = current_tid { + FieldRef::TransformedField(TransformedField { + value: Arc::new(TransformedValue { + base: TransformBase::ContextField(context_field), + transforms: transforms.clone(), + }), + tid, + field_type: current_type.clone(), + }) + } else { + FieldRef::ContextField(context_field) + }; - for tag_directive in &subfield.tag { - // The tag's name is the first of the following that is defined: - // - the explicit "name" parameter in the @tag directive itself - // - the alias of the field with the @tag directive - // - the name of the field with the @tag directive - let tag_name = - tag_directive.name.as_ref().map(|x| x.as_ref()).unwrap_or_else(|| { - subfield + // The output's name can be either explicit or local (i.e. implicitly prefixed). + // Explicit names are given explicitly in the directive: + // @output(name: "foo") + // This would result in a "foo" output name, regardless of any prefixes. + // Local names use the field's alias, if present, falling back to the field's name + // otherwise. The local name is appended to any prefixes given as aliases + // applied to the edges whose scopes enclose the output. + if let Some(explicit_name) = output_directive.name.as_ref() { + output_handler + .register_explicitly_named_output(explicit_name.clone(), field_ref); + } else { + let local_name = subfield .alias .as_ref() .map(|x| x.as_ref()) - .unwrap_or_else(|| subfield.name.as_ref()) - }); - let tag_field = ContextField { - vertex_id: current_vid, - field_name: subfield.name.clone(), - field_type: subfield_raw_type.clone(), - }; - - // TODO: handle tags on non-fold-related transformed fields here - if let Err(e) = - tags.register_tag(tag_name, FieldRef::ContextField(tag_field), component_path) - { - errors.push(FrontendError::MultipleTagsWithSameName(tag_name.to_string())); + .unwrap_or_else(|| subfield.name.as_ref()); + output_handler.register_locally_named_output( + local_name, + Some(Box::new(transforms.iter().map(|t| t.operation_name()))), + field_ref, + ); + } + } + + for tag_directive in tag_directives { + // The tag's name is the first of the following that is defined: + // - the explicit "name" parameter in the @tag directive itself + // - the alias of the field with the @tag directive + // - the name of the field with the @tag directive + let tag_name = + tag_directive.name.as_ref().map(|x| x.as_ref()).unwrap_or_else(|| { + subfield + .alias + .as_ref() + .map(|x| x.as_ref()) + .unwrap_or_else(|| subfield.name.as_ref()) + }); + + // TODO: deduplicate this block, it's identical to the above. + let context_field = ContextField { + vertex_id: current_vid, + field_name: subfield.name.clone(), + field_type: subfield_raw_type.clone(), + }; + let tag_field = if let Some(tid) = current_tid { + FieldRef::TransformedField(TransformedField { + value: Arc::new(TransformedValue { + base: TransformBase::ContextField(context_field), + transforms: transforms.clone(), + }), + tid, + field_type: current_type.clone(), + }) + } else { + FieldRef::ContextField(context_field) + }; + + if let Err(e) = tags.register_tag(tag_name, tag_field, component_path) { + errors.push(FrontendError::MultipleTagsWithSameName(tag_name.to_string())); + } + } + + if let Some(transform_group) = next_transform_group { + let next_transform = + extract_property_like_transform_from_directive(&transform_group.transform); + current_tid = Some(transform_group.tid); + output_directives = transform_group.output.as_slice(); + tag_directives = transform_group.tag.as_slice(); + next_transform_group = transform_group.retransform.as_deref(); + + current_type = + match determine_transformed_field_type(current_type, &next_transform) { + Ok(t) => t, + Err(e) => { + errors.push(e); + break; + } + }; + transforms.push(next_transform); + } else { + break; } } } else { @@ -1068,6 +1141,88 @@ where } } +fn extract_property_like_transform_from_directive( + transform_directive: &TransformDirective, +) -> Transform { + match &transform_directive.kind { + TransformOp::Len => Transform::Len, + TransformOp::Abs => Transform::Abs, + TransformOp::Add(arg) => Transform::Add(resolve_transform_argument(arg)), + TransformOp::Fadd(arg) => Transform::Fadd(resolve_transform_argument(arg)), + TransformOp::Count => { + // TODO: Add a test for this: it should produce an error, not a panic. + unreachable!("unexpected @transform on property: {transform_directive:?}") + } + } +} + +fn resolve_transform_argument(arg: &OperatorArgument) -> Argument { + todo!() +} + +fn determine_transformed_field_type( + initial_type: Type, + next_transform: &Transform, +) -> Result { + // TODO: refactor type-checking errors into helpers + three categories: + // - inappropriate left-hand type for transform + // - inappropriate tag type for transform + // - inappropriate (conflicting) variable type for transform; + // for this last one, ensure inferring `Int` vs `Int!` narrows to `Int!` correctly + // but other inference mismatches get reported as errors just like from filters + match next_transform { + Transform::Len => { + if initial_type.is_list() { + Ok(Type::new_named_type("Int", initial_type.nullable())) + } else { + Err(todo!()) + } + } + Transform::Abs => { + let base_type = initial_type.base_type(); + if base_type == "Int" || base_type == "Float" { + Ok(initial_type) + } else { + Err(todo!()) + } + } + Transform::Add(op) => { + match op { + Argument::Tag(tag) => { + let op_type = tag.field_type(); + if op_type.base_type() != "Int" { + return Err(todo!()); + } + } + Argument::Variable(var) => { + let op_type = &var.variable_type; + if op_type.base_type() != "Int" { + return Err(todo!()); + } + } + }; + Ok(Type::new_named_type("Int", initial_type.nullable())) + } + Transform::Fadd(op) => { + match op { + Argument::Tag(tag) => { + let op_type = tag.field_type(); + if op_type.base_type() != "Float" { + return Err(todo!()); + } + } + Argument::Variable(var) => { + let op_type = &var.variable_type; + if op_type.base_type() != "Float" { + return Err(todo!()); + } + } + }; + Ok(Type::new_named_type("Float", initial_type.nullable())) + } + } +} + #[allow(clippy::too_many_arguments)] fn make_fold<'schema, 'query, V, E>( schema: &'schema Schema, @@ -1124,15 +1279,21 @@ where if let Some(transform_group) = &fold_group.transform { if transform_group.retransform.is_some() { + // TODO: add support for this, this should work unimplemented!("re-transforming a @fold @transform value is currently not supported"); } let fold_specific_field = match transform_group.transform.kind { - TransformationKind::Count => FoldSpecificField { + TransformOp::Count => FoldSpecificField { fold_eid, fold_root_vid: starting_vid, kind: FoldSpecificFieldKind::Count, }, + _ => { + // TODO: Add a test where we transform a `@fold` with an inappropriate operator, + // to make sure this produces an error, not a panic. + unreachable!("unexpected @transform operator on @fold: {transform_group:?}") + } }; let field_ref = FieldRef::FoldSpecificField(fold_specific_field.clone()); let subject = OperationSubject::FoldSpecificField(fold_specific_field.clone()); @@ -1169,7 +1330,7 @@ where }; output_handler.register_locally_named_output( local_name, - Some(&[fold_specific_field.kind.transform_suffix()]), + Some(Box::new([fold_specific_field.kind.transform_suffix()].into_iter())), field_ref.clone(), ) } diff --git a/trustfall_core/src/frontend/outputs.rs b/trustfall_core/src/frontend/outputs.rs index 34b52874..c10f287d 100644 --- a/trustfall_core/src/frontend/outputs.rs +++ b/trustfall_core/src/frontend/outputs.rs @@ -45,10 +45,10 @@ impl<'query> OutputHandler<'query> { self.component_outputs_stack.pop().expect("stack was unexpectedly empty") } - fn make_output_name( + fn make_output_name<'a>( &self, local_name: &str, - transforms: impl Iterator + 'query, + transforms: impl Iterator + 'a, ) -> Arc { let mut name = String::with_capacity(16); if let Some(prefix) = &self.root_prefix { @@ -80,16 +80,13 @@ impl<'query> OutputHandler<'query> { self.global_outputs.entry(name).or_default().push(value); } - pub(super) fn register_locally_named_output( + pub(super) fn register_locally_named_output<'a>( &mut self, local_name: &str, - transforms: Option<&[&str]>, + transforms: Option + 'a>>, value: FieldRef, ) -> Arc { - let complete_name = self.make_output_name( - local_name, - transforms.map(|inner| inner.iter().copied()).into_iter().flatten(), - ); + let complete_name = self.make_output_name(local_name, transforms.into_iter().flatten()); self.register_output(complete_name.clone(), value); complete_name } diff --git a/trustfall_core/src/graphql_query/directives.rs b/trustfall_core/src/graphql_query/directives.rs index ff83a0d3..53af5cb8 100644 --- a/trustfall_core/src/graphql_query/directives.rs +++ b/trustfall_core/src/graphql_query/directives.rs @@ -7,7 +7,7 @@ use async_graphql_value::Value; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; -use crate::ir::{Operation, Tid, TransformationKind}; +use crate::ir::{Operation, Tid}; use super::error::ParseError; @@ -279,12 +279,12 @@ impl TryFrom<&Positioned> for OutputDirective { /// and /// /// ```ignore -/// TransformDirective { kind: TransformationKind::Count } +/// TransformDirective { kind: TransformOp::Count } /// ``` #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] pub(crate) struct TransformDirective { - /// The `op` in a GraphQL `@transform` - pub kind: TransformationKind, + /// The `op` in a GraphQL `@transform`, including any `value` operands that may be required. + pub kind: TransformOp, } impl TryFrom<&Positioned> for TransformDirective { @@ -332,7 +332,15 @@ impl TryFrom<&Positioned> for TransformDirective { }; let kind = match transform_argument.as_ref() { - "count" => TransformationKind::Count, + "count" => TransformOp::Count, + "len" => TransformOp::Len, + "abs" => TransformOp::Abs, + "+" => { + todo!() + } + "+f" => { + todo!() + } _ => { return Err(ParseError::UnsupportedTransformOperator( transform_argument.to_string(), @@ -345,6 +353,15 @@ impl TryFrom<&Positioned> for TransformDirective { } } +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +pub(crate) enum TransformOp { + Count, + Len, + Abs, + Add(OperatorArgument), + Fadd(OperatorArgument), +} + /// A Trustfall `@tag` directive. /// /// For example, the following Trustfall and Rust would be equivalent: diff --git a/trustfall_core/src/graphql_query/query.rs b/trustfall_core/src/graphql_query/query.rs index 63d3a766..967bde5a 100644 --- a/trustfall_core/src/graphql_query/query.rs +++ b/trustfall_core/src/graphql_query/query.rs @@ -315,10 +315,7 @@ fn make_field_node( // keep processing these directives either way. break None; } - Some( - ParsedDirective::Optional(..) - | ParsedDirective::Recurse(..), - ) => { + Some(ParsedDirective::Optional(..) | ParsedDirective::Recurse(..)) => { // edge-specific directives, ignore them } None => break None, @@ -421,19 +418,11 @@ fn make_field_connection( } } Some(ParsedDirective::Fold(fold, _)) => break Some(fold), - Some(ParsedDirective::Transform(_, pos)) => { - return Err(ParseError::UnsupportedDirectivePosition( - "@transform".to_owned(), - "Cannot transform an edge prior to a @fold directive. \ - Consider adding @fold before the @transform here." - .to_owned(), - pos, - )); - } Some( ParsedDirective::Filter(..) | ParsedDirective::Output(..) - | ParsedDirective::Tag(..), + | ParsedDirective::Tag(..) + | ParsedDirective::Transform(..), ) => {} None => break None, } @@ -469,6 +458,13 @@ fn make_fold_group( ParsedDirective::Fold(_, pos) => { return Err(ParseError::UnsupportedDuplicatedDirective("@fold".to_string(), pos)); } + ParsedDirective::Filter(_, pos) | ParsedDirective::Output(_, pos) | ParsedDirective::Tag(_, pos) => { + return Err(ParseError::UnsupportedDirectivePosition( + directive.kind().to_string(), + "this directive can only be used together with @fold if it's placed after `@fold @transform(op: \"count\")`".to_string(), + pos, + )) + } _ => { return Err(ParseError::UnsupportedDirectivePosition( directive.kind().to_string(), @@ -501,11 +497,20 @@ fn make_transform_group( ParsedDirective::Output(o, _) => output.push(o), ParsedDirective::Tag(t, _) => tag.push(t), ParsedDirective::Transform(xform, _) => { - break Some(Box::new(make_transform_group(xform, directive_iter, tid_generator)?)); + break Some(Box::new(make_transform_group( + xform, + directive_iter, + tid_generator, + )?)); + } + ParsedDirective::Fold(..) => { + return Err(ParseError::UnsupportedDirectivePosition( + directive.kind().to_string(), + "this directive cannot appear after a @transform directive, did you mean to apply the @fold first?".to_string(), + directive.pos(), + )) } - ParsedDirective::Fold(..) - | ParsedDirective::Optional(..) - | ParsedDirective::Recurse(..) => { + ParsedDirective::Optional(..) | ParsedDirective::Recurse(..) => { return Err(ParseError::UnsupportedDirectivePosition( directive.kind().to_string(), "this directive cannot appear after a @transform directive".to_string(), diff --git a/trustfall_core/src/interpreter/transformation.rs b/trustfall_core/src/interpreter/transformation.rs index e11de1eb..739c1cf4 100644 --- a/trustfall_core/src/interpreter/transformation.rs +++ b/trustfall_core/src/interpreter/transformation.rs @@ -20,7 +20,7 @@ pub(super) fn push_transform_argument_tag_values_onto_stack<'query, AdapterT: Ad // We push them on the stack in reverse order, since the stack is LIFO. for transform in transforms.iter().rev() { match transform { - Transform::Add(op) => match op { + Transform::Add(op) | Transform::Fadd(op) => match op { Argument::Tag(tag) => { iterator = Box::new( compute_tag_with_separate_value( @@ -87,6 +87,18 @@ fn apply_one_transform( apply_add_transform(value, &operand) } }, + Transform::Fadd(argument) => match argument { + Argument::Variable(var) => { + let operand = &variables[&var.variable_name]; + apply_fadd_transform(value, operand) + } + Argument::Tag(_) => { + let operand = stack.pop().expect( + "empty stack while attempting to resolve transform operand: {transform:?}", + ); + apply_fadd_transform(value, &operand) + } + }, } } @@ -121,11 +133,18 @@ fn apply_add_transform(value: &FieldValue, operand: &FieldValue) -> FieldValue { | (FieldValue::Uint64(unsigned), FieldValue::Int64(signed)) => { add_unlike_signedness_integers(*signed, *unsigned) } + _ => unreachable!("{value:?} {operand:?}"), + } +} + +#[inline] +fn apply_fadd_transform(value: &FieldValue, operand: &FieldValue) -> FieldValue { + match (value, operand) { + (FieldValue::Null, _) => FieldValue::Null, + (_, FieldValue::Null) => FieldValue::Null, + (FieldValue::Int64(x), FieldValue::Float64(y)) => FieldValue::Float64(y + (*x as f64)), + (FieldValue::Uint64(x), FieldValue::Float64(y)) => FieldValue::Float64(y + (*x as f64)), (FieldValue::Float64(x), FieldValue::Float64(y)) => FieldValue::Float64(x + y), - (FieldValue::Float64(x), FieldValue::Int64(y)) - | (FieldValue::Int64(y), FieldValue::Float64(x)) => FieldValue::Float64(x + (*y as f64)), - (FieldValue::Float64(x), FieldValue::Uint64(y)) - | (FieldValue::Uint64(y), FieldValue::Float64(x)) => FieldValue::Float64(x + (*y as f64)), _ => unreachable!("{value:?} {operand:?}"), } } diff --git a/trustfall_core/src/ir/mod.rs b/trustfall_core/src/ir/mod.rs index 04344409..04895f7f 100644 --- a/trustfall_core/src/ir/mod.rs +++ b/trustfall_core/src/ir/mod.rs @@ -809,9 +809,21 @@ pub enum TransformBase { #[non_exhaustive] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Transform { + /// List length. Null on null inputs. Len, + + /// Absolute value. Allowed on both integer and floating-point types. + /// Null on null inputs. Abs, + + /// Integer addition. For floating-point addition, use [`Transform::Fadd`] instead. Add(Argument), + + /// The floating-point equivalent of [`Transform::Add`]. + /// Separate because we want clean and explicit type-inference for variables: + /// what's the type of `"$arg"` in `@transform(op: "add", value: ["$arg"])`? + /// It's `Int!` because the operator is `add` -- and would have been `Float!` for `fadd`. + Fadd(Argument), } impl Transform { @@ -820,6 +832,7 @@ impl Transform { Self::Len => "len", Self::Abs => "abs", Self::Add(..) => "add", + Self::Fadd(..) => "fadd", } } } diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge.frontend-error.ron new file mode 100644 index 00000000..7cfdd09f --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge.frontend-error.ron @@ -0,0 +1 @@ +Err(CannotTransformEdgeWithoutFold("primeFactor")) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge.graphql-parsed.ron new file mode 100644 index 00000000..9cf155f7 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge.graphql-parsed.ron @@ -0,0 +1,84 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(6), + "min": Int64(4), + }, + ), + 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", + ), FieldNode( + position: Pos( + line: 7, + column: 13, + ), + name: "primeFactor", + connections: [ + (FieldConnection( + position: Pos( + line: 8, + column: 17, + ), + name: "value", + alias: Some("factors"), + ), FieldNode( + position: Pos( + line: 8, + column: 17, + ), + name: "value", + alias: Some("factors"), + output: [ + OutputDirective(), + ], + )), + ], + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Count, + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge.graphql.ron new file mode 100644 index 00000000..416e2480 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge.graphql.ron @@ -0,0 +1,16 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: 4, max: 6) { + ... on Composite { + value @output + + primeFactor @transform(op: "count") @output { + factors: value @output + } + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge_without_inner_scope.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge_without_inner_scope.frontend-error.ron new file mode 100644 index 00000000..7cfdd09f --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge_without_inner_scope.frontend-error.ron @@ -0,0 +1 @@ +Err(CannotTransformEdgeWithoutFold("primeFactor")) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge_without_inner_scope.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge_without_inner_scope.graphql-parsed.ron new file mode 100644 index 00000000..26320fc0 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge_without_inner_scope.graphql-parsed.ron @@ -0,0 +1,64 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(6), + "min": Int64(4), + }, + ), + 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", + ), FieldNode( + position: Pos( + line: 7, + column: 13, + ), + name: "primeFactor", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Count, + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge_without_inner_scope.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge_without_inner_scope.graphql.ron new file mode 100644 index 00000000..5bf20e5e --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge_without_inner_scope.graphql.ron @@ -0,0 +1,14 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: 4, max: 6) { + ... on Composite { + value @output + + primeFactor @transform(op: "count") @output + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/parse_errors/fold_and_output.parse-error.ron b/trustfall_core/test_data/tests/parse_errors/fold_and_output.parse-error.ron index 45744c3f..f0e9e57b 100644 --- a/trustfall_core/test_data/tests/parse_errors/fold_and_output.parse-error.ron +++ b/trustfall_core/test_data/tests/parse_errors/fold_and_output.parse-error.ron @@ -1,4 +1,4 @@ -Err(UnsupportedDirectivePosition("@output", "this directive cannot appear after a @fold directive", Pos( +Err(UnsupportedDirectivePosition("@output", "this directive can only be used together with @fold if it\'s placed after `@fold @transform(op: \"count\")`", Pos( line: 4, column: 42, ))) diff --git a/trustfall_core/test_data/tests/parse_errors/tag_on_fold.parse-error.ron b/trustfall_core/test_data/tests/parse_errors/tag_on_fold.parse-error.ron index 3c88a56e..12c3f6e4 100644 --- a/trustfall_core/test_data/tests/parse_errors/tag_on_fold.parse-error.ron +++ b/trustfall_core/test_data/tests/parse_errors/tag_on_fold.parse-error.ron @@ -1,4 +1,4 @@ -Err(UnsupportedDirectivePosition("@tag", "this directive cannot appear after a @fold directive", Pos( +Err(UnsupportedDirectivePosition("@tag", "this directive can only be used together with @fold if it\'s placed after `@fold @transform(op: \"count\")`", Pos( line: 6, column: 27, ))) diff --git a/trustfall_core/test_data/tests/parse_errors/transform_count_before_fold_on_edge.graphql.ron b/trustfall_core/test_data/tests/parse_errors/transform_count_before_fold_on_edge.graphql.ron new file mode 100644 index 00000000..8651af68 --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/transform_count_before_fold_on_edge.graphql.ron @@ -0,0 +1,17 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: 4, max: 6) { + ... on Composite { + value @output + + # `@fold` must come before `@transform` so this invocation is invalid. + primeFactor @transform(op: "count") @output @fold { + factors: value @output + } + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/parse_errors/transform_count_before_fold_on_edge.parse-error.ron b/trustfall_core/test_data/tests/parse_errors/transform_count_before_fold_on_edge.parse-error.ron new file mode 100644 index 00000000..3c9b656b --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/transform_count_before_fold_on_edge.parse-error.ron @@ -0,0 +1,4 @@ +Err(UnsupportedDirectivePosition("@fold", "this directive cannot appear after a @transform directive, did you mean to apply the @fold first?", Pos( + line: 8, + column: 57, +))) diff --git a/trustfall_core/test_data/tests/parse_errors/transform_count_before_fold_on_edge_without_inner_scope.graphql.ron b/trustfall_core/test_data/tests/parse_errors/transform_count_before_fold_on_edge_without_inner_scope.graphql.ron new file mode 100644 index 00000000..920e0fd4 --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/transform_count_before_fold_on_edge_without_inner_scope.graphql.ron @@ -0,0 +1,15 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: 4, max: 6) { + ... on Composite { + value @output + + # `@fold` must come before `@transform` so this invocation is invalid. + primeFactor @transform(op: "count") @output @fold + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/parse_errors/transform_count_before_fold_on_edge_without_inner_scope.parse-error.ron b/trustfall_core/test_data/tests/parse_errors/transform_count_before_fold_on_edge_without_inner_scope.parse-error.ron new file mode 100644 index 00000000..3c9b656b --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/transform_count_before_fold_on_edge_without_inner_scope.parse-error.ron @@ -0,0 +1,4 @@ +Err(UnsupportedDirectivePosition("@fold", "this directive cannot appear after a @transform directive, did you mean to apply the @fold first?", Pos( + line: 8, + column: 57, +))) diff --git a/trustfall_core/test_data/tests/parse_errors/transform_first_directive.graphql.ron b/trustfall_core/test_data/tests/parse_errors/transform_first_directive.graphql.ron deleted file mode 100644 index 19a17525..00000000 --- a/trustfall_core/test_data/tests/parse_errors/transform_first_directive.graphql.ron +++ /dev/null @@ -1,12 +0,0 @@ -TestGraphQLQuery ( - schema_name: "filesystem", - query: r#" -{ - OriginDirectory { - out_Directory_ContainsFile @transform(op: "count") @output { - name @output - } - } -}"#, - arguments: {}, -) diff --git a/trustfall_core/test_data/tests/parse_errors/transform_first_directive.parse-error.ron b/trustfall_core/test_data/tests/parse_errors/transform_first_directive.parse-error.ron deleted file mode 100644 index cd58db45..00000000 --- a/trustfall_core/test_data/tests/parse_errors/transform_first_directive.parse-error.ron +++ /dev/null @@ -1,4 +0,0 @@ -Err(UnsupportedDirectivePosition("@transform", "Cannot transform an edge prior to a @fold directive. Consider adding @fold before the @transform here.", Pos( - line: 4, - column: 36, -))) From 0b748c255b64d00557070acf941204cde968f0a7 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Tue, 18 Jun 2024 04:37:10 +0000 Subject: [PATCH 10/30] Implemented minimal portion of transform execution logic. First passing end-to-end test featuring `@transform` on a property! --- trustfall_core/src/frontend/error.rs | 2 +- trustfall_core/src/frontend/mod.rs | 6 +- .../src/graphql_query/directives.rs | 2 +- trustfall_core/src/interpreter/execution.rs | 150 +++++++-- .../src/interpreter/transformation.rs | 74 +++-- trustfall_core/src/ir/mod.rs | 11 +- ...bs_transform_and_output.graphql-parsed.ron | 50 +++ ...perty_abs_transform_and_output.graphql.ron | 13 + .../property_abs_transform_and_output.ir.ron | 42 +++ ...operty_abs_transform_and_output.output.ron | 29 ++ ...roperty_abs_transform_and_output.trace.ron | 303 ++++++++++++++++++ 11 files changed, 631 insertions(+), 51 deletions(-) create mode 100644 trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.trace.ron diff --git a/trustfall_core/src/frontend/error.rs b/trustfall_core/src/frontend/error.rs index d2f7307c..183e715e 100644 --- a/trustfall_core/src/frontend/error.rs +++ b/trustfall_core/src/frontend/error.rs @@ -388,7 +388,7 @@ fn write_name_of_transformed_field(buf: &mut String, field: &TransformedField) { }); for transform in &field.value.transforms { buf.push('.'); - buf.push_str(transform.operation_name()); + buf.push_str(transform.operation_output_name()); } buf.push('"'); } diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index 5d7d4589..1e1ab7e4 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -1064,7 +1064,7 @@ where .unwrap_or_else(|| subfield.name.as_ref()); output_handler.register_locally_named_output( local_name, - Some(Box::new(transforms.iter().map(|t| t.operation_name()))), + Some(Box::new(transforms.iter().map(|t| t.operation_output_name()))), field_ref, ); } @@ -1148,7 +1148,7 @@ fn extract_property_like_transform_from_directive( TransformOp::Len => Transform::Len, TransformOp::Abs => Transform::Abs, TransformOp::Add(arg) => Transform::Add(resolve_transform_argument(arg)), - TransformOp::Fadd(arg) => Transform::Fadd(resolve_transform_argument(arg)), + TransformOp::AddF(arg) => Transform::AddF(resolve_transform_argument(arg)), TransformOp::Count => { // TODO: Add a test for this: it should produce an error, not a panic. unreachable!("unexpected @transform on property: {transform_directive:?}") @@ -1203,7 +1203,7 @@ fn determine_transformed_field_type( }; Ok(Type::new_named_type("Int", initial_type.nullable())) } - Transform::Fadd(op) => { + Transform::AddF(op) => { match op { Argument::Tag(tag) => { let op_type = tag.field_type(); diff --git a/trustfall_core/src/graphql_query/directives.rs b/trustfall_core/src/graphql_query/directives.rs index 53af5cb8..855b3a00 100644 --- a/trustfall_core/src/graphql_query/directives.rs +++ b/trustfall_core/src/graphql_query/directives.rs @@ -359,7 +359,7 @@ pub(crate) enum TransformOp { Len, Abs, Add(OperatorArgument), - Fadd(OperatorArgument), + AddF(OperatorArgument), } /// A Trustfall `@tag` directive. diff --git a/trustfall_core/src/interpreter/execution.rs b/trustfall_core/src/interpreter/execution.rs index 8835e807..015bdb72 100644 --- a/trustfall_core/src/interpreter/execution.rs +++ b/trustfall_core/src/interpreter/execution.rs @@ -8,7 +8,7 @@ use crate::{ ir::{ Argument, ContextField, EdgeParameters, Eid, FieldRef, FieldValue, FoldSpecificFieldKind, IREdge, IRFold, IRQueryComponent, IRVertex, IndexedQuery, LocalField, Operation, - OperationSubject, Recursive, TransformBase, Vid, + OperationSubject, Recursive, TransformBase, TransformedField, Vid, }, util::BTreeMapTryInsertExt, }; @@ -16,7 +16,10 @@ use crate::{ use super::{ error::QueryArgumentsError, filtering::apply_filter, - transformation::{apply_transforms, push_transform_argument_tag_values_onto_stack}, + transformation::{ + apply_transforms, push_transform_argument_tag_values_onto_stack, + push_transform_argument_tag_values_onto_stack_during_main_query, + }, Adapter, AsVertex, ContextIterator, ContextOutcomeIterator, DataContext, InterpretedQuery, ResolveEdgeInfo, ResolveInfo, TaggedValue, ValueOrVec, VertexIterator, }; @@ -180,6 +183,106 @@ fn compute_component<'query, AdapterT: Adapter<'query> + 'query>( iterator } +fn compute_context_field_with_separate_value_for_outputs<'query, AdapterT: Adapter<'query>>( + adapter: &AdapterT, + mut query: InterpretedQuery, + root_component: &IRQueryComponent, + field: &ContextField, + iterator: ContextIterator<'query, AdapterT::Vertex>, +) -> (InterpretedQuery, ContextOutcomeIterator<'query, AdapterT::Vertex, FieldValue>) { + let vertex_id = field.vertex_id; + + let moved_iterator = Box::new(iterator.map(move |context| { + let new_vertex = context.vertices[&vertex_id].clone(); + context.move_to_vertex(new_vertex) + })); + + let resolve_info = ResolveInfo::new(query, vertex_id, true); + + let type_name = &root_component.vertices[&vertex_id].type_name; + let field_data_iterator = + adapter.resolve_property(moved_iterator, type_name, &field.field_name, &resolve_info); + query = resolve_info.into_inner(); + + (query, field_data_iterator) +} + +fn compute_transformed_field_with_separate_value_for_outputs<'query, AdapterT: Adapter<'query>>( + adapter: &AdapterT, + carrier: &mut QueryCarrier, + root_component: &IRQueryComponent, + transformed: &TransformedField, + iterator: ContextIterator<'query, AdapterT::Vertex>, +) -> ContextOutcomeIterator<'query, AdapterT::Vertex, FieldValue> { + let prepped_iterator = push_transform_argument_tag_values_onto_stack::( + carrier, + &transformed.value.transforms, + |inner_carrier, tag, inner_iterator| { + let field_data_iterator = match tag { + FieldRef::ContextField(field) => { + let query = inner_carrier.query.take().expect("query was not returned"); + let (query, iter) = compute_context_field_with_separate_value_for_outputs( + adapter, + query, + root_component, + field, + inner_iterator, + ); + inner_carrier.query = Some(query); + iter + } + FieldRef::TransformedField(inner) => { + compute_transformed_field_with_separate_value_for_outputs( + adapter, + inner_carrier, + root_component, + inner, + inner_iterator, + ) + } + FieldRef::FoldSpecificField(_) => todo!(), + }; + + Box::new(field_data_iterator.map(|(mut context, value)| { + context.values.push(value); + context + })) + }, + iterator, + ); + + let mut query = carrier.query.take().expect("query was not returned"); + let query_variables = Arc::clone(&query.arguments); + let transform_data = Arc::clone(&transformed.value); + + let output_iterator: ContextOutcomeIterator<'query, AdapterT::Vertex, FieldValue> = + match &transformed.value.base { + TransformBase::ContextField(field) => { + let (new_query, field_data_iterator) = + compute_context_field_with_separate_value_for_outputs( + adapter, + query, + root_component, + field, + prepped_iterator, + ); + query = new_query; + + Box::new(field_data_iterator.map(move |(mut ctx, mut value)| { + value = + apply_transforms(&transform_data, &query_variables, &mut ctx.values, value); + (ctx, value) + })) + } + TransformBase::FoldSpecificField(..) => unreachable!( + "found transformed fold-specific field in component outputs: {root_component:#?}" + ), + }; + carrier.query = Some(query); + + output_iterator +} + fn construct_outputs<'query, AdapterT: Adapter<'query>>( adapter: &AdapterT, carrier: &mut QueryCarrier, @@ -198,31 +301,38 @@ fn construct_outputs<'query, AdapterT: Adapter<'query>>( let field_ref = &root_component.outputs[output_name]; match field_ref { - FieldRef::ContextField(context_field) => { - let vertex_id = context_field.vertex_id; + FieldRef::ContextField(field) => { + let (new_query, field_data_iterator) = + compute_context_field_with_separate_value_for_outputs( + adapter, + query, + &root_component, + field, + output_iterator, + ); + query = new_query; - let moved_iterator = Box::new(output_iterator.map(move |context| { - let new_vertex = context.vertices[&vertex_id].clone(); - context.move_to_vertex(new_vertex) + output_iterator = Box::new(field_data_iterator.map(|(mut context, value)| { + context.values.push(value); + context })); - - let resolve_info = ResolveInfo::new(query, vertex_id, true); - - let type_name = &root_component.vertices[&vertex_id].type_name; - let field_data_iterator = adapter.resolve_property( - moved_iterator, - type_name, - &context_field.field_name, - &resolve_info, + } + FieldRef::TransformedField(transformed) => { + carrier.query = Some(query); + let field_data_iterator = compute_transformed_field_with_separate_value_for_outputs( + adapter, + carrier, + &root_component, + transformed, + output_iterator, ); - query = resolve_info.into_inner(); + query = carrier.query.take().expect("query was not returned"); output_iterator = Box::new(field_data_iterator.map(|(mut context, value)| { context.values.push(value); context })); } - FieldRef::TransformedField(_) => todo!(), FieldRef::FoldSpecificField(_) => { unreachable!("found fold-specific field in component outputs: {root_component:#?}") } @@ -802,7 +912,7 @@ fn apply_filter_with_non_folded_field_subject<'query, AdapterT: Adapter<'query>> iterator, ), OperationSubject::TransformedField(transformed) => { - let prepped_iterator = push_transform_argument_tag_values_onto_stack( + let prepped_iterator = push_transform_argument_tag_values_onto_stack_during_main_query( adapter, carrier, component, @@ -912,7 +1022,7 @@ fn apply_fold_specific_filter<'query, AdapterT: Adapter<'query>>( let field_iterator: ContextIterator<'query, AdapterT::Vertex> = if let Some(transform_data) = transform_data { - let prepped_iterator = push_transform_argument_tag_values_onto_stack( + let prepped_iterator = push_transform_argument_tag_values_onto_stack_during_main_query( adapter, carrier, component, diff --git a/trustfall_core/src/interpreter/transformation.rs b/trustfall_core/src/interpreter/transformation.rs index 739c1cf4..fcfe2e06 100644 --- a/trustfall_core/src/interpreter/transformation.rs +++ b/trustfall_core/src/interpreter/transformation.rs @@ -1,18 +1,65 @@ use std::{collections::BTreeMap, sync::Arc}; -use crate::ir::{Argument, FieldValue, IRQueryComponent, Transform, TransformedValue, Vid}; +use crate::ir::{ + Argument, FieldRef, FieldValue, IRQueryComponent, Transform, TransformedValue, Vid, +}; use super::{ execution::QueryCarrier, tags::compute_tag_with_separate_value, Adapter, ContextIterator, TaggedValue, }; -pub(super) fn push_transform_argument_tag_values_onto_stack<'query, AdapterT: Adapter<'query>>( +pub(super) fn push_transform_argument_tag_values_onto_stack_during_main_query< + 'query, + AdapterT: Adapter<'query>, +>( adapter: &AdapterT, carrier: &mut QueryCarrier, component: &IRQueryComponent, current_vid: Vid, transforms: &[Transform], + iterator: ContextIterator<'query, AdapterT::Vertex>, +) -> ContextIterator<'query, AdapterT::Vertex> { + let tag_func = move |inner_carrier: &mut QueryCarrier, + tag: &FieldRef, + inner_iterator: ContextIterator<'query, AdapterT::Vertex>| + -> ContextIterator<'query, AdapterT::Vertex> { + Box::new( + compute_tag_with_separate_value( + adapter, + inner_carrier, + component, + current_vid, + tag, + inner_iterator, + ) + .map(|(mut ctx, tag_value)| { + let value = match tag_value { + TaggedValue::NonexistentOptional => FieldValue::Null, + TaggedValue::Some(value) => value, + }; + ctx.values.push(value); + ctx + }), + ) + }; + + push_transform_argument_tag_values_onto_stack::( + carrier, transforms, tag_func, iterator, + ) +} + +/// At different points during query evaluation, we compute tag values differently. +/// The `tag_func` argument allows us to generalize over the tag value computation +/// while reusing the logic for determining which tag values are necessary. +pub(super) fn push_transform_argument_tag_values_onto_stack<'query, AdapterT: Adapter<'query>>( + carrier: &mut QueryCarrier, + transforms: &[Transform], + mut tag_func: impl FnMut( + &mut QueryCarrier, + &FieldRef, + ContextIterator<'query, AdapterT::Vertex>, + ) -> ContextIterator<'query, AdapterT::Vertex>, mut iterator: ContextIterator<'query, AdapterT::Vertex>, ) -> ContextIterator<'query, AdapterT::Vertex> { // Ensure any non-immediate operands (like values coming from tags) are pushed @@ -20,26 +67,9 @@ pub(super) fn push_transform_argument_tag_values_onto_stack<'query, AdapterT: Ad // We push them on the stack in reverse order, since the stack is LIFO. for transform in transforms.iter().rev() { match transform { - Transform::Add(op) | Transform::Fadd(op) => match op { + Transform::Add(op) | Transform::AddF(op) => match op { Argument::Tag(tag) => { - iterator = Box::new( - compute_tag_with_separate_value( - adapter, - carrier, - component, - current_vid, - tag, - iterator, - ) - .map(|(mut ctx, tag_value)| { - let value = match tag_value { - TaggedValue::NonexistentOptional => FieldValue::Null, - TaggedValue::Some(value) => value, - }; - ctx.values.push(value); - ctx - }), - ); + iterator = tag_func(carrier, tag, iterator); } Argument::Variable(..) => {} }, @@ -87,7 +117,7 @@ fn apply_one_transform( apply_add_transform(value, &operand) } }, - Transform::Fadd(argument) => match argument { + Transform::AddF(argument) => match argument { Argument::Variable(var) => { let operand = &variables[&var.variable_name]; apply_fadd_transform(value, operand) diff --git a/trustfall_core/src/ir/mod.rs b/trustfall_core/src/ir/mod.rs index 04895f7f..f581c3a0 100644 --- a/trustfall_core/src/ir/mod.rs +++ b/trustfall_core/src/ir/mod.rs @@ -816,23 +816,26 @@ pub enum Transform { /// Null on null inputs. Abs, - /// Integer addition. For floating-point addition, use [`Transform::Fadd`] instead. + /// Integer addition. For floating-point addition, use [`Transform::AddF`] instead. Add(Argument), /// The floating-point equivalent of [`Transform::Add`]. /// Separate because we want clean and explicit type-inference for variables: /// what's the type of `"$arg"` in `@transform(op: "add", value: ["$arg"])`? /// It's `Int!` because the operator is `add` -- and would have been `Float!` for `fadd`. - Fadd(Argument), + AddF(Argument), } impl Transform { - pub(crate) fn operation_name(&self) -> &str { + /// The human-readable, symbol-free name of the transform operation. + /// + /// Symbol-free means the name is alphanumeric, so the `+` operation is `"add"` etc. + pub(crate) fn operation_output_name(&self) -> &str { match self { Self::Len => "len", Self::Abs => "abs", Self::Add(..) => "add", - Self::Fadd(..) => "fadd", + Self::AddF(..) => "addf", } } } diff --git a/trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.graphql-parsed.ron new file mode 100644 index 00000000..6d05343d --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.graphql-parsed.ron @@ -0,0 +1,50 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(1), + "min": Int64(-1), + }, + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + output: [ + OutputDirective(), + ], + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Abs, + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.graphql.ron b/trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.graphql.ron new file mode 100644 index 00000000..3d9bcfa8 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.graphql.ron @@ -0,0 +1,13 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: -1, max: 1) { + value + @output + @transform(op: "abs") + @output + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.ir.ron b/trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.ir.ron new file mode 100644 index 00000000..c2c93e44 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.ir.ron @@ -0,0 +1,42 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(1), + "min": Int64(-1), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + "valueabs": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Abs, + ], + ), + tid: Tid(1), + field_type: "Int", + )), + }, + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.output.ron b/trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.output.ron new file mode 100644 index 00000000..fe0604f0 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.output.ron @@ -0,0 +1,29 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "value": Output( + name: "value", + value_type: "Int", + vid: Vid(1), + ), + "valueabs": Output( + name: "valueabs", + value_type: "Int", + vid: Vid(1), + ), + }, + results: [ + { + "value": Int64(-1), + "valueabs": Uint64(1), + }, + { + "value": Int64(0), + "valueabs": Uint64(0), + }, + { + "value": Int64(1), + "valueabs": Uint64(1), + }, + ], +) diff --git a/trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.trace.ron b/trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.trace.ron new file mode 100644 index 00000000..f62ec3ff --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_abs_transform_and_output.trace.ron @@ -0,0 +1,303 @@ +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(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Prime(PrimeNumber(-1)))), + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(-1))), + }, + )), + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(-1))), + }, + ), Int64(-1))), + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(-1))), + }, + values: [ + Int64(-1), + ], + )), + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(-1))), + }, + values: [ + Int64(-1), + ], + ), Int64(-1))), + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: None, + content: ProduceQueryResult({ + "value": Int64(-1), + "valueabs": Uint64(1), + }), + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Neither(NeitherNumber(0)))), + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + )), + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + ), Int64(0))), + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + values: [ + Int64(0), + ], + )), + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + values: [ + Int64(0), + ], + ), Int64(0))), + ), + Opid(19): TraceOp( + opid: Opid(19), + parent_opid: None, + content: ProduceQueryResult({ + "value": Int64(0), + "valueabs": Uint64(0), + }), + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Neither(NeitherNumber(1)))), + ), + Opid(23): TraceOp( + opid: Opid(23), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + )), + ), + Opid(24): TraceOp( + opid: Opid(24), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + ), Int64(1))), + ), + Opid(25): TraceOp( + opid: Opid(25), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(1), + ], + )), + ), + Opid(26): TraceOp( + opid: Opid(26), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(1), + ], + ), Int64(1))), + ), + Opid(27): TraceOp( + opid: Opid(27), + parent_opid: None, + content: ProduceQueryResult({ + "value": Int64(1), + "valueabs": Uint64(1), + }), + ), + Opid(28): TraceOp( + opid: Opid(28), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(29): TraceOp( + opid: Opid(29), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(30): TraceOp( + opid: Opid(30), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(31): TraceOp( + opid: Opid(31), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(32): TraceOp( + opid: Opid(32), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(33): TraceOp( + opid: Opid(33), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(34): TraceOp( + opid: Opid(34), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(1), + "min": Int64(-1), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + "valueabs": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Abs, + ], + ), + tid: Tid(1), + field_type: "Int", + )), + }, + ), + ), + ), +) From 9ffbf82c8bb47c7ff74710ecfa7b573c406d77e9 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Tue, 18 Jun 2024 20:56:11 +0000 Subject: [PATCH 11/30] Implement frontend for handling `@filter` on transformed properties. --- trustfall_core/src/frontend/mod.rs | 61 ++++ ...en_transform_and_output.graphql-parsed.ron | 50 +++ ...perty_len_transform_and_output.graphql.ron | 13 + .../property_len_transform_and_output.ir.ron | 42 +++ ...operty_len_transform_and_output.output.ron | 35 ++ ...roperty_len_transform_and_output.trace.ron | 333 ++++++++++++++++++ ...operty_transform_filter.graphql-parsed.ron | 68 ++++ .../property_transform_filter.graphql.ron | 14 + .../property_transform_filter.ir.ron | 53 +++ .../property_transform_filter.output.ron | 18 + .../property_transform_filter.trace.ron | 249 +++++++++++++ ...ansform_filter_with_tag.graphql-parsed.ron | 68 ++++ ...erty_transform_filter_with_tag.graphql.ron | 12 + .../property_transform_filter_with_tag.ir.ron | 48 +++ ...perty_transform_filter_with_tag.output.ron | 15 + ...operty_transform_filter_with_tag.trace.ron | 313 ++++++++++++++++ 16 files changed, 1392 insertions(+) create mode 100644 trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.trace.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_transform_filter.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_transform_filter.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_transform_filter.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_transform_filter.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_transform_filter.trace.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.trace.ron diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index 1e1ab7e4..39cf977d 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -796,6 +796,12 @@ fn make_vertex<'query>( } }; + // Filters have to be processed here, and cannot be processed inside `fill_in_vertex_data()`. + // This is because filters may reference data that won't be complete until all of + // `fill_in_vertex_data()` has finished running: for example, if a filter on one property + // in a given vertex references a tag produced from another property on the same vertex. + // + // For similar reasons, we have to process filters over transformed property values here too. let mut filters = vec![]; for property_name in property_names_by_vertex.get(&vid).into_iter().flatten() { let (_, property_type, property_fields) = @@ -820,6 +826,61 @@ fn make_vertex<'query>( } } } + + let mut transforms: Vec = vec![]; + let mut current_type = property_type.clone(); + let mut next_transform_group = property_field.transform_group.as_ref(); + while let Some(transform_group) = next_transform_group { + let next_transform = + extract_property_like_transform_from_directive(&transform_group.transform); + current_type = match determine_transformed_field_type(current_type, &next_transform) + { + Ok(t) => t, + Err(e) => { + // This error should already have been reported while initially + // processing transforms for output and tag purposes + // in `fill_in_vertex_data()`. + // + // Ignore it here. + break; + } + }; + transforms.push(next_transform); + + for filter_directive in &transform_group.filter { + let base = ContextField { + field_name: property_name.clone(), + field_type: property_type.clone(), + vertex_id: vid, + }; + let transformed_field = TransformedField { + value: Arc::new(TransformedValue { + base: TransformBase::ContextField(base), + transforms: transforms.clone(), + }), + tid: transform_group.tid, + field_type: current_type.clone(), + }; + + match filters::make_filter_expr( + schema, + component_path, + tags, + vid, + OperationSubject::TransformedField(transformed_field), + filter_directive, + ) { + Ok(filter_operation) => { + filters.push(filter_operation); + } + Err(e) => { + errors.extend(e); + } + } + } + + next_transform_group = transform_group.retransform.as_deref(); + } } } diff --git a/trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.graphql-parsed.ron new file mode 100644 index 00000000..c32e1800 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.graphql-parsed.ron @@ -0,0 +1,50 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(1), + "min": Int64(-1), + }, + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "vowelsInName", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "vowelsInName", + output: [ + OutputDirective(), + ], + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Len, + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.graphql.ron b/trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.graphql.ron new file mode 100644 index 00000000..73592a2f --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.graphql.ron @@ -0,0 +1,13 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: -1, max: 1) { + vowelsInName + @output + @transform(op: "len") + @output + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.ir.ron b/trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.ir.ron new file mode 100644 index 00000000..d7604fa9 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.ir.ron @@ -0,0 +1,42 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(1), + "min": Int64(-1), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + ), + }, + outputs: { + "vowelsInName": ContextField(ContextField( + vertex_id: Vid(1), + field_name: "vowelsInName", + field_type: "[String]", + )), + "vowelsInNamelen": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "vowelsInName", + field_type: "[String]", + )), + transforms: [ + Len, + ], + ), + tid: Tid(1), + field_type: "Int", + )), + }, + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.output.ron b/trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.output.ron new file mode 100644 index 00000000..3141b247 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.output.ron @@ -0,0 +1,35 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "vowelsInName": Output( + name: "vowelsInName", + value_type: "[String]", + vid: Vid(1), + ), + "vowelsInNamelen": Output( + name: "vowelsInNamelen", + value_type: "Int", + vid: Vid(1), + ), + }, + results: [ + { + "vowelsInName": Null, + "vowelsInNamelen": Null, + }, + { + "vowelsInName": List([ + String("e"), + String("o"), + ]), + "vowelsInNamelen": Int64(2), + }, + { + "vowelsInName": List([ + String("o"), + String("e"), + ]), + "vowelsInNamelen": Int64(2), + }, + ], +) diff --git a/trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.trace.ron b/trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.trace.ron new file mode 100644 index 00000000..bce95146 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_len_transform_and_output.trace.ron @@ -0,0 +1,333 @@ +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(ResolveProperty(Vid(1), "Number", "vowelsInName")), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "vowelsInName")), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Prime(PrimeNumber(-1)))), + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(-1))), + }, + )), + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(-1))), + }, + ), Null)), + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(-1))), + }, + values: [ + Null, + ], + )), + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(-1))), + }, + values: [ + Null, + ], + ), Null)), + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: None, + content: ProduceQueryResult({ + "vowelsInName": Null, + "vowelsInNamelen": Null, + }), + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Neither(NeitherNumber(0)))), + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + )), + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + ), List([ + String("e"), + String("o"), + ]))), + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + values: [ + List([ + String("e"), + String("o"), + ]), + ], + )), + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + values: [ + List([ + String("e"), + String("o"), + ]), + ], + ), List([ + String("e"), + String("o"), + ]))), + ), + Opid(19): TraceOp( + opid: Opid(19), + parent_opid: None, + content: ProduceQueryResult({ + "vowelsInName": List([ + String("e"), + String("o"), + ]), + "vowelsInNamelen": Int64(2), + }), + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Neither(NeitherNumber(1)))), + ), + Opid(23): TraceOp( + opid: Opid(23), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + )), + ), + Opid(24): TraceOp( + opid: Opid(24), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + ), List([ + String("o"), + String("e"), + ]))), + ), + Opid(25): TraceOp( + opid: Opid(25), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + List([ + String("o"), + String("e"), + ]), + ], + )), + ), + Opid(26): TraceOp( + opid: Opid(26), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + List([ + String("o"), + String("e"), + ]), + ], + ), List([ + String("o"), + String("e"), + ]))), + ), + Opid(27): TraceOp( + opid: Opid(27), + parent_opid: None, + content: ProduceQueryResult({ + "vowelsInName": List([ + String("o"), + String("e"), + ]), + "vowelsInNamelen": Int64(2), + }), + ), + Opid(28): TraceOp( + opid: Opid(28), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(29): TraceOp( + opid: Opid(29), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(30): TraceOp( + opid: Opid(30), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(31): TraceOp( + opid: Opid(31), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(32): TraceOp( + opid: Opid(32), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(33): TraceOp( + opid: Opid(33), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(34): TraceOp( + opid: Opid(34), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(1), + "min": Int64(-1), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + ), + }, + outputs: { + "vowelsInName": ContextField(ContextField( + vertex_id: Vid(1), + field_name: "vowelsInName", + field_type: "[String]", + )), + "vowelsInNamelen": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "vowelsInName", + field_type: "[String]", + )), + transforms: [ + Len, + ], + ), + tid: Tid(1), + field_type: "Int", + )), + }, + ), + ), + ), +) diff --git a/trustfall_core/test_data/tests/valid_queries/property_transform_filter.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/property_transform_filter.graphql-parsed.ron new file mode 100644 index 00000000..770f56a6 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_transform_filter.graphql-parsed.ron @@ -0,0 +1,68 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(1), + "min": Int64(-1), + }, + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + output: [ + OutputDirective(), + ], + )), + (FieldConnection( + position: Pos( + line: 6, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 6, + column: 9, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Abs, + ), + filter: [ + FilterDirective( + operation: GreaterThan((), VariableRef("zero")), + ), + ], + )), + )), + ], + ), + ), + arguments: { + "zero": Uint64(0), + }, +)) diff --git a/trustfall_core/test_data/tests/valid_queries/property_transform_filter.graphql.ron b/trustfall_core/test_data/tests/valid_queries/property_transform_filter.graphql.ron new file mode 100644 index 00000000..ebcc6187 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_transform_filter.graphql.ron @@ -0,0 +1,14 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: -1, max: 1) { + value @output + + value @transform(op: "abs") @filter(op: ">", value: ["$zero"]) + } +}"#, + arguments: { + "zero": Uint64(0), + }, +) diff --git a/trustfall_core/test_data/tests/valid_queries/property_transform_filter.ir.ron b/trustfall_core/test_data/tests/valid_queries/property_transform_filter.ir.ron new file mode 100644 index 00000000..16979740 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_transform_filter.ir.ron @@ -0,0 +1,53 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(1), + "min": Int64(-1), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + filters: [ + GreaterThan(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Abs, + ], + ), + tid: Tid(1), + field_type: "Int", + )), Variable(VariableRef( + variable_name: "zero", + variable_type: "Int!", + ))), + ], + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + }, + ), + variables: { + "zero": "Int!", + }, + ), + arguments: { + "zero": Uint64(0), + }, +)) diff --git a/trustfall_core/test_data/tests/valid_queries/property_transform_filter.output.ron b/trustfall_core/test_data/tests/valid_queries/property_transform_filter.output.ron new file mode 100644 index 00000000..a9fb70f0 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_transform_filter.output.ron @@ -0,0 +1,18 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "value": Output( + name: "value", + value_type: "Int", + vid: Vid(1), + ), + }, + results: [ + { + "value": Int64(-1), + }, + { + "value": Int64(1), + }, + ], +) diff --git a/trustfall_core/test_data/tests/valid_queries/property_transform_filter.trace.ron b/trustfall_core/test_data/tests/valid_queries/property_transform_filter.trace.ron new file mode 100644 index 00000000..03949e75 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_transform_filter.trace.ron @@ -0,0 +1,249 @@ +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(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Prime(PrimeNumber(-1)))), + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: {}, + )), + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: {}, + ), Int64(-1))), + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(-1))), + }, + )), + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(-1))), + }, + ), Int64(-1))), + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: None, + content: ProduceQueryResult({ + "value": Int64(-1), + }), + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Neither(NeitherNumber(0)))), + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: {}, + )), + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: {}, + ), Int64(0))), + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Neither(NeitherNumber(1)))), + ), + Opid(19): TraceOp( + opid: Opid(19), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + )), + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + ), Int64(1))), + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + )), + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + ), Int64(1))), + ), + Opid(23): TraceOp( + opid: Opid(23), + parent_opid: None, + content: ProduceQueryResult({ + "value": Int64(1), + }), + ), + Opid(24): TraceOp( + opid: Opid(24), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(25): TraceOp( + opid: Opid(25), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(26): TraceOp( + opid: Opid(26), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(27): TraceOp( + opid: Opid(27), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(28): TraceOp( + opid: Opid(28), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(29): TraceOp( + opid: Opid(29), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(30): TraceOp( + opid: Opid(30), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(1), + "min": Int64(-1), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + filters: [ + GreaterThan(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Abs, + ], + ), + tid: Tid(1), + field_type: "Int", + )), Variable(VariableRef( + variable_name: "zero", + variable_type: "Int!", + ))), + ], + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + }, + ), + variables: { + "zero": "Int!", + }, + ), + arguments: { + "zero": Uint64(0), + }, + ), +) diff --git a/trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.graphql-parsed.ron new file mode 100644 index 00000000..557e6cd6 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.graphql-parsed.ron @@ -0,0 +1,68 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(1), + "min": Int64(-1), + }, + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + output: [ + OutputDirective(), + ], + tag: [ + TagDirective(), + ], + )), + (FieldConnection( + position: Pos( + line: 6, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 6, + column: 9, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Abs, + ), + filter: [ + FilterDirective( + operation: GreaterThan((), TagRef("value")), + ), + ], + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.graphql.ron b/trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.graphql.ron new file mode 100644 index 00000000..bf195057 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.graphql.ron @@ -0,0 +1,12 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: -1, max: 1) { + value @output @tag + + value @transform(op: "abs") @filter(op: ">", value: ["%value"]) + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.ir.ron new file mode 100644 index 00000000..35ad468f --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.ir.ron @@ -0,0 +1,48 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(1), + "min": Int64(-1), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + filters: [ + GreaterThan(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Abs, + ], + ), + tid: Tid(1), + field_type: "Int", + )), Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + }, + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.output.ron b/trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.output.ron new file mode 100644 index 00000000..c2ee21bf --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.output.ron @@ -0,0 +1,15 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "value": Output( + name: "value", + value_type: "Int", + vid: Vid(1), + ), + }, + results: [ + { + "value": Int64(-1), + }, + ], +) diff --git a/trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.trace.ron new file mode 100644 index 00000000..679b78e0 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_transform_filter_with_tag.trace.ron @@ -0,0 +1,313 @@ +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(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "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(Prime(PrimeNumber(-1)))), + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: {}, + )), + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: {}, + ), Int64(-1))), + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: {}, + values: [ + Uint64(1), + ], + )), + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: {}, + values: [ + Uint64(1), + ], + ), Int64(-1))), + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: Some(Opid(4)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(-1))), + }, + )), + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(4)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(-1))), + }, + ), Int64(-1))), + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: None, + content: ProduceQueryResult({ + "value": Int64(-1), + }), + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + 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: YieldFrom(ResolveStartingVertices(Neither(NeitherNumber(0)))), + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: {}, + )), + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: {}, + ), Int64(0))), + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: {}, + values: [ + Uint64(0), + ], + )), + ), + Opid(23): TraceOp( + opid: Opid(23), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: {}, + values: [ + Uint64(0), + ], + ), Int64(0))), + ), + Opid(24): TraceOp( + opid: Opid(24), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(25): TraceOp( + opid: Opid(25), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(26): TraceOp( + opid: Opid(26), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Neither(NeitherNumber(1)))), + ), + Opid(27): TraceOp( + opid: Opid(27), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + )), + ), + Opid(28): TraceOp( + opid: Opid(28), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + ), Int64(1))), + ), + Opid(29): TraceOp( + opid: Opid(29), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + values: [ + Uint64(1), + ], + )), + ), + Opid(30): TraceOp( + opid: Opid(30), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + values: [ + Uint64(1), + ], + ), Int64(1))), + ), + Opid(31): TraceOp( + opid: Opid(31), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(32): TraceOp( + opid: Opid(32), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(33): TraceOp( + opid: Opid(33), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(34): TraceOp( + opid: Opid(34), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(35): TraceOp( + opid: Opid(35), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(36): TraceOp( + opid: Opid(36), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(37): TraceOp( + opid: Opid(37), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + Opid(38): TraceOp( + opid: Opid(38), + parent_opid: Some(Opid(4)), + content: InputIteratorExhausted, + ), + Opid(39): TraceOp( + opid: Opid(39), + parent_opid: Some(Opid(4)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(1), + "min": Int64(-1), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + filters: [ + GreaterThan(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Abs, + ], + ), + tid: Tid(1), + field_type: "Int", + )), Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + }, + ), + ), + ), +) From da482155fdee55473039d7ff9dc515a9aab511b4 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Wed, 19 Jun 2024 16:33:27 +0000 Subject: [PATCH 12/30] Minor cleanup refactoring. --- trustfall_core/src/frontend/mod.rs | 333 ++++++++++++++++------------- 1 file changed, 188 insertions(+), 145 deletions(-) diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index 39cf977d..f4f9bda4 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -1038,167 +1038,210 @@ where || subfield_name == TYPENAME_META_FIELD { // Processing a property. + fill_in_property_data( + property_names_by_vertex, + properties, + component_path, + output_handler, + tags, + current_vid, + subfield, + connection, + subfield_name, + subfield_raw_type, + &mut errors, + ); + } else { + unreachable!("field name: {}", subfield_name); + } + } - // @fold is not allowed on a property - if connection.fold.is_some() { - errors.push(FrontendError::UnsupportedDirectiveOnProperty( - "@fold".into(), - subfield.name.to_string(), - )); - } + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } +} - // @optional is not allowed on a property - if connection.optional.is_some() { - errors.push(FrontendError::UnsupportedDirectiveOnProperty( - "@optional".into(), - subfield.name.to_string(), - )); - } +#[allow(clippy::type_complexity)] +#[allow(clippy::too_many_arguments)] +fn fill_in_property_data<'query>( + property_names_by_vertex: &mut BTreeMap>>, + properties: &mut BTreeMap<(Vid, Arc), (Arc, Type, SmallVec<[&'query FieldNode; 1]>)>, + component_path: &mut ComponentPath, + output_handler: &mut OutputHandler<'query>, + tags: &mut TagHandler<'query>, + current_vid: Vid, + property_node: &'query FieldNode, + connection: &'query FieldConnection, + property_name: &str, + property_type: Type, + errors: &mut Vec, +) { + // @fold is not allowed on a property + if connection.fold.is_some() { + errors.push(FrontendError::UnsupportedDirectiveOnProperty( + "@fold".into(), + property_node.name.to_string(), + )); + } - // @recurse is not allowed on a property - if connection.recurse.is_some() { - errors.push(FrontendError::UnsupportedDirectiveOnProperty( - "@recurse".into(), - subfield.name.to_string(), - )); - } + // @optional is not allowed on a property + if connection.optional.is_some() { + errors.push(FrontendError::UnsupportedDirectiveOnProperty( + "@optional".into(), + property_node.name.to_string(), + )); + } - let subfield_name: Arc = subfield_name.into(); - let key = (current_vid, subfield_name.clone()); - properties - .entry(key) - .and_modify(|(prior_name, prior_type, subfields)| { - assert_eq!(subfield_name.as_ref(), prior_name.as_ref()); - assert_eq!(&subfield_raw_type, prior_type); - subfields.push(subfield); - }) - .or_insert_with(|| { - property_names_by_vertex - .entry(current_vid) - .or_default() - .push(subfield_name.clone()); - - (subfield_name, subfield_raw_type.clone(), SmallVec::from([subfield])) - }); + // @recurse is not allowed on a property + if connection.recurse.is_some() { + errors.push(FrontendError::UnsupportedDirectiveOnProperty( + "@recurse".into(), + property_node.name.to_string(), + )); + } - let mut transforms: Vec = vec![]; - let mut current_tid: Option = None; - let mut current_type = subfield_raw_type.clone(); - let mut output_directives = subfield.output.as_slice(); - let mut tag_directives = subfield.tag.as_slice(); - let mut next_transform_group = subfield.transform_group.as_ref(); - loop { - for output_directive in output_directives { - let context_field = ContextField { - vertex_id: current_vid, - field_name: subfield.name.clone(), - field_type: subfield_raw_type.clone(), - }; - let field_ref = if let Some(tid) = current_tid { - FieldRef::TransformedField(TransformedField { - value: Arc::new(TransformedValue { - base: TransformBase::ContextField(context_field), - transforms: transforms.clone(), - }), - tid, - field_type: current_type.clone(), - }) - } else { - FieldRef::ContextField(context_field) - }; + let property_name: Arc = property_name.into(); + let key = (current_vid, property_name.clone()); + properties + .entry(key) + .and_modify(|(prior_name, prior_type, subfields)| { + assert_eq!(property_name.as_ref(), prior_name.as_ref()); + assert_eq!(&property_type, prior_type); + subfields.push(property_node); + }) + .or_insert_with(|| { + property_names_by_vertex.entry(current_vid).or_default().push(property_name.clone()); - // The output's name can be either explicit or local (i.e. implicitly prefixed). - // Explicit names are given explicitly in the directive: - // @output(name: "foo") - // This would result in a "foo" output name, regardless of any prefixes. - // Local names use the field's alias, if present, falling back to the field's name - // otherwise. The local name is appended to any prefixes given as aliases - // applied to the edges whose scopes enclose the output. - if let Some(explicit_name) = output_directive.name.as_ref() { - output_handler - .register_explicitly_named_output(explicit_name.clone(), field_ref); - } else { - let local_name = subfield - .alias - .as_ref() - .map(|x| x.as_ref()) - .unwrap_or_else(|| subfield.name.as_ref()); - output_handler.register_locally_named_output( - local_name, - Some(Box::new(transforms.iter().map(|t| t.operation_output_name()))), - field_ref, - ); - } - } + (property_name, property_type.clone(), SmallVec::from([property_node])) + }); - for tag_directive in tag_directives { - // The tag's name is the first of the following that is defined: - // - the explicit "name" parameter in the @tag directive itself - // - the alias of the field with the @tag directive - // - the name of the field with the @tag directive - let tag_name = - tag_directive.name.as_ref().map(|x| x.as_ref()).unwrap_or_else(|| { - subfield - .alias - .as_ref() - .map(|x| x.as_ref()) - .unwrap_or_else(|| subfield.name.as_ref()) - }); - - // TODO: deduplicate this block, it's identical to the above. - let context_field = ContextField { - vertex_id: current_vid, - field_name: subfield.name.clone(), - field_type: subfield_raw_type.clone(), - }; - let tag_field = if let Some(tid) = current_tid { - FieldRef::TransformedField(TransformedField { - value: Arc::new(TransformedValue { - base: TransformBase::ContextField(context_field), - transforms: transforms.clone(), - }), - tid, - field_type: current_type.clone(), - }) - } else { - FieldRef::ContextField(context_field) - }; + let mut transforms: Vec = vec![]; + let mut current_tid: Option = None; + let mut current_type = property_type.clone(); + let mut output_directives = property_node.output.as_slice(); + let mut tag_directives = property_node.tag.as_slice(); + let mut next_transform_group = property_node.transform_group.as_ref(); + + loop { + for output_directive in output_directives { + let field_ref = make_field_ref_for_possibly_transformed_property( + current_vid, + &property_node.name, + &property_type, + current_tid, + &transforms, + ¤t_type, + ); - if let Err(e) = tags.register_tag(tag_name, tag_field, component_path) { - errors.push(FrontendError::MultipleTagsWithSameName(tag_name.to_string())); - } - } + // The output's name can be either explicit or local (i.e. implicitly prefixed). + // Explicit names are given explicitly in the directive: + // @output(name: "foo") + // This would result in a "foo" output name, regardless of any prefixes. + // Local names use the field's alias, if present, falling back to the field's name + // otherwise. The local name is appended to any prefixes given as aliases + // applied to the edges whose scopes enclose the output. + if let Some(explicit_name) = output_directive.name.as_ref() { + output_handler.register_explicitly_named_output(explicit_name.clone(), field_ref); + } else { + let local_name = property_node + .alias + .as_ref() + .map(|x| x.as_ref()) + .unwrap_or_else(|| property_node.name.as_ref()); + output_handler.register_locally_named_output( + local_name, + Some(Box::new(transforms.iter().map(|t| t.operation_output_name()))), + field_ref, + ); + } + } - if let Some(transform_group) = next_transform_group { - let next_transform = - extract_property_like_transform_from_directive(&transform_group.transform); - current_tid = Some(transform_group.tid); - output_directives = transform_group.output.as_slice(); - tag_directives = transform_group.tag.as_slice(); - next_transform_group = transform_group.retransform.as_deref(); - - current_type = - match determine_transformed_field_type(current_type, &next_transform) { - Ok(t) => t, - Err(e) => { - errors.push(e); - break; - } - }; - transforms.push(next_transform); - } else { + for tag_directive in tag_directives { + // The tag's name is the first of the following that is defined: + // - the explicit "name" parameter in the @tag directive itself + // - the alias of the field with the @tag directive + // - the name of the field with the @tag directive + let tag_name = tag_directive.name.as_ref().map(|x| x.as_ref()).unwrap_or_else(|| { + property_node + .alias + .as_ref() + .map(|x| x.as_ref()) + .unwrap_or_else(|| property_node.name.as_ref()) + }); + + let tag_field = make_field_ref_for_possibly_transformed_property( + current_vid, + &property_node.name, + &property_type, + current_tid, + &transforms, + ¤t_type, + ); + + if let Err(e) = tags.register_tag(tag_name, tag_field, component_path) { + errors.push(FrontendError::MultipleTagsWithSameName(tag_name.to_string())); + } + } + + if let Some(transform_group) = next_transform_group { + let next_transform = + extract_property_like_transform_from_directive(&transform_group.transform); + current_tid = Some(transform_group.tid); + output_directives = transform_group.output.as_slice(); + tag_directives = transform_group.tag.as_slice(); + next_transform_group = transform_group.retransform.as_deref(); + + current_type = match determine_transformed_field_type(current_type, &next_transform) { + Ok(t) => t, + Err(e) => { + errors.push(e); break; } - } + }; + transforms.push(next_transform); } else { - unreachable!("field name: {}", subfield_name); + break; } } +} - if errors.is_empty() { - Ok(()) +fn make_field_ref_for_possibly_transformed_property( + current_vid: Vid, + property_name: &Arc, + property_type: &Type, + current_tid: Option, + transforms: &[Transform], + transformed_type: &Type, +) -> FieldRef { + let context_field = ContextField { + vertex_id: current_vid, + field_name: Arc::clone(property_name), + field_type: property_type.clone(), + }; + if let Some(tid) = current_tid { + FieldRef::TransformedField(TransformedField { + value: Arc::new(TransformedValue { + base: TransformBase::ContextField(context_field), + // TODO: This `.to_owned()` is a full copy of the `Vec`, + // so if we have a very deep chain of `@transform` directives where each + // intermediate result is used in a tag, filter, or output, + // that would result in O(n^2) copies. + // + // In principle, we should be able to make a "smarter" data type here: + // we only ever append to the underlying `Vec` as we process + // new `@transform` directives, so prior uses of it see an immutable + // prefix of the full final `Vec`. This may lend itself to + // an `Arc`-ed shared-ownership prefix view over an underlying allocation. + // This is a potential future optimization opportunity! + transforms: transforms.to_owned(), + }), + tid, + field_type: transformed_type.clone(), + }) } else { - Err(errors) + FieldRef::ContextField(context_field) } } From 20cc45d9004cf6ad20f7b1c14000530af20dc187 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Wed, 19 Jun 2024 18:47:02 +0000 Subject: [PATCH 13/30] Produce proper error if `count` transform is used on a property-like value. --- trustfall_core/src/frontend/error.rs | 65 +++++++++++++++++-- trustfall_core/src/frontend/mod.rs | 58 +++++++++++++---- .../src/graphql_query/directives.rs | 16 +++++ ...roperty_produces_advice.frontend-error.ron | 1 + ...roperty_produces_advice.graphql-parsed.ron | 43 ++++++++++++ ..._list_property_produces_advice.graphql.ron | 10 +++ ...sform_count_on_property.frontend-error.ron | 1 + ...sform_count_on_property.graphql-parsed.ron | 43 ++++++++++++ .../transform_count_on_property.graphql.ron | 10 +++ ...on_transformed_property.frontend-error.ron | 1 + ...on_transformed_property.graphql-parsed.ron | 49 ++++++++++++++ ..._count_on_transformed_property.graphql.ron | 10 +++ 12 files changed, 288 insertions(+), 19 deletions(-) create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_list_property_produces_advice.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_list_property_produces_advice.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_list_property_produces_advice.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_property.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_property.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_property.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_transformed_property.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_transformed_property.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_transformed_property.graphql.ron diff --git a/trustfall_core/src/frontend/error.rs b/trustfall_core/src/frontend/error.rs index 183e715e..5ba2bac3 100644 --- a/trustfall_core/src/frontend/error.rs +++ b/trustfall_core/src/frontend/error.rs @@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize}; use crate::{ ir::{ - Argument, FieldValue, FoldSpecificField, OperationSubject, TransformBase, TransformedField, - Type, + Argument, FieldValue, FoldSpecificField, OperationSubject, Transform, TransformBase, + TransformedField, Type, }, util::DisplayVec, }; @@ -58,6 +58,9 @@ pub enum FrontendError { #[error("Incompatible types encountered in @filter: {0}")] FilterTypeError(#[from] FilterTypeError), + #[error("Incompatible types encountered in @transform: {0}")] + TransformTypeError(#[from] TransformTypeError), + #[error("Found {0} applied to \"{1}\" property, which is not supported since that directive can only be applied to edges.")] UnsupportedDirectiveOnProperty(String, String), @@ -379,14 +382,62 @@ impl FilterTypeError { } } -fn write_name_of_transformed_field(buf: &mut String, field: &TransformedField) { - buf.push_str("transformed field \""); +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, thiserror::Error)] +pub enum TransformTypeError { + #[error( + "Transform operation \"{0}\" may only be applied to edges that are marked @fold, \ + but is used on {1}.{2}" + )] + FoldSpecificTransformUsedOnProperty(String, String, String), +} - buf.push_str(match &field.value.base { +impl TransformTypeError { + pub(crate) fn fold_specific_transform_on_propertylike_value( + transform_op: &str, + property_name: &str, + transforms_so_far: &[Transform], + type_so_far: &Type, + ) -> Self { + let base_name = if transforms_so_far.is_empty() { + format!("property \"{property_name}\"") + } else { + let mut buf = String::with_capacity(16); + write_name_of_transformed_field_by_parts(&mut buf, property_name, transforms_so_far); + buf + }; + + let advice = if type_so_far.is_list() { + format!( + " To get the number of elements in the list value here (type \"{type_so_far}\"), \ + use the \"len\" transform operation instead.", + ) + } else { + String::new() + }; + + Self::FoldSpecificTransformUsedOnProperty(transform_op.to_string(), base_name, advice) + } +} + +fn write_name_of_transformed_field(buf: &mut String, field: &TransformedField) { + let base_name = match &field.value.base { TransformBase::ContextField(c) => &c.field_name, TransformBase::FoldSpecificField(f) => f.kind.field_name(), - }); - for transform in &field.value.transforms { + }; + + write_name_of_transformed_field_by_parts(buf, base_name, &field.value.transforms); +} + +fn write_name_of_transformed_field_by_parts( + buf: &mut String, + base_name: &str, + transforms: &[Transform], +) { + buf.push_str("transformed field \""); + + buf.push_str(base_name); + for transform in transforms { buf.push('.'); buf.push_str(transform.operation_output_name()); } diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index f4f9bda4..f7090f61 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -6,6 +6,7 @@ use async_graphql_parser::{ types::{ExecutableDocument, FieldDefinition, TypeDefinition, TypeKind}, Positioned, }; +use error::TransformTypeError; use filters::make_filter_expr; use smallvec::SmallVec; @@ -807,7 +808,7 @@ fn make_vertex<'query>( let (_, property_type, property_fields) = properties.get(&(vid, property_name.clone())).unwrap(); - for property_field in property_fields.iter() { + for property_field in property_fields { for filter_directive in property_field.filter.iter() { match make_local_field_filter_expr( schema, @@ -831,8 +832,23 @@ fn make_vertex<'query>( let mut current_type = property_type.clone(); let mut next_transform_group = property_field.transform_group.as_ref(); while let Some(transform_group) = next_transform_group { - let next_transform = - extract_property_like_transform_from_directive(&transform_group.transform); + let next_transform = match extract_property_like_transform_from_directive( + &transform_group.transform, + property_name, + &transforms, + ¤t_type, + ) { + Ok(t) => t, + Err(e) => { + // This error should already have been reported while initially + // processing transforms for output and tag purposes + // in `fill_in_vertex_data()`. + // We have tests enforcing this. + // + // Ignore it here. + break; + } + }; current_type = match determine_transformed_field_type(current_type, &next_transform) { Ok(t) => t, @@ -840,6 +856,7 @@ fn make_vertex<'query>( // This error should already have been reported while initially // processing transforms for output and tag purposes // in `fill_in_vertex_data()`. + // We have tests enforcing this. // // Ignore it here. break; @@ -1186,8 +1203,18 @@ fn fill_in_property_data<'query>( } if let Some(transform_group) = next_transform_group { - let next_transform = - extract_property_like_transform_from_directive(&transform_group.transform); + let next_transform = match extract_property_like_transform_from_directive( + &transform_group.transform, + &property_node.name, + &transforms, + ¤t_type, + ) { + Ok(t) => t, + Err(e) => { + errors.push(e.into()); + break; + } + }; current_tid = Some(transform_group.tid); output_directives = transform_group.output.as_slice(); tag_directives = transform_group.tag.as_slice(); @@ -1247,15 +1274,22 @@ fn make_field_ref_for_possibly_transformed_property( fn extract_property_like_transform_from_directive( transform_directive: &TransformDirective, -) -> Transform { + property_name: &str, + transforms_so_far: &[Transform], + type_so_far: &Type, +) -> Result { match &transform_directive.kind { - TransformOp::Len => Transform::Len, - TransformOp::Abs => Transform::Abs, - TransformOp::Add(arg) => Transform::Add(resolve_transform_argument(arg)), - TransformOp::AddF(arg) => Transform::AddF(resolve_transform_argument(arg)), + TransformOp::Len => Ok(Transform::Len), + TransformOp::Abs => Ok(Transform::Abs), + TransformOp::Add(arg) => Ok(Transform::Add(resolve_transform_argument(arg))), + TransformOp::AddF(arg) => Ok(Transform::AddF(resolve_transform_argument(arg))), TransformOp::Count => { - // TODO: Add a test for this: it should produce an error, not a panic. - unreachable!("unexpected @transform on property: {transform_directive:?}") + Err(TransformTypeError::fold_specific_transform_on_propertylike_value( + transform_directive.kind.op_name(), + property_name, + transforms_so_far, + type_so_far, + )) } } } diff --git a/trustfall_core/src/graphql_query/directives.rs b/trustfall_core/src/graphql_query/directives.rs index 855b3a00..ed44896d 100644 --- a/trustfall_core/src/graphql_query/directives.rs +++ b/trustfall_core/src/graphql_query/directives.rs @@ -362,6 +362,22 @@ pub(crate) enum TransformOp { AddF(OperatorArgument), } +impl TransformOp { + /// The exact operation name we parse from `@transform` directives. + /// + /// For example: `@transform(op: "+", value: ["$value"])` corresponds to [`TransformOp::Add`], + /// so `TransformOp::Add.op_name() == "+"`. + pub(crate) fn op_name(&self) -> &'static str { + match self { + TransformOp::Count => "count", + TransformOp::Len => "len", + TransformOp::Abs => "abs", + TransformOp::Add(_) => "+", + TransformOp::AddF(_) => "+f", + } + } +} + /// A Trustfall `@tag` directive. /// /// For example, the following Trustfall and Rust would be equivalent: diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_list_property_produces_advice.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_list_property_produces_advice.frontend-error.ron new file mode 100644 index 00000000..06f4586f --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_list_property_produces_advice.frontend-error.ron @@ -0,0 +1 @@ +Err(TransformTypeError(FoldSpecificTransformUsedOnProperty("count", "property \"vowelsInName\"", " To get the number of elements in the list value here (type \"[String]\"), use the \"len\" transform operation instead."))) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_list_property_produces_advice.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_list_property_produces_advice.graphql-parsed.ron new file mode 100644 index 00000000..c39dddae --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_list_property_produces_advice.graphql-parsed.ron @@ -0,0 +1,43 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "vowelsInName", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "vowelsInName", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Count, + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_list_property_produces_advice.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_list_property_produces_advice.graphql.ron new file mode 100644 index 00000000..dbbb793b --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_list_property_produces_advice.graphql.ron @@ -0,0 +1,10 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + vowelsInName @transform(op: "count") @output + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_property.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_property.frontend-error.ron new file mode 100644 index 00000000..ca7ef0d6 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_property.frontend-error.ron @@ -0,0 +1 @@ +Err(TransformTypeError(FoldSpecificTransformUsedOnProperty("count", "property \"value\"", ""))) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_property.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_property.graphql-parsed.ron new file mode 100644 index 00000000..20ad0926 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_property.graphql-parsed.ron @@ -0,0 +1,43 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Count, + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_property.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_property.graphql.ron new file mode 100644 index 00000000..86d43eb8 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_property.graphql.ron @@ -0,0 +1,10 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + value @transform(op: "count") @output + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_transformed_property.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_transformed_property.frontend-error.ron new file mode 100644 index 00000000..34b2fada --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_transformed_property.frontend-error.ron @@ -0,0 +1 @@ +Err(TransformTypeError(FoldSpecificTransformUsedOnProperty("count", "transformed field \"value.abs\"", ""))) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_transformed_property.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_transformed_property.graphql-parsed.ron new file mode 100644 index 00000000..2fdbe863 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_transformed_property.graphql-parsed.ron @@ -0,0 +1,49 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Abs, + ), + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Count, + ), + output: [ + OutputDirective(), + ], + )), + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_transformed_property.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_transformed_property.graphql.ron new file mode 100644 index 00000000..2b827485 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_transformed_property.graphql.ron @@ -0,0 +1,10 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + value @transform(op: "abs") @transform(op: "count") @output + } +}"#, + arguments: {}, +) From 54bbfca6c4e4827045b9197612a5fe1ded3028ba Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Thu, 20 Jun 2024 03:02:09 +0000 Subject: [PATCH 14/30] Catch more instances of invalid `@transform` use and offer useful advice. --- trustfall_core/src/frontend/error.rs | 61 ++++++- trustfall_core/src/frontend/mod.rs | 153 ++++++++++-------- ...d_with_invalid_operator.frontend-error.ron | 1 + ...d_with_invalid_operator.graphql-parsed.ron | 71 ++++++++ ...orm_fold_with_invalid_operator.graphql.ron | 14 ++ ...perator_produces_advice.frontend-error.ron | 1 + ...perator_produces_advice.graphql-parsed.ron | 71 ++++++++ ...h_len_operator_produces_advice.graphql.ron | 14 ++ ...with_non_count_operator.frontend-error.ron | 4 + ...with_non_count_operator.graphql-parsed.ron | 68 ++++++++ ...d_edge_with_non_count_operator.graphql.ron | 14 ++ 11 files changed, 401 insertions(+), 71 deletions(-) create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_fold_with_invalid_operator.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_fold_with_invalid_operator.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_fold_with_invalid_operator.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_fold_with_len_operator_produces_advice.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_fold_with_len_operator_produces_advice.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_fold_with_len_operator_produces_advice.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_unfolded_edge_with_non_count_operator.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_unfolded_edge_with_non_count_operator.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_unfolded_edge_with_non_count_operator.graphql.ron diff --git a/trustfall_core/src/frontend/error.rs b/trustfall_core/src/frontend/error.rs index 5ba2bac3..ce2ac1e5 100644 --- a/trustfall_core/src/frontend/error.rs +++ b/trustfall_core/src/frontend/error.rs @@ -3,6 +3,7 @@ use std::{collections::BTreeMap, fmt::Write}; use serde::{Deserialize, Serialize}; use crate::{ + graphql_query::directives::{TransformDirective, TransformOp}, ir::{ Argument, FieldValue, FoldSpecificField, OperationSubject, Transform, TransformBase, TransformedField, Type, @@ -76,9 +77,6 @@ pub enum FrontendError { #[error("Found an unsupported {1} directive on an edge with @fold: {0}")] UnsupportedDirectiveOnFoldedEdge(String, String), - #[error("Found a @transform directive applied to edge \"{0}\" which is not marked @fold, and therefore cannot be transformed. Did you mean to apply @fold to the edge before the @transform directive?")] - CannotTransformEdgeWithoutFold(String), - #[error("Missing required edge parameter \"{0}\" on edge {1}")] MissingRequiredEdgeParameter(String, String), @@ -390,6 +388,14 @@ pub enum TransformTypeError { but is used on {1}.{2}" )] FoldSpecificTransformUsedOnProperty(String, String, String), + + #[error( + "Transform operation \"{0}\" is not supported on edges, but was applied to edge \"{1}\".{2}" + )] + UnsupportedTransformUsedOnEdge(String, String, String), + + #[error("Found a @transform directive applied to edge \"{0}\" which is not marked @fold, and therefore cannot be transformed.{1}")] + CannotTransformEdgeWithoutFold(String, String), } impl TransformTypeError { @@ -418,6 +424,55 @@ impl TransformTypeError { Self::FoldSpecificTransformUsedOnProperty(transform_op.to_string(), base_name, advice) } + + pub(crate) fn add_errors_for_transform_used_on_unfolded_edge( + edge_name: &str, + transform_directive: &TransformDirective, + errors: &mut Vec, + ) { + match &transform_directive.kind { + TransformOp::Count => { + // The user probably just forgot `@fold` on the edge, let's suggest that advice. + errors.push( + Self::CannotTransformEdgeWithoutFold( + edge_name.to_string(), + " Did you mean to apply @fold to the edge before the @transform directive?" + .into(), + ) + .into(), + ); + } + _ => { + // The user is using a transform operation that is inappropriate for edges, + // regardless of whether `@fold` is used or not. + // They might have meant to apply the `@transform` on a property instead. + // We should suggest that instead of suggesting adding `@fold` on the edge. + errors.push( + Self::CannotTransformEdgeWithoutFold(edge_name.to_string(), "".into()).into(), + ); + errors.push( + Self::UnsupportedTransformUsedOnEdge( + transform_directive.kind.op_name().to_string(), + edge_name.to_string(), + " Did you mean to apply the @transform directive to some property instead?" + .to_string(), + ) + .into(), + ); + } + } + } + + pub(crate) fn unsupported_transform_used_on_folded_edge( + edge_name: &str, + transform_op: &str, + ) -> Self { + Self::UnsupportedTransformUsedOnEdge( + transform_op.to_string(), + edge_name.to_string(), + " Did you mean to use @transform(op: \"count\") instead?".to_string(), + ) + } } fn write_name_of_transformed_field(buf: &mut String, field: &TransformedField) { diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index f7090f61..f057ab2e 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -958,12 +958,14 @@ where // Ensure we don't have `@transform` applied to the edge directly, // either completely without `@fold` or placed before it. // Both cases are an error, though a different error for each for better UX. - if subfield.transform_group.is_some() { + if let Some(transform_group) = &subfield.transform_group { if connection.fold.is_none() { // `@transform` used without `@fold` on the edge at all. - errors.push(FrontendError::CannotTransformEdgeWithoutFold( - subfield.name.to_string(), - )); + TransformTypeError::add_errors_for_transform_used_on_unfolded_edge( + subfield_name, + &transform_group.transform, + &mut errors, + ); } else { // `@transform` placed before `@fold` on the edge. unreachable!("@transform placed before @fold, but this error wasn't caught in the parser") @@ -1421,80 +1423,95 @@ where unimplemented!("re-transforming a @fold @transform value is currently not supported"); } - let fold_specific_field = match transform_group.transform.kind { - TransformOp::Count => FoldSpecificField { + let maybe_field = match transform_group.transform.kind { + TransformOp::Count => Ok(FoldSpecificField { fold_eid, fold_root_vid: starting_vid, kind: FoldSpecificFieldKind::Count, - }, - _ => { - // TODO: Add a test where we transform a `@fold` with an inappropriate operator, - // to make sure this produces an error, not a panic. - unreachable!("unexpected @transform operator on @fold: {transform_group:?}") - } + }), + _ => Err(TransformTypeError::unsupported_transform_used_on_folded_edge( + &edge_name, + transform_group.transform.kind.op_name(), + ) + .into()), }; - let field_ref = FieldRef::FoldSpecificField(fold_specific_field.clone()); - let subject = OperationSubject::FoldSpecificField(fold_specific_field.clone()); + match maybe_field { + Ok(fold_specific_field) => { + let field_ref = FieldRef::FoldSpecificField(fold_specific_field.clone()); + let subject = OperationSubject::FoldSpecificField(fold_specific_field.clone()); - for filter_directive in &transform_group.filter { - match make_filter_expr( - schema, - component_path, - tags, - starting_vid, - subject.clone(), - filter_directive, - ) { - Ok(filter) => post_filters.push(filter), - Err(e) => errors.extend(e), - } - } - for output in &transform_group.output { - let final_output_name = match output.name.as_ref() { - Some(explicit_name) => { - output_handler - .register_explicitly_named_output(explicit_name.clone(), field_ref.clone()); - explicit_name.clone() + for filter_directive in &transform_group.filter { + match make_filter_expr( + schema, + component_path, + tags, + starting_vid, + subject.clone(), + filter_directive, + ) { + Ok(filter) => post_filters.push(filter), + Err(e) => errors.extend(e), + } } - None => { - let local_name = if starting_field.alias.is_some() { - // The field has an alias already, so don't bother adding the edge name - // to the output name. - "" - } else { - // The field does not have an alias, so use the edge name as the base - // of the name. - starting_field.name.as_ref() + for output in &transform_group.output { + let final_output_name = match output.name.as_ref() { + Some(explicit_name) => { + output_handler.register_explicitly_named_output( + explicit_name.clone(), + field_ref.clone(), + ); + explicit_name.clone() + } + None => { + let local_name = if starting_field.alias.is_some() { + // The field has an alias already, so don't bother adding the edge name + // to the output name. + "" + } else { + // The field does not have an alias, so use the edge name as the base + // of the name. + starting_field.name.as_ref() + }; + output_handler.register_locally_named_output( + local_name, + Some(Box::new( + [fold_specific_field.kind.transform_suffix()].into_iter(), + )), + field_ref.clone(), + ) + } }; - output_handler.register_locally_named_output( - local_name, - Some(Box::new([fold_specific_field.kind.transform_suffix()].into_iter())), - field_ref.clone(), - ) - } - }; - let prior_output_by_that_name = - fold_specific_outputs.insert(final_output_name.clone(), field_ref.clone()); - if let Some(prior_output_kind) = prior_output_by_that_name { - errors.push(FrontendError::MultipleOutputsWithSameName(DuplicatedNamesConflict { - duplicates: btreemap! { - final_output_name.to_string() => vec![ - (starting_field.name.to_string(), prior_output_kind.field_name().to_string()), - (starting_field.name.to_string(), fold_specific_field.kind.field_name().to_string()), - ] + let prior_output_by_that_name = + fold_specific_outputs.insert(final_output_name.clone(), field_ref.clone()); + if let Some(prior_output_kind) = prior_output_by_that_name { + errors.push(FrontendError::MultipleOutputsWithSameName(DuplicatedNamesConflict { + duplicates: btreemap! { + final_output_name.to_string() => vec![ + (starting_field.name.to_string(), prior_output_kind.field_name().to_string()), + (starting_field.name.to_string(), fold_specific_field.kind.field_name().to_string()), + ] + } + })) } - })) - } - } - for tag_directive in &transform_group.tag { - let tag_name = tag_directive.name.as_ref().map(|x| x.as_ref()); - if let Some(tag_name) = tag_name { - if let Err(e) = tags.register_tag(tag_name, field_ref.clone(), component_path) { - errors.push(FrontendError::MultipleTagsWithSameName(tag_name.to_string())); } - } else { - errors.push(FrontendError::explicit_tag_name_required(&subject)) + for tag_directive in &transform_group.tag { + let tag_name = tag_directive.name.as_ref().map(|x| x.as_ref()); + if let Some(tag_name) = tag_name { + if let Err(e) = + tags.register_tag(tag_name, field_ref.clone(), component_path) + { + errors.push(FrontendError::MultipleTagsWithSameName( + tag_name.to_string(), + )); + } + } else { + errors.push(FrontendError::explicit_tag_name_required(&subject)) + } + } + } + Err(e) => { + errors.push(e); } } } diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_invalid_operator.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_invalid_operator.frontend-error.ron new file mode 100644 index 00000000..4d31c42d --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_invalid_operator.frontend-error.ron @@ -0,0 +1 @@ +Err(TransformTypeError(UnsupportedTransformUsedOnEdge("abs", "primeFactor", " Did you mean to use @transform(op: \"count\") instead?"))) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_invalid_operator.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_invalid_operator.graphql-parsed.ron new file mode 100644 index 00000000..dc8422eb --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_invalid_operator.graphql-parsed.ron @@ -0,0 +1,71 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(6), + "min": Int64(4), + }, + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + coerced_to: Some("Composite"), + connections: [ + (FieldConnection( + position: Pos( + line: 5, + column: 13, + ), + name: "primeFactor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Abs, + ), + output: [ + OutputDirective(), + ], + )), + )), + ), FieldNode( + position: Pos( + line: 5, + column: 13, + ), + name: "primeFactor", + connections: [ + (FieldConnection( + position: Pos( + line: 6, + column: 17, + ), + name: "value", + alias: Some("factors"), + ), FieldNode( + position: Pos( + line: 6, + column: 17, + ), + name: "value", + alias: Some("factors"), + output: [ + OutputDirective(), + ], + )), + ], + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_invalid_operator.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_invalid_operator.graphql.ron new file mode 100644 index 00000000..b0cb366b --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_invalid_operator.graphql.ron @@ -0,0 +1,14 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: 4, max: 6) { + ... on Composite { + primeFactor @fold @transform(op: "abs") @output { + factors: value @output + } + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_len_operator_produces_advice.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_len_operator_produces_advice.frontend-error.ron new file mode 100644 index 00000000..17d80623 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_len_operator_produces_advice.frontend-error.ron @@ -0,0 +1 @@ +Err(TransformTypeError(UnsupportedTransformUsedOnEdge("len", "primeFactor", " Did you mean to use @transform(op: \"count\") instead?"))) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_len_operator_produces_advice.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_len_operator_produces_advice.graphql-parsed.ron new file mode 100644 index 00000000..db6e766c --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_len_operator_produces_advice.graphql-parsed.ron @@ -0,0 +1,71 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(6), + "min": Int64(4), + }, + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + coerced_to: Some("Composite"), + connections: [ + (FieldConnection( + position: Pos( + line: 5, + column: 13, + ), + name: "primeFactor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Len, + ), + output: [ + OutputDirective(), + ], + )), + )), + ), FieldNode( + position: Pos( + line: 5, + column: 13, + ), + name: "primeFactor", + connections: [ + (FieldConnection( + position: Pos( + line: 6, + column: 17, + ), + name: "value", + alias: Some("factors"), + ), FieldNode( + position: Pos( + line: 6, + column: 17, + ), + name: "value", + alias: Some("factors"), + output: [ + OutputDirective(), + ], + )), + ], + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_len_operator_produces_advice.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_len_operator_produces_advice.graphql.ron new file mode 100644 index 00000000..4d042ebb --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_fold_with_len_operator_produces_advice.graphql.ron @@ -0,0 +1,14 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: 4, max: 6) { + ... on Composite { + primeFactor @fold @transform(op: "len") @output { + factors: value @output + } + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_unfolded_edge_with_non_count_operator.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_unfolded_edge_with_non_count_operator.frontend-error.ron new file mode 100644 index 00000000..2f63139c --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_unfolded_edge_with_non_count_operator.frontend-error.ron @@ -0,0 +1,4 @@ +Err(MultipleErrors(DisplayVec([ + TransformTypeError(CannotTransformEdgeWithoutFold("primeFactor", "")), + TransformTypeError(UnsupportedTransformUsedOnEdge("abs", "primeFactor", " Did you mean to apply the @transform directive to some property instead?")), +]))) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_unfolded_edge_with_non_count_operator.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/transform_unfolded_edge_with_non_count_operator.graphql-parsed.ron new file mode 100644 index 00000000..733cb2a9 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_unfolded_edge_with_non_count_operator.graphql-parsed.ron @@ -0,0 +1,68 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(6), + "min": Int64(4), + }, + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + coerced_to: Some("Composite"), + connections: [ + (FieldConnection( + position: Pos( + line: 5, + column: 13, + ), + name: "primeFactor", + ), FieldNode( + position: Pos( + line: 5, + column: 13, + ), + name: "primeFactor", + connections: [ + (FieldConnection( + position: Pos( + line: 6, + column: 17, + ), + name: "value", + alias: Some("factors"), + ), FieldNode( + position: Pos( + line: 6, + column: 17, + ), + name: "value", + alias: Some("factors"), + output: [ + OutputDirective(), + ], + )), + ], + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Abs, + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_unfolded_edge_with_non_count_operator.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/transform_unfolded_edge_with_non_count_operator.graphql.ron new file mode 100644 index 00000000..b89af3e4 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_unfolded_edge_with_non_count_operator.graphql.ron @@ -0,0 +1,14 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: 4, max: 6) { + ... on Composite { + primeFactor @transform(op: "abs") @output { + factors: value @output + } + } + } +}"#, + arguments: {}, +) From 1a57d6894e299d9b64e5b0a2df06710d8b9959ca Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Thu, 20 Jun 2024 17:59:41 +0000 Subject: [PATCH 15/30] Implement filters, tags, and outputs for transformed fold-count values. --- trustfall_core/src/frontend/mod.rs | 274 +++++++++++------- ..._count_from_inside_fold.frontend-error.ron | 1 + ..._count_from_inside_fold.graphql-parsed.ron | 136 +++++++++ ...on_fold_count_from_inside_fold.graphql.ron | 23 ++ ..._then_filter_and_output.graphql-parsed.ron | 85 ++++++ ...ansform_then_filter_and_output.graphql.ron | 22 ++ ...nt_transform_then_filter_and_output.ir.ron | 87 ++++++ ...ount_transform_then_tag.graphql-parsed.ron | 90 ++++++ .../fold_count_transform_then_tag.graphql.ron | 18 ++ .../fold_count_transform_then_tag.ir.ron | 70 +++++ ..._tag_and_filter_on_self.graphql-parsed.ron | 87 ++++++ ...rm_then_tag_and_filter_on_self.graphql.ron | 21 ++ ...ansform_then_tag_and_filter_on_self.ir.ron | 82 ++++++ 13 files changed, 898 insertions(+), 98 deletions(-) create mode 100644 trustfall_core/test_data/tests/frontend_errors/tag_on_fold_count_from_inside_fold.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/tag_on_fold_count_from_inside_fold.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/tag_on_fold_count_from_inside_fold.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.ir.ron diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index f057ab2e..84beed90 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -604,7 +604,13 @@ where // TODO: fixme, temporary hack to avoid changing the IRQueryComponent struct let hacked_outputs = component_outputs .into_iter() - .filter(|(k, v)| !matches!(&v, FieldRef::FoldSpecificField(..))) + .filter(|(_, v)| match &v { + FieldRef::ContextField(..) => true, + FieldRef::FoldSpecificField(..) => false, + FieldRef::TransformedField(inner) => { + !matches!(inner.value.base, TransformBase::FoldSpecificField(..)) + } + }) .collect(); Ok(IRQueryComponent { @@ -1279,24 +1285,47 @@ fn extract_property_like_transform_from_directive( property_name: &str, transforms_so_far: &[Transform], type_so_far: &Type, +) -> Result { + extract_transform_from_directive(transform_directive, type_so_far, || { + TransformTypeError::fold_specific_transform_on_propertylike_value( + transform_directive.kind.op_name(), + property_name, + transforms_so_far, + type_so_far, + ) + }) +} + +fn extract_transform_on_fold_count_from_directive( + transform_directive: &TransformDirective, + edge_name: &str, + subsequent_transforms: &[Transform], + type_so_far: &Type, +) -> Result { + extract_transform_from_directive(transform_directive, type_so_far, || { + // A fold-specific filter is used *after* a fold-count value has already been created. + // For example: `some_edge @fold @transform(op: "count") @transform(op: "count")` + // ^^^^^^^^^^^^^^^^^^^^^^^ + // we are here, this is the error + todo!("cover this with tests, it shouldn't panic") + }) +} + +fn extract_transform_from_directive( + transform_directive: &TransformDirective, + type_so_far: &Type, + err_func: impl FnOnce() -> TransformTypeError, ) -> Result { match &transform_directive.kind { TransformOp::Len => Ok(Transform::Len), TransformOp::Abs => Ok(Transform::Abs), - TransformOp::Add(arg) => Ok(Transform::Add(resolve_transform_argument(arg))), - TransformOp::AddF(arg) => Ok(Transform::AddF(resolve_transform_argument(arg))), - TransformOp::Count => { - Err(TransformTypeError::fold_specific_transform_on_propertylike_value( - transform_directive.kind.op_name(), - property_name, - transforms_so_far, - type_so_far, - )) - } + TransformOp::Add(arg) => Ok(Transform::Add(resolve_transform_argument(arg)?)), + TransformOp::AddF(arg) => Ok(Transform::AddF(resolve_transform_argument(arg)?)), + TransformOp::Count => Err(err_func()), } } -fn resolve_transform_argument(arg: &OperatorArgument) -> Argument { +fn resolve_transform_argument(arg: &OperatorArgument) -> Result { todo!() } @@ -1416,102 +1445,151 @@ where let mut post_filters = vec![]; let mut fold_specific_outputs = BTreeMap::new(); + let mut maybe_base_field: Option = None; + let mut subsequent_transforms = vec![]; + let mut current_type = Type::new_named_type("Int", false); + let mut next_transform_group = fold_group.transform.as_ref(); - if let Some(transform_group) = &fold_group.transform { - if transform_group.retransform.is_some() { - // TODO: add support for this, this should work - unimplemented!("re-transforming a @fold @transform value is currently not supported"); - } + while let Some(transform_group) = next_transform_group { + next_transform_group = transform_group.retransform.as_deref(); - let maybe_field = match transform_group.transform.kind { - TransformOp::Count => Ok(FoldSpecificField { - fold_eid, - fold_root_vid: starting_vid, - kind: FoldSpecificFieldKind::Count, - }), - _ => Err(TransformTypeError::unsupported_transform_used_on_folded_edge( - &edge_name, - transform_group.transform.kind.op_name(), - ) - .into()), - }; - match maybe_field { - Ok(fold_specific_field) => { - let field_ref = FieldRef::FoldSpecificField(fold_specific_field.clone()); - let subject = OperationSubject::FoldSpecificField(fold_specific_field.clone()); + let (field_ref, subject) = if let Some(base_field) = maybe_base_field.as_ref() { + let next_transform = match extract_transform_on_fold_count_from_directive( + &transform_group.transform, + edge_name.as_ref(), + &subsequent_transforms, + ¤t_type, + ) { + Ok(t) => t, + Err(e) => { + errors.push(e.into()); + break; + } + }; - for filter_directive in &transform_group.filter { - match make_filter_expr( - schema, - component_path, - tags, - starting_vid, - subject.clone(), - filter_directive, - ) { - Ok(filter) => post_filters.push(filter), - Err(e) => errors.extend(e), - } + current_type = match determine_transformed_field_type(current_type, &next_transform) { + Ok(t) => t, + Err(e) => { + errors.push(e); + break; } - for output in &transform_group.output { - let final_output_name = match output.name.as_ref() { - Some(explicit_name) => { - output_handler.register_explicitly_named_output( - explicit_name.clone(), - field_ref.clone(), - ); - explicit_name.clone() - } - None => { - let local_name = if starting_field.alias.is_some() { - // The field has an alias already, so don't bother adding the edge name - // to the output name. - "" - } else { - // The field does not have an alias, so use the edge name as the base - // of the name. - starting_field.name.as_ref() - }; - output_handler.register_locally_named_output( - local_name, - Some(Box::new( - [fold_specific_field.kind.transform_suffix()].into_iter(), - )), - field_ref.clone(), - ) - } - }; + }; + subsequent_transforms.push(next_transform); + + let transformed_field = TransformedField { + value: Arc::new(TransformedValue { + base: TransformBase::FoldSpecificField(base_field.clone()), + // TODO: This `.clone()` is a full copy of the `Vec`, + // so if we have a very deep chain of `@transform` directives where each + // intermediate result is used in a tag, filter, or output, + // that would result in O(n^2) copies. + // + // In principle, we should be able to make a "smarter" data type here: + // we only ever append to the underlying `Vec` as we process + // new `@transform` directives, so prior uses of it see an immutable + // prefix of the full final `Vec`. This may lend itself to + // an `Arc`-ed shared-ownership prefix view over an underlying allocation. + // This is a potential future optimization opportunity! + transforms: subsequent_transforms.clone(), + }), + tid: transform_group.tid, + field_type: current_type.clone(), + }; + let field_ref = FieldRef::TransformedField(transformed_field.clone()); + let subject = OperationSubject::TransformedField(transformed_field); - let prior_output_by_that_name = - fold_specific_outputs.insert(final_output_name.clone(), field_ref.clone()); - if let Some(prior_output_kind) = prior_output_by_that_name { - errors.push(FrontendError::MultipleOutputsWithSameName(DuplicatedNamesConflict { - duplicates: btreemap! { - final_output_name.to_string() => vec![ - (starting_field.name.to_string(), prior_output_kind.field_name().to_string()), - (starting_field.name.to_string(), fold_specific_field.kind.field_name().to_string()), - ] - } - })) + (field_ref, subject) + } else { + let fold_specific_field = match transform_group.transform.kind { + TransformOp::Count => { + current_type = Type::new_named_type("Int", false); + FoldSpecificField { + fold_eid, + fold_root_vid: starting_vid, + kind: FoldSpecificFieldKind::Count, } } - for tag_directive in &transform_group.tag { - let tag_name = tag_directive.name.as_ref().map(|x| x.as_ref()); - if let Some(tag_name) = tag_name { - if let Err(e) = - tags.register_tag(tag_name, field_ref.clone(), component_path) - { - errors.push(FrontendError::MultipleTagsWithSameName( - tag_name.to_string(), - )); - } + _ => { + errors.push( + TransformTypeError::unsupported_transform_used_on_folded_edge( + &edge_name, + transform_group.transform.kind.op_name(), + ) + .into(), + ); + break; + } + }; + + let field_ref = FieldRef::FoldSpecificField(fold_specific_field.clone()); + let subject = OperationSubject::FoldSpecificField(fold_specific_field.clone()); + maybe_base_field = Some(fold_specific_field); + + (field_ref, subject) + }; + + for filter_directive in &transform_group.filter { + match make_filter_expr( + schema, + component_path, + tags, + starting_vid, + subject.clone(), + filter_directive, + ) { + Ok(filter) => post_filters.push(filter), + Err(e) => errors.extend(e), + } + } + for output in &transform_group.output { + let final_output_name = match output.name.as_ref() { + Some(explicit_name) => { + output_handler + .register_explicitly_named_output(explicit_name.clone(), field_ref.clone()); + explicit_name.clone() + } + None => { + let local_name = if starting_field.alias.is_some() { + // The field has an alias already, so don't bother adding the edge name + // to the output name. + "" } else { - errors.push(FrontendError::explicit_tag_name_required(&subject)) - } + // The field does not have an alias, so use the edge name as the base + // of the name. + starting_field.name.as_ref() + }; + output_handler.register_locally_named_output( + local_name, + Some(Box::new([TransformOp::Count.op_name()].into_iter().chain( + subsequent_transforms.iter().map(|t| t.operation_output_name()), + ))), + field_ref.clone(), + ) } + }; + + let prior_output_by_that_name = + fold_specific_outputs.insert(final_output_name.clone(), field_ref.clone()); + if let Some(prior_output_kind) = prior_output_by_that_name { + errors.push(FrontendError::MultipleOutputsWithSameName(DuplicatedNamesConflict { + duplicates: btreemap! { + final_output_name.to_string() => vec![ + (starting_field.name.to_string(), prior_output_kind.field_name().to_string()), + todo!(), + // (starting_field.name.to_string(), fold_specific_field.kind.field_name().to_string()), + ] + } + })) } - Err(e) => { - errors.push(e); + } + for tag_directive in &transform_group.tag { + let tag_name = tag_directive.name.as_ref().map(|x| x.as_ref()); + if let Some(tag_name) = tag_name { + if let Err(e) = tags.register_tag(tag_name, field_ref.clone(), component_path) { + errors.push(FrontendError::MultipleTagsWithSameName(tag_name.to_string())); + } + } else { + errors.push(FrontendError::explicit_tag_name_required(&subject)) } } } diff --git a/trustfall_core/test_data/tests/frontend_errors/tag_on_fold_count_from_inside_fold.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/tag_on_fold_count_from_inside_fold.frontend-error.ron new file mode 100644 index 00000000..b8214acc --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/tag_on_fold_count_from_inside_fold.frontend-error.ron @@ -0,0 +1 @@ +Err(TagUsedOutsideItsFoldedSubquery("property \"value\"", "prime_count")) diff --git a/trustfall_core/test_data/tests/frontend_errors/tag_on_fold_count_from_inside_fold.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/tag_on_fold_count_from_inside_fold.graphql-parsed.ron new file mode 100644 index 00000000..bfede1f7 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/tag_on_fold_count_from_inside_fold.graphql-parsed.ron @@ -0,0 +1,136 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Four", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Four", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "successor", + fold: Some(FoldGroup( + fold: FoldDirective(), + )), + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "successor", + connections: [ + (FieldConnection( + position: Pos( + line: 5, + column: 13, + ), + name: "successor", + ), FieldNode( + position: Pos( + line: 5, + column: 13, + ), + name: "successor", + coerced_to: Some("Composite"), + connections: [ + (FieldConnection( + position: Pos( + line: 7, + column: 21, + ), + name: "primeFactor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Count, + ), + output: [ + OutputDirective(), + ], + tag: [ + TagDirective( + name: Some("prime_count"), + ), + ], + )), + )), + ), FieldNode( + position: Pos( + line: 7, + column: 21, + ), + name: "primeFactor", + )), + ], + )), + ], + )), + (FieldConnection( + position: Pos( + line: 11, + column: 9, + ), + name: "predecessor", + ), FieldNode( + position: Pos( + line: 11, + column: 9, + ), + name: "predecessor", + connections: [ + (FieldConnection( + position: Pos( + line: 12, + column: 13, + ), + name: "predecessor", + ), FieldNode( + position: Pos( + line: 12, + column: 13, + ), + name: "predecessor", + connections: [ + (FieldConnection( + position: Pos( + line: 13, + column: 17, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 13, + column: 17, + ), + name: "value", + filter: [ + FilterDirective( + operation: Equals((), TagRef("prime_count")), + ), + ], + output: [ + OutputDirective(), + ], + )), + ], + )), + ], + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/tag_on_fold_count_from_inside_fold.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/tag_on_fold_count_from_inside_fold.graphql.ron new file mode 100644 index 00000000..78c7bf9c --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/tag_on_fold_count_from_inside_fold.graphql.ron @@ -0,0 +1,23 @@ +TestGraphQLQuery ( + schema_name: "numbers", + + // The @tag is within a folded component relative to its use, which is not supported right now. + query: r#" +{ + Four { + successor @fold { # this is five + successor { # this is six, and it has two prime factors + ... on Composite { + primeFactor @fold @transform(op: "count") @tag(name: "prime_count") @output + } + } + } + predecessor { # this is three + predecessor { # this is two, so it's equal to the number of prime factors of six + value @filter(op: "=", value: ["%prime_count"]) @output + } + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.graphql-parsed.ron new file mode 100644 index 00000000..2d9272b0 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.graphql-parsed.ron @@ -0,0 +1,85 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(31), + "min": Int64(28), + }, + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + coerced_to: Some("Composite"), + connections: [ + (FieldConnection( + position: Pos( + line: 5, + column: 13, + ), + name: "primeFactor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Count, + ), + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Abs, + ), + output: [ + OutputDirective(), + ], + filter: [ + FilterDirective( + operation: GreaterThan((), VariableRef("two")), + ), + ], + )), + )), + )), + ), FieldNode( + position: Pos( + line: 5, + column: 13, + ), + name: "primeFactor", + connections: [ + (FieldConnection( + position: Pos( + line: 12, + column: 17, + ), + name: "value", + alias: Some("factors"), + ), FieldNode( + position: Pos( + line: 12, + column: 17, + ), + name: "value", + alias: Some("factors"), + output: [ + OutputDirective(), + ], + )), + ], + )), + ], + ), + ), + arguments: { + "two": Uint64(2), + }, +)) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.graphql.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.graphql.ron new file mode 100644 index 00000000..f308c3cb --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.graphql.ron @@ -0,0 +1,22 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: 28, max: 31) { + ... on Composite { + primeFactor + @fold + @transform(op: "count") + @transform(op: "abs") + @filter(op: ">", value: ["$two"]) + @output + { + factors: value @output + } + } + } +}"#, + arguments: { + "two": Uint64(2), + }, +) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.ir.ron new file mode 100644 index 00000000..60a7b717 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.ir.ron @@ -0,0 +1,87 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(31), + "min": Int64(28), + }, + ), + 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", + ), + }, + outputs: { + "factors": ContextField(ContextField( + vertex_id: Vid(2), + field_name: "value", + field_type: "Int", + )), + }, + ), + fold_specific_outputs: { + "primeFactorcountabs": TransformedField(TransformedField( + value: TransformedValue( + base: FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), + transforms: [ + Abs, + ], + ), + tid: Tid(2), + field_type: "Int!", + )), + }, + post_filters: [ + GreaterThan(TransformedField(TransformedField( + value: TransformedValue( + base: FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), + transforms: [ + Abs, + ], + ), + tid: Tid(2), + field_type: "Int!", + )), Variable(VariableRef( + variable_name: "two", + variable_type: "Int!", + ))), + ], + ), + }, + ), + variables: { + "two": "Int!", + }, + ), + arguments: { + "two": Uint64(2), + }, +)) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.graphql-parsed.ron new file mode 100644 index 00000000..230518a8 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.graphql-parsed.ron @@ -0,0 +1,90 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Four", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Four", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "primeFactor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Count, + ), + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Abs, + ), + tag: [ + TagDirective( + name: Some("primes_count"), + ), + ], + )), + )), + )), + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "primeFactor", + )), + (FieldConnection( + position: Pos( + line: 10, + column: 9, + ), + name: "predecessor", + ), FieldNode( + position: Pos( + line: 10, + column: 9, + ), + name: "predecessor", + connections: [ + (FieldConnection( + position: Pos( + line: 11, + column: 13, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 11, + column: 13, + ), + name: "value", + filter: [ + FilterDirective( + operation: GreaterThan((), TagRef("primes_count")), + ), + ], + output: [ + OutputDirective(), + ], + )), + ], + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.graphql.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.graphql.ron new file mode 100644 index 00000000..e45dcec8 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.graphql.ron @@ -0,0 +1,18 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Four { + primeFactor + @fold + @transform(op: "count") + @transform(op: "abs") + @tag(name: "primes_count") + + predecessor { + value @filter(op: ">", value: ["%primes_count"]) @output + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.ir.ron new file mode 100644 index 00000000..e3eec2a2 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.ir.ron @@ -0,0 +1,70 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "Four", + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Composite", + ), + Vid(3): IRVertex( + vid: Vid(3), + type_name: "Number", + filters: [ + GreaterThan(LocalField(LocalField( + field_name: "value", + field_type: "Int", + )), Tag(TransformedField(TransformedField( + value: TransformedValue( + base: FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), + transforms: [ + Abs, + ], + ), + tid: Tid(2), + field_type: "Int!", + )))), + ], + ), + }, + edges: { + Eid(2): IREdge( + eid: Eid(2), + from_vid: Vid(1), + to_vid: Vid(3), + edge_name: "predecessor", + ), + }, + 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", + ), + }, + ), + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(3), + field_name: "value", + field_type: "Int", + )), + }, + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.graphql-parsed.ron new file mode 100644 index 00000000..e022b1c5 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.graphql-parsed.ron @@ -0,0 +1,87 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(31), + "min": Int64(28), + }, + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + coerced_to: Some("Composite"), + connections: [ + (FieldConnection( + position: Pos( + line: 5, + column: 13, + ), + name: "primeFactor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Count, + ), + tag: [ + TagDirective( + name: Some("tagged"), + ), + ], + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Abs, + ), + output: [ + OutputDirective(), + ], + filter: [ + FilterDirective( + operation: Equals((), TagRef("tagged")), + ), + ], + )), + )), + )), + ), FieldNode( + position: Pos( + line: 5, + column: 13, + ), + name: "primeFactor", + connections: [ + (FieldConnection( + position: Pos( + line: 13, + column: 17, + ), + name: "value", + alias: Some("factors"), + ), FieldNode( + position: Pos( + line: 13, + column: 17, + ), + name: "value", + alias: Some("factors"), + output: [ + OutputDirective(), + ], + )), + ], + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.graphql.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.graphql.ron new file mode 100644 index 00000000..2d6f1010 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.graphql.ron @@ -0,0 +1,21 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: 28, max: 31) { + ... on Composite { + primeFactor + @fold + @transform(op: "count") + @tag(name: "tagged") + @transform(op: "abs") + @filter(op: "=", value: ["%tagged"]) + @output + { + factors: value @output + } + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.ir.ron new file mode 100644 index 00000000..9613badd --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.ir.ron @@ -0,0 +1,82 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(31), + "min": Int64(28), + }, + ), + 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", + ), + }, + outputs: { + "factors": ContextField(ContextField( + vertex_id: Vid(2), + field_name: "value", + field_type: "Int", + )), + }, + ), + fold_specific_outputs: { + "primeFactorcountabs": TransformedField(TransformedField( + value: TransformedValue( + base: FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), + transforms: [ + Abs, + ], + ), + tid: Tid(2), + field_type: "Int!", + )), + }, + post_filters: [ + Equals(TransformedField(TransformedField( + value: TransformedValue( + base: FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), + transforms: [ + Abs, + ], + ), + tid: Tid(2), + field_type: "Int!", + )), Tag(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )))), + ], + ), + }, + ), + ), +)) From 05a8caacd1dc0b5cefe0c58d124babd4c9615370 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Thu, 20 Jun 2024 19:55:40 +0000 Subject: [PATCH 16/30] Handle repeated `@transform(op: "count")` directives where only first is valid. --- trustfall_core/src/frontend/error.rs | 10 ++ trustfall_core/src/frontend/mod.rs | 4 +- ..._on_existing_fold_count.frontend-error.ron | 1 + ..._on_existing_fold_count.graphql-parsed.ron | 93 +++++++++++++++++++ ...m_count_on_existing_fold_count.graphql.ron | 22 +++++ 5 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_existing_fold_count.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_existing_fold_count.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_count_on_existing_fold_count.graphql.ron diff --git a/trustfall_core/src/frontend/error.rs b/trustfall_core/src/frontend/error.rs index ce2ac1e5..41b37e1d 100644 --- a/trustfall_core/src/frontend/error.rs +++ b/trustfall_core/src/frontend/error.rs @@ -389,6 +389,12 @@ pub enum TransformTypeError { )] FoldSpecificTransformUsedOnProperty(String, String, String), + #[error( + "Folded edge \"{0}\" has more than one @transform(op: \"count\") directive applied to it, \ + which is not allowed. Please remove all but the first such directive after the @fold." + )] + DuplicatedCountTransformOnEdge(String), + #[error( "Transform operation \"{0}\" is not supported on edges, but was applied to edge \"{1}\".{2}" )] @@ -473,6 +479,10 @@ impl TransformTypeError { " Did you mean to use @transform(op: \"count\") instead?".to_string(), ) } + + pub(crate) fn duplicated_count_transform_on_folded_edge(edge_name: &str) -> Self { + Self::DuplicatedCountTransformOnEdge(edge_name.to_string()) + } } fn write_name_of_transformed_field(buf: &mut String, field: &TransformedField) { diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index 84beed90..29241319 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -1299,7 +1299,6 @@ fn extract_property_like_transform_from_directive( fn extract_transform_on_fold_count_from_directive( transform_directive: &TransformDirective, edge_name: &str, - subsequent_transforms: &[Transform], type_so_far: &Type, ) -> Result { extract_transform_from_directive(transform_directive, type_so_far, || { @@ -1307,7 +1306,7 @@ fn extract_transform_on_fold_count_from_directive( // For example: `some_edge @fold @transform(op: "count") @transform(op: "count")` // ^^^^^^^^^^^^^^^^^^^^^^^ // we are here, this is the error - todo!("cover this with tests, it shouldn't panic") + TransformTypeError::duplicated_count_transform_on_folded_edge(edge_name) }) } @@ -1457,7 +1456,6 @@ where let next_transform = match extract_transform_on_fold_count_from_directive( &transform_group.transform, edge_name.as_ref(), - &subsequent_transforms, ¤t_type, ) { Ok(t) => t, diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_existing_fold_count.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_existing_fold_count.frontend-error.ron new file mode 100644 index 00000000..b48add02 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_existing_fold_count.frontend-error.ron @@ -0,0 +1 @@ +Err(TransformTypeError(DuplicatedCountTransformOnEdge("primeFactor"))) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_existing_fold_count.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_existing_fold_count.graphql-parsed.ron new file mode 100644 index 00000000..65047dfe --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_existing_fold_count.graphql-parsed.ron @@ -0,0 +1,93 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(6), + "min": Int64(4), + }, + ), + 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: 8, + column: 13, + ), + name: "primeFactor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Count, + ), + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Count, + ), + output: [ + OutputDirective(), + ], + )), + )), + )), + ), FieldNode( + position: Pos( + line: 8, + column: 13, + ), + name: "primeFactor", + connections: [ + (FieldConnection( + position: Pos( + line: 14, + column: 17, + ), + name: "value", + alias: Some("factors"), + ), FieldNode( + position: Pos( + line: 14, + column: 17, + ), + name: "value", + alias: Some("factors"), + output: [ + OutputDirective(), + ], + )), + ], + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_existing_fold_count.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_existing_fold_count.graphql.ron new file mode 100644 index 00000000..53e344f1 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_existing_fold_count.graphql.ron @@ -0,0 +1,22 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: 4, max: 6) { + ... on Composite { + value @output + + # The duplicated `@transform(op: "count") is the source of the error here. + primeFactor + @fold + @transform(op: "count") + @transform(op: "count") + @output + { + factors: value @output + } + } + } +}"#, + arguments: {}, +) From 5c1226dbdcccaf7f4106f2e0e2ca109d625a5b5c Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Thu, 20 Jun 2024 20:34:48 +0000 Subject: [PATCH 17/30] Handle duplicate-output-name errors with transformed fields. --- trustfall_core/src/frontend/mod.rs | 127 +++++++++++++----- .../src/interpreter/hints/vertex_info.rs | 21 ++- trustfall_core/src/ir/mod.rs | 16 --- ...ld_count_transform_name.frontend-error.ron | 8 ++ ...ld_count_transform_name.graphql-parsed.ron | 75 +++++++++++ ...icit_fold_count_transform_name.graphql.ron | 18 +++ ...vs_fold_count_transform.frontend-error.ron | 8 ++ ...vs_fold_count_transform.graphql-parsed.ron | 72 ++++++++++ ...operty_vs_fold_count_transform.graphql.ron | 17 +++ ..._count_on_unfolded_edge.frontend-error.ron | 2 +- ...dge_without_inner_scope.frontend-error.ron | 2 +- 11 files changed, 309 insertions(+), 57 deletions(-) create mode 100644 trustfall_core/test_data/tests/frontend_errors/duplicate_output_implicit_and_explicit_fold_count_transform_name.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/duplicate_output_implicit_and_explicit_fold_count_transform_name.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/duplicate_output_implicit_and_explicit_fold_count_transform_name.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/duplicate_output_name_property_vs_fold_count_transform.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/duplicate_output_name_property_vs_fold_count_transform.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/duplicate_output_name_property_vs_fold_count_transform.graphql.ron diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index 29241319..dd65338f 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -1,6 +1,6 @@ //! Frontend for Trustfall: takes a parsed query, validates it, and turns it into IR. #![allow(dead_code, unused_variables, unused_mut)] -use std::{collections::BTreeMap, iter::successors, num::NonZeroUsize, sync::Arc}; +use std::{collections::BTreeMap, fmt::Write, iter::successors, num::NonZeroUsize, sync::Arc}; use async_graphql_parser::{ types::{ExecutableDocument, FieldDefinition, TypeDefinition, TypeKind}, @@ -312,8 +312,8 @@ pub fn make_ir_for_query(schema: &Schema, query: &Query) -> Result Result BTreeMap { - let mut result = Default::default(); - collect_ir_vertices_recursive_step(&mut result, root_component); - result +fn collect_ir_vertices_and_folds( + root_component: &IRQueryComponent, +) -> (BTreeMap, BTreeMap>) { + let mut vertices = Default::default(); + let mut folds = Default::default(); + collect_ir_vertices_and_folds_recursive_step(&mut vertices, &mut folds, root_component); + (vertices, folds) } -fn collect_ir_vertices_recursive_step( - result: &mut BTreeMap, +fn collect_ir_vertices_and_folds_recursive_step( + vertices: &mut BTreeMap, + folds: &mut BTreeMap>, component: &IRQueryComponent, ) { - result.extend(component.vertices.iter().map(|(k, v)| (*k, v.clone()))); + vertices.extend(component.vertices.iter().map(|(k, v)| (*k, v.clone()))); - component - .folds - .values() - .for_each(move |fold| collect_ir_vertices_recursive_step(result, &fold.component)) + component.folds.iter().for_each(move |(eid, fold)| { + folds.insert(*eid, Arc::clone(fold)); + + collect_ir_vertices_and_folds_recursive_step(vertices, folds, &fold.component); + }) } fn fill_in_query_variables( @@ -403,6 +408,7 @@ fn fill_in_query_variables( fn make_duplicated_output_names_error( ir_vertices: &BTreeMap, + folds: &BTreeMap>, duplicates: BTreeMap, Vec>, ) -> Vec { let conflict_info = DuplicatedNamesConflict { @@ -411,21 +417,38 @@ fn make_duplicated_output_names_error( .map(|(k, fields)| { let duplicate_values = fields .iter() - .map(|field| match field { - FieldRef::ContextField(field) => { - let vid = field.vertex_id; - (ir_vertices[&vid].type_name.to_string(), field.field_name.to_string()) - } - FieldRef::FoldSpecificField(field) => { - let vid = field.fold_root_vid; - match field.kind { - FoldSpecificFieldKind::Count => ( - ir_vertices[&vid].type_name.to_string(), - "fold count value".to_string(), - ), + .map(|field| { + let field_name = describe_field_ref(field); + + let type_name = match field { + FieldRef::ContextField(field) => { + let vid = field.vertex_id; + ir_vertices[&vid].type_name.to_string() } - } - FieldRef::TransformedField(field) => todo!(), + FieldRef::FoldSpecificField(field) => match field.kind { + FoldSpecificFieldKind::Count => { + folds[&field.fold_eid].edge_name.to_string() + } + }, + FieldRef::TransformedField(transformed) => { + match &transformed.value.base { + TransformBase::ContextField(field) => { + let vid = field.vertex_id; + ir_vertices[&vid].type_name.to_string() + } + TransformBase::FoldSpecificField(field) => { + let vid = field.fold_root_vid; + match field.kind { + FoldSpecificFieldKind::Count => { + folds[&field.fold_eid].edge_name.to_string() + } + } + } + } + } + }; + + (type_name, field_name) }) .collect(); (k.to_string(), duplicate_values) @@ -597,7 +620,7 @@ where let component_outputs = match check_for_duplicate_output_names(maybe_duplicated_outputs) { Ok(outputs) => outputs, Err(duplicates) => { - return Err(make_duplicated_output_names_error(&ir_vertices, duplicates)) + return Err(make_duplicated_output_names_error(&ir_vertices, &folds, duplicates)) } }; @@ -1568,15 +1591,17 @@ where let prior_output_by_that_name = fold_specific_outputs.insert(final_output_name.clone(), field_ref.clone()); - if let Some(prior_output_kind) = prior_output_by_that_name { + if let Some(prior_output) = prior_output_by_that_name { + let new_field_description = + describe_edge_with_fold_count_and_transforms(&subsequent_transforms); + errors.push(FrontendError::MultipleOutputsWithSameName(DuplicatedNamesConflict { duplicates: btreemap! { final_output_name.to_string() => vec![ - (starting_field.name.to_string(), prior_output_kind.field_name().to_string()), - todo!(), - // (starting_field.name.to_string(), fold_specific_field.kind.field_name().to_string()), + (edge_name.to_string(), describe_field_ref(&prior_output)), + (edge_name.to_string(), new_field_description), ] - } + }, })) } } @@ -1609,6 +1634,40 @@ where }) } +fn describe_edge_with_fold_count_and_transforms(subsequent_transforms: &[Transform]) -> String { + let mut buf = String::with_capacity(32); + buf.write_str(FoldSpecificFieldKind::Count.field_name()).expect("write failed"); + for transform in subsequent_transforms { + buf.write_char('.').expect("write failed"); + buf.write_str(transform.operation_output_name()).expect("write failed"); + } + buf +} + +fn describe_field_ref(field_ref: &FieldRef) -> String { + match field_ref { + FieldRef::ContextField(f) => f.field_name.to_string(), + FieldRef::FoldSpecificField(f) => f.kind.field_name().to_string(), + FieldRef::TransformedField(transformed) => { + let mut buf = String::with_capacity(32); + match &transformed.value.base { + TransformBase::ContextField(f) => { + buf.write_str(&f.field_name).expect("write failed"); + } + TransformBase::FoldSpecificField(f) => { + buf.write_str(f.kind.field_name()).expect("write failed"); + } + } + + for transform in &transformed.value.transforms { + buf.write_char('.').expect("write failed"); + buf.write_str(transform.operation_output_name()).expect("write failed"); + } + buf + } + } +} + #[cfg(test)] mod tests { use std::{ diff --git a/trustfall_core/src/interpreter/hints/vertex_info.rs b/trustfall_core/src/interpreter/hints/vertex_info.rs index 06773569..94f01aa5 100644 --- a/trustfall_core/src/interpreter/hints/vertex_info.rs +++ b/trustfall_core/src/interpreter/hints/vertex_info.rs @@ -164,11 +164,22 @@ impl VertexInfo for T { let current_vertex = self.current_vertex(); - let properties = current_component - .outputs - .values() - .filter(|c| c.defined_at() == current_vertex.vid) - .map(|c| RequiredProperty::new(c.field_name_arc())); + let properties = current_component.outputs.values().filter_map(|c| { + let maybe_name = match c { + FieldRef::ContextField(field) => { + (field.vertex_id == current_vertex.vid).then_some(&field.field_name) + } + FieldRef::FoldSpecificField(..) => None, + FieldRef::TransformedField(transformed) => match &transformed.value.base { + TransformBase::ContextField(field) => { + (field.vertex_id == current_vertex.vid).then_some(&field.field_name) + } + TransformBase::FoldSpecificField(..) => None, + }, + }; + + maybe_name.map(Arc::clone).map(RequiredProperty::new) + }); let properties = properties.chain(current_vertex.filters.iter().map(move |f| { RequiredProperty::new(match f.left() { diff --git a/trustfall_core/src/ir/mod.rs b/trustfall_core/src/ir/mod.rs index f581c3a0..69e5d1da 100644 --- a/trustfall_core/src/ir/mod.rs +++ b/trustfall_core/src/ir/mod.rs @@ -350,22 +350,6 @@ impl FieldRef { } } - pub fn field_name(&self) -> &str { - match self { - FieldRef::ContextField(c) => c.field_name.as_ref(), - FieldRef::FoldSpecificField(f) => f.kind.field_name(), - FieldRef::TransformedField(..) => todo!(), - } - } - - pub fn field_name_arc(&self) -> Arc { - match self { - FieldRef::ContextField(c) => c.field_name.clone(), - FieldRef::FoldSpecificField(f) => f.kind.field_name().into(), - FieldRef::TransformedField(..) => todo!(), - } - } - /// The vertex ID at which this reference is considered defined. pub fn defined_at(&self) -> Vid { match self { diff --git a/trustfall_core/test_data/tests/frontend_errors/duplicate_output_implicit_and_explicit_fold_count_transform_name.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/duplicate_output_implicit_and_explicit_fold_count_transform_name.frontend-error.ron new file mode 100644 index 00000000..2f49fad0 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/duplicate_output_implicit_and_explicit_fold_count_transform_name.frontend-error.ron @@ -0,0 +1,8 @@ +Err(MultipleOutputsWithSameName(DuplicatedNamesConflict( + duplicates: { + "primeFactorcountabs": [ + ("primeFactor", "@fold.count.abs"), + ("primeFactor", "@fold.count.abs"), + ], + }, +))) diff --git a/trustfall_core/test_data/tests/frontend_errors/duplicate_output_implicit_and_explicit_fold_count_transform_name.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/duplicate_output_implicit_and_explicit_fold_count_transform_name.graphql-parsed.ron new file mode 100644 index 00000000..0139aec2 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/duplicate_output_implicit_and_explicit_fold_count_transform_name.graphql-parsed.ron @@ -0,0 +1,75 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Four", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Four", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "primeFactor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Count, + ), + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Abs, + ), + output: [ + OutputDirective(), + OutputDirective( + name: Some("primeFactorcountabs"), + ), + ], + )), + )), + )), + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "primeFactor", + connections: [ + (FieldConnection( + position: Pos( + line: 11, + column: 13, + ), + name: "value", + alias: Some("factors"), + ), FieldNode( + position: Pos( + line: 11, + column: 13, + ), + name: "value", + alias: Some("factors"), + output: [ + OutputDirective(), + ], + )), + ], + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/duplicate_output_implicit_and_explicit_fold_count_transform_name.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/duplicate_output_implicit_and_explicit_fold_count_transform_name.graphql.ron new file mode 100644 index 00000000..f4e1740b --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/duplicate_output_implicit_and_explicit_fold_count_transform_name.graphql.ron @@ -0,0 +1,18 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Four { + primeFactor + @fold + @transform(op: "count") + @transform(op: "abs") + @output + @output(name: "primeFactorcountabs") + { + factors: value @output + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/duplicate_output_name_property_vs_fold_count_transform.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/duplicate_output_name_property_vs_fold_count_transform.frontend-error.ron new file mode 100644 index 00000000..c13f67fb --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/duplicate_output_name_property_vs_fold_count_transform.frontend-error.ron @@ -0,0 +1,8 @@ +Err(MultipleOutputsWithSameName(DuplicatedNamesConflict( + duplicates: { + "primeFactorcountabs": [ + ("Prime", "value"), + ("primeFactor", "@fold.count.abs"), + ], + }, +))) diff --git a/trustfall_core/test_data/tests/frontend_errors/duplicate_output_name_property_vs_fold_count_transform.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/duplicate_output_name_property_vs_fold_count_transform.graphql-parsed.ron new file mode 100644 index 00000000..7fef8beb --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/duplicate_output_name_property_vs_fold_count_transform.graphql-parsed.ron @@ -0,0 +1,72 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Four", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Four", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "primeFactor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Count, + ), + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Abs, + ), + output: [ + OutputDirective(), + ], + )), + )), + )), + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "primeFactor", + connections: [ + (FieldConnection( + position: Pos( + line: 10, + column: 13, + ), + name: "value", + alias: Some("primeFactorcountabs"), + ), FieldNode( + position: Pos( + line: 10, + column: 13, + ), + name: "value", + alias: Some("primeFactorcountabs"), + output: [ + OutputDirective(), + ], + )), + ], + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/duplicate_output_name_property_vs_fold_count_transform.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/duplicate_output_name_property_vs_fold_count_transform.graphql.ron new file mode 100644 index 00000000..68e0b707 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/duplicate_output_name_property_vs_fold_count_transform.graphql.ron @@ -0,0 +1,17 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Four { + primeFactor + @fold + @transform(op: "count") + @transform(op: "abs") + @output + { + primeFactorcountabs: value @output + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge.frontend-error.ron index 7cfdd09f..d721faf7 100644 --- a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge.frontend-error.ron +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge.frontend-error.ron @@ -1 +1 @@ -Err(CannotTransformEdgeWithoutFold("primeFactor")) +Err(TransformTypeError(CannotTransformEdgeWithoutFold("primeFactor", " Did you mean to apply @fold to the edge before the @transform directive?"))) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge_without_inner_scope.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge_without_inner_scope.frontend-error.ron index 7cfdd09f..d721faf7 100644 --- a/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge_without_inner_scope.frontend-error.ron +++ b/trustfall_core/test_data/tests/frontend_errors/transform_count_on_unfolded_edge_without_inner_scope.frontend-error.ron @@ -1 +1 @@ -Err(CannotTransformEdgeWithoutFold("primeFactor")) +Err(TransformTypeError(CannotTransformEdgeWithoutFold("primeFactor", " Did you mean to apply @fold to the edge before the @transform directive?"))) From 639131d61505247224dda73528c14a30a07de8c4 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Mon, 24 Jun 2024 17:40:55 +0000 Subject: [PATCH 18/30] Implement using transformed values in a tag. --- trustfall_core/src/interpreter/execution.rs | 105 ++--- trustfall_core/src/interpreter/filtering.rs | 2 +- .../src/interpreter/hints/dynamic.rs | 2 +- trustfall_core/src/interpreter/tags.rs | 184 ++++++--- .../src/interpreter/transformation.rs | 4 +- .../fold_count_transform_then_tag.output.ron | 15 + .../fold_count_transform_then_tag.trace.ron | 384 ++++++++++++++++++ 7 files changed, 584 insertions(+), 112 deletions(-) create mode 100644 trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.trace.ron diff --git a/trustfall_core/src/interpreter/execution.rs b/trustfall_core/src/interpreter/execution.rs index 015bdb72..3de13812 100644 --- a/trustfall_core/src/interpreter/execution.rs +++ b/trustfall_core/src/interpreter/execution.rs @@ -16,6 +16,7 @@ use crate::{ use super::{ error::QueryArgumentsError, filtering::apply_filter, + tags::compute_tag_with_separate_value, transformation::{ apply_transforms, push_transform_argument_tag_values_onto_stack, push_transform_argument_tag_values_onto_stack_during_main_query, @@ -570,56 +571,21 @@ fn compute_fold<'query, AdapterT: Adapter<'query> + 'query>( ) -> ContextIterator<'query, AdapterT::Vertex> { // Get any imported tag values needed inside the fold component or one of its subcomponents. for imported_field in fold.imported_tags.iter() { - match &imported_field { - FieldRef::ContextField(field) => { - let vertex_id = field.vertex_id; - let activated_vertex_iterator: ContextIterator<'query, AdapterT::Vertex> = - Box::new(iterator.map(move |x| x.activate_vertex(&vertex_id))); - - let field_vertex = &parent_component.vertices[&field.vertex_id]; - let type_name = &field_vertex.type_name; - - let query = carrier.query.take().expect("query was not returned"); - let resolve_info = ResolveInfo::new(query, vertex_id, true); - - let context_and_value_iterator = adapter.resolve_property( - activated_vertex_iterator, - type_name, - &field.field_name, - &resolve_info, - ); - carrier.query = Some(resolve_info.into_inner()); - - let cloned_field = imported_field.clone(); - iterator = Box::new(context_and_value_iterator.map(move |(mut context, value)| { - // Check whether the tagged value is coming from an `@optional` scope - // that did not exist, in order to satisfy its filtering semantics. - let tag_value = if context.vertices[&vertex_id].is_some() { - TaggedValue::Some(value) - } else { - TaggedValue::NonexistentOptional - }; - context.imported_tags.insert(cloned_field.clone(), tag_value); - context - })); - } - FieldRef::FoldSpecificField(fold_specific_field) => { - let cloned_field = imported_field.clone(); - let fold_eid = fold_specific_field.fold_eid; - iterator = Box::new( - compute_fold_specific_field_with_separate_value( - fold_specific_field.fold_eid, - &fold_specific_field.kind, - iterator, - ) - .map(move |(mut ctx, tagged_value)| { - ctx.imported_tags.insert(cloned_field.clone(), tagged_value); - ctx - }), - ); - } - FieldRef::TransformedField(_) => todo!(), - } + let cloned_field = imported_field.clone(); + iterator = Box::new( + compute_tag_with_separate_value::( + adapter.as_ref(), + carrier, + parent_component, + expanding_from.vid, + imported_field, + iterator, + ) + .map(move |(mut ctx, tagged_value)| { + ctx.imported_tags.insert(cloned_field.clone(), tagged_value); + ctx + }), + ); } // Get the initial vertices inside the folded scope. @@ -1074,6 +1040,10 @@ pub(super) fn compute_context_field_with_separate_value< 'query, AdapterT: Adapter<'query>, V: AsVertex + 'query, + // For the contexts inside the input iterator, we sometimes may need to change what their + // `ctx.active_vertex` value holds. This const generic controls whether we restore + // the contents of `ctx.active_vertex` to its prior value after we're done, or not. + const RESTORE_CONTEXT: bool, >( adapter: &AdapterT, carrier: &mut QueryCarrier, @@ -1084,12 +1054,17 @@ pub(super) fn compute_context_field_with_separate_value< let vertex_id = context_field.vertex_id; if let Some(vertex) = component.vertices.get(&vertex_id) { - let moved_iterator = iterator.map(move |mut context| { - let active_vertex = context.active_vertex.clone(); - let new_vertex = context.vertices[&vertex_id].clone(); - context.suspended_vertices.push(active_vertex); - context.move_to_vertex(new_vertex) - }); + let resolve_property_input_iterator: Box> + 'query> = + if RESTORE_CONTEXT { + Box::new(iterator.map(move |mut context| { + let active_vertex = context.active_vertex.clone(); + let new_vertex = context.vertices[&vertex_id].clone(); + context.suspended_vertices.push(active_vertex); + context.move_to_vertex(new_vertex) + })) + } else { + Box::new(iterator.map(move |ctx| ctx.activate_vertex(&vertex_id))) + }; let type_name = &vertex.type_name; let query = carrier.query.take().expect("query was not returned"); @@ -1097,23 +1072,27 @@ pub(super) fn compute_context_field_with_separate_value< let context_and_value_iterator = adapter .resolve_property( - Box::new(moved_iterator), + resolve_property_input_iterator, type_name, &context_field.field_name, &resolve_info, ) - .map(move |(mut context, value)| { - let tagged_value = if context.vertices[&vertex_id].is_some() { + .map(move |(mut ctx, value)| { + let tagged_value = if ctx.vertices[&vertex_id].is_some() { TaggedValue::Some(value) } else { // The value is coming from an @optional scope that didn't exist. TaggedValue::NonexistentOptional }; - // Make sure that the context has the same "current" token - // as before evaluating the context field. - let old_current_token = context.suspended_vertices.pop().unwrap(); - (context.move_to_vertex(old_current_token), tagged_value) + if RESTORE_CONTEXT { + // Make sure that the context has the same "current" token + // as before evaluating the context field. + let old_current_token = ctx.suspended_vertices.pop().unwrap(); + (ctx.move_to_vertex(old_current_token), tagged_value) + } else { + (ctx, tagged_value) + } }); carrier.query = Some(resolve_info.into_inner()); diff --git a/trustfall_core/src/interpreter/filtering.rs b/trustfall_core/src/interpreter/filtering.rs index 3c38996a..4e1e1eb6 100644 --- a/trustfall_core/src/interpreter/filtering.rs +++ b/trustfall_core/src/interpreter/filtering.rs @@ -279,7 +279,7 @@ pub(super) fn apply_filter<'query, AdapterT: Adapter<'query>>( apply_filter_with_static_argument_value(filter, right_value, iterator) } Some(Argument::Tag(field_ref)) => { - let argument_value_iterator = compute_tag_with_separate_value( + let argument_value_iterator = compute_tag_with_separate_value::( adapter, carrier, component, diff --git a/trustfall_core/src/interpreter/hints/dynamic.rs b/trustfall_core/src/interpreter/hints/dynamic.rs index 25747807..53763d98 100644 --- a/trustfall_core/src/interpreter/hints/dynamic.rs +++ b/trustfall_core/src/interpreter/hints/dynamic.rs @@ -265,7 +265,7 @@ impl<'a> DynamicallyResolvedValue<'a> { contexts: ContextIterator<'vertex, V>, ) -> ContextOutcomeIterator<'vertex, V, CandidateValue> { let mut carrier = QueryCarrier { query: Some(self.query) }; - let iterator = compute_context_field_with_separate_value( + let iterator = compute_context_field_with_separate_value::( adapter, &mut carrier, self.resolve_on_component, diff --git a/trustfall_core/src/interpreter/tags.rs b/trustfall_core/src/interpreter/tags.rs index dec68995..f666c8e1 100644 --- a/trustfall_core/src/interpreter/tags.rs +++ b/trustfall_core/src/interpreter/tags.rs @@ -1,14 +1,26 @@ -use crate::ir::{FieldRef, IRQueryComponent, LocalField, Vid}; +use std::{fmt::Debug, sync::Arc}; + +use crate::ir::{ + ContextField, FieldRef, FoldSpecificField, IRQueryComponent, LocalField, TransformBase, Vid, +}; use super::{ execution::{ compute_context_field_with_separate_value, compute_fold_specific_field_with_separate_value, compute_local_field_with_separate_value, QueryCarrier, }, + transformation::apply_transforms, Adapter, ContextIterator, DataContext, TaggedValue, }; -pub(super) fn compute_tag_with_separate_value<'query, AdapterT: Adapter<'query>>( +pub(super) fn compute_tag_with_separate_value< + 'query, + AdapterT: Adapter<'query>, + // For the contexts inside the input iterator, we sometimes may need to change what their + // `ctx.active_vertex` value holds. This const generic controls whether we restore + // the contents of `ctx.active_vertex` to its prior value after we're done, or not. + const RESTORE_CONTEXT: bool, +>( adapter: &AdapterT, carrier: &mut QueryCarrier, component: &IRQueryComponent, @@ -18,55 +30,135 @@ pub(super) fn compute_tag_with_separate_value<'query, AdapterT: Adapter<'query>> ) -> Box, TaggedValue)> + 'query> { match field_ref { FieldRef::ContextField(context_field) => { - // TODO: Benchmark if it would be faster to duplicate the code to special-case - // the situation when the tag is always known to exist, so we don't have to unwrap - // a TaggedValue enum, because we know it would be TaggedValue::Some. - if context_field.vertex_id == current_vid { - // This tag is from the vertex we're currently evaluating. That means the field - // whose value we want to get is actually local, so there's no need to compute it - // using the more expensive approach we use for non-local fields. - let local_equivalent_field = LocalField { - field_name: context_field.field_name.clone(), - field_type: context_field.field_type.clone(), - }; - Box::new( - compute_local_field_with_separate_value( + compute_context_field_tag_with_separate_value::( + current_vid, + adapter, + carrier, + component, + context_field, + iterator, + ) + } + FieldRef::FoldSpecificField(fold_field) => { + compute_fold_specific_field_tag_with_separate_value(component, fold_field, iterator) + } + FieldRef::TransformedField(transformed_field) => { + let base_value_iterator = match &transformed_field.value.base { + TransformBase::ContextField(context_field) => { + compute_context_field_tag_with_separate_value::( + current_vid, adapter, carrier, component, - current_vid, - &local_equivalent_field, + context_field, iterator, ) - .map(|(ctx, value)| (ctx, TaggedValue::Some(value))), - ) - } else { - compute_context_field_with_separate_value( - adapter, - carrier, - component, - context_field, - iterator, - ) - } - } - FieldRef::FoldSpecificField(fold_field) => { - if component.folds.contains_key(&fold_field.fold_eid) { - compute_fold_specific_field_with_separate_value( - fold_field.fold_eid, - &fold_field.kind, - iterator, - ) - } else { - // This value represents an imported tag value from an outer component. - // Grab its value from the context itself. - let cloned_ref = field_ref.clone(); - Box::new(iterator.map(move |ctx| { - let right_value = ctx.imported_tags[&cloned_ref].clone(); - (ctx, right_value) - })) - } + } + TransformBase::FoldSpecificField(fold_field) => { + compute_fold_specific_field_tag_with_separate_value( + component, fold_field, iterator, + ) + } + }; + + let transformed_value = Arc::clone(&transformed_field.value); + + let variables = carrier + .query + .as_ref() + .map(|query| Arc::clone(&query.arguments)) + .expect("query was not returned"); + + Box::new(base_value_iterator.map(move |(mut ctx, base_value)| { + let value = match base_value { + TaggedValue::NonexistentOptional => TaggedValue::NonexistentOptional, + TaggedValue::Some(value) => TaggedValue::Some(apply_transforms( + &transformed_value, + &variables, + &mut ctx.values, + value, + )), + }; + + (ctx, value) + })) } - FieldRef::TransformedField(_) => todo!(), + } +} + +fn compute_context_field_tag_with_separate_value< + 'query, + AdapterT: Adapter<'query>, + // For the contexts inside the input iterator, we sometimes may need to change what their + // `ctx.active_vertex` value holds. This const generic controls whether we restore + // the contents of `ctx.active_vertex` to its prior value after we're done, or not. + const RESTORE_CONTEXT: bool, +>( + current_vid: Vid, + adapter: &AdapterT, + carrier: &mut QueryCarrier, + component: &IRQueryComponent, + context_field: &ContextField, + iterator: ContextIterator<'query, AdapterT::Vertex>, +) -> Box, TaggedValue)> + 'query> { + // TODO: Benchmark if it would be faster to duplicate the code to special-case + // the situation when the tag is always known to exist, so we don't have to unwrap + // a TaggedValue enum, because we know it would be TaggedValue::Some. + if context_field.vertex_id == current_vid { + // This tag is from the vertex we're currently evaluating. That means the field + // whose value we want to get is actually local, so there's no need to compute it + // using the more expensive approach we use for non-local fields. + let local_equivalent_field = LocalField { + field_name: context_field.field_name.clone(), + field_type: context_field.field_type.clone(), + }; + Box::new( + compute_local_field_with_separate_value( + adapter, + carrier, + component, + current_vid, + &local_equivalent_field, + iterator, + ) + .map(|(ctx, value)| (ctx, TaggedValue::Some(value))), + ) + } else { + compute_context_field_with_separate_value::( + adapter, + carrier, + component, + context_field, + iterator, + ) + } +} + +fn compute_fold_specific_field_tag_with_separate_value<'query, Vertex: Debug + Clone + 'query>( + component: &IRQueryComponent, + fold_field: &FoldSpecificField, + iterator: ContextIterator<'query, Vertex>, +) -> Box, TaggedValue)> + 'query> { + if component.folds.contains_key(&fold_field.fold_eid) { + // This value represents a fold-specific field of a `@fold` that + // is directly part of this query component, such as a `@fold @transform(op: "count")`. + // That makes the fold-specific value directly computable via the folded `DataContext`s. + compute_fold_specific_field_with_separate_value( + fold_field.fold_eid, + &fold_field.kind, + iterator, + ) + } else { + // This value represents an imported tag value from an outer component. + // Grab its value from the context itself. + // + // TODO: If we ever allow exporting tags from inside `@fold` (including getting + // fold-specific values of a *nested* fold), then those cases will end up + // inside this `else` block as well and we'll need to tell them apart. + let moved_ref = FieldRef::FoldSpecificField(fold_field.to_owned()); + Box::new(iterator.map(move |ctx| { + let right_value = ctx.imported_tags[&moved_ref].clone(); + (ctx, right_value) + })) } } diff --git a/trustfall_core/src/interpreter/transformation.rs b/trustfall_core/src/interpreter/transformation.rs index fcfe2e06..a027925f 100644 --- a/trustfall_core/src/interpreter/transformation.rs +++ b/trustfall_core/src/interpreter/transformation.rs @@ -25,7 +25,9 @@ pub(super) fn push_transform_argument_tag_values_onto_stack_during_main_query< inner_iterator: ContextIterator<'query, AdapterT::Vertex>| -> ContextIterator<'query, AdapterT::Vertex> { Box::new( - compute_tag_with_separate_value( + // TODO: We should propagate `RESTORE_CONTEXT` here instead of setting it to `true`, + // because it might be unnecessary. + compute_tag_with_separate_value::( adapter, inner_carrier, component, diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.output.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.output.ron new file mode 100644 index 00000000..570daa88 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.output.ron @@ -0,0 +1,15 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "value": Output( + name: "value", + value_type: "Int", + vid: Vid(3), + ), + }, + results: [ + { + "value": Int64(3), + }, + ], +) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.trace.ron new file mode 100644 index 00000000..2c55e1da --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag.trace.ron @@ -0,0 +1,384 @@ +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(ResolveNeighbors(Vid(1), "Composite", Eid(1))), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveNeighbors(Vid(1), "Composite", Eid(2))), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: None, + content: Call(ResolveProperty(Vid(3), "Number", "value")), + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: None, + content: Call(ResolveProperty(Vid(3), "Number", "value")), + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: Some(Opid(5)), + content: AdvanceInputIterator, + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Composite(CompositeNumber(4, [ + 2, + ])))), + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(4, [ + 2, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(4, [ + 2, + ]))), + }, + )), + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(4, [ + 2, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(4, [ + 2, + ]))), + }, + ))), + ), + 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: OutputIteratorExhausted, + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(4, [ + 2, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(4, [ + 2, + ]))), + }, + folded_contexts: { + Eid(1): Some([ + SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + ), + ]), + }, + )), + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(4, [ + 2, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(4, [ + 2, + ]))), + }, + folded_contexts: { + Eid(1): Some([ + SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + ), + ]), + }, + ))), + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(16)), + content: YieldFrom(ResolveNeighborsInner(0, Prime(PrimeNumber(3)))), + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(4)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(4, [ + 2, + ]))), + }, + folded_contexts: { + Eid(1): Some([ + SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + ), + ]), + }, + )), + ), + Opid(19): TraceOp( + opid: Opid(19), + parent_opid: Some(Opid(4)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(4, [ + 2, + ]))), + }, + folded_contexts: { + Eid(1): Some([ + SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + ), + ]), + }, + ), Int64(3))), + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: Some(Opid(5)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(4, [ + 2, + ]))), + Vid(3): Some(Prime(PrimeNumber(3))), + }, + folded_contexts: { + Eid(1): Some([ + SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + ), + ]), + }, + )), + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: Some(Opid(5)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(4, [ + 2, + ]))), + Vid(3): Some(Prime(PrimeNumber(3))), + }, + folded_contexts: { + Eid(1): Some([ + SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + ), + ]), + }, + ), Int64(3))), + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: None, + content: ProduceQueryResult({ + "value": Int64(3), + }), + ), + Opid(23): TraceOp( + opid: Opid(23), + parent_opid: Some(Opid(5)), + content: AdvanceInputIterator, + ), + Opid(24): TraceOp( + opid: Opid(24), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(25): TraceOp( + opid: Opid(25), + parent_opid: Some(Opid(16)), + content: OutputIteratorExhausted, + ), + Opid(26): TraceOp( + opid: Opid(26), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(27): TraceOp( + opid: Opid(27), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(28): TraceOp( + opid: Opid(28), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(29): TraceOp( + opid: Opid(29), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(30): TraceOp( + opid: Opid(30), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(31): TraceOp( + opid: Opid(31), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(32): TraceOp( + opid: Opid(32), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + Opid(33): TraceOp( + opid: Opid(33), + parent_opid: Some(Opid(4)), + content: InputIteratorExhausted, + ), + Opid(34): TraceOp( + opid: Opid(34), + parent_opid: Some(Opid(4)), + content: OutputIteratorExhausted, + ), + Opid(35): TraceOp( + opid: Opid(35), + parent_opid: Some(Opid(5)), + content: InputIteratorExhausted, + ), + Opid(36): TraceOp( + opid: Opid(36), + parent_opid: Some(Opid(5)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "Four", + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Composite", + ), + Vid(3): IRVertex( + vid: Vid(3), + type_name: "Number", + filters: [ + GreaterThan(LocalField(LocalField( + field_name: "value", + field_type: "Int", + )), Tag(TransformedField(TransformedField( + value: TransformedValue( + base: FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), + transforms: [ + Abs, + ], + ), + tid: Tid(2), + field_type: "Int!", + )))), + ], + ), + }, + edges: { + Eid(2): IREdge( + eid: Eid(2), + from_vid: Vid(1), + to_vid: Vid(3), + edge_name: "predecessor", + ), + }, + 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", + ), + }, + ), + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(3), + field_name: "value", + field_type: "Int", + )), + }, + ), + ), + ), +) From ad7b6c39fd46a7d9bdf26cf1b3156a518261beb5 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Mon, 24 Jun 2024 23:29:39 +0000 Subject: [PATCH 19/30] Finalize implementation of outputing transformed values. --- trustfall_core/src/interpreter/execution.rs | 93 +- ...ransform_then_filter_and_output.output.ron | 25 + ...transform_then_filter_and_output.trace.ron | 486 ++++++++ ...orm_then_tag_and_filter_on_self.output.ron | 32 + ...form_then_tag_and_filter_on_self.trace.ron | 562 +++++++++ ..._count_transform_output.graphql-parsed.ron | 147 +++ ...ed_fold_count_transform_output.graphql.ron | 20 + .../nested_fold_count_transform_output.ir.ron | 114 ++ ...ted_fold_count_transform_output.output.ron | 54 + ...sted_fold_count_transform_output.trace.ron | 1036 +++++++++++++++++ 10 files changed, 2529 insertions(+), 40 deletions(-) create mode 100644 trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.trace.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.trace.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.trace.ron diff --git a/trustfall_core/src/interpreter/execution.rs b/trustfall_core/src/interpreter/execution.rs index 3de13812..efa8faaf 100644 --- a/trustfall_core/src/interpreter/execution.rs +++ b/trustfall_core/src/interpreter/execution.rs @@ -6,9 +6,9 @@ use std::{ use crate::{ ir::{ - Argument, ContextField, EdgeParameters, Eid, FieldRef, FieldValue, FoldSpecificFieldKind, - IREdge, IRFold, IRQueryComponent, IRVertex, IndexedQuery, LocalField, Operation, - OperationSubject, Recursive, TransformBase, TransformedField, Vid, + Argument, ContextField, EdgeParameters, Eid, FieldRef, FieldValue, FoldSpecificField, + FoldSpecificFieldKind, IREdge, IRFold, IRQueryComponent, IRVertex, IndexedQuery, + LocalField, Operation, OperationSubject, Recursive, TransformBase, TransformedField, Vid, }, util::BTreeMapTryInsertExt, }; @@ -595,6 +595,7 @@ fn compute_fold<'query, AdapterT: Adapter<'query> + 'query>( let type_name = &expanding_from.type_name; let query = carrier.query.take().expect("query was not returned"); + let variables = Arc::clone(&query.arguments); let resolve_info = ResolveEdgeInfo::new(query, expanding_from_vid, fold.to_vid, fold.eid); let edge_iterator = adapter.resolve_neighbors( @@ -733,14 +734,24 @@ mismatch on whether the fold below {expanding_from_vid:?} was inside an `@option // its outputs should be `null` rather than empty lists (the usual for empty folds). // Transformed outputs should also be `null` rather than their usual transformed defaults. let value = fold_elements.as_ref().map(|elements| match field_ref { - FieldRef::FoldSpecificField(field) => match field.kind { - FoldSpecificFieldKind::Count => { - ValueOrVec::Value(FieldValue::Uint64(elements.len() as u64)) - } - }, - FieldRef::TransformedField(field) => todo!(), + FieldRef::FoldSpecificField(field) => ValueOrVec::Value(compute_fold_specific_value::(field, elements)), + FieldRef::TransformedField(field) => { + let base_value = match &field.value.base { + TransformBase::FoldSpecificField(fold_field) => compute_fold_specific_value::(fold_field, &elements), + TransformBase::ContextField(_) => unreachable!("found transformed ContextField inside a fold's fold-specific outputs: {fold:#?} {field_ref:#?}"), + }; + + // TODO: Call push_transform_argument_tag_values_onto_stack_during_main_query() + // for all transforms across all outputs, in the same order as here. + // Probably reorganize the code so that we don't push too many args + // onto the stack, and instead go output-at-a-time per iterator layer. + + let final_value = apply_transforms(&field.value, &variables, &mut ctx.values, base_value); + + ValueOrVec::Value(final_value) + } FieldRef::ContextField(_) => unreachable!( - "found ContextField inside a fold's fold-specific outputs: {fold:#?}" + "found ContextField inside a fold's fold-specific outputs: {fold:#?} {field_ref:?}" ), }); ctx.folded_values @@ -780,40 +791,33 @@ mismatch on whether the fold below {expanding_from_vid:?} was inside an `@option fold_elements.as_ref().expect("fold did not contain elements").clone().into_iter(), ); for output_name in output_names.iter() { - // This is a slimmed-down version of computing a context field: - // - it does not restore the prior active vertex after getting each value - // - it already knows that the context field is guaranteed to exist let field_ref = &fold.component.outputs[output_name.as_ref()]; - match field_ref { - FieldRef::ContextField(context_field) => { - let vertex_id = context_field.vertex_id; - let moved_iterator = Box::new(output_iterator.map(move |context| { - let new_vertex = context.vertices[&vertex_id].clone(); - context.move_to_vertex(new_vertex) - })); - - let query = cloned_carrier.query.take().expect("query was not returned"); - let resolve_info = ResolveInfo::new(query, vertex_id, true); - let field_data_iterator = cloned_adapter.resolve_property( - moved_iterator, - &fold.component.vertices[&vertex_id].type_name, - &context_field.field_name, - &resolve_info, - ); - cloned_carrier.query = Some(resolve_info.into_inner()); - - output_iterator = - Box::new(field_data_iterator.map(|(mut context, value)| { - context.values.push(value); - context - })); - } - FieldRef::TransformedField(_) => todo!(), - FieldRef::FoldSpecificField(_) => { - unreachable!("found fold-specific field in component outputs: {fold:#?}") + if cfg!(debug_assertions) { + // Only `ContextField` and transformations thereof are expected here. + // Fold-specific fields (and their transformations) are handled elsewhere. + match field_ref { + FieldRef::ContextField(_) => {} + FieldRef::TransformedField(field) => { + match &field.value.base { + TransformBase::ContextField(_) => {} + TransformBase::FoldSpecificField(_) => unreachable!("fold component outputs contained transformed fold-specific field: {fold:#?} {field_ref:?}"), + } + } + FieldRef::FoldSpecificField(_) => unreachable!("fold component outputs contained fold-specific field: {fold:#?} {field_ref:?}"), } } + + output_iterator = Box::new( + compute_tag_with_separate_value::(cloned_adapter.as_ref(), &mut cloned_carrier, &fold.component, fold.from_vid, field_ref, output_iterator).map(|(mut ctx, tag_value)| { + let value = match tag_value { + TaggedValue::NonexistentOptional => FieldValue::Null, + TaggedValue::Some(v) => v, + }; + ctx.values.push(value); + ctx + }) + ); } for mut folded_context in output_iterator { @@ -858,6 +862,15 @@ mismatch on whether the fold below {expanding_from_vid:?} was inside an `@option Box::new(final_iterator) } +fn compute_fold_specific_value<'query, AdapterT: Adapter<'query>>( + fold_field: &FoldSpecificField, + fold_elements: &[DataContext], +) -> FieldValue { + match fold_field.kind { + FoldSpecificFieldKind::Count => FieldValue::Uint64(fold_elements.len() as u64), + } +} + fn apply_filter_with_non_folded_field_subject<'query, AdapterT: Adapter<'query>>( adapter: &AdapterT, carrier: &mut QueryCarrier, diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.output.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.output.ron new file mode 100644 index 00000000..1679a992 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.output.ron @@ -0,0 +1,25 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "factors": Output( + name: "factors", + value_type: "[Int]!", + vid: Vid(2), + ), + "primeFactorcountabs": Output( + name: "primeFactorcountabs", + value_type: "Int!", + vid: Vid(2), + ), + }, + results: [ + { + "factors": List([ + Int64(2), + Int64(3), + Int64(5), + ]), + "primeFactorcountabs": Uint64(3), + }, + ], +) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.trace.ron new file mode 100644 index 00000000..c0281c36 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_filter_and_output.trace.ron @@ -0,0 +1,486 @@ +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: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Composite(CompositeNumber(28, [ + 2, + 7, + ])))), + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(28, [ + 2, + 7, + ]))), + vertices: {}, + )), + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveCoercion(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(28, [ + 2, + 7, + ]))), + vertices: {}, + ), true)), + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(28, [ + 2, + 7, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(28, [ + 2, + 7, + ]))), + }, + )), + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(28, [ + 2, + 7, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(28, [ + 2, + 7, + ]))), + }, + ))), + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: Some(Opid(10)), + content: YieldFrom(ResolveNeighborsInner(0, Prime(PrimeNumber(2)))), + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(10)), + content: YieldFrom(ResolveNeighborsInner(1, Prime(PrimeNumber(7)))), + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: Some(Opid(10)), + content: OutputIteratorExhausted, + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Prime(PrimeNumber(29)))), + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(29))), + vertices: {}, + )), + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveCoercion(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(29))), + vertices: {}, + ), false)), + ), + Opid(19): TraceOp( + opid: Opid(19), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ])))), + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + vertices: {}, + )), + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveCoercion(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + vertices: {}, + ), true)), + ), + Opid(23): TraceOp( + opid: Opid(23), + 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(24): TraceOp( + opid: Opid(24), + 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(25): TraceOp( + opid: Opid(25), + parent_opid: Some(Opid(24)), + content: YieldFrom(ResolveNeighborsInner(0, Prime(PrimeNumber(2)))), + ), + Opid(26): TraceOp( + opid: Opid(26), + parent_opid: Some(Opid(24)), + content: YieldFrom(ResolveNeighborsInner(1, Prime(PrimeNumber(3)))), + ), + Opid(27): TraceOp( + opid: Opid(27), + parent_opid: Some(Opid(24)), + content: YieldFrom(ResolveNeighborsInner(2, Prime(PrimeNumber(5)))), + ), + Opid(28): TraceOp( + opid: Opid(28), + parent_opid: Some(Opid(24)), + content: OutputIteratorExhausted, + ), + Opid(29): TraceOp( + opid: Opid(29), + parent_opid: None, + content: Call(ResolveProperty(Vid(2), "Prime", "value")), + ), + Opid(30): TraceOp( + opid: Opid(30), + parent_opid: Some(Opid(29)), + content: AdvanceInputIterator, + ), + Opid(31): TraceOp( + opid: Opid(31), + parent_opid: Some(Opid(29)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + )), + ), + Opid(32): TraceOp( + opid: Opid(32), + parent_opid: Some(Opid(29)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + ), Int64(2))), + ), + Opid(33): TraceOp( + opid: Opid(33), + parent_opid: Some(Opid(29)), + content: AdvanceInputIterator, + ), + Opid(34): TraceOp( + opid: Opid(34), + parent_opid: Some(Opid(29)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(3))), + }, + )), + ), + Opid(35): TraceOp( + opid: Opid(35), + parent_opid: Some(Opid(29)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(3))), + }, + ), Int64(3))), + ), + Opid(36): TraceOp( + opid: Opid(36), + parent_opid: Some(Opid(29)), + content: AdvanceInputIterator, + ), + Opid(37): TraceOp( + opid: Opid(37), + parent_opid: Some(Opid(29)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(5))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(5))), + }, + )), + ), + Opid(38): TraceOp( + opid: Opid(38), + parent_opid: Some(Opid(29)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(5))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(5))), + }, + ), Int64(5))), + ), + Opid(39): TraceOp( + opid: Opid(39), + parent_opid: Some(Opid(29)), + content: AdvanceInputIterator, + ), + Opid(40): TraceOp( + opid: Opid(40), + parent_opid: Some(Opid(29)), + content: InputIteratorExhausted, + ), + Opid(41): TraceOp( + opid: Opid(41), + parent_opid: Some(Opid(29)), + content: OutputIteratorExhausted, + ), + Opid(42): TraceOp( + opid: Opid(42), + parent_opid: None, + content: ProduceQueryResult({ + "factors": List([ + Int64(2), + Int64(3), + Int64(5), + ]), + "primeFactorcountabs": Uint64(3), + }), + ), + Opid(43): TraceOp( + opid: Opid(43), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(44): TraceOp( + opid: Opid(44), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(45): TraceOp( + opid: Opid(45), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Prime(PrimeNumber(31)))), + ), + Opid(46): TraceOp( + opid: Opid(46), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(31))), + vertices: {}, + )), + ), + Opid(47): TraceOp( + opid: Opid(47), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveCoercion(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(31))), + vertices: {}, + ), false)), + ), + Opid(48): TraceOp( + opid: Opid(48), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(49): TraceOp( + opid: Opid(49), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(50): TraceOp( + opid: Opid(50), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(51): TraceOp( + opid: Opid(51), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(52): TraceOp( + opid: Opid(52), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(53): TraceOp( + opid: Opid(53), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(31), + "min": Int64(28), + }, + ), + 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", + ), + }, + outputs: { + "factors": ContextField(ContextField( + vertex_id: Vid(2), + field_name: "value", + field_type: "Int", + )), + }, + ), + fold_specific_outputs: { + "primeFactorcountabs": TransformedField(TransformedField( + value: TransformedValue( + base: FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), + transforms: [ + Abs, + ], + ), + tid: Tid(2), + field_type: "Int!", + )), + }, + post_filters: [ + GreaterThan(TransformedField(TransformedField( + value: TransformedValue( + base: FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), + transforms: [ + Abs, + ], + ), + tid: Tid(2), + field_type: "Int!", + )), Variable(VariableRef( + variable_name: "two", + variable_type: "Int!", + ))), + ], + ), + }, + ), + variables: { + "two": "Int!", + }, + ), + arguments: { + "two": Uint64(2), + }, + ), +) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.output.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.output.ron new file mode 100644 index 00000000..c0f5280a --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.output.ron @@ -0,0 +1,32 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "factors": Output( + name: "factors", + value_type: "[Int]!", + vid: Vid(2), + ), + "primeFactorcountabs": Output( + name: "primeFactorcountabs", + value_type: "Int!", + vid: Vid(2), + ), + }, + results: [ + { + "factors": List([ + Int64(2), + Int64(7), + ]), + "primeFactorcountabs": Uint64(2), + }, + { + "factors": List([ + Int64(2), + Int64(3), + Int64(5), + ]), + "primeFactorcountabs": Uint64(3), + }, + ], +) diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.trace.ron new file mode 100644 index 00000000..ec6fefd2 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_transform_then_tag_and_filter_on_self.trace.ron @@ -0,0 +1,562 @@ +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: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Composite(CompositeNumber(28, [ + 2, + 7, + ])))), + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(28, [ + 2, + 7, + ]))), + vertices: {}, + )), + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveCoercion(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(28, [ + 2, + 7, + ]))), + vertices: {}, + ), true)), + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(28, [ + 2, + 7, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(28, [ + 2, + 7, + ]))), + }, + )), + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(28, [ + 2, + 7, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(28, [ + 2, + 7, + ]))), + }, + ))), + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: Some(Opid(10)), + content: YieldFrom(ResolveNeighborsInner(0, Prime(PrimeNumber(2)))), + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(10)), + content: YieldFrom(ResolveNeighborsInner(1, Prime(PrimeNumber(7)))), + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: Some(Opid(10)), + content: OutputIteratorExhausted, + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: None, + content: Call(ResolveProperty(Vid(2), "Prime", "value")), + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(14)), + content: AdvanceInputIterator, + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(14)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + )), + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(14)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + ), Int64(2))), + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(14)), + content: AdvanceInputIterator, + ), + Opid(19): TraceOp( + opid: Opid(19), + parent_opid: Some(Opid(14)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(7))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(7))), + }, + )), + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: Some(Opid(14)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(7))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(7))), + }, + ), Int64(7))), + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: Some(Opid(14)), + content: AdvanceInputIterator, + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: Some(Opid(14)), + content: InputIteratorExhausted, + ), + Opid(23): TraceOp( + opid: Opid(23), + parent_opid: Some(Opid(14)), + content: OutputIteratorExhausted, + ), + Opid(24): TraceOp( + opid: Opid(24), + parent_opid: None, + content: ProduceQueryResult({ + "factors": List([ + Int64(2), + Int64(7), + ]), + "primeFactorcountabs": Uint64(2), + }), + ), + Opid(25): TraceOp( + opid: Opid(25), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(26): TraceOp( + opid: Opid(26), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(27): TraceOp( + opid: Opid(27), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Prime(PrimeNumber(29)))), + ), + Opid(28): TraceOp( + opid: Opid(28), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(29))), + vertices: {}, + )), + ), + Opid(29): TraceOp( + opid: Opid(29), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveCoercion(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(29))), + vertices: {}, + ), false)), + ), + Opid(30): TraceOp( + opid: Opid(30), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(31): TraceOp( + opid: Opid(31), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ])))), + ), + Opid(32): TraceOp( + opid: Opid(32), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + vertices: {}, + )), + ), + Opid(33): TraceOp( + opid: Opid(33), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveCoercion(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(30, [ + 2, + 3, + 5, + ]))), + vertices: {}, + ), true)), + ), + Opid(34): TraceOp( + opid: Opid(34), + 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(35): TraceOp( + opid: Opid(35), + 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(36): TraceOp( + opid: Opid(36), + parent_opid: Some(Opid(35)), + content: YieldFrom(ResolveNeighborsInner(0, Prime(PrimeNumber(2)))), + ), + Opid(37): TraceOp( + opid: Opid(37), + parent_opid: Some(Opid(35)), + content: YieldFrom(ResolveNeighborsInner(1, Prime(PrimeNumber(3)))), + ), + Opid(38): TraceOp( + opid: Opid(38), + parent_opid: Some(Opid(35)), + content: YieldFrom(ResolveNeighborsInner(2, Prime(PrimeNumber(5)))), + ), + Opid(39): TraceOp( + opid: Opid(39), + parent_opid: Some(Opid(35)), + content: OutputIteratorExhausted, + ), + Opid(40): TraceOp( + opid: Opid(40), + parent_opid: None, + content: Call(ResolveProperty(Vid(2), "Prime", "value")), + ), + Opid(41): TraceOp( + opid: Opid(41), + parent_opid: Some(Opid(40)), + content: AdvanceInputIterator, + ), + Opid(42): TraceOp( + opid: Opid(42), + parent_opid: Some(Opid(40)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + )), + ), + Opid(43): TraceOp( + opid: Opid(43), + parent_opid: Some(Opid(40)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + ), Int64(2))), + ), + Opid(44): TraceOp( + opid: Opid(44), + parent_opid: Some(Opid(40)), + content: AdvanceInputIterator, + ), + Opid(45): TraceOp( + opid: Opid(45), + parent_opid: Some(Opid(40)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(3))), + }, + )), + ), + Opid(46): TraceOp( + opid: Opid(46), + parent_opid: Some(Opid(40)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(3))), + }, + ), Int64(3))), + ), + Opid(47): TraceOp( + opid: Opid(47), + parent_opid: Some(Opid(40)), + content: AdvanceInputIterator, + ), + Opid(48): TraceOp( + opid: Opid(48), + parent_opid: Some(Opid(40)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(5))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(5))), + }, + )), + ), + Opid(49): TraceOp( + opid: Opid(49), + parent_opid: Some(Opid(40)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(5))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(5))), + }, + ), Int64(5))), + ), + Opid(50): TraceOp( + opid: Opid(50), + parent_opid: Some(Opid(40)), + content: AdvanceInputIterator, + ), + Opid(51): TraceOp( + opid: Opid(51), + parent_opid: Some(Opid(40)), + content: InputIteratorExhausted, + ), + Opid(52): TraceOp( + opid: Opid(52), + parent_opid: Some(Opid(40)), + content: OutputIteratorExhausted, + ), + Opid(53): TraceOp( + opid: Opid(53), + parent_opid: None, + content: ProduceQueryResult({ + "factors": List([ + Int64(2), + Int64(3), + Int64(5), + ]), + "primeFactorcountabs": Uint64(3), + }), + ), + Opid(54): TraceOp( + opid: Opid(54), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(55): TraceOp( + opid: Opid(55), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(56): TraceOp( + opid: Opid(56), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Prime(PrimeNumber(31)))), + ), + Opid(57): TraceOp( + opid: Opid(57), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(31))), + vertices: {}, + )), + ), + Opid(58): TraceOp( + opid: Opid(58), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveCoercion(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(31))), + vertices: {}, + ), false)), + ), + Opid(59): TraceOp( + opid: Opid(59), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(60): TraceOp( + opid: Opid(60), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(61): TraceOp( + opid: Opid(61), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(62): TraceOp( + opid: Opid(62), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(63): TraceOp( + opid: Opid(63), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(64): TraceOp( + opid: Opid(64), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(31), + "min": Int64(28), + }, + ), + 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", + ), + }, + outputs: { + "factors": ContextField(ContextField( + vertex_id: Vid(2), + field_name: "value", + field_type: "Int", + )), + }, + ), + fold_specific_outputs: { + "primeFactorcountabs": TransformedField(TransformedField( + value: TransformedValue( + base: FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), + transforms: [ + Abs, + ], + ), + tid: Tid(2), + field_type: "Int!", + )), + }, + post_filters: [ + Equals(TransformedField(TransformedField( + value: TransformedValue( + base: FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), + transforms: [ + Abs, + ], + ), + tid: Tid(2), + field_type: "Int!", + )), Tag(FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )))), + ], + ), + }, + ), + ), + ), +) diff --git a/trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.graphql-parsed.ron new file mode 100644 index 00000000..5a08db7f --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.graphql-parsed.ron @@ -0,0 +1,147 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(6), + "min": Int64(6), + }, + ), + 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( + tid: Tid(1), + transform: TransformDirective( + kind: Count, + ), + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Abs, + ), + output: [ + OutputDirective(), + ], + )), + )), + )), + ), FieldNode( + position: Pos( + line: 7, + column: 13, + ), + name: "primeFactor", + connections: [ + (FieldConnection( + position: Pos( + line: 8, + column: 17, + ), + name: "value", + alias: Some("factors"), + ), FieldNode( + position: Pos( + line: 8, + column: 17, + ), + name: "value", + alias: Some("factors"), + output: [ + OutputDirective(), + ], + )), + (FieldConnection( + position: Pos( + line: 10, + column: 17, + ), + name: "multiple", + arguments: { + "max": Int64(3), + }, + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + tid: Tid(3), + transform: TransformDirective( + kind: Count, + ), + retransform: Some(TransformGroup( + tid: Tid(4), + transform: TransformDirective( + kind: Abs, + ), + output: [ + OutputDirective(), + ], + )), + )), + )), + ), FieldNode( + position: Pos( + line: 10, + column: 17, + ), + name: "multiple", + connections: [ + (FieldConnection( + position: Pos( + line: 11, + column: 21, + ), + name: "value", + alias: Some("multiples"), + ), FieldNode( + position: Pos( + line: 11, + column: 21, + ), + name: "value", + alias: Some("multiples"), + output: [ + OutputDirective(), + ], + )), + ], + )), + ], + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.graphql.ron b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.graphql.ron new file mode 100644 index 00000000..4e72fe5c --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.graphql.ron @@ -0,0 +1,20 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: 6, max: 6) { + ... on Composite { + value @output + + primeFactor @fold @transform(op: "count") @transform(op: "abs") @output { + factors: value @output + + multiple(max: 3) @fold @transform(op: "count") @transform(op: "abs") @output { + multiples: value @output + } + } + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.ir.ron b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.ir.ron new file mode 100644 index 00000000..8cd4528a --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.ir.ron @@ -0,0 +1,114 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(6), + "min": Int64(6), + }, + ), + 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", + ), + }, + folds: { + Eid(2): IRFold( + eid: Eid(2), + from_vid: Vid(2), + to_vid: Vid(3), + edge_name: "multiple", + parameters: EdgeParameters( + contents: { + "max": Int64(3), + }, + ), + component: IRQueryComponent( + root: Vid(3), + vertices: { + Vid(3): IRVertex( + vid: Vid(3), + type_name: "Composite", + ), + }, + outputs: { + "multiples": ContextField(ContextField( + vertex_id: Vid(3), + field_name: "value", + field_type: "Int", + )), + }, + ), + fold_specific_outputs: { + "multiplecountabs": TransformedField(TransformedField( + value: TransformedValue( + base: FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), + transforms: [ + Abs, + ], + ), + tid: Tid(4), + field_type: "Int!", + )), + }, + ), + }, + outputs: { + "factors": ContextField(ContextField( + vertex_id: Vid(2), + field_name: "value", + field_type: "Int", + )), + }, + ), + fold_specific_outputs: { + "primeFactorcountabs": TransformedField(TransformedField( + value: TransformedValue( + base: FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), + transforms: [ + Abs, + ], + ), + tid: Tid(2), + field_type: "Int!", + )), + }, + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + }, + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.output.ron b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.output.ron new file mode 100644 index 00000000..3a606ab3 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.output.ron @@ -0,0 +1,54 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "factors": Output( + name: "factors", + value_type: "[Int]!", + vid: Vid(2), + ), + "multiplecountabs": Output( + name: "multiplecountabs", + value_type: "[Int!]!", + vid: Vid(3), + ), + "multiples": Output( + name: "multiples", + value_type: "[[Int]!]!", + vid: Vid(3), + ), + "primeFactorcountabs": Output( + name: "primeFactorcountabs", + value_type: "Int!", + vid: Vid(2), + ), + "value": Output( + name: "value", + value_type: "Int", + vid: Vid(1), + ), + }, + results: [ + { + "factors": List([ + Int64(2), + Int64(3), + ]), + "multiplecountabs": List([ + Uint64(2), + Uint64(2), + ]), + "multiples": List([ + List([ + Int64(4), + Int64(6), + ]), + List([ + Int64(6), + Int64(9), + ]), + ]), + "primeFactorcountabs": Uint64(2), + "value": Int64(6), + }, + ], +) diff --git a/trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.trace.ron b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.trace.ron new file mode 100644 index 00000000..48e2c7c9 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/nested_fold_count_transform_output.trace.ron @@ -0,0 +1,1036 @@ +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(6, [ + 2, + 3, + ])))), + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: {}, + )), + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveCoercion(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: {}, + ), true)), + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + }, + )), + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + }, + ))), + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: None, + content: Call(ResolveNeighbors(Vid(2), "Prime", Eid(2))), + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(13)), + content: AdvanceInputIterator, + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(12)), + content: YieldFrom(ResolveNeighborsInner(0, Prime(PrimeNumber(2)))), + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(13)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + )), + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(13)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + ))), + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(17)), + content: YieldFrom(ResolveNeighborsInner(0, Composite(CompositeNumber(4, [ + 2, + ])))), + ), + Opid(19): TraceOp( + opid: Opid(19), + parent_opid: Some(Opid(17)), + content: YieldFrom(ResolveNeighborsInner(1, Composite(CompositeNumber(6, [ + 2, + 3, + ])))), + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: Some(Opid(17)), + content: OutputIteratorExhausted, + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: None, + content: Call(ResolveProperty(Vid(3), "Composite", "value")), + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: Some(Opid(21)), + content: AdvanceInputIterator, + ), + Opid(23): TraceOp( + opid: Opid(23), + parent_opid: Some(Opid(21)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(4, [ + 2, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(4, [ + 2, + ]))), + }, + )), + ), + Opid(24): TraceOp( + opid: Opid(24), + parent_opid: Some(Opid(21)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(4, [ + 2, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(4, [ + 2, + ]))), + }, + ), Int64(4))), + ), + Opid(25): TraceOp( + opid: Opid(25), + parent_opid: Some(Opid(21)), + content: AdvanceInputIterator, + ), + Opid(26): TraceOp( + opid: Opid(26), + parent_opid: Some(Opid(21)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + }, + )), + ), + Opid(27): TraceOp( + opid: Opid(27), + parent_opid: Some(Opid(21)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + }, + ), Int64(6))), + ), + Opid(28): TraceOp( + opid: Opid(28), + parent_opid: Some(Opid(21)), + content: AdvanceInputIterator, + ), + Opid(29): TraceOp( + opid: Opid(29), + parent_opid: Some(Opid(21)), + content: InputIteratorExhausted, + ), + Opid(30): TraceOp( + opid: Opid(30), + parent_opid: Some(Opid(21)), + content: OutputIteratorExhausted, + ), + Opid(31): TraceOp( + opid: Opid(31), + parent_opid: Some(Opid(13)), + content: AdvanceInputIterator, + ), + Opid(32): TraceOp( + opid: Opid(32), + parent_opid: Some(Opid(12)), + content: YieldFrom(ResolveNeighborsInner(1, Prime(PrimeNumber(3)))), + ), + Opid(33): TraceOp( + opid: Opid(33), + parent_opid: Some(Opid(13)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(3))), + }, + )), + ), + Opid(34): TraceOp( + opid: Opid(34), + parent_opid: Some(Opid(13)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(3))), + }, + ))), + ), + Opid(35): TraceOp( + opid: Opid(35), + parent_opid: Some(Opid(34)), + content: YieldFrom(ResolveNeighborsInner(0, Composite(CompositeNumber(6, [ + 2, + 3, + ])))), + ), + Opid(36): TraceOp( + opid: Opid(36), + parent_opid: Some(Opid(34)), + content: YieldFrom(ResolveNeighborsInner(1, Composite(CompositeNumber(9, [ + 3, + ])))), + ), + Opid(37): TraceOp( + opid: Opid(37), + parent_opid: Some(Opid(34)), + content: OutputIteratorExhausted, + ), + Opid(38): TraceOp( + opid: Opid(38), + parent_opid: None, + content: Call(ResolveProperty(Vid(3), "Composite", "value")), + ), + Opid(39): TraceOp( + opid: Opid(39), + parent_opid: Some(Opid(38)), + content: AdvanceInputIterator, + ), + Opid(40): TraceOp( + opid: Opid(40), + parent_opid: Some(Opid(38)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + }, + )), + ), + Opid(41): TraceOp( + opid: Opid(41), + parent_opid: Some(Opid(38)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + }, + ), Int64(6))), + ), + Opid(42): TraceOp( + opid: Opid(42), + parent_opid: Some(Opid(38)), + content: AdvanceInputIterator, + ), + Opid(43): TraceOp( + opid: Opid(43), + parent_opid: Some(Opid(38)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(9, [ + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(9, [ + 3, + ]))), + }, + )), + ), + Opid(44): TraceOp( + opid: Opid(44), + parent_opid: Some(Opid(38)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(9, [ + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(9, [ + 3, + ]))), + }, + ), Int64(9))), + ), + Opid(45): TraceOp( + opid: Opid(45), + parent_opid: Some(Opid(38)), + content: AdvanceInputIterator, + ), + Opid(46): TraceOp( + opid: Opid(46), + parent_opid: Some(Opid(38)), + content: InputIteratorExhausted, + ), + Opid(47): TraceOp( + opid: Opid(47), + parent_opid: Some(Opid(38)), + content: OutputIteratorExhausted, + ), + Opid(48): TraceOp( + opid: Opid(48), + parent_opid: Some(Opid(13)), + content: AdvanceInputIterator, + ), + Opid(49): TraceOp( + opid: Opid(49), + parent_opid: Some(Opid(12)), + content: OutputIteratorExhausted, + ), + Opid(50): TraceOp( + opid: Opid(50), + parent_opid: Some(Opid(13)), + content: InputIteratorExhausted, + ), + Opid(51): TraceOp( + opid: Opid(51), + parent_opid: Some(Opid(13)), + content: OutputIteratorExhausted, + ), + Opid(52): TraceOp( + opid: Opid(52), + parent_opid: None, + content: Call(ResolveProperty(Vid(2), "Prime", "value")), + ), + Opid(53): TraceOp( + opid: Opid(53), + parent_opid: Some(Opid(52)), + content: AdvanceInputIterator, + ), + Opid(54): TraceOp( + opid: Opid(54), + parent_opid: Some(Opid(52)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + folded_contexts: { + Eid(2): Some([ + SerializableContext( + active_vertex: Some(Composite(CompositeNumber(4, [ + 2, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(4, [ + 2, + ]))), + }, + ), + SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + }, + ), + ]), + }, + folded_values: { + (Eid(2), "multiplecountabs"): Some(Value(Uint64(2))), + (Eid(2), "multiples"): Some(Vec([ + Value(Int64(4)), + Value(Int64(6)), + ])), + }, + )), + ), + Opid(55): TraceOp( + opid: Opid(55), + parent_opid: Some(Opid(52)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + folded_contexts: { + Eid(2): Some([ + SerializableContext( + active_vertex: Some(Composite(CompositeNumber(4, [ + 2, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(4, [ + 2, + ]))), + }, + ), + SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + }, + ), + ]), + }, + folded_values: { + (Eid(2), "multiplecountabs"): Some(Value(Uint64(2))), + (Eid(2), "multiples"): Some(Vec([ + Value(Int64(4)), + Value(Int64(6)), + ])), + }, + ), Int64(2))), + ), + Opid(56): TraceOp( + opid: Opid(56), + parent_opid: Some(Opid(52)), + content: AdvanceInputIterator, + ), + Opid(57): TraceOp( + opid: Opid(57), + parent_opid: Some(Opid(52)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(3))), + }, + folded_contexts: { + Eid(2): Some([ + SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + }, + ), + SerializableContext( + active_vertex: Some(Composite(CompositeNumber(9, [ + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(9, [ + 3, + ]))), + }, + ), + ]), + }, + folded_values: { + (Eid(2), "multiplecountabs"): Some(Value(Uint64(2))), + (Eid(2), "multiples"): Some(Vec([ + Value(Int64(6)), + Value(Int64(9)), + ])), + }, + )), + ), + Opid(58): TraceOp( + opid: Opid(58), + parent_opid: Some(Opid(52)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(3))), + }, + folded_contexts: { + Eid(2): Some([ + SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + }, + ), + SerializableContext( + active_vertex: Some(Composite(CompositeNumber(9, [ + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(9, [ + 3, + ]))), + }, + ), + ]), + }, + folded_values: { + (Eid(2), "multiplecountabs"): Some(Value(Uint64(2))), + (Eid(2), "multiples"): Some(Vec([ + Value(Int64(6)), + Value(Int64(9)), + ])), + }, + ), Int64(3))), + ), + Opid(59): TraceOp( + opid: Opid(59), + parent_opid: Some(Opid(52)), + content: AdvanceInputIterator, + ), + Opid(60): TraceOp( + opid: Opid(60), + parent_opid: Some(Opid(52)), + content: InputIteratorExhausted, + ), + Opid(61): TraceOp( + opid: Opid(61), + parent_opid: Some(Opid(52)), + content: OutputIteratorExhausted, + ), + Opid(62): TraceOp( + opid: Opid(62), + parent_opid: Some(Opid(4)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + }, + folded_contexts: { + Eid(1): Some([ + SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + folded_contexts: { + Eid(2): Some([ + SerializableContext( + active_vertex: Some(Composite(CompositeNumber(4, [ + 2, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(4, [ + 2, + ]))), + }, + ), + SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + }, + ), + ]), + }, + folded_values: { + (Eid(2), "multiplecountabs"): Some(Value(Uint64(2))), + (Eid(2), "multiples"): Some(Vec([ + Value(Int64(4)), + Value(Int64(6)), + ])), + }, + ), + SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(3))), + }, + folded_contexts: { + Eid(2): Some([ + SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + }, + ), + SerializableContext( + active_vertex: Some(Composite(CompositeNumber(9, [ + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(9, [ + 3, + ]))), + }, + ), + ]), + }, + folded_values: { + (Eid(2), "multiplecountabs"): Some(Value(Uint64(2))), + (Eid(2), "multiples"): Some(Vec([ + Value(Int64(6)), + Value(Int64(9)), + ])), + }, + ), + ]), + }, + folded_values: { + (Eid(1), "factors"): Some(Vec([ + Value(Int64(2)), + Value(Int64(3)), + ])), + (Eid(1), "primeFactorcountabs"): Some(Value(Uint64(2))), + (Eid(2), "multiplecountabs"): Some(Vec([ + Value(Uint64(2)), + Value(Uint64(2)), + ])), + (Eid(2), "multiples"): Some(Vec([ + Vec([ + Value(Int64(4)), + Value(Int64(6)), + ]), + Vec([ + Value(Int64(6)), + Value(Int64(9)), + ]), + ])), + }, + )), + ), + Opid(63): TraceOp( + opid: Opid(63), + parent_opid: Some(Opid(4)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + }, + folded_contexts: { + Eid(1): Some([ + SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(2))), + }, + folded_contexts: { + Eid(2): Some([ + SerializableContext( + active_vertex: Some(Composite(CompositeNumber(4, [ + 2, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(4, [ + 2, + ]))), + }, + ), + SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + }, + ), + ]), + }, + folded_values: { + (Eid(2), "multiplecountabs"): Some(Value(Uint64(2))), + (Eid(2), "multiples"): Some(Vec([ + Value(Int64(4)), + Value(Int64(6)), + ])), + }, + ), + SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: { + Vid(2): Some(Prime(PrimeNumber(3))), + }, + folded_contexts: { + Eid(2): Some([ + SerializableContext( + active_vertex: Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(6, [ + 2, + 3, + ]))), + }, + ), + SerializableContext( + active_vertex: Some(Composite(CompositeNumber(9, [ + 3, + ]))), + vertices: { + Vid(3): Some(Composite(CompositeNumber(9, [ + 3, + ]))), + }, + ), + ]), + }, + folded_values: { + (Eid(2), "multiplecountabs"): Some(Value(Uint64(2))), + (Eid(2), "multiples"): Some(Vec([ + Value(Int64(6)), + Value(Int64(9)), + ])), + }, + ), + ]), + }, + folded_values: { + (Eid(1), "factors"): Some(Vec([ + Value(Int64(2)), + Value(Int64(3)), + ])), + (Eid(1), "primeFactorcountabs"): Some(Value(Uint64(2))), + (Eid(2), "multiplecountabs"): Some(Vec([ + Value(Uint64(2)), + Value(Uint64(2)), + ])), + (Eid(2), "multiples"): Some(Vec([ + Vec([ + Value(Int64(4)), + Value(Int64(6)), + ]), + Vec([ + Value(Int64(6)), + Value(Int64(9)), + ]), + ])), + }, + ), Int64(6))), + ), + Opid(64): TraceOp( + opid: Opid(64), + parent_opid: None, + content: ProduceQueryResult({ + "factors": List([ + Int64(2), + Int64(3), + ]), + "multiplecountabs": List([ + Uint64(2), + Uint64(2), + ]), + "multiples": List([ + List([ + Int64(4), + Int64(6), + ]), + List([ + Int64(6), + Int64(9), + ]), + ]), + "primeFactorcountabs": Uint64(2), + "value": Int64(6), + }), + ), + Opid(65): TraceOp( + opid: Opid(65), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(66): TraceOp( + opid: Opid(66), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(67): TraceOp( + opid: Opid(67), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(68): TraceOp( + opid: Opid(68), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(69): TraceOp( + opid: Opid(69), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(70): TraceOp( + opid: Opid(70), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(71): TraceOp( + opid: Opid(71), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(72): TraceOp( + opid: Opid(72), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + Opid(73): TraceOp( + opid: Opid(73), + parent_opid: Some(Opid(4)), + content: InputIteratorExhausted, + ), + Opid(74): TraceOp( + opid: Opid(74), + parent_opid: Some(Opid(4)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(6), + "min": Int64(6), + }, + ), + 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", + ), + }, + folds: { + Eid(2): IRFold( + eid: Eid(2), + from_vid: Vid(2), + to_vid: Vid(3), + edge_name: "multiple", + parameters: EdgeParameters( + contents: { + "max": Int64(3), + }, + ), + component: IRQueryComponent( + root: Vid(3), + vertices: { + Vid(3): IRVertex( + vid: Vid(3), + type_name: "Composite", + ), + }, + outputs: { + "multiples": ContextField(ContextField( + vertex_id: Vid(3), + field_name: "value", + field_type: "Int", + )), + }, + ), + fold_specific_outputs: { + "multiplecountabs": TransformedField(TransformedField( + value: TransformedValue( + base: FoldSpecificField(FoldSpecificField( + fold_eid: Eid(2), + fold_root_vid: Vid(3), + kind: Count, + )), + transforms: [ + Abs, + ], + ), + tid: Tid(4), + field_type: "Int!", + )), + }, + ), + }, + outputs: { + "factors": ContextField(ContextField( + vertex_id: Vid(2), + field_name: "value", + field_type: "Int", + )), + }, + ), + fold_specific_outputs: { + "primeFactorcountabs": TransformedField(TransformedField( + value: TransformedValue( + base: FoldSpecificField(FoldSpecificField( + fold_eid: Eid(1), + fold_root_vid: Vid(2), + kind: Count, + )), + transforms: [ + Abs, + ], + ), + tid: Tid(2), + field_type: "Int!", + )), + }, + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + }, + ), + ), + ), +) From b3aa2190d83dbb9f4dd661fe0c7dd33251792b43 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Tue, 25 Jun 2024 15:45:47 +0000 Subject: [PATCH 20/30] Return appropriate parser/frontend errors for invalid transforms. --- trustfall_core/src/frontend/error.rs | 65 ++- trustfall_core/src/frontend/mod.rs | 433 ++++++++++++------ .../src/graphql_query/directives.rs | 257 ++++++++--- trustfall_core/src/graphql_query/error.rs | 13 +- trustfall_core/src/interpreter/execution.rs | 2 +- .../src/interpreter/helpers/tests.rs | 2 +- trustfall_core/src/schema/mod.rs | 2 +- .../test_data/schemas/filesystem.graphql | 2 +- .../test_data/schemas/nullables.graphql | 2 +- .../test_data/schemas/numbers.graphql | 2 +- .../schemas/parameterized_edges.graphql | 2 +- .../test_data/schemas/recurses.graphql | 2 +- ...nsform_of_same_property.frontend-error.ron | 1 + ...nsform_of_same_property.graphql-parsed.ron | 48 ++ ..._on_transform_of_same_property.graphql.ron | 15 + ...dd_with_nonexistent_tag.frontend-error.ron | 1 + ...dd_with_nonexistent_tag.graphql-parsed.ron | 43 ++ ...sform_add_with_nonexistent_tag.graphql.ron | 11 + ...tag_wrong_argument_type.frontend-error.ron | 1 + ...tag_wrong_argument_type.graphql-parsed.ron | 59 +++ ...d_with_tag_wrong_argument_type.graphql.ron | 14 + ...appropriate_type_op_abs.frontend-error.ron | 1 + ...appropriate_type_op_abs.graphql-parsed.ron | 43 ++ ...d_on_inappropriate_type_op_abs.graphql.ron | 12 + ...appropriate_type_op_add.frontend-error.ron | 1 + ...appropriate_type_op_add.graphql-parsed.ron | 46 ++ ...d_on_inappropriate_type_op_add.graphql.ron | 14 + ...appropriate_type_op_len.frontend-error.ron | 1 + ...appropriate_type_op_len.graphql-parsed.ron | 43 ++ ...d_on_inappropriate_type_op_len.graphql.ron | 12 + ...nvalid_transform_argument_name.graphql.ron | 11 + ...id_transform_argument_name.parse-error.ron | 4 + ...rm_lacking_argument_for_add_op.graphql.ron | 11 + ...acking_argument_for_add_op.parse-error.ron | 4 + .../invalid_transform_op.graphql.ron | 10 + .../invalid_transform_op.parse-error.ron | 4 + ..._supplying_argument_for_abs_op.graphql.ron | 13 + ...plying_argument_for_abs_op.parse-error.ron | 4 + ..._supplying_argument_for_len_op.graphql.ron | 13 + ...plying_argument_for_len_op.parse-error.ron | 4 + ..._multiple_arguments_for_add_op.graphql.ron | 14 + ...tiple_arguments_for_add_op.parse-error.ron | 4 + .../invalid_transform_value_type.graphql.ron | 10 + ...valid_transform_value_type.parse-error.ron | 4 + ..._of_array_with_variable_string.graphql.ron | 17 + ...array_with_variable_string.parse-error.ron | 4 + .../ambiguous_field_origin.graphql | 2 +- .../circular_implements_relationship.graphql | 2 +- .../completely_different_field_type.graphql | 2 +- .../edge_to_root_query_type.graphql | 2 +- ...efault_for_non_nullable_edge_param.graphql | 2 +- .../implementing_non_existent_type.graphql | 2 +- .../implementing_object_type.graphql | 2 +- .../implementing_self_type.graphql | 2 +- ..._default_value_type_for_edge_param.graphql | 2 +- .../interface_with_same_field_twice.graphql | 4 + .../schema_errors/list_of_list_edge.graphql | 2 +- .../list_of_list_root_edge.graphql | 2 +- ...ng_field_and_illegal_type_widening.graphql | 2 +- .../missing_required_field.graphql | 2 +- ..._type_one_interface_both_same_name.graphql | 4 + ...field_missing_parameter_in_subtype.graphql | 2 +- .../parameters_in_property_field.graphql | 2 +- .../property_on_root_query_type.graphql | 2 +- .../reserved_prefix_edge_defined.graphql | 2 +- .../reserved_prefix_property_defined.graphql | 2 +- .../reserved_prefix_query_type.graphql | 2 +- .../reserved_prefix_top_level_field.graphql | 2 +- .../reserved_prefix_type_name.graphql | 2 +- .../root_edge_to_root_query_type.graphql | 2 +- .../subtype_adds_parameter_to_field.graphql | 2 +- ...ansitive_interface_not_implemented.graphql | 2 +- .../schema_errors/triple_list_edge.graphql | 2 +- .../triple_list_root_edge.graphql | 2 +- .../two_interface_same_name.graphql | 4 + .../schema_errors/two_types_same_name.graphql | 4 + .../type_narrowing_parameterized_edge.graphql | 2 +- ...wing_parameterized_edge_list_param.graphql | 2 +- .../type_widening_edge_supertype.graphql | 2 +- .../type_widening_list_edge_supertype.graphql | 2 +- ...widening_nullability_on_edge_field.graphql | 2 +- ...ing_nullability_on_list_edge_field.graphql | 2 +- ..._widening_of_inherited_list_fields.graphql | 2 +- ...widening_of_inherited_scalar_field.graphql | 2 +- .../type_with_same_field_twice.graphql | 4 + .../deeply_nested_list_property_field.graphql | 2 +- .../type_narrowing_edge_nullability.graphql | 2 +- .../type_narrowing_edge_subtype.graphql | 2 +- ...pe_narrowing_list_edge_nullability.graphql | 2 +- .../type_narrowing_list_edge_subtype.graphql | 2 +- ...type_narrowing_of_inherited_fields.graphql | 2 +- ...ning_parameterized_field_parameter.graphql | 2 +- 92 files changed, 1123 insertions(+), 260 deletions(-) create mode 100644 trustfall_core/test_data/tests/frontend_errors/tag_used_before_defining_on_transform_of_same_property.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/tag_used_before_defining_on_transform_of_same_property.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/tag_used_before_defining_on_transform_of_same_property.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_add_with_nonexistent_tag.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_add_with_nonexistent_tag.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_add_with_nonexistent_tag.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_add_with_tag_wrong_argument_type.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_add_with_tag_wrong_argument_type.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_add_with_tag_wrong_argument_type.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_abs.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_abs.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_abs.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_add.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_add.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_add.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_len.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_len.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_len.graphql.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/invalid_transform_argument_name.graphql.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/invalid_transform_argument_name.parse-error.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/invalid_transform_lacking_argument_for_add_op.graphql.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/invalid_transform_lacking_argument_for_add_op.parse-error.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/invalid_transform_op.graphql.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/invalid_transform_op.parse-error.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_argument_for_abs_op.graphql.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_argument_for_abs_op.parse-error.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_argument_for_len_op.graphql.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_argument_for_len_op.parse-error.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_multiple_arguments_for_add_op.graphql.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_multiple_arguments_for_add_op.parse-error.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/invalid_transform_value_type.graphql.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/invalid_transform_value_type.parse-error.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/transform_with_variable_string_instead_of_array_with_variable_string.graphql.ron create mode 100644 trustfall_core/test_data/tests/parse_errors/transform_with_variable_string_instead_of_array_with_variable_string.parse-error.ron diff --git a/trustfall_core/src/frontend/error.rs b/trustfall_core/src/frontend/error.rs index 41b37e1d..cd87e951 100644 --- a/trustfall_core/src/frontend/error.rs +++ b/trustfall_core/src/frontend/error.rs @@ -5,8 +5,8 @@ use serde::{Deserialize, Serialize}; use crate::{ graphql_query::directives::{TransformDirective, TransformOp}, ir::{ - Argument, FieldValue, FoldSpecificField, OperationSubject, Transform, TransformBase, - TransformedField, Type, + Argument, FieldValue, FoldSpecificField, FoldSpecificFieldKind, OperationSubject, + Transform, TransformBase, TransformedField, Type, }, util::DisplayVec, }; @@ -23,8 +23,11 @@ pub enum FrontendError { #[error("Filter on {0} uses undefined tag: %{1}")] UndefinedTagInFilter(String, String), + #[error("Transform on {0} uses undefined tag: %{1}")] + UndefinedTagInTransform(String, String), + #[error( - "Filter on {0} uses tag \"{1}\" which is not yet defined at that point \ + "An operation on {0} uses tag \"{1}\" which is not yet defined at that point \ in the query. Please reorder the query components so that the @tag directive \ comes before all uses of its tagged value." )] @@ -127,12 +130,22 @@ pub enum FrontendError { } impl FrontendError { + #[inline] + pub(super) fn represent_property(property_name: &str) -> String { + format!("property \"{property_name}\"") + } + + #[inline] + pub(super) fn represent_fold_specific_field(kind: &FoldSpecificFieldKind) -> String { + format!("transformed field \"{}\"", kind.field_name()) + } + #[inline] fn represent_subject(subject: &OperationSubject) -> String { match subject { OperationSubject::LocalField(field) => { let property_name = &field.field_name; - format!("property \"{property_name}\"") + Self::represent_property(property_name) } OperationSubject::TransformedField(field) => { let mut buf = String::with_capacity(32); @@ -140,8 +153,7 @@ impl FrontendError { buf } OperationSubject::FoldSpecificField(field) => { - let field_name = field.kind.field_name(); - format!("transformed field \"{field_name}\"") + Self::represent_fold_specific_field(&field.kind) } } } @@ -402,6 +414,16 @@ pub enum TransformTypeError { #[error("Found a @transform directive applied to edge \"{0}\" which is not marked @fold, and therefore cannot be transformed.{1}")] CannotTransformEdgeWithoutFold(String, String), + + #[error( + "Transform operation \"{0}\" can only be applied on {1}, but was used on {2} of incompatible type \"{3}\"." + )] + TypeMismatchBetweenTransformOperationAndSubject(String, String, String, String), + + #[error( + "Transform operation \"{0}\" requires an argument of type {1}, but was used with an argument of incompatible type \"{2}\"." + )] + TypeMismatchBetweenTransformOperationAndArgument(String, String, String), } impl TransformTypeError { @@ -412,7 +434,7 @@ impl TransformTypeError { type_so_far: &Type, ) -> Self { let base_name = if transforms_so_far.is_empty() { - format!("property \"{property_name}\"") + FrontendError::represent_property(property_name) } else { let mut buf = String::with_capacity(16); write_name_of_transformed_field_by_parts(&mut buf, property_name, transforms_so_far); @@ -483,6 +505,33 @@ impl TransformTypeError { pub(crate) fn duplicated_count_transform_on_folded_edge(edge_name: &str) -> Self { Self::DuplicatedCountTransformOnEdge(edge_name.to_string()) } + + pub(crate) fn operation_requires_list_type_subject(op: &str, subject_representation: String, subject_type: &Type) -> Self { + Self::TypeMismatchBetweenTransformOperationAndSubject( + op.to_string(), + "list-typed values".to_string(), + subject_representation, + subject_type.to_string(), + ) + } + + pub(crate) fn operation_requires_different_type_subject(op: &str, required_type: &Type, subject_representation: String, subject_type: &Type) -> Self { + Self::TypeMismatchBetweenTransformOperationAndSubject( + op.to_string(), + format!("values of type \"{required_type}\""), + subject_representation, + subject_type.to_string(), + ) + } + + pub(crate) fn operation_requires_different_choice_of_type_subject(op: &str, required_type_a: &Type, required_type_b: &Type, subject_representation: String, subject_type: &Type) -> Self { + Self::TypeMismatchBetweenTransformOperationAndSubject( + op.to_string(), + format!("values of type \"{required_type_a}\" or \"{required_type_b}\""), + subject_representation, + subject_type.to_string(), + ) + } } fn write_name_of_transformed_field(buf: &mut String, field: &TransformedField) { @@ -494,7 +543,7 @@ fn write_name_of_transformed_field(buf: &mut String, field: &TransformedField) { write_name_of_transformed_field_by_parts(buf, base_name, &field.value.transforms); } -fn write_name_of_transformed_field_by_parts( +pub(super) fn write_name_of_transformed_field_by_parts( buf: &mut String, base_name: &str, transforms: &[Transform], diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index dd65338f..e68805c3 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -6,8 +6,6 @@ use async_graphql_parser::{ types::{ExecutableDocument, FieldDefinition, TypeDefinition, TypeKind}, Positioned, }; -use error::TransformTypeError; -use filters::make_filter_expr; use smallvec::SmallVec; use crate::{ @@ -22,16 +20,21 @@ use crate::{ get_typename_meta_field, Argument, ContextField, EdgeParameters, Eid, FieldRef, FieldValue, FoldSpecificField, FoldSpecificFieldKind, IREdge, IRFold, IRQuery, IRQueryComponent, IRVertex, IndexedQuery, LocalField, Operation, OperationSubject, Recursive, Tid, Transform, - TransformBase, TransformedField, TransformedValue, Type, Vid, TYPENAME_META_FIELD, + TransformBase, TransformedField, TransformedValue, Type, VariableRef, Vid, + TYPENAME_META_FIELD, }, schema::{get_builtin_scalars, FieldOrigin, Schema}, util::{BTreeMapTryInsertExt, TryCollectUniqueKey}, }; use self::{ - error::{DuplicatedNamesConflict, FilterTypeError, FrontendError, ValidationError}, + error::{ + DuplicatedNamesConflict, FilterTypeError, FrontendError, TransformTypeError, + ValidationError, + }, + filters::make_filter_expr, outputs::OutputHandler, - tags::TagHandler, + tags::{TagHandler, TagLookupError}, util::{get_underlying_named_type, ComponentPath}, validation::validate_query_against_schema, }; @@ -374,6 +377,11 @@ fn fill_in_query_variables( Some(Argument::Variable(vref)) => Some(vref), _ => None, }); + // TODO: include transformed uses of variables here: + // - transform on output property + // - transform on output of fold-specific field + // - transform on filter subject + // - retransform of transformed property for vref in all_variable_uses { let existing_type = variables .entry(vref.variable_name.clone()) @@ -861,36 +869,28 @@ fn make_vertex<'query>( let mut current_type = property_type.clone(); let mut next_transform_group = property_field.transform_group.as_ref(); while let Some(transform_group) = next_transform_group { - let next_transform = match extract_property_like_transform_from_directive( - &transform_group.transform, - property_name, - &transforms, - ¤t_type, - ) { - Ok(t) => t, - Err(e) => { - // This error should already have been reported while initially - // processing transforms for output and tag purposes - // in `fill_in_vertex_data()`. - // We have tests enforcing this. - // - // Ignore it here. - break; - } - }; - current_type = match determine_transformed_field_type(current_type, &next_transform) - { - Ok(t) => t, - Err(e) => { - // This error should already have been reported while initially - // processing transforms for output and tag purposes - // in `fill_in_vertex_data()`. - // We have tests enforcing this. - // - // Ignore it here. - break; - } - }; + let (next_transform, next_type) = + match extract_property_like_transform_from_directive( + tags, + component_path, + vid, + &transform_group.transform, + property_name, + &transforms, + ¤t_type, + ) { + Ok(t) => t, + Err(e) => { + // This error should already have been reported while initially + // processing transforms for output and tag purposes + // in `fill_in_vertex_data()`. + // We have tests enforcing this. + // + // Ignore it here. + break; + } + }; + current_type = next_type; transforms.push(next_transform); for filter_directive in &transform_group.filter { @@ -1234,7 +1234,10 @@ fn fill_in_property_data<'query>( } if let Some(transform_group) = next_transform_group { - let next_transform = match extract_property_like_transform_from_directive( + let (next_transform, next_type) = match extract_property_like_transform_from_directive( + tags, + component_path, + current_vid, &transform_group.transform, &property_node.name, &transforms, @@ -1242,7 +1245,7 @@ fn fill_in_property_data<'query>( ) { Ok(t) => t, Err(e) => { - errors.push(e.into()); + errors.push(e); break; } }; @@ -1251,13 +1254,7 @@ fn fill_in_property_data<'query>( tag_directives = transform_group.tag.as_slice(); next_transform_group = transform_group.retransform.as_deref(); - current_type = match determine_transformed_field_type(current_type, &next_transform) { - Ok(t) => t, - Err(e) => { - errors.push(e); - break; - } - }; + current_type = next_type; transforms.push(next_transform); } else { break; @@ -1303,114 +1300,286 @@ fn make_field_ref_for_possibly_transformed_property( } } +enum TransformErrorSource { + TypeCheck(TransformTypeError), + Tag(TagLookupError), +} + +impl From for TransformErrorSource { + fn from(value: TagLookupError) -> Self { + Self::Tag(value) + } +} + +impl From for TransformErrorSource { + fn from(value: TransformTypeError) -> Self { + Self::TypeCheck(value) + } +} + fn extract_property_like_transform_from_directive( + tags: &mut TagHandler<'_>, + use_path: &ComponentPath, + use_vid: Vid, transform_directive: &TransformDirective, property_name: &str, transforms_so_far: &[Transform], type_so_far: &Type, -) -> Result { - extract_transform_from_directive(transform_directive, type_so_far, || { - TransformTypeError::fold_specific_transform_on_propertylike_value( - transform_directive.kind.op_name(), - property_name, - transforms_so_far, - type_so_far, - ) +) -> Result<(Transform, Type), FrontendError> { + extract_transform_and_next_type_from_directive( + tags, + use_path, + use_vid, + transform_directive, + type_so_far, + || { + if transforms_so_far.is_empty() { + FrontendError::represent_property(property_name) + } else { + let mut buf = String::with_capacity(32); + error::write_name_of_transformed_field_by_parts( + &mut buf, + property_name, + transforms_so_far, + ); + buf + } + }, + || { + TransformTypeError::fold_specific_transform_on_propertylike_value( + transform_directive.kind.op_name(), + property_name, + transforms_so_far, + type_so_far, + ) + }, + ) + .map_err(|e| match e { + TransformErrorSource::TypeCheck(type_err) => type_err.into(), + TransformErrorSource::Tag(tag_err) => { + let subject = if transforms_so_far.is_empty() { + FrontendError::represent_property(property_name) + } else { + let mut buf = String::with_capacity(32); + error::write_name_of_transformed_field_by_parts( + &mut buf, + property_name, + transforms_so_far, + ); + buf + }; + + match tag_err { + TagLookupError::UndefinedTag(tag_name) => { + FrontendError::UndefinedTagInTransform(subject, tag_name) + } + TagLookupError::TagUsedBeforeDefinition(tag_name) => { + FrontendError::TagUsedBeforeDefinition(subject, tag_name) + } + TagLookupError::TagDefinedInsideFold(tag_name) => { + FrontendError::TagUsedOutsideItsFoldedSubquery(subject, tag_name) + } + } + } }) } fn extract_transform_on_fold_count_from_directive( + tags: &mut TagHandler<'_>, + use_path: &ComponentPath, + use_vid: Vid, transform_directive: &TransformDirective, edge_name: &str, + transforms_so_far: &[Transform], type_so_far: &Type, -) -> Result { - extract_transform_from_directive(transform_directive, type_so_far, || { - // A fold-specific filter is used *after* a fold-count value has already been created. - // For example: `some_edge @fold @transform(op: "count") @transform(op: "count")` - // ^^^^^^^^^^^^^^^^^^^^^^^ - // we are here, this is the error - TransformTypeError::duplicated_count_transform_on_folded_edge(edge_name) +) -> Result<(Transform, Type), FrontendError> { + extract_transform_and_next_type_from_directive( + tags, + use_path, + use_vid, + transform_directive, + type_so_far, + || { + if transforms_so_far.is_empty() { + FrontendError::represent_fold_specific_field(&FoldSpecificFieldKind::Count) + } else { + let mut buf = String::with_capacity(32); + error::write_name_of_transformed_field_by_parts( + &mut buf, + FoldSpecificFieldKind::Count.field_name(), + transforms_so_far, + ); + buf + } + }, + || { + // A fold-specific filter is used *after* a fold-count value has already been created. + // For example: `some_edge @fold @transform(op: "count") @transform(op: "count")` + // ^^^^^^^^^^^^^^^^^^^^^^^ + // we are here, this is the error + TransformTypeError::duplicated_count_transform_on_folded_edge(edge_name) + }, + ) + .map_err(|e| match e { + TransformErrorSource::TypeCheck(type_err) => type_err.into(), + TransformErrorSource::Tag(tag_err) => { + let subject = if transforms_so_far.is_empty() { + FrontendError::represent_fold_specific_field(&FoldSpecificFieldKind::Count) + } else { + let mut buf = String::with_capacity(32); + error::write_name_of_transformed_field_by_parts( + &mut buf, + FoldSpecificFieldKind::Count.field_name(), + transforms_so_far, + ); + buf + }; + + match tag_err { + TagLookupError::UndefinedTag(tag_name) => { + FrontendError::UndefinedTagInTransform(subject, tag_name) + } + TagLookupError::TagUsedBeforeDefinition(tag_name) => { + FrontendError::TagUsedBeforeDefinition(subject, tag_name) + } + TagLookupError::TagDefinedInsideFold(tag_name) => { + FrontendError::TagUsedOutsideItsFoldedSubquery(subject, tag_name) + } + } + } }) } -fn extract_transform_from_directive( +fn extract_transform_and_next_type_from_directive( + tags: &mut TagHandler<'_>, + use_path: &ComponentPath, + use_vid: Vid, transform_directive: &TransformDirective, type_so_far: &Type, - err_func: impl FnOnce() -> TransformTypeError, -) -> Result { + represent_subject: impl FnOnce() -> String, + invalid_count_op: impl FnOnce() -> TransformTypeError, +) -> Result<(Transform, Type), TransformErrorSource> { match &transform_directive.kind { - TransformOp::Len => Ok(Transform::Len), - TransformOp::Abs => Ok(Transform::Abs), - TransformOp::Add(arg) => Ok(Transform::Add(resolve_transform_argument(arg)?)), - TransformOp::AddF(arg) => Ok(Transform::AddF(resolve_transform_argument(arg)?)), - TransformOp::Count => Err(err_func()), - } -} - -fn resolve_transform_argument(arg: &OperatorArgument) -> Result { - todo!() -} - -fn determine_transformed_field_type( - initial_type: Type, - next_transform: &Transform, -) -> Result { - // TODO: refactor type-checking errors into helpers + three categories: - // - inappropriate left-hand type for transform - // - inappropriate tag type for transform - // - inappropriate (conflicting) variable type for transform; - // for this last one, ensure inferring `Int` vs `Int!` narrows to `Int!` correctly - // but other inference mismatches get reported as errors just like from filters - match next_transform { - Transform::Len => { - if initial_type.is_list() { - Ok(Type::new_named_type("Int", initial_type.nullable())) + TransformOp::Len => { + if type_so_far.is_list() { + Ok((Transform::Len, Type::new_named_type("Int", type_so_far.nullable()))) } else { - Err(todo!()) + Err(TransformTypeError::operation_requires_list_type_subject( + transform_directive.kind.op_name(), + represent_subject(), + type_so_far, + ) + .into()) } } - Transform::Abs => { - let base_type = initial_type.base_type(); + TransformOp::Abs => { + let base_type = type_so_far.base_type(); if base_type == "Int" || base_type == "Float" { - Ok(initial_type) + Ok((Transform::Abs, Type::new_named_type("Int", type_so_far.nullable()))) } else { - Err(todo!()) + Err(TransformTypeError::operation_requires_different_choice_of_type_subject( + transform_directive.kind.op_name(), + &Type::new_named_type("Int", true), + &Type::new_named_type("Float", true), + represent_subject(), + type_so_far, + ) + .into()) } } - Transform::Add(op) => { - match op { - Argument::Tag(tag) => { - let op_type = tag.field_type(); - if op_type.base_type() != "Int" { - return Err(todo!()); - } - } - Argument::Variable(var) => { - let op_type = &var.variable_type; - if op_type.base_type() != "Int" { - return Err(todo!()); - } - } - }; - Ok(Type::new_named_type("Int", initial_type.nullable())) + TransformOp::Add(arg) => { + let base_type = type_so_far.base_type(); + if base_type != "Int" { + return Err(TransformTypeError::operation_requires_different_type_subject( + transform_directive.kind.op_name(), + &Type::new_named_type("Int", true), + represent_subject(), + type_so_far, + ) + .into()); + } + + let required_type = Type::new_named_type("Int", true); + let transform = Transform::Add(resolve_transform_argument( + tags, + use_path, + use_vid, + arg, + required_type, + true, + &transform_directive.kind, + )?); + + let next_type = Type::new_named_type("Int", type_so_far.nullable()); + Ok((transform, next_type)) } - Transform::AddF(op) => { - match op { - Argument::Tag(tag) => { - let op_type = tag.field_type(); - if op_type.base_type() != "Float" { - return Err(todo!()); - } - } - Argument::Variable(var) => { - let op_type = &var.variable_type; - if op_type.base_type() != "Float" { - return Err(todo!()); - } - } + TransformOp::AddF(arg) => { + let base_type = type_so_far.base_type(); + if base_type != "Float" { + return Err(TransformTypeError::operation_requires_different_type_subject( + transform_directive.kind.op_name(), + &Type::new_named_type("Float", true), + represent_subject(), + type_so_far, + ) + .into()); + } + + let required_type = Type::new_named_type("Float", true); + let transform = Transform::AddF(resolve_transform_argument( + tags, + use_path, + use_vid, + arg, + required_type, + true, + &transform_directive.kind, + )?); + + let next_type = Type::new_named_type("Int", type_so_far.nullable()); + Ok((transform, next_type)) + } + TransformOp::Count => Err(invalid_count_op().into()), + } +} + +fn resolve_transform_argument( + tags: &mut TagHandler<'_>, + use_path: &ComponentPath, + use_vid: Vid, + arg: &OperatorArgument, + required_type: Type, + require_non_null_in_variables: bool, + transform_op: &TransformOp, +) -> Result { + match arg { + OperatorArgument::VariableRef(var_name) => { + let variable_type = if required_type.nullable() && require_non_null_in_variables { + required_type.with_nullability(false) + } else { + required_type }; - Ok(Type::new_named_type("Float", initial_type.nullable())) + Ok(Argument::Variable(VariableRef { + variable_name: Arc::clone(var_name), + variable_type, + })) } + OperatorArgument::TagRef(name) => match tags.reference_tag(name, use_path, use_vid) { + Ok(tag_entry) => { + let tag_type = tag_entry.field.field_type(); + if !tag_type.equal_ignoring_nullability(&required_type) { + Err(TransformTypeError::TypeMismatchBetweenTransformOperationAndArgument( + transform_op.op_name().to_string(), + required_type.to_string(), + tag_type.to_string(), + ) + .into()) + } else { + Ok(Argument::Tag(tag_entry.field.to_owned())) + } + } + Err(e) => Err(TransformErrorSource::Tag(e)), + }, } } @@ -1476,25 +1645,23 @@ where next_transform_group = transform_group.retransform.as_deref(); let (field_ref, subject) = if let Some(base_field) = maybe_base_field.as_ref() { - let next_transform = match extract_transform_on_fold_count_from_directive( + let (next_transform, next_type) = match extract_transform_on_fold_count_from_directive( + tags, + component_path, + parent_vid, &transform_group.transform, edge_name.as_ref(), + &subsequent_transforms, ¤t_type, ) { - Ok(t) => t, - Err(e) => { - errors.push(e.into()); - break; - } - }; - - current_type = match determine_transformed_field_type(current_type, &next_transform) { Ok(t) => t, Err(e) => { errors.push(e); break; } }; + + current_type = next_type; subsequent_transforms.push(next_transform); let transformed_field = TransformedField { diff --git a/trustfall_core/src/graphql_query/directives.rs b/trustfall_core/src/graphql_query/directives.rs index ed44896d..7fd61419 100644 --- a/trustfall_core/src/graphql_query/directives.rs +++ b/trustfall_core/src/graphql_query/directives.rs @@ -2,7 +2,7 @@ //! This module contains the logic for parsing Trustfall query directives. use std::{collections::HashSet, num::NonZeroUsize, sync::Arc}; -use async_graphql_parser::{types::Directive, Positioned}; +use async_graphql_parser::{types::Directive, Pos, Positioned}; use async_graphql_value::Value; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; @@ -96,49 +96,31 @@ impl TryFrom<&Positioned> for FilterDirective { value_list .iter() .map(|v| match v { - Value::String(s) => { - let (prefix, name) = if s.starts_with('$') || s.starts_with('%') { - s.split_at(1) - } else { - return Err(ParseError::InvalidFilterOperandName( - s.to_owned(), - format!("Filter argument was expected to start with '$' or '%' but did not: {s}"), - value_argument.pos, - )); - }; - - if name.is_empty() { - return Err(ParseError::InvalidFilterOperandName( - s.to_owned(), - format!("Filter argument is empty after '{}' prefix.", prefix), - value_argument.pos, - )); - } - - let first_char = name.chars().next().unwrap(); - if !first_char.is_ascii_alphabetic() && first_char != '_' { - return Err(ParseError::InvalidFilterOperandName( - s.to_owned(), - format!("Filter argument names must start with an ASCII letter or underscore character: {name}"), - value_argument.pos)) - } - - if name.chars().any(|c| !c.is_ascii_alphanumeric() && c != '_') - { - return Err(ParseError::InvalidFilterOperandName( - s.to_owned(), - format!("Filter argument names must only contain ASCII alphanumerics or underscore characters: {name}"), - value_argument.pos, - )); - } - - if s.starts_with('$') { - Ok(OperatorArgument::VariableRef(name.into())) - } else if s.starts_with('%') { - Ok(OperatorArgument::TagRef(name.into())) - } else { - unreachable!() - } + Value::String(operand) => { + parse_operand(operand).map_err(|e| { + match e { + InvalidOperandError::InvalidPrefix => ParseError::InvalidFilterOperandName( + operand.to_owned(), + format!("Filter argument was expected to start with '$' or '%' but did not: {operand}"), + value_argument.pos, + ), + InvalidOperandError::EmptyName => ParseError::InvalidFilterOperandName( + operand.to_owned(), + format!("Filter argument is empty after '{}' prefix.", operand), + value_argument.pos, + ), + InvalidOperandError::InvalidNameStartChar(name) => ParseError::InvalidFilterOperandName( + operand.to_owned(), + format!("Filter argument names must start with an ASCII letter or underscore character: {name}"), + value_argument.pos + ), + InvalidOperandError::InvalidCharsInName(name) => ParseError::InvalidFilterOperandName( + operand.to_owned(), + format!("Filter argument names must only contain ASCII alphanumerics or underscore characters: {name}"), + value_argument.pos, + ), + } + }) } _ => Err(ParseError::InappropriateTypeForDirectiveArgument( "@filter".to_owned(), @@ -196,6 +178,43 @@ impl TryFrom<&Positioned> for FilterDirective { } } +enum InvalidOperandError<'a> { + InvalidPrefix, + EmptyName, + InvalidNameStartChar(&'a str), + InvalidCharsInName(&'a str), +} + +fn parse_operand(operand: &str) -> Result> { + let (_, name) = if operand.starts_with('$') || operand.starts_with('%') { + operand.split_at(1) + } else { + return Err(InvalidOperandError::InvalidPrefix); + }; + + if name.is_empty() { + return Err(InvalidOperandError::EmptyName); + } + + let first_char = name.chars().next().unwrap(); + if !first_char.is_ascii_alphabetic() && first_char != '_' { + return Err(InvalidOperandError::InvalidNameStartChar(name)) + } + + if name.chars().any(|c| !c.is_ascii_alphanumeric() && c != '_') + { + return Err(InvalidOperandError::InvalidCharsInName(name)); + } + + if operand.starts_with('$') { + Ok(OperatorArgument::VariableRef(name.into())) + } else if operand.starts_with('%') { + Ok(OperatorArgument::TagRef(name.into())) + } else { + unreachable!() + } +} + /// A Trustfall `@output` directive. /// /// For example, the following Trustfall and Rust would be equivalent: @@ -291,60 +310,147 @@ impl TryFrom<&Positioned> for TransformDirective { type Error = ParseError; fn try_from(value: &Positioned) -> Result { - let mut seen_op: bool = false; + let mut seen_op = false; + let mut seen_value = false; for (arg_name, _) in &value.node.arguments { - if arg_name.node.as_ref() == "op" { - if !seen_op { - seen_op = true; - } else { - return Err(ParseError::DuplicatedDirectiveArgument( + let node_name = arg_name.node.as_ref(); + match node_name { + "op" => { + if !seen_op { + seen_op = true; + } else { + return Err(ParseError::DuplicatedDirectiveArgument( + "@transform".to_owned(), + arg_name.node.to_string(), + arg_name.pos, + )); + } + } + "value" => { + if !seen_value { + seen_value = true; + } else { + return Err(ParseError::DuplicatedDirectiveArgument( + "@transform".to_owned(), + arg_name.node.to_string(), + arg_name.pos, + )); + } + } + _ => { + return Err(ParseError::UnrecognizedDirectiveArgument( "@transform".to_owned(), arg_name.node.to_string(), arg_name.pos, )); } - } else { - return Err(ParseError::UnrecognizedDirectiveArgument( - "@transform".to_owned(), - arg_name.node.to_string(), - arg_name.pos, - )); } } - let transform_argument_node = value.node.get_argument("op").ok_or_else(|| { + let transform_op_node = value.node.get_argument("op").ok_or_else(|| { ParseError::MissingRequiredDirectiveArgument( "@transform".to_owned(), "op".to_owned(), value.pos, ) })?; - - let transform_argument: Arc = match &transform_argument_node.node { + let transform_op: Arc = match &transform_op_node.node { Value::String(s) => s.to_owned().into(), _ => { return Err(ParseError::InappropriateTypeForDirectiveArgument( "@transform".to_owned(), "op".to_owned(), - transform_argument_node.pos, + transform_op_node.pos, )) } }; - let kind = match transform_argument.as_ref() { - "count" => TransformOp::Count, - "len" => TransformOp::Len, - "abs" => TransformOp::Abs, + let mut transform_value: SmallVec<[OperatorArgument; 2]> = match value.node.get_argument("value") { + None => SmallVec::new(), + Some(content) => { + match &content.node { + Value::List(value_contents) => { + let mut values = SmallVec::new(); + for v in value_contents { + match v { + Value::String(operand) => { + let operator_argument = parse_operand(operand).map_err(|e| { + match e { + InvalidOperandError::InvalidPrefix => ParseError::InvalidTransformOperandName( + operand.to_owned(), + format!("Transform argument was expected to start with '$' or '%' but did not: {operand}"), + content.pos, + ), + InvalidOperandError::EmptyName => ParseError::InvalidTransformOperandName( + operand.to_owned(), + format!("Transform argument is empty after '{}' prefix.", operand), + content.pos, + ), + InvalidOperandError::InvalidNameStartChar(name) => ParseError::InvalidTransformOperandName( + operand.to_owned(), + format!("Transform argument names must start with an ASCII letter or underscore character: {name}"), + content.pos + ), + InvalidOperandError::InvalidCharsInName(name) => ParseError::InvalidTransformOperandName( + operand.to_owned(), + format!("Transform argument names must only contain ASCII alphanumerics or underscore characters: {name}"), + content.pos, + ), + } + })?; + values.push(operator_argument); + } + _ => return Err(ParseError::InappropriateTypeForDirectiveArgument( + "@transform".to_owned(), + "value".to_owned(), + content.pos, + )), + } + } + values + } + Value::String(argument_value) => return Err(ParseError::TransformExpectsListNotString( + transform_op.to_string(), + argument_value.to_owned(), + content.pos, + )), + _ => { + return Err(ParseError::InappropriateTypeForDirectiveArgument( + "@transform".to_owned(), + "value".to_owned(), + content.pos, + )) + } + } + } + }; + + let operands_span = value.node.get_argument("value").map(|p| &p.pos).unwrap_or(&value.pos); + let kind = match transform_op.as_ref() { + "count" => { + assert_operand_count(&transform_value, 0, operands_span)?; + TransformOp::Count + } + "len" => { + assert_operand_count(&transform_value, 0, operands_span)?; + TransformOp::Len + } + "abs" => { + assert_operand_count(&transform_value, 0, operands_span)?; + TransformOp::Abs + } "+" => { - todo!() + assert_operand_count(&transform_value, 1, operands_span)?; + TransformOp::Add(transform_value.pop().unwrap()) } "+f" => { - todo!() + assert_operand_count(&transform_value, 1, operands_span)?; + TransformOp::AddF(transform_value.pop().unwrap()) } _ => { return Err(ParseError::UnsupportedTransformOperator( - transform_argument.to_string(), - transform_argument_node.pos, + transform_op.to_string(), + transform_op_node.pos, )) } }; @@ -353,6 +459,21 @@ impl TryFrom<&Positioned> for TransformDirective { } } +fn assert_operand_count(operands: &[OperatorArgument], expected_count: usize, pos: &Pos) -> Result<(), ParseError> { + let provided = operands.len(); + if provided != expected_count { + Err(unexpected_arguments_provided(expected_count, provided, pos)) + } else { + Ok(()) + } +} + +fn unexpected_arguments_provided(expected_count: usize, actual_count: usize, pos: &Pos) -> ParseError { + ParseError::OtherError(format!( + "Transform argument count mismatch: expected {expected_count} but found {actual_count}" + ), pos.to_owned()) +} + #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] pub(crate) enum TransformOp { Count, diff --git a/trustfall_core/src/graphql_query/error.rs b/trustfall_core/src/graphql_query/error.rs index 73567ec1..55fda9b7 100644 --- a/trustfall_core/src/graphql_query/error.rs +++ b/trustfall_core/src/graphql_query/error.rs @@ -25,11 +25,17 @@ pub enum ParseError { InappropriateTypeForDirectiveArgument(String, String, Pos), #[error( - "Value argument in @filter directive is a string instead of a list. \ - Did you mean to put '@filter(op: \"{0}\", value: [\"{1}\"])' instead?" + "Filter directives require a list-typed \"value\" argument, but received a string instead. \ + Did you mean to write '@filter(op: \"{0}\", value: [\"{1}\"])' instead?" )] FilterExpectsListNotString(String, String, Pos), + #[error( + "Transform directives allow a list-typed \"value\" argument, but received a string instead. \ + Did you mean to write '@transform(op: \"{0}\", value: [\"{1}\"])' instead?" + )] + TransformExpectsListNotString(String, String, Pos), + #[error("Field {0} received an invalid value for argument {1}: {2}")] InvalidFieldArgument(String, String, Value, Pos), @@ -60,6 +66,9 @@ pub enum ParseError { #[error("Filter is passed an invalid operand argument '{0}': {1}")] InvalidFilterOperandName(String, String, Pos), + #[error("Transform is passed an invalid operand argument '{0}': {1}")] + InvalidTransformOperandName(String, String, Pos), + #[error("Unrecognized transform operator: {0}")] UnsupportedTransformOperator(String, Pos), diff --git a/trustfall_core/src/interpreter/execution.rs b/trustfall_core/src/interpreter/execution.rs index efa8faaf..1946ffd5 100644 --- a/trustfall_core/src/interpreter/execution.rs +++ b/trustfall_core/src/interpreter/execution.rs @@ -737,7 +737,7 @@ mismatch on whether the fold below {expanding_from_vid:?} was inside an `@option FieldRef::FoldSpecificField(field) => ValueOrVec::Value(compute_fold_specific_value::(field, elements)), FieldRef::TransformedField(field) => { let base_value = match &field.value.base { - TransformBase::FoldSpecificField(fold_field) => compute_fold_specific_value::(fold_field, &elements), + TransformBase::FoldSpecificField(fold_field) => compute_fold_specific_value::(fold_field, elements), TransformBase::ContextField(_) => unreachable!("found transformed ContextField inside a fold's fold-specific outputs: {fold:#?} {field_ref:#?}"), }; diff --git a/trustfall_core/src/interpreter/helpers/tests.rs b/trustfall_core/src/interpreter/helpers/tests.rs index 71a53c90..028a8764 100644 --- a/trustfall_core/src/interpreter/helpers/tests.rs +++ b/trustfall_core/src/interpreter/helpers/tests.rs @@ -30,7 +30,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Vertex: Vertex! diff --git a/trustfall_core/src/schema/mod.rs b/trustfall_core/src/schema/mod.rs index 4da0c735..7b0e6b10 100644 --- a/trustfall_core/src/schema/mod.rs +++ b/trustfall_core/src/schema/mod.rs @@ -97,7 +97,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD "; pub fn parse(input: impl AsRef) -> Result { diff --git a/trustfall_core/test_data/schemas/filesystem.graphql b/trustfall_core/test_data/schemas/filesystem.graphql index c3cdc786..efc4bfc5 100644 --- a/trustfall_core/test_data/schemas/filesystem.graphql +++ b/trustfall_core/test_data/schemas/filesystem.graphql @@ -13,7 +13,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD # This is an old and DEPRECATED schema. # diff --git a/trustfall_core/test_data/schemas/nullables.graphql b/trustfall_core/test_data/schemas/nullables.graphql index 04c9fc3a..6d001391 100644 --- a/trustfall_core/test_data/schemas/nullables.graphql +++ b/trustfall_core/test_data/schemas/nullables.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { MainType: MainType diff --git a/trustfall_core/test_data/schemas/numbers.graphql b/trustfall_core/test_data/schemas/numbers.graphql index 9cc05207..d5249af1 100644 --- a/trustfall_core/test_data/schemas/numbers.graphql +++ b/trustfall_core/test_data/schemas/numbers.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Number(min: Int! = 0, max: Int!): [Number!] diff --git a/trustfall_core/test_data/schemas/parameterized_edges.graphql b/trustfall_core/test_data/schemas/parameterized_edges.graphql index b5d44f62..cfbd54c9 100644 --- a/trustfall_core/test_data/schemas/parameterized_edges.graphql +++ b/trustfall_core/test_data/schemas/parameterized_edges.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Vertex: Vertex! diff --git a/trustfall_core/test_data/schemas/recurses.graphql b/trustfall_core/test_data/schemas/recurses.graphql index 6e416e5f..5b220589 100644 --- a/trustfall_core/test_data/schemas/recurses.graphql +++ b/trustfall_core/test_data/schemas/recurses.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/frontend_errors/tag_used_before_defining_on_transform_of_same_property.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/tag_used_before_defining_on_transform_of_same_property.frontend-error.ron new file mode 100644 index 00000000..2c8b2803 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/tag_used_before_defining_on_transform_of_same_property.frontend-error.ron @@ -0,0 +1 @@ +Err(UndefinedTagInTransform("property \"value\"", "transformed")) diff --git a/trustfall_core/test_data/tests/frontend_errors/tag_used_before_defining_on_transform_of_same_property.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/tag_used_before_defining_on_transform_of_same_property.graphql-parsed.ron new file mode 100644 index 00000000..7592b6d7 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/tag_used_before_defining_on_transform_of_same_property.graphql-parsed.ron @@ -0,0 +1,48 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 6, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 6, + column: 9, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Add(TagRef("transformed")), + ), + output: [ + OutputDirective(), + ], + tag: [ + TagDirective( + name: Some("transformed"), + ), + ], + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/tag_used_before_defining_on_transform_of_same_property.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/tag_used_before_defining_on_transform_of_same_property.graphql.ron new file mode 100644 index 00000000..9a4b449c --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/tag_used_before_defining_on_transform_of_same_property.graphql.ron @@ -0,0 +1,15 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + # The "transformed" tag is defined *after* the use, so this is not valid or else + # it would cause infinite recursion. + value + @transform(op: "+", value: ["%transformed"]) + @tag(name: "transformed") + @output + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_add_with_nonexistent_tag.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_add_with_nonexistent_tag.frontend-error.ron new file mode 100644 index 00000000..fae9b646 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_add_with_nonexistent_tag.frontend-error.ron @@ -0,0 +1 @@ +Err(UndefinedTagInTransform("property \"value\"", "name")) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_add_with_nonexistent_tag.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/transform_add_with_nonexistent_tag.graphql-parsed.ron new file mode 100644 index 00000000..ee919ff3 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_add_with_nonexistent_tag.graphql-parsed.ron @@ -0,0 +1,43 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 5, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 5, + column: 9, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Add(TagRef("name")), + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_add_with_nonexistent_tag.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/transform_add_with_nonexistent_tag.graphql.ron new file mode 100644 index 00000000..0f5e7599 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_add_with_nonexistent_tag.graphql.ron @@ -0,0 +1,11 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + # The tag referenced here doesn't exist. This is an error. + value @transform(op: "+", value: ["%name"]) @output + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_add_with_tag_wrong_argument_type.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_add_with_tag_wrong_argument_type.frontend-error.ron new file mode 100644 index 00000000..b75d8628 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_add_with_tag_wrong_argument_type.frontend-error.ron @@ -0,0 +1 @@ +Err(TransformTypeError(TypeMismatchBetweenTransformOperationAndArgument("+", "Int", "String"))) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_add_with_tag_wrong_argument_type.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/transform_add_with_tag_wrong_argument_type.graphql-parsed.ron new file mode 100644 index 00000000..fa4389a9 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_add_with_tag_wrong_argument_type.graphql-parsed.ron @@ -0,0 +1,59 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "name", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "name", + tag: [ + TagDirective(), + ], + )), + (FieldConnection( + position: Pos( + line: 8, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 8, + column: 9, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Add(TagRef("name")), + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_add_with_tag_wrong_argument_type.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/transform_add_with_tag_wrong_argument_type.graphql.ron new file mode 100644 index 00000000..28db3e0b --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_add_with_tag_wrong_argument_type.graphql.ron @@ -0,0 +1,14 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + name @tag + + # The tag provided here is the wrong type: + # we can't add a string to an integer. + value @transform(op: "+", value: ["%name"]) @output + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_abs.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_abs.frontend-error.ron new file mode 100644 index 00000000..2a3bf623 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_abs.frontend-error.ron @@ -0,0 +1 @@ +Err(TransformTypeError(TypeMismatchBetweenTransformOperationAndSubject("abs", "values of type \"Int\" or \"Float\"", "property \"name\"", "String"))) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_abs.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_abs.graphql-parsed.ron new file mode 100644 index 00000000..241149d7 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_abs.graphql-parsed.ron @@ -0,0 +1,43 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 6, + column: 9, + ), + name: "name", + ), FieldNode( + position: Pos( + line: 6, + column: 9, + ), + name: "name", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Abs, + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_abs.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_abs.graphql.ron new file mode 100644 index 00000000..0adeefcf --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_abs.graphql.ron @@ -0,0 +1,12 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + # The "abs" transform operator does not operate on `String` values. + # This is an error. + name @transform(op: "abs") @output + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_add.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_add.frontend-error.ron new file mode 100644 index 00000000..c22d80ea --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_add.frontend-error.ron @@ -0,0 +1 @@ +Err(TransformTypeError(TypeMismatchBetweenTransformOperationAndSubject("+", "values of type \"Int\"", "property \"name\"", "String"))) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_add.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_add.graphql-parsed.ron new file mode 100644 index 00000000..f62e8aef --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_add.graphql-parsed.ron @@ -0,0 +1,46 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 6, + column: 9, + ), + name: "name", + ), FieldNode( + position: Pos( + line: 6, + column: 9, + ), + name: "name", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Add(VariableRef("arg")), + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + ), + ), + arguments: { + "arg": Uint64(0), + }, +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_add.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_add.graphql.ron new file mode 100644 index 00000000..e0e45872 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_add.graphql.ron @@ -0,0 +1,14 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + # The "+" transform operator does not operate on `String` values. + # This is an error. + name @transform(op: "+", value: ["$arg"]) @output + } +}"#, + arguments: { + "arg": Uint64(0), + }, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_len.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_len.frontend-error.ron new file mode 100644 index 00000000..88f6895e --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_len.frontend-error.ron @@ -0,0 +1 @@ +Err(TransformTypeError(TypeMismatchBetweenTransformOperationAndSubject("len", "list-typed values", "property \"value\"", "Int"))) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_len.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_len.graphql-parsed.ron new file mode 100644 index 00000000..9a6ce03f --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_len.graphql-parsed.ron @@ -0,0 +1,43 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 6, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 6, + column: 9, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Len, + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_len.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_len.graphql.ron new file mode 100644 index 00000000..88f69103 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_len.graphql.ron @@ -0,0 +1,12 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + # The "len" transform operator does not operate on `Int` values. + # This is an error. + value @transform(op: "len") @output + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/parse_errors/invalid_transform_argument_name.graphql.ron b/trustfall_core/test_data/tests/parse_errors/invalid_transform_argument_name.graphql.ron new file mode 100644 index 00000000..27d18675 --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/invalid_transform_argument_name.graphql.ron @@ -0,0 +1,11 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + # "10" is not a valid variable identifier + value @transform(op: "add", value: ["$10"]) @output + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/parse_errors/invalid_transform_argument_name.parse-error.ron b/trustfall_core/test_data/tests/parse_errors/invalid_transform_argument_name.parse-error.ron new file mode 100644 index 00000000..59b33127 --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/invalid_transform_argument_name.parse-error.ron @@ -0,0 +1,4 @@ +Err(InvalidTransformOperandName("$10", "Transform argument names must start with an ASCII letter or underscore character: 10", Pos( + line: 5, + column: 44, +))) diff --git a/trustfall_core/test_data/tests/parse_errors/invalid_transform_lacking_argument_for_add_op.graphql.ron b/trustfall_core/test_data/tests/parse_errors/invalid_transform_lacking_argument_for_add_op.graphql.ron new file mode 100644 index 00000000..481c9ff8 --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/invalid_transform_lacking_argument_for_add_op.graphql.ron @@ -0,0 +1,11 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + # The "+" transform operator requires an argument. + value @transform(op: "+") @output + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/parse_errors/invalid_transform_lacking_argument_for_add_op.parse-error.ron b/trustfall_core/test_data/tests/parse_errors/invalid_transform_lacking_argument_for_add_op.parse-error.ron new file mode 100644 index 00000000..09aecb49 --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/invalid_transform_lacking_argument_for_add_op.parse-error.ron @@ -0,0 +1,4 @@ +Err(OtherError("Transform argument count mismatch: expected 1 but found 0", Pos( + line: 5, + column: 15, +))) diff --git a/trustfall_core/test_data/tests/parse_errors/invalid_transform_op.graphql.ron b/trustfall_core/test_data/tests/parse_errors/invalid_transform_op.graphql.ron new file mode 100644 index 00000000..c02ba88a --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/invalid_transform_op.graphql.ron @@ -0,0 +1,10 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + value @transform(op: "nonexistent") @output + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/parse_errors/invalid_transform_op.parse-error.ron b/trustfall_core/test_data/tests/parse_errors/invalid_transform_op.parse-error.ron new file mode 100644 index 00000000..89d8a65a --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/invalid_transform_op.parse-error.ron @@ -0,0 +1,4 @@ +Err(UnsupportedTransformOperator("nonexistent", Pos( + line: 4, + column: 30, +))) diff --git a/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_argument_for_abs_op.graphql.ron b/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_argument_for_abs_op.graphql.ron new file mode 100644 index 00000000..162628ec --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_argument_for_abs_op.graphql.ron @@ -0,0 +1,13 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + # The "abs" transform operator does not accept arguments. + name @transform(op: "abs", value: ["$arg"]) @output + } +}"#, + arguments: { + "arg": Uint64(0), + }, +) diff --git a/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_argument_for_abs_op.parse-error.ron b/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_argument_for_abs_op.parse-error.ron new file mode 100644 index 00000000..f22a94a0 --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_argument_for_abs_op.parse-error.ron @@ -0,0 +1,4 @@ +Err(OtherError("Transform argument count mismatch: expected 0 but found 1", Pos( + line: 5, + column: 43, +))) diff --git a/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_argument_for_len_op.graphql.ron b/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_argument_for_len_op.graphql.ron new file mode 100644 index 00000000..031ca607 --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_argument_for_len_op.graphql.ron @@ -0,0 +1,13 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + # The "len" transform operator does not accept arguments. + vowelsInName @transform(op: "len", value: ["$arg"]) @output + } +}"#, + arguments: { + "arg": Uint64(0), + }, +) diff --git a/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_argument_for_len_op.parse-error.ron b/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_argument_for_len_op.parse-error.ron new file mode 100644 index 00000000..1cfbb3e9 --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_argument_for_len_op.parse-error.ron @@ -0,0 +1,4 @@ +Err(OtherError("Transform argument count mismatch: expected 0 but found 1", Pos( + line: 5, + column: 51, +))) diff --git a/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_multiple_arguments_for_add_op.graphql.ron b/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_multiple_arguments_for_add_op.graphql.ron new file mode 100644 index 00000000..89f0c75f --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_multiple_arguments_for_add_op.graphql.ron @@ -0,0 +1,14 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + # The "+" transform operator accepts only a single argument. + value @transform(op: "+", value: ["$first", "$second"]) @output + } +}"#, + arguments: { + "first": Int64(1), + "second": Int64(2), + }, +) diff --git a/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_multiple_arguments_for_add_op.parse-error.ron b/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_multiple_arguments_for_add_op.parse-error.ron new file mode 100644 index 00000000..6a668426 --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/invalid_transform_supplying_multiple_arguments_for_add_op.parse-error.ron @@ -0,0 +1,4 @@ +Err(OtherError("Transform argument count mismatch: expected 1 but found 2", Pos( + line: 5, + column: 42, +))) diff --git a/trustfall_core/test_data/tests/parse_errors/invalid_transform_value_type.graphql.ron b/trustfall_core/test_data/tests/parse_errors/invalid_transform_value_type.graphql.ron new file mode 100644 index 00000000..1512bc49 --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/invalid_transform_value_type.graphql.ron @@ -0,0 +1,10 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + value @transform(op: "+", value: 42) @output + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/parse_errors/invalid_transform_value_type.parse-error.ron b/trustfall_core/test_data/tests/parse_errors/invalid_transform_value_type.parse-error.ron new file mode 100644 index 00000000..02b19727 --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/invalid_transform_value_type.parse-error.ron @@ -0,0 +1,4 @@ +Err(InappropriateTypeForDirectiveArgument("@transform", "value", Pos( + line: 4, + column: 42, +))) diff --git a/trustfall_core/test_data/tests/parse_errors/transform_with_variable_string_instead_of_array_with_variable_string.graphql.ron b/trustfall_core/test_data/tests/parse_errors/transform_with_variable_string_instead_of_array_with_variable_string.graphql.ron new file mode 100644 index 00000000..0b334e5b --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/transform_with_variable_string_instead_of_array_with_variable_string.graphql.ron @@ -0,0 +1,17 @@ +TestGraphQLQuery ( + // The `value` in a transform expects an array with a single string instead of + // just a single value. + // + // This test explicitly checks this using a variable reference (a string prefixed + // by a dollar sign). + schema_name: "numbers", + query: r#" +{ + Zero { + value @transform(op: "+", value: "$zero") @output + } +}"#, + arguments: { + "zero": Uint64(0) + }, +) diff --git a/trustfall_core/test_data/tests/parse_errors/transform_with_variable_string_instead_of_array_with_variable_string.parse-error.ron b/trustfall_core/test_data/tests/parse_errors/transform_with_variable_string_instead_of_array_with_variable_string.parse-error.ron new file mode 100644 index 00000000..28196aa3 --- /dev/null +++ b/trustfall_core/test_data/tests/parse_errors/transform_with_variable_string_instead_of_array_with_variable_string.parse-error.ron @@ -0,0 +1,4 @@ +Err(TransformExpectsListNotString("+", "$zero", Pos( + line: 4, + column: 42, +))) diff --git a/trustfall_core/test_data/tests/schema_errors/ambiguous_field_origin.graphql b/trustfall_core/test_data/tests/schema_errors/ambiguous_field_origin.graphql index aa5ffcde..28dd4305 100644 --- a/trustfall_core/test_data/tests/schema_errors/ambiguous_field_origin.graphql +++ b/trustfall_core/test_data/tests/schema_errors/ambiguous_field_origin.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Derived: Derived diff --git a/trustfall_core/test_data/tests/schema_errors/circular_implements_relationship.graphql b/trustfall_core/test_data/tests/schema_errors/circular_implements_relationship.graphql index e4c9d55e..6e4c125b 100644 --- a/trustfall_core/test_data/tests/schema_errors/circular_implements_relationship.graphql +++ b/trustfall_core/test_data/tests/schema_errors/circular_implements_relationship.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { A: A diff --git a/trustfall_core/test_data/tests/schema_errors/completely_different_field_type.graphql b/trustfall_core/test_data/tests/schema_errors/completely_different_field_type.graphql index baee9d40..19435a87 100644 --- a/trustfall_core/test_data/tests/schema_errors/completely_different_field_type.graphql +++ b/trustfall_core/test_data/tests/schema_errors/completely_different_field_type.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/edge_to_root_query_type.graphql b/trustfall_core/test_data/tests/schema_errors/edge_to_root_query_type.graphql index 4dacffbf..35cb5f55 100644 --- a/trustfall_core/test_data/tests/schema_errors/edge_to_root_query_type.graphql +++ b/trustfall_core/test_data/tests/schema_errors/edge_to_root_query_type.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/explicit_null_default_for_non_nullable_edge_param.graphql b/trustfall_core/test_data/tests/schema_errors/explicit_null_default_for_non_nullable_edge_param.graphql index db8e9171..91801cdc 100644 --- a/trustfall_core/test_data/tests/schema_errors/explicit_null_default_for_non_nullable_edge_param.graphql +++ b/trustfall_core/test_data/tests/schema_errors/explicit_null_default_for_non_nullable_edge_param.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Vertex(param: Int! = null): Vertex diff --git a/trustfall_core/test_data/tests/schema_errors/implementing_non_existent_type.graphql b/trustfall_core/test_data/tests/schema_errors/implementing_non_existent_type.graphql index 846987d1..18bdb60d 100644 --- a/trustfall_core/test_data/tests/schema_errors/implementing_non_existent_type.graphql +++ b/trustfall_core/test_data/tests/schema_errors/implementing_non_existent_type.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Derived: Derived! diff --git a/trustfall_core/test_data/tests/schema_errors/implementing_object_type.graphql b/trustfall_core/test_data/tests/schema_errors/implementing_object_type.graphql index 1b7ebbd4..642c1d16 100644 --- a/trustfall_core/test_data/tests/schema_errors/implementing_object_type.graphql +++ b/trustfall_core/test_data/tests/schema_errors/implementing_object_type.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base! diff --git a/trustfall_core/test_data/tests/schema_errors/implementing_self_type.graphql b/trustfall_core/test_data/tests/schema_errors/implementing_self_type.graphql index 249401e7..df4f683f 100644 --- a/trustfall_core/test_data/tests/schema_errors/implementing_self_type.graphql +++ b/trustfall_core/test_data/tests/schema_errors/implementing_self_type.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base! diff --git a/trustfall_core/test_data/tests/schema_errors/incorrect_default_value_type_for_edge_param.graphql b/trustfall_core/test_data/tests/schema_errors/incorrect_default_value_type_for_edge_param.graphql index 0934753e..2569b9e0 100644 --- a/trustfall_core/test_data/tests/schema_errors/incorrect_default_value_type_for_edge_param.graphql +++ b/trustfall_core/test_data/tests/schema_errors/incorrect_default_value_type_for_edge_param.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Vertex(param: String = 123): Vertex diff --git a/trustfall_core/test_data/tests/schema_errors/interface_with_same_field_twice.graphql b/trustfall_core/test_data/tests/schema_errors/interface_with_same_field_twice.graphql index 63d01b7c..8c05a8b7 100644 --- a/trustfall_core/test_data/tests/schema_errors/interface_with_same_field_twice.graphql +++ b/trustfall_core/test_data/tests/schema_errors/interface_with_same_field_twice.graphql @@ -37,6 +37,10 @@ directive @transform( Name of the transformation operation to perform. """ op: String! + """ + Optional list of operands for the transform operator, if any. + """ + value: [String!] ) on FIELD """ diff --git a/trustfall_core/test_data/tests/schema_errors/list_of_list_edge.graphql b/trustfall_core/test_data/tests/schema_errors/list_of_list_edge.graphql index 5bd99316..0d469964 100644 --- a/trustfall_core/test_data/tests/schema_errors/list_of_list_edge.graphql +++ b/trustfall_core/test_data/tests/schema_errors/list_of_list_edge.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/list_of_list_root_edge.graphql b/trustfall_core/test_data/tests/schema_errors/list_of_list_root_edge.graphql index 605da820..2a83bc01 100644 --- a/trustfall_core/test_data/tests/schema_errors/list_of_list_root_edge.graphql +++ b/trustfall_core/test_data/tests/schema_errors/list_of_list_root_edge.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: [[Base]] diff --git a/trustfall_core/test_data/tests/schema_errors/missing_field_and_illegal_type_widening.graphql b/trustfall_core/test_data/tests/schema_errors/missing_field_and_illegal_type_widening.graphql index 2ac03f6f..ec5186d8 100644 --- a/trustfall_core/test_data/tests/schema_errors/missing_field_and_illegal_type_widening.graphql +++ b/trustfall_core/test_data/tests/schema_errors/missing_field_and_illegal_type_widening.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/missing_required_field.graphql b/trustfall_core/test_data/tests/schema_errors/missing_required_field.graphql index fb1faf28..6007c349 100644 --- a/trustfall_core/test_data/tests/schema_errors/missing_required_field.graphql +++ b/trustfall_core/test_data/tests/schema_errors/missing_required_field.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/one_type_one_interface_both_same_name.graphql b/trustfall_core/test_data/tests/schema_errors/one_type_one_interface_both_same_name.graphql index 47ad3965..7b4869a6 100644 --- a/trustfall_core/test_data/tests/schema_errors/one_type_one_interface_both_same_name.graphql +++ b/trustfall_core/test_data/tests/schema_errors/one_type_one_interface_both_same_name.graphql @@ -37,6 +37,10 @@ directive @transform( Name of the transformation operation to perform. """ op: String! + """ + Optional list of operands for the transform operator, if any. + """ + value: [String!] ) on FIELD """ diff --git a/trustfall_core/test_data/tests/schema_errors/parameterized_field_missing_parameter_in_subtype.graphql b/trustfall_core/test_data/tests/schema_errors/parameterized_field_missing_parameter_in_subtype.graphql index 31e5c5fb..f598d3b7 100644 --- a/trustfall_core/test_data/tests/schema_errors/parameterized_field_missing_parameter_in_subtype.graphql +++ b/trustfall_core/test_data/tests/schema_errors/parameterized_field_missing_parameter_in_subtype.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/parameters_in_property_field.graphql b/trustfall_core/test_data/tests/schema_errors/parameters_in_property_field.graphql index 99bbad4e..dc80020d 100644 --- a/trustfall_core/test_data/tests/schema_errors/parameters_in_property_field.graphql +++ b/trustfall_core/test_data/tests/schema_errors/parameters_in_property_field.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/property_on_root_query_type.graphql b/trustfall_core/test_data/tests/schema_errors/property_on_root_query_type.graphql index 1c0e0156..082025f7 100644 --- a/trustfall_core/test_data/tests/schema_errors/property_on_root_query_type.graphql +++ b/trustfall_core/test_data/tests/schema_errors/property_on_root_query_type.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/reserved_prefix_edge_defined.graphql b/trustfall_core/test_data/tests/schema_errors/reserved_prefix_edge_defined.graphql index 5ef05438..7f87fc39 100644 --- a/trustfall_core/test_data/tests/schema_errors/reserved_prefix_edge_defined.graphql +++ b/trustfall_core/test_data/tests/schema_errors/reserved_prefix_edge_defined.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Foo: Foo diff --git a/trustfall_core/test_data/tests/schema_errors/reserved_prefix_property_defined.graphql b/trustfall_core/test_data/tests/schema_errors/reserved_prefix_property_defined.graphql index 07af2d60..0d8e183c 100644 --- a/trustfall_core/test_data/tests/schema_errors/reserved_prefix_property_defined.graphql +++ b/trustfall_core/test_data/tests/schema_errors/reserved_prefix_property_defined.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Foo: Foo diff --git a/trustfall_core/test_data/tests/schema_errors/reserved_prefix_query_type.graphql b/trustfall_core/test_data/tests/schema_errors/reserved_prefix_query_type.graphql index a3be4f4f..27a9cc88 100644 --- a/trustfall_core/test_data/tests/schema_errors/reserved_prefix_query_type.graphql +++ b/trustfall_core/test_data/tests/schema_errors/reserved_prefix_query_type.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type __RootSchemaQuery { Foo: Foo diff --git a/trustfall_core/test_data/tests/schema_errors/reserved_prefix_top_level_field.graphql b/trustfall_core/test_data/tests/schema_errors/reserved_prefix_top_level_field.graphql index 7c9b750b..abb5eca7 100644 --- a/trustfall_core/test_data/tests/schema_errors/reserved_prefix_top_level_field.graphql +++ b/trustfall_core/test_data/tests/schema_errors/reserved_prefix_top_level_field.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { __Foo: Foo diff --git a/trustfall_core/test_data/tests/schema_errors/reserved_prefix_type_name.graphql b/trustfall_core/test_data/tests/schema_errors/reserved_prefix_type_name.graphql index 76a9e4fb..ee74470e 100644 --- a/trustfall_core/test_data/tests/schema_errors/reserved_prefix_type_name.graphql +++ b/trustfall_core/test_data/tests/schema_errors/reserved_prefix_type_name.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Foo: __Foo diff --git a/trustfall_core/test_data/tests/schema_errors/root_edge_to_root_query_type.graphql b/trustfall_core/test_data/tests/schema_errors/root_edge_to_root_query_type.graphql index 4b6ccd0f..d6f7c3f7 100644 --- a/trustfall_core/test_data/tests/schema_errors/root_edge_to_root_query_type.graphql +++ b/trustfall_core/test_data/tests/schema_errors/root_edge_to_root_query_type.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/subtype_adds_parameter_to_field.graphql b/trustfall_core/test_data/tests/schema_errors/subtype_adds_parameter_to_field.graphql index 8be913e8..f9c6f8c1 100644 --- a/trustfall_core/test_data/tests/schema_errors/subtype_adds_parameter_to_field.graphql +++ b/trustfall_core/test_data/tests/schema_errors/subtype_adds_parameter_to_field.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/transitive_interface_not_implemented.graphql b/trustfall_core/test_data/tests/schema_errors/transitive_interface_not_implemented.graphql index 9dfda4f9..21adbcd2 100644 --- a/trustfall_core/test_data/tests/schema_errors/transitive_interface_not_implemented.graphql +++ b/trustfall_core/test_data/tests/schema_errors/transitive_interface_not_implemented.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Final: Final diff --git a/trustfall_core/test_data/tests/schema_errors/triple_list_edge.graphql b/trustfall_core/test_data/tests/schema_errors/triple_list_edge.graphql index e624a1dd..39cda1d2 100644 --- a/trustfall_core/test_data/tests/schema_errors/triple_list_edge.graphql +++ b/trustfall_core/test_data/tests/schema_errors/triple_list_edge.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/triple_list_root_edge.graphql b/trustfall_core/test_data/tests/schema_errors/triple_list_root_edge.graphql index b66402eb..6b6ce840 100644 --- a/trustfall_core/test_data/tests/schema_errors/triple_list_root_edge.graphql +++ b/trustfall_core/test_data/tests/schema_errors/triple_list_root_edge.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: [[[Base]]] diff --git a/trustfall_core/test_data/tests/schema_errors/two_interface_same_name.graphql b/trustfall_core/test_data/tests/schema_errors/two_interface_same_name.graphql index c4803c9e..73a0d6b9 100644 --- a/trustfall_core/test_data/tests/schema_errors/two_interface_same_name.graphql +++ b/trustfall_core/test_data/tests/schema_errors/two_interface_same_name.graphql @@ -37,6 +37,10 @@ directive @transform( Name of the transformation operation to perform. """ op: String! + """ + Optional list of operands for the transform operator, if any. + """ + value: [String!] ) on FIELD """ diff --git a/trustfall_core/test_data/tests/schema_errors/two_types_same_name.graphql b/trustfall_core/test_data/tests/schema_errors/two_types_same_name.graphql index a5e4bdcc..dd6db39b 100644 --- a/trustfall_core/test_data/tests/schema_errors/two_types_same_name.graphql +++ b/trustfall_core/test_data/tests/schema_errors/two_types_same_name.graphql @@ -37,6 +37,10 @@ directive @transform( Name of the transformation operation to perform. """ op: String! + """ + Optional list of operands for the transform operator, if any. + """ + value: [String!] ) on FIELD """ diff --git a/trustfall_core/test_data/tests/schema_errors/type_narrowing_parameterized_edge.graphql b/trustfall_core/test_data/tests/schema_errors/type_narrowing_parameterized_edge.graphql index dab91f74..408a54de 100644 --- a/trustfall_core/test_data/tests/schema_errors/type_narrowing_parameterized_edge.graphql +++ b/trustfall_core/test_data/tests/schema_errors/type_narrowing_parameterized_edge.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/type_narrowing_parameterized_edge_list_param.graphql b/trustfall_core/test_data/tests/schema_errors/type_narrowing_parameterized_edge_list_param.graphql index e713387a..05869ace 100644 --- a/trustfall_core/test_data/tests/schema_errors/type_narrowing_parameterized_edge_list_param.graphql +++ b/trustfall_core/test_data/tests/schema_errors/type_narrowing_parameterized_edge_list_param.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/type_widening_edge_supertype.graphql b/trustfall_core/test_data/tests/schema_errors/type_widening_edge_supertype.graphql index 92dd73f2..b41c77ab 100644 --- a/trustfall_core/test_data/tests/schema_errors/type_widening_edge_supertype.graphql +++ b/trustfall_core/test_data/tests/schema_errors/type_widening_edge_supertype.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/type_widening_list_edge_supertype.graphql b/trustfall_core/test_data/tests/schema_errors/type_widening_list_edge_supertype.graphql index 9a48ddfd..dad598ac 100644 --- a/trustfall_core/test_data/tests/schema_errors/type_widening_list_edge_supertype.graphql +++ b/trustfall_core/test_data/tests/schema_errors/type_widening_list_edge_supertype.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/type_widening_nullability_on_edge_field.graphql b/trustfall_core/test_data/tests/schema_errors/type_widening_nullability_on_edge_field.graphql index 4165b375..a9a09222 100644 --- a/trustfall_core/test_data/tests/schema_errors/type_widening_nullability_on_edge_field.graphql +++ b/trustfall_core/test_data/tests/schema_errors/type_widening_nullability_on_edge_field.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/type_widening_nullability_on_list_edge_field.graphql b/trustfall_core/test_data/tests/schema_errors/type_widening_nullability_on_list_edge_field.graphql index 64d651b3..db66f4b9 100644 --- a/trustfall_core/test_data/tests/schema_errors/type_widening_nullability_on_list_edge_field.graphql +++ b/trustfall_core/test_data/tests/schema_errors/type_widening_nullability_on_list_edge_field.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/type_widening_of_inherited_list_fields.graphql b/trustfall_core/test_data/tests/schema_errors/type_widening_of_inherited_list_fields.graphql index d5b6db65..ef6fd0b7 100644 --- a/trustfall_core/test_data/tests/schema_errors/type_widening_of_inherited_list_fields.graphql +++ b/trustfall_core/test_data/tests/schema_errors/type_widening_of_inherited_list_fields.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/type_widening_of_inherited_scalar_field.graphql b/trustfall_core/test_data/tests/schema_errors/type_widening_of_inherited_scalar_field.graphql index bce05301..328f50c7 100644 --- a/trustfall_core/test_data/tests/schema_errors/type_widening_of_inherited_scalar_field.graphql +++ b/trustfall_core/test_data/tests/schema_errors/type_widening_of_inherited_scalar_field.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/schema_errors/type_with_same_field_twice.graphql b/trustfall_core/test_data/tests/schema_errors/type_with_same_field_twice.graphql index 1bc1443d..64e4ad93 100644 --- a/trustfall_core/test_data/tests/schema_errors/type_with_same_field_twice.graphql +++ b/trustfall_core/test_data/tests/schema_errors/type_with_same_field_twice.graphql @@ -37,6 +37,10 @@ directive @transform( Name of the transformation operation to perform. """ op: String! + """ + Optional list of operands for the transform operator, if any. + """ + value: [String!] ) on FIELD """ diff --git a/trustfall_core/test_data/tests/valid_schemas/deeply_nested_list_property_field.graphql b/trustfall_core/test_data/tests/valid_schemas/deeply_nested_list_property_field.graphql index c970684c..9880c8d1 100644 --- a/trustfall_core/test_data/tests/valid_schemas/deeply_nested_list_property_field.graphql +++ b/trustfall_core/test_data/tests/valid_schemas/deeply_nested_list_property_field.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/valid_schemas/type_narrowing_edge_nullability.graphql b/trustfall_core/test_data/tests/valid_schemas/type_narrowing_edge_nullability.graphql index 36d1eaef..1409dcc2 100644 --- a/trustfall_core/test_data/tests/valid_schemas/type_narrowing_edge_nullability.graphql +++ b/trustfall_core/test_data/tests/valid_schemas/type_narrowing_edge_nullability.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/valid_schemas/type_narrowing_edge_subtype.graphql b/trustfall_core/test_data/tests/valid_schemas/type_narrowing_edge_subtype.graphql index 3116265e..306728fa 100644 --- a/trustfall_core/test_data/tests/valid_schemas/type_narrowing_edge_subtype.graphql +++ b/trustfall_core/test_data/tests/valid_schemas/type_narrowing_edge_subtype.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/valid_schemas/type_narrowing_list_edge_nullability.graphql b/trustfall_core/test_data/tests/valid_schemas/type_narrowing_list_edge_nullability.graphql index 89e62e08..f9aaa45b 100644 --- a/trustfall_core/test_data/tests/valid_schemas/type_narrowing_list_edge_nullability.graphql +++ b/trustfall_core/test_data/tests/valid_schemas/type_narrowing_list_edge_nullability.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/valid_schemas/type_narrowing_list_edge_subtype.graphql b/trustfall_core/test_data/tests/valid_schemas/type_narrowing_list_edge_subtype.graphql index e29b2c30..472aee25 100644 --- a/trustfall_core/test_data/tests/valid_schemas/type_narrowing_list_edge_subtype.graphql +++ b/trustfall_core/test_data/tests/valid_schemas/type_narrowing_list_edge_subtype.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/valid_schemas/type_narrowing_of_inherited_fields.graphql b/trustfall_core/test_data/tests/valid_schemas/type_narrowing_of_inherited_fields.graphql index dcadeba3..7bdc930a 100644 --- a/trustfall_core/test_data/tests/valid_schemas/type_narrowing_of_inherited_fields.graphql +++ b/trustfall_core/test_data/tests/valid_schemas/type_narrowing_of_inherited_fields.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base diff --git a/trustfall_core/test_data/tests/valid_schemas/type_widening_parameterized_field_parameter.graphql b/trustfall_core/test_data/tests/valid_schemas/type_widening_parameterized_field_parameter.graphql index c16f7430..31676696 100644 --- a/trustfall_core/test_data/tests/valid_schemas/type_widening_parameterized_field_parameter.graphql +++ b/trustfall_core/test_data/tests/valid_schemas/type_widening_parameterized_field_parameter.graphql @@ -7,7 +7,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type RootSchemaQuery { Base: Base From d79f1721740501b39b73c0fc5c3b4401b0565a20 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Tue, 25 Jun 2024 16:47:03 +0000 Subject: [PATCH 21/30] Report variable type conflicts in both filters and transforms. --- trustfall_core/src/frontend/error.rs | 35 +++-- trustfall_core/src/frontend/mod.rs | 138 +++++++++++------- .../src/graphql_query/directives.rs | 81 +++++----- ...inferred_variable_types.frontend-error.ron | 1 + ...inferred_variable_types.graphql-parsed.ron | 76 ++++++++++ ...filter_inferred_variable_types.graphql.ron | 22 +++ ...inferred_variable_types.frontend-error.ron | 1 + ...inferred_variable_types.graphql-parsed.ron | 70 +++++++++ ...filter_inferred_variable_types.graphql.ron | 18 +++ ...inferred_variable_types.frontend-error.ron | 1 + ...inferred_variable_types.graphql-parsed.ron | 73 +++++++++ ...filter_inferred_variable_types.graphql.ron | 21 +++ ...inferred_variable_types.frontend-error.ron | 2 +- ...ted_list_variable_types.frontend-error.ron | 2 +- ...inferred_variable_types.frontend-error.ron | 1 + ...inferred_variable_types.graphql-parsed.ron | 70 +++++++++ ...filter_inferred_variable_types.graphql.ron | 16 ++ ...inferred_variable_types.frontend-error.ron | 2 +- ...inferred_variable_types.frontend-error.ron | 2 +- ...inferred_variable_types.frontend-error.ron | 1 + ...inferred_variable_types.graphql-parsed.ron | 64 ++++++++ ...filter_inferred_variable_types.graphql.ron | 17 +++ 22 files changed, 614 insertions(+), 100 deletions(-) create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_fold_count_transform_vs_filter_inferred_variable_types.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_fold_count_transform_vs_filter_inferred_variable_types.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_fold_count_transform_vs_filter_inferred_variable_types.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_transform_vs_filter_inferred_variable_types.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_transform_vs_filter_inferred_variable_types.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_transform_vs_filter_inferred_variable_types.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_fold_count_transform_vs_filter_inferred_variable_types.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_fold_count_transform_vs_filter_inferred_variable_types.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_fold_count_transform_vs_filter_inferred_variable_types.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_filter_inferred_variable_types.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_filter_inferred_variable_types.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_filter_inferred_variable_types.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_transform_vs_filter_inferred_variable_types.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_transform_vs_filter_inferred_variable_types.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_transform_vs_filter_inferred_variable_types.graphql.ron diff --git a/trustfall_core/src/frontend/error.rs b/trustfall_core/src/frontend/error.rs index cd87e951..8c2d7d33 100644 --- a/trustfall_core/src/frontend/error.rs +++ b/trustfall_core/src/frontend/error.rs @@ -59,6 +59,13 @@ pub enum FrontendError { )] ExplicitTagNameRequired(String), + #[error( + "Variable \"{0}\" is used in multiple places in the query that require values of \ + incompatible types \"{1}\" and \"{2}\". Please split up the uses that require different \ + types into separate variables." + )] + IncompatibleVariableTypeRequirements(String, String, String), + #[error("Incompatible types encountered in @filter: {0}")] FilterTypeError(#[from] FilterTypeError), @@ -187,13 +194,6 @@ impl FrontendError { #[non_exhaustive] #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, thiserror::Error)] pub enum FilterTypeError { - #[error( - "Variable \"{0}\" is used in multiple places in the query that require values of \ - incompatible types \"{1}\" and \"{2}\". Please split up the uses that require different \ - types into separate variables." - )] - IncompatibleVariableTypeRequirements(String, String, String), - #[error( "Filter operation \"{0}\" is applied on non-nullable {1}. \ The filter's result would always be {2}. Please rewrite the query to avoid this filter." @@ -506,7 +506,11 @@ impl TransformTypeError { Self::DuplicatedCountTransformOnEdge(edge_name.to_string()) } - pub(crate) fn operation_requires_list_type_subject(op: &str, subject_representation: String, subject_type: &Type) -> Self { + pub(crate) fn operation_requires_list_type_subject( + op: &str, + subject_representation: String, + subject_type: &Type, + ) -> Self { Self::TypeMismatchBetweenTransformOperationAndSubject( op.to_string(), "list-typed values".to_string(), @@ -515,7 +519,12 @@ impl TransformTypeError { ) } - pub(crate) fn operation_requires_different_type_subject(op: &str, required_type: &Type, subject_representation: String, subject_type: &Type) -> Self { + pub(crate) fn operation_requires_different_type_subject( + op: &str, + required_type: &Type, + subject_representation: String, + subject_type: &Type, + ) -> Self { Self::TypeMismatchBetweenTransformOperationAndSubject( op.to_string(), format!("values of type \"{required_type}\""), @@ -524,7 +533,13 @@ impl TransformTypeError { ) } - pub(crate) fn operation_requires_different_choice_of_type_subject(op: &str, required_type_a: &Type, required_type_b: &Type, subject_representation: String, subject_type: &Type) -> Self { + pub(crate) fn operation_requires_different_choice_of_type_subject( + op: &str, + required_type_a: &Type, + required_type_b: &Type, + subject_representation: String, + subject_type: &Type, + ) -> Self { Self::TypeMismatchBetweenTransformOperationAndSubject( op.to_string(), format!("values of type \"{required_type_a}\" or \"{required_type_b}\""), diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index e68805c3..0a37508f 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -28,10 +28,7 @@ use crate::{ }; use self::{ - error::{ - DuplicatedNamesConflict, FilterTypeError, FrontendError, TransformTypeError, - ValidationError, - }, + error::{DuplicatedNamesConflict, FrontendError, TransformTypeError, ValidationError}, filters::make_filter_expr, outputs::OutputHandler, tags::{TagHandler, TagLookupError}, @@ -305,9 +302,7 @@ pub fn make_ir_for_query(schema: &Schema, query: &Query) -> Result, Type> = Default::default(); - if let Err(v) = fill_in_query_variables(&mut variables, &root_component) { - errors.extend(v.into_iter().map(|x| x.into())); - } + fill_in_query_variables(&mut variables, &root_component, &mut errors); if let Err(e) = tags.finish() { errors.push(FrontendError::UnusedTags(e.into_iter().map(String::from).collect())); @@ -358,59 +353,96 @@ fn collect_ir_vertices_and_folds_recursive_step( fn fill_in_query_variables( variables: &mut BTreeMap, Type>, component: &IRQueryComponent, -) -> Result<(), Vec> { - let mut errors: Vec = vec![]; - - let all_variable_uses = component - .vertices - .values() - .flat_map(|vertex| &vertex.filters) - .map(|filter| filter.right()) - .chain( - component - .folds - .values() - .flat_map(|fold| &fold.post_filters) - .map(|filter| filter.right()), - ) - .filter_map(|rhs| match rhs { - Some(Argument::Variable(vref)) => Some(vref), - _ => None, - }); - // TODO: include transformed uses of variables here: - // - transform on output property - // - transform on output of fold-specific field - // - transform on filter subject - // - retransform of transformed property - for vref in all_variable_uses { - let existing_type = variables - .entry(vref.variable_name.clone()) - .or_insert_with(|| vref.variable_type.clone()); - - match existing_type.intersect(&vref.variable_type) { - Some(intersection) => { - *existing_type = intersection; - } - None => { - errors.push(FilterTypeError::IncompatibleVariableTypeRequirements( - vref.variable_name.to_string(), - existing_type.to_string(), - vref.variable_type.to_string(), - )); - } + errors: &mut Vec, +) { + for vertex in component.vertices.values() { + for filter in &vertex.filters { + process_variable_uses_in_filter(variables, filter, errors); + } + } + + for output in component.outputs.values() { + if let FieldRef::TransformedField(transformed) = output { + process_variable_uses_in_transforms(variables, &transformed.value.transforms, errors); } } for fold in component.folds.values() { - if let Err(e) = fill_in_query_variables(variables, fold.component.as_ref()) { - errors.extend(e); + fill_in_query_variables(variables, fold.component.as_ref(), errors); + + for output in fold.fold_specific_outputs.values() { + if let FieldRef::TransformedField(transformed) = output { + process_variable_uses_in_transforms( + variables, + &transformed.value.transforms, + errors, + ); + } + } + + for filter in &fold.post_filters { + process_variable_uses_in_filter(variables, filter, errors); } } +} - if errors.is_empty() { - Ok(()) - } else { - Err(errors) +fn process_variable_uses_in_filter( + variables: &mut BTreeMap, Type>, + filter: &Operation, + errors: &mut Vec, +) { + // Handle variable uses when the left-hand side of the filter is a `@transform`, + // which might take operands. For example: `@transform(op: "+", value: ["$number"])` + if let OperationSubject::TransformedField(transformed) = filter.left() { + process_variable_uses_in_transforms(variables, &transformed.value.transforms, errors); + } + + // Handle variable uses as part of the filter operands. + // For example: `@filter(op: "=", value: ["$expected"])` + if let Some(Argument::Variable(vref)) = filter.right() { + process_variable_use(variables, vref, errors); + } +} + +fn process_variable_uses_in_transforms( + variables: &mut BTreeMap, Type>, + transforms: &[Transform], + errors: &mut Vec, +) { + for transform in transforms { + match transform { + Transform::Add(operand) | Transform::AddF(operand) => match operand { + Argument::Variable(vref) => { + process_variable_use(variables, vref, errors); + } + Argument::Tag(..) => {} + }, + Transform::Len | Transform::Abs => { + // These transforms don't take operands, so no variables here. + } + } + } +} + +fn process_variable_use( + variables: &mut BTreeMap, Type>, + vref: &VariableRef, + errors: &mut Vec, +) { + let existing_type = + variables.entry(vref.variable_name.clone()).or_insert_with(|| vref.variable_type.clone()); + + match existing_type.intersect(&vref.variable_type) { + Some(intersection) => { + *existing_type = intersection; + } + None => { + errors.push(FrontendError::IncompatibleVariableTypeRequirements( + vref.variable_name.to_string(), + existing_type.to_string(), + vref.variable_type.to_string(), + )); + } } } diff --git a/trustfall_core/src/graphql_query/directives.rs b/trustfall_core/src/graphql_query/directives.rs index 7fd61419..23bba5b2 100644 --- a/trustfall_core/src/graphql_query/directives.rs +++ b/trustfall_core/src/graphql_query/directives.rs @@ -197,12 +197,11 @@ fn parse_operand(operand: &str) -> Result> for TransformDirective { } }; - let mut transform_value: SmallVec<[OperatorArgument; 2]> = match value.node.get_argument("value") { + let mut transform_value: SmallVec<[OperatorArgument; 2]> = match value + .node + .get_argument("value") + { None => SmallVec::new(), - Some(content) => { - match &content.node { - Value::List(value_contents) => { - let mut values = SmallVec::new(); - for v in value_contents { - match v { - Value::String(operand) => { - let operator_argument = parse_operand(operand).map_err(|e| { + Some(content) => match &content.node { + Value::List(value_contents) => { + let mut values = SmallVec::new(); + for v in value_contents { + match v { + Value::String(operand) => { + let operator_argument = parse_operand(operand).map_err(|e| { match e { InvalidOperandError::InvalidPrefix => ParseError::InvalidTransformOperandName( operand.to_owned(), @@ -398,31 +399,34 @@ impl TryFrom<&Positioned> for TransformDirective { ), } })?; - values.push(operator_argument); - } - _ => return Err(ParseError::InappropriateTypeForDirectiveArgument( + values.push(operator_argument); + } + _ => { + return Err(ParseError::InappropriateTypeForDirectiveArgument( "@transform".to_owned(), "value".to_owned(), content.pos, - )), + )) } } - values } - Value::String(argument_value) => return Err(ParseError::TransformExpectsListNotString( + values + } + Value::String(argument_value) => { + return Err(ParseError::TransformExpectsListNotString( transform_op.to_string(), argument_value.to_owned(), content.pos, - )), - _ => { - return Err(ParseError::InappropriateTypeForDirectiveArgument( - "@transform".to_owned(), - "value".to_owned(), - content.pos, - )) - } + )) } - } + _ => { + return Err(ParseError::InappropriateTypeForDirectiveArgument( + "@transform".to_owned(), + "value".to_owned(), + content.pos, + )) + } + }, }; let operands_span = value.node.get_argument("value").map(|p| &p.pos).unwrap_or(&value.pos); @@ -459,7 +463,11 @@ impl TryFrom<&Positioned> for TransformDirective { } } -fn assert_operand_count(operands: &[OperatorArgument], expected_count: usize, pos: &Pos) -> Result<(), ParseError> { +fn assert_operand_count( + operands: &[OperatorArgument], + expected_count: usize, + pos: &Pos, +) -> Result<(), ParseError> { let provided = operands.len(); if provided != expected_count { Err(unexpected_arguments_provided(expected_count, provided, pos)) @@ -468,10 +476,17 @@ fn assert_operand_count(operands: &[OperatorArgument], expected_count: usize, po } } -fn unexpected_arguments_provided(expected_count: usize, actual_count: usize, pos: &Pos) -> ParseError { - ParseError::OtherError(format!( - "Transform argument count mismatch: expected {expected_count} but found {actual_count}" - ), pos.to_owned()) +fn unexpected_arguments_provided( + expected_count: usize, + actual_count: usize, + pos: &Pos, +) -> ParseError { + ParseError::OtherError( + format!( + "Transform argument count mismatch: expected {expected_count} but found {actual_count}" + ), + pos.to_owned(), + ) } #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_fold_count_transform_vs_filter_inferred_variable_types.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_fold_count_transform_vs_filter_inferred_variable_types.frontend-error.ron new file mode 100644 index 00000000..97a08f1a --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_fold_count_transform_vs_filter_inferred_variable_types.frontend-error.ron @@ -0,0 +1 @@ +Err(IncompatibleVariableTypeRequirements("arg", "[String]", "Int!")) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_fold_count_transform_vs_filter_inferred_variable_types.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_fold_count_transform_vs_filter_inferred_variable_types.graphql-parsed.ron new file mode 100644 index 00000000..b8dd0a15 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_fold_count_transform_vs_filter_inferred_variable_types.graphql-parsed.ron @@ -0,0 +1,76 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "vowelsInName", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "vowelsInName", + filter: [ + FilterDirective( + operation: Equals((), VariableRef("arg")), + ), + ], + )), + (FieldConnection( + position: Pos( + line: 9, + column: 9, + ), + name: "successor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Count, + ), + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Add(VariableRef("arg")), + ), + filter: [ + FilterDirective( + operation: GreaterThan((), VariableRef("num")), + ), + ], + )), + )), + )), + ), FieldNode( + position: Pos( + line: 9, + column: 9, + ), + name: "successor", + )), + ], + ), + ), + arguments: { + "arg": String(""), + "num": Int64(0), + }, +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_fold_count_transform_vs_filter_inferred_variable_types.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_fold_count_transform_vs_filter_inferred_variable_types.graphql.ron new file mode 100644 index 00000000..4025f243 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_fold_count_transform_vs_filter_inferred_variable_types.graphql.ron @@ -0,0 +1,22 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + vowelsInName @filter(op: "=", value: ["$arg"]) + + # There are incompatible demands for the type of `$arg` here: + # it needs to be a `[String]` to match the filter, + # and an `Int!` to fit the transform. This is a frontend error. + successor + @fold + @transform(op: "count") + @transform(op: "+", value: ["$arg"]) + @filter(op: ">", value: ["$num"]) + } +}"#, + arguments: { + "arg": String(""), + "num": Int64(0), + }, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_transform_vs_filter_inferred_variable_types.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_transform_vs_filter_inferred_variable_types.frontend-error.ron new file mode 100644 index 00000000..cd4958de --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_transform_vs_filter_inferred_variable_types.frontend-error.ron @@ -0,0 +1 @@ +Err(IncompatibleVariableTypeRequirements("arg", "String", "Int!")) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_transform_vs_filter_inferred_variable_types.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_transform_vs_filter_inferred_variable_types.graphql-parsed.ron new file mode 100644 index 00000000..0a6d7284 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_transform_vs_filter_inferred_variable_types.graphql-parsed.ron @@ -0,0 +1,70 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "name", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "name", + filter: [ + FilterDirective( + operation: Equals((), VariableRef("arg")), + ), + ], + output: [ + OutputDirective(), + ], + )), + (FieldConnection( + position: Pos( + line: 9, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 9, + column: 9, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Add(VariableRef("arg")), + ), + filter: [ + FilterDirective( + operation: GreaterThan((), VariableRef("num")), + ), + ], + )), + )), + ], + ), + ), + arguments: { + "arg": String(""), + "num": Int64(0), + }, +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_transform_vs_filter_inferred_variable_types.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_transform_vs_filter_inferred_variable_types.graphql.ron new file mode 100644 index 00000000..f5bc2dd2 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_filter_subject_transform_vs_filter_inferred_variable_types.graphql.ron @@ -0,0 +1,18 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + name @filter(op: "=", value: ["$arg"]) @output + + # There are incompatible demands for the type of `$arg` here: + # it needs to be a `String` to match the filter, + # and an `Int!` to fit the transform. This is a frontend error. + value @transform(op: "+", value: ["$arg"]) @filter(op: ">", value: ["$num"]) + } +}"#, + arguments: { + "arg": String(""), + "num": Int64(0), + }, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_fold_count_transform_vs_filter_inferred_variable_types.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_fold_count_transform_vs_filter_inferred_variable_types.frontend-error.ron new file mode 100644 index 00000000..97a08f1a --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_fold_count_transform_vs_filter_inferred_variable_types.frontend-error.ron @@ -0,0 +1 @@ +Err(IncompatibleVariableTypeRequirements("arg", "[String]", "Int!")) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_fold_count_transform_vs_filter_inferred_variable_types.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_fold_count_transform_vs_filter_inferred_variable_types.graphql-parsed.ron new file mode 100644 index 00000000..78593b0f --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_fold_count_transform_vs_filter_inferred_variable_types.graphql-parsed.ron @@ -0,0 +1,73 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "vowelsInName", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "vowelsInName", + filter: [ + FilterDirective( + operation: Equals((), VariableRef("arg")), + ), + ], + )), + (FieldConnection( + position: Pos( + line: 9, + column: 9, + ), + name: "successor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Count, + ), + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Add(VariableRef("arg")), + ), + output: [ + OutputDirective(), + ], + )), + )), + )), + ), FieldNode( + position: Pos( + line: 9, + column: 9, + ), + name: "successor", + )), + ], + ), + ), + arguments: { + "arg": String(""), + }, +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_fold_count_transform_vs_filter_inferred_variable_types.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_fold_count_transform_vs_filter_inferred_variable_types.graphql.ron new file mode 100644 index 00000000..10859d31 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_fold_count_transform_vs_filter_inferred_variable_types.graphql.ron @@ -0,0 +1,21 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + vowelsInName @filter(op: "=", value: ["$arg"]) + + # There are incompatible demands for the type of `$arg` here: + # it needs to be a `[String]` to match the filter, + # and an `Int!` to fit the transform. This is a frontend error. + successor + @fold + @transform(op: "count") + @transform(op: "+", value: ["$arg"]) + @output + } +}"#, + arguments: { + "arg": String(""), + }, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_list_inferred_variable_types.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_list_inferred_variable_types.frontend-error.ron index e2628381..e36b440f 100644 --- a/trustfall_core/test_data/tests/frontend_errors/incompatible_list_inferred_variable_types.frontend-error.ron +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_list_inferred_variable_types.frontend-error.ron @@ -1 +1 @@ -Err(FilterTypeError(IncompatibleVariableTypeRequirements("arg", "[Int]!", "[String!]!"))) +Err(IncompatibleVariableTypeRequirements("arg", "[Int]!", "[String!]!")) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_list_vs_nested_list_variable_types.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_list_vs_nested_list_variable_types.frontend-error.ron index d3925e81..93d20e9b 100644 --- a/trustfall_core/test_data/tests/frontend_errors/incompatible_list_vs_nested_list_variable_types.frontend-error.ron +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_list_vs_nested_list_variable_types.frontend-error.ron @@ -1 +1 @@ -Err(FilterTypeError(IncompatibleVariableTypeRequirements("arg", "[Int!]!", "[[Int!]!]!"))) +Err(IncompatibleVariableTypeRequirements("arg", "[Int!]!", "[[Int!]!]!")) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_filter_inferred_variable_types.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_filter_inferred_variable_types.frontend-error.ron new file mode 100644 index 00000000..cd4958de --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_filter_inferred_variable_types.frontend-error.ron @@ -0,0 +1 @@ +Err(IncompatibleVariableTypeRequirements("arg", "String", "Int!")) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_filter_inferred_variable_types.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_filter_inferred_variable_types.graphql-parsed.ron new file mode 100644 index 00000000..6a07c3d0 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_filter_inferred_variable_types.graphql-parsed.ron @@ -0,0 +1,70 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 7, + column: 9, + ), + name: "vowelsInName", + ), FieldNode( + position: Pos( + line: 7, + column: 9, + ), + name: "vowelsInName", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Len, + ), + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Add(VariableRef("arg")), + ), + output: [ + OutputDirective(), + ], + )), + )), + )), + (FieldConnection( + position: Pos( + line: 8, + column: 9, + ), + name: "name", + ), FieldNode( + position: Pos( + line: 8, + column: 9, + ), + name: "name", + filter: [ + FilterDirective( + operation: Equals((), VariableRef("arg")), + ), + ], + )), + ], + ), + ), + arguments: { + "arg": String(""), + }, +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_filter_inferred_variable_types.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_filter_inferred_variable_types.graphql.ron new file mode 100644 index 00000000..1f2771f0 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_filter_inferred_variable_types.graphql.ron @@ -0,0 +1,16 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + # There are incompatible demands for the type of `$arg` here: + # it needs to be a `String` to match the filter, + # and an `Int!` to fit the transform. This is a frontend error. + vowelsInName @transform(op: "len") @transform(op: "+", value: ["$arg"]) @output + name @filter(op: "=", value: ["$arg"]) + } +}"#, + arguments: { + "arg": String(""), + }, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_scalar_inferred_variable_types.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_scalar_inferred_variable_types.frontend-error.ron index 14f12263..aad58f86 100644 --- a/trustfall_core/test_data/tests/frontend_errors/incompatible_scalar_inferred_variable_types.frontend-error.ron +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_scalar_inferred_variable_types.frontend-error.ron @@ -1 +1 @@ -Err(FilterTypeError(IncompatibleVariableTypeRequirements("arg", "Int", "String!"))) +Err(IncompatibleVariableTypeRequirements("arg", "Int", "String!")) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_scalar_vs_list_inferred_variable_types.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_scalar_vs_list_inferred_variable_types.frontend-error.ron index aaadfe2e..5b33b1fc 100644 --- a/trustfall_core/test_data/tests/frontend_errors/incompatible_scalar_vs_list_inferred_variable_types.frontend-error.ron +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_scalar_vs_list_inferred_variable_types.frontend-error.ron @@ -1 +1 @@ -Err(FilterTypeError(IncompatibleVariableTypeRequirements("arg", "[Int]!", "String!"))) +Err(IncompatibleVariableTypeRequirements("arg", "[Int]!", "String!")) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_transform_vs_filter_inferred_variable_types.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_transform_vs_filter_inferred_variable_types.frontend-error.ron new file mode 100644 index 00000000..cd4958de --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_transform_vs_filter_inferred_variable_types.frontend-error.ron @@ -0,0 +1 @@ +Err(IncompatibleVariableTypeRequirements("arg", "String", "Int!")) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_transform_vs_filter_inferred_variable_types.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_transform_vs_filter_inferred_variable_types.graphql-parsed.ron new file mode 100644 index 00000000..2701aad8 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_transform_vs_filter_inferred_variable_types.graphql-parsed.ron @@ -0,0 +1,64 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "name", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "name", + filter: [ + FilterDirective( + operation: Equals((), VariableRef("arg")), + ), + ], + )), + (FieldConnection( + position: Pos( + line: 9, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 9, + column: 9, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Add(VariableRef("arg")), + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + ), + ), + arguments: { + "arg": String(""), + }, +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_transform_vs_filter_inferred_variable_types.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_transform_vs_filter_inferred_variable_types.graphql.ron new file mode 100644 index 00000000..a7ea1a35 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_transform_vs_filter_inferred_variable_types.graphql.ron @@ -0,0 +1,17 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + name @filter(op: "=", value: ["$arg"]) + + # There are incompatible demands for the type of `$arg` here: + # it needs to be a `String` to match the filter, + # and an `Int!` to fit the transform. This is a frontend error. + value @transform(op: "+", value: ["$arg"]) @output + } +}"#, + arguments: { + "arg": String(""), + }, +) From 99d81898356f5a0a1d56acc55209ff151ece5e04 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Tue, 25 Jun 2024 20:18:57 +0000 Subject: [PATCH 22/30] Update downstream project's schemas to include new feature. --- experiments/schemaless/src/schema_inference.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/experiments/schemaless/src/schema_inference.rs b/experiments/schemaless/src/schema_inference.rs index 3cbcfaf4..eb89414a 100644 --- a/experiments/schemaless/src/schema_inference.rs +++ b/experiments/schemaless/src/schema_inference.rs @@ -646,7 +646,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type GitHubActionsImportedStep implements _AnonType5 { _AnonField: String @@ -718,7 +718,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type GitHubActionsImportedStep implements _AnonType5 { _AnonField: String @@ -787,7 +787,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type GitHubActionsImportedStep implements _AnonType5 { _AnonField: String @@ -859,7 +859,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type GitHubActionsRunStep implements _AnonType5 { _AnonField: String @@ -938,7 +938,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type HackerNewsComment implements _AnonType2 { _AnonField: String @@ -994,7 +994,7 @@ directive @output(name: String) on FIELD directive @optional on FIELD directive @recurse(depth: Int!) on FIELD directive @fold on FIELD -directive @transform(op: String!) on FIELD +directive @transform(op: String!, value: [String!]) on FIELD type GitHubRepository implements _AnonType2 { _AnonField: String From 5a31e02a3d47d11793e01d5d04a3fc39196a9ab2 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Tue, 2 Jul 2024 00:03:04 +0000 Subject: [PATCH 23/30] Fix output type inference when using tags from optional scopes. --- trustfall_core/src/frontend/error.rs | 4 +- trustfall_core/src/frontend/filters.rs | 8 +- trustfall_core/src/frontend/mod.rs | 88 ++-- trustfall_core/src/frontend/tags.rs | 40 +- trustfall_core/src/frontend/util.rs | 108 ++++- trustfall_core/src/interpreter/execution.rs | 2 +- trustfall_core/src/interpreter/filtering.rs | 2 +- .../src/interpreter/hints/vertex_info.rs | 8 +- trustfall_core/src/interpreter/replay.rs | 19 + .../src/interpreter/transformation.rs | 6 +- trustfall_core/src/ir/mod.rs | 18 +- trustfall_core/src/nullables_interpreter.rs | 12 +- ...d_count_tag_on_nonexistent_optional.ir.ron | 2 +- ...ount_tag_on_nonexistent_optional.trace.ron | 2 +- ...onexistent_optional_tag.graphql-parsed.ron | 123 +++++ ..._with_nonexistent_optional_tag.graphql.ron | 23 + ...sform_with_nonexistent_optional_tag.ir.ron | 73 +++ ...m_with_nonexistent_optional_tag.output.ron | 21 + ...rm_with_nonexistent_optional_tag.trace.ron | 433 ++++++++++++++++++ ...output_type_is_nullable.graphql-parsed.ron | 92 ++++ ...al_tag_output_type_is_nullable.graphql.ron | 27 ++ ...ptional_tag_output_type_is_nullable.ir.ron | 58 +++ ...nal_tag_output_type_is_nullable.output.ron | 11 + ...onal_tag_output_type_is_nullable.trace.ron | 152 ++++++ ...es_nullable_output_type.graphql-parsed.ron | 91 ++++ ..._produces_nullable_output_type.graphql.ron | 25 + ...e_tag_produces_nullable_output_type.ir.ron | 57 +++ ...g_produces_nullable_output_type.output.ron | 11 + ...ag_produces_nullable_output_type.trace.ron | 151 ++++++ 29 files changed, 1585 insertions(+), 82 deletions(-) create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.trace.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.trace.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.trace.ron diff --git a/trustfall_core/src/frontend/error.rs b/trustfall_core/src/frontend/error.rs index 8c2d7d33..0aca5189 100644 --- a/trustfall_core/src/frontend/error.rs +++ b/trustfall_core/src/frontend/error.rs @@ -293,9 +293,9 @@ impl FilterTypeError { /// the tag name separately if needed. fn represent_argument(argument: &Argument, tag_name: Option<&str>) -> String { match argument { - Argument::Tag(tag) => Self::represent_tag_name_and_type( + Argument::Tag(tag, ..) => Self::represent_tag_name_and_type( tag_name.expect("tag argument without a name"), - tag.field_type(), + argument.field_type(), ), Argument::Variable(var) => { Self::represent_variable_name_and_type(&var.variable_name, &var.variable_type) diff --git a/trustfall_core/src/frontend/filters.rs b/trustfall_core/src/frontend/filters.rs index ffd7f0cb..2a92a218 100644 --- a/trustfall_core/src/frontend/filters.rs +++ b/trustfall_core/src/frontend/filters.rs @@ -7,13 +7,13 @@ use crate::{ use super::{ error::{FilterTypeError, FrontendError}, tags::{TagHandler, TagLookupError}, - util::ComponentPath, + util::QueryPath, }; #[allow(clippy::too_many_arguments)] pub(super) fn make_filter_expr( schema: &Schema, - component_path: &ComponentPath, + query_path: &QueryPath, tags: &mut TagHandler<'_>, current_vertex_vid: Vid, left_operand: OperationSubject, @@ -36,7 +36,7 @@ pub(super) fn make_filter_expr( OperatorArgument::TagRef(tag_name) => { let defined_tag = match tags.reference_tag( tag_name.as_ref(), - component_path, + query_path, current_vertex_vid, ) { Ok(defined_tag) => defined_tag, @@ -60,7 +60,7 @@ pub(super) fn make_filter_expr( } }; - Argument::Tag(defined_tag.field.clone()) + defined_tag.create_tag_argument(query_path) } }) }, diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index 0a37508f..d794fd1e 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -32,7 +32,7 @@ use self::{ filters::make_filter_expr, outputs::OutputHandler, tags::{TagHandler, TagLookupError}, - util::{get_underlying_named_type, ComponentPath}, + util::{get_underlying_named_type, QueryPath}, validation::validate_query_against_schema, }; @@ -230,7 +230,7 @@ fn make_edge_parameters( #[allow(clippy::too_many_arguments)] fn make_local_field_filter_expr( schema: &Schema, - component_path: &ComponentPath, + query_path: &QueryPath, tags: &mut TagHandler<'_>, current_vertex_vid: Vid, property_name: &Arc, @@ -241,7 +241,7 @@ fn make_local_field_filter_expr( filters::make_filter_expr( schema, - component_path, + query_path, tags, current_vertex_vid, OperationSubject::LocalField(left), @@ -272,7 +272,7 @@ pub fn make_ir_for_query(schema: &Schema, query: &Query) -> Result Result( query: &'query Query, vid_maker: &mut V, eid_maker: &mut E, - component_path: &mut ComponentPath, + query_path: &mut QueryPath, output_handler: &mut OutputHandler<'query>, tags: &mut TagHandler<'query>, parent_vid: Option, @@ -559,7 +559,7 @@ where &mut folds, &mut property_names_by_vertex, &mut properties, - component_path, + query_path, output_handler, tags, None, @@ -577,7 +577,7 @@ where &property_names_by_vertex, &properties, tags, - component_path, + query_path, *vid, uncoerced_type_name, field_node, @@ -819,7 +819,7 @@ fn make_vertex<'query>( property_names_by_vertex: &BTreeMap>>, properties: &BTreeMap<(Vid, Arc), (Arc, Type, SmallVec<[&'query FieldNode; 1]>)>, tags: &mut TagHandler<'_>, - component_path: &ComponentPath, + query_path: &QueryPath, vid: Vid, uncoerced_type_name: &Arc, field_node: &'query FieldNode, @@ -831,7 +831,7 @@ fn make_vertex<'query>( // // If the current vertex is not the root of a fold, then outputs are not allowed // and we should report an error. - let is_fold_root = component_path.is_component_root(vid); + let is_fold_root = query_path.is_component_root(vid); if !is_fold_root && !field_node.output.is_empty() { errors.push(FrontendError::UnsupportedEdgeOutput(field_node.name.as_ref().to_owned())); } @@ -881,7 +881,7 @@ fn make_vertex<'query>( for filter_directive in property_field.filter.iter() { match make_local_field_filter_expr( schema, - component_path, + query_path, tags, vid, property_name, @@ -904,7 +904,7 @@ fn make_vertex<'query>( let (next_transform, next_type) = match extract_property_like_transform_from_directive( tags, - component_path, + query_path, vid, &transform_group.transform, property_name, @@ -942,7 +942,7 @@ fn make_vertex<'query>( match filters::make_filter_expr( schema, - component_path, + query_path, tags, vid, OperationSubject::TransformedField(transformed_field), @@ -981,7 +981,7 @@ fn fill_in_vertex_data<'schema, 'query, V, E>( folds: &mut BTreeMap>, property_names_by_vertex: &mut BTreeMap>>, properties: &mut BTreeMap<(Vid, Arc), (Arc, Type, SmallVec<[&'query FieldNode; 1]>)>, - component_path: &mut ComponentPath, + query_path: &mut QueryPath, output_handler: &mut OutputHandler<'query>, tags: &mut TagHandler<'query>, parent_vid: Option, @@ -1059,7 +1059,7 @@ where query, vid_maker, eid_maker, - component_path, + query_path, output_handler, tags, fold_group, @@ -1085,6 +1085,8 @@ where } } } else { + query_path.push_traversal(next_vid, connection.optional.is_some()); + edges .insert_or_error(next_eid, (current_vid, next_vid, connection)) .expect("Unexpectedly encountered duplicate eid"); @@ -1099,7 +1101,7 @@ where folds, property_names_by_vertex, properties, - component_path, + query_path, output_handler, tags, Some(current_vid), @@ -1110,6 +1112,8 @@ where ) { errors.extend(e); } + + query_path.pop_traversal(next_vid); } output_handler.end_nested_scope(next_vid); @@ -1121,7 +1125,7 @@ where fill_in_property_data( property_names_by_vertex, properties, - component_path, + query_path, output_handler, tags, current_vid, @@ -1148,7 +1152,7 @@ where fn fill_in_property_data<'query>( property_names_by_vertex: &mut BTreeMap>>, properties: &mut BTreeMap<(Vid, Arc), (Arc, Type, SmallVec<[&'query FieldNode; 1]>)>, - component_path: &mut ComponentPath, + query_path: &mut QueryPath, output_handler: &mut OutputHandler<'query>, tags: &mut TagHandler<'query>, current_vid: Vid, @@ -1260,7 +1264,7 @@ fn fill_in_property_data<'query>( ¤t_type, ); - if let Err(e) = tags.register_tag(tag_name, tag_field, component_path) { + if let Err(e) = tags.register_tag(tag_name, tag_field, query_path) { errors.push(FrontendError::MultipleTagsWithSameName(tag_name.to_string())); } } @@ -1268,7 +1272,7 @@ fn fill_in_property_data<'query>( if let Some(transform_group) = next_transform_group { let (next_transform, next_type) = match extract_property_like_transform_from_directive( tags, - component_path, + query_path, current_vid, &transform_group.transform, &property_node.name, @@ -1351,7 +1355,7 @@ impl From for TransformErrorSource { fn extract_property_like_transform_from_directive( tags: &mut TagHandler<'_>, - use_path: &ComponentPath, + use_path: &QueryPath, use_vid: Vid, transform_directive: &TransformDirective, property_name: &str, @@ -1418,7 +1422,7 @@ fn extract_property_like_transform_from_directive( fn extract_transform_on_fold_count_from_directive( tags: &mut TagHandler<'_>, - use_path: &ComponentPath, + use_path: &QueryPath, use_vid: Vid, transform_directive: &TransformDirective, edge_name: &str, @@ -1484,7 +1488,7 @@ fn extract_transform_on_fold_count_from_directive( fn extract_transform_and_next_type_from_directive( tags: &mut TagHandler<'_>, - use_path: &ComponentPath, + use_path: &QueryPath, use_vid: Vid, transform_directive: &TransformDirective, type_so_far: &Type, @@ -1532,7 +1536,7 @@ fn extract_transform_and_next_type_from_directive( } let required_type = Type::new_named_type("Int", true); - let transform = Transform::Add(resolve_transform_argument( + let transform_argument = resolve_transform_argument( tags, use_path, use_vid, @@ -1540,9 +1544,13 @@ fn extract_transform_and_next_type_from_directive( required_type, true, &transform_directive.kind, - )?); + )?; + let next_type_nullable = + type_so_far.nullable() || transform_argument.field_type().nullable(); - let next_type = Type::new_named_type("Int", type_so_far.nullable()); + let transform = Transform::Add(transform_argument); + + let next_type = Type::new_named_type("Int", next_type_nullable); Ok((transform, next_type)) } TransformOp::AddF(arg) => { @@ -1558,7 +1566,7 @@ fn extract_transform_and_next_type_from_directive( } let required_type = Type::new_named_type("Float", true); - let transform = Transform::AddF(resolve_transform_argument( + let transform_argument = resolve_transform_argument( tags, use_path, use_vid, @@ -1566,9 +1574,13 @@ fn extract_transform_and_next_type_from_directive( required_type, true, &transform_directive.kind, - )?); + )?; + let next_type_nullable = + type_so_far.nullable() || transform_argument.field_type().nullable(); + + let transform = Transform::AddF(transform_argument); - let next_type = Type::new_named_type("Int", type_so_far.nullable()); + let next_type = Type::new_named_type("Float", next_type_nullable); Ok((transform, next_type)) } TransformOp::Count => Err(invalid_count_op().into()), @@ -1577,7 +1589,7 @@ fn extract_transform_and_next_type_from_directive( fn resolve_transform_argument( tags: &mut TagHandler<'_>, - use_path: &ComponentPath, + use_path: &QueryPath, use_vid: Vid, arg: &OperatorArgument, required_type: Type, @@ -1607,7 +1619,7 @@ fn resolve_transform_argument( ) .into()) } else { - Ok(Argument::Tag(tag_entry.field.to_owned())) + Ok(tag_entry.create_tag_argument(use_path)) } } Err(e) => Err(TransformErrorSource::Tag(e)), @@ -1621,7 +1633,7 @@ fn make_fold<'schema, 'query, V, E>( query: &'query Query, vid_maker: &mut V, eid_maker: &mut E, - component_path: &mut ComponentPath, + query_path: &mut QueryPath, output_handler: &mut OutputHandler<'query>, tags: &mut TagHandler<'query>, fold_group: &'query FoldGroup, @@ -1639,7 +1651,7 @@ where V: Iterator, E: Iterator, { - component_path.push(starting_vid); + query_path.push_fold(starting_vid); tags.begin_subcomponent(starting_vid); let mut errors = vec![]; @@ -1648,7 +1660,7 @@ where query, vid_maker, eid_maker, - component_path, + query_path, output_handler, tags, Some(parent_vid), @@ -1657,8 +1669,8 @@ where starting_post_coercion_type, starting_field, )?; - component_path.pop(starting_vid); let imported_tags = tags.end_subcomponent(starting_vid); + query_path.pop_fold(starting_vid); if !starting_field.output.is_empty() { // The edge has @fold @output but no @transform. @@ -1679,7 +1691,7 @@ where let (field_ref, subject) = if let Some(base_field) = maybe_base_field.as_ref() { let (next_transform, next_type) = match extract_transform_on_fold_count_from_directive( tags, - component_path, + query_path, parent_vid, &transform_group.transform, edge_name.as_ref(), @@ -1751,7 +1763,7 @@ where for filter_directive in &transform_group.filter { match make_filter_expr( schema, - component_path, + query_path, tags, starting_vid, subject.clone(), @@ -1807,7 +1819,7 @@ where for tag_directive in &transform_group.tag { let tag_name = tag_directive.name.as_ref().map(|x| x.as_ref()); if let Some(tag_name) = tag_name { - if let Err(e) = tags.register_tag(tag_name, field_ref.clone(), component_path) { + if let Err(e) = tags.register_tag(tag_name, field_ref.clone(), query_path) { errors.push(FrontendError::MultipleTagsWithSameName(tag_name.to_string())); } } else { diff --git a/trustfall_core/src/frontend/tags.rs b/trustfall_core/src/frontend/tags.rs index 67e38f48..8647ca1f 100644 --- a/trustfall_core/src/frontend/tags.rs +++ b/trustfall_core/src/frontend/tags.rs @@ -3,9 +3,9 @@ use std::{ fmt::Debug, }; -use super::util::ComponentPath; +use super::util::QueryPath; use crate::{ - ir::{FieldRef, Vid}, + ir::{Argument, FieldRef, Vid}, util::{BTreeMapOccupiedError, BTreeMapTryInsertExt}, }; @@ -20,13 +20,31 @@ pub(super) struct TagHandler<'a> { pub(super) struct TagEntry<'a> { pub(super) name: &'a str, pub(super) field: FieldRef, - pub(super) path: ComponentPath, + pub(super) path: QueryPath, } impl<'a> TagEntry<'a> { - fn new(name: &'a str, field: FieldRef, path: ComponentPath) -> Self { + fn new(name: &'a str, field: FieldRef, path: QueryPath) -> Self { Self { name, field, path } } + + pub(super) fn create_tag_argument(&self, use_path: &QueryPath) -> Argument { + let overridden_type = 'override_type: { + let underlying_type = self.field.field_type(); + if !underlying_type.nullable() { + for (_, modifier) in self.path.diff_suffix(use_path) { + if modifier.is_optional() { + break 'override_type Some(underlying_type.with_nullability(true)); + } + } + break 'override_type None; + } else { + break 'override_type None; + } + }; + + Argument::Tag(self.field.to_owned(), overridden_type) + } } impl<'a> TagHandler<'a> { @@ -39,7 +57,7 @@ impl<'a> TagHandler<'a> { &mut self, name: &'a str, field: FieldRef, - path: &ComponentPath, + path: &QueryPath, ) -> Result<(), BTreeMapOccupiedError<'_, &'a str, TagEntry<'a>>> { self.tags.insert_or_error(name, TagEntry::new(name, field, path.clone()))?; @@ -59,26 +77,28 @@ impl<'a> TagHandler<'a> { pub(super) fn reference_tag( &mut self, name: &str, - use_path: &ComponentPath, + use_path: &QueryPath, use_vid: Vid, ) -> Result<&TagEntry<'_>, TagLookupError> { let entry = self.tags.get(name).ok_or_else(|| TagLookupError::UndefinedTag(name.to_string()))?; - if entry.path.is_parent(use_path) { + if entry.path.is_same_or_parent_component(use_path) { if entry.field.defined_at() > use_vid { return Err(TagLookupError::TagUsedBeforeDefinition(name.to_string())); } - if &entry.path != use_path { + let tag_components = entry.path.component_roots(); + let use_components = use_path.component_roots(); + if tag_components != use_components { // The tag is used inside a fold and imported from an outer component. // Mark it as imported at the appropriate level. - let importing_component_root = use_path[entry.path.len()]; + let importing_component_root = use_components[entry.path.component_len()]; // The -1 in the index calculation is because the root component // cannot import tags -- it has no parent component to import from. let (component_root, imported_tags) = - self.component_imported_tags.get_mut(entry.path.len() - 1).unwrap(); + self.component_imported_tags.get_mut(entry.path.component_len() - 1).unwrap(); assert_eq!(*component_root, importing_component_root); imported_tags.push(entry.field.clone()); } diff --git a/trustfall_core/src/frontend/util.rs b/trustfall_core/src/frontend/util.rs index da7a02eb..ec37c3e8 100644 --- a/trustfall_core/src/frontend/util.rs +++ b/trustfall_core/src/frontend/util.rs @@ -1,5 +1,3 @@ -use std::ops::Index; - use async_graphql_parser::types::{BaseType, Type}; use async_graphql_value::Name; @@ -17,6 +15,8 @@ pub(super) fn get_underlying_named_type(t: &Type) -> &Name { } } +/// The list of component root vertices that have to be traversed in order to reach +/// the component that contains the location of interest. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub(super) struct ComponentPath { path: Vec, @@ -41,7 +41,7 @@ impl ComponentPath { /// Will panic if the popped value is not the provided `component_start_vid` pub(super) fn pop(&mut self, component_start_vid: Vid) { let popped_vid = self.path.pop().unwrap(); - assert_eq!(popped_vid, component_start_vid); + debug_assert_eq!(popped_vid, component_start_vid); } pub(super) fn is_parent(&self, other: &ComponentPath) -> bool { @@ -61,11 +61,103 @@ impl ComponentPath { } } -impl Index for ComponentPath { - type Output = Vid; +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub(super) struct QueryPath { + component_path: ComponentPath, + vertices: Vec<(Vid, TraversalModifier)>, +} - #[inline(always)] - fn index(&self, index: usize) -> &Self::Output { - &self.path[index] +impl QueryPath { + pub(super) fn new(starting_vid: Vid) -> Self { + Self { + component_path: ComponentPath::new(starting_vid), + vertices: vec![(starting_vid, TraversalModifier::NONE)], + } + } + + pub(super) fn component_roots(&self) -> &[Vid] { + self.component_path.path.as_slice() + } + + pub(super) fn is_component_root(&self, vid: Vid) -> bool { + self.component_path.is_component_root(vid) + } + + pub(super) fn push_fold(&mut self, fold_root_vid: Vid) { + self.component_path.push(fold_root_vid); + self.vertices.push((fold_root_vid, TraversalModifier::FOLDED)); + } + + pub(super) fn pop_fold(&mut self, fold_root_vid: Vid) { + let (vid, modifier) = self.vertices.pop().expect("no vertex to pop, this is a bug"); + debug_assert_eq!(vid, fold_root_vid, "unexpected vertex was popped: {fold_root_vid:?} != {vid:?} with modifier {modifier:?} for {self:?}"); + debug_assert!(modifier.is_folded()); + + self.component_path.pop(fold_root_vid); + } + + pub(super) fn push_traversal(&mut self, next_vid: Vid, optional: bool) { + let modifier = if optional { TraversalModifier::OPTIONAL } else { TraversalModifier::NONE }; + self.vertices.push((next_vid, modifier)); + } + + pub(super) fn pop_traversal(&mut self, pop_vid: Vid) { + let (vid, modifier) = self.vertices.pop().expect("no vertex to pop, this is a bug"); + debug_assert_eq!(vid, pop_vid, "unexpected vertex was popped: {pop_vid:?} != {vid:?} with modifier {modifier:?} for {self:?}"); + } + + pub(super) fn is_same_or_parent_component(&self, other: &QueryPath) -> bool { + self.component_path.is_parent(&other.component_path) + } + + pub(super) fn component_len(&self) -> usize { + self.component_path.len() + } + + pub(super) fn diff_suffix(&self, other: &QueryPath) -> &'_ [(Vid, TraversalModifier)] { + let slice = self.vertices.as_slice(); + let mut iter = slice.iter(); + let mut count = 0; + + for (diff_vid, diff_modifier) in &other.vertices { + let Some((vid, modifier)) = iter.next() else { + break; + }; + + if vid == diff_vid { + debug_assert_eq!(modifier, diff_modifier, "found the same vid with different modifiers in two QueryPath values: {self:?} {other:?}"); + count += 1; + } else { + break; + } + } + + slice.split_at(count).1 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub(super) struct TraversalModifier { + mask: i8, +} + +impl TraversalModifier { + const OPTIONAL_MASK: i8 = 1; + const FOLDED_MASK: i8 = 2; + + pub(super) const NONE: Self = Self { mask: 0 }; + + pub(super) const OPTIONAL: Self = Self { mask: Self::OPTIONAL_MASK }; + + pub(super) const FOLDED: Self = Self { mask: Self::FOLDED_MASK }; + + #[inline] + pub(super) fn is_optional(&self) -> bool { + (self.mask & Self::OPTIONAL_MASK) != 0 + } + + #[inline] + pub(super) fn is_folded(&self) -> bool { + (self.mask & Self::FOLDED_MASK) != 0 } } diff --git a/trustfall_core/src/interpreter/execution.rs b/trustfall_core/src/interpreter/execution.rs index 1946ffd5..d93a1b32 100644 --- a/trustfall_core/src/interpreter/execution.rs +++ b/trustfall_core/src/interpreter/execution.rs @@ -635,7 +635,7 @@ fn compute_fold<'query, AdapterT: Adapter<'query> + 'query>( // It currently assumes that use of tags on `@fold @transform(op: "count")` // can only happen within the direct parent component of that fold. vertex.filters.iter().any(|filter| { - let Some(Argument::Tag(field_ref)) = filter.right() else { + let Some(Argument::Tag(field_ref, ..)) = filter.right() else { return false; }; diff --git a/trustfall_core/src/interpreter/filtering.rs b/trustfall_core/src/interpreter/filtering.rs index 4e1e1eb6..2958bed0 100644 --- a/trustfall_core/src/interpreter/filtering.rs +++ b/trustfall_core/src/interpreter/filtering.rs @@ -278,7 +278,7 @@ pub(super) fn apply_filter<'query, AdapterT: Adapter<'query>>( let right_value = query_arguments[var.variable_name.as_ref()].to_owned(); apply_filter_with_static_argument_value(filter, right_value, iterator) } - Some(Argument::Tag(field_ref)) => { + Some(Argument::Tag(field_ref, ..)) => { let argument_value_iterator = compute_tag_with_separate_value::( adapter, carrier, diff --git a/trustfall_core/src/interpreter/hints/vertex_info.rs b/trustfall_core/src/interpreter/hints/vertex_info.rs index 94f01aa5..c9b86334 100644 --- a/trustfall_core/src/interpreter/hints/vertex_info.rs +++ b/trustfall_core/src/interpreter/hints/vertex_info.rs @@ -198,7 +198,7 @@ impl VertexInfo for T { v.filters .iter() .filter_map(|f| match f.right() { - Some(Argument::Tag(FieldRef::ContextField(ctx))) => { + Some(Argument::Tag(FieldRef::ContextField(ctx), ..)) => { if current_vertex.vid == ctx.vertex_id { Some(ctx.field_name.clone()) } else { @@ -310,11 +310,11 @@ impl VertexInfo for T { | Operation::GreaterThanOrEqual(..) | Operation::OneOf(..) ) && match op.right() { - Some(Argument::Tag(FieldRef::ContextField(ctx))) => { + Some(Argument::Tag(FieldRef::ContextField(ctx), ..)) => { // Ensure the vertex holding the @tag has already been computed. resolved_range.contains(&ctx.vertex_id) } - Some(Argument::Tag(FieldRef::FoldSpecificField(fsf))) => { + Some(Argument::Tag(FieldRef::FoldSpecificField(fsf), ..)) => { // Ensure the fold holding the @tag has already been computed. resolved_range.contains(&fsf.fold_root_vid) } @@ -384,7 +384,7 @@ impl VertexInfo for T { ) }; - let field = filter_to_use + let (field, _) = filter_to_use .right() .expect("filter did not have an operand") .as_tag() diff --git a/trustfall_core/src/interpreter/replay.rs b/trustfall_core/src/interpreter/replay.rs index 23a6225c..03af64b5 100644 --- a/trustfall_core/src/interpreter/replay.rs +++ b/trustfall_core/src/interpreter/replay.rs @@ -578,6 +578,7 @@ mod tests { use crate::{ filesystem_interpreter::FilesystemVertex, interpreter::replay::assert_interpreted_results, + nullables_interpreter::NullablesVertex, numbers_interpreter::NumbersVertex, test_types::{ TestIRQuery, TestIRQueryResult, TestInterpreterOutputData, TestInterpreterOutputTrace, @@ -632,6 +633,23 @@ mod tests { } } + fn check_nullables_trace( + expected_ir: TestIRQuery, + input_data: &str, + test_outputs: TestInterpreterOutputData, + ) { + match ron::from_str::>(input_data) { + Ok(test_data) => { + assert_eq!(expected_ir.schema_name, "nullables"); + assert_eq!(test_data.schema_name, "nullables"); + check_trace(expected_ir, test_data, test_outputs); + } + Err(e) => { + unreachable!("failed to parse trace file: {e}"); + } + } + } + #[parameterize("trustfall_core/test_data/tests/valid_queries")] fn parameterized_tester(base: &Path, stem: &str) { let mut input_path = PathBuf::from(base); @@ -655,6 +673,7 @@ mod tests { match expected_ir.schema_name.as_str() { "filesystem" => check_filesystem_trace(expected_ir, input_data.as_str(), test_outputs), "numbers" => check_numbers_trace(expected_ir, input_data.as_str(), test_outputs), + "nullables" => check_nullables_trace(expected_ir, input_data.as_str(), test_outputs), _ => unreachable!("{}", expected_ir.schema_name), } } diff --git a/trustfall_core/src/interpreter/transformation.rs b/trustfall_core/src/interpreter/transformation.rs index a027925f..b6bffc6e 100644 --- a/trustfall_core/src/interpreter/transformation.rs +++ b/trustfall_core/src/interpreter/transformation.rs @@ -70,7 +70,7 @@ pub(super) fn push_transform_argument_tag_values_onto_stack<'query, AdapterT: Ad for transform in transforms.iter().rev() { match transform { Transform::Add(op) | Transform::AddF(op) => match op { - Argument::Tag(tag) => { + Argument::Tag(tag, ..) => { iterator = tag_func(carrier, tag, iterator); } Argument::Variable(..) => {} @@ -112,7 +112,7 @@ fn apply_one_transform( let operand = &variables[&var.variable_name]; apply_add_transform(value, operand) } - Argument::Tag(_) => { + Argument::Tag(..) => { let operand = stack.pop().expect( "empty stack while attempting to resolve transform operand: {transform:?}", ); @@ -124,7 +124,7 @@ fn apply_one_transform( let operand = &variables[&var.variable_name]; apply_fadd_transform(value, operand) } - Argument::Tag(_) => { + Argument::Tag(..) => { let operand = stack.pop().expect( "empty stack while attempting to resolve transform operand: {transform:?}", ); diff --git a/trustfall_core/src/ir/mod.rs b/trustfall_core/src/ir/mod.rs index 69e5d1da..2803daae 100644 --- a/trustfall_core/src/ir/mod.rs +++ b/trustfall_core/src/ir/mod.rs @@ -386,7 +386,16 @@ impl FieldRef { /// produces a value like `Operation::Equals(..., Argument::Variable(...))`. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Argument { - Tag(FieldRef), + /// A tag referencing another value from the query, plus an optional overridden type for it. + /// + /// The overridden type is set in cases where the tag at the time of use may have a different + /// type from the underlying value. For example, if the tag references a value from inside + /// an `@optional` block, the nullability of the tagged value may depend on where it is used: + /// even if the underlying value has non-nullable type, using it outside its `@optional` block + /// would cause it to have nullable type in order to handle the optional edge not being present. + Tag(FieldRef, #[serde(default, skip_serializing_if = "Option::is_none")] Option), + + /// A variable whose value is supplied at runtime. Variable(VariableRef), } @@ -398,9 +407,9 @@ impl Argument { } } - pub(crate) fn as_tag(&self) -> Option<&FieldRef> { + pub(crate) fn as_tag(&self) -> Option<(&FieldRef, &Option)> { match self { - Argument::Tag(t) => Some(t), + Argument::Tag(r, t) => Some((r, t)), Argument::Variable(_) => None, } } @@ -419,7 +428,8 @@ impl Argument { pub fn field_type(&self) -> &Type { match self { - Argument::Tag(tag) => tag.field_type(), + Argument::Tag(_, Some(overridden_type)) => overridden_type, + Argument::Tag(tag, None) => tag.field_type(), Argument::Variable(var) => &var.variable_type, } } diff --git a/trustfall_core/src/nullables_interpreter.rs b/trustfall_core/src/nullables_interpreter.rs index 2c692a1e..63b0e0f7 100644 --- a/trustfall_core/src/nullables_interpreter.rs +++ b/trustfall_core/src/nullables_interpreter.rs @@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize}; use crate::{ interpreter::{ - Adapter, AsVertex, ContextIterator, ContextOutcomeIterator, ResolveEdgeInfo, ResolveInfo, - VertexIterator, + Adapter, AsVertex, ContextIterator, ContextOutcomeIterator, DataContext, ResolveEdgeInfo, + ResolveInfo, VertexIterator, }, ir::{EdgeParameters, FieldValue}, }; @@ -36,7 +36,7 @@ impl<'a> Adapter<'a> for NullablesAdapter { property_name: &Arc, resolve_info: &ResolveInfo, ) -> ContextOutcomeIterator<'a, V, FieldValue> { - unimplemented!() + Box::new(contexts.map(|ctx| (ctx, FieldValue::Null))) } fn resolve_neighbors + 'a>( @@ -47,7 +47,9 @@ impl<'a> Adapter<'a> for NullablesAdapter { parameters: &EdgeParameters, resolve_info: &ResolveEdgeInfo, ) -> ContextOutcomeIterator<'a, V, VertexIterator<'a, Self::Vertex>> { - unimplemented!() + Box::new(contexts.map(|ctx| -> (DataContext, VertexIterator<'a, Self::Vertex>) { + (ctx, Box::new(std::iter::empty())) + })) } fn resolve_coercion + 'a>( @@ -57,6 +59,6 @@ impl<'a> Adapter<'a> for NullablesAdapter { coerce_to_type: &Arc, resolve_info: &ResolveInfo, ) -> ContextOutcomeIterator<'a, V, bool> { - unimplemented!() + Box::new(contexts.map(|ctx| (ctx, false))) } } diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.ir.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.ir.ron index b6893521..9c3bd7be 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.ir.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.ir.ron @@ -28,7 +28,7 @@ Ok(TestIRQuery( fold_eid: Eid(3), fold_root_vid: Vid(4), kind: Count, - )))), + )), Some("Int"))), ], ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.trace.ron b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.trace.ron index 7cb4fafe..61ce86d3 100644 --- a/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.trace.ron +++ b/trustfall_core/test_data/tests/valid_queries/fold_count_tag_on_nonexistent_optional.trace.ron @@ -475,7 +475,7 @@ TestInterpreterOutputTrace( fold_eid: Eid(3), fold_root_vid: Vid(4), kind: Count, - )))), + )), Some("Int"))), ], ), }, diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.graphql-parsed.ron new file mode 100644 index 00000000..564b2031 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.graphql-parsed.ron @@ -0,0 +1,123 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "One", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "One", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "predecessor", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "predecessor", + connections: [ + (FieldConnection( + position: Pos( + line: 5, + column: 13, + ), + name: "predecessor", + optional: Some(OptionalDirective()), + ), FieldNode( + position: Pos( + line: 5, + column: 13, + ), + name: "predecessor", + connections: [ + (FieldConnection( + position: Pos( + line: 6, + column: 17, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 6, + column: 17, + ), + name: "value", + tag: [ + TagDirective( + name: Some("optional_nonexistent"), + ), + ], + )), + ], + )), + ], + )), + (FieldConnection( + position: Pos( + line: 10, + column: 9, + ), + name: "successor", + ), FieldNode( + position: Pos( + line: 10, + column: 9, + ), + name: "successor", + connections: [ + (FieldConnection( + position: Pos( + line: 11, + column: 13, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 11, + column: 13, + ), + name: "value", + output: [ + OutputDirective(), + ], + )), + (FieldConnection( + position: Pos( + line: 12, + column: 13, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 12, + column: 13, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Add(TagRef("optional_nonexistent")), + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.graphql.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.graphql.ron new file mode 100644 index 00000000..1d6462ec --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.graphql.ron @@ -0,0 +1,23 @@ +TestGraphQLQuery ( + schema_name: "numbers", + // This query's @optional edge doesn't exist for the vertex in question. + // + // It checks that @transform using a @tag from a non-existent optional vertex + // produces the correct result: `null`. + query: r#" +{ + One { + predecessor { + predecessor @optional { + value @tag(name: "optional_nonexistent") + } + } + + successor { + value @output # this is `2` + value @transform(op: "+", value: ["%optional_nonexistent"]) @output # this is `null` + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.ir.ron new file mode 100644 index 00000000..063cc862 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.ir.ron @@ -0,0 +1,73 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "One", + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + ), + Vid(2): IRVertex( + vid: Vid(2), + type_name: "Number", + ), + Vid(3): IRVertex( + vid: Vid(3), + type_name: "Number", + ), + Vid(4): IRVertex( + vid: Vid(4), + type_name: "Number", + ), + }, + edges: { + Eid(1): IREdge( + eid: Eid(1), + from_vid: Vid(1), + to_vid: Vid(2), + edge_name: "predecessor", + ), + Eid(2): IREdge( + eid: Eid(2), + from_vid: Vid(2), + to_vid: Vid(3), + edge_name: "predecessor", + optional: true, + ), + Eid(3): IREdge( + eid: Eid(3), + from_vid: Vid(1), + to_vid: Vid(4), + edge_name: "successor", + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(4), + field_name: "value", + field_type: "Int", + )), + "valueadd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(4), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(3), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )), + }, + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.output.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.output.ron new file mode 100644 index 00000000..9f83971e --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.output.ron @@ -0,0 +1,21 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "value": Output( + name: "value", + value_type: "Int", + vid: Vid(4), + ), + "valueadd": Output( + name: "valueadd", + value_type: "Int", + vid: Vid(4), + ), + }, + results: [ + { + "value": Int64(2), + "valueadd": Null, + }, + ], +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.trace.ron new file mode 100644 index 00000000..aec6a3e4 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag.trace.ron @@ -0,0 +1,433 @@ +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(ResolveNeighbors(Vid(1), "Number", Eid(1))), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveNeighbors(Vid(2), "Number", Eid(2))), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: None, + content: Call(ResolveNeighbors(Vid(1), "Number", Eid(3))), + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: None, + content: Call(ResolveProperty(Vid(4), "Number", "value")), + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: None, + content: Call(ResolveProperty(Vid(3), "Number", "value")), + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: None, + content: Call(ResolveProperty(Vid(4), "Number", "value")), + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: Some(Opid(7)), + content: AdvanceInputIterator, + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(6)), + content: AdvanceInputIterator, + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(5)), + content: AdvanceInputIterator, + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Neither(NeitherNumber(1)))), + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + )), + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + ))), + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(16)), + content: YieldFrom(ResolveNeighborsInner(0, Neither(NeitherNumber(0)))), + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + }, + )), + ), + Opid(19): TraceOp( + opid: Opid(19), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + }, + ))), + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: Some(Opid(19)), + content: OutputIteratorExhausted, + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: Some(Opid(4)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + }, + )), + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: Some(Opid(4)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + }, + ))), + ), + Opid(23): TraceOp( + opid: Opid(23), + parent_opid: Some(Opid(22)), + content: YieldFrom(ResolveNeighborsInner(0, Prime(PrimeNumber(2)))), + ), + Opid(24): TraceOp( + opid: Opid(24), + parent_opid: Some(Opid(5)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + Vid(4): Some(Prime(PrimeNumber(2))), + }, + )), + ), + Opid(25): TraceOp( + opid: Opid(25), + parent_opid: Some(Opid(5)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + Vid(4): Some(Prime(PrimeNumber(2))), + }, + ), Int64(2))), + ), + Opid(26): TraceOp( + opid: Opid(26), + parent_opid: Some(Opid(6)), + content: YieldInto(SerializableContext( + active_vertex: None, + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + Vid(4): Some(Prime(PrimeNumber(2))), + }, + values: [ + Int64(2), + ], + )), + ), + Opid(27): TraceOp( + opid: Opid(27), + parent_opid: Some(Opid(6)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: None, + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + Vid(4): Some(Prime(PrimeNumber(2))), + }, + values: [ + Int64(2), + ], + ), Null)), + ), + Opid(28): TraceOp( + opid: Opid(28), + parent_opid: Some(Opid(7)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + Vid(4): Some(Prime(PrimeNumber(2))), + }, + values: [ + Int64(2), + Null, + ], + )), + ), + Opid(29): TraceOp( + opid: Opid(29), + parent_opid: Some(Opid(7)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + Vid(4): Some(Prime(PrimeNumber(2))), + }, + values: [ + Int64(2), + Null, + ], + ), Int64(2))), + ), + Opid(30): TraceOp( + opid: Opid(30), + parent_opid: None, + content: ProduceQueryResult({ + "value": Int64(2), + "valueadd": Null, + }), + ), + Opid(31): TraceOp( + opid: Opid(31), + parent_opid: Some(Opid(7)), + content: AdvanceInputIterator, + ), + Opid(32): TraceOp( + opid: Opid(32), + parent_opid: Some(Opid(6)), + content: AdvanceInputIterator, + ), + Opid(33): TraceOp( + opid: Opid(33), + parent_opid: Some(Opid(5)), + content: AdvanceInputIterator, + ), + Opid(34): TraceOp( + opid: Opid(34), + parent_opid: Some(Opid(22)), + content: OutputIteratorExhausted, + ), + Opid(35): TraceOp( + opid: Opid(35), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(36): TraceOp( + opid: Opid(36), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(37): TraceOp( + opid: Opid(37), + parent_opid: Some(Opid(16)), + content: OutputIteratorExhausted, + ), + Opid(38): TraceOp( + opid: Opid(38), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(39): TraceOp( + opid: Opid(39), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(40): TraceOp( + opid: Opid(40), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(41): TraceOp( + opid: Opid(41), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(42): TraceOp( + opid: Opid(42), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(43): TraceOp( + opid: Opid(43), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + Opid(44): TraceOp( + opid: Opid(44), + parent_opid: Some(Opid(4)), + content: InputIteratorExhausted, + ), + Opid(45): TraceOp( + opid: Opid(45), + parent_opid: Some(Opid(4)), + content: OutputIteratorExhausted, + ), + Opid(46): TraceOp( + opid: Opid(46), + parent_opid: Some(Opid(5)), + content: InputIteratorExhausted, + ), + Opid(47): TraceOp( + opid: Opid(47), + parent_opid: Some(Opid(5)), + content: OutputIteratorExhausted, + ), + Opid(48): TraceOp( + opid: Opid(48), + parent_opid: Some(Opid(6)), + content: InputIteratorExhausted, + ), + Opid(49): TraceOp( + opid: Opid(49), + parent_opid: Some(Opid(6)), + content: OutputIteratorExhausted, + ), + Opid(50): TraceOp( + opid: Opid(50), + parent_opid: Some(Opid(7)), + content: InputIteratorExhausted, + ), + Opid(51): TraceOp( + opid: Opid(51), + parent_opid: Some(Opid(7)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "One", + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + ), + Vid(2): IRVertex( + vid: Vid(2), + type_name: "Number", + ), + Vid(3): IRVertex( + vid: Vid(3), + type_name: "Number", + ), + Vid(4): IRVertex( + vid: Vid(4), + type_name: "Number", + ), + }, + edges: { + Eid(1): IREdge( + eid: Eid(1), + from_vid: Vid(1), + to_vid: Vid(2), + edge_name: "predecessor", + ), + Eid(2): IREdge( + eid: Eid(2), + from_vid: Vid(2), + to_vid: Vid(3), + edge_name: "predecessor", + optional: true, + ), + Eid(3): IREdge( + eid: Eid(3), + from_vid: Vid(1), + to_vid: Vid(4), + edge_name: "successor", + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(4), + field_name: "value", + field_type: "Int", + )), + "valueadd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(4), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(3), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )), + }, + ), + ), + ), +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.graphql-parsed.ron new file mode 100644 index 00000000..0f58f04b --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.graphql-parsed.ron @@ -0,0 +1,92 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "nullables", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "NonNullMainType", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "NonNullMainType", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "neighbor", + optional: Some(OptionalDirective()), + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "neighbor", + connections: [ + (FieldConnection( + position: Pos( + line: 7, + column: 13, + ), + name: "nonNullInteger", + ), FieldNode( + position: Pos( + line: 7, + column: 13, + ), + name: "nonNullInteger", + tag: [ + TagDirective( + name: Some("tagged"), + ), + ], + )), + ], + )), + (FieldConnection( + position: Pos( + line: 10, + column: 9, + ), + name: "nonNullNeighbor", + ), FieldNode( + position: Pos( + line: 10, + column: 9, + ), + name: "nonNullNeighbor", + connections: [ + (FieldConnection( + position: Pos( + line: 12, + column: 13, + ), + name: "nonNullInteger", + ), FieldNode( + position: Pos( + line: 12, + column: 13, + ), + name: "nonNullInteger", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Add(TagRef("tagged")), + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.graphql.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.graphql.ron new file mode 100644 index 00000000..7bfc0070 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.graphql.ron @@ -0,0 +1,27 @@ +TestGraphQLQuery ( + schema_name: "nullables", + // This query's @optional edge doesn't exist for the vertex in question. + // + // It checks that @transform using a @tag from a non-existent optional vertex + // produces a output column of a nullable type, even though the original property + // that is being transformed is non-nullable. + // + // Running queries over this schema never produces any results. + // We are only testing compilation metadata here. + query: r#" +{ + NonNullMainType { + neighbor @optional { + # Even though this property is non-null, it's tagged inside `@optional` + # which means the tag value may still be `null`. + nonNullInteger @tag(name: "tagged") + } + + nonNullNeighbor { + # This output's type should be `Int`, not `Int!` due to the tag's type. + nonNullInteger @transform(op: "+", value: ["%tagged"]) @output + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.ir.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.ir.ron new file mode 100644 index 00000000..ed0d92d4 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.ir.ron @@ -0,0 +1,58 @@ +Ok(TestIRQuery( + schema_name: "nullables", + ir_query: IRQuery( + root_name: "NonNullMainType", + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "MainType", + ), + Vid(2): IRVertex( + vid: Vid(2), + type_name: "MainType", + ), + Vid(3): IRVertex( + vid: Vid(3), + type_name: "MainType", + ), + }, + edges: { + Eid(1): IREdge( + eid: Eid(1), + from_vid: Vid(1), + to_vid: Vid(2), + edge_name: "neighbor", + optional: true, + ), + Eid(2): IREdge( + eid: Eid(2), + from_vid: Vid(1), + to_vid: Vid(3), + edge_name: "nonNullNeighbor", + ), + }, + outputs: { + "nonNullIntegeradd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(3), + field_name: "nonNullInteger", + field_type: "Int!", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(2), + field_name: "nonNullInteger", + field_type: "Int!", + )), Some("Int"))), + ], + ), + tid: Tid(1), + field_type: "Int", + )), + }, + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.output.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.output.ron new file mode 100644 index 00000000..611dc8fb --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.output.ron @@ -0,0 +1,11 @@ +TestInterpreterOutputData( + schema_name: "nullables", + outputs: { + "nonNullIntegeradd": Output( + name: "nonNullIntegeradd", + value_type: "Int", + vid: Vid(3), + ), + }, + results: [], +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.trace.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.trace.ron new file mode 100644 index 00000000..7e07d7e9 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_tag_output_type_is_nullable.trace.ron @@ -0,0 +1,152 @@ +TestInterpreterOutputTrace( + schema_name: "nullables", + 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(ResolveNeighbors(Vid(1), "MainType", Eid(1))), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveNeighbors(Vid(1), "MainType", Eid(2))), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: None, + content: Call(ResolveProperty(Vid(2), "MainType", "nonNullInteger")), + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: None, + content: Call(ResolveProperty(Vid(3), "MainType", "nonNullInteger")), + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: Some(Opid(5)), + content: AdvanceInputIterator, + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(4)), + content: InputIteratorExhausted, + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(4)), + content: OutputIteratorExhausted, + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(5)), + content: InputIteratorExhausted, + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(5)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "NonNullMainType", + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "MainType", + ), + Vid(2): IRVertex( + vid: Vid(2), + type_name: "MainType", + ), + Vid(3): IRVertex( + vid: Vid(3), + type_name: "MainType", + ), + }, + edges: { + Eid(1): IREdge( + eid: Eid(1), + from_vid: Vid(1), + to_vid: Vid(2), + edge_name: "neighbor", + optional: true, + ), + Eid(2): IREdge( + eid: Eid(2), + from_vid: Vid(1), + to_vid: Vid(3), + edge_name: "nonNullNeighbor", + ), + }, + outputs: { + "nonNullIntegeradd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(3), + field_name: "nonNullInteger", + field_type: "Int!", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(2), + field_name: "nonNullInteger", + field_type: "Int!", + )), Some("Int"))), + ], + ), + tid: Tid(1), + field_type: "Int", + )), + }, + ), + ), + ), +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.graphql-parsed.ron new file mode 100644 index 00000000..a6583732 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.graphql-parsed.ron @@ -0,0 +1,91 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "nullables", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "NonNullMainType", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "NonNullMainType", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "neighbor", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "neighbor", + connections: [ + (FieldConnection( + position: Pos( + line: 6, + column: 13, + ), + name: "integer", + ), FieldNode( + position: Pos( + line: 6, + column: 13, + ), + name: "integer", + tag: [ + TagDirective( + name: Some("tagged"), + ), + ], + )), + ], + )), + (FieldConnection( + position: Pos( + line: 9, + column: 9, + ), + name: "nonNullNeighbor", + ), FieldNode( + position: Pos( + line: 9, + column: 9, + ), + name: "nonNullNeighbor", + connections: [ + (FieldConnection( + position: Pos( + line: 11, + column: 13, + ), + name: "nonNullInteger", + ), FieldNode( + position: Pos( + line: 11, + column: 13, + ), + name: "nonNullInteger", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Add(TagRef("tagged")), + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.graphql.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.graphql.ron new file mode 100644 index 00000000..4c298f2d --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.graphql.ron @@ -0,0 +1,25 @@ +TestGraphQLQuery ( + schema_name: "nullables", + // This query's @optional edge doesn't exist for the vertex in question. + // + // It checks that @transform of a non-nullable value using a nullable transform argument + // produces a nullable output type. + // + // Running queries over this schema never produces any results. + // We are only testing compilation metadata here. + query: r#" +{ + NonNullMainType { + neighbor { + # This property is nullable. + integer @tag(name: "tagged") + } + + nonNullNeighbor { + # This output's type should be `Int`, not `Int!` due to the tag's type. + nonNullInteger @transform(op: "+", value: ["%tagged"]) @output + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.ir.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.ir.ron new file mode 100644 index 00000000..2b9591bf --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.ir.ron @@ -0,0 +1,57 @@ +Ok(TestIRQuery( + schema_name: "nullables", + ir_query: IRQuery( + root_name: "NonNullMainType", + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "MainType", + ), + Vid(2): IRVertex( + vid: Vid(2), + type_name: "MainType", + ), + Vid(3): IRVertex( + vid: Vid(3), + type_name: "MainType", + ), + }, + edges: { + Eid(1): IREdge( + eid: Eid(1), + from_vid: Vid(1), + to_vid: Vid(2), + edge_name: "neighbor", + ), + Eid(2): IREdge( + eid: Eid(2), + from_vid: Vid(1), + to_vid: Vid(3), + edge_name: "nonNullNeighbor", + ), + }, + outputs: { + "nonNullIntegeradd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(3), + field_name: "nonNullInteger", + field_type: "Int!", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(2), + field_name: "integer", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )), + }, + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.output.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.output.ron new file mode 100644 index 00000000..611dc8fb --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.output.ron @@ -0,0 +1,11 @@ +TestInterpreterOutputData( + schema_name: "nullables", + outputs: { + "nonNullIntegeradd": Output( + name: "nonNullIntegeradd", + value_type: "Int", + vid: Vid(3), + ), + }, + results: [], +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.trace.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.trace.ron new file mode 100644 index 00000000..1c2b6e92 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nullable_tag_produces_nullable_output_type.trace.ron @@ -0,0 +1,151 @@ +TestInterpreterOutputTrace( + schema_name: "nullables", + 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(ResolveNeighbors(Vid(1), "MainType", Eid(1))), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveNeighbors(Vid(1), "MainType", Eid(2))), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: None, + content: Call(ResolveProperty(Vid(2), "MainType", "integer")), + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: None, + content: Call(ResolveProperty(Vid(3), "MainType", "nonNullInteger")), + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: Some(Opid(5)), + content: AdvanceInputIterator, + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(4)), + content: InputIteratorExhausted, + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(4)), + content: OutputIteratorExhausted, + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(5)), + content: InputIteratorExhausted, + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(5)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "NonNullMainType", + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "MainType", + ), + Vid(2): IRVertex( + vid: Vid(2), + type_name: "MainType", + ), + Vid(3): IRVertex( + vid: Vid(3), + type_name: "MainType", + ), + }, + edges: { + Eid(1): IREdge( + eid: Eid(1), + from_vid: Vid(1), + to_vid: Vid(2), + edge_name: "neighbor", + ), + Eid(2): IREdge( + eid: Eid(2), + from_vid: Vid(1), + to_vid: Vid(3), + edge_name: "nonNullNeighbor", + ), + }, + outputs: { + "nonNullIntegeradd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(3), + field_name: "nonNullInteger", + field_type: "Int!", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(2), + field_name: "integer", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )), + }, + ), + ), + ), +) From fd5a19cd851611dd19f24ee44e9124bd86b49dcf Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Wed, 3 Jul 2024 18:46:54 +0000 Subject: [PATCH 24/30] Fix variables detection in transformed properties with transformed tag. --- trustfall_core/src/frontend/mod.rs | 26 +- ...n_filter_tag_and_output.graphql-parsed.ron | 83 ++ ...orm_then_filter_tag_and_output.graphql.ron | 20 + ...ransform_then_filter_tag_and_output.ir.ron | 91 ++ ...form_then_filter_tag_and_output.output.ron | 45 + ...sform_then_filter_tag_and_output.trace.ron | 980 ++++++++++++++++++ ...rm_with_tagged_argument.graphql-parsed.ron | 71 ++ ...transform_with_tagged_argument.graphql.ron | 17 + .../transform_with_tagged_argument.ir.ron | 58 ++ .../transform_with_tagged_argument.output.ron | 21 + .../transform_with_tagged_argument.trace.ron | 234 +++++ 11 files changed, 1635 insertions(+), 11 deletions(-) create mode 100644 trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.trace.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.trace.ron diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index d794fd1e..46e93c4a 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -362,22 +362,14 @@ fn fill_in_query_variables( } for output in component.outputs.values() { - if let FieldRef::TransformedField(transformed) = output { - process_variable_uses_in_transforms(variables, &transformed.value.transforms, errors); - } + process_variable_use_in_field_ref(variables, output, errors); } for fold in component.folds.values() { fill_in_query_variables(variables, fold.component.as_ref(), errors); for output in fold.fold_specific_outputs.values() { - if let FieldRef::TransformedField(transformed) = output { - process_variable_uses_in_transforms( - variables, - &transformed.value.transforms, - errors, - ); - } + process_variable_use_in_field_ref(variables, output, errors); } for filter in &fold.post_filters { @@ -415,7 +407,9 @@ fn process_variable_uses_in_transforms( Argument::Variable(vref) => { process_variable_use(variables, vref, errors); } - Argument::Tag(..) => {} + Argument::Tag(tag, ..) => { + process_variable_use_in_field_ref(variables, tag, errors); + } }, Transform::Len | Transform::Abs => { // These transforms don't take operands, so no variables here. @@ -424,6 +418,16 @@ fn process_variable_uses_in_transforms( } } +fn process_variable_use_in_field_ref( + variables: &mut BTreeMap, Type>, + field_ref: &FieldRef, + errors: &mut Vec, +) { + if let FieldRef::TransformedField(transformed) = field_ref { + process_variable_uses_in_transforms(variables, &transformed.value.transforms, errors); + } +} + fn process_variable_use( variables: &mut BTreeMap, Type>, vref: &VariableRef, diff --git a/trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.graphql-parsed.ron new file mode 100644 index 00000000..395f164d --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.graphql-parsed.ron @@ -0,0 +1,83 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(3), + "min": Int64(-1), + }, + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + tag: [ + TagDirective(), + ], + )), + (FieldConnection( + position: Pos( + line: 6, + column: 9, + ), + name: "vowelsInName", + ), FieldNode( + position: Pos( + line: 6, + column: 9, + ), + name: "vowelsInName", + output: [ + OutputDirective(), + ], + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Len, + ), + output: [ + OutputDirective(), + ], + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Add(TagRef("value")), + ), + output: [ + OutputDirective(), + ], + filter: [ + FilterDirective( + operation: LessThanOrEqual((), VariableRef("four")), + ), + ], + )), + )), + )), + ], + ), + ), + arguments: { + "four": Int64(4), + }, +)) diff --git a/trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.graphql.ron b/trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.graphql.ron new file mode 100644 index 00000000..f70752a1 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.graphql.ron @@ -0,0 +1,20 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: -1, max: 3) { + value @tag + + vowelsInName + @output + @transform(op: "len") + @output + @transform(op: "+", value: ["%value"]) + @filter(op: "<=", value: ["$four"]) + @output + } +}"#, + arguments: { + "four": Int64(4), + }, +) diff --git a/trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.ir.ron b/trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.ir.ron new file mode 100644 index 00000000..a423eeec --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.ir.ron @@ -0,0 +1,91 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(3), + "min": Int64(-1), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + filters: [ + LessThanOrEqual(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "vowelsInName", + field_type: "[String]", + )), + transforms: [ + Len, + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(2), + field_type: "Int", + )), Variable(VariableRef( + variable_name: "four", + variable_type: "Int!", + ))), + ], + ), + }, + outputs: { + "vowelsInName": ContextField(ContextField( + vertex_id: Vid(1), + field_name: "vowelsInName", + field_type: "[String]", + )), + "vowelsInNamelen": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "vowelsInName", + field_type: "[String]", + )), + transforms: [ + Len, + ], + ), + tid: Tid(1), + field_type: "Int", + )), + "vowelsInNamelenadd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "vowelsInName", + field_type: "[String]", + )), + transforms: [ + Len, + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(2), + field_type: "Int", + )), + }, + ), + variables: { + "four": "Int!", + }, + ), + arguments: { + "four": Int64(4), + }, +)) diff --git a/trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.output.ron b/trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.output.ron new file mode 100644 index 00000000..29d9bce4 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.output.ron @@ -0,0 +1,45 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "vowelsInName": Output( + name: "vowelsInName", + value_type: "[String]", + vid: Vid(1), + ), + "vowelsInNamelen": Output( + name: "vowelsInNamelen", + value_type: "Int", + vid: Vid(1), + ), + "vowelsInNamelenadd": Output( + name: "vowelsInNamelenadd", + value_type: "Int", + vid: Vid(1), + ), + }, + results: [ + { + "vowelsInName": List([ + String("e"), + String("o"), + ]), + "vowelsInNamelen": Int64(2), + "vowelsInNamelenadd": Int64(2), + }, + { + "vowelsInName": List([ + String("o"), + String("e"), + ]), + "vowelsInNamelen": Int64(2), + "vowelsInNamelenadd": Int64(3), + }, + { + "vowelsInName": List([ + String("o"), + ]), + "vowelsInNamelen": Int64(1), + "vowelsInNamelenadd": Int64(3), + }, + ], +) diff --git a/trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.trace.ron b/trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.trace.ron new file mode 100644 index 00000000..b27ab88f --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/retransform_then_filter_tag_and_output.trace.ron @@ -0,0 +1,980 @@ +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(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "vowelsInName")), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "vowelsInName")), + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "vowelsInName")), + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "vowelsInName")), + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: Some(Opid(7)), + content: AdvanceInputIterator, + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(6)), + content: AdvanceInputIterator, + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(5)), + content: AdvanceInputIterator, + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Prime(PrimeNumber(-1)))), + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: {}, + )), + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: {}, + ), Int64(-1))), + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: {}, + values: [ + Int64(-1), + ], + )), + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: {}, + values: [ + Int64(-1), + ], + ), Null)), + ), + Opid(19): TraceOp( + opid: Opid(19), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Neither(NeitherNumber(0)))), + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: {}, + )), + ), + Opid(23): TraceOp( + opid: Opid(23), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: {}, + ), Int64(0))), + ), + Opid(24): TraceOp( + opid: Opid(24), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: {}, + values: [ + Int64(0), + ], + )), + ), + Opid(25): TraceOp( + opid: Opid(25), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: {}, + values: [ + Int64(0), + ], + ), List([ + String("e"), + String("o"), + ]))), + ), + Opid(26): TraceOp( + opid: Opid(26), + parent_opid: Some(Opid(4)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + )), + ), + Opid(27): TraceOp( + opid: Opid(27), + parent_opid: Some(Opid(4)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + ), List([ + String("e"), + String("o"), + ]))), + ), + Opid(28): TraceOp( + opid: Opid(28), + parent_opid: Some(Opid(5)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + values: [ + List([ + String("e"), + String("o"), + ]), + ], + )), + ), + Opid(29): TraceOp( + opid: Opid(29), + parent_opid: Some(Opid(5)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + values: [ + List([ + String("e"), + String("o"), + ]), + ], + ), List([ + String("e"), + String("o"), + ]))), + ), + Opid(30): TraceOp( + opid: Opid(30), + parent_opid: Some(Opid(6)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + values: [ + List([ + String("e"), + String("o"), + ]), + Int64(2), + ], + )), + ), + Opid(31): TraceOp( + opid: Opid(31), + parent_opid: Some(Opid(6)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + values: [ + List([ + String("e"), + String("o"), + ]), + Int64(2), + ], + ), Int64(0))), + ), + Opid(32): TraceOp( + opid: Opid(32), + parent_opid: Some(Opid(7)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + values: [ + List([ + String("e"), + String("o"), + ]), + Int64(2), + Int64(0), + ], + )), + ), + Opid(33): TraceOp( + opid: Opid(33), + parent_opid: Some(Opid(7)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + values: [ + List([ + String("e"), + String("o"), + ]), + Int64(2), + Int64(0), + ], + ), List([ + String("e"), + String("o"), + ]))), + ), + Opid(34): TraceOp( + opid: Opid(34), + parent_opid: None, + content: ProduceQueryResult({ + "vowelsInName": List([ + String("e"), + String("o"), + ]), + "vowelsInNamelen": Int64(2), + "vowelsInNamelenadd": Int64(2), + }), + ), + Opid(35): TraceOp( + opid: Opid(35), + parent_opid: Some(Opid(7)), + content: AdvanceInputIterator, + ), + Opid(36): TraceOp( + opid: Opid(36), + parent_opid: Some(Opid(6)), + content: AdvanceInputIterator, + ), + Opid(37): TraceOp( + opid: Opid(37), + parent_opid: Some(Opid(5)), + content: AdvanceInputIterator, + ), + Opid(38): TraceOp( + opid: Opid(38), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(39): TraceOp( + opid: Opid(39), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(40): TraceOp( + opid: Opid(40), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(41): TraceOp( + opid: Opid(41), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Neither(NeitherNumber(1)))), + ), + Opid(42): TraceOp( + opid: Opid(42), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + )), + ), + Opid(43): TraceOp( + opid: Opid(43), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + ), Int64(1))), + ), + Opid(44): TraceOp( + opid: Opid(44), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + values: [ + Int64(1), + ], + )), + ), + Opid(45): TraceOp( + opid: Opid(45), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + values: [ + Int64(1), + ], + ), List([ + String("o"), + String("e"), + ]))), + ), + Opid(46): TraceOp( + opid: Opid(46), + parent_opid: Some(Opid(4)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + )), + ), + Opid(47): TraceOp( + opid: Opid(47), + parent_opid: Some(Opid(4)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + ), List([ + String("o"), + String("e"), + ]))), + ), + Opid(48): TraceOp( + opid: Opid(48), + parent_opid: Some(Opid(5)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + List([ + String("o"), + String("e"), + ]), + ], + )), + ), + Opid(49): TraceOp( + opid: Opid(49), + parent_opid: Some(Opid(5)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + List([ + String("o"), + String("e"), + ]), + ], + ), List([ + String("o"), + String("e"), + ]))), + ), + Opid(50): TraceOp( + opid: Opid(50), + parent_opid: Some(Opid(6)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + List([ + String("o"), + String("e"), + ]), + Int64(2), + ], + )), + ), + Opid(51): TraceOp( + opid: Opid(51), + parent_opid: Some(Opid(6)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + List([ + String("o"), + String("e"), + ]), + Int64(2), + ], + ), Int64(1))), + ), + Opid(52): TraceOp( + opid: Opid(52), + parent_opid: Some(Opid(7)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + List([ + String("o"), + String("e"), + ]), + Int64(2), + Int64(1), + ], + )), + ), + Opid(53): TraceOp( + opid: Opid(53), + parent_opid: Some(Opid(7)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + List([ + String("o"), + String("e"), + ]), + Int64(2), + Int64(1), + ], + ), List([ + String("o"), + String("e"), + ]))), + ), + Opid(54): TraceOp( + opid: Opid(54), + parent_opid: None, + content: ProduceQueryResult({ + "vowelsInName": List([ + String("o"), + String("e"), + ]), + "vowelsInNamelen": Int64(2), + "vowelsInNamelenadd": Int64(3), + }), + ), + Opid(55): TraceOp( + opid: Opid(55), + parent_opid: Some(Opid(7)), + content: AdvanceInputIterator, + ), + Opid(56): TraceOp( + opid: Opid(56), + parent_opid: Some(Opid(6)), + content: AdvanceInputIterator, + ), + Opid(57): TraceOp( + opid: Opid(57), + parent_opid: Some(Opid(5)), + content: AdvanceInputIterator, + ), + Opid(58): TraceOp( + opid: Opid(58), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(59): TraceOp( + opid: Opid(59), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(60): TraceOp( + opid: Opid(60), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(61): TraceOp( + opid: Opid(61), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Prime(PrimeNumber(2)))), + ), + Opid(62): TraceOp( + opid: Opid(62), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: {}, + )), + ), + Opid(63): TraceOp( + opid: Opid(63), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: {}, + ), Int64(2))), + ), + Opid(64): TraceOp( + opid: Opid(64), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: {}, + values: [ + Int64(2), + ], + )), + ), + Opid(65): TraceOp( + opid: Opid(65), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: {}, + values: [ + Int64(2), + ], + ), List([ + String("o"), + ]))), + ), + Opid(66): TraceOp( + opid: Opid(66), + parent_opid: Some(Opid(4)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(2))), + }, + )), + ), + Opid(67): TraceOp( + opid: Opid(67), + parent_opid: Some(Opid(4)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(2))), + }, + ), List([ + String("o"), + ]))), + ), + Opid(68): TraceOp( + opid: Opid(68), + parent_opid: Some(Opid(5)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(2))), + }, + values: [ + List([ + String("o"), + ]), + ], + )), + ), + Opid(69): TraceOp( + opid: Opid(69), + parent_opid: Some(Opid(5)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(2))), + }, + values: [ + List([ + String("o"), + ]), + ], + ), List([ + String("o"), + ]))), + ), + Opid(70): TraceOp( + opid: Opid(70), + parent_opid: Some(Opid(6)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(2))), + }, + values: [ + List([ + String("o"), + ]), + Int64(1), + ], + )), + ), + Opid(71): TraceOp( + opid: Opid(71), + parent_opid: Some(Opid(6)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(2))), + }, + values: [ + List([ + String("o"), + ]), + Int64(1), + ], + ), Int64(2))), + ), + Opid(72): TraceOp( + opid: Opid(72), + parent_opid: Some(Opid(7)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(2))), + }, + values: [ + List([ + String("o"), + ]), + Int64(1), + Int64(2), + ], + )), + ), + Opid(73): TraceOp( + opid: Opid(73), + parent_opid: Some(Opid(7)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(2))), + }, + values: [ + List([ + String("o"), + ]), + Int64(1), + Int64(2), + ], + ), List([ + String("o"), + ]))), + ), + Opid(74): TraceOp( + opid: Opid(74), + parent_opid: None, + content: ProduceQueryResult({ + "vowelsInName": List([ + String("o"), + ]), + "vowelsInNamelen": Int64(1), + "vowelsInNamelenadd": Int64(3), + }), + ), + Opid(75): TraceOp( + opid: Opid(75), + parent_opid: Some(Opid(7)), + content: AdvanceInputIterator, + ), + Opid(76): TraceOp( + opid: Opid(76), + parent_opid: Some(Opid(6)), + content: AdvanceInputIterator, + ), + Opid(77): TraceOp( + opid: Opid(77), + parent_opid: Some(Opid(5)), + content: AdvanceInputIterator, + ), + Opid(78): TraceOp( + opid: Opid(78), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(79): TraceOp( + opid: Opid(79), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(80): TraceOp( + opid: Opid(80), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(81): TraceOp( + opid: Opid(81), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Prime(PrimeNumber(3)))), + ), + Opid(82): TraceOp( + opid: Opid(82), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: {}, + )), + ), + Opid(83): TraceOp( + opid: Opid(83), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: {}, + ), Int64(3))), + ), + Opid(84): TraceOp( + opid: Opid(84), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: {}, + values: [ + Int64(3), + ], + )), + ), + Opid(85): TraceOp( + opid: Opid(85), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(3))), + vertices: {}, + values: [ + Int64(3), + ], + ), List([ + String("e"), + String("e"), + ]))), + ), + Opid(86): TraceOp( + opid: Opid(86), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(87): TraceOp( + opid: Opid(87), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(88): TraceOp( + opid: Opid(88), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(89): TraceOp( + opid: Opid(89), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(90): TraceOp( + opid: Opid(90), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(91): TraceOp( + opid: Opid(91), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(92): TraceOp( + opid: Opid(92), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + Opid(93): TraceOp( + opid: Opid(93), + parent_opid: Some(Opid(4)), + content: InputIteratorExhausted, + ), + Opid(94): TraceOp( + opid: Opid(94), + parent_opid: Some(Opid(4)), + content: OutputIteratorExhausted, + ), + Opid(95): TraceOp( + opid: Opid(95), + parent_opid: Some(Opid(5)), + content: InputIteratorExhausted, + ), + Opid(96): TraceOp( + opid: Opid(96), + parent_opid: Some(Opid(5)), + content: OutputIteratorExhausted, + ), + Opid(97): TraceOp( + opid: Opid(97), + parent_opid: Some(Opid(6)), + content: InputIteratorExhausted, + ), + Opid(98): TraceOp( + opid: Opid(98), + parent_opid: Some(Opid(6)), + content: OutputIteratorExhausted, + ), + Opid(99): TraceOp( + opid: Opid(99), + parent_opid: Some(Opid(7)), + content: InputIteratorExhausted, + ), + Opid(100): TraceOp( + opid: Opid(100), + parent_opid: Some(Opid(7)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(3), + "min": Int64(-1), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + filters: [ + LessThanOrEqual(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "vowelsInName", + field_type: "[String]", + )), + transforms: [ + Len, + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(2), + field_type: "Int", + )), Variable(VariableRef( + variable_name: "four", + variable_type: "Int!", + ))), + ], + ), + }, + outputs: { + "vowelsInName": ContextField(ContextField( + vertex_id: Vid(1), + field_name: "vowelsInName", + field_type: "[String]", + )), + "vowelsInNamelen": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "vowelsInName", + field_type: "[String]", + )), + transforms: [ + Len, + ], + ), + tid: Tid(1), + field_type: "Int", + )), + "vowelsInNamelenadd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "vowelsInName", + field_type: "[String]", + )), + transforms: [ + Len, + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(2), + field_type: "Int", + )), + }, + ), + variables: { + "four": "Int!", + }, + ), + arguments: { + "four": Int64(4), + }, + ), +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.graphql-parsed.ron new file mode 100644 index 00000000..77afe02f --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.graphql-parsed.ron @@ -0,0 +1,71 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "One", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "One", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Add(VariableRef("ten")), + ), + tag: [ + TagDirective(), + ], + )), + )), + (FieldConnection( + position: Pos( + line: 6, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 6, + column: 9, + ), + name: "value", + output: [ + OutputDirective(), + ], + transform_group: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Add(TagRef("value")), + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + ), + ), + arguments: { + "ten": Int64(10), + }, +)) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.graphql.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.graphql.ron new file mode 100644 index 00000000..88f74578 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.graphql.ron @@ -0,0 +1,17 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + One { + value @transform(op: "+", value: ["$ten"]) @tag + + value + @output + @transform(op: "+", value: ["%value"]) + @output + } +}"#, + arguments: { + "ten": Int64(10), + }, +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.ir.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.ir.ron new file mode 100644 index 00000000..7b8c730b --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.ir.ron @@ -0,0 +1,58 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "One", + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + "valueadd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Variable(VariableRef( + variable_name: "ten", + variable_type: "Int!", + ))), + ], + ), + tid: Tid(1), + field_type: "Int", + )))), + ], + ), + tid: Tid(2), + field_type: "Int", + )), + }, + ), + variables: { + "ten": "Int!", + }, + ), + arguments: { + "ten": Int64(10), + }, +)) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.output.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.output.ron new file mode 100644 index 00000000..edef3cec --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.output.ron @@ -0,0 +1,21 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "value": Output( + name: "value", + value_type: "Int", + vid: Vid(1), + ), + "valueadd": Output( + name: "valueadd", + value_type: "Int", + vid: Vid(1), + ), + }, + results: [ + { + "value": Int64(1), + "valueadd": Int64(12), + }, + ], +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.trace.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.trace.ron new file mode 100644 index 00000000..bc20fbca --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_argument.trace.ron @@ -0,0 +1,234 @@ +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(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "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(Neither(NeitherNumber(1)))), + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + )), + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + ), Int64(1))), + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(1), + ], + )), + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(1), + ], + ), Int64(1))), + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: Some(Opid(4)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(1), + Int64(11), + ], + )), + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(4)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(1), + Int64(11), + ], + ), Int64(1))), + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: None, + content: ProduceQueryResult({ + "value": Int64(1), + "valueadd": Int64(12), + }), + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + 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: "One", + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + "valueadd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Variable(VariableRef( + variable_name: "ten", + variable_type: "Int!", + ))), + ], + ), + tid: Tid(1), + field_type: "Int", + )))), + ], + ), + tid: Tid(2), + field_type: "Int", + )), + }, + ), + variables: { + "ten": "Int!", + }, + ), + arguments: { + "ten": Int64(10), + }, + ), +) From 1b2cde2ca21805e29340eda68a8fb82ed31ab8b8 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Fri, 5 Jul 2024 17:29:51 +0000 Subject: [PATCH 25/30] Handle lookups of transformed values while resolving transformed tag. --- trustfall_core/src/interpreter/tags.rs | 20 +- .../src/interpreter/transformation.rs | 16 +- ...th_tagged_self_argument.graphql-parsed.ron | 65 ++ ...form_with_tagged_self_argument.graphql.ron | 17 + ...transform_with_tagged_self_argument.ir.ron | 126 +++ ...sform_with_tagged_self_argument.output.ron | 21 + ...nsform_with_tagged_self_argument.trace.ron | 741 ++++++++++++++++++ 7 files changed, 997 insertions(+), 9 deletions(-) create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.trace.ron diff --git a/trustfall_core/src/interpreter/tags.rs b/trustfall_core/src/interpreter/tags.rs index f666c8e1..c0131c5d 100644 --- a/trustfall_core/src/interpreter/tags.rs +++ b/trustfall_core/src/interpreter/tags.rs @@ -9,7 +9,9 @@ use super::{ compute_context_field_with_separate_value, compute_fold_specific_field_with_separate_value, compute_local_field_with_separate_value, QueryCarrier, }, - transformation::apply_transforms, + transformation::{ + apply_transforms, push_transform_argument_tag_values_onto_stack_during_main_query, + }, Adapter, ContextIterator, DataContext, TaggedValue, }; @@ -43,6 +45,16 @@ pub(super) fn compute_tag_with_separate_value< compute_fold_specific_field_tag_with_separate_value(component, fold_field, iterator) } FieldRef::TransformedField(transformed_field) => { + let transform_arguments_iterator = + push_transform_argument_tag_values_onto_stack_during_main_query( + adapter, + carrier, + component, + current_vid, + &transformed_field.value.transforms, + iterator, + ); + let base_value_iterator = match &transformed_field.value.base { TransformBase::ContextField(context_field) => { compute_context_field_tag_with_separate_value::( @@ -51,12 +63,14 @@ pub(super) fn compute_tag_with_separate_value< carrier, component, context_field, - iterator, + transform_arguments_iterator, ) } TransformBase::FoldSpecificField(fold_field) => { compute_fold_specific_field_tag_with_separate_value( - component, fold_field, iterator, + component, + fold_field, + transform_arguments_iterator, ) } }; diff --git a/trustfall_core/src/interpreter/transformation.rs b/trustfall_core/src/interpreter/transformation.rs index b6bffc6e..afd3d4c5 100644 --- a/trustfall_core/src/interpreter/transformation.rs +++ b/trustfall_core/src/interpreter/transformation.rs @@ -113,9 +113,11 @@ fn apply_one_transform( apply_add_transform(value, operand) } Argument::Tag(..) => { - let operand = stack.pop().expect( - "empty stack while attempting to resolve transform operand: {transform:?}", - ); + let operand = stack.pop().unwrap_or_else(|| { + panic!( + "empty stack while attempting to resolve transform operand: {transform:?}" + ) + }); apply_add_transform(value, &operand) } }, @@ -125,9 +127,11 @@ fn apply_one_transform( apply_fadd_transform(value, operand) } Argument::Tag(..) => { - let operand = stack.pop().expect( - "empty stack while attempting to resolve transform operand: {transform:?}", - ); + let operand = stack.pop().unwrap_or_else(|| { + panic!( + "empty stack while attempting to resolve transform operand: {transform:?}" + ) + }); apply_fadd_transform(value, &operand) } }, diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.graphql-parsed.ron new file mode 100644 index 00000000..880865e8 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.graphql-parsed.ron @@ -0,0 +1,65 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "One", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "One", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + tag: [ + TagDirective(), + ], + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Add(TagRef("value")), + ), + output: [ + OutputDirective(), + ], + tag: [ + TagDirective( + name: Some("transformed"), + ), + ], + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Add(TagRef("transformed")), + ), + output: [ + OutputDirective(), + ], + filter: [ + FilterDirective( + operation: NotEquals((), TagRef("transformed")), + ), + ], + )), + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.graphql.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.graphql.ron new file mode 100644 index 00000000..431c3115 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.graphql.ron @@ -0,0 +1,17 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + One { + value + @tag + @transform(op: "+", value: ["%value"]) + @tag(name: "transformed") + @output + @transform(op: "+", value: ["%transformed"]) + @filter(op: "!=", value: ["%transformed"]) + @output + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.ir.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.ir.ron new file mode 100644 index 00000000..8d2a094b --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.ir.ron @@ -0,0 +1,126 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "One", + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + filters: [ + NotEquals(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + Add(Tag(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )))), + ], + ), + tid: Tid(2), + field_type: "Int", + )), Tag(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )))), + ], + ), + }, + outputs: { + "valueadd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )), + "valueaddadd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + Add(Tag(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )))), + ], + ), + tid: Tid(2), + field_type: "Int", + )), + }, + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.output.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.output.ron new file mode 100644 index 00000000..46a2b70d --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.output.ron @@ -0,0 +1,21 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "valueadd": Output( + name: "valueadd", + value_type: "Int", + vid: Vid(1), + ), + "valueaddadd": Output( + name: "valueaddadd", + value_type: "Int", + vid: Vid(1), + ), + }, + results: [ + { + "valueadd": Int64(2), + "valueaddadd": Int64(4), + }, + ], +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.trace.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.trace.ron new file mode 100644 index 00000000..4e2346b5 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_tagged_self_argument.trace.ron @@ -0,0 +1,741 @@ +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(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(13)), + content: AdvanceInputIterator, + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(12)), + content: AdvanceInputIterator, + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(11)), + content: AdvanceInputIterator, + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(10)), + content: AdvanceInputIterator, + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(9)), + content: AdvanceInputIterator, + ), + Opid(19): TraceOp( + opid: Opid(19), + parent_opid: Some(Opid(8)), + content: AdvanceInputIterator, + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: Some(Opid(7)), + content: AdvanceInputIterator, + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: Some(Opid(6)), + content: AdvanceInputIterator, + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: Some(Opid(5)), + content: AdvanceInputIterator, + ), + Opid(23): TraceOp( + opid: Opid(23), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(24): TraceOp( + opid: Opid(24), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(25): TraceOp( + opid: Opid(25), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(26): TraceOp( + opid: Opid(26), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Neither(NeitherNumber(1)))), + ), + Opid(27): TraceOp( + opid: Opid(27), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + )), + ), + Opid(28): TraceOp( + opid: Opid(28), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + ), Int64(1))), + ), + Opid(29): TraceOp( + opid: Opid(29), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + values: [ + Int64(1), + ], + )), + ), + Opid(30): TraceOp( + opid: Opid(30), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + values: [ + Int64(1), + ], + ), Int64(1))), + ), + Opid(31): TraceOp( + opid: Opid(31), + parent_opid: Some(Opid(4)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + values: [ + Int64(2), + ], + )), + ), + Opid(32): TraceOp( + opid: Opid(32), + parent_opid: Some(Opid(4)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + values: [ + Int64(2), + ], + ), Int64(1))), + ), + Opid(33): TraceOp( + opid: Opid(33), + parent_opid: Some(Opid(5)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + values: [ + Int64(2), + Int64(1), + ], + )), + ), + Opid(34): TraceOp( + opid: Opid(34), + parent_opid: Some(Opid(5)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + values: [ + Int64(2), + Int64(1), + ], + ), Int64(1))), + ), + Opid(35): TraceOp( + opid: Opid(35), + parent_opid: Some(Opid(6)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + values: [ + Int64(4), + ], + )), + ), + Opid(36): TraceOp( + opid: Opid(36), + parent_opid: Some(Opid(6)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + values: [ + Int64(4), + ], + ), Int64(1))), + ), + Opid(37): TraceOp( + opid: Opid(37), + parent_opid: Some(Opid(7)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + values: [ + Int64(4), + Int64(1), + ], + )), + ), + Opid(38): TraceOp( + opid: Opid(38), + parent_opid: Some(Opid(7)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: {}, + values: [ + Int64(4), + Int64(1), + ], + ), Int64(1))), + ), + Opid(39): TraceOp( + opid: Opid(39), + parent_opid: Some(Opid(8)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + )), + ), + Opid(40): TraceOp( + opid: Opid(40), + parent_opid: Some(Opid(8)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + ), Int64(1))), + ), + Opid(41): TraceOp( + opid: Opid(41), + parent_opid: Some(Opid(9)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(1), + ], + )), + ), + Opid(42): TraceOp( + opid: Opid(42), + parent_opid: Some(Opid(9)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(1), + ], + ), Int64(1))), + ), + Opid(43): TraceOp( + opid: Opid(43), + parent_opid: Some(Opid(10)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(2), + ], + )), + ), + Opid(44): TraceOp( + opid: Opid(44), + parent_opid: Some(Opid(10)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(2), + ], + ), Int64(1))), + ), + Opid(45): TraceOp( + opid: Opid(45), + parent_opid: Some(Opid(11)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(2), + Int64(1), + ], + )), + ), + Opid(46): TraceOp( + opid: Opid(46), + parent_opid: Some(Opid(11)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(2), + Int64(1), + ], + ), Int64(1))), + ), + Opid(47): TraceOp( + opid: Opid(47), + parent_opid: Some(Opid(12)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(2), + Int64(2), + ], + )), + ), + Opid(48): TraceOp( + opid: Opid(48), + parent_opid: Some(Opid(12)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(2), + Int64(2), + ], + ), Int64(1))), + ), + Opid(49): TraceOp( + opid: Opid(49), + parent_opid: Some(Opid(13)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(2), + Int64(2), + Int64(1), + ], + )), + ), + Opid(50): TraceOp( + opid: Opid(50), + parent_opid: Some(Opid(13)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(2), + Int64(2), + Int64(1), + ], + ), Int64(1))), + ), + Opid(51): TraceOp( + opid: Opid(51), + parent_opid: None, + content: ProduceQueryResult({ + "valueadd": Int64(2), + "valueaddadd": Int64(4), + }), + ), + Opid(52): TraceOp( + opid: Opid(52), + parent_opid: Some(Opid(13)), + content: AdvanceInputIterator, + ), + Opid(53): TraceOp( + opid: Opid(53), + parent_opid: Some(Opid(12)), + content: AdvanceInputIterator, + ), + Opid(54): TraceOp( + opid: Opid(54), + parent_opid: Some(Opid(11)), + content: AdvanceInputIterator, + ), + Opid(55): TraceOp( + opid: Opid(55), + parent_opid: Some(Opid(10)), + content: AdvanceInputIterator, + ), + Opid(56): TraceOp( + opid: Opid(56), + parent_opid: Some(Opid(9)), + content: AdvanceInputIterator, + ), + Opid(57): TraceOp( + opid: Opid(57), + parent_opid: Some(Opid(8)), + content: AdvanceInputIterator, + ), + Opid(58): TraceOp( + opid: Opid(58), + parent_opid: Some(Opid(7)), + content: AdvanceInputIterator, + ), + Opid(59): TraceOp( + opid: Opid(59), + parent_opid: Some(Opid(6)), + content: AdvanceInputIterator, + ), + Opid(60): TraceOp( + opid: Opid(60), + parent_opid: Some(Opid(5)), + content: AdvanceInputIterator, + ), + Opid(61): TraceOp( + opid: Opid(61), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(62): TraceOp( + opid: Opid(62), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(63): TraceOp( + opid: Opid(63), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(64): TraceOp( + opid: Opid(64), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(65): TraceOp( + opid: Opid(65), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(66): TraceOp( + opid: Opid(66), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(67): TraceOp( + opid: Opid(67), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(68): TraceOp( + opid: Opid(68), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + Opid(69): TraceOp( + opid: Opid(69), + parent_opid: Some(Opid(4)), + content: InputIteratorExhausted, + ), + Opid(70): TraceOp( + opid: Opid(70), + parent_opid: Some(Opid(4)), + content: OutputIteratorExhausted, + ), + Opid(71): TraceOp( + opid: Opid(71), + parent_opid: Some(Opid(5)), + content: InputIteratorExhausted, + ), + Opid(72): TraceOp( + opid: Opid(72), + parent_opid: Some(Opid(5)), + content: OutputIteratorExhausted, + ), + Opid(73): TraceOp( + opid: Opid(73), + parent_opid: Some(Opid(6)), + content: InputIteratorExhausted, + ), + Opid(74): TraceOp( + opid: Opid(74), + parent_opid: Some(Opid(6)), + content: OutputIteratorExhausted, + ), + Opid(75): TraceOp( + opid: Opid(75), + parent_opid: Some(Opid(7)), + content: InputIteratorExhausted, + ), + Opid(76): TraceOp( + opid: Opid(76), + parent_opid: Some(Opid(7)), + content: OutputIteratorExhausted, + ), + Opid(77): TraceOp( + opid: Opid(77), + parent_opid: Some(Opid(8)), + content: InputIteratorExhausted, + ), + Opid(78): TraceOp( + opid: Opid(78), + parent_opid: Some(Opid(8)), + content: OutputIteratorExhausted, + ), + Opid(79): TraceOp( + opid: Opid(79), + parent_opid: Some(Opid(9)), + content: InputIteratorExhausted, + ), + Opid(80): TraceOp( + opid: Opid(80), + parent_opid: Some(Opid(9)), + content: OutputIteratorExhausted, + ), + Opid(81): TraceOp( + opid: Opid(81), + parent_opid: Some(Opid(10)), + content: InputIteratorExhausted, + ), + Opid(82): TraceOp( + opid: Opid(82), + parent_opid: Some(Opid(10)), + content: OutputIteratorExhausted, + ), + Opid(83): TraceOp( + opid: Opid(83), + parent_opid: Some(Opid(11)), + content: InputIteratorExhausted, + ), + Opid(84): TraceOp( + opid: Opid(84), + parent_opid: Some(Opid(11)), + content: OutputIteratorExhausted, + ), + Opid(85): TraceOp( + opid: Opid(85), + parent_opid: Some(Opid(12)), + content: InputIteratorExhausted, + ), + Opid(86): TraceOp( + opid: Opid(86), + parent_opid: Some(Opid(12)), + content: OutputIteratorExhausted, + ), + Opid(87): TraceOp( + opid: Opid(87), + parent_opid: Some(Opid(13)), + content: InputIteratorExhausted, + ), + Opid(88): TraceOp( + opid: Opid(88), + parent_opid: Some(Opid(13)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "One", + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + filters: [ + NotEquals(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + Add(Tag(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )))), + ], + ), + tid: Tid(2), + field_type: "Int", + )), Tag(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )))), + ], + ), + }, + outputs: { + "valueadd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )), + "valueaddadd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + Add(Tag(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )))), + ], + ), + tid: Tid(2), + field_type: "Int", + )), + }, + ), + ), + ), +) From 01f6a493d70063a9c39746b22fd21fa38cb61c37 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Sat, 6 Jul 2024 14:58:08 +0000 Subject: [PATCH 26/30] Ensure unused transform arguments are cleaned up from the stack. --- trustfall_core/src/interpreter/tags.rs | 12 +- .../src/interpreter/transformation.rs | 45 ++ ...ptional_transformed_tag.graphql-parsed.ron | 150 ++++ ...stent_optional_transformed_tag.graphql.ron | 32 + ...onexistent_optional_transformed_tag.ir.ron | 109 +++ ...istent_optional_transformed_tag.output.ron | 21 + ...xistent_optional_transformed_tag.trace.ron | 701 ++++++++++++++++++ 7 files changed, 1068 insertions(+), 2 deletions(-) create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.trace.ron diff --git a/trustfall_core/src/interpreter/tags.rs b/trustfall_core/src/interpreter/tags.rs index c0131c5d..ace696bc 100644 --- a/trustfall_core/src/interpreter/tags.rs +++ b/trustfall_core/src/interpreter/tags.rs @@ -10,7 +10,8 @@ use super::{ compute_local_field_with_separate_value, QueryCarrier, }, transformation::{ - apply_transforms, push_transform_argument_tag_values_onto_stack_during_main_query, + apply_transforms, drop_unused_transform_arguments, + push_transform_argument_tag_values_onto_stack_during_main_query, }, Adapter, ContextIterator, DataContext, TaggedValue, }; @@ -85,7 +86,14 @@ pub(super) fn compute_tag_with_separate_value< Box::new(base_value_iterator.map(move |(mut ctx, base_value)| { let value = match base_value { - TaggedValue::NonexistentOptional => TaggedValue::NonexistentOptional, + TaggedValue::NonexistentOptional => { + // We may have pushed arguments onto the `ctx.values` stack for use by + // the transforms here, but it turns out we aren't going to need them. + // Remove them from the stack to avoid corrupting its state. + drop_unused_transform_arguments(&transformed_value, &mut ctx.values); + + TaggedValue::NonexistentOptional + } TaggedValue::Some(value) => TaggedValue::Some(apply_transforms( &transformed_value, &variables, diff --git a/trustfall_core/src/interpreter/transformation.rs b/trustfall_core/src/interpreter/transformation.rs index afd3d4c5..01566b5a 100644 --- a/trustfall_core/src/interpreter/transformation.rs +++ b/trustfall_core/src/interpreter/transformation.rs @@ -84,6 +84,51 @@ pub(super) fn push_transform_argument_tag_values_onto_stack<'query, AdapterT: Ad iterator } +/// Pop and drop any arguments pushed to the values stack that correspond to the given transforms. +/// +/// Consider a query like: +/// ```graphql +/// { +/// Example { +/// value @tag(name: "first") +/// +/// possibly_nonexistent @optional { +/// value @transform(op: "+", value: ["%first"]) @tag(name: "opt") +/// } +/// +/// # ... +/// } +/// } +/// ``` +/// Evaluating the `opt` tag here requires that we push the `first` tag's value to the stack. +/// Then, we compute the value of the `value` property, and apply the `+` transform while popping +/// the `first` value off the stack. +/// +/// But what if the optional `possibly_nonexistent` edge doesn't exist? +/// Then we've pushed a value onto the stack, but we aren't going to use it since there's nothing +/// to transform. We must explicitly remove that value from the stack, to avoid corrupting +/// the stack's state and impacting downstream operations. +/// +/// That's what this function does: it pops all stack arguments that were pushed to satisfy +/// a given slice of transforms. +pub(super) fn drop_unused_transform_arguments( + transformed_value: &TransformedValue, + stack: &mut Vec, +) { + for transform in &transformed_value.transforms { + match transform { + Transform::Add(arg) | Transform::AddF(arg) => match arg { + Argument::Tag(..) => { + // One argument on the stack here. + stack.pop().expect("nothing to pop, this is a bug"); + } + Argument::Variable(..) => {} + }, + Transform::Len | Transform::Abs => {} + } + } +} + pub(super) fn apply_transforms( transformed_value: &TransformedValue, variables: &BTreeMap, FieldValue>, diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.graphql-parsed.ron new file mode 100644 index 00000000..3f0768da --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.graphql-parsed.ron @@ -0,0 +1,150 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "One", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "One", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + tag: [ + TagDirective(), + ], + )), + (FieldConnection( + position: Pos( + line: 6, + column: 9, + ), + name: "predecessor", + ), FieldNode( + position: Pos( + line: 6, + column: 9, + ), + name: "predecessor", + connections: [ + (FieldConnection( + position: Pos( + line: 7, + column: 13, + ), + name: "predecessor", + optional: Some(OptionalDirective()), + ), FieldNode( + position: Pos( + line: 7, + column: 13, + ), + name: "predecessor", + connections: [ + (FieldConnection( + position: Pos( + line: 8, + column: 17, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 8, + column: 17, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Add(TagRef("value")), + ), + tag: [ + TagDirective( + name: Some("optional_nonexistent"), + ), + ], + )), + )), + ], + )), + ], + )), + (FieldConnection( + position: Pos( + line: 12, + column: 9, + ), + name: "successor", + ), FieldNode( + position: Pos( + line: 12, + column: 9, + ), + name: "successor", + connections: [ + (FieldConnection( + position: Pos( + line: 13, + column: 13, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 13, + column: 13, + ), + name: "value", + filter: [ + FilterDirective( + operation: Equals((), TagRef("optional_nonexistent")), + ), + ], + output: [ + OutputDirective(), + ], + )), + (FieldConnection( + position: Pos( + line: 17, + column: 13, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 17, + column: 13, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Add(TagRef("optional_nonexistent")), + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.graphql.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.graphql.ron new file mode 100644 index 00000000..5ad67735 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.graphql.ron @@ -0,0 +1,32 @@ +TestGraphQLQuery ( + schema_name: "numbers", + // This query's @optional edge doesn't exist for the vertex in question. + // + // It ensures that our @transform implementation does not fail to clean up arguments + // that were prepared for a transform that turned out to not exist + // due to being inside a non-existent optional vertex. + // + // The @transform using that @tag should still produce the correct result: `null`. + // The @filter using that @tag should still be elided, per the usual optional-tag rules. + query: r#" +{ + One { + value @tag + + predecessor { + predecessor @optional { + value @transform(op: "+", value: ["%value"]) @tag(name: "optional_nonexistent") + } + } + + successor { + value + @filter(op: "=", value: ["%optional_nonexistent"]) # this filter is elided + @output # this is `2` + + value @transform(op: "+", value: ["%optional_nonexistent"]) @output # this is `null` + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.ir.ron new file mode 100644 index 00000000..371846ee --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.ir.ron @@ -0,0 +1,109 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "One", + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + ), + Vid(2): IRVertex( + vid: Vid(2), + type_name: "Number", + ), + Vid(3): IRVertex( + vid: Vid(3), + type_name: "Number", + ), + Vid(4): IRVertex( + vid: Vid(4), + type_name: "Number", + filters: [ + Equals(LocalField(LocalField( + field_name: "value", + field_type: "Int", + )), Tag(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(3), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )))), + ], + ), + }, + edges: { + Eid(1): IREdge( + eid: Eid(1), + from_vid: Vid(1), + to_vid: Vid(2), + edge_name: "predecessor", + ), + Eid(2): IREdge( + eid: Eid(2), + from_vid: Vid(2), + to_vid: Vid(3), + edge_name: "predecessor", + optional: true, + ), + Eid(3): IREdge( + eid: Eid(3), + from_vid: Vid(1), + to_vid: Vid(4), + edge_name: "successor", + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(4), + field_name: "value", + field_type: "Int", + )), + "valueadd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(4), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(3), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )))), + ], + ), + tid: Tid(2), + field_type: "Int", + )), + }, + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.output.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.output.ron new file mode 100644 index 00000000..9f83971e --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.output.ron @@ -0,0 +1,21 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "value": Output( + name: "value", + value_type: "Int", + vid: Vid(4), + ), + "valueadd": Output( + name: "valueadd", + value_type: "Int", + vid: Vid(4), + ), + }, + results: [ + { + "value": Int64(2), + "valueadd": Null, + }, + ], +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.trace.ron new file mode 100644 index 00000000..367f91d6 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_nonexistent_optional_transformed_tag.trace.ron @@ -0,0 +1,701 @@ +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(ResolveNeighbors(Vid(1), "Number", Eid(1))), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveNeighbors(Vid(2), "Number", Eid(2))), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: None, + content: Call(ResolveNeighbors(Vid(1), "Number", Eid(3))), + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: None, + content: Call(ResolveProperty(Vid(4), "Number", "value")), + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: None, + content: Call(ResolveProperty(Vid(3), "Number", "value")), + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: None, + content: Call(ResolveProperty(Vid(4), "Number", "value")), + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: None, + content: Call(ResolveProperty(Vid(3), "Number", "value")), + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: None, + content: Call(ResolveProperty(Vid(4), "Number", "value")), + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(11)), + content: AdvanceInputIterator, + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: Some(Opid(10)), + content: AdvanceInputIterator, + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(9)), + content: AdvanceInputIterator, + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(8)), + content: AdvanceInputIterator, + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(7)), + content: AdvanceInputIterator, + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(6)), + content: AdvanceInputIterator, + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(5)), + content: AdvanceInputIterator, + ), + Opid(19): TraceOp( + opid: Opid(19), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Neither(NeitherNumber(1)))), + ), + Opid(23): TraceOp( + opid: Opid(23), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + )), + ), + Opid(24): TraceOp( + opid: Opid(24), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + ))), + ), + Opid(25): TraceOp( + opid: Opid(25), + parent_opid: Some(Opid(24)), + content: YieldFrom(ResolveNeighborsInner(0, Neither(NeitherNumber(0)))), + ), + Opid(26): TraceOp( + opid: Opid(26), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + }, + )), + ), + Opid(27): TraceOp( + opid: Opid(27), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + }, + ))), + ), + Opid(28): TraceOp( + opid: Opid(28), + parent_opid: Some(Opid(27)), + content: OutputIteratorExhausted, + ), + Opid(29): TraceOp( + opid: Opid(29), + parent_opid: Some(Opid(4)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + }, + )), + ), + Opid(30): TraceOp( + opid: Opid(30), + parent_opid: Some(Opid(4)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + }, + ))), + ), + Opid(31): TraceOp( + opid: Opid(31), + parent_opid: Some(Opid(30)), + content: YieldFrom(ResolveNeighborsInner(0, Prime(PrimeNumber(2)))), + ), + Opid(32): TraceOp( + opid: Opid(32), + parent_opid: Some(Opid(5)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + }, + )), + ), + Opid(33): TraceOp( + opid: Opid(33), + parent_opid: Some(Opid(5)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + }, + ), Int64(2))), + ), + Opid(34): TraceOp( + opid: Opid(34), + parent_opid: Some(Opid(6)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + }, + values: [ + Int64(2), + ], + suspended_vertices: [ + Some(Prime(PrimeNumber(2))), + ], + )), + ), + Opid(35): TraceOp( + opid: Opid(35), + parent_opid: Some(Opid(6)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + }, + values: [ + Int64(2), + ], + suspended_vertices: [ + Some(Prime(PrimeNumber(2))), + ], + ), Int64(1))), + ), + Opid(36): TraceOp( + opid: Opid(36), + parent_opid: Some(Opid(7)), + content: YieldInto(SerializableContext( + active_vertex: None, + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + }, + values: [ + Int64(2), + Int64(1), + ], + suspended_vertices: [ + Some(Prime(PrimeNumber(2))), + ], + )), + ), + Opid(37): TraceOp( + opid: Opid(37), + parent_opid: Some(Opid(7)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: None, + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + }, + values: [ + Int64(2), + Int64(1), + ], + suspended_vertices: [ + Some(Prime(PrimeNumber(2))), + ], + ), Null)), + ), + Opid(38): TraceOp( + opid: Opid(38), + parent_opid: Some(Opid(8)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + Vid(4): Some(Prime(PrimeNumber(2))), + }, + )), + ), + Opid(39): TraceOp( + opid: Opid(39), + parent_opid: Some(Opid(8)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + Vid(4): Some(Prime(PrimeNumber(2))), + }, + ), Int64(2))), + ), + Opid(40): TraceOp( + opid: Opid(40), + parent_opid: Some(Opid(9)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + Vid(4): Some(Prime(PrimeNumber(2))), + }, + values: [ + Int64(2), + ], + )), + ), + Opid(41): TraceOp( + opid: Opid(41), + parent_opid: Some(Opid(9)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + Vid(4): Some(Prime(PrimeNumber(2))), + }, + values: [ + Int64(2), + ], + ), Int64(1))), + ), + Opid(42): TraceOp( + opid: Opid(42), + parent_opid: Some(Opid(10)), + content: YieldInto(SerializableContext( + active_vertex: None, + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + Vid(4): Some(Prime(PrimeNumber(2))), + }, + values: [ + Int64(2), + Int64(1), + ], + )), + ), + Opid(43): TraceOp( + opid: Opid(43), + parent_opid: Some(Opid(10)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: None, + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + Vid(4): Some(Prime(PrimeNumber(2))), + }, + values: [ + Int64(2), + Int64(1), + ], + ), Null)), + ), + Opid(44): TraceOp( + opid: Opid(44), + parent_opid: Some(Opid(11)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + Vid(4): Some(Prime(PrimeNumber(2))), + }, + values: [ + Int64(2), + Null, + ], + )), + ), + Opid(45): TraceOp( + opid: Opid(45), + parent_opid: Some(Opid(11)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Neither(NeitherNumber(0))), + Vid(3): None, + Vid(4): Some(Prime(PrimeNumber(2))), + }, + values: [ + Int64(2), + Null, + ], + ), Int64(2))), + ), + Opid(46): TraceOp( + opid: Opid(46), + parent_opid: None, + content: ProduceQueryResult({ + "value": Int64(2), + "valueadd": Null, + }), + ), + Opid(47): TraceOp( + opid: Opid(47), + parent_opid: Some(Opid(11)), + content: AdvanceInputIterator, + ), + Opid(48): TraceOp( + opid: Opid(48), + parent_opid: Some(Opid(10)), + content: AdvanceInputIterator, + ), + Opid(49): TraceOp( + opid: Opid(49), + parent_opid: Some(Opid(9)), + content: AdvanceInputIterator, + ), + Opid(50): TraceOp( + opid: Opid(50), + parent_opid: Some(Opid(8)), + content: AdvanceInputIterator, + ), + Opid(51): TraceOp( + opid: Opid(51), + parent_opid: Some(Opid(7)), + content: AdvanceInputIterator, + ), + Opid(52): TraceOp( + opid: Opid(52), + parent_opid: Some(Opid(6)), + content: AdvanceInputIterator, + ), + Opid(53): TraceOp( + opid: Opid(53), + parent_opid: Some(Opid(5)), + content: AdvanceInputIterator, + ), + Opid(54): TraceOp( + opid: Opid(54), + parent_opid: Some(Opid(30)), + content: OutputIteratorExhausted, + ), + Opid(55): TraceOp( + opid: Opid(55), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(56): TraceOp( + opid: Opid(56), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(57): TraceOp( + opid: Opid(57), + parent_opid: Some(Opid(24)), + content: OutputIteratorExhausted, + ), + Opid(58): TraceOp( + opid: Opid(58), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(59): TraceOp( + opid: Opid(59), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(60): TraceOp( + opid: Opid(60), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(61): TraceOp( + opid: Opid(61), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(62): TraceOp( + opid: Opid(62), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(63): TraceOp( + opid: Opid(63), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + Opid(64): TraceOp( + opid: Opid(64), + parent_opid: Some(Opid(4)), + content: InputIteratorExhausted, + ), + Opid(65): TraceOp( + opid: Opid(65), + parent_opid: Some(Opid(4)), + content: OutputIteratorExhausted, + ), + Opid(66): TraceOp( + opid: Opid(66), + parent_opid: Some(Opid(5)), + content: InputIteratorExhausted, + ), + Opid(67): TraceOp( + opid: Opid(67), + parent_opid: Some(Opid(5)), + content: OutputIteratorExhausted, + ), + Opid(68): TraceOp( + opid: Opid(68), + parent_opid: Some(Opid(6)), + content: InputIteratorExhausted, + ), + Opid(69): TraceOp( + opid: Opid(69), + parent_opid: Some(Opid(6)), + content: OutputIteratorExhausted, + ), + Opid(70): TraceOp( + opid: Opid(70), + parent_opid: Some(Opid(7)), + content: InputIteratorExhausted, + ), + Opid(71): TraceOp( + opid: Opid(71), + parent_opid: Some(Opid(7)), + content: OutputIteratorExhausted, + ), + Opid(72): TraceOp( + opid: Opid(72), + parent_opid: Some(Opid(8)), + content: InputIteratorExhausted, + ), + Opid(73): TraceOp( + opid: Opid(73), + parent_opid: Some(Opid(8)), + content: OutputIteratorExhausted, + ), + Opid(74): TraceOp( + opid: Opid(74), + parent_opid: Some(Opid(9)), + content: InputIteratorExhausted, + ), + Opid(75): TraceOp( + opid: Opid(75), + parent_opid: Some(Opid(9)), + content: OutputIteratorExhausted, + ), + Opid(76): TraceOp( + opid: Opid(76), + parent_opid: Some(Opid(10)), + content: InputIteratorExhausted, + ), + Opid(77): TraceOp( + opid: Opid(77), + parent_opid: Some(Opid(10)), + content: OutputIteratorExhausted, + ), + Opid(78): TraceOp( + opid: Opid(78), + parent_opid: Some(Opid(11)), + content: InputIteratorExhausted, + ), + Opid(79): TraceOp( + opid: Opid(79), + parent_opid: Some(Opid(11)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "One", + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + ), + Vid(2): IRVertex( + vid: Vid(2), + type_name: "Number", + ), + Vid(3): IRVertex( + vid: Vid(3), + type_name: "Number", + ), + Vid(4): IRVertex( + vid: Vid(4), + type_name: "Number", + filters: [ + Equals(LocalField(LocalField( + field_name: "value", + field_type: "Int", + )), Tag(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(3), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )))), + ], + ), + }, + edges: { + Eid(1): IREdge( + eid: Eid(1), + from_vid: Vid(1), + to_vid: Vid(2), + edge_name: "predecessor", + ), + Eid(2): IREdge( + eid: Eid(2), + from_vid: Vid(2), + to_vid: Vid(3), + edge_name: "predecessor", + optional: true, + ), + Eid(3): IREdge( + eid: Eid(3), + from_vid: Vid(1), + to_vid: Vid(4), + edge_name: "successor", + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(4), + field_name: "value", + field_type: "Int", + )), + "valueadd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(4), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(3), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )))), + ], + ), + tid: Tid(2), + field_type: "Int", + )), + }, + ), + ), + ), +) From 2685817869339dfaa24c4a09209182508714ec11 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Sat, 6 Jul 2024 16:36:28 +0000 Subject: [PATCH 27/30] Add test case for `@transform` with a tag that is itself transformed. --- ...rm_with_transformed_tag.graphql-parsed.ron | 85 +++++ ...transform_with_transformed_tag.graphql.ron | 14 + .../transform_with_transformed_tag.ir.ron | 60 ++++ .../transform_with_transformed_tag.output.ron | 15 + .../transform_with_transformed_tag.trace.ron | 294 ++++++++++++++++++ 5 files changed, 468 insertions(+) create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.trace.ron diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.graphql-parsed.ron new file mode 100644 index 00000000..a7436f1d --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.graphql-parsed.ron @@ -0,0 +1,85 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "One", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "One", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + tag: [ + TagDirective(), + ], + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Add(TagRef("value")), + ), + tag: [ + TagDirective( + name: Some("doubled"), + ), + ], + )), + )), + (FieldConnection( + position: Pos( + line: 6, + column: 9, + ), + name: "successor", + ), FieldNode( + position: Pos( + line: 6, + column: 9, + ), + name: "successor", + connections: [ + (FieldConnection( + position: Pos( + line: 7, + column: 13, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 7, + column: 13, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Add(TagRef("doubled")), + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.graphql.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.graphql.ron new file mode 100644 index 00000000..c2a8b1d5 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.graphql.ron @@ -0,0 +1,14 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + One { + value @tag @transform(op: "+", value: ["%value"]) @tag(name: "doubled") + + successor { + value @transform(op: "+", value: ["%doubled"]) @output + } + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.ir.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.ir.ron new file mode 100644 index 00000000..d9c36338 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.ir.ron @@ -0,0 +1,60 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "One", + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + ), + Vid(2): IRVertex( + vid: Vid(2), + type_name: "Number", + ), + }, + edges: { + Eid(1): IREdge( + eid: Eid(1), + from_vid: Vid(1), + to_vid: Vid(2), + edge_name: "successor", + ), + }, + outputs: { + "valueadd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(2), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )))), + ], + ), + tid: Tid(2), + field_type: "Int", + )), + }, + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.output.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.output.ron new file mode 100644 index 00000000..697fa36e --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.output.ron @@ -0,0 +1,15 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "valueadd": Output( + name: "valueadd", + value_type: "Int", + vid: Vid(2), + ), + }, + results: [ + { + "valueadd": Int64(4), + }, + ], +) diff --git a/trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.trace.ron b/trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.trace.ron new file mode 100644 index 00000000..e7239994 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/transform_with_transformed_tag.trace.ron @@ -0,0 +1,294 @@ +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(ResolveNeighbors(Vid(1), "Number", Eid(1))), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: None, + content: Call(ResolveProperty(Vid(2), "Number", "value")), + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: Some(Opid(5)), + content: AdvanceInputIterator, + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Neither(NeitherNumber(1)))), + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + )), + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveNeighborsOuter(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + ))), + ), + 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(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Prime(PrimeNumber(2))), + }, + )), + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Prime(PrimeNumber(2))), + }, + ), Int64(1))), + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(4)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Prime(PrimeNumber(2))), + }, + values: [ + Int64(1), + ], + )), + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(4)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Prime(PrimeNumber(2))), + }, + values: [ + Int64(1), + ], + ), Int64(1))), + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(5)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Prime(PrimeNumber(2))), + }, + values: [ + Int64(2), + ], + )), + ), + Opid(19): TraceOp( + opid: Opid(19), + parent_opid: Some(Opid(5)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + Vid(2): Some(Prime(PrimeNumber(2))), + }, + values: [ + Int64(2), + ], + ), Int64(2))), + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: None, + content: ProduceQueryResult({ + "valueadd": Int64(4), + }), + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: Some(Opid(5)), + content: AdvanceInputIterator, + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: Some(Opid(4)), + content: AdvanceInputIterator, + ), + Opid(23): TraceOp( + opid: Opid(23), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(24): TraceOp( + opid: Opid(24), + parent_opid: Some(Opid(12)), + content: OutputIteratorExhausted, + ), + Opid(25): TraceOp( + opid: Opid(25), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(26): TraceOp( + opid: Opid(26), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(27): TraceOp( + opid: Opid(27), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(28): TraceOp( + opid: Opid(28), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(29): TraceOp( + opid: Opid(29), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(30): TraceOp( + opid: Opid(30), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + Opid(31): TraceOp( + opid: Opid(31), + parent_opid: Some(Opid(4)), + content: InputIteratorExhausted, + ), + Opid(32): TraceOp( + opid: Opid(32), + parent_opid: Some(Opid(4)), + content: OutputIteratorExhausted, + ), + Opid(33): TraceOp( + opid: Opid(33), + parent_opid: Some(Opid(5)), + content: InputIteratorExhausted, + ), + Opid(34): TraceOp( + opid: Opid(34), + parent_opid: Some(Opid(5)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "One", + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + ), + Vid(2): IRVertex( + vid: Vid(2), + type_name: "Number", + ), + }, + edges: { + Eid(1): IREdge( + eid: Eid(1), + from_vid: Vid(1), + to_vid: Vid(2), + edge_name: "successor", + ), + }, + outputs: { + "valueadd": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(2), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Add(Tag(ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )))), + ], + ), + tid: Tid(1), + field_type: "Int", + )))), + ], + ), + tid: Tid(2), + field_type: "Int", + )), + }, + ), + ), + ), +) From bf0bdcc0e913132506754b1fca2b75e2fed6bd5b Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Sat, 6 Jul 2024 23:50:35 +0000 Subject: [PATCH 28/30] Add test coverage for cyclic uses of transform + tags. --- ...in_back_and_forth_order.frontend-error.ron | 1 + ...in_back_and_forth_order.graphql-parsed.ron | 95 +++++++++++++++++++ ...g_used_in_back_and_forth_order.graphql.ron | 34 +++++++ ...e_manner_in_same_vertex.frontend-error.ron | 4 + ...e_manner_in_same_vertex.graphql-parsed.ron | 81 ++++++++++++++++ ...ecursive_manner_in_same_vertex.graphql.ron | 22 +++++ 6 files changed, 237 insertions(+) create mode 100644 trustfall_core/test_data/tests/frontend_errors/tag_used_in_back_and_forth_order.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/tag_used_in_back_and_forth_order.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/tag_used_in_back_and_forth_order.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/tag_used_in_cyclic_corecursive_manner_in_same_vertex.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/tag_used_in_cyclic_corecursive_manner_in_same_vertex.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/tag_used_in_cyclic_corecursive_manner_in_same_vertex.graphql.ron diff --git a/trustfall_core/test_data/tests/frontend_errors/tag_used_in_back_and_forth_order.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/tag_used_in_back_and_forth_order.frontend-error.ron new file mode 100644 index 00000000..639718a8 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/tag_used_in_back_and_forth_order.frontend-error.ron @@ -0,0 +1 @@ +Err(UndefinedTagInTransform("transformed field \"value.add\"", "len")) diff --git a/trustfall_core/test_data/tests/frontend_errors/tag_used_in_back_and_forth_order.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/tag_used_in_back_and_forth_order.graphql-parsed.ron new file mode 100644 index 00000000..7d33a103 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/tag_used_in_back_and_forth_order.graphql-parsed.ron @@ -0,0 +1,95 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 16, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 16, + column: 9, + ), + name: "value", + tag: [ + TagDirective( + name: Some("self"), + ), + ], + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Add(TagRef("self")), + ), + output: [ + OutputDirective(), + ], + tag: [ + TagDirective( + name: Some("transformed"), + ), + ], + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Add(TagRef("len")), + ), + output: [ + OutputDirective(), + ], + )), + )), + )), + (FieldConnection( + position: Pos( + line: 24, + column: 9, + ), + name: "vowelsInName", + ), FieldNode( + position: Pos( + line: 24, + column: 9, + ), + name: "vowelsInName", + transform_group: Some(TransformGroup( + tid: Tid(3), + transform: TransformDirective( + kind: Len, + ), + retransform: Some(TransformGroup( + tid: Tid(4), + transform: TransformDirective( + kind: Add(TagRef("transformed")), + ), + output: [ + OutputDirective(), + ], + tag: [ + TagDirective( + name: Some("len"), + ), + ], + )), + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/tag_used_in_back_and_forth_order.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/tag_used_in_back_and_forth_order.graphql.ron new file mode 100644 index 00000000..bcc308bf --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/tag_used_in_back_and_forth_order.graphql.ron @@ -0,0 +1,34 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + # We always require that tags are defined before use in order to statically prevent cycles, + # so this query produces an error: the tag `len` is used before being defined. + # + # In the future, this query may become valid, at the cost of substantially increased + # frontend complexity. + # + # In the meantime, we require users to rewrite queries like this in a fashion that + # orders tag uses after their definition. There are several ways to do that for this query: + # - Replace the `@transform(op: "+", value: ["%len"])` with a tag, and perform the addition + # of that tag to the `len` value instead. + # - Duplicate the portion of the `value` property that uses the `len` tag, moving it to + # after `len` has already been defined so that it is allowed to use the `len` value. + value + @tag(name: "self") + @transform(op: "+", value: ["%self"]) + @tag(name: "transformed") + @output + @transform(op: "+", value: ["%len"]) + @output + + vowelsInName + @transform(op: "len") + @transform(op: "+", value: ["%transformed"]) + @output + @tag(name: "len") + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/tag_used_in_cyclic_corecursive_manner_in_same_vertex.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/tag_used_in_cyclic_corecursive_manner_in_same_vertex.frontend-error.ron new file mode 100644 index 00000000..98b0784b --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/tag_used_in_cyclic_corecursive_manner_in_same_vertex.frontend-error.ron @@ -0,0 +1,4 @@ +Err(MultipleErrors(DisplayVec([ + UndefinedTagInTransform("property \"value\"", "cyclic"), + UndefinedTagInTransform("transformed field \"vowelsInName.len\"", "transformed"), +]))) diff --git a/trustfall_core/test_data/tests/frontend_errors/tag_used_in_cyclic_corecursive_manner_in_same_vertex.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/tag_used_in_cyclic_corecursive_manner_in_same_vertex.graphql-parsed.ron new file mode 100644 index 00000000..53f718d5 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/tag_used_in_cyclic_corecursive_manner_in_same_vertex.graphql-parsed.ron @@ -0,0 +1,81 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 7, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 7, + column: 9, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Add(TagRef("cyclic")), + ), + output: [ + OutputDirective(), + ], + tag: [ + TagDirective( + name: Some("transformed"), + ), + ], + )), + )), + (FieldConnection( + position: Pos( + line: 12, + column: 9, + ), + name: "vowelsInName", + ), FieldNode( + position: Pos( + line: 12, + column: 9, + ), + name: "vowelsInName", + transform_group: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Len, + ), + retransform: Some(TransformGroup( + tid: Tid(3), + transform: TransformDirective( + kind: Add(TagRef("transformed")), + ), + output: [ + OutputDirective(), + ], + tag: [ + TagDirective( + name: Some("cyclic"), + ), + ], + )), + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/tag_used_in_cyclic_corecursive_manner_in_same_vertex.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/tag_used_in_cyclic_corecursive_manner_in_same_vertex.graphql.ron new file mode 100644 index 00000000..bc6614a9 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/tag_used_in_cyclic_corecursive_manner_in_same_vertex.graphql.ron @@ -0,0 +1,22 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + # The "transformed" and the "cyclic" tags form a dependency cycle: + # to evaluate either of them, we need the value of the other. + # No value can be computed here, so this is an error. + value + @transform(op: "+", value: ["%cyclic"]) + @tag(name: "transformed") + @output + + vowelsInName + @transform(op: "len") + @transform(op: "+", value: ["%transformed"]) + @output + @tag(name: "cyclic") + } +}"#, + arguments: {}, +) From 009363e21057dfec8f8b7a36ffa3b559a77cd87e Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Sun, 7 Jul 2024 14:19:12 +0000 Subject: [PATCH 29/30] Implement `sqrt` transform. --- trustfall_core/src/frontend/mod.rs | 19 +- .../src/graphql_query/directives.rs | 6 + .../src/interpreter/transformation.rs | 21 +- trustfall_core/src/ir/mod.rs | 5 + ...df_transform_and_output.graphql-parsed.ron | 59 +++ ...sqrt_addf_transform_and_output.graphql.ron | 16 + ...erty_sqrt_addf_transform_and_output.ir.ron | 52 +++ ..._sqrt_addf_transform_and_output.output.ron | 33 ++ ...y_sqrt_addf_transform_and_output.trace.ron | 382 ++++++++++++++++++ ...rt_transform_and_output.graphql-parsed.ron | 53 +++ ...then_sqrt_transform_and_output.graphql.ron | 13 + ...sqrt_then_sqrt_transform_and_output.ir.ron | 38 ++ ..._then_sqrt_transform_and_output.output.ron | 15 + ...t_then_sqrt_transform_and_output.trace.ron | 119 ++++++ 14 files changed, 828 insertions(+), 3 deletions(-) create mode 100644 trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.trace.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.graphql.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.ir.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.output.ron create mode 100644 trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.trace.ron diff --git a/trustfall_core/src/frontend/mod.rs b/trustfall_core/src/frontend/mod.rs index 46e93c4a..4ed434a9 100644 --- a/trustfall_core/src/frontend/mod.rs +++ b/trustfall_core/src/frontend/mod.rs @@ -411,7 +411,7 @@ fn process_variable_uses_in_transforms( process_variable_use_in_field_ref(variables, tag, errors); } }, - Transform::Len | Transform::Abs => { + Transform::Sqrt | Transform::Len | Transform::Abs => { // These transforms don't take operands, so no variables here. } } @@ -1527,6 +1527,23 @@ fn extract_transform_and_next_type_from_directive( .into()) } } + TransformOp::Sqrt => { + let base_type = type_so_far.base_type(); + if base_type == "Int" || base_type == "Float" { + // The resulting type is a nullable float even if the input is non-nullable, + // since we define `sqrt()` of a negative number to be `null`. + Ok((Transform::Sqrt, Type::new_named_type("Float", true))) + } else { + Err(TransformTypeError::operation_requires_different_choice_of_type_subject( + transform_directive.kind.op_name(), + &Type::new_named_type("Int", true), + &Type::new_named_type("Float", true), + represent_subject(), + type_so_far, + ) + .into()) + } + } TransformOp::Add(arg) => { let base_type = type_so_far.base_type(); if base_type != "Int" { diff --git a/trustfall_core/src/graphql_query/directives.rs b/trustfall_core/src/graphql_query/directives.rs index 23bba5b2..7fef4146 100644 --- a/trustfall_core/src/graphql_query/directives.rs +++ b/trustfall_core/src/graphql_query/directives.rs @@ -443,6 +443,10 @@ impl TryFrom<&Positioned> for TransformDirective { assert_operand_count(&transform_value, 0, operands_span)?; TransformOp::Abs } + "sqrt" => { + assert_operand_count(&transform_value, 0, operands_span)?; + TransformOp::Sqrt + } "+" => { assert_operand_count(&transform_value, 1, operands_span)?; TransformOp::Add(transform_value.pop().unwrap()) @@ -494,6 +498,7 @@ pub(crate) enum TransformOp { Count, Len, Abs, + Sqrt, Add(OperatorArgument), AddF(OperatorArgument), } @@ -508,6 +513,7 @@ impl TransformOp { TransformOp::Count => "count", TransformOp::Len => "len", TransformOp::Abs => "abs", + TransformOp::Sqrt => "sqrt", TransformOp::Add(_) => "+", TransformOp::AddF(_) => "+f", } diff --git a/trustfall_core/src/interpreter/transformation.rs b/trustfall_core/src/interpreter/transformation.rs index 01566b5a..00509c5d 100644 --- a/trustfall_core/src/interpreter/transformation.rs +++ b/trustfall_core/src/interpreter/transformation.rs @@ -75,7 +75,7 @@ pub(super) fn push_transform_argument_tag_values_onto_stack<'query, AdapterT: Ad } Argument::Variable(..) => {} }, - Transform::Len | Transform::Abs => { + Transform::Sqrt | Transform::Len | Transform::Abs => { // No tag arguments here! } } @@ -124,7 +124,7 @@ pub(super) fn drop_unused_transform_arguments( } Argument::Variable(..) => {} }, - Transform::Len | Transform::Abs => {} + Transform::Sqrt | Transform::Len | Transform::Abs => {} } } } @@ -152,6 +152,7 @@ fn apply_one_transform( match transform { Transform::Len => apply_len_transform(value), Transform::Abs => apply_abs_transform(value), + Transform::Sqrt => apply_sqrt_transform(value), Transform::Add(argument) => match argument { Argument::Variable(var) => { let operand = &variables[&var.variable_name]; @@ -203,6 +204,22 @@ fn apply_abs_transform(value: &FieldValue) -> FieldValue { } } +#[inline] +fn apply_sqrt_transform(value: &FieldValue) -> FieldValue { + let input = match value { + FieldValue::Null => return FieldValue::Null, + FieldValue::Int64(x) => *x as f64, + FieldValue::Uint64(x) => *x as f64, + FieldValue::Float64(x) => *x, + _ => unreachable!("{value:?}"), + }; + if input.is_sign_negative() { + FieldValue::NULL + } else { + FieldValue::Float64(input.sqrt()) + } +} + #[inline] fn apply_add_transform(value: &FieldValue, operand: &FieldValue) -> FieldValue { match (value, operand) { diff --git a/trustfall_core/src/ir/mod.rs b/trustfall_core/src/ir/mod.rs index 2803daae..03807d68 100644 --- a/trustfall_core/src/ir/mod.rs +++ b/trustfall_core/src/ir/mod.rs @@ -810,6 +810,10 @@ pub enum Transform { /// Null on null inputs. Abs, + /// Square root. Allowed on both integer and floating-point types. + /// Null on null or negative inputs. + Sqrt, + /// Integer addition. For floating-point addition, use [`Transform::AddF`] instead. Add(Argument), @@ -828,6 +832,7 @@ impl Transform { match self { Self::Len => "len", Self::Abs => "abs", + Self::Sqrt => "sqrt", Self::Add(..) => "add", Self::AddF(..) => "addf", } diff --git a/trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.graphql-parsed.ron new file mode 100644 index 00000000..b9b469ca --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.graphql-parsed.ron @@ -0,0 +1,59 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(2), + "min": Int64(-1), + }, + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + output: [ + OutputDirective(), + ], + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Sqrt, + ), + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: AddF(VariableRef("add")), + ), + output: [ + OutputDirective(), + ], + )), + )), + )), + ], + ), + ), + arguments: { + "add": Float64(3.14), + }, +)) diff --git a/trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.graphql.ron b/trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.graphql.ron new file mode 100644 index 00000000..11cf2952 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.graphql.ron @@ -0,0 +1,16 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: -1, max: 2) { + value + @output + @transform(op: "sqrt") + @transform(op: "+f", value: ["$add"]) + @output + } +}"#, + arguments: { + "add": Float64(3.14), + }, +) diff --git a/trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.ir.ron b/trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.ir.ron new file mode 100644 index 00000000..f3c501bf --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.ir.ron @@ -0,0 +1,52 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(2), + "min": Int64(-1), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + "valuesqrtaddf": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Sqrt, + AddF(Variable(VariableRef( + variable_name: "add", + variable_type: "Float!", + ))), + ], + ), + tid: Tid(2), + field_type: "Float", + )), + }, + ), + variables: { + "add": "Float!", + }, + ), + arguments: { + "add": Float64(3.14), + }, +)) diff --git a/trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.output.ron b/trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.output.ron new file mode 100644 index 00000000..15f6d59e --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.output.ron @@ -0,0 +1,33 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "value": Output( + name: "value", + value_type: "Int", + vid: Vid(1), + ), + "valuesqrtaddf": Output( + name: "valuesqrtaddf", + value_type: "Float", + vid: Vid(1), + ), + }, + results: [ + { + "value": Int64(-1), + "valuesqrtaddf": Null, + }, + { + "value": Int64(0), + "valuesqrtaddf": Float64(3.14), + }, + { + "value": Int64(1), + "valuesqrtaddf": Float64(4.140000000000001), + }, + { + "value": Int64(2), + "valuesqrtaddf": Float64(4.5542135623730955), + }, + ], +) diff --git a/trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.trace.ron b/trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.trace.ron new file mode 100644 index 00000000..63da39fc --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_sqrt_addf_transform_and_output.trace.ron @@ -0,0 +1,382 @@ +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(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: None, + content: Call(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Prime(PrimeNumber(-1)))), + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(-1))), + }, + )), + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(-1))), + }, + ), Int64(-1))), + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(-1))), + }, + values: [ + Int64(-1), + ], + )), + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(-1))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(-1))), + }, + values: [ + Int64(-1), + ], + ), Int64(-1))), + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: None, + content: ProduceQueryResult({ + "value": Int64(-1), + "valuesqrtaddf": Null, + }), + ), + Opid(12): TraceOp( + opid: Opid(12), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(13): TraceOp( + opid: Opid(13), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(14): TraceOp( + opid: Opid(14), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Neither(NeitherNumber(0)))), + ), + Opid(15): TraceOp( + opid: Opid(15), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + )), + ), + Opid(16): TraceOp( + opid: Opid(16), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + ), Int64(0))), + ), + Opid(17): TraceOp( + opid: Opid(17), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + values: [ + Int64(0), + ], + )), + ), + Opid(18): TraceOp( + opid: Opid(18), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(0))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(0))), + }, + values: [ + Int64(0), + ], + ), Int64(0))), + ), + Opid(19): TraceOp( + opid: Opid(19), + parent_opid: None, + content: ProduceQueryResult({ + "value": Int64(0), + "valuesqrtaddf": Float64(3.14), + }), + ), + Opid(20): TraceOp( + opid: Opid(20), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(21): TraceOp( + opid: Opid(21), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(22): TraceOp( + opid: Opid(22), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Neither(NeitherNumber(1)))), + ), + Opid(23): TraceOp( + opid: Opid(23), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + )), + ), + Opid(24): TraceOp( + opid: Opid(24), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + ), Int64(1))), + ), + Opid(25): TraceOp( + opid: Opid(25), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(1), + ], + )), + ), + Opid(26): TraceOp( + opid: Opid(26), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Neither(NeitherNumber(1))), + vertices: { + Vid(1): Some(Neither(NeitherNumber(1))), + }, + values: [ + Int64(1), + ], + ), Int64(1))), + ), + Opid(27): TraceOp( + opid: Opid(27), + parent_opid: None, + content: ProduceQueryResult({ + "value": Int64(1), + "valuesqrtaddf": Float64(4.140000000000001), + }), + ), + Opid(28): TraceOp( + opid: Opid(28), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(29): TraceOp( + opid: Opid(29), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(30): TraceOp( + opid: Opid(30), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Prime(PrimeNumber(2)))), + ), + Opid(31): TraceOp( + opid: Opid(31), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(2))), + }, + )), + ), + Opid(32): TraceOp( + opid: Opid(32), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(2))), + }, + ), Int64(2))), + ), + Opid(33): TraceOp( + opid: Opid(33), + parent_opid: Some(Opid(3)), + content: YieldInto(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(2))), + }, + values: [ + Int64(2), + ], + )), + ), + Opid(34): TraceOp( + opid: Opid(34), + parent_opid: Some(Opid(3)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Prime(PrimeNumber(2))), + vertices: { + Vid(1): Some(Prime(PrimeNumber(2))), + }, + values: [ + Int64(2), + ], + ), Int64(2))), + ), + Opid(35): TraceOp( + opid: Opid(35), + parent_opid: None, + content: ProduceQueryResult({ + "value": Int64(2), + "valuesqrtaddf": Float64(4.5542135623730955), + }), + ), + Opid(36): TraceOp( + opid: Opid(36), + parent_opid: Some(Opid(3)), + content: AdvanceInputIterator, + ), + Opid(37): TraceOp( + opid: Opid(37), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(38): TraceOp( + opid: Opid(38), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(39): TraceOp( + opid: Opid(39), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(40): TraceOp( + opid: Opid(40), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + Opid(41): TraceOp( + opid: Opid(41), + parent_opid: Some(Opid(3)), + content: InputIteratorExhausted, + ), + Opid(42): TraceOp( + opid: Opid(42), + parent_opid: Some(Opid(3)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(2), + "min": Int64(-1), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + ), + }, + outputs: { + "value": ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + "valuesqrtaddf": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Sqrt, + AddF(Variable(VariableRef( + variable_name: "add", + variable_type: "Float!", + ))), + ], + ), + tid: Tid(2), + field_type: "Float", + )), + }, + ), + variables: { + "add": "Float!", + }, + ), + arguments: { + "add": Float64(3.14), + }, + ), +) diff --git a/trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.graphql-parsed.ron b/trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.graphql-parsed.ron new file mode 100644 index 00000000..c86cd0f2 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.graphql-parsed.ron @@ -0,0 +1,53 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + arguments: { + "max": Int64(9), + "min": Int64(9), + }, + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Number", + connections: [ + (FieldConnection( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + ), FieldNode( + position: Pos( + line: 4, + column: 9, + ), + name: "value", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Sqrt, + ), + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Sqrt, + ), + output: [ + OutputDirective(), + ], + )), + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.graphql.ron b/trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.graphql.ron new file mode 100644 index 00000000..44af0a88 --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.graphql.ron @@ -0,0 +1,13 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Number(min: 9, max: 9) { + value + @transform(op: "sqrt") + @transform(op: "sqrt") + @output + } +}"#, + arguments: {}, +) diff --git a/trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.ir.ron b/trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.ir.ron new file mode 100644 index 00000000..37a7c81a --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.ir.ron @@ -0,0 +1,38 @@ +Ok(TestIRQuery( + schema_name: "numbers", + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(9), + "min": Int64(9), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + ), + }, + outputs: { + "valuesqrtsqrt": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Sqrt, + Sqrt, + ], + ), + tid: Tid(2), + field_type: "Float", + )), + }, + ), + ), +)) diff --git a/trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.output.ron b/trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.output.ron new file mode 100644 index 00000000..adbfd5dd --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.output.ron @@ -0,0 +1,15 @@ +TestInterpreterOutputData( + schema_name: "numbers", + outputs: { + "valuesqrtsqrt": Output( + name: "valuesqrtsqrt", + value_type: "Float", + vid: Vid(1), + ), + }, + results: [ + { + "valuesqrtsqrt": Float64(1.7320508075688772), + }, + ], +) diff --git a/trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.trace.ron b/trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.trace.ron new file mode 100644 index 00000000..078dacef --- /dev/null +++ b/trustfall_core/test_data/tests/valid_queries/property_sqrt_then_sqrt_transform_and_output.trace.ron @@ -0,0 +1,119 @@ +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(ResolveProperty(Vid(1), "Number", "value")), + ), + Opid(3): TraceOp( + opid: Opid(3), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(4): TraceOp( + opid: Opid(4), + parent_opid: Some(Opid(1)), + content: YieldFrom(ResolveStartingVertices(Composite(CompositeNumber(9, [ + 3, + ])))), + ), + Opid(5): TraceOp( + opid: Opid(5), + parent_opid: Some(Opid(2)), + content: YieldInto(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(9, [ + 3, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(9, [ + 3, + ]))), + }, + )), + ), + Opid(6): TraceOp( + opid: Opid(6), + parent_opid: Some(Opid(2)), + content: YieldFrom(ResolveProperty(SerializableContext( + active_vertex: Some(Composite(CompositeNumber(9, [ + 3, + ]))), + vertices: { + Vid(1): Some(Composite(CompositeNumber(9, [ + 3, + ]))), + }, + ), Int64(9))), + ), + Opid(7): TraceOp( + opid: Opid(7), + parent_opid: None, + content: ProduceQueryResult({ + "valuesqrtsqrt": Float64(1.7320508075688772), + }), + ), + Opid(8): TraceOp( + opid: Opid(8), + parent_opid: Some(Opid(2)), + content: AdvanceInputIterator, + ), + Opid(9): TraceOp( + opid: Opid(9), + parent_opid: Some(Opid(1)), + content: OutputIteratorExhausted, + ), + Opid(10): TraceOp( + opid: Opid(10), + parent_opid: Some(Opid(2)), + content: InputIteratorExhausted, + ), + Opid(11): TraceOp( + opid: Opid(11), + parent_opid: Some(Opid(2)), + content: OutputIteratorExhausted, + ), + }, + ir_query: IRQuery( + root_name: "Number", + root_parameters: EdgeParameters( + contents: { + "max": Int64(9), + "min": Int64(9), + }, + ), + root_component: IRQueryComponent( + root: Vid(1), + vertices: { + Vid(1): IRVertex( + vid: Vid(1), + type_name: "Number", + ), + }, + outputs: { + "valuesqrtsqrt": TransformedField(TransformedField( + value: TransformedValue( + base: ContextField(ContextField( + vertex_id: Vid(1), + field_name: "value", + field_type: "Int", + )), + transforms: [ + Sqrt, + Sqrt, + ], + ), + tid: Tid(2), + field_type: "Float", + )), + }, + ), + ), + ), +) From bed2ac58fa239c1b69c66d7c92fd8984ebfef790 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Sun, 7 Jul 2024 16:24:27 +0000 Subject: [PATCH 30/30] Improve transform argument type inference test coverage. --- ...inferred_variable_types.frontend-error.ron | 1 + ...inferred_variable_types.graphql-parsed.ron | 89 +++++++++++++++++++ ...nsform_inferred_variable_types.graphql.ron | 23 +++++ ...ppropriate_type_op_sqrt.frontend-error.ron | 1 + ...ppropriate_type_op_sqrt.graphql-parsed.ron | 43 +++++++++ ..._on_inappropriate_type_op_sqrt.graphql.ron | 12 +++ 6 files changed, 169 insertions(+) create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_retransform_inferred_variable_types.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_retransform_inferred_variable_types.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_retransform_inferred_variable_types.graphql.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_sqrt.frontend-error.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_sqrt.graphql-parsed.ron create mode 100644 trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_sqrt.graphql.ron diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_retransform_inferred_variable_types.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_retransform_inferred_variable_types.frontend-error.ron new file mode 100644 index 00000000..215e6e3c --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_retransform_inferred_variable_types.frontend-error.ron @@ -0,0 +1 @@ +Err(IncompatibleVariableTypeRequirements("arg", "Int!", "Float!")) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_retransform_inferred_variable_types.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_retransform_inferred_variable_types.graphql-parsed.ron new file mode 100644 index 00000000..5d33fa3f --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_retransform_inferred_variable_types.graphql-parsed.ron @@ -0,0 +1,89 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 8, + column: 9, + ), + name: "vowelsInName", + ), FieldNode( + position: Pos( + line: 8, + column: 9, + ), + name: "vowelsInName", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Len, + ), + retransform: Some(TransformGroup( + tid: Tid(2), + transform: TransformDirective( + kind: Add(VariableRef("arg")), + ), + output: [ + OutputDirective(), + ], + )), + )), + )), + (FieldConnection( + position: Pos( + line: 10, + column: 9, + ), + name: "predecessor", + fold: Some(FoldGroup( + fold: FoldDirective(), + transform: Some(TransformGroup( + tid: Tid(3), + transform: TransformDirective( + kind: Count, + ), + retransform: Some(TransformGroup( + tid: Tid(4), + transform: TransformDirective( + kind: Sqrt, + ), + retransform: Some(TransformGroup( + tid: Tid(5), + transform: TransformDirective( + kind: AddF(VariableRef("arg")), + ), + output: [ + OutputDirective(), + ], + )), + )), + )), + )), + ), FieldNode( + position: Pos( + line: 10, + column: 9, + ), + name: "predecessor", + )), + ], + ), + ), + arguments: { + "arg": Int64(0), + }, +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_retransform_inferred_variable_types.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_retransform_inferred_variable_types.graphql.ron new file mode 100644 index 00000000..50960ab8 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/incompatible_retransform_vs_retransform_inferred_variable_types.graphql.ron @@ -0,0 +1,23 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + # There are incompatible demands for the type of `$arg` here: + # it needs to be an `Int!` to fit the first transform, + # and a `Float!` to fit the second transform. + # This is a frontend error. + vowelsInName @transform(op: "len") @transform(op: "+", value: ["$arg"]) @output + + predecessor + @fold + @transform(op: "count") + @transform(op: "sqrt") + @transform(op: "+f", value: ["$arg"]) + @output + } +}"#, + arguments: { + "arg": Int64(0), + }, +) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_sqrt.frontend-error.ron b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_sqrt.frontend-error.ron new file mode 100644 index 00000000..5283c425 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_sqrt.frontend-error.ron @@ -0,0 +1 @@ +Err(TransformTypeError(TypeMismatchBetweenTransformOperationAndSubject("sqrt", "values of type \"Int\" or \"Float\"", "property \"name\"", "String"))) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_sqrt.graphql-parsed.ron b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_sqrt.graphql-parsed.ron new file mode 100644 index 00000000..b214a649 --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_sqrt.graphql-parsed.ron @@ -0,0 +1,43 @@ +Ok(TestParsedGraphQLQuery( + schema_name: "numbers", + query: Query( + root_connection: FieldConnection( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + ), + root_field: FieldNode( + position: Pos( + line: 3, + column: 5, + ), + name: "Two", + connections: [ + (FieldConnection( + position: Pos( + line: 6, + column: 9, + ), + name: "name", + ), FieldNode( + position: Pos( + line: 6, + column: 9, + ), + name: "name", + transform_group: Some(TransformGroup( + tid: Tid(1), + transform: TransformDirective( + kind: Sqrt, + ), + output: [ + OutputDirective(), + ], + )), + )), + ], + ), + ), +)) diff --git a/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_sqrt.graphql.ron b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_sqrt.graphql.ron new file mode 100644 index 00000000..d16de5cb --- /dev/null +++ b/trustfall_core/test_data/tests/frontend_errors/transform_applied_on_inappropriate_type_op_sqrt.graphql.ron @@ -0,0 +1,12 @@ +TestGraphQLQuery ( + schema_name: "numbers", + query: r#" +{ + Two { + # The "sqrt" transform operator does not operate on `String` values. + # This is an error. + name @transform(op: "sqrt") @output + } +}"#, + arguments: {}, +)