@@ -504,6 +504,63 @@ impl IndexJoin {
504504 }
505505 }
506506 }
507+
508+ // Convert this index join to an inner join, followed by a projection.
509+ // This is needed for incremental evaluation of index joins.
510+ // In particular when there are updates to both the left and right tables.
511+ // In other words, when an index join has two delta tables.
512+ pub fn to_inner_join ( self ) -> QueryExpr {
513+ if self . return_index_rows {
514+ let col_lhs = self . index_side . head ( ) . fields [ usize:: from ( self . index_col ) ] . field . clone ( ) ;
515+ let col_rhs = self . probe_field ;
516+ let rhs = self . probe_side ;
517+
518+ let fields = self
519+ . index_side
520+ . head ( )
521+ . fields
522+ . iter ( )
523+ . cloned ( )
524+ . map ( |Column { field, .. } | field. into ( ) )
525+ . collect ( ) ;
526+
527+ let table = self . index_side . get_db_table ( ) . map ( |t| t. table_id ) ;
528+ let source = self . index_side . into ( ) ;
529+ let inner_join = Query :: JoinInner ( JoinExpr :: new ( rhs, col_lhs, col_rhs) ) ;
530+ let project = Query :: Project ( fields, table) ;
531+ let query = if let Some ( predicate) = self . index_select {
532+ vec ! [ predicate. into( ) , inner_join, project]
533+ } else {
534+ vec ! [ inner_join, project]
535+ } ;
536+ QueryExpr { source, query }
537+ } else {
538+ let col_rhs = self . index_side . head ( ) . fields [ usize:: from ( self . index_col ) ] . field . clone ( ) ;
539+ let col_lhs = self . probe_field ;
540+ let mut rhs: QueryExpr = self . index_side . into ( ) ;
541+
542+ if let Some ( predicate) = self . index_select {
543+ rhs. query . push ( predicate. into ( ) ) ;
544+ }
545+
546+ let fields = self
547+ . probe_side
548+ . source
549+ . head ( )
550+ . fields
551+ . iter ( )
552+ . cloned ( )
553+ . map ( |Column { field, .. } | field. into ( ) )
554+ . collect ( ) ;
555+
556+ let table = self . probe_side . source . get_db_table ( ) . map ( |t| t. table_id ) ;
557+ let source = self . probe_side . source ;
558+ let inner_join = Query :: JoinInner ( JoinExpr :: new ( rhs, col_lhs, col_rhs) ) ;
559+ let project = Query :: Project ( fields, table) ;
560+ let query = vec ! [ inner_join, project] ;
561+ QueryExpr { source, query }
562+ }
563+ }
507564}
508565
509566#[ derive( Debug , Clone , Eq , PartialEq , PartialOrd , Ord ) ]
@@ -513,25 +570,6 @@ pub struct JoinExpr {
513570 pub col_rhs : FieldName ,
514571}
515572
516- //TODO: A posterior PR should fix the pass of multi-columns to the query optimization
517- impl From < IndexJoin > for JoinExpr {
518- fn from ( mut value : IndexJoin ) -> Self {
519- if let Some ( predicate) = value. index_select {
520- value. probe_side . query . push ( predicate. into ( ) ) ;
521- } ;
522- let pos = value. index_col ;
523- if value. return_index_rows {
524- let col_lhs = value. index_side . head ( ) . fields [ usize:: from ( pos) ] . field . clone ( ) ;
525- let col_rhs = value. probe_field ;
526- JoinExpr :: new ( value. probe_side , col_lhs, col_rhs)
527- } else {
528- let col_rhs = value. index_side . head ( ) . fields [ usize:: from ( pos) ] . field . clone ( ) ;
529- let col_lhs = value. probe_field ;
530- JoinExpr :: new ( value. index_side . into ( ) , col_lhs, col_rhs)
531- }
532- }
533- }
534-
535573impl JoinExpr {
536574 pub fn new ( rhs : QueryExpr , col_lhs : FieldName , col_rhs : FieldName ) -> Self {
537575 Self { rhs, col_lhs, col_rhs }
@@ -1931,6 +1969,88 @@ mod tests {
19311969 assert ! ( matches!( auth. check_auth( ALICE , BOB ) , Err ( AuthError :: OwnerRequired ) ) ) ;
19321970 }
19331971
1972+ fn mem_table ( name : & str , fields : & [ ( & str , AlgebraicType , bool ) ] ) -> MemTable {
1973+ let table_access = StAccess :: Public ;
1974+ let data = Vec :: new ( ) ;
1975+ let head = Header :: new (
1976+ name. into ( ) ,
1977+ fields
1978+ . iter ( )
1979+ . enumerate ( )
1980+ . map ( |( i, ( field, ty, _) ) | Column :: new ( FieldName :: named ( name, field) , ty. clone ( ) , i. into ( ) ) )
1981+ . collect ( ) ,
1982+ fields
1983+ . iter ( )
1984+ . enumerate ( )
1985+ . filter ( |( _, ( _, _, indexed) ) | * indexed)
1986+ . map ( |( i, _) | ( ColId ( i as u32 ) . into ( ) , Constraints :: indexed ( ) ) )
1987+ . collect ( ) ,
1988+ ) ;
1989+ MemTable {
1990+ head,
1991+ data,
1992+ table_access,
1993+ }
1994+ }
1995+
1996+ #[ test]
1997+ fn test_index_to_inner_join ( ) {
1998+ let index_side = mem_table (
1999+ "index" ,
2000+ & [ ( "a" , AlgebraicType :: U8 , false ) , ( "b" , AlgebraicType :: U8 , true ) ] ,
2001+ ) ;
2002+ let probe_side = mem_table (
2003+ "probe" ,
2004+ & [ ( "c" , AlgebraicType :: U8 , false ) , ( "b" , AlgebraicType :: U8 , true ) ] ,
2005+ ) ;
2006+
2007+ let probe_field = probe_side. head . fields [ 1 ] . field . clone ( ) ;
2008+ let select_field = FieldName :: Name {
2009+ table : "index" . into ( ) ,
2010+ field : "a" . into ( ) ,
2011+ } ;
2012+ let index_select = ColumnOp :: cmp ( select_field, OpCmp :: Eq , 0 . into ( ) ) ;
2013+ let join = IndexJoin {
2014+ probe_side : probe_side. clone ( ) . into ( ) ,
2015+ probe_field,
2016+ index_side : index_side. clone ( ) . into ( ) ,
2017+ index_select : Some ( index_select. clone ( ) ) ,
2018+ index_col : 1 . into ( ) ,
2019+ return_index_rows : false ,
2020+ } ;
2021+
2022+ let expr = join. to_inner_join ( ) ;
2023+
2024+ assert_eq ! ( expr. source, SourceExpr :: MemTable ( probe_side) ) ;
2025+ assert_eq ! ( expr. query. len( ) , 2 ) ;
2026+
2027+ let Query :: JoinInner ( ref join) = expr. query [ 0 ] else {
2028+ panic ! ( "expected an inner join, but got {:#?}" , expr. query[ 0 ] ) ;
2029+ } ;
2030+
2031+ assert_eq ! ( join. col_lhs, FieldName :: named( "probe" , "b" ) ) ;
2032+ assert_eq ! ( join. col_rhs, FieldName :: named( "index" , "b" ) ) ;
2033+ assert_eq ! (
2034+ join. rhs,
2035+ QueryExpr {
2036+ source: SourceExpr :: MemTable ( index_side) ,
2037+ query: vec![ index_select. into( ) ]
2038+ }
2039+ ) ;
2040+
2041+ let Query :: Project ( ref fields, None ) = expr. query [ 1 ] else {
2042+ panic ! ( "expected a projection, but got {:#?}" , expr. query[ 1 ] ) ;
2043+ } ;
2044+
2045+ assert_eq ! (
2046+ fields,
2047+ & vec![
2048+ FieldName :: named( "probe" , "c" ) . into( ) ,
2049+ FieldName :: named( "probe" , "b" ) . into( )
2050+ ]
2051+ ) ;
2052+ }
2053+
19342054 #[ test]
19352055 fn test_auth_table ( ) {
19362056 tables ( ) . iter ( ) . for_each ( assert_owner_private)
0 commit comments