blob: a11fa90a43ebed9b8b37f0e6fbfd9afc1a68edc9 [file] [log] [blame]
/*
* 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.
*/
/* Contains code submitted in email to jena-user@incubator.apache.org
* so software grant and relicensed under the Apache Software License.
* transformFilterConditional
*/
package org.apache.jena.sparql.algebra.optimize;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.jena.graph.Node ;
import org.apache.jena.graph.Triple ;
import org.apache.jena.query.ARQ ;
import org.apache.jena.sparql.algebra.Op ;
import org.apache.jena.sparql.algebra.OpVars ;
import org.apache.jena.sparql.algebra.TransformCopy ;
import org.apache.jena.sparql.algebra.op.* ;
import org.apache.jena.sparql.core.BasicPattern ;
import org.apache.jena.sparql.core.Var ;
import org.apache.jena.sparql.expr.Expr ;
import org.apache.jena.sparql.expr.ExprList ;
import org.apache.jena.sparql.util.VarUtils ;
/**
* Rewrite an algebra expression to put filters as close to their bound
* variables in a BGP. Works on (filter (BGP ...) )
* <p>
* This is a conservative and relatively limited optimization and has been
* superseded in the default optimizer by the {@link TransformFilterPlacement}
* as of the 2.11.x releases. This original version of TransformFilterPlacement
* only operates on filters over BGPs, quad blocks, sequences and conditions
* (a form of LeftJoin with no scope issues) of the same.
* However in some cases it may be desirable to have
* the more limited and conservative behaviour so this is preserved in the code
* for those who want to use this.
* </p>
* <p>
* The context flag {@link ARQ#optFilterPlacementConservative} may be set to
* have the default optimizer use this in place of the newer and more aggressive
* {@link TransformFilterPlacement}
* </p>
*/
public class TransformFilterPlacementConservative extends TransformCopy {
public TransformFilterPlacementConservative( ) {}
@Override
public Op transform(OpFilter opFilter, Op x) {
// Destructive use of exprs - copy it.
ExprList exprs = ExprList.copy(opFilter.getExprs());
Set<Var> varsScope = new HashSet<>();
Op op = transform(exprs, varsScope, x);
if (op == x)
// Didn't do anything.
return super.transform(opFilter, x);
// Remaining exprs
op = buildFilter(exprs, op);
return op;
}
private static Op transform(ExprList exprs, Set<Var> varsScope, Op x) {
// OpAssign/OpExtend could be done if the assignment and exprs are
// independent.
// Dispatch by visitor??
if (x instanceof OpBGP)
return transformFilterBGP(exprs, varsScope, (OpBGP) x);
if (x instanceof OpSequence)
return transformFilterSequence(exprs, varsScope, (OpSequence) x);
if (x instanceof OpQuadPattern)
return transformFilterQuadPattern(exprs, varsScope, (OpQuadPattern) x);
if (x instanceof OpSequence)
return transformFilterSequence(exprs, varsScope, (OpSequence) x);
if (x instanceof OpConditional)
return transformFilterConditional(exprs, varsScope, (OpConditional) x);
// Not special - advance the variable scope tracking.
OpVars.visibleVars(x, varsScope);
return x;
}
// == The transformFilter* modify the exprs and patternVarsScope arguments
private static Op transformFilterBGP(ExprList exprs, Set<Var> patternVarsScope, OpBGP x) {
return transformFilterBGP(exprs, patternVarsScope, x.getPattern());
}
// Mutates exprs
private static Op transformFilterBGP(ExprList exprs, Set<Var> patternVarsScope, BasicPattern pattern) {
// Any filters that depend on no variables.
Op op = insertAnyFilter(exprs, patternVarsScope, null);
for (Triple triple : pattern) {
OpBGP opBGP = getBGP(op);
if (opBGP == null) {
// Last thing was not a BGP (so it likely to be a filter)
// Need to pass the results from that into the next triple.
// Which is a join and sequence is a special case of join
// which always evaluates by passing results of the early
// part into the next element of the sequence.
opBGP = new OpBGP();
op = OpSequence.create(op, opBGP);
}
opBGP.getPattern().add(triple);
// Update variables in scope.
VarUtils.addVarsFromTriple(patternVarsScope, triple);
// Attempt to place any filters
op = insertAnyFilter(exprs, patternVarsScope, op);
}
// Leave any remaining filter expressions - don't wrap up any as
// something else may take them.
return op;
}
/** Find the current OpBGP, or return null. */
private static OpBGP getBGP(Op op) {
if (op instanceof OpBGP)
return (OpBGP) op;
if (op instanceof OpSequence) {
// Is last in OpSequence an BGP?
OpSequence opSeq = (OpSequence) op;
List<Op> x = opSeq.getElements();
if (x.size() > 0) {
Op opTop = x.get(x.size() - 1);
if (opTop instanceof OpBGP)
return (OpBGP) opTop;
// Drop through
}
}
// Can't find.
return null;
}
private static Op transformFilterQuadPattern(ExprList exprs, Set<Var> patternVarsScope, OpQuadPattern pattern) {
return transformFilterQuadPattern(exprs, patternVarsScope, pattern.getGraphNode(), pattern.getBasicPattern());
}
private static Op transformFilterQuadPattern(ExprList exprs, Set<Var> patternVarsScope, Node graphNode, BasicPattern pattern) {
// Any filters that depend on no variables.
Op op = insertAnyFilter(exprs, patternVarsScope, null);
if (Var.isVar(graphNode)) {
// Add in the graph node of the quad block.
// It's picked up after the first triple is processed.
VarUtils.addVar(patternVarsScope, Var.alloc(graphNode));
}
for (Triple triple : pattern) {
OpQuadPattern opQuad = getQuads(op);
if (opQuad == null) {
opQuad = new OpQuadPattern(graphNode, new BasicPattern());
op = OpSequence.create(op, opQuad);
}
opQuad.getBasicPattern().add(triple);
// Update variables in scope.
VarUtils.addVarsFromTriple(patternVarsScope, triple);
// Attempt to place any filters
op = insertAnyFilter(exprs, patternVarsScope, op);
}
return op;
}
/** Find the current OpQuadPattern, or return null. */
private static OpQuadPattern getQuads(Op op) {
if (op instanceof OpQuadPattern)
return (OpQuadPattern) op;
if (op instanceof OpSequence) {
// Is last in OpSequence an BGP?
OpSequence opSeq = (OpSequence) op;
List<Op> x = opSeq.getElements();
if (x.size() > 0) {
Op opTop = x.get(x.size() - 1);
if (opTop instanceof OpQuadPattern)
return (OpQuadPattern) opTop;
// Drop through
}
}
// Can't find.
return null;
}
private static Op transformFilterSequence(ExprList exprs, Set<Var> varScope, OpSequence opSequence) {
List<Op> ops = opSequence.getElements();
// Any filters that depend on no variables.
Op op = insertAnyFilter(exprs, varScope, null);
for ( Op seqElt : ops )
{
// Process the sequence element. This may insert filters (sequence
// or BGP)
seqElt = transform( exprs, varScope, seqElt );
// Merge into sequence.
op = OpSequence.create( op, seqElt );
// Place any filters now ready.
op = insertAnyFilter( exprs, varScope, op );
}
return op;
}
// Modularize.
private static Op transformFilterConditional(ExprList exprs, Set<Var> varScope, OpConditional opConditional) {
// Any filters that depend on no variables.
Op op = insertAnyFilter(exprs, varScope, null);
Op left = opConditional.getLeft();
left = transform(exprs, varScope, left);
Op right = opConditional.getRight();
op = new OpConditional(left, right);
op = insertAnyFilter(exprs, varScope, op);
return op;
}
// ---- Utilities
/** For any expression now in scope, wrap the op with a filter */
private static Op insertAnyFilter(ExprList exprs, Set<Var> patternVarsScope, Op op) {
for (Iterator<Expr> iter = exprs.iterator(); iter.hasNext();) {
Expr expr = iter.next();
// Cache
Set<Var> exprVars = expr.getVarsMentioned();
if (patternVarsScope.containsAll(exprVars)) {
if (op == null)
op = OpTable.unit();
op = OpFilter.filter(expr, op);
iter.remove();
}
}
return op;
}
/** Place expressions around an Op */
private static Op buildFilter(ExprList exprs, Op op) {
if (exprs.isEmpty())
return op;
for (Iterator<Expr> iter = exprs.iterator(); iter.hasNext();) {
Expr expr = iter.next();
if (op == null)
op = OpTable.unit();
op = OpFilter.filter(expr, op);
iter.remove();
}
return op;
}
}