1515import static org .junit .jupiter .api .Assertions .assertInstanceOf ;
1616
1717import java .util .HashMap ;
18+ import java .util .List ;
1819import java .util .Map ;
1920import java .util .Set ;
2021
3839
3940class LmdbLftjOptimizerTest {
4041
42+ private static final SimpleValueFactory VF = SimpleValueFactory .getInstance ();
43+
4144 @ Test
42- void optimizeShouldReusePreparedPlanAcrossEquivalentVariableRenames () {
45+ void optimizeShouldNotReusePreparedPlanAcrossEquivalentVariableRenames () {
4346 TestQueryAccess queryAccess = new TestQueryAccess ();
4447 LmdbLftjOptimizer optimizer = new LmdbLftjOptimizer (
4548 new LmdbLftjTripleSource (new EmptyTripleSource (), queryAccess ));
4649
4750 TupleExpr first = new QueryRoot (cycle ("a" , "b" , "c" ));
4851 TupleExpr second = new QueryRoot (cycle ("x" , "y" , "z" ));
52+ Set <String > expectedBindingNames = second .getBindingNames ();
53+
54+ optimizer .optimize (first , (Dataset ) null , EmptyBindingSet .getInstance ());
55+ optimizer .optimize (second , (Dataset ) null , EmptyBindingSet .getInstance ());
56+
57+ assertEquals (2 , queryAccess .cachedPlanPuts ,
58+ "renamed visible variables must produce a fresh prepared plan" );
59+ assertEquals (0 , queryAccess .cachedPlanHits ,
60+ "prepared-plan reuse across renamed variables leaks stale binding names" );
61+ assertEquals (List .of ("x" , "y" , "z" ), lftjNode (second ).plan ().variableOrder ());
62+ assertEquals (expectedBindingNames , lftjNode (second ).plan ().bindingNames ());
63+ }
64+
65+ @ Test
66+ void optimizeShouldReusePreparedPlanAcrossEquivalentJoinReorders () {
67+ TestQueryAccess queryAccess = new TestQueryAccess ();
68+ LmdbLftjOptimizer optimizer = new LmdbLftjOptimizer (
69+ new LmdbLftjTripleSource (new EmptyTripleSource (), queryAccess ));
70+
71+ TupleExpr first = new QueryRoot (distinctPredicateCycle ("a" , "b" , "c" ));
72+ TupleExpr second = new QueryRoot (reorderedDistinctPredicateCycle ("a" , "b" , "c" ));
4973
5074 optimizer .optimize (first , (Dataset ) null , EmptyBindingSet .getInstance ());
5175 optimizer .optimize (second , (Dataset ) null , EmptyBindingSet .getInstance ());
5276
5377 assertEquals (1 , queryAccess .cachedPlanPuts ,
54- "first cyclic plan should populate the prepared- plan cache once " );
78+ "equivalent cyclic shapes should share one prepared plan entry " );
5579 assertEquals (1 , queryAccess .cachedPlanHits ,
56- "equivalent cyclic shape should reuse the cached prepared plan on the second optimize" );
57- assertInstanceOf (LmdbLftjTupleExpr .class , assertInstanceOf (QueryRoot .class , first ).getArg ());
58- assertInstanceOf (LmdbLftjTupleExpr .class , assertInstanceOf (QueryRoot .class , second ).getArg ());
80+ "commuted join order should still hit the normalized prepared-plan cache" );
5981 }
6082
6183 private TupleExpr cycle (String a , String b , String c ) {
@@ -69,6 +91,24 @@ private StatementPattern statementPattern(String subjectName, String objectName)
6991 return new StatementPattern (new Var (subjectName ), new Var ("pred" , FOAF .KNOWS ), new Var (objectName ));
7092 }
7193
94+ private TupleExpr distinctPredicateCycle (String a , String b , String c ) {
95+ StatementPattern pattern1 = new StatementPattern (new Var (a ), new Var ("p1" , VF .createIRI ("urn:p1" )), new Var (b ));
96+ StatementPattern pattern2 = new StatementPattern (new Var (b ), new Var ("p2" , VF .createIRI ("urn:p2" )), new Var (c ));
97+ StatementPattern pattern3 = new StatementPattern (new Var (c ), new Var ("p3" , VF .createIRI ("urn:p3" )), new Var (a ));
98+ return new Join (new Join (pattern1 , pattern2 ), pattern3 );
99+ }
100+
101+ private TupleExpr reorderedDistinctPredicateCycle (String a , String b , String c ) {
102+ StatementPattern pattern1 = new StatementPattern (new Var (a ), new Var ("p1" , VF .createIRI ("urn:p1" )), new Var (b ));
103+ StatementPattern pattern2 = new StatementPattern (new Var (b ), new Var ("p2" , VF .createIRI ("urn:p2" )), new Var (c ));
104+ StatementPattern pattern3 = new StatementPattern (new Var (c ), new Var ("p3" , VF .createIRI ("urn:p3" )), new Var (a ));
105+ return new Join (new Join (pattern2 , pattern3 ), pattern1 );
106+ }
107+
108+ private LmdbLftjTupleExpr lftjNode (TupleExpr tupleExpr ) {
109+ return assertInstanceOf (LmdbLftjTupleExpr .class , assertInstanceOf (QueryRoot .class , tupleExpr ).getArg ());
110+ }
111+
72112 private static final class EmptyTripleSource implements TripleSource {
73113
74114 @ Override
0 commit comments