[fix] Fix the query result error caused by the grouping sets statemen… (#11316)
* [fix] Fix the query result error caused by the grouping sets statement grouping as an expression
This commit is contained in:
@ -22,6 +22,7 @@ import org.apache.doris.catalog.Type;
|
||||
import org.apache.doris.common.AnalysisException;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
@ -36,47 +37,76 @@ public class GroupingInfo {
|
||||
public static final String GROUPING_PREFIX = "GROUPING_PREFIX_";
|
||||
private VirtualSlotRef groupingIDSlot;
|
||||
private TupleDescriptor virtualTuple;
|
||||
private Set<VirtualSlotRef> groupingSlots;
|
||||
private TupleDescriptor outputTupleDesc;
|
||||
private ExprSubstitutionMap outputTupleSmap;
|
||||
private List<SlotDescriptor> groupingSlotDescList;
|
||||
private Set<VirtualSlotRef> virtualSlotRefs;
|
||||
private List<BitSet> groupingIdList;
|
||||
private GroupByClause.GroupingType groupingType;
|
||||
private BitSet bitSetAll;
|
||||
|
||||
private List<Expr> preRepeatExprs;
|
||||
|
||||
public GroupingInfo(Analyzer analyzer, GroupByClause groupByClause) throws AnalysisException {
|
||||
this.groupingType = groupByClause.getGroupingType();
|
||||
groupingSlots = new LinkedHashSet<>();
|
||||
virtualSlotRefs = new LinkedHashSet<>();
|
||||
virtualTuple = analyzer.getDescTbl().createTupleDescriptor("VIRTUAL_TUPLE");
|
||||
groupingIDSlot = new VirtualSlotRef(COL_GROUPING_ID, Type.BIGINT, virtualTuple, new ArrayList<>());
|
||||
groupingIDSlot.analyze(analyzer);
|
||||
groupingSlots.add(groupingIDSlot);
|
||||
virtualSlotRefs.add(groupingIDSlot);
|
||||
|
||||
outputTupleDesc = analyzer.getDescTbl().createTupleDescriptor("repeat-tuple");
|
||||
outputTupleSmap = new ExprSubstitutionMap();
|
||||
groupingSlotDescList = Lists.newArrayList();
|
||||
preRepeatExprs = Lists.newArrayList();
|
||||
}
|
||||
|
||||
public Set<VirtualSlotRef> getGroupingSlots() {
|
||||
return groupingSlots;
|
||||
public Set<VirtualSlotRef> getVirtualSlotRefs() {
|
||||
return virtualSlotRefs;
|
||||
}
|
||||
|
||||
public TupleDescriptor getVirtualTuple() {
|
||||
return virtualTuple;
|
||||
}
|
||||
|
||||
public TupleDescriptor getOutputTupleDesc() {
|
||||
return outputTupleDesc;
|
||||
}
|
||||
|
||||
public ExprSubstitutionMap getOutputTupleSmap() {
|
||||
return outputTupleSmap;
|
||||
}
|
||||
|
||||
public List<SlotDescriptor> getGroupingSlotDescList() {
|
||||
return groupingSlotDescList;
|
||||
}
|
||||
|
||||
public List<BitSet> getGroupingIdList() {
|
||||
return groupingIdList;
|
||||
}
|
||||
|
||||
public List<Expr> getPreRepeatExprs() {
|
||||
return preRepeatExprs;
|
||||
}
|
||||
|
||||
public void substitutePreRepeatExprs(ExprSubstitutionMap smap, Analyzer analyzer) {
|
||||
preRepeatExprs = Expr.substituteList(preRepeatExprs, smap, analyzer, true);
|
||||
}
|
||||
|
||||
// generate virtual slots for grouping or grouping_id functions
|
||||
public VirtualSlotRef addGroupingSlots(List<Expr> realSlots, Analyzer analyzer) throws AnalysisException {
|
||||
String colName = realSlots.stream().map(expr -> expr.toSql()).collect(Collectors.joining(
|
||||
"_"));
|
||||
String colName = realSlots.stream().map(expr -> expr.toSql()).collect(Collectors.joining("_"));
|
||||
colName = GROUPING_PREFIX + colName;
|
||||
VirtualSlotRef virtualSlot = new VirtualSlotRef(colName, Type.BIGINT, virtualTuple, realSlots);
|
||||
virtualSlot.analyze(analyzer);
|
||||
if (groupingSlots.contains(virtualSlot)) {
|
||||
for (VirtualSlotRef vs : groupingSlots) {
|
||||
if (virtualSlotRefs.contains(virtualSlot)) {
|
||||
for (VirtualSlotRef vs : virtualSlotRefs) {
|
||||
if (vs.equals(virtualSlot)) {
|
||||
return vs;
|
||||
}
|
||||
}
|
||||
}
|
||||
groupingSlots.add(virtualSlot);
|
||||
virtualSlotRefs.add(virtualSlot);
|
||||
return virtualSlot;
|
||||
}
|
||||
|
||||
@ -124,13 +154,13 @@ public class GroupingInfo {
|
||||
default:
|
||||
Preconditions.checkState(false);
|
||||
}
|
||||
groupingExprs.addAll(groupingSlots);
|
||||
groupingExprs.addAll(virtualSlotRefs);
|
||||
}
|
||||
|
||||
// generate grouping function's value
|
||||
public List<List<Long>> genGroupingList(ArrayList<Expr> groupingExprs) throws AnalysisException {
|
||||
List<List<Long>> groupingList = new ArrayList<>();
|
||||
for (SlotRef slot : groupingSlots) {
|
||||
for (SlotRef slot : virtualSlotRefs) {
|
||||
List<Long> glist = new ArrayList<>();
|
||||
for (BitSet bitSet : groupingIdList) {
|
||||
long l = 0L;
|
||||
@ -150,7 +180,7 @@ public class GroupingInfo {
|
||||
int slotSize = ((VirtualSlotRef) slot).getRealSlots().size();
|
||||
for (int i = 0; i < slotSize; ++i) {
|
||||
int j = groupingExprs.indexOf(((VirtualSlotRef) slot).getRealSlots().get(i));
|
||||
if (j < 0 || j >= bitSet.size()) {
|
||||
if (j < 0 || j >= bitSet.size()) {
|
||||
throw new AnalysisException("Column " + ((VirtualSlotRef) slot).getRealColumnName()
|
||||
+ " in GROUP_ID() does not exist in GROUP BY clause.");
|
||||
}
|
||||
@ -164,6 +194,68 @@ public class GroupingInfo {
|
||||
return groupingList;
|
||||
}
|
||||
|
||||
public void genOutputTupleDescAndSMap(Analyzer analyzer, ArrayList<Expr> groupingAndVirtualSlotExprs,
|
||||
List<FunctionCallExpr> aggExprs) {
|
||||
List<Expr> groupingExprs = Lists.newArrayList();
|
||||
List<Expr> virtualSlotExprs = Lists.newArrayList();
|
||||
for (Expr expr : groupingAndVirtualSlotExprs) {
|
||||
if (expr instanceof VirtualSlotRef) {
|
||||
virtualSlotExprs.add(expr);
|
||||
} else {
|
||||
groupingExprs.add(expr);
|
||||
}
|
||||
}
|
||||
for (Expr expr : groupingExprs) {
|
||||
SlotDescriptor slotDesc = addSlot(analyzer, expr);
|
||||
slotDesc.setIsNullable(true);
|
||||
groupingSlotDescList.add(slotDesc);
|
||||
preRepeatExprs.add(expr);
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
List<SlotRef> aggSlot = Lists.newArrayList();
|
||||
aggExprs.forEach(expr -> aggSlot.addAll(getSlotRefChildren(expr)));
|
||||
for (SlotRef slotRef : aggSlot) {
|
||||
addSlot(analyzer, slotRef);
|
||||
preRepeatExprs.add(slotRef);
|
||||
}
|
||||
for (Expr expr : virtualSlotExprs) {
|
||||
addSlot(analyzer, expr);
|
||||
}
|
||||
}
|
||||
|
||||
private SlotDescriptor addSlot(Analyzer analyzer, Expr expr) {
|
||||
SlotDescriptor slotDesc = analyzer.addSlotDescriptor(outputTupleDesc);
|
||||
slotDesc.initFromExpr(expr);
|
||||
slotDesc.setIsMaterialized(true);
|
||||
if (expr instanceof SlotRef) {
|
||||
slotDesc.setColumn(((SlotRef) expr).getColumn());
|
||||
}
|
||||
if (expr instanceof VirtualSlotRef) {
|
||||
outputTupleSmap.put(expr.clone(), new VirtualSlotRef(slotDesc));
|
||||
} else {
|
||||
outputTupleSmap.put(expr.clone(), new SlotRef(slotDesc));
|
||||
}
|
||||
return slotDesc;
|
||||
}
|
||||
|
||||
private List<SlotRef> getSlotRefChildren(Expr root) {
|
||||
List<SlotRef> result = new ArrayList<>();
|
||||
for (Expr child : root.getChildren()) {
|
||||
if (child instanceof SlotRef) {
|
||||
result.add((SlotRef) child);
|
||||
} else {
|
||||
result.addAll(getSlotRefChildren(child));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void substituteGroupingFn(List<Expr> exprs, Analyzer analyzer) throws AnalysisException {
|
||||
if (groupingType == GroupByClause.GroupingType.GROUP_BY) {
|
||||
throw new AnalysisException("cannot use GROUPING functions without [grouping sets|rollup|cube] a"
|
||||
|
||||
@ -1047,15 +1047,16 @@ public class SelectStmt extends QueryStmt {
|
||||
|
||||
List<TupleId> groupingByTupleIds = new ArrayList<>();
|
||||
if (groupByClause != null) {
|
||||
// must do it before copying for createAggInfo()
|
||||
if (groupingInfo != null) {
|
||||
groupingByTupleIds.add(groupingInfo.getVirtualTuple().getId());
|
||||
}
|
||||
groupByClause.genGroupingExprs();
|
||||
if (groupingInfo != null) {
|
||||
groupingInfo.buildRepeat(groupByClause.getGroupingExprs(), groupByClause.getGroupingSetList());
|
||||
}
|
||||
substituteOrdinalsAliases(groupByClause.getGroupingExprs(), "GROUP BY", analyzer);
|
||||
if (groupingInfo != null) {
|
||||
groupingInfo.genOutputTupleDescAndSMap(analyzer, groupByClause.getGroupingExprs(), aggExprs);
|
||||
// must do it before copying for createAggInfo()
|
||||
groupingByTupleIds.add(groupingInfo.getOutputTupleDesc().getId());
|
||||
}
|
||||
groupByClause.analyze(analyzer);
|
||||
createAggInfo(groupByClause.getGroupingExprs(), aggExprs, analyzer);
|
||||
} else {
|
||||
|
||||
@ -29,6 +29,8 @@ import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* It like a SlotRef except that it is not a real column exist in table.
|
||||
@ -55,6 +57,10 @@ public class VirtualSlotRef extends SlotRef {
|
||||
tupleDescriptor = other.tupleDescriptor;
|
||||
}
|
||||
|
||||
public VirtualSlotRef(SlotDescriptor desc) {
|
||||
super(desc);
|
||||
}
|
||||
|
||||
public static VirtualSlotRef read(DataInput in) throws IOException {
|
||||
VirtualSlotRef virtualSlotRef = new VirtualSlotRef(null, Type.BIGINT, null, new ArrayList<>());
|
||||
virtualSlotRef.readFields(in);
|
||||
@ -68,6 +74,10 @@ public class VirtualSlotRef extends SlotRef {
|
||||
return getColumnName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getTableIdToColumnNames(Map<Long, Set<String>> tableIdToColumnNames) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out) throws IOException {
|
||||
super.write(out);
|
||||
|
||||
@ -19,9 +19,8 @@ package org.apache.doris.planner;
|
||||
|
||||
import org.apache.doris.analysis.Analyzer;
|
||||
import org.apache.doris.analysis.Expr;
|
||||
import org.apache.doris.analysis.FunctionCallExpr;
|
||||
import org.apache.doris.analysis.ExprSubstitutionMap;
|
||||
import org.apache.doris.analysis.GroupByClause;
|
||||
import org.apache.doris.analysis.GroupingFunctionCallExpr;
|
||||
import org.apache.doris.analysis.GroupingInfo;
|
||||
import org.apache.doris.analysis.SlotDescriptor;
|
||||
import org.apache.doris.analysis.SlotId;
|
||||
@ -68,17 +67,16 @@ public class RepeatNode extends PlanNode {
|
||||
private GroupByClause groupByClause;
|
||||
|
||||
protected RepeatNode(PlanNodeId id, PlanNode input, GroupingInfo groupingInfo, GroupByClause groupByClause) {
|
||||
super(id, input.getTupleIds(), "REPEAT_NODE", StatisticalType.REPEAT_NODE);
|
||||
super(id, groupingInfo.getOutputTupleDesc().getId().asList(), "REPEAT_NODE", StatisticalType.REPEAT_NODE);
|
||||
this.children.add(input);
|
||||
this.groupingInfo = groupingInfo;
|
||||
this.input = input;
|
||||
this.groupByClause = groupByClause;
|
||||
|
||||
}
|
||||
|
||||
// only for unittest
|
||||
protected RepeatNode(PlanNodeId id, PlanNode input, List<Set<SlotId>> repeatSlotIdList,
|
||||
TupleDescriptor outputTupleDesc, List<List<Long>> groupingList) {
|
||||
TupleDescriptor outputTupleDesc, List<List<Long>> groupingList) {
|
||||
super(id, input.getTupleIds(), "REPEAT_NODE", StatisticalType.REPEAT_NODE);
|
||||
this.children.add(input);
|
||||
this.repeatSlotIdList = buildIdSetList(repeatSlotIdList);
|
||||
@ -116,25 +114,18 @@ public class RepeatNode extends PlanNode {
|
||||
@Override
|
||||
public void init(Analyzer analyzer) throws UserException {
|
||||
Preconditions.checkState(conjuncts.isEmpty());
|
||||
groupByClause.substituteGroupingExprs(groupingInfo.getGroupingSlots(), input.getOutputSmap(),
|
||||
analyzer);
|
||||
|
||||
for (Expr expr : groupByClause.getGroupingExprs()) {
|
||||
if (expr instanceof SlotRef || (expr instanceof GroupingFunctionCallExpr)) {
|
||||
continue;
|
||||
}
|
||||
// throw new AnalysisException("function or expr is not allowed in grouping sets clause.");
|
||||
ExprSubstitutionMap childSmap = getCombinedChildSmap();
|
||||
groupByClause.substituteGroupingExprs(groupingInfo.getVirtualSlotRefs(), childSmap, analyzer);
|
||||
groupingInfo.substitutePreRepeatExprs(childSmap, analyzer);
|
||||
outputSmap = groupingInfo.getOutputTupleSmap();
|
||||
conjuncts = Expr.substituteList(conjuncts, outputSmap, analyzer, false);
|
||||
outputTupleDesc = groupingInfo.getOutputTupleDesc();
|
||||
List<TupleId> inputTupleIds = input.getOutputTupleIds();
|
||||
if (inputTupleIds.size() == 1) {
|
||||
// used for MaterializedViewSelector getTableIdToColumnNames
|
||||
outputTupleDesc.setTable(analyzer.getTupleDesc(inputTupleIds.get(0)).getTable());
|
||||
}
|
||||
|
||||
// build new BitSet List for tupleDesc
|
||||
Set<SlotDescriptor> slotDescSet = new HashSet<>();
|
||||
for (TupleId tupleId : input.getTupleIds()) {
|
||||
TupleDescriptor tupleDescriptor = analyzer.getDescTbl().getTupleDesc(tupleId);
|
||||
slotDescSet.addAll(tupleDescriptor.getSlots());
|
||||
}
|
||||
|
||||
// build tupleDesc according to child's tupleDesc info
|
||||
outputTupleDesc = groupingInfo.getVirtualTuple();
|
||||
//set aggregate nullable
|
||||
for (Expr slot : groupByClause.getGroupingExprs()) {
|
||||
if (slot instanceof SlotRef && !(slot instanceof VirtualSlotRef)) {
|
||||
@ -144,74 +135,42 @@ public class RepeatNode extends PlanNode {
|
||||
outputTupleDesc.computeStatAndMemLayout();
|
||||
|
||||
List<Set<SlotId>> groupingIdList = new ArrayList<>();
|
||||
List<Expr> exprList = groupByClause.getGroupingExprs();
|
||||
Preconditions.checkState(exprList.size() >= 2);
|
||||
allSlotId = new HashSet<>();
|
||||
List<SlotDescriptor> groupingSlotDescList = groupingInfo.getGroupingSlotDescList();
|
||||
for (BitSet bitSet : Collections.unmodifiableList(groupingInfo.getGroupingIdList())) {
|
||||
Set<SlotId> slotIdSet = new HashSet<>();
|
||||
for (SlotDescriptor slotDesc : slotDescSet) {
|
||||
SlotId slotId = slotDesc.getId();
|
||||
if (slotId == null) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < exprList.size(); i++) {
|
||||
if (exprList.get(i) instanceof SlotRef) {
|
||||
SlotRef slotRef = (SlotRef) (exprList.get(i));
|
||||
if (bitSet.get(i) && slotRef.getSlotId() == slotId) {
|
||||
slotIdSet.add(slotId);
|
||||
break;
|
||||
}
|
||||
} else if (exprList.get(i) instanceof FunctionCallExpr) {
|
||||
List<SlotRef> slotRefs = getSlotRefChildren(exprList.get(i));
|
||||
for (SlotRef slotRef : slotRefs) {
|
||||
if (bitSet.get(i) && slotRef.getSlotId() == slotId) {
|
||||
slotIdSet.add(slotId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < groupingSlotDescList.size(); i++) {
|
||||
if (bitSet.get(i)) {
|
||||
slotIdSet.add(groupingSlotDescList.get(i).getId());
|
||||
}
|
||||
}
|
||||
groupingIdList.add(slotIdSet);
|
||||
}
|
||||
|
||||
this.repeatSlotIdList = buildIdSetList(groupingIdList);
|
||||
allSlotId = new HashSet<>();
|
||||
for (Set<Integer> s : this.repeatSlotIdList) {
|
||||
allSlotId.addAll(s);
|
||||
}
|
||||
this.groupingList = groupingInfo.genGroupingList(groupByClause.getGroupingExprs());
|
||||
tupleIds.add(outputTupleDesc.getId());
|
||||
for (TupleId id : tupleIds) {
|
||||
analyzer.getTupleDesc(id).setIsMaterialized(true);
|
||||
}
|
||||
computeTupleStatAndMemLayout(analyzer);
|
||||
computeStats(analyzer);
|
||||
createDefaultSmap(analyzer);
|
||||
}
|
||||
|
||||
private List<SlotRef> getSlotRefChildren(Expr root) {
|
||||
List<SlotRef> result = new ArrayList<>();
|
||||
for (Expr child : root.getChildren()) {
|
||||
if (child instanceof SlotRef) {
|
||||
result.add((SlotRef) child);
|
||||
} else {
|
||||
result.addAll(getSlotRefChildren(child));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toThrift(TPlanNode msg) {
|
||||
msg.node_type = TPlanNodeType.REPEAT_NODE;
|
||||
msg.repeat_node = new TRepeatNode(outputTupleDesc.getId().asInt(), repeatSlotIdList, groupingList.get(0),
|
||||
groupingList, allSlotId);
|
||||
msg.repeat_node =
|
||||
new TRepeatNode(outputTupleDesc.getId().asInt(), repeatSlotIdList, groupingList.get(0), groupingList,
|
||||
allSlotId, Expr.treesToThrift(groupingInfo.getPreRepeatExprs()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String debugString() {
|
||||
return MoreObjects.toStringHelper(this).add("Repeat", repeatSlotIdList.size()).addValue(
|
||||
super.debugString()).toString();
|
||||
return MoreObjects.toStringHelper(this).add("Repeat", repeatSlotIdList.size()).addValue(super.debugString())
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -223,11 +182,12 @@ public class RepeatNode extends PlanNode {
|
||||
output.append(detailPrefix + "repeat: repeat ");
|
||||
output.append(repeatSlotIdList.size() - 1);
|
||||
output.append(" lines ");
|
||||
output.append(repeatSlotIdList);
|
||||
output.append(repeatSlotIdList).append("\n");
|
||||
output.append(detailPrefix).append("exprs: ").append(getExplainString(groupingInfo.getPreRepeatExprs()));
|
||||
output.append("\n");
|
||||
if (CollectionUtils.isNotEmpty(outputTupleDesc.getSlots())) {
|
||||
output.append(detailPrefix + "generate: ");
|
||||
output.append(outputTupleDesc.getSlots().stream().map(slot -> "`" + slot.getColumn().getName() + "`")
|
||||
output.append(detailPrefix + "output slots: ");
|
||||
output.append(outputTupleDesc.getSlots().stream().map(slot -> "`" + slot.getLabel() + "`")
|
||||
.collect(Collectors.joining(", ")) + "\n");
|
||||
}
|
||||
return output.toString();
|
||||
|
||||
@ -421,7 +421,7 @@ public class QueryPlanTest extends TestWithFeService {
|
||||
public void testFunctionViewGroupingSet() throws Exception {
|
||||
String queryStr = "select query_id, client_ip, concat from test.function_view group by rollup("
|
||||
+ "query_id, client_ip, concat);";
|
||||
assertSQLPlanOrErrorMsgContains(queryStr, "repeat: repeat 3 lines [[], [0], [0, 1], [0, 1, 2, 3]]");
|
||||
assertSQLPlanOrErrorMsgContains(queryStr, "repeat: repeat 3 lines [[], [8], [8, 9], [8, 9, 10]]");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -17,79 +17,64 @@
|
||||
|
||||
package org.apache.doris.planner;
|
||||
|
||||
import org.apache.doris.analysis.AccessTestUtil;
|
||||
import org.apache.doris.analysis.Analyzer;
|
||||
import org.apache.doris.analysis.DescriptorTable;
|
||||
import org.apache.doris.analysis.SlotId;
|
||||
import org.apache.doris.analysis.SlotRef;
|
||||
import org.apache.doris.analysis.TableName;
|
||||
import org.apache.doris.analysis.TupleDescriptor;
|
||||
import org.apache.doris.analysis.TupleId;
|
||||
import org.apache.doris.datasource.InternalDataSource;
|
||||
import org.apache.doris.thrift.TExplainLevel;
|
||||
import org.apache.doris.thrift.TPlanNode;
|
||||
import org.apache.doris.thrift.TPlanNodeType;
|
||||
import org.apache.doris.utframe.TestWithFeService;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
public class RepeatNodeTest extends TestWithFeService {
|
||||
|
||||
public class RepeatNodeTest {
|
||||
private Analyzer analyzer;
|
||||
private RepeatNode node;
|
||||
private TupleDescriptor virtualTuple;
|
||||
private List<Set<SlotId>> groupingIdList = new ArrayList<>();
|
||||
private List<List<Long>> groupingList = new ArrayList<>();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Analyzer analyzerBase = AccessTestUtil.fetchTableAnalyzer();
|
||||
analyzer = new Analyzer(analyzerBase.getEnv(), analyzerBase.getContext());
|
||||
String[] cols = {"k1", "k2", "k3"};
|
||||
List<SlotRef> slots = new ArrayList<>();
|
||||
for (String col : cols) {
|
||||
SlotRef expr = new SlotRef(new TableName(InternalDataSource.INTERNAL_DS_NAME, "testdb", "t"), col);
|
||||
slots.add(expr);
|
||||
}
|
||||
try {
|
||||
Field f = analyzer.getClass().getDeclaredField("tupleByAlias");
|
||||
f.setAccessible(true);
|
||||
Multimap<String, TupleDescriptor> tupleByAlias = ArrayListMultimap.create();
|
||||
TupleDescriptor td = new TupleDescriptor(new TupleId(0));
|
||||
td.setTable(analyzerBase.getTableOrAnalysisException(new TableName(InternalDataSource.INTERNAL_DS_NAME, "testdb", "t")));
|
||||
tupleByAlias.put("testdb.t", td);
|
||||
f.set(analyzer, tupleByAlias);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
virtualTuple = analyzer.getDescTbl().createTupleDescriptor("VIRTUAL_TUPLE");
|
||||
groupingList.add(Arrays.asList(0L, 7L, 3L, 5L, 1L, 6L, 2L, 4L));
|
||||
groupingList.add(Arrays.asList(0L, 7L, 3L, 5L, 1L, 6L, 2L, 4L));
|
||||
DescriptorTable descTable = new DescriptorTable();
|
||||
TupleDescriptor tuple = descTable.createTupleDescriptor("DstTable");
|
||||
node = new RepeatNode(new PlanNodeId(1),
|
||||
new OlapScanNode(new PlanNodeId(0), tuple, "null"), groupingIdList, virtualTuple, groupingList);
|
||||
@Override
|
||||
protected void runBeforeAll() throws Exception {
|
||||
createDatabase("testdb");
|
||||
useDatabase("testdb");
|
||||
createTable(" CREATE TABLE `testdb`.`mycost` (\n" + " `id` tinyint(4) NULL,\n" + " `name` varchar(20) NULL,\n"
|
||||
+ " `date` date NULL,\n" + " `cost` bigint(20) SUM NULL\n" + ") ENGINE=OLAP\n"
|
||||
+ "AGGREGATE KEY(`id`, `name`, `date`)\n" + "COMMENT 'OLAP'\n" + "PARTITION BY RANGE(`date`)\n"
|
||||
+ "(PARTITION p2020 VALUES [('0000-01-01'), ('2021-01-01')),\n"
|
||||
+ "PARTITION p2021 VALUES [('2021-01-01'), ('2022-01-01')),\n"
|
||||
+ "PARTITION p2022 VALUES [('2022-01-01'), ('2023-01-01')))\n" + "DISTRIBUTED BY HASH(`id`) BUCKETS 8\n"
|
||||
+ "PROPERTIES (\n" + "\"replication_allocation\" = \"tag.location.default: 1\",\n"
|
||||
+ "\"in_memory\" = \"false\",\n" + "\"storage_format\" = \"V2\"\n" + ");");
|
||||
|
||||
createTable(
|
||||
" CREATE TABLE `testdb`.`mypeople` (\n" + " `id` bigint(20) NULL,\n" + " `name` varchar(20) NULL,\n"
|
||||
+ " `sex` varchar(10) NULL,\n" + " `age` int(11) NULL,\n" + " `phone` char(15) NULL,\n"
|
||||
+ " `address` varchar(50) NULL\n" + ") ENGINE=OLAP\n" + "DUPLICATE KEY(`id`, `name`)\n"
|
||||
+ "COMMENT 'OLAP'\n" + "DISTRIBUTED BY HASH(`id`) BUCKETS 8\n" + "PROPERTIES (\n"
|
||||
+ "\"replication_allocation\" = \"tag.location.default: 1\",\n" + "\"in_memory\" = \"false\",\n"
|
||||
+ "\"storage_format\" = \"V2\"\n" + ");");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormal() {
|
||||
try {
|
||||
TPlanNode msg = new TPlanNode();
|
||||
node.toThrift(msg);
|
||||
node.getNodeExplainString("", TExplainLevel.NORMAL);
|
||||
node.debugString();
|
||||
Assert.assertEquals(TPlanNodeType.REPEAT_NODE, msg.node_type);
|
||||
} catch (Exception e) {
|
||||
Assert.fail("throw exceptions");
|
||||
}
|
||||
public void testNormal() throws Exception {
|
||||
String sql = "select id, name, sum(cost), grouping_id(id, name) from mycost group by cube(id, name);";
|
||||
String explainString = getSQLPlanOrErrorMsg("explain " + sql);
|
||||
Assertions.assertTrue(explainString.contains("exprs: `id`, `name`, `cost`"));
|
||||
Assertions.assertTrue(explainString.contains(
|
||||
"output slots: ``id``, ``name``, ``cost``, ``GROUPING_ID``, ``GROUPING_PREFIX_`id`_`name```"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpr() throws Exception {
|
||||
String sql1 = "select if(c.id > 0, 1, 0) as id_, p.name, sum(c.cost) from mycost c "
|
||||
+ "join mypeople p on c.id = p.id group by grouping sets((id_, name),());";
|
||||
String explainString1 = getSQLPlanOrErrorMsg("explain " + sql1);
|
||||
System.out.println(explainString1);
|
||||
Assertions.assertTrue(explainString1.contains(
|
||||
"output slots: `if(`c`.`id` > 0, 1, 0)`, ``p`.`name``, ``c`.`cost``, ``GROUPING_ID``"));
|
||||
|
||||
String sql2 = "select (id + 1) id_, name, sum(cost) from mycost group by grouping sets((id_, name),());";
|
||||
String explainString2 = getSQLPlanOrErrorMsg("explain " + sql2);
|
||||
System.out.println(explainString2);
|
||||
Assertions.assertTrue(explainString2.contains("exprs: (`id` + 1), `name`, `cost`"));
|
||||
Assertions.assertTrue(
|
||||
explainString2.contains(" output slots: `(`id` + 1)`, ``name``, ``cost``, ``GROUPING_ID``"));
|
||||
|
||||
String sql3 = "select 1 as id_, name, sum(cost) from mycost group by grouping sets((id_, name),());";
|
||||
String explainString3 = getSQLPlanOrErrorMsg("explain " + sql3);
|
||||
System.out.println(explainString3);
|
||||
Assertions.assertTrue(explainString3.contains("exprs: 1, `name`, `cost`"));
|
||||
Assertions.assertTrue(explainString3.contains("output slots: `1`, ``name``, ``cost``, ``GROUPING_ID``"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user