You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by th...@apache.org on 2014/02/19 14:50:49 UTC

svn commit: r1569740 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/api/ main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ main/java/org/apache/jackrabbit/oak/query/ main/java/org/apache/jackrabbit/oak/q...

Author: thomasm
Date: Wed Feb 19 13:50:48 2014
New Revision: 1569740

URL: http://svn.apache.org/r1569740
Log:
OAK-1372 XPath queries with both path and property restrictions are slow

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/ExecutionPlan.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/JoinExecutionPlan.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/Permutations.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/SelectorExecutionPlan.java
      - copied, changed from r1568919, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SelectorExecutionPlan.java
Removed:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SelectorExecutionPlan.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/QueryEngine.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeJoinConditionImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinConditionImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyInexistenceImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeJoinConditionImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java
    jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt
    jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_measure.txt
    jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/QueryEngine.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/QueryEngine.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/QueryEngine.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/QueryEngine.java Wed Feb 19 13:50:48 2014
@@ -29,6 +29,21 @@ import java.util.Set;
  * What query languages are supported depends on the registered query parsers.
  */
 public interface QueryEngine {
+    
+    /**
+     * Empty set of variables bindings. Useful as an argument to
+     * {@link #executeQuery(String, String, long, long, Map, Map)} when
+     * there are no variables in a query.
+     */
+    Map<String, PropertyValue> NO_BINDINGS = emptyMap();
+
+    /**
+     * Empty set of namespace prefix mappings. Useful as an argument to
+     * {@link #getBindVariableNames(String, String, Map)} and
+     * {@link #executeQuery(String, String, long, long, Map, Map)} when
+     * there are no local namespace mappings.
+     */
+    Map<String, String> NO_MAPPINGS = emptyMap();
 
     /**
      * Get the set of supported query languages.
@@ -68,19 +83,4 @@ public interface QueryEngine {
             Map<String, ? extends PropertyValue> bindings,
             Map<String, String> mappings) throws ParseException;
 
-    /**
-     * Empty set of variables bindings. Useful as an argument to
-     * {@link #executeQuery(String, String, long, long, Map, Map)} when
-     * there are no variables in a query.
-     */
-    Map<String, PropertyValue> NO_BINDINGS = emptyMap();
-
-    /**
-     * Empty set of namespace prefix mappings. Useful as an argument to
-     * {@link #getBindVariableNames(String, String, Map)} and
-     * {@link #executeQuery(String, String, long, long, Map, Map)} when
-     * there are no local namespace mappings.
-     */
-    Map<String, String> NO_MAPPINGS = emptyMap();
-
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java Wed Feb 19 13:50:48 2014
@@ -150,7 +150,7 @@ public class UniqueEntryStoreStrategy im
             if (ec != null) {
                 return ec.getValue(Type.LONG);
             }
-            count = index.getChildNodeCount(max);
+            count = 1 + index.getChildNodeCount(max);
             // "is not null" queries typically read more data
             count *= 10;
         } else if (values.size() == 1) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java Wed Feb 19 13:50:48 2014
@@ -41,8 +41,6 @@ public interface Query {
 
     void setTraversalEnabled(boolean traversalEnabled);
 
-    void prepare();
-
     Result executeQuery();
 
     List<String> getBindVariableNames();
@@ -63,17 +61,33 @@ public interface Query {
 
     void setMeasure(boolean measure);
 
-    void init();
-
     void setOrderings(OrderingImpl[] orderings);
     
     /**
+     * Initialize the query. This will 'wire' selectors into constraints bind
+     * variables into expressions. It will also simplify expressions if
+     * possible, but will not prepare the query.
+     */
+    void init();
+    
+    /**
+     * Prepare the query. The cost is estimated and the execution plan is
+     * decided here.
+     */
+    void prepare();
+
+    /**
      * Get the query plan. The query must already be prepared.
      * 
      * @return the query plan
      */
     String getPlan();
-    
+
+    /**
+     * Get the estimated cost.
+     * 
+     * @return the estimated cost
+     */
     double getEstimatedCost();
 
     Tree getTree(String path);

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java Wed Feb 19 13:50:48 2014
@@ -61,6 +61,8 @@ import org.apache.jackrabbit.oak.query.a
 import org.apache.jackrabbit.oak.query.ast.UpperCaseImpl;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 import org.apache.jackrabbit.oak.query.index.TraversingIndex;
+import org.apache.jackrabbit.oak.query.plan.ExecutionPlan;
+import org.apache.jackrabbit.oak.query.plan.SelectorExecutionPlan;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
@@ -317,7 +319,6 @@ public class QueryImpl implements Query 
             constraint = constraint.simplify();
         }
         source.setQueryConstraint(constraint);
-        source.init(this);
         for (ColumnImpl column : columns) {
             column.bindSelector(source);
         }
@@ -446,36 +447,42 @@ public class QueryImpl implements Query 
         List<SourceImpl> sources = source.getInnerJoinSelectors();
         List<JoinConditionImpl> conditions = source.getInnerJoinConditions();
 
-//        if (sources.size() <= 1) {
+        if (sources.size() <= 1) {
             // simple case (no join)
-            estimatedCost = source.prepare();
-//            return;
-//        }
-        
-//        if (sources.size() < 6) {
-//            // TODO iterate over all permutations
-//        }
-//        
-//        // use a greedy algorithm
-//        SourceImpl result = null;
-//        Set<SourceImpl> available = new HashSet<SourceImpl>();
-//        while (sources.size() > 0) {
-//            int bestIndex = 0;
-//            double bestCost = Double.POSITIVE_INFINITY;
-//            for (int i = 0; i < sources.size(); i++) {
-//                SourceImpl source = buildJoin(result, sources.get(i).createClone(), conditions);
-//                double cost = source.prepare();
-//                if (cost <= bestCost) {
-//                    bestCost = cost;
-//                    bestIndex = i;
-//                }
-//            }
-//            SourceImpl s = sources.get(bestIndex).createClone();
-//            available.add(s);
-//            sources.remove(bestIndex);
-//            result = buildJoin(result, s, conditions);
-//        }
-//        source = result;
+            estimatedCost = source.prepare().getEstimatedCost();
+            return;
+        }
+
+        // use a greedy algorithm
+        SourceImpl result = null;
+        Set<SourceImpl> available = new HashSet<SourceImpl>();
+        while (sources.size() > 0) {
+            int bestIndex = 0;
+            double bestCost = Double.POSITIVE_INFINITY;
+            ExecutionPlan bestPlan = null;
+            SourceImpl best = null;
+            for (int i = 0; i < sources.size(); i++) {
+                SourceImpl test = buildJoin(result, sources.get(i), conditions);
+                if (test == null) {
+                    // no join condition
+                    continue;
+                }
+                ExecutionPlan testPlan = test.prepare();
+                double cost = testPlan.getEstimatedCost();
+                if (best == null || cost < bestCost) {
+                    bestPlan = testPlan;
+                    bestCost = cost;
+                    bestIndex = i;
+                    best = test;
+                }
+                test.unprepare();
+            }
+            available.add(sources.remove(bestIndex));
+            result = best;
+            best.prepare(bestPlan);
+        }
+        estimatedCost = result.prepare().getEstimatedCost();
+        source = result;
                 
     }
     
@@ -483,17 +490,22 @@ public class QueryImpl implements Query 
         if (result == null) {
             return last;
         }
-        Set<SourceImpl> available = new HashSet<SourceImpl>();
-        available.addAll(result.getInnerJoinSelectors());
-        available.add(last);
+        List<SourceImpl> selectors = result.getInnerJoinSelectors();
+        Set<SourceImpl> oldSelectors = new HashSet<SourceImpl>();
+        oldSelectors.addAll(selectors);
+        Set<SourceImpl> newSelectors = new HashSet<SourceImpl>();
+        newSelectors.addAll(selectors);
+        newSelectors.add(last);
         for (JoinConditionImpl j : conditions) {
-            if (j.canEvaluate(available)) {
+            // only join conditions can now be evaluated,
+            // but couldn't be evaluated before
+            if (!j.canEvaluate(oldSelectors) && j.canEvaluate(newSelectors)) {
                 JoinImpl join = new JoinImpl(result, last, JoinType.INNER, j);
                 return join;
             }
         }
-        // this is an internal error
-        throw new IllegalArgumentException("No join condition was found");
+        // no join condition was found
+        return null;
     }
  
     /**
@@ -681,7 +693,6 @@ public class QueryImpl implements Query 
             }
         }
 
-
         if (traversalEnabled) {
             QueryIndex traversal = new TraversingIndex();
             double cost = traversal.getCost(filter, rootState);
@@ -690,12 +701,7 @@ public class QueryImpl implements Query 
                 best = traversal;
             }
         }
-        SelectorExecutionPlan plan = new SelectorExecutionPlan();
-        plan.filter = filter;
-        plan.estimatedCost = bestCost;
-        plan.index = best;
-        plan.selector = filter.getSelector();
-        return plan;
+        return new SelectorExecutionPlan(filter.getSelector(), best, bestCost);
     }
 
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java Wed Feb 19 13:50:48 2014
@@ -88,7 +88,7 @@ public class ChildNodeImpl extends Const
 
     @Override
     public void restrict(FilterImpl f) {
-        if (selector == f.getSelector()) {
+        if (selector.equals(f.getSelector())) {
             String path = normalizePath(parentPath);
             f.restrictPath(path, Filter.PathRestriction.DIRECT_CHILDREN);
         }
@@ -96,7 +96,7 @@ public class ChildNodeImpl extends Const
 
     @Override
     public void restrictPushDown(SelectorImpl s) {
-        if (s == selector) {
+        if (s.equals(selector)) {
             s.restrictSelector(this);
         }
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java Wed Feb 19 13:50:48 2014
@@ -66,25 +66,23 @@ public class ChildNodeJoinConditionImpl 
 
     @Override
     public void restrict(FilterImpl f) {
-        if (f.getSelector() == parentSelector) {
+        if (f.getSelector().equals(parentSelector)) {
             String c = childSelector.currentPath();
-            if (c == null && f.isPreparing() && childSelector.isPrepared()) {
+            if (c == null && f.isPreparing() && f.isPrepared(childSelector)) {
                 // during the prepare phase, if the selector is already
                 // prepared, then we would know the value
-                c = KNOWN_PATH;
-            }
-            if (c != null) {
+                f.restrictPath(KNOWN_PARENT_PATH, Filter.PathRestriction.EXACT);
+            } else if (c != null) {
                 f.restrictPath(PathUtils.getParentPath(c), Filter.PathRestriction.EXACT);
             }
         }
-        if (f.getSelector() == childSelector) {
+        if (f.getSelector().equals(childSelector)) {
             String p = parentSelector.currentPath();
-            if (p == null && f.isPreparing() && parentSelector.isPrepared()) {
+            if (p == null && f.isPreparing() && f.isPrepared(parentSelector)) {
                 // during the prepare phase, if the selector is already
                 // prepared, then we would know the value
-                p = KNOWN_PATH;
-            }
-            if (p != null) {
+                f.restrictPath(KNOWN_PATH, Filter.PathRestriction.DIRECT_CHILDREN);
+            } else if (p != null) {
                 f.restrictPath(p, Filter.PathRestriction.DIRECT_CHILDREN);
             }
         }
@@ -97,7 +95,7 @@ public class ChildNodeJoinConditionImpl 
 
     @Override
     public boolean isParent(SourceImpl source) {
-        return source == parentSelector;
+        return source.equals(parentSelector);
     }
  
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java Wed Feb 19 13:50:48 2014
@@ -85,7 +85,7 @@ public class DescendantNodeImpl extends 
 
     @Override
     public void restrict(FilterImpl f) {
-        if (f.getSelector() == selector) {
+        if (f.getSelector().equals(selector)) {
             String path = normalizePath(ancestorPath);
             f.restrictPath(path, Filter.PathRestriction.ALL_CHILDREN);
         }
@@ -93,7 +93,7 @@ public class DescendantNodeImpl extends 
 
     @Override
     public void restrictPushDown(SelectorImpl s) {
-        if (s == selector) {
+        if (s.equals(selector)) {
             s.restrictSelector(this);
         }
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeJoinConditionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeJoinConditionImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeJoinConditionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeJoinConditionImpl.java Wed Feb 19 13:50:48 2014
@@ -66,25 +66,23 @@ public class DescendantNodeJoinCondition
 
     @Override
     public void restrict(FilterImpl f) {
-        if (f.getSelector() == ancestorSelector) {
+        if (f.getSelector().equals(ancestorSelector)) {
             String d = descendantSelector.currentPath();
-            if (d == null && f.isPreparing() && descendantSelector.isPrepared()) {
+            if (d == null && f.isPreparing() && f.isPrepared(descendantSelector)) {
                 // during the prepare phase, if the selector is already
                 // prepared, then we would know the value
-                d = KNOWN_PATH;
-            }
-            if (d != null) {
+                f.restrictPath(KNOWN_PARENT_PATH, Filter.PathRestriction.PARENT);
+            } else if (d != null) {
                 f.restrictPath(PathUtils.getParentPath(d), Filter.PathRestriction.PARENT);
             }
         }
-        if (f.getSelector() == descendantSelector) {
+        if (f.getSelector().equals(descendantSelector)) {
             String a = ancestorSelector.currentPath();
-            if (a == null && f.isPreparing() && ancestorSelector.isPrepared()) {
+            if (a == null && f.isPreparing() && f.isPrepared(ancestorSelector)) {
                 // during the prepare phase, if the selector is already
                 // prepared, then we would know the value
-                a = KNOWN_PATH;
-            }
-            if (a != null) {
+                f.restrictPath(KNOWN_PATH, Filter.PathRestriction.ALL_CHILDREN);
+            } else if (a != null) {
                 f.restrictPath(a, Filter.PathRestriction.ALL_CHILDREN);
             }
         }
@@ -97,7 +95,7 @@ public class DescendantNodeJoinCondition
 
     @Override
     public boolean isParent(SourceImpl source) {
-        return source == ancestorSelector;
+        return source.equals(ancestorSelector);
     }
     
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java Wed Feb 19 13:50:48 2014
@@ -93,9 +93,9 @@ public class EquiJoinConditionImpl exten
 
     @Override
     public void restrict(FilterImpl f) {
-        if (f.getSelector() == selector1) {
+        if (f.getSelector().equals(selector1)) {
             PropertyValue p2 = selector2.currentProperty(property2Name);
-            if (p2 == null && f.isPreparing() && selector2.isPrepared()) {
+            if (p2 == null && f.isPreparing() && f.isPrepared(selector2)) {
                 // during the prepare phase, if the selector is already
                 // prepared, then we would know the value
                 p2 = PropertyValues.newString(KNOWN_VALUE);
@@ -110,9 +110,9 @@ public class EquiJoinConditionImpl exten
             String p1n = normalizePropertyName(property1Name);
             f.restrictProperty(p1n, Operator.EQUAL, p2);
         }
-        if (f.getSelector() == selector2) {
+        if (f.getSelector().equals(selector2)) {
             PropertyValue p1 = selector1.currentProperty(property1Name);
-            if (p1 == null && f.isPreparing() && selector1.isPrepared()) {
+            if (p1 == null && f.isPreparing() && f.isPrepared(selector1)) {
                 // during the prepare phase, if the selector is already
                 // prepared, then we would know the value
                 p1 = PropertyValues.newString(KNOWN_VALUE);
@@ -132,11 +132,11 @@ public class EquiJoinConditionImpl exten
     @Override
     public void restrictPushDown(SelectorImpl s) {
         // both properties may not be null
-        if (s == selector1) {
+        if (s.equals(selector1)) {
             PropertyExistenceImpl ex = new PropertyExistenceImpl(s.getSelectorName(), property1Name);
             ex.bindSelector(s);
             s.restrictSelector(ex);
-        } else if (s == selector2) {
+        } else if (s.equals(selector2)) {
             PropertyExistenceImpl ex = new PropertyExistenceImpl(s.getSelectorName(), property2Name);
             ex.bindSelector(s);
             s.restrictSelector(ex);

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java Wed Feb 19 13:50:48 2014
@@ -126,7 +126,7 @@ public class FullTextSearchImpl extends 
 
     @Override
     public FullTextExpression getFullTextConstraint(SelectorImpl s) {
-        if (s != selector) {
+        if (!s.equals(selector)) {
             return null;
         }
         PropertyValue v = fullTextSearchExpression.currentValue();
@@ -230,7 +230,7 @@ public class FullTextSearchImpl extends 
     @Override
     public void restrict(FilterImpl f) {
         if (propertyName != null) {
-            if (f.getSelector() == selector) {
+            if (f.getSelector().equals(selector)) {
                 String pn = normalizePropertyName(propertyName);
                 f.restrictProperty(pn, Operator.NOT_EQUAL, null);
             }
@@ -240,7 +240,7 @@ public class FullTextSearchImpl extends 
 
     @Override
     public void restrictPushDown(SelectorImpl s) {
-        if (s == selector) {
+        if (s.equals(selector)) {
             selector.restrictSelector(this);
         }
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java Wed Feb 19 13:50:48 2014
@@ -77,7 +77,7 @@ public class FullTextSearchScoreImpl ext
 
     @Override
     public void restrict(FilterImpl f, Operator operator, PropertyValue v) {
-        if (f.getSelector() == selector) {
+        if (f.getSelector().equals(selector)) {
             if (operator == Operator.NOT_EQUAL && v != null) {
                 // not supported
                 return;
@@ -93,7 +93,7 @@ public class FullTextSearchScoreImpl ext
 
     @Override
     public boolean canRestrictSelector(SelectorImpl s) {
-        return s == selector;
+        return s.equals(selector);
     }
     
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinConditionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinConditionImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinConditionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinConditionImpl.java Wed Feb 19 13:50:48 2014
@@ -23,10 +23,20 @@ import org.apache.jackrabbit.oak.query.i
 public abstract class JoinConditionImpl extends AstElement {
     
     /**
-     * A path with 6 elements, which is the expected average for a join.
+     * The prefix for known paths.
      */
-    protected static final String KNOWN_PATH = "/path/from/the/join/selector";
+    public static final String SPECIAL_PATH_PREFIX = "//";
+    
+    /**
+     * A path for a join.
+     */
+    protected static final String KNOWN_PATH = "//path/from/join";
 
+    /**
+     * The parent path of the joined selector
+     */
+    protected static final String KNOWN_PARENT_PATH = "//parent/of/join";
+    
     protected static final String KNOWN_VALUE = "valueFromTheJoinSelector";
 
     /**

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinImpl.java Wed Feb 19 13:50:48 2014
@@ -16,7 +16,8 @@ package org.apache.jackrabbit.oak.query.
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.jackrabbit.oak.query.QueryImpl;
+import org.apache.jackrabbit.oak.query.plan.ExecutionPlan;
+import org.apache.jackrabbit.oak.query.plan.JoinExecutionPlan;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
@@ -36,6 +37,8 @@ public class JoinImpl extends SourceImpl
     private boolean foundJoinedRow;
     private boolean end;
     private NodeState rootState;
+    
+    private JoinExecutionPlan plan;
 
     public JoinImpl(SourceImpl left, SourceImpl right, JoinType joinType,
             JoinConditionImpl joinCondition) {
@@ -45,14 +48,6 @@ public class JoinImpl extends SourceImpl
         this.joinCondition = joinCondition;
     }
     
-    private JoinImpl(JoinImpl clone) {
-        this.left = clone.left;
-        this.right = clone.right;
-        this.joinType = clone.joinType;
-        this.joinCondition = clone.joinCondition;
-        this.query = clone.query;
-    }
-    
     @Override
     public ArrayList<SourceImpl> getInnerJoinSelectors() {
         ArrayList<SourceImpl> list = new ArrayList<SourceImpl>();
@@ -63,11 +58,9 @@ public class JoinImpl extends SourceImpl
             break;
         case LEFT_OUTER:
             list.addAll(left.getInnerJoinSelectors());
-            list.add(right);
             break;
         case RIGHT_OUTER:
             list.addAll(right.getInnerJoinSelectors());
-            list.add(left);
         }
         return list;
     }
@@ -85,11 +78,6 @@ public class JoinImpl extends SourceImpl
         return set;
     }
 
-    @Override
-    public JoinImpl createClone() {
-        return new JoinImpl(this);
-    }
-
     public JoinConditionImpl getJoinCondition() {
         return joinCondition;
     }
@@ -125,9 +113,15 @@ public class JoinImpl extends SourceImpl
         return left + " " + joinType +
                 " " + right + " on " + joinCondition;
     }
-
+    
     @Override
-    public void init(QueryImpl query) {
+    public void unprepare() {
+        left.unprepare();
+        right.unprepare();
+        plan = null;
+    }
+    
+    private void applyJoinConditions() {
         switch (joinType) {
         case INNER:
             left.addJoinCondition(joinCondition, false);
@@ -153,26 +147,38 @@ public class JoinImpl extends SourceImpl
             right.addJoinCondition(joinCondition, true);
             break;
         }
-        setParent(joinCondition);
-        right.init(query);
-        left.init(query);
     }
     
     @Override
-    protected void setParent(JoinConditionImpl joinCondition) {
-        left.setParent(joinCondition);
-        right.setParent(joinCondition);
+    public void prepare(ExecutionPlan p) {
+        if (!(p instanceof JoinExecutionPlan)) {
+            throw new IllegalArgumentException("Not a join plan");
+        }
+        JoinExecutionPlan joinPlan = (JoinExecutionPlan) p;
+        if (joinPlan.getJoin() != this) {
+            throw new IllegalArgumentException("Not a plan for this join");
+        }
+        this.plan = joinPlan;
+        applyJoinConditions();
+        left.prepare(joinPlan.getLeftPlan());
+        right.prepare(joinPlan.getRightPlan());
     }
 
     @Override
-    public double prepare() {
+    public ExecutionPlan prepare() {
+        if (plan != null) {
+            return plan;
+        }
+        applyJoinConditions();
         // the estimated cost is the cost of the left selector,
         // plus twice the cost of the right selector (we expect
         // two rows for the right selector for each node 
         // on the left selector)
-        double cost = left.prepare();
-        cost += 2 * right.prepare();
-        return cost;
+        ExecutionPlan leftPlan = left.prepare();
+        ExecutionPlan rightPlan = right.prepare();
+        double cost = leftPlan.getEstimatedCost() + 2 * rightPlan.getEstimatedCost();
+        plan = new JoinExecutionPlan(this, leftPlan, rightPlan, cost);
+        return plan;
     }
 
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java Wed Feb 19 13:50:48 2014
@@ -87,7 +87,7 @@ public class NodeLocalNameImpl extends D
 
     @Override
     public boolean canRestrictSelector(SelectorImpl s) {
-        return s == selector;
+        return s.equals(selector);
     }
     
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java Wed Feb 19 13:50:48 2014
@@ -100,7 +100,7 @@ public class NodeNameImpl extends Dynami
 
     @Override
     public boolean canRestrictSelector(SelectorImpl s) {
-        return s == selector;
+        return s.equals(selector);
     }
 
     /**

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.java Wed Feb 19 13:50:48 2014
@@ -210,7 +210,7 @@ public class OrImpl extends ConstraintIm
         } else {
             // exactly one selector: check if it's the right one
             SelectorImpl s2 = set.iterator().next();
-            if (s2 != s) {
+            if (!s2.equals(s)) {
                 // a different selector
                 return false;
             }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java Wed Feb 19 13:50:48 2014
@@ -80,7 +80,7 @@ public class PropertyExistenceImpl exten
 
     @Override
     public void restrict(FilterImpl f) {
-        if (f.getSelector() == selector) {
+        if (f.getSelector().equals(selector)) {
             String pn = normalizePropertyName(propertyName);
             f.restrictProperty(pn, Operator.NOT_EQUAL, null);
         }
@@ -88,7 +88,7 @@ public class PropertyExistenceImpl exten
 
     @Override
     public void restrictPushDown(SelectorImpl s) {
-        if (s == selector) {
+        if (s.equals(selector)) {
             s.restrictSelector(this);
         }
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyInexistenceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyInexistenceImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyInexistenceImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyInexistenceImpl.java Wed Feb 19 13:50:48 2014
@@ -134,7 +134,7 @@ public class PropertyInexistenceImpl ext
             // because that would alter the result
             return;
         }
-        if (s == selector) {
+        if (s.equals(selector)) {
             s.restrictSelector(this);
         }
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java Wed Feb 19 13:50:48 2014
@@ -26,9 +26,11 @@ import java.util.Set;
 import javax.jcr.PropertyType;
 
 import org.apache.jackrabbit.oak.api.PropertyValue;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.query.QueryImpl;
 import org.apache.jackrabbit.oak.query.SQL2Parser;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.query.Filter.PathRestriction;
 
 /**
  * A property expression.
@@ -114,22 +116,28 @@ public class PropertyValueImpl extends D
 
     @Override
     public void restrict(FilterImpl f, Operator operator, PropertyValue v) {
-        if (f.getSelector() == selector) {
+        if (f.getSelector().equals(selector)) {
             if (operator == Operator.NOT_EQUAL && v != null) {
                 // not supported
                 return;
             }
-            String pn = normalizePropertyName(propertyName);            
-            f.restrictProperty(pn, operator, v);
-            if (propertyType != PropertyType.UNDEFINED) {
-                f.restrictPropertyType(pn, operator, propertyType);
+            String pn = normalizePropertyName(propertyName);
+            if (pn.equals(QueryImpl.JCR_PATH)) {
+                if (operator == Operator.EQUAL) {
+                    f.restrictPath(v.getValue(Type.STRING), PathRestriction.EXACT);
+                }
+            } else {
+                f.restrictProperty(pn, operator, v);
+                if (propertyType != PropertyType.UNDEFINED) {
+                    f.restrictPropertyType(pn, operator, propertyType);
+                }
             }
         }
     }
     
     @Override
     public void restrictList(FilterImpl f, List<PropertyValue> list) {
-        if (f.getSelector() == selector) {
+        if (f.getSelector().equals(selector)) {
             String pn = normalizePropertyName(propertyName);            
             f.restrictPropertyAsList(pn, list);
         }
@@ -137,7 +145,7 @@ public class PropertyValueImpl extends D
 
     @Override
     public boolean canRestrictSelector(SelectorImpl s) {
-        return s == selector;
+        return s.equals(selector);
     }
     
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java Wed Feb 19 13:50:48 2014
@@ -77,7 +77,7 @@ public class SameNodeImpl extends Constr
 
     @Override
     public void restrict(FilterImpl f) {
-        if (f.getSelector() == selector) {
+        if (f.getSelector().equals(selector)) {
             String p = normalizePath(path);
             f.restrictPath(p, Filter.PathRestriction.EXACT);
         }
@@ -85,7 +85,7 @@ public class SameNodeImpl extends Constr
 
     @Override
     public void restrictPushDown(SelectorImpl s) {
-        if (s == selector) {
+        if (s.equals(selector)) {
             s.restrictSelector(this);
         }
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeJoinConditionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeJoinConditionImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeJoinConditionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeJoinConditionImpl.java Wed Feb 19 13:50:48 2014
@@ -81,14 +81,13 @@ public class SameNodeJoinConditionImpl e
 
     @Override
     public void restrict(FilterImpl f) {
-        if (f.getSelector() == selector1) {
+        if (f.getSelector().equals(selector1)) {
             String p2 = selector2.currentPath();
-            if (p2 == null && f.isPreparing() && selector2.isPrepared()) {
+            if (p2 == null && f.isPreparing() && f.isPrepared(selector2)) {
                 // during the prepare phase, if the selector is already
                 // prepared, then we would know the value
-                p2 = KNOWN_PATH;
-            }
-            if (p2 != null) {
+                f.restrictPath(KNOWN_PATH, Filter.PathRestriction.EXACT);
+            } else if (p2 != null) {
                 if (selector2Path.equals(".")) {
                     f.restrictPath(p2, Filter.PathRestriction.EXACT);
                 } else {
@@ -98,14 +97,13 @@ public class SameNodeJoinConditionImpl e
                 }
             }
         }
-        if (f.getSelector() == selector2) {
+        if (f.getSelector().equals(selector2)) {
             String p1 = selector1.currentPath();
-            if (p1 == null && f.isPreparing() && selector1.isPrepared()) {
+            if (p1 == null && f.isPreparing() && f.isPrepared(selector1)) {
                 // during the prepare phase, if the selector is already
                 // prepared, then we would know the value
-                p1 = KNOWN_PATH;
-            }
-            if (p1 != null) {
+                f.restrictPath(KNOWN_PATH, Filter.PathRestriction.EXACT);
+            } else if (p1 != null) {
                 if (selector2Path.equals(".")) {
                     f.restrictPath(p1, Filter.PathRestriction.EXACT);
                 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java Wed Feb 19 13:50:48 2014
@@ -43,9 +43,10 @@ import org.apache.jackrabbit.oak.api.Tre
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.query.QueryImpl;
-import org.apache.jackrabbit.oak.query.SelectorExecutionPlan;
 import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.query.plan.ExecutionPlan;
+import org.apache.jackrabbit.oak.query.plan.SelectorExecutionPlan;
 import org.apache.jackrabbit.oak.spi.query.Cursor;
 import org.apache.jackrabbit.oak.spi.query.Cursors;
 import org.apache.jackrabbit.oak.spi.query.IndexRow;
@@ -131,6 +132,7 @@ public class SelectorImpl extends Source
      * "select * from nt:base as a inner join nt:base as b on a.x =
      * b.x", the join condition "a.x = b.x" is set for both selectors a and b,
      * so both can check if the property x is set.
+     * The join conditions are added during the init phase.
      */
     private ArrayList<JoinConditionImpl> allJoinConditions =
             new ArrayList<JoinConditionImpl>();
@@ -177,41 +179,6 @@ public class SelectorImpl extends Source
             this.mixinTypes = ImmutableSet.of();
         }
     }
-    
-    private SelectorImpl(SelectorImpl clone) {
-        this.nodeType = clone.nodeType;
-        this.selectorName = clone.selectorName;
-        this.nodeTypeName = clone.nodeTypeName;
-        this.matchesAllTypes = clone.matchesAllTypes;
-        this.supertypes = clone.supertypes;
-        this.primaryTypes = clone.primaryTypes;
-        this.mixinTypes = clone.mixinTypes;
-        this.query = clone.query;
-        this.isParent = clone.isParent;
-        this.outerJoinLeftHandSide = clone.outerJoinLeftHandSide;
-        this.outerJoinRightHandSide = clone.outerJoinRightHandSide;
-        this.allJoinConditions = clone.allJoinConditions;
-    }
-    
-
-//   queryConstraint;
-//   protected JoinConditionImpl joinCondition;
-//   protected ArrayList<JoinConditionImpl> allJoinConditions =
-//           new ArrayList<JoinConditionImpl>();
-//   protected boolean join;
-//   protected boolean outerJoinLeftHandSide;
-//   protected boolean outerJoinRightHandSide;
-//   protected boolean isParent;    
-    
-    /**
-     * Create a shallow clone of this instance.
-     * 
-     * @return the clone
-     */
-    @Override
-    public SelectorImpl createClone() {
-        return new SelectorImpl(this);
-    }
 
     public String getSelectorName() {
         return selectorName;
@@ -265,9 +232,30 @@ public class SelectorImpl extends Source
     public boolean isPrepared() {
         return plan != null;
     }
-
+    
     @Override
-    public double prepare() {
+    public void unprepare() {
+        plan = null;
+        selectorCondition = null;
+        isParent = false;
+        joinCondition = null;
+        allJoinConditions.clear();
+    }
+    
+    @Override
+    public void prepare(ExecutionPlan p) {
+        if (!(p instanceof SelectorExecutionPlan)) {
+            throw new IllegalArgumentException("Not a selector plan");
+        }
+        SelectorExecutionPlan selectorPlan = (SelectorExecutionPlan) p;
+        if (selectorPlan.getSelector() != this) {
+            throw new IllegalArgumentException("Not a plan for this selector");
+        }
+        pushDown();
+        this.plan = selectorPlan;
+    }
+    
+    private void pushDown() {
         if (queryConstraint != null) {
             queryConstraint.restrictPushDown(this);
         }
@@ -276,8 +264,16 @@ public class SelectorImpl extends Source
                 c.restrictPushDown(this);
             }
         }
+    }
+
+    @Override
+    public ExecutionPlan prepare() {
+        if (plan != null) {
+            return plan;
+        }
+        pushDown();
         plan = query.getBestSelectorExecutionPlan(createFilter(true));
-        return plan.estimatedCost;
+        return plan;
     }
     
     @Override
@@ -297,12 +293,15 @@ public class SelectorImpl extends Source
             this.joinCondition = joinCondition;
         }
         allJoinConditions.add(joinCondition);
+        if (joinCondition.isParent(this)) {
+            isParent = true;
+        }
     }
 
     @Override
     public void execute(NodeState rootState) {
-        if (plan.index != null) {
-            cursor = plan.index.query(createFilter(false), rootState);
+        if (plan.getIndex() != null) {
+            cursor = plan.getIndex().query(createFilter(false), rootState);
         } else {
             cursor = Cursors.newPathCursor(new ArrayList<String>());
         }
@@ -342,7 +341,7 @@ public class SelectorImpl extends Source
         // "rep:excerpt is not null" to let the index know that
         // we will need the excerpt
         for (ColumnImpl c : query.getColumns()) {
-            if (c.getSelector() == this) {
+            if (c.getSelector().equals(this)) {
                 if (c.getColumnName().equals("rep:excerpt")) {
                     f.restrictProperty("rep:excerpt", Operator.NOT_EQUAL, null);
                 }
@@ -615,11 +614,6 @@ public class SelectorImpl extends Source
     }
 
     @Override
-    public void init(QueryImpl query) {
-        // nothing to do
-    }
-
-    @Override
     public SelectorImpl getSelector(String selectorName) {
         if (selectorName.equals(this.selectorName)) {
             return this;
@@ -654,15 +648,8 @@ public class SelectorImpl extends Source
         return selectorName.hashCode();
     }
 
-    @Override
-    protected void setParent(JoinConditionImpl joinCondition) {
-        if (joinCondition.isParent(this)) {
-            isParent = true;
-        }
-    }
-
     QueryIndex getIndex() {
-        return plan == null ? null : plan.index;
+        return plan == null ? null : plan.getIndex();
     }
 
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java Wed Feb 19 13:50:48 2014
@@ -21,7 +21,7 @@ package org.apache.jackrabbit.oak.query.
 import java.util.Collections;
 import java.util.List;
 
-import org.apache.jackrabbit.oak.query.QueryImpl;
+import org.apache.jackrabbit.oak.query.plan.ExecutionPlan;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
@@ -55,14 +55,6 @@ public abstract class SourceImpl extends
     public abstract void setOuterJoin(boolean outerJoinLeftHandSide, boolean outerJoinRightHandSide);
 
     /**
-     * Initialize the query. This will 'wire' the selectors with the
-     * constraints.
-     *
-     * @param query the query
-     */
-    public abstract void init(QueryImpl query);
-
-    /**
      * Get the selector with the given name, or null if not found.
      *
      * @param selectorName the selector name
@@ -93,12 +85,25 @@ public abstract class SourceImpl extends
     public abstract String getPlan(NodeState rootState);
 
     /**
-     * Prepare executing the query (recursively). This method will decide which
-     * index to use.
+     * Prepare executing the query (recursively). This will 'wire' the
+     * selectors with the join constraints, and decide which index to use.
+     * 
+     * @return the execution plan
+     */
+    public abstract ExecutionPlan prepare();
+    
+    /**
+     * Undo a prepare.
+     */
+    public abstract void unprepare();
+
+    /**
+     * Re-apply a previously prepared plan. This will also 're-wire' the
+     * selectors with the join constraints
      * 
-     * @return the estimated cost
+     * @param p the plan to use
      */
-    public abstract double prepare();
+    public abstract void prepare(ExecutionPlan p);
     
     /**
      * Execute the query. The current node is set to before the first row.
@@ -114,15 +119,6 @@ public abstract class SourceImpl extends
      * @return true if there is a next row
      */
     public abstract boolean next();
-    
-    /**
-     * Create a shallow clone of this instance.
-     * 
-     * @return the clone
-     */
-    public abstract SourceImpl createClone();
-
-    abstract void setParent(JoinConditionImpl joinCondition);
 
     /**
      * <b>!Test purpose only! <b>
@@ -156,5 +152,5 @@ public abstract class SourceImpl extends
      * @return true if there is any
      */
     public abstract boolean isOuterJoinRightHandSide();
-    
+
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java Wed Feb 19 13:50:48 2014
@@ -32,6 +32,7 @@ import javax.jcr.PropertyType;
 
 import org.apache.jackrabbit.oak.api.PropertyValue;
 import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.query.ast.JoinConditionImpl;
 import org.apache.jackrabbit.oak.query.ast.Operator;
 import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
 import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression;
@@ -64,10 +65,16 @@ public class FilterImpl implements Filte
      *  The path, or "/" (the root node, meaning no filter) if not set.
      */
     private String path = "/";
-
+    
     private PathRestriction pathRestriction = PathRestriction.NO_RESTRICTION;
 
     /**
+     * Additional path restrictions whose values are known only at runtime,
+     * for example paths set by other (join-) selectors.
+     */
+    private String pathPlan;
+
+    /**
      * The fulltext search conditions, if any.
      */
     private final ArrayList<String> fulltextConditions = new ArrayList<String>();
@@ -93,6 +100,12 @@ public class FilterImpl implements Filte
         this(null, null);
     }
 
+    /**
+     * Create a filter.
+     * 
+     * @param selector the selector for the given filter
+     * @param queryStatement the query statement
+     */
     public FilterImpl(SelectorImpl selector, String queryStatement) {
         this.selector = selector;
         this.queryStatement = queryStatement;
@@ -122,6 +135,18 @@ public class FilterImpl implements Filte
     public boolean isPreparing() {
         return preparing;
     }
+    
+    /**
+     * Whether the given selector is already prepared during the prepare phase
+     * of a join. That means, check whether the passed selector can already
+     * provide data.
+     * 
+     * @param selector the selector to test
+     * @return true if it is already prepared
+     */
+    public boolean isPrepared(SelectorImpl selector) {
+        return selector.isPrepared();
+    }
 
     /**
      * Get the path.
@@ -137,6 +162,20 @@ public class FilterImpl implements Filte
     public PathRestriction getPathRestriction() {
         return pathRestriction;
     }
+    
+    @Override
+    public String getPathPlan() {
+        StringBuilder buff = new StringBuilder();
+        String p = path;
+        if (PathUtils.denotesRoot(path)) {
+            p = "";
+        }
+        buff.append(p).append(pathRestriction);
+        if (pathPlan != null) {
+            buff.append(" && ").append(pathPlan);
+        }
+        return buff.toString();
+    }
 
     public void setPath(String path) {
         this.path = path;
@@ -354,7 +393,7 @@ public class FilterImpl implements Filte
         if (fullTextConstraint != null) {
             buff.append("fullText=").append(fullTextConstraint);
         }
-        buff.append(", path=").append(path).append(pathRestriction);
+        buff.append(", path=").append(getPathPlan());
         if (!propertyRestrictions.isEmpty()) {
             buff.append(", property=[");
             Iterator<Entry<String, PropertyRestriction>> iterator = propertyRestrictions
@@ -377,6 +416,18 @@ public class FilterImpl implements Filte
             // currently unknown (prepare time)
             addedPath = "/";
         }
+        if (addedPath.startsWith(JoinConditionImpl.SPECIAL_PATH_PREFIX)) {
+            // not a real path, that means we only adapt the plan 
+            // and that's it
+            if (pathPlan == null) {
+                pathPlan = "";
+            } else {
+                pathPlan += " && ";
+            }
+            pathPlan += addedPath + addedPathRestriction;
+            return;
+        }
+        
         // calculating the intersection of path restrictions
         // this is ugly code, but I don't currently see a radically simpler method
         switch (addedPathRestriction) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingIndex.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingIndex.java Wed Feb 19 13:50:48 2014
@@ -46,6 +46,8 @@ public class TraversingIndex implements 
             return 0;
         }
         double nodeCount = 10000000;
+        // if the path is from a join, then the depth is not correct
+        // (the path might be the root node), but that's OK
         String path = filter.getPath();
         PathRestriction restriction = filter.getPathRestriction();
         switch (restriction) {
@@ -81,12 +83,7 @@ public class TraversingIndex implements 
 
     @Override
     public String getPlan(Filter filter, NodeState rootState) {
-        String p = filter.getPath();
-        String r = filter.getPathRestriction().toString();
-        if (PathUtils.denotesRoot(p)) {
-            p = "";
-        }
-        return "traverse \"" + p + r + '"';
+        return "traverse \"" + filter.getPathPlan() + '"';
     }
 
     @Override

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/ExecutionPlan.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/ExecutionPlan.java?rev=1569740&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/ExecutionPlan.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/ExecutionPlan.java Wed Feb 19 13:50:48 2014
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.query.plan;
+
+/**
+ * An execution plan for a join or a selector.
+ */
+public interface ExecutionPlan {
+
+    double getEstimatedCost();
+    
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/JoinExecutionPlan.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/JoinExecutionPlan.java?rev=1569740&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/JoinExecutionPlan.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/JoinExecutionPlan.java Wed Feb 19 13:50:48 2014
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.query.plan;
+
+import org.apache.jackrabbit.oak.query.ast.JoinImpl;
+
+
+/**
+ * An execution plan for a join.
+ */
+public class JoinExecutionPlan implements ExecutionPlan {
+
+    private final JoinImpl join;
+    private final ExecutionPlan leftPlan, rightPlan;
+    private final double estimatedCost;
+    
+    public JoinExecutionPlan(JoinImpl join, ExecutionPlan leftPlan, ExecutionPlan rightPlan, double estimatedCost) {
+        this.join = join;
+        this.leftPlan = leftPlan;
+        this.rightPlan = rightPlan;
+        this.estimatedCost = estimatedCost;
+    }
+    
+    @Override
+    public double getEstimatedCost() {
+        return estimatedCost;
+    }
+
+    public JoinImpl getJoin() {
+        return join;
+    }
+
+    public ExecutionPlan getLeftPlan() {
+        return leftPlan;
+    }
+
+    public ExecutionPlan getRightPlan() {
+        return rightPlan;
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/Permutations.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/Permutations.java?rev=1569740&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/Permutations.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/Permutations.java Wed Feb 19 13:50:48 2014
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.query.plan;
+
+/**
+ * A class to iterate over all permutations of an array.
+ * The algorithm is from Applied Combinatorics, by Alan Tucker.
+ *
+ * @param <T> the element type
+ */
+public class Permutations<T> {
+
+    private final T[] in;
+    private final T[] out;
+    private final int n, m;
+    private final int[] index;
+    private boolean hasNext = true;
+
+    private Permutations(T[] in, T[] out, int m) {
+        this.n = in.length;
+        this.m = m;
+        if (n < m || m < 0) {
+            throw new IllegalArgumentException("n < m or m < 0");
+        }
+        this.in = in;
+        this.out = out;
+        index = new int[n];
+        for (int i = 0; i < n; i++) {
+            index[i] = i;
+        }
+
+        // The elements from m to n are always kept ascending right to left.
+        // This keeps the dip in the interesting region.
+        reverseAfter(m - 1);
+    }
+
+    /**
+     * Create a new permutations object.
+     *
+     * @param <T> the type
+     * @param in the source array
+     * @param out the target array
+     * @return the generated permutations object
+     */
+    public static <T> Permutations<T> create(T[] in, T[] out) {
+        return new Permutations<T>(in, out, in.length);
+    }
+
+    /**
+     * Create a new permutations object.
+     *
+     * @param <T> the type
+     * @param in the source array
+     * @param out the target array
+     * @param m the number of output elements to generate
+     * @return the generated permutations object
+     */
+    public static <T> Permutations<T> create(T[] in, T[] out, int m) {
+        return new Permutations<T>(in, out, m);
+    }
+
+    /**
+     * Move the index forward a notch. The algorithm first finds the rightmost
+     * index that is less than its neighbor to the right. This is the dip point.
+     * The algorithm next finds the least element to the right of the dip that
+     * is greater than the dip. That element is switched with the dip. Finally,
+     * the list of elements to the right of the dip is reversed.
+     * For example, in a permutation of 5 items, the index may be {1, 2, 4, 3,
+     * 0}. The dip is 2 the rightmost element less than its neighbor on its
+     * right. The least element to the right of 2 that is greater than 2 is 3.
+     * These elements are swapped, yielding {1, 3, 4, 2, 0}, and the list right
+     * of the dip point is reversed, yielding {1, 3, 0, 2, 4}.
+     */
+    private void moveIndex() {
+        // find the index of the first element that dips
+        int i = rightmostDip();
+        if (i < 0) {
+            hasNext = false;
+            return;
+        }
+
+        // find the least greater element to the right of the dip
+        int leastToRightIndex = i + 1;
+        for (int j = i + 2; j < n; j++) {
+            if (index[j] < index[leastToRightIndex] && index[j] > index[i]) {
+                leastToRightIndex = j;
+            }
+        }
+
+        // switch dip element with least greater element to its right
+        int t = index[i];
+        index[i] = index[leastToRightIndex];
+        index[leastToRightIndex] = t;
+
+        if (m - 1 > i) {
+            // reverse the elements to the right of the dip
+            reverseAfter(i);
+
+            // reverse the elements to the right of m - 1
+            reverseAfter(m - 1);
+        }
+    }
+
+    /**
+     * Get the index of the first element from the right that is less
+     * than its neighbor on the right.
+     *
+     * @return the index or -1 if non is found
+     */
+    private int rightmostDip() {
+        for (int i = n - 2; i >= 0; i--) {
+            if (index[i] < index[i + 1]) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Reverse the elements to the right of the specified index.
+     *
+     * @param i the index
+     */
+    private void reverseAfter(int i) {
+        int start = i + 1;
+        int end = n - 1;
+        while (start < end) {
+            int t = index[start];
+            index[start] = index[end];
+            index[end] = t;
+            start++;
+            end--;
+        }
+    }
+
+    /**
+     * Go to the next lineup, and if available, fill the target array.
+     *
+     * @return if a new lineup is available
+     */
+    public boolean next() {
+        if (!hasNext) {
+            return false;
+        }
+        for (int i = 0; i < m; i++) {
+            out[i] = in[index[i]];
+        }
+        moveIndex();
+        return true;
+    }
+
+}

Copied: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/SelectorExecutionPlan.java (from r1568919, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SelectorExecutionPlan.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/SelectorExecutionPlan.java?p2=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/SelectorExecutionPlan.java&p1=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SelectorExecutionPlan.java&r1=1568919&r2=1569740&rev=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SelectorExecutionPlan.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/plan/SelectorExecutionPlan.java Wed Feb 19 13:50:48 2014
@@ -14,10 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.oak.query;
+package org.apache.jackrabbit.oak.query.plan;
 
 import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
-import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
 
 /**
@@ -26,14 +25,29 @@ import org.apache.jackrabbit.oak.spi.que
  * is to use a certain query index, which will result in an estimated cost to
  * use that index to retrieve nodes for this index.
  */
-public class SelectorExecutionPlan {
+public class SelectorExecutionPlan implements ExecutionPlan {
     
-    public SelectorImpl selector;
-    
-    public Filter filter;
-    
-    public double estimatedCost;
+    private final SelectorImpl selector;
+    private final double estimatedCost;
+    private final QueryIndex index;
+
+    public SelectorExecutionPlan(SelectorImpl selector, QueryIndex index, double estimatedCost) {
+        this.selector = selector;
+        this.index = index;
+        this.estimatedCost = estimatedCost;
+    }
     
-    public QueryIndex index;
+    @Override
+    public double getEstimatedCost() {
+        return estimatedCost;
+    }
+
+    public SelectorImpl getSelector() {
+        return selector;
+    }
+
+    public QueryIndex getIndex() {
+        return index;
+    }
 
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java Wed Feb 19 13:50:48 2014
@@ -85,6 +85,13 @@ public interface Filter {
      * @return the path
      */
     String getPath();
+    
+    /**
+     * Get the plan for the path.
+     * 
+     * @return the plan
+     */
+    String getPathPlan();
 
     /**
      * Checks whether nodes of all types can match this filter.

Modified: jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt Wed Feb 19 13:50:48 2014
@@ -23,8 +23,11 @@
 # * new tests are typically be added on top, after the syntax docs
 # * use ascii character only
 
-explain select e.[jcr:path] from [nt:base] as a inner join [nt:base] as b on ischildnode(b, a) inner join [nt:base] as c on ischildnode(c, b) inner join [nt:base] as d on ischildnode(d, c) inner join [nt:base] as e on ischildnode(e, d) where name(a) = 'a' and isdescendantnode(a, '/b') and name(b) = 'c' and name(c) = 'd' and name(d) = 'e' and (e.[type] = '1' or e.[type] = '2' or e.[type] = '3' or e.[type] = '4')
-[nt:base] as [a] /* traverse "/b//*" where (name([a]) = cast('a' as string)) and (isdescendantnode([a], [/b])) */ inner join [nt:base] as [b] /* traverse "/path/from/the/join/selector/*" where name([b]) = cast('c' as string) */ on ischildnode([b], [a]) inner join [nt:base] as [c] /* traverse "/path/from/the/join/selector/*" where name([c]) = cast('d' as string) */ on ischildnode([c], [b]) inner join [nt:base] as [d] /* traverse "/path/from/the/join/selector/*" where name([d]) = cast('e' as string) */ on ischildnode([d], [c]) inner join [nt:base] as [e] /* traverse "/path/from/the/join/selector/*" where ([e].[type] is not null) and ([e].[type] in(cast('1' as string), cast('2' as string), cast('3' as string), cast('4' as string))) */ on ischildnode([e], [d])
+explain select e.[jcr:path] from [nt:base] as a inner join [nt:base] as b on ischildnode(b, a) inner join [nt:base] as c on ischildnode(c, b) inner join [nt:base] as d on ischildnode(d, c) inner join [nt:base] as e on ischildnode(e, d) where name(a) = 'a' and isdescendantnode(a, '/b') and name(b) = 'c' and name(c) = 'd' and name(d) = 'e' and (e.[jcr:uuid] = '1' or e.[jcr:uuid] = '2' or e.[jcr:uuid] = '3' or e.[jcr:uuid] = '4')
+[nt:base] as [e] /* property jcr:uuid where ([e].[jcr:uuid] is not null) and ([e].[jcr:uuid] in(cast('1' as string), cast('2' as string), cast('3' as string), cast('4' as string))) */ inner join [nt:base] as [d] /* traverse "* && //parent/of/join" where name([d]) = cast('e' as string) */ on ischildnode([e], [d]) inner join [nt:base] as [c] /* traverse "* && //parent/of/join" where name([c]) = cast('d' as string) */ on ischildnode([d], [c]) inner join [nt:base] as [b] /* traverse "* && //parent/of/join" where name([b]) = cast('c' as string) */ on ischildnode([c], [b]) inner join [nt:base] as [a] /* traverse "/b//* && //parent/of/join" where (name([a]) = cast('a' as string)) and (isdescendantnode([a], [/b])) */ on ischildnode([b], [a])
+
+explain select e.[jcr:path] from [nt:base] as a inner join [nt:base] as b on ischildnode(b, a) inner join [nt:base] as c on ischildnode(c, b) inner join [nt:base] as d on ischildnode(d, c) inner join [nt:base] as e on ischildnode(e, d) where name(a) = 'a' and isdescendantnode(a, '/b') and name(b) = 'c' and name(c) = 'd' and name(d) = 'e' and (e.[jcr:uuid] = '1' or e.[jcr:uuid] = '2' or e.[jcr:uuid] = '3' or e.[jcr:uuid] = '4')
+[nt:base] as [e] /* property jcr:uuid where ([e].[jcr:uuid] is not null) and ([e].[jcr:uuid] in(cast('1' as string), cast('2' as string), cast('3' as string), cast('4' as string))) */ inner join [nt:base] as [d] /* traverse "* && //parent/of/join" where name([d]) = cast('e' as string) */ on ischildnode([e], [d]) inner join [nt:base] as [c] /* traverse "* && //parent/of/join" where name([c]) = cast('d' as string) */ on ischildnode([d], [c]) inner join [nt:base] as [b] /* traverse "* && //parent/of/join" where name([b]) = cast('c' as string) */ on ischildnode([c], [b]) inner join [nt:base] as [a] /* traverse "/b//* && //parent/of/join" where (name([a]) = cast('a' as string)) and (isdescendantnode([a], [/b])) */ on ischildnode([b], [a])
 
 explain select excerpt(.) from [nt:resource] where contains(*, 'jackrabbit')
 [nt:resource] as [nt:resource] /* traverse "*" where contains([nt:resource].[*], cast('jackrabbit' as string)) */
@@ -38,6 +41,9 @@ explain select excerpt(.) from [nt:resou
 explain select * from [nt:base] where [jcr:uuid]=1 or [b]=2
 [nt:base] as [nt:base] /* traverse "*" */
 
+explain select b.[jcr:uuid] from [nt:base] as a inner join [nt:base] as b on isdescendantnode(b, a) where (a.[jcr:uuid] = '1' or a.[jcr:uuid] = '2')
+[nt:base] as [a] /* property jcr:uuid where ([a].[jcr:uuid] is not null) and ([a].[jcr:uuid] in(cast('1' as string), cast('2' as string))) */ inner join [nt:base] as [b] /* traverse "* && //path/from/join//*" */ on isdescendantnode([b], [a])
+
 explain select b.[jcr:uuid] from [nt:base] as a inner join [nt:base] as b on isdescendantnode(b, a) where (a.[jcr:uuid] = '1' or a.[jcr:uuid] = '2') and b.[jcr:uuid] is not null
 [nt:base] as [a] /* property jcr:uuid where ([a].[jcr:uuid] is not null) and ([a].[jcr:uuid] in(cast('1' as string), cast('2' as string))) */ inner join [nt:base] as [b] /* property jcr:uuid where [b].[jcr:uuid] is not null */ on isdescendantnode([b], [a])
 
@@ -66,7 +72,7 @@ explain select * from [nt:base] as a inn
 [nt:base] as [a] /* property jcr:uuid where ([a].[jcr:uuid] is not null) and ([a].[jcr:uuid] in(cast('1' as long), cast('2' as long))) */ inner join [nt:base] as [b] /* property jcr:uuid where ([b].[jcr:uuid] is not null) and ([b].[jcr:uuid] in(cast('3' as long), cast('4' as long))) */ on isdescendantnode([b], [a])
 
 explain select * from [nt:base] as a inner join [nt:base] as b on isdescendantnode(b, a) where a.[jcr:uuid] is not null and b.[x] is not null
-[nt:base] as [a] /* property jcr:uuid where [a].[jcr:uuid] is not null */ inner join [nt:base] as [b] /* traverse "/path/from/the/join/selector//*" where [b].[x] is not null */ on isdescendantnode([b], [a])
+[nt:base] as [a] /* property jcr:uuid where [a].[jcr:uuid] is not null */ inner join [nt:base] as [b] /* traverse "* && //path/from/join//*" where [b].[x] is not null */ on isdescendantnode([b], [a])
 
 explain select [rep:excerpt] from [nt:base] where [jcr:uuid] is not null
 [nt:base] as [nt:base] /* property jcr:uuid (rep:excerpt) where [nt:base].[jcr:uuid] is not null */

Modified: jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_measure.txt
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_measure.txt?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_measure.txt (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_measure.txt Wed Feb 19 13:50:48 2014
@@ -27,6 +27,11 @@ commit / + "testRoot": { }
 commit /testRoot + "parents": { "p0": {"id": "0"}, "p1": {"id": "1"}, "p2": {"id": "2"}}
 commit /testRoot + "children": { "c1": {"p": "1"}, "c2": {"p": "1"}, "c3": {"p": "2"}, "c4": {"p": "3"}}
 
+select p.[jcr:path], c.[jcr:path] from [nt:base] as p inner join [nt:base] as c on p.id = c.p where isdescendantnode(p, '/testRoot') and isdescendantnode(c, '/testRoot')
+/testRoot/parents/p1, /testRoot/children/c1
+/testRoot/parents/p1, /testRoot/children/c2
+/testRoot/parents/p2, /testRoot/children/c3
+
 select [jcr:path] from [nt:base] as p where p.[testRoot/children/*/*] = '3'
 /
 

Modified: jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt?rev=1569740&r1=1569739&r2=1569740&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt Wed Feb 19 13:50:48 2014
@@ -23,6 +23,10 @@
 # * new tests are typically be added on top, after the syntax docs
 # * use ascii character only
 
+# "or" problem (OAK-1432)
+# xpath2sql /jcr:root/content/dam//element(*, nt:unstructured)[((@sling:resourceType = 'dam/smartcollection' or @sling:resourceType = 'dam/collection') or @sling:resourceSuperType = 'dam/collection')]
+# select
+
 # property names with missing @
 
 xpath2sql /jcr:root/testroot//b/c/d/*[@jcr:uuid='1' or @jcr:uuid='2']