| /* |
| * 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; |
| } |
| } |