Beispiel #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;
  }
Beispiel #2
0
  public CastExpr(Type targetType, Expr e, boolean isImplicit) {
    super();
    Preconditions.checkArgument(targetType.isValid());
    this.targetType_ = targetType;
    this.isImplicit_ = isImplicit;
    Preconditions.checkNotNull(e);
    if (isImplicit) {
      // replace existing implicit casts
      if (e instanceof CastExpr) {
        CastExpr castExpr = (CastExpr) e;
        if (castExpr.isImplicit()) e = castExpr.getChild(0);
      }
      children_.add(e);

      // Implicit casts don't call analyze()
      // TODO: this doesn't seem like the cleanest approach but there are places
      // we generate these (e.g. table loading) where there is no analyzer object.
      try {
        analyze();
        computeNumDistinctValues();
      } catch (AnalysisException ex) {
        Preconditions.checkState(false, "Implicit casts should never throw analysis exception.");
      }
      isAnalyzed_ = true;
    } else {
      children_.add(e);
    }
  }
Beispiel #3
0
  /** Checks for type compatibility of column and expr. Returns compatible (possibly cast) expr. */
  private Expr checkTypeCompatibility(Column column, Expr expr) throws AnalysisException {
    // Check for compatible type, and add casts to the selectListExprs if necessary.
    // We don't allow casting to a lower precision type.
    Type colType = column.getType();
    Type exprType = expr.getType();
    // Trivially compatible.
    if (colType.equals(exprType)) return expr;

    Type compatibleType = Type.getAssignmentCompatibleType(colType, exprType);
    // Incompatible types.
    if (!compatibleType.isValid()) {
      throw new AnalysisException(
          String.format(
              "Target table '%s' is incompatible with SELECT / PARTITION expressions.\n"
                  + "Expression '%s' (type: %s) is not compatible with column '%s' (type: %s)",
              targetTableName_, expr.toSql(), exprType, column.getName(), colType));
    }
    // Loss of precision when inserting into the table.
    if (!compatibleType.equals(colType) && !compatibleType.isNull()) {
      throw new AnalysisException(
          String.format(
              "Possible loss of precision for target table '%s'.\n"
                  + "Expression '%s' (type: %s) would need to be cast to %s"
                  + " for column '%s'",
              targetTableName_, expr.toSql(), exprType, colType, column.getName()));
    }
    // Add a cast to the selectListExpr to the higher type.
    return expr.castTo(compatibleType);
  }
Beispiel #4
0
 @Override
 public boolean equals(Object obj) {
   if (this == obj) return true;
   if (obj instanceof CastExpr) {
     CastExpr other = (CastExpr) obj;
     return isImplicit_ == other.isImplicit_
         && targetType_.equals(other.targetType_)
         && super.equals(obj);
   }
   // Ignore implicit casts when comparing expr trees.
   if (isImplicit_) return getChild(0).equals(obj);
   return false;
 }
Beispiel #5
0
 public static void initBuiltins(Db db) {
   for (Type fromType : Type.getSupportedTypes()) {
     if (fromType.isNull()) continue;
     for (Type toType : Type.getSupportedTypes()) {
       if (toType.isNull()) continue;
       // Disable casting from string to boolean
       if (fromType.isStringType() && toType.isBoolean()) continue;
       // Disable casting from boolean/timestamp to decimal
       if ((fromType.isBoolean() || fromType.isDateType()) && toType.isDecimal()) {
         continue;
       }
       if (fromType.getPrimitiveType() == PrimitiveType.STRING
           && toType.getPrimitiveType() == PrimitiveType.CHAR) {
         // Allow casting from String to Char(N)
         String beSymbol = "impala::CastFunctions::CastToChar";
         db.addBuiltin(
             ScalarFunction.createBuiltin(
                 getFnName(ScalarType.CHAR),
                 Lists.newArrayList((Type) ScalarType.STRING),
                 false,
                 ScalarType.CHAR,
                 beSymbol,
                 null,
                 null,
                 true));
         continue;
       }
       if (fromType.getPrimitiveType() == PrimitiveType.CHAR
           && toType.getPrimitiveType() == PrimitiveType.CHAR) {
         // Allow casting from CHAR(N) to Char(N)
         String beSymbol = "impala::CastFunctions::CastToChar";
         db.addBuiltin(
             ScalarFunction.createBuiltin(
                 getFnName(ScalarType.CHAR),
                 Lists.newArrayList((Type) ScalarType.createCharType(-1)),
                 false,
                 ScalarType.CHAR,
                 beSymbol,
                 null,
                 null,
                 true));
         continue;
       }
       if (fromType.getPrimitiveType() == PrimitiveType.VARCHAR
           && toType.getPrimitiveType() == PrimitiveType.VARCHAR) {
         // Allow casting from VARCHAR(N) to VARCHAR(M)
         String beSymbol = "impala::CastFunctions::CastToStringVal";
         db.addBuiltin(
             ScalarFunction.createBuiltin(
                 getFnName(ScalarType.VARCHAR),
                 Lists.newArrayList((Type) ScalarType.VARCHAR),
                 false,
                 ScalarType.VARCHAR,
                 beSymbol,
                 null,
                 null,
                 true));
         continue;
       }
       if (fromType.getPrimitiveType() == PrimitiveType.VARCHAR
           && toType.getPrimitiveType() == PrimitiveType.CHAR) {
         // Allow casting from VARCHAR(N) to CHAR(M)
         String beSymbol = "impala::CastFunctions::CastToChar";
         db.addBuiltin(
             ScalarFunction.createBuiltin(
                 getFnName(ScalarType.CHAR),
                 Lists.newArrayList((Type) ScalarType.VARCHAR),
                 false,
                 ScalarType.CHAR,
                 beSymbol,
                 null,
                 null,
                 true));
         continue;
       }
       if (fromType.getPrimitiveType() == PrimitiveType.CHAR
           && toType.getPrimitiveType() == PrimitiveType.VARCHAR) {
         // Allow casting from CHAR(N) to VARCHAR(M)
         String beSymbol = "impala::CastFunctions::CastToStringVal";
         db.addBuiltin(
             ScalarFunction.createBuiltin(
                 getFnName(ScalarType.VARCHAR),
                 Lists.newArrayList((Type) ScalarType.CHAR),
                 false,
                 ScalarType.VARCHAR,
                 beSymbol,
                 null,
                 null,
                 true));
         continue;
       }
       // Disable no-op casts
       if (fromType.equals(toType) && !fromType.isDecimal()) continue;
       String beClass =
           toType.isDecimal() || fromType.isDecimal() ? "DecimalOperators" : "CastFunctions";
       String beSymbol = "impala::" + beClass + "::CastTo" + Function.getUdfType(toType);
       db.addBuiltin(
           ScalarFunction.createBuiltin(
               getFnName(toType),
               Lists.newArrayList(fromType),
               false,
               toType,
               beSymbol,
               null,
               null,
               true));
     }
   }
 }
Beispiel #6
0
 private static String getFnName(Type targetType) {
   return "castTo" + targetType.getPrimitiveType().toString();
 }
Beispiel #7
0
  private void analyze() throws AnalysisException {
    targetType_.analyze();
    if (targetType_.isComplexType()) {
      throw new AnalysisException("Unsupported cast to complex type: " + targetType_.toSql());
    }

    boolean readyForCharCast =
        children_.get(0).getType().getPrimitiveType() == PrimitiveType.STRING
            || children_.get(0).getType().getPrimitiveType() == PrimitiveType.CHAR;
    if (targetType_.getPrimitiveType() == PrimitiveType.CHAR && !readyForCharCast) {
      // Back end functions only exist to cast string types to CHAR, there is not a cast
      // for every type since it is redundant with STRING. Casts to go through 2 casts:
      // (1) cast to string, to stringify the value
      // (2) cast to CHAR, to truncate or pad with spaces
      CastExpr tostring = new CastExpr(ScalarType.STRING, children_.get(0), true);
      tostring.analyze();
      children_.set(0, tostring);
    }

    if (children_.get(0) instanceof NumericLiteral && targetType_.isFloatingPointType()) {
      // Special case casting a decimal literal to a floating point number. The
      // decimal literal can be interpreted as either and we want to avoid casts
      // since that can result in loss of accuracy.
      ((NumericLiteral) children_.get(0)).explicitlyCastToFloat(targetType_);
    }

    if (children_.get(0).getType().isNull()) {
      // Make sure BE never sees TYPE_NULL
      uncheckedCastChild(targetType_, 0);
    }

    // Ensure child has non-null type (even if it's a null literal). This is required
    // for the UDF interface.
    if (children_.get(0) instanceof NullLiteral) {
      NullLiteral nullChild = (NullLiteral) (children_.get(0));
      nullChild.uncheckedCastTo(targetType_);
    }

    Type childType = children_.get(0).type_;
    Preconditions.checkState(!childType.isNull());
    if (childType.equals(targetType_)) {
      noOp_ = true;
      type_ = targetType_;
      return;
    }

    FunctionName fnName = new FunctionName(Catalog.BUILTINS_DB, getFnName(targetType_));
    Type[] args = {childType};
    Function searchDesc = new Function(fnName, args, Type.INVALID, false);
    if (isImplicit_) {
      fn_ = Catalog.getBuiltin(searchDesc, CompareMode.IS_SUPERTYPE_OF);
      Preconditions.checkState(fn_ != null);
    } else {
      fn_ = Catalog.getBuiltin(searchDesc, CompareMode.IS_IDENTICAL);
      if (fn_ == null) {
        // allow for promotion from CHAR to STRING; only if no exact match is found
        fn_ = Catalog.getBuiltin(searchDesc.promoteCharsToStrings(), CompareMode.IS_IDENTICAL);
      }
    }
    if (fn_ == null) {
      throw new AnalysisException(
          "Invalid type cast of "
              + getChild(0).toSql()
              + " from "
              + childType
              + " to "
              + targetType_);
    }

    Preconditions.checkState(
        targetType_.matchesType(fn_.getReturnType()), targetType_ + " != " + fn_.getReturnType());
    type_ = targetType_;
  }
Beispiel #8
0
 @Override
 public String toSqlImpl() {
   if (isImplicit_) return getChild(0).toSql();
   return "CAST(" + getChild(0).toSql() + " AS " + targetType_.toString() + ")";
 }