@Override
  public QueryNodeWithBindingSet optimize(
      final AST2BOpContext context, final QueryNodeWithBindingSet input) {

    final IQueryNode queryNode = input.getQueryNode();
    final IBindingSet[] bindingSets = input.getBindingSets();

    final QueryRoot queryRoot = (QueryRoot) queryNode;

    final StaticAnalysis sa = new StaticAnalysis(queryRoot, context);

    // First, process any pre-existing named subqueries.
    {
      final NamedSubqueriesNode namedSubqueries = queryRoot.getNamedSubqueries();

      if (namedSubqueries != null) {

        // Note: works around concurrent modification error.
        final List<NamedSubqueryRoot> list =
            BOpUtility.toList(namedSubqueries, NamedSubqueryRoot.class);

        for (NamedSubqueryRoot namedSubquery : list) {

          liftSubqueries(context, sa, namedSubquery.getWhereClause());
        }
      }
    }

    // Now process the main where clause.
    liftSubqueries(context, sa, queryRoot.getWhereClause());

    if (false) {

      /*
       * Note: This may be enabled to lift all SPARQL 1.1 subqueries into
       * named subqueries. However, I think that the better way to handle
       * this is to run the subqueries either as-bound "chunked" or with
       * ALL solutions from the parent as their inputs (the extreme case
       * of chunked). This can also be applied to handling OPTIONAL groups.
       *
       * @see https://sourceforge.net/apps/trac/bigdata/ticket/377
       */

      rewriteSparql11Subqueries(context, sa, queryRoot);
    }

    return new QueryNodeWithBindingSet(queryRoot, bindingSets);
  }
  private void rewriteSparql11Subqueries(
      final AST2BOpContext context, final StaticAnalysis sa, final QueryRoot queryRoot) {

    final Striterator itr2 =
        new Striterator(BOpUtility.postOrderIterator((BOp) queryRoot.getWhereClause()));

    itr2.addTypeFilter(SubqueryRoot.class);

    final List<SubqueryRoot> subqueries = new LinkedList<SubqueryRoot>();

    while (itr2.hasNext()) {

      subqueries.add((SubqueryRoot) itr2.next());
    }

    for (SubqueryRoot subquery : subqueries) {

      liftSparql11Subquery(context, sa, subquery);
    }
  }
Example #3
0
  public GroupByState(
      final IValueExpression<?>[] select,
      final IValueExpression<?>[] groupBy,
      final IConstraint[] having) {

    // normalize an empty[] to a null.
    this.groupBy = groupBy != null && groupBy.length == 0 ? null : groupBy;

    // must be non-null, non-empty array.
    this.select = select;

    if (select == null) throw new IllegalArgumentException();

    if (select.length == 0) throw new IllegalArgumentException();

    // normalize an empty[] to a null.
    this.having = having != null && having.length == 0 ? null : having;

    // true iff any aggregate expression uses DISTINCT.
    final AtomicBoolean anyDistinct = new AtomicBoolean(false);

    // true iff any aggregate expression nests another aggregate expression.
    final AtomicBoolean nestedAggregates = new AtomicBoolean(false);

    /*
     * Validate GROUP_BY value expressions.
     *
     * Note: The GROUP BY clause may include bare variables such as "?x",
     * non-aggregate expressions such as "STR(?x)" and declarations of
     * variables for non-aggregate expressions such as "STR(?x) as strX".
     * However, only bare variables or variables declared using "AS" may
     * appear in the SELECT clause. Those variables are collected in
     * [groupByVars].
     *
     * Note: Aggregate functions MAY NOT appear in the GROUP_BY clause.
     */
    if (groupBy != null) {

      // Collect top-level variables from GROUP_BY value expressions.
      for (IValueExpression<?> expr : groupBy) {
        if (expr instanceof IVariable<?>) {
          groupByVars.add((IVariable<?>) expr);
        } else if (expr instanceof IBind<?>) {
          final IBind<?> bindExpr = (IBind<?>) expr;
          final IValueExpression<?> e = bindExpr.getExpr();
          if (isAggregate(
              e,
              false /* isSelectClause */,
              null /* isSelectDependency */,
              nestedAggregates,
              anyDistinct)) {
            throw new IllegalArgumentException(
                "Aggregate expression not allowed in GROUP_BY: " + expr);
          }
          groupByVars.add(bindExpr.getVar());
        }
      }
    }

    /*
     * Validate SELECT value expressions.
     *
     * Note: SELECT value expressions must be either variables appearing in
     * the top-level of the GROUP BY value expressions -or- a IBind wrapping
     * an aggregate function.
     *
     * Note: Certain optimizations are possible when none of the SELECT
     * value expressions use DISTINCT.
     *
     * Note: Certain optimizations are possible when all of the SELECT value
     * expressions may be computed based on per-group counters.
     */
    {
      // true iff any aggregate expression uses a reference to another
      // aggregate expression in the select clause.
      final AtomicBoolean selectDependency = new AtomicBoolean(false);
      for (IValueExpression<?> expr : select) {
        /*
         * Each SELECT value expression must be either a top-level
         * IVariable in the GROUP BY clause or an IBind wrapping a value
         * expression consisting solely of aggregates (which may of
         * course wrap bare variables) and constants.
         */
        if (expr instanceof IVariable<?>) {
          final IVariable<?> var = (IVariable<?>) expr;
          if (!groupByVars.contains(var)) {
            throw new IllegalArgumentException(
                "Bare variable not declared by GROUP_BY clause: " + var);
          }
          selectVars.add(var);
        } else if (expr instanceof IBind<?>) {
          /*
           * Child of IBind must be a valid aggregate expression
           * consisting solely of aggregates (which may wrap bare
           * variables declared in the GROUP_BY clause) and constants.
           *
           * Note: Top-level variables already declared in a GROUP_BY
           * or SELECT clause MAY appear within other value
           * expressions in the SELECT clause.
           *
           * Note: If any aggregate in the expression uses DISTINCT
           * then we make a note of that as certain optimizations are
           * not possible when DISTINCT is used within an aggregate
           * expression (this is done by isAggregate()).
           */
          final IBind<?> bindExpr = (IBind<?>) expr;
          final IValueExpression<?> e = bindExpr.getExpr();
          if (!isAggregate(
              e, true /* isSelectClause */, selectDependency, nestedAggregates, anyDistinct))
            throw new IllegalArgumentException("Not an aggregate: " + bindExpr);
          selectVars.add(bindExpr.getVar());
        } else {
          throw new IllegalArgumentException(
              "Top-level of SELECT expression must be IVariable or IBind: " + expr);
        }
      }
      this.selectDependency = selectDependency.get();
    }

    /*
     * HAVING clause.
     *
     * The having[] may be null or an empty[]. However, any value
     * expressions used within the IConstraint[] must be aggregates (as
     * defined for SELECT expressions).
     */
    /*
     * true iff none of the value expressions in the HAVING clause involve
     * IAggregate functions.
     */
    boolean simpleHaving = true;
    if (having != null) {

      for (IConstraint c : having) {

        /*
         * The constraint must be an aggregate expression.
         *
         * Note: Top-level variables already declared in a GROUP_BY or
         * SELECT clause MAY appear within value expressions in the
         * HAVING clause.
         *
         * Note: If any aggregate in the expression uses DISTINCT then
         * we make a note of that as certain optimizations are not
         * possible when DISTINCT is used within an aggregate expression
         * (this is done by isAggregate()).
         */

        if (!isAggregate(
            c,
            false /* isSelectClause */,
            null /* isSelectDependency */,
            nestedAggregates,
            anyDistinct)) throw new IllegalArgumentException("Not an aggregate: " + c);

        if (simpleHaving) {
          /*
           * Inspect the value expression for each constraint.
           * Typically the constraint will be a SPARQLConstraint,
           * which reports the EBV of a value expression. If that
           * value expression uses an IAggregate function then we set
           * [simpleHaving := false]. We are done as soon as we have
           * falsified the "simpleHaving" hypothesis.
           */
          final IValueExpression<?> expr = ((IValueExpressionConstraint<?>) c).getValueExpression();
          final Iterator<BOp> itr = BOpUtility.preOrderIterator(expr);
          while (itr.hasNext()) {
            final BOp t = itr.next();
            if (t instanceof IAggregate<?>) {
              simpleHaving = false;
              break;
            }
          }
        }
      }
    }
    this.simpleHaving = simpleHaving;

    // true iff any aggregate function nests another aggregate function
    // within it.
    this.nestedAggregates = nestedAggregates.get();

    // true iff DISTINCT used w/in aggregate function in SELECT or HAVING.
    this.anyDistinct = anyDistinct.get();
  }