[SQL] Support Grouping Sets, Rollup and Cube to extend group by statement

Support Grouping Sets, Rollup and Cube to extend group by statement
support GROUPING SETS syntax 
```
SELECT a, b, SUM( c ) FROM tab1 GROUP BY GROUPING SETS ( (a, b), (a), (b), ( ) );
```
cube  or rollup like 
```
SELECT a, b,c, SUM( d ) FROM tab1 GROUP BY ROLLUP|CUBE(a,b,c)
```

[ADD] support grouping functions in expr like grouping(a) + grouping(b) (#2039)
[FIX] fix analyzer error in window function(#2039)
This commit is contained in:
yangzhg
2020-01-17 16:24:02 +08:00
committed by Mingyu Chen
parent 3b24287251
commit fc55423032
52 changed files with 3971 additions and 402 deletions

View File

@ -383,4 +383,138 @@ public class AccessTestUtil {
};
return analyzer;
}
public static Analyzer fetchTableAnalyzer() {
Column column1 = new Column("k1", PrimitiveType.VARCHAR);
Column column2 = new Column("k2", PrimitiveType.VARCHAR);
Column column3 = new Column("k3", PrimitiveType.VARCHAR);
Column column4 = new Column("k4", PrimitiveType.BIGINT);
MaterializedIndex index = new MaterializedIndex();
new Expectations(index) {
{
index.getId();
minTimes = 0;
result = 30000L;
}
};
Partition partition = Deencapsulation.newInstance(Partition.class);
new Expectations(partition) {
{
partition.getBaseIndex();
minTimes = 0;
result = index;
partition.getIndex(30000L);
minTimes = 0;
result = index;
}
};
OlapTable table = new OlapTable();
new Expectations(table) {
{
table.getBaseSchema();
minTimes = 0;
result = Lists.newArrayList(column1, column2, column3, column4);
table.getPartition(40000L);
minTimes = 0;
result = partition;
table.getColumn("k1");
minTimes = 0;
result = column1;
table.getColumn("k2");
minTimes = 0;
result = column2;
table.getColumn("k3");
minTimes = 0;
result = column3;
table.getColumn("k4");
minTimes = 0;
result = column4;
}
};
Database db = new Database();
new Expectations(db) {
{
db.getTable("t");
minTimes = 0;
result = table;
db.getTable("emptyTable");
minTimes = 0;
result = null;
db.getTableNamesWithLock();
minTimes = 0;
result = Sets.newHashSet("t");
db.getTables();
minTimes = 0;
result = Lists.newArrayList(table);
db.readLock();
minTimes = 0;
db.readUnlock();
minTimes = 0;
db.getFullName();
minTimes = 0;
result = "testDb";
}
};
Catalog catalog = fetchBlockCatalog();
Analyzer analyzer = new Analyzer(catalog, new ConnectContext(null));
new Expectations(analyzer) {
{
analyzer.getDefaultDb();
minTimes = 0;
result = "testDb";
analyzer.getTable((TableName) any);
minTimes = 0;
result = table;
analyzer.getQualifiedUser();
minTimes = 0;
result = "testUser";
analyzer.getCatalog();
minTimes = 0;
result = catalog;
analyzer.getClusterName();
minTimes = 0;
result = "testCluster";
analyzer.incrementCallDepth();
minTimes = 0;
result = 1;
analyzer.decrementCallDepth();
minTimes = 0;
result = 0;
analyzer.getCallDepth();
minTimes = 0;
result = 1;
analyzer.getContext();
minTimes = 0;
result = new ConnectContext(null);
}
};
return analyzer;
}
}

View File

@ -0,0 +1,285 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.doris.analysis;
import org.apache.doris.common.AnalysisException;
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 java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class GroupByClauseTest {
private Analyzer analyzer;
@Before
public void setUp() {
Analyzer analyzerBase = AccessTestUtil.fetchTableAnalyzer();
analyzer = new Analyzer(analyzerBase.getCatalog(), analyzerBase.getContext());
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.getTable(new TableName("testdb", "t")));
tupleByAlias.put("testdb.t", td);
f.set(analyzer, tupleByAlias);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
@Test
public void testGroupingSets() {
List<ArrayList<Expr>> groupingExprsList = new ArrayList<>();
ArrayList<Expr> groupByExprs = new ArrayList<>();
String[][] colsLists = {
{"k3", "k1"},
{"k2", "k3", "k2"},
{"k1", "k3"},
{"k4"},
{"k1", "k2", "k3", "k4"}
};
for (String[] colsList : colsLists) {
ArrayList<Expr> exprList = new ArrayList<>();
for (String col : colsList) {
exprList.add(new SlotRef(new TableName("testdb", "t"), col));
}
groupingExprsList.add(exprList);
}
String[] groupByCols = {"k1", "k2", "k3", "k4"};
for (String col : groupByCols) {
groupByExprs.add(new SlotRef(new TableName("testdb", "t"), col));
}
GroupByClause groupByClause = new GroupByClause(groupingExprsList,
GroupByClause.GroupingType.GROUPING_SETS);
GroupingInfo groupingInfo = null;
try {
groupingInfo = new GroupingInfo(analyzer, GroupByClause.GroupingType.GROUPING_SETS);
groupByClause.genGroupingExprs();
groupingInfo.buildRepeat(groupByClause.getGroupingExprs(), groupByClause.getGroupingSetList());
groupByClause.analyze(analyzer);
} catch (AnalysisException execption) {
execption.printStackTrace();
Assert.assertTrue(false);
}
Assert.assertEquals(5, groupByClause.getGroupingExprs().size());
Assert.assertEquals("GROUPING SETS ((`testdb`.`t`.`k3`, `testdb`.`t`.`k1`), (`testdb`.`t`.`k2`, `testdb`.`t`"
+ ".`k3`, `testdb`.`t`.`k2`), (`testdb`.`t`.`k1`, `testdb`.`t`.`k3`), (`testdb`.`t`.`k4`), (`testdb`"
+ ".`t`.`k1`, `testdb`.`t`.`k2`, `testdb`.`t`.`k3`, `testdb`.`t`.`k4`))", groupByClause.toSql());
List<BitSet> bitSetList = groupingInfo.getGroupingIdList();
bitSetList.remove(0);
{
String[] answer = {"{0, 1}", "{0, 2}", "{3}"};
Set<String> answerSet = new HashSet<String>(Arrays.asList(answer));
Set<String> resultSet = new HashSet<>();
for (BitSet aBitSetList : bitSetList) {
String s = aBitSetList.toString();
resultSet.add(s);
}
Assert.assertEquals(answerSet, resultSet);
}
}
@Test
public void testRollUp() {
ArrayList<Expr> groupingExprs = new ArrayList<>();
String[] cols = {"k2", "k3", "k4", "k3"};
for (String col : cols) {
Expr expr = new SlotRef(new TableName("testdb", "t"), col);
groupingExprs.add(expr);
}
GroupByClause groupByClause =
new GroupByClause(
Expr.cloneList(groupingExprs),
GroupByClause.GroupingType.ROLLUP);
GroupingInfo groupingInfo = null;
try {
groupingInfo = new GroupingInfo(analyzer, GroupByClause.GroupingType.ROLLUP);
groupByClause.genGroupingExprs();
groupingInfo.buildRepeat(groupByClause.getGroupingExprs(), groupByClause.getGroupingSetList());
groupByClause.analyze(analyzer);
} catch (AnalysisException execption) {
Assert.assertTrue(false);
}
Assert.assertEquals(4, groupByClause.getGroupingExprs().size());
Assert.assertEquals("ROLLUP (`testdb`.`t`.`k2`, `testdb`.`t`.`k3`, "
+ "`testdb`.`t`.`k4`, `testdb`.`t`.`k3`)", groupByClause.toSql());
List<BitSet> bitSetList = groupingInfo.getGroupingIdList();
bitSetList.remove(0);
{
String[] answer = {"{}", "{0}", "{0, 1}"};
Set<String> answerSet = new HashSet<String>(Arrays.asList(answer));
Set<String> resultSet = new HashSet<>();
for (BitSet aBitSetList : bitSetList) {
String s = aBitSetList.toString();
resultSet.add(s);
}
Assert.assertEquals(answerSet, resultSet);
}
}
@Test
public void testCube() {
ArrayList<Expr> groupingExprs = new ArrayList<>();
String[] cols = {"k1", "k2", "k3", "k1"};
for (String col : cols) {
Expr expr = new SlotRef(new TableName("testdb", "t"), col);
groupingExprs.add(expr);
}
GroupByClause groupByClause = new GroupByClause(Expr.cloneList(groupingExprs),
GroupByClause.GroupingType.CUBE);
GroupingInfo groupingInfo = null;
try {
groupingInfo = new GroupingInfo(analyzer, GroupByClause.GroupingType.CUBE);
groupByClause.genGroupingExprs();
groupingInfo.buildRepeat(groupByClause.getGroupingExprs(), groupByClause.getGroupingSetList());
groupByClause.analyze(analyzer);
} catch (AnalysisException exception) {
Assert.assertTrue(false);
}
Assert.assertEquals("CUBE (`testdb`.`t`.`k1`, `testdb`.`t`.`k2`, "
+ "`testdb`.`t`.`k3`, `testdb`.`t`.`k1`)", groupByClause.toSql());
Assert.assertEquals(4, groupByClause.getGroupingExprs().size());
List<BitSet> bitSetList = groupingInfo.getGroupingIdList();
bitSetList.remove(0);
{
String[] answer = {"{}", "{1}", "{0}", "{0, 1}", "{2}", "{1, 2}", "{0, 2}"};
Set<String> answerSet = new HashSet<String>(Arrays.asList(answer));
Set<String> resultSet = new HashSet<>();
for (BitSet aBitSetList : bitSetList) {
String s = aBitSetList.toString();
resultSet.add(s);
}
Assert.assertEquals(answerSet, resultSet);
}
}
@Test
public void testGroupBy() {
ArrayList<Expr> groupingExprs = new ArrayList<>();
String[] cols = {"k2", "k2", "k3", "k1"};
for (String col : cols) {
Expr expr = new SlotRef(new TableName("testdb", "t"), col);
groupingExprs.add(expr);
}
GroupByClause groupByClause = new GroupByClause(Expr.cloneList(groupingExprs),
GroupByClause.GroupingType.GROUP_BY);
try {
groupByClause.analyze(analyzer);
} catch (AnalysisException execption) {
Assert.assertTrue(false);
}
Assert.assertEquals("`testdb`.`t`.`k2`, `testdb`.`t`.`k2`, `testdb`.`t`.`k3`, `testdb`.`t`.`k1`", groupByClause.toSql());
Assert.assertEquals(3, groupByClause.getGroupingExprs().size());
groupingExprs.remove(0);
Assert.assertEquals(groupByClause.getGroupingExprs(), groupingExprs);
}
@Test
public void testReset() {
ArrayList<Expr> groupingExprs = new ArrayList<>();
String[] cols = {"k2", "k2", "k3", "k1"};
for (String col : cols) {
Expr expr = new SlotRef(new TableName("testdb", "t"), col);
groupingExprs.add(expr);
}
GroupByClause groupByClause = new GroupByClause(Expr.cloneList(groupingExprs),
GroupByClause.GroupingType.GROUP_BY);
try {
groupByClause.analyze(analyzer);
} catch (AnalysisException execption) {
Assert.assertTrue(false);
}
try {
groupByClause.reset();
} catch (Exception e) {
Assert.fail("reset throw exceptions!" + e);
}
}
@Test
public void testGetTuple() throws AnalysisException {
ArrayList<Expr> groupingExprs = new ArrayList<>();
String[] cols = {"k1", "k2", "k3", "k1"};
for (String col : cols) {
Expr expr = new SlotRef(new TableName("testdb", "t"), col);
groupingExprs.add(expr);
}
GroupByClause groupByClause = new GroupByClause(Expr.cloneList(groupingExprs),
GroupByClause.GroupingType.GROUP_BY);
try {
groupByClause.analyze(analyzer);
} catch (AnalysisException exception) {
Assert.assertTrue(false);
}
}
@Test
public void testGenGroupingList() throws AnalysisException {
ArrayList<Expr> groupingExprs = new ArrayList<>();
String[] cols = {"k1", "k2", "k3"};
for (String col : cols) {
Expr expr = new SlotRef(new TableName("testdb", "t"), col);
groupingExprs.add(expr);
}
GroupByClause groupByClause = new GroupByClause(Expr.cloneList(groupingExprs),
GroupByClause.GroupingType.CUBE);
List<Expr> slots = new ArrayList<>();
for (String col : cols) {
SlotRef expr = new SlotRef(new TableName("testdb", "t"), col);
slots.add(expr);
}
GroupingInfo groupingInfo = null;
try {
groupingInfo = new GroupingInfo(analyzer, GroupByClause.GroupingType.CUBE);
groupingInfo.addGroupingSlots(slots, analyzer);
groupByClause.genGroupingExprs();
groupingInfo.buildRepeat(groupByClause.getGroupingExprs(), groupByClause.getGroupingSetList());
groupByClause.analyze(analyzer);
} catch (AnalysisException exception) {
Assert.assertTrue(false);
}
List<List<Long>> list = groupingInfo.genGroupingList(groupByClause.getGroupingExprs());
Assert.assertEquals(2, list.size());
Assert.assertEquals(list.get(0), list.get(1));
}
}

View File

@ -0,0 +1,113 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.doris.analysis;
import org.apache.doris.catalog.Type;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
public class VirtualSlotRefTest {
private Analyzer analyzer;
private List<Expr> slots;
private TupleDescriptor virtualTuple;
private VirtualSlotRef virtualSlot;
DataOutputStream dos;
File file;
DataInputStream dis;
@Before
public void setUp() throws IOException {
Analyzer analyzerBase = AccessTestUtil.fetchTableAnalyzer();
analyzer = new Analyzer(analyzerBase.getCatalog(), analyzerBase.getContext());
String[] cols = {"k1", "k2", "k3"};
slots = new ArrayList<>();
for (String col : cols) {
SlotRef expr = new SlotRef(new TableName("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.getTable(new TableName("testdb", "t")));
tupleByAlias.put("testdb.t", td);
f.set(analyzer, tupleByAlias);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
virtualTuple = analyzer.getDescTbl().createTupleDescriptor("VIRTUAL_TUPLE");
virtualSlot = new VirtualSlotRef("colName", Type.BIGINT, virtualTuple, slots);
file = new File("./virtualSlot");
file.createNewFile();
dos = new DataOutputStream(new FileOutputStream(file));
dis = new DataInputStream(new FileInputStream(file));
}
@After
public void tearDown() throws Exception {
dis.close();
dos.close();
file.delete();
}
@Test
public void read() throws IOException {
virtualSlot.write(dos);
virtualSlot.setRealSlots(slots);
VirtualSlotRef v = VirtualSlotRef.read(dis);
Assert.assertEquals(3, v.getRealSlots().size());
}
@Test
public void testClone() {
Expr v = virtualSlot.clone();
Assert.assertTrue(v instanceof VirtualSlotRef);
Assert.assertTrue(((VirtualSlotRef) v).getRealSlots().get(0).equals(virtualSlot.getRealSlots().get(0)));
Assert.assertFalse(((VirtualSlotRef) v).getRealSlots().get(0) == virtualSlot.getRealSlots().get(0));
}
@Test
public void analyzeImpl() {
try {
virtualSlot.analyzeImpl(analyzer);
} catch (Exception e) {
Assert.fail("analyze throw exception");
}
}
}

View File

@ -0,0 +1,97 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
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.thrift.TExplainLevel;
import org.apache.doris.thrift.TPlanNode;
import org.apache.doris.thrift.TPlanNodeType;
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 java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
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.getCatalog(), analyzerBase.getContext());
String[] cols = {"k1", "k2", "k3"};
List<SlotRef> slots = new ArrayList<>();
for (String col : cols) {
SlotRef expr = new SlotRef(new TableName("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.getTable(new TableName("testdb", "t")));
tupleByAlias.put("testdb.t", td);
f.set(analyzer, tupleByAlias);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (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);
}
@Test
public void testNornal() {
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");
}
}
}