Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.modeshape.jcr.query.process.QueryProcessor Maven / Gradle / Ivy
/*
* ModeShape (http://www.modeshape.org)
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
* See the AUTHORS.txt file in the distribution for a full listing of
* individual contributors.
*
* ModeShape is free software. Unless otherwise indicated, all code in ModeShape
* is licensed to you under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* ModeShape is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.modeshape.jcr.query.process;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.modeshape.jcr.query.QueryContext;
import org.modeshape.jcr.query.QueryResults;
import org.modeshape.jcr.query.QueryResults.Columns;
import org.modeshape.jcr.query.QueryResults.Statistics;
import org.modeshape.jcr.query.model.ChildNodeJoinCondition;
import org.modeshape.jcr.query.model.Column;
import org.modeshape.jcr.query.model.Constraint;
import org.modeshape.jcr.query.model.EquiJoinCondition;
import org.modeshape.jcr.query.model.JoinCondition;
import org.modeshape.jcr.query.model.JoinType;
import org.modeshape.jcr.query.model.Limit;
import org.modeshape.jcr.query.model.Ordering;
import org.modeshape.jcr.query.model.QueryCommand;
import org.modeshape.jcr.query.model.SameNodeJoinCondition;
import org.modeshape.jcr.query.model.SelectorName;
import org.modeshape.jcr.query.model.SetQuery.Operation;
import org.modeshape.jcr.query.plan.JoinAlgorithm;
import org.modeshape.jcr.query.plan.PlanHints;
import org.modeshape.jcr.query.plan.PlanNode;
import org.modeshape.jcr.query.plan.PlanNode.Property;
import org.modeshape.jcr.query.plan.PlanNode.Type;
/**
* An abstract {@link Processor} implementation that builds a tree of {@link ProcessingComponent} objects to perform the different
* parts of the query processing logic. Subclasses are required to only implement one method: the
* {@link #createAccessComponent(QueryCommand, QueryContext, PlanNode, Columns, Object)} should create a ProcessorComponent object
* that will perform the (low-level access) query described by the {@link PlanNode plan} given as a parameter.
*
* @param the processing context object type
*/
public abstract class QueryProcessor implements Processor {
@Override
public QueryResults execute( QueryContext context,
QueryCommand command,
Statistics statistics,
PlanNode plan ) {
long nanos = System.nanoTime();
Columns columns = null;
List tuples = null;
try {
// Find the topmost PROJECT node and build the Columns ...
PlanNode project = plan.findAtOrBelow(Type.PROJECT);
assert project != null;
List projectedColumns = project.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
assert projectedColumns != null;
assert !projectedColumns.isEmpty();
List columnTypes = project.getPropertyAsList(Property.PROJECT_COLUMN_TYPES, String.class);
assert columnTypes != null;
assert columnTypes.size() == projectedColumns.size();
columns = new QueryResultColumns(projectedColumns, columnTypes, context.getHints().hasFullTextSearch);
if (context.getHints().planOnly) {
tuples = Collections.emptyList();
} else {
// Create the processing context ...
final ProcessingContextType processingContext = createProcessingContext(context);
try {
// Go through the plan and create the corresponding ProcessingComponents ...
ProcessingComponent component = createComponent(command, context, plan, columns, processingContext);
long nanos2 = System.nanoTime();
statistics = statistics.withResultsFormulationTime(Math.abs(nanos2 - nanos));
nanos = nanos2;
if (component != null) {
// Now execute the component ...
columns = component.getColumns();
tuples = component.execute();
} else {
// There must have been an error or was cancelled ...
assert context.getProblems().hasErrors() || context.isCancelled();
tuples = Collections.emptyList();
}
} finally {
// Always close the processing context !!!
closeProcessingContext(processingContext);
}
}
} finally {
statistics = statistics.withExecutionTime(Math.abs(System.nanoTime() - nanos));
}
assert tuples != null;
final String planDesc = context.getHints().showPlan ? plan.getString() : null;
return new org.modeshape.jcr.query.process.QueryResults(columns, statistics, tuples, context.getProblems(), planDesc);
}
/**
* A method that can be overridden by subclasses to create a single context object used for all the access queries for a
* single query.
*
* @param queryContext the context in which the query is being executed; never null
* @return the processing context object; may be null
*/
protected ProcessingContextType createProcessingContext( QueryContext queryContext ) {
return null;
}
/**
* A method that can be overridden to close the supplied processing context. This method will always be called after a call to
* {@link #createProcessingContext(QueryContext)}
*
* @param processingContext the processing context in which the query is being executed; null if
* {@link #createProcessingContext(QueryContext)} returned null
*/
protected void closeProcessingContext( ProcessingContextType processingContext ) {
// do nothing ...
}
protected abstract boolean supportsPushDownExistConstraints();
/**
* Create the {@link ProcessingComponent} that processes a single {@link Type#ACCESS} branch of a query plan.
*
* @param originalQuery the original query that is being executed; never null
* @param context the context in which query is being evaluated; never null
* @param accessNode the node in the query plan that represents the {@link Type#ACCESS} plan; never null
* @param resultColumns the columns that are to be returned; never null
* @param processingContext the processing context in which the query is being executed; null if
* {@link #createProcessingContext(QueryContext)} returned null
* @return the processing component; may not be null
*/
protected abstract ProcessingComponent createAccessComponent( QueryCommand originalQuery,
QueryContext context,
PlanNode accessNode,
Columns resultColumns,
ProcessingContextType processingContext );
/**
* Method that is called to build up the {@link ProcessingComponent} objects that correspond to the optimized query plan. This
* method is called by {@link #execute(QueryContext, QueryCommand, Statistics, PlanNode)} for each of the various
* {@link PlanNode} objects in the optimized query plan, and the method is actually recursive (since the optimized query plan
* forms a tree). However, whenever this call structure reaches the {@link Type#ACCESS ACCESS} nodes in the query plan (which
* each represents a separate atomic low-level query to the underlying system), the
* {@link #createAccessComponent(QueryCommand, QueryContext, PlanNode, Columns, Object)} method is called. Subclasses should
* create an appropriate ProcessingComponent implementation that performs this atomic low-level query.
*
* @param originalQuery the original query that is being executed; never null
* @param context the context in which query is being evaluated
* @param node the plan node for which the ProcessingComponent is to be created
* @param columns the definition of the result columns for this portion of the query
* @param processingContext the processing context in which the query is being executed; null if
* {@link #createProcessingContext(QueryContext)} returned null
* @return the processing component for this plan node; or null if there was an error recorded in the
* {@link QueryContext#getProblems() problems}
*/
protected ProcessingComponent createComponent( QueryCommand originalQuery,
QueryContext context,
PlanNode node,
Columns columns,
ProcessingContextType processingContext ) {
ProcessingComponent component = null;
switch (node.getType()) {
case ACCESS:
// If the ACCESS node will not have results ...
if (node.hasProperty(Property.ACCESS_NO_RESULTS)) {
component = new NoResultsComponent(context, columns);
} else {
// Create the component to handle the ACCESS node ...
assert node.getChildCount() == 1;
component = createAccessComponent(originalQuery, context, node, columns, processingContext);
// // Don't do anything special with an access node at the moment ...
// component = createComponent(context, node.getFirstChild(), columns, analyzer);
}
break;
case DUP_REMOVE:
// Create the component under the DUP_REMOVE ...
assert node.getChildCount() == 1;
ProcessingComponent distinctDelegate = createComponent(originalQuery,
context,
node.getFirstChild(),
columns,
processingContext);
component = new DistinctComponent(distinctDelegate);
break;
case GROUP:
throw new UnsupportedOperationException();
case JOIN:
// Create the components under the JOIN ...
assert node.getChildCount() == 2;
PlanNode leftPlan = node.getFirstChild();
PlanNode rightPlan = node.getLastChild();
// Define the columns for each side, taken from the supplied columns ...
Columns leftColumns = createColumnsFor(leftPlan, columns);
Columns rightColumns = createColumnsFor(rightPlan, columns);
// Query context for the join (must remove isExists condition).
QueryContext joinQueryContext = context;
if (context.getHints().isExistsQuery) {
// must not push down a LIMIT 1 condition to joins.
PlanHints joinPlanHints = context.getHints().clone();
joinPlanHints.isExistsQuery = false;
joinQueryContext = context.with(joinPlanHints);
}
ProcessingComponent left = createComponent(originalQuery,
joinQueryContext,
leftPlan,
leftColumns,
processingContext);
ProcessingComponent right = createComponent(originalQuery,
joinQueryContext,
rightPlan,
rightColumns,
processingContext);
// Create the join component ...
JoinAlgorithm algorithm = node.getProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.class);
JoinType joinType = node.getProperty(Property.JOIN_TYPE, JoinType.class);
JoinCondition joinCondition = node.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
switch (algorithm) {
case MERGE:
if (joinCondition instanceof SameNodeJoinCondition) {
SameNodeJoinCondition condition = (SameNodeJoinCondition)joinCondition;
component = new MergeJoinComponent(context, left, right, condition, joinType);
} else if (joinCondition instanceof ChildNodeJoinCondition) {
ChildNodeJoinCondition condition = (ChildNodeJoinCondition)joinCondition;
component = new MergeJoinComponent(context, left, right, condition, joinType);
} else if (joinCondition instanceof EquiJoinCondition) {
EquiJoinCondition condition = (EquiJoinCondition)joinCondition;
component = new MergeJoinComponent(context, left, right, condition, joinType);
} else {
assert false : "Unable to use merge algorithm with descendant node join conditions";
throw new UnsupportedOperationException();
}
break;
case NESTED_LOOP:
component = new NestedLoopJoinComponent(context, left, right, joinCondition, joinType);
break;
}
// For each Constraint object applied to the JOIN, simply create a SelectComponent on top ...
List constraints = node.getPropertyAsList(Property.JOIN_CONSTRAINTS, Constraint.class);
if (constraints != null) {
for (Constraint constraint : constraints) {
component = new SelectComponent(component, constraint, context.getVariables());
}
}
break;
case LIMIT:
// Create the component under the LIMIT ...
assert node.getChildCount() == 1;
ProcessingComponent delegate = createComponent(originalQuery,
context,
node.getFirstChild(),
columns,
processingContext);
// Then create the limit component ...
Integer rowLimit = node.getProperty(Property.LIMIT_COUNT, Integer.class);
Integer offset = node.getProperty(Property.LIMIT_OFFSET, Integer.class);
Limit limit = Limit.NONE;
if (rowLimit != null) limit = limit.withRowLimit(rowLimit.intValue());
if (offset != null) limit = limit.withOffset(offset.intValue());
// And wrap the delegate
if (!limit.isUnlimited()) component = new LimitComponent(delegate, limit);
break;
case NULL:
component = new NoResultsComponent(context, columns);
break;
case PROJECT:
// Create the component under the PROJECT ...
assert node.getChildCount() == 1;
ProcessingComponent projectDelegate = createComponent(originalQuery,
context,
node.getFirstChild(),
columns,
processingContext);
// Then create the project component ...
List projectedColumns = node.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
component = new ProjectComponent(projectDelegate, projectedColumns);
break;
case SELECT:
// Create the component under the SELECT ...
assert node.getChildCount() == 1;
ProcessingComponent selectDelegate = createComponent(originalQuery,
context,
node.getFirstChild(),
columns,
processingContext);
// Then create the select component ...
Constraint constraint = node.getProperty(Property.SELECT_CRITERIA, Constraint.class);
component = new SelectComponent(selectDelegate, constraint, context.getVariables());
break;
case SET_OPERATION:
// Create the components under the SET_OPERATION ...
List setDelegates = new LinkedList();
for (PlanNode child : node) {
setDelegates.add(createComponent(originalQuery, context, child, columns, processingContext));
}
// Then create the select component ...
Operation operation = node.getProperty(Property.SET_OPERATION, Operation.class);
boolean all = node.getProperty(Property.SET_USE_ALL, Boolean.class);
boolean alreadySorted = false; // ????
switch (operation) {
case EXCEPT:
component = new ExceptComponent(context, columns, setDelegates, alreadySorted, all);
break;
case INTERSECT:
component = new IntersectComponent(context, columns, setDelegates, alreadySorted, all);
break;
case UNION:
component = new UnionComponent(context, columns, setDelegates, alreadySorted, all);
break;
}
break;
case SORT:
// Create the component under the SORT ...
assert node.getChildCount() == 1;
ProcessingComponent sortDelegate = createComponent(originalQuery,
context,
node.getFirstChild(),
columns,
processingContext);
// Then create the sort component ...
List orderBys = node.getPropertyAsList(Property.SORT_ORDER_BY, Object.class);
if (orderBys.isEmpty()) {
component = sortDelegate;
} else {
if (orderBys.get(0) instanceof Ordering) {
List orderings = new ArrayList(orderBys.size());
for (Object orderBy : orderBys) {
orderings.add((Ordering)orderBy);
}
// Determine the alias-to-name mappings for the selectors in the orderings ...
Map sourceNamesByAlias = new HashMap();
for (PlanNode source : node.findAllAtOrBelow(Type.SOURCE)) {
SelectorName name = source.getProperty(Property.SOURCE_NAME, SelectorName.class);
SelectorName alias = source.getProperty(Property.SOURCE_ALIAS, SelectorName.class);
if (alias != null) sourceNamesByAlias.put(alias, name);
}
// Now create the sorting component ...
component = new SortValuesComponent(sortDelegate, orderings, sourceNamesByAlias);
} else {
// Order by the location(s) because it's before a merge-join ...
component = new SortLocationsComponent(sortDelegate);
}
}
break;
case DEPENDENT_QUERY:
// Create the components under the JOIN ...
assert node.getChildCount() == 2;
leftPlan = node.getFirstChild();
rightPlan = node.getLastChild();
// Define the columns for each side, taken from the supplied columns ...
leftColumns = createColumnsFor(leftPlan, columns);
rightColumns = createColumnsFor(rightPlan, columns);
left = createComponent(originalQuery, context, leftPlan, leftColumns, processingContext);
right = createComponent(originalQuery, context, rightPlan, rightColumns, processingContext);
// Look for a variable name on the left and right plans ...
String leftVariableName = leftPlan.getProperty(Property.VARIABLE_NAME, String.class);
String rightVariableName = rightPlan.getProperty(Property.VARIABLE_NAME, String.class);
component = new DependentQueryComponent(context, left, right, leftVariableName, rightVariableName);
break;
case SOURCE:
assert false : "Source nodes should always be below ACCESS nodes by the time a plan is executed";
throw new UnsupportedOperationException();
}
assert component != null;
return component;
}
protected Columns createColumnsFor( PlanNode node,
Columns projectedColumns ) {
PlanNode project = node.findAtOrBelow(Type.PROJECT);
assert project != null;
List columns = project.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
List columnTypes = project.getPropertyAsList(Property.PROJECT_COLUMN_TYPES, String.class);
assert columns != null;
assert !columns.isEmpty();
assert columnTypes != null;
assert columnTypes.size() == columns.size();
return new QueryResultColumns(columns, columnTypes, projectedColumns.hasFullTextSearchScores());
}
}