Index: oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/QueryEngineSettingsMBean.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/QueryEngineSettingsMBean.java (revision 1669328) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/QueryEngineSettingsMBean.java (working copy) @@ -20,6 +20,11 @@ String TYPE = "QueryEngineSettings"; /** + * Clear the plan cache. + */ + void clearPlanCache(); + + /** * Get the limit on how many nodes a query may read at most into memory, for * "order by" and "distinct" queries. If this limit is exceeded, the query * throws an exception. Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettings.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettings.java (revision 1669328) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettings.java (working copy) @@ -41,6 +41,17 @@ private boolean fullTextComparisonWithoutIndex = DEFAULT_FULL_TEXT_COMPARISON_WITHOUT_INDEX; + private final QueryPlanCache planCache = new QueryPlanCache(); + + @Override + public void clearPlanCache() { + planCache.clear(); + } + + public QueryPlanCache getPlanCache() { + return planCache; + } + /** * Get the limit on how many nodes a query may read at most into memory, for * "order by" and "distinct" queries. If this limit is exceeded, the query Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java (revision 1669328) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java (working copy) @@ -565,7 +565,15 @@ if (sources.size() <= 1) { // simple case (no join) - estimatedCost = source.prepare().getEstimatedCost(); + QueryPlanCache cache = settings.getPlanCache(); + ExecutionPlan cachedPlan = cache.get(statement); + if (cachedPlan != null) { + ExecutionPlan plan = source.prepare(cachedPlan); + estimatedCost = plan.getEstimatedCost(); + return; + } + ExecutionPlan plan = source.prepare(); + estimatedCost = plan.getEstimatedCost(); return; } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryPlanCache.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryPlanCache.java (revision 0) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryPlanCache.java (working copy) @@ -0,0 +1,23 @@ +package org.apache.jackrabbit.oak.query; + +import java.util.HashMap; + +import org.apache.jackrabbit.oak.query.plan.ExecutionPlan; + +public class QueryPlanCache { + + private final HashMap cache = new HashMap(); + + public void clear() { + cache.clear(); + } + + public void put(String query, ExecutionPlan plan) { + cache.put(query, plan); + } + + public ExecutionPlan get(String query) { + return cache.get(query); + } + +} Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinImpl.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinImpl.java (revision 1669328) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinImpl.java (working copy) @@ -150,7 +150,7 @@ } @Override - public void prepare(ExecutionPlan p) { + public ExecutionPlan prepare(ExecutionPlan p) { if (!(p instanceof JoinExecutionPlan)) { throw new IllegalArgumentException("Not a join plan"); } @@ -162,6 +162,7 @@ applyJoinConditions(); left.prepare(joinPlan.getLeftPlan()); right.prepare(joinPlan.getRightPlan()); + return p; } @Override Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java (revision 1669328) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java (working copy) @@ -251,16 +251,23 @@ } @Override - public void prepare(ExecutionPlan p) { + public ExecutionPlan 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"); + QueryIndex index = selectorPlan.getSelector().getIndex(); + SelectorExecutionPlan newPlan = new SelectorExecutionPlan( + this, index, selectorPlan.getIndexPlan(), selectorPlan.getEstimatedCost()); + pushDown(); + this.plan = newPlan; + return plan; + // throw new IllegalArgumentException("Not a plan for this selector"); } pushDown(); this.plan = selectorPlan; + return plan; } private void pushDown() { Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java (revision 1669328) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java (working copy) @@ -102,8 +102,9 @@ * selectors with the join constraints * * @param p the plan to use + * @return the execution plan (possibly a copy of the original plan) */ - public abstract void prepare(ExecutionPlan p); + public abstract ExecutionPlan prepare(ExecutionPlan p); /** * Execute the query. The current node is set to before the first row.