/** Create SortInfo, including sort tuple, to sort entire input row on sortExprs. */ private SortInfo createSortInfo( PlanNode input, List<Expr> sortExprs, List<Boolean> isAsc, List<Boolean> nullsFirst) { // create tuple for sort output = the entire materialized input in a single tuple TupleDescriptor sortTupleDesc = analyzer_.getDescTbl().createTupleDescriptor("sort-tuple"); ExprSubstitutionMap sortSmap = new ExprSubstitutionMap(); List<Expr> sortSlotExprs = Lists.newArrayList(); sortTupleDesc.setIsMaterialized(true); for (TupleId tid : input.getTupleIds()) { TupleDescriptor tupleDesc = analyzer_.getTupleDesc(tid); for (SlotDescriptor inputSlotDesc : tupleDesc.getSlots()) { if (!inputSlotDesc.isMaterialized()) continue; SlotDescriptor sortSlotDesc = analyzer_.copySlotDescriptor(inputSlotDesc, sortTupleDesc); // all output slots need to be materialized sortSlotDesc.setIsMaterialized(true); sortSmap.put(new SlotRef(inputSlotDesc), new SlotRef(sortSlotDesc)); sortSlotExprs.add(new SlotRef(inputSlotDesc)); } } SortInfo sortInfo = new SortInfo(Expr.substituteList(sortExprs, sortSmap, analyzer_, false), isAsc, nullsFirst); LOG.trace("sortinfo exprs: " + Expr.debugString(sortInfo.getOrderingExprs())); sortInfo.setMaterializedTupleInfo(sortTupleDesc, sortSlotExprs); return sortInfo; }
/** Returns an smap that combines the childrens' smaps. */ protected ExprSubstitutionMap getCombinedChildSmap() { if (getChildren().size() == 0) return new ExprSubstitutionMap(); if (getChildren().size() == 1) return getChild(0).getOutputSmap(); ExprSubstitutionMap result = ExprSubstitutionMap.combine(getChild(0).getOutputSmap(), getChild(1).getOutputSmap()); for (int i = 2; i < getChildren().size(); ++i) { result = ExprSubstitutionMap.combine(result, getChild(i).getOutputSmap()); } return result; }
/** * Creates the physical output and intermediate tuples as well as the logical to physical smap * for this window group. Computes the mem layout for the tuple descriptors. */ public void init(Analyzer analyzer, String tupleName) { Preconditions.checkState(physicalOutputTuple == null); Preconditions.checkState(physicalIntermediateTuple == null); Preconditions.checkState(analyticFnCalls.size() == analyticExprs.size()); // If needed, create the intermediate tuple first to maintain // intermediateTupleId < outputTupleId for debugging purposes and consistency with // tuple creation for aggregations. boolean requiresIntermediateTuple = AggregateInfoBase.requiresIntermediateTuple(analyticFnCalls); if (requiresIntermediateTuple) { physicalIntermediateTuple = analyzer.getDescTbl().createTupleDescriptor(tupleName + "intermed"); physicalOutputTuple = analyzer.getDescTbl().createTupleDescriptor(tupleName + "out"); } else { physicalOutputTuple = analyzer.getDescTbl().createTupleDescriptor(tupleName + "out"); physicalIntermediateTuple = physicalOutputTuple; } Preconditions.checkState(analyticExprs.size() == logicalIntermediateSlots.size()); Preconditions.checkState(analyticExprs.size() == logicalOutputSlots.size()); for (int i = 0; i < analyticExprs.size(); ++i) { SlotDescriptor logicalOutputSlot = logicalOutputSlots.get(i); SlotDescriptor physicalOutputSlot = analyzer.copySlotDescriptor(logicalOutputSlot, physicalOutputTuple); physicalOutputSlot.setIsMaterialized(true); if (requiresIntermediateTuple) { SlotDescriptor logicalIntermediateSlot = logicalIntermediateSlots.get(i); SlotDescriptor physicalIntermediateSlot = analyzer.copySlotDescriptor(logicalIntermediateSlot, physicalIntermediateTuple); physicalIntermediateSlot.setIsMaterialized(true); } logicalToPhysicalSmap.put(new SlotRef(logicalOutputSlot), new SlotRef(physicalOutputSlot)); } physicalOutputTuple.computeMemLayout(); if (requiresIntermediateTuple) physicalIntermediateTuple.computeMemLayout(); }
/** * Sets outputSmap_ to compose(existing smap, combined child smap). Also substitutes conjuncts_ * using the combined child smap. */ protected void createDefaultSmap(Analyzer analyzer) { ExprSubstitutionMap combinedChildSmap = getCombinedChildSmap(); outputSmap_ = ExprSubstitutionMap.compose(outputSmap_, combinedChildSmap, analyzer); conjuncts_ = Expr.substituteList(conjuncts_, outputSmap_, analyzer, false); }
/** * Create plan tree for the entire sort group, including all contained window groups. Marks the * SortNode as requiring its input to be partitioned if partitionExprs is not null (partitionExprs * represent the data partition of the entire partition group of which this sort group is a part). */ private PlanNode createSortGroupPlan( PlanNode root, SortGroup sortGroup, List<Expr> partitionExprs) throws ImpalaException { List<Expr> partitionByExprs = sortGroup.partitionByExprs; List<OrderByElement> orderByElements = sortGroup.orderByElements; ExprSubstitutionMap sortSmap = null; TupleId sortTupleId = null; TupleDescriptor bufferedTupleDesc = null; // map from input to buffered tuple ExprSubstitutionMap bufferedSmap = new ExprSubstitutionMap(); // sort on partition by (pb) + order by (ob) exprs and create pb/ob predicates if (!partitionByExprs.isEmpty() || !orderByElements.isEmpty()) { // first sort on partitionExprs (direction doesn't matter) List<Expr> sortExprs = Lists.newArrayList(partitionByExprs); List<Boolean> isAsc = Lists.newArrayList(Collections.nCopies(sortExprs.size(), new Boolean(true))); // TODO: utilize a direction and nulls/first last that has benefit // for subsequent sort groups List<Boolean> nullsFirst = Lists.newArrayList(Collections.nCopies(sortExprs.size(), new Boolean(true))); // then sort on orderByExprs for (OrderByElement orderByElement : sortGroup.orderByElements) { sortExprs.add(orderByElement.getExpr()); isAsc.add(orderByElement.isAsc()); nullsFirst.add(orderByElement.getNullsFirstParam()); } SortInfo sortInfo = createSortInfo(root, sortExprs, isAsc, nullsFirst); SortNode sortNode = new SortNode(idGenerator_.getNextId(), root, sortInfo, false, 0); // if this sort group does not have partitioning exprs, we want the sort // to be executed like a regular distributed sort if (!partitionByExprs.isEmpty()) sortNode.setIsAnalyticSort(true); if (partitionExprs != null) { // create required input partition DataPartition inputPartition = DataPartition.UNPARTITIONED; if (!partitionExprs.isEmpty()) { inputPartition = new DataPartition(TPartitionType.HASH_PARTITIONED, partitionExprs); } sortNode.setInputPartition(inputPartition); } root = sortNode; root.init(analyzer_); sortSmap = sortNode.getOutputSmap(); // create bufferedTupleDesc and bufferedSmap sortTupleId = sortNode.tupleIds_.get(0); bufferedTupleDesc = analyzer_.getDescTbl().copyTupleDescriptor(sortTupleId, "buffered-tuple"); LOG.trace("desctbl: " + analyzer_.getDescTbl().debugString()); List<SlotDescriptor> inputSlots = analyzer_.getTupleDesc(sortTupleId).getSlots(); List<SlotDescriptor> bufferedSlots = bufferedTupleDesc.getSlots(); for (int i = 0; i < inputSlots.size(); ++i) { bufferedSmap.put(new SlotRef(inputSlots.get(i)), new SlotRef(bufferedSlots.get(i))); } } // create one AnalyticEvalNode per window group for (WindowGroup windowGroup : sortGroup.windowGroups) { // Create partition-by (pb) and order-by (ob) less-than predicates between the // input tuple (the output of the preceding sort) and a buffered tuple that is // identical to the input tuple. We need a different tuple descriptor for the // buffered tuple because the generated predicates should compare two different // tuple instances from the same input stream (i.e., the predicates should be // evaluated over a row that is composed of the input and the buffered tuple). // we need to remap the pb/ob exprs to a) the sort output, b) our buffer of the // sort input Expr partitionByEq = null; if (!windowGroup.partitionByExprs.isEmpty()) { partitionByEq = createNullMatchingEquals( Expr.substituteList(windowGroup.partitionByExprs, sortSmap, analyzer_, false), sortTupleId, bufferedSmap); LOG.trace("partitionByEq: " + partitionByEq.debugString()); } Expr orderByEq = null; if (!windowGroup.orderByElements.isEmpty()) { orderByEq = createNullMatchingEquals( OrderByElement.getOrderByExprs( OrderByElement.substitute(windowGroup.orderByElements, sortSmap, analyzer_)), sortTupleId, bufferedSmap); LOG.trace("orderByEq: " + orderByEq.debugString()); } root = new AnalyticEvalNode( idGenerator_.getNextId(), root, stmtTupleIds_, windowGroup.analyticFnCalls, windowGroup.partitionByExprs, windowGroup.orderByElements, windowGroup.window, analyticInfo_.getOutputTupleDesc(), windowGroup.physicalIntermediateTuple, windowGroup.physicalOutputTuple, windowGroup.logicalToPhysicalSmap, partitionByEq, orderByEq, bufferedTupleDesc); root.init(analyzer_); } return root; }