private StreamingAggregator createAggregatorInternal() throws SchemaChangeException, ClassTransformationException, IOException { ClassGenerator<StreamingAggregator> cg = CodeGenerator.getRoot( StreamingAggTemplate.TEMPLATE_DEFINITION, context.getFunctionRegistry()); container.clear(); LogicalExpression[] keyExprs = new LogicalExpression[popConfig.getKeys().length]; LogicalExpression[] valueExprs = new LogicalExpression[popConfig.getExprs().length]; TypedFieldId[] keyOutputIds = new TypedFieldId[popConfig.getKeys().length]; ErrorCollector collector = new ErrorCollectorImpl(); for (int i = 0; i < keyExprs.length; i++) { final NamedExpression ne = popConfig.getKeys()[i]; final LogicalExpression expr = ExpressionTreeMaterializer.materialize( ne.getExpr(), incoming, collector, context.getFunctionRegistry()); if (expr == null) { continue; } keyExprs[i] = expr; final MaterializedField outputField = MaterializedField.create(ne.getRef(), expr.getMajorType()); final ValueVector vector = TypeHelper.getNewVector(outputField, oContext.getAllocator()); keyOutputIds[i] = container.add(vector); } for (int i = 0; i < valueExprs.length; i++) { final NamedExpression ne = popConfig.getExprs()[i]; final LogicalExpression expr = ExpressionTreeMaterializer.materialize( ne.getExpr(), incoming, collector, context.getFunctionRegistry()); if (expr instanceof IfExpression) { throw UserException.unsupportedError( new UnsupportedOperationException( "Union type not supported in aggregate functions")) .build(logger); } if (expr == null) { continue; } final MaterializedField outputField = MaterializedField.create(ne.getRef(), expr.getMajorType()); ValueVector vector = TypeHelper.getNewVector(outputField, oContext.getAllocator()); TypedFieldId id = container.add(vector); valueExprs[i] = new ValueVectorWriteExpression(id, expr, true); } if (collector.hasErrors()) { throw new SchemaChangeException( "Failure while materializing expression. " + collector.toErrorString()); } setupIsSame(cg, keyExprs); setupIsSameApart(cg, keyExprs); addRecordValues(cg, valueExprs); outputRecordKeys(cg, keyOutputIds, keyExprs); outputRecordKeysPrev(cg, keyOutputIds, keyExprs); cg.getBlock("resetValues")._return(JExpr.TRUE); getIndex(cg); container.buildSchema(SelectionVectorMode.NONE); StreamingAggregator agg = context.getImplementationClass(cg); agg.setup(oContext, incoming, this); return agg; }
@Override public IterOutcome innerNext() { // if a special batch has been sent, we have no data in the incoming so exit early if (specialBatchSent) { return IterOutcome.NONE; } // this is only called on the first batch. Beyond this, the aggregator manages batches. if (aggregator == null || first) { IterOutcome outcome; if (first && incoming.getRecordCount() > 0) { first = false; outcome = IterOutcome.OK_NEW_SCHEMA; } else { outcome = next(incoming); } logger.debug("Next outcome of {}", outcome); switch (outcome) { case NONE: if (first && popConfig.getKeys().length == 0) { // if we have a straight aggregate and empty input batch, we need to handle it in a // different way constructSpecialBatch(); first = false; // set state to indicate the fact that we have sent a special batch and input is empty specialBatchSent = true; return IterOutcome.OK; } case OUT_OF_MEMORY: case NOT_YET: case STOP: return outcome; case OK_NEW_SCHEMA: if (!createAggregator()) { done = true; return IterOutcome.STOP; } break; case OK: break; default: throw new IllegalStateException(String.format("unknown outcome %s", outcome)); } } AggOutcome out = aggregator.doWork(); recordCount = aggregator.getOutputCount(); logger.debug("Aggregator response {}, records {}", out, aggregator.getOutputCount()); switch (out) { case CLEANUP_AND_RETURN: if (!first) { container.zeroVectors(); } done = true; // fall through case RETURN_OUTCOME: IterOutcome outcome = aggregator.getOutcome(); if (outcome == IterOutcome.NONE && first) { first = false; done = true; return IterOutcome.OK_NEW_SCHEMA; } else if (outcome == IterOutcome.OK && first) { outcome = IterOutcome.OK_NEW_SCHEMA; } else if (outcome != IterOutcome.OUT_OF_MEMORY) { first = false; } return outcome; case UPDATE_AGGREGATOR: context.fail( UserException.unsupportedError() .message("Streaming aggregate does not support schema changes") .build(logger)); close(); killIncoming(false); return IterOutcome.STOP; default: throw new IllegalStateException(String.format("Unknown state %s.", out)); } }