/** * 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; }
/** * Sets table_ based on targetTableName_ and performs table-type specific analysis: - Partition * clause is invalid for unpartitioned Hdfs tables and HBase tables - Overwrite is invalid for * HBase tables - Check INSERT privileges as well as write access to Hdfs paths - Cannot insert * into a view Adds table_ to the analyzer's descriptor table if analysis succeeds. */ private void setTargetTable(Analyzer analyzer) throws AnalysisException { // If the table has not yet been set, load it from the Catalog. This allows for // callers to set a table to analyze that may not actually be created in the Catalog. // One example use case is CREATE TABLE AS SELECT which must run analysis on the // INSERT before the table has actually been created. if (table_ == null) { if (!targetTableName_.isFullyQualified()) { targetTableName_ = new TableName(analyzer.getDefaultDb(), targetTableName_.getTbl()); } table_ = analyzer.getTable(targetTableName_, Privilege.INSERT); } else { targetTableName_ = new TableName(table_.getDb().getName(), table_.getName()); PrivilegeRequestBuilder pb = new PrivilegeRequestBuilder(); analyzer.registerPrivReq( pb.onTable(table_.getDb().getName(), table_.getName()) .allOf(Privilege.INSERT) .toRequest()); } // We do not support inserting into views. if (table_ instanceof View) { throw new AnalysisException( String.format("Impala does not support inserting into views: %s", table_.getFullName())); } boolean isHBaseTable = (table_ instanceof HBaseTable); int numClusteringCols = isHBaseTable ? 0 : table_.getNumClusteringCols(); if (partitionKeyValues_ != null && numClusteringCols == 0) { if (isHBaseTable) { throw new AnalysisException( "PARTITION clause is not valid for INSERT into " + "HBase tables. '" + targetTableName_ + "' is an HBase table"); } else { // Unpartitioned table, but INSERT has PARTITION clause throw new AnalysisException( "PARTITION clause is only valid for INSERT into " + "partitioned table. '" + targetTableName_ + "' is not partitioned"); } } if (table_ instanceof HdfsTable) { HdfsTable hdfsTable = (HdfsTable) table_; if (!hdfsTable.hasWriteAccess()) { throw new AnalysisException( String.format( "Unable to INSERT into target table " + "(%s) because Impala does not have WRITE access to at least one HDFS path" + ": %s", targetTableName_, hdfsTable.getFirstLocationWithoutWriteAccess())); } for (int colIdx = 0; colIdx < numClusteringCols; ++colIdx) { Column col = hdfsTable.getColumns().get(colIdx); // Hive has a number of issues handling BOOLEAN partition columns (see HIVE-6590). // Instead of working around the Hive bugs, INSERT is disabled for BOOLEAN // partitions in Impala. Once the Hive JIRA is resolved, we can remove this // analysis check. if (col.getType() == Type.BOOLEAN) { throw new AnalysisException( String.format( "INSERT into table with BOOLEAN " + "partition column (%s) is not supported: %s", col.getName(), targetTableName_)); } } } if (isHBaseTable && overwrite_) { throw new AnalysisException("HBase doesn't have a way to perform INSERT OVERWRITE"); } // Add target table to descriptor table. analyzer.getDescTbl().addReferencedTable(table_); }