@@ -759,6 +759,74 @@ impl CypherQuery {
759759 message : "Graph configuration is required for execution" . to_string ( ) ,
760760 location : snafu:: Location :: new ( file ! ( ) , line ! ( ) , column ! ( ) ) ,
761761 } ) ?;
762+
763+ // Handle single-segment variable-length paths by unrolling ranges (*1..N, capped)
764+ if path. segments . len ( ) == 1 {
765+ if let Some ( length_range) = & path. segments [ 0 ] . relationship . length {
766+ let cap: u32 = crate :: MAX_VARIABLE_LENGTH_HOPS ;
767+ let min_len = length_range. min . unwrap_or ( 1 ) . max ( 1 ) ;
768+ let max_len = length_range. max . unwrap_or ( cap) ;
769+
770+ if min_len > max_len {
771+ return Err ( GraphError :: InvalidPattern {
772+ message : format ! (
773+ "Invalid variable-length range: min {:?} greater than max {:?}" ,
774+ length_range. min, length_range. max
775+ ) ,
776+ location : snafu:: Location :: new ( file ! ( ) , line ! ( ) , column ! ( ) ) ,
777+ } ) ;
778+ }
779+
780+ if max_len > cap {
781+ return Err ( GraphError :: UnsupportedFeature {
782+ feature : format ! (
783+ "Variable-length paths with length > {} are not supported (got {:?}..{:?})" ,
784+ cap, length_range. min, length_range. max
785+ ) ,
786+ location : snafu:: Location :: new ( file ! ( ) , line ! ( ) , column ! ( ) ) ,
787+ } ) ;
788+ }
789+
790+ use datafusion:: dataframe:: DataFrame ;
791+ let mut union_df: Option < DataFrame > = None ;
792+
793+ for hops in min_len..=max_len {
794+ // Build a fixed-length synthetic path by repeating the single segment
795+ let mut synthetic = crate :: ast:: PathPattern {
796+ start_node : path. start_node . clone ( ) ,
797+ segments : Vec :: with_capacity ( hops as usize ) ,
798+ } ;
799+
800+ for i in 0 ..hops {
801+ let mut seg = path. segments [ 0 ] . clone ( ) ;
802+ // Drop variables to avoid alias collisions on repeated hops
803+ seg. relationship . variable = None ;
804+ if ( i + 1 ) < hops {
805+ seg. end_node . variable = None ; // intermediate hop
806+ }
807+ // Clear length spec for this fixed hop
808+ seg. relationship . length = None ;
809+ synthetic. segments . push ( seg) ;
810+ }
811+
812+ let exec = PathExecutor :: new ( ctx, cfg, & synthetic) ?;
813+ let mut df = exec. build_chain ( ) . await ?;
814+ df = exec. apply_where ( df, & self . ast ) ?;
815+ df = exec. apply_return ( df, & self . ast ) ?;
816+
817+ union_df = Some ( match union_df {
818+ Some ( acc) => acc. union ( df) . map_err ( |e| GraphError :: PlanError {
819+ message : format ! ( "Failed to UNION variable-length paths: {}" , e) ,
820+ location : snafu:: Location :: new ( file ! ( ) , line ! ( ) , column ! ( ) ) ,
821+ } ) ?,
822+ None => df,
823+ } ) ;
824+ }
825+
826+ return Ok ( union_df) ;
827+ }
828+ }
829+
762830 let exec = PathExecutor :: new ( ctx, cfg, path) ?;
763831 let df = exec. build_chain ( ) . await ?;
764832 let df = exec. apply_where ( df, & self . ast ) ?;
0 commit comments