예제 #1
0
  /**
   * Returns a tuple descriptor for the aggregation/analytic's intermediate or final result,
   * depending on whether isOutputTuple is true or false. Also updates the appropriate substitution
   * map, and creates and registers auxiliary equality predicates between the grouping slots and the
   * grouping exprs.
   */
  private TupleDescriptor createTupleDesc(Analyzer analyzer, boolean isOutputTuple) {
    TupleDescriptor result =
        analyzer
            .getDescTbl()
            .createTupleDescriptor(tupleDebugName() + (isOutputTuple ? "-out" : "-intermed"));
    List<Expr> exprs =
        Lists.newArrayListWithCapacity(groupingExprs_.size() + aggregateExprs_.size());
    exprs.addAll(groupingExprs_);
    exprs.addAll(aggregateExprs_);

    int aggregateExprStartIndex = groupingExprs_.size();
    for (int i = 0; i < exprs.size(); ++i) {
      Expr expr = exprs.get(i);
      SlotDescriptor slotDesc = analyzer.addSlotDescriptor(result);
      slotDesc.setLabel(expr.toSql());
      slotDesc.setStats(ColumnStats.fromExpr(expr));
      Preconditions.checkState(expr.getType().isValid());
      slotDesc.setType(expr.getType());
      if (i < aggregateExprStartIndex) {
        // register equivalence between grouping slot and grouping expr;
        // do this only when the grouping expr isn't a constant, otherwise
        // it'll simply show up as a gratuitous HAVING predicate
        // (which would actually be incorrect if the constant happens to be NULL)
        if (!expr.isConstant()) {
          analyzer.createAuxEquivPredicate(new SlotRef(slotDesc), expr.clone());
        }
      } else {
        Preconditions.checkArgument(expr instanceof FunctionCallExpr);
        FunctionCallExpr aggExpr = (FunctionCallExpr) expr;
        if (aggExpr.isMergeAggFn()) {
          slotDesc.setLabel(aggExpr.getChild(0).toSql());
        } else {
          slotDesc.setLabel(aggExpr.toSql());
        }

        // count(*) is non-nullable.
        if (aggExpr.getFnName().getFunction().equals("count")) {
          // TODO: Consider making nullability a property of types or of builtin agg fns.
          // row_number, rank, and dense_rank are non-nullable as well.
          slotDesc.setIsNullable(false);
        }
        if (!isOutputTuple) {
          Type intermediateType = ((AggregateFunction) aggExpr.fn_).getIntermediateType();
          if (intermediateType != null) {
            // Use the output type as intermediate if the function has a wildcard decimal.
            if (!intermediateType.isWildcardDecimal()) {
              slotDesc.setType(intermediateType);
            } else {
              Preconditions.checkState(expr.getType().isDecimal());
            }
          }
        }
      }
    }
    String prefix = (isOutputTuple ? "result " : "intermediate ");
    LOG.trace(prefix + " tuple=" + result.debugString());
    return result;
  }
예제 #2
0
  /*
   * Create a map from COUNT([ALL]) -> zeroifnull(COUNT([ALL])) if
   * i) There is no GROUP-BY, and
   * ii) There are other distinct aggregates to be evaluated.
   * This transformation is necessary for COUNT to correctly return 0 for empty
   * input relations.
   */
  private Expr.SubstitutionMap createCountAllMap(List<FunctionCallExpr> aggExprs, Analyzer analyzer)
      throws AuthorizationException, AnalysisException {
    Expr.SubstitutionMap scalarCountAllMap = new Expr.SubstitutionMap();

    if (groupingExprs_ != null && !groupingExprs_.isEmpty()) {
      // There are grouping expressions, so no substitution needs to be done.
      return scalarCountAllMap;
    }

    com.google.common.base.Predicate<FunctionCallExpr> isNotDistinctPred =
        new com.google.common.base.Predicate<FunctionCallExpr>() {
          public boolean apply(FunctionCallExpr expr) {
            return !expr.isDistinct();
          }
        };
    if (Iterables.all(aggExprs, isNotDistinctPred)) {
      // Only [ALL] aggs, so no substitution needs to be done.
      return scalarCountAllMap;
    }

    com.google.common.base.Predicate<FunctionCallExpr> isCountPred =
        new com.google.common.base.Predicate<FunctionCallExpr>() {
          public boolean apply(FunctionCallExpr expr) {
            return expr.getFnName().getFunction().equals("count");
          }
        };

    Iterable<FunctionCallExpr> countAllAggs =
        Iterables.filter(aggExprs, Predicates.and(isCountPred, isNotDistinctPred));
    for (FunctionCallExpr countAllAgg : countAllAggs) {
      // Replace COUNT(ALL) with zeroifnull(COUNT(ALL))
      ArrayList<Expr> zeroIfNullParam = Lists.newArrayList(countAllAgg.clone(null));
      FunctionCallExpr zeroIfNull = new FunctionCallExpr("zeroifnull", zeroIfNullParam);
      zeroIfNull.analyze(analyzer);
      scalarCountAllMap.addMapping(countAllAgg, zeroIfNull);
    }

    return scalarCountAllMap;
  }
예제 #3
0
  /** Build smap AVG -> SUM/COUNT; assumes that select list and having clause have been analyzed. */
  private Expr.SubstitutionMap createAvgSMap(
      ArrayList<FunctionCallExpr> aggExprs, Analyzer analyzer)
      throws AnalysisException, AuthorizationException {
    Expr.SubstitutionMap result = new Expr.SubstitutionMap();
    for (FunctionCallExpr aggExpr : aggExprs) {
      if (!aggExpr.getFnName().getFunction().equals("avg")) continue;
      // Transform avg(TIMESTAMP) to cast(avg(cast(TIMESTAMP as DOUBLE)) as TIMESTAMP)
      CastExpr inCastExpr = null;
      if (aggExpr.getChild(0).type_.getPrimitiveType() == PrimitiveType.TIMESTAMP) {
        inCastExpr = new CastExpr(ColumnType.DOUBLE, aggExpr.getChild(0).clone(null), false);
      }

      List<Expr> sumInputExprs =
          Lists.newArrayList(
              aggExpr.getChild(0).type_.getPrimitiveType() == PrimitiveType.TIMESTAMP
                  ? inCastExpr
                  : aggExpr.getChild(0).clone(null));
      List<Expr> countInputExpr = Lists.newArrayList(aggExpr.getChild(0).clone(null));

      FunctionCallExpr sumExpr =
          new FunctionCallExpr("sum", new FunctionParams(aggExpr.isDistinct(), sumInputExprs));
      FunctionCallExpr countExpr =
          new FunctionCallExpr("count", new FunctionParams(aggExpr.isDistinct(), countInputExpr));
      ArithmeticExpr divExpr =
          new ArithmeticExpr(ArithmeticExpr.Operator.DIVIDE, sumExpr, countExpr);

      if (aggExpr.getChild(0).type_.getPrimitiveType() == PrimitiveType.TIMESTAMP) {
        CastExpr outCastExpr = new CastExpr(ColumnType.TIMESTAMP, divExpr, false);
        outCastExpr.analyze(analyzer);
        result.addMapping(aggExpr, outCastExpr);
      } else {
        divExpr.analyze(analyzer);
        result.addMapping(aggExpr, divExpr);
      }
    }
    LOG.debug("avg smap: " + result.debugString());
    return result;
  }