private void liftSparql11Subquery( final AST2BOpContext context, final StaticAnalysis sa, final SubqueryRoot subqueryRoot) { final IGroupNode<?> parent = subqueryRoot.getParent(); final String newName = "-subSelect-" + context.nextId(); final NamedSubqueryInclude include = new NamedSubqueryInclude(newName); /** * Set query hints from the parent join group. * * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/791" > Clean up query hints * </a> */ include.setQueryHints((Properties) parent.getProperty(ASTBase.Annotations.QUERY_HINTS)); /** * Copy across attached join filters. * * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/796" >Filter assigned to * sub-query by query generator is dropped from evaluation</a> */ include.setAttachedJoinFilters(subqueryRoot.getAttachedJoinFilters()); /* * Note: A SubqueryRoot normally starts out as the sole child of a * JoinGroupNode. However, other rewrites may have written out that * JoinGroupNode and it does not appear to be present for an ASK * subquery. * * Therefore, when the parent of the SubqueryRoot is a JoinGroupNode * having the SubqueryRoot as its only child, we use the parent's parent * in order to replace the JoinGroupNode when we lift out the * SubqueryRoot. Otherwise we use the parent since there is no wrapping * JoinGroupNode (or if there is, it has some other stuff in there as * well). * * BLZG-1542 -> there is an additional thing we need to take care of: * whenever the parent node is an OPTIONAL or MINUS, we * must not remove it, otherwise we would just "drop" an * OPTIONAL or MINUS, thus changing the query's semantics * */ if ((parent instanceof JoinGroupNode) && !((JoinGroupNode) parent).isOptional() && !((JoinGroupNode) parent).isMinus() && ((BOp) parent).arity() == 1 && parent.getParent() != null && !((IGroupNode<?>) parent.getParent() instanceof UnionNode)) { final IGroupNode<IGroupMemberNode> pp = parent.getParent(); // Replace the sub-select with the include. if (((ASTBase) pp).replaceWith((BOp) parent, include) == 0) throw new AssertionError(); } else { // Replace the sub-select with the include. if (((ASTBase) parent).replaceWith((BOp) subqueryRoot, include) == 0) throw new AssertionError(); } final NamedSubqueryRoot nsr = new NamedSubqueryRoot(subqueryRoot.getQueryType(), newName); /** * Copy across query hints from the original subquery. * * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/791" > Clean up query hints * </a> */ nsr.setQueryHints(subqueryRoot.getQueryHints()); nsr.setConstruct(subqueryRoot.getConstruct()); nsr.setGroupBy(subqueryRoot.getGroupBy()); nsr.setHaving(subqueryRoot.getHaving()); nsr.setOrderBy(subqueryRoot.getOrderBy()); nsr.setProjection(subqueryRoot.getProjection()); nsr.setSlice(subqueryRoot.getSlice()); nsr.setWhereClause(subqueryRoot.getWhereClause()); nsr.setBindingsClause(subqueryRoot.getBindingsClause()); sa.getQueryRoot().getNamedSubqueriesNotNull().add(nsr); }
/** Apply all optimizations. */ private void liftSubqueries( final AST2BOpContext context, final StaticAnalysis sa, final GraphPatternGroup<IGroupMemberNode> group) { final int arity = group.arity(); for (int i = 0; i < arity; i++) { final BOp child = (BOp) group.get(i); if (child instanceof GraphPatternGroup<?>) { /* * Note: Do recursion *before* we do the rewrite so we will * rewrite Sub-Sub-Selects. * * FIXME Unit test for sub-sub-select optimization. */ liftSubqueries(context, sa, ((GraphPatternGroup<IGroupMemberNode>) child)); } else if (child instanceof SubqueryRoot) { // Recursion into subqueries. final SubqueryRoot subqueryRoot = (SubqueryRoot) child; liftSubqueries(context, sa, subqueryRoot.getWhereClause()); } else if (child instanceof ServiceNode) { // Do not rewrite things inside of a SERVICE node. continue; } if (!(child instanceof SubqueryRoot)) { continue; } final SubqueryRoot subqueryRoot = (SubqueryRoot) child; if (subqueryRoot.getQueryType() == QueryType.ASK) { /* * FIXME Look at what would be involved in lifting an ASK * sub-query. There are going to be at least two cases. If there * is no join variable, then we always want to lift the ASK * sub-query as it is completely independent of the parent * group. If there is a join variable, then we need to project * solutions which include the join variables from the subquery * and the "ASK". At that point we can hash join against the * projected solutions and the ASK succeeds if the hash join * succeeds. [Add unit tests for this too.] */ continue; } if (subqueryRoot.hasSlice()) { /* * Lift out SPARQL 1.1 subqueries which use LIMIT and/or OFFSET. * * The SliceOp in the subquery will cause the IRunningQuery in * which it appears to be interrupted. Therefore, when a SLICE * is required for a subquery we need to lift it out to run it * as a named subquery. * * TODO There may well be other cases that we have to handle * with as-bound evaluation of a Subquery with a LIMIT/OFFSET. * If so, then the subquery will have to be run using the * SubqueryOp. */ liftSparql11Subquery(context, sa, subqueryRoot); continue; } if (subqueryRoot.hasSlice() && subqueryRoot.getOrderBy() != null) { /* * Lift out SPARQL 1.1 subqueries which use both LIMIT and ORDER * BY. Due to the interaction of the LIMIT and ORDER BY clause, * these subqueries MUST be run first since they can produce * different results if they are run "as-bound". */ liftSparql11Subquery(context, sa, subqueryRoot); continue; } if (StaticAnalysis.isAggregate(subqueryRoot)) { /* * Lift out SPARQL 1.1 subqueries which use {@link IAggregate}s. * This typically provides more efficient evaluation than * repeated as-bound evaluation of the sub-select. It also * prevents inappropriate sharing of the internal state of the * {@link IAggregate} functions. */ liftSparql11Subquery(context, sa, subqueryRoot); continue; } if (subqueryRoot.isRunOnce()) { /* * Lift out SPARQL 1.1 subqueries for which the RUN_ONCE * annotation was specified. */ liftSparql11Subquery(context, sa, subqueryRoot); continue; } /* * FIXME We can not correctly predict the join variables at this * time because that depends on the actual evaluation order. This * has been commented out for now because it will otherwise cause * all sub-selects to be lifted out. */ if (false) { final Set<IVariable<?>> joinVars = sa.getJoinVars(subqueryRoot, new LinkedHashSet<IVariable<?>>()); if (joinVars.isEmpty()) { /* * Lift out SPARQL 1.1 subqueries for which the RUN_ONCE * annotation was specified. */ liftSparql11Subquery(context, sa, subqueryRoot); continue; } } } }