[GroupingSet] Disable column both in select list and aggregate functions when using GROUPING SETS/CUBE/ROLLUP (#2921)

This commit is contained in:
yangzhg
2020-02-18 13:56:56 +08:00
committed by GitHub
parent b3c5f0fac7
commit 7be2871c36
5 changed files with 150 additions and 48 deletions

View File

@ -17,6 +17,7 @@
package org.apache.doris.analysis;
import org.apache.doris.catalog.AggregateFunction;
import org.apache.doris.catalog.AggregateType;
import org.apache.doris.catalog.Catalog;
import org.apache.doris.catalog.Column;
@ -334,7 +335,7 @@ public class SelectStmt extends QueryStmt {
// of expr child and depth limits (toColumn() label may call toSql()).
item.getExpr().analyze(analyzer);
if (item.getExpr().contains(Predicates.instanceOf(Subquery.class))) {
throw new AnalysisException("Subqueries are not supported in the select list.");
throw new AnalysisException("Subquery is not supported in the select list.");
}
resultExprs.add(item.getExpr());
SlotRef aliasRef = new SlotRef(null, item.toColumnLabel());
@ -349,6 +350,17 @@ public class SelectStmt extends QueryStmt {
}
}
if (groupByClause != null && groupByClause.isGroupByExtension()) {
for (SelectListItem item : selectList.getItems()) {
if (item.getExpr() instanceof FunctionCallExpr && item.getExpr().fn instanceof AggregateFunction) {
for (Expr expr: groupByClause.getGroupingExprs()) {
if (item.getExpr().contains(expr)) {
throw new AnalysisException("column: " + expr.toSql() + " cannot both in select list and "
+ "aggregate functions when using GROUPING SETS/CUBE/ROLLUP, please use union"
+ " instead.");
}
}
}
}
groupingInfo = new GroupingInfo(analyzer, groupByClause.getGroupingType());
groupingInfo.substituteGroupingFn(resultExprs, analyzer);
} else {

View File

@ -0,0 +1,52 @@
package org.apache.doris.analysis;
import java.util.UUID;
import org.apache.doris.catalog.Catalog;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.qe.ConnectContext;
import org.apache.doris.utframe.UtFrameUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class SelectStmtTest {
private static String runningDir = "fe/mocked/DemoTest/" + UUID.randomUUID().toString() + "/";
@Rule
public ExpectedException expectedEx = ExpectedException.none();
@Test
public void testGroupingSets() throws Exception {
ConnectContext ctx = UtFrameUtils.createDefaultCtx();
UtFrameUtils.createMinDorisCluster(runningDir);
String createDbStmtStr = "create database db1;";
CreateDbStmt createDbStmt = (CreateDbStmt) UtFrameUtils.parseAndAnalyzeStmt(createDbStmtStr, ctx);
Catalog.getCurrentCatalog().createDb(createDbStmt);
System.out.println(Catalog.getCurrentCatalog().getDbNames());
// 3. create table tbl1
String createTblStmtStr = "create table db1.tbl1(k1 varchar(32), k2 varchar(32), k3 varchar(32), k4 int) "
+ "AGGREGATE KEY(k1, k2,k3,k4) distributed by hash(k1) buckets 3 properties('replication_num' = '1');";
CreateTableStmt createTableStmt = (CreateTableStmt) UtFrameUtils.parseAndAnalyzeStmt(createTblStmtStr, ctx);
Catalog.getCurrentCatalog().createTable(createTableStmt);
String selectStmtStr = "select k1,k2,MAX(k4) from db1.tbl1 GROUP BY GROUPING sets ((k1,k2),(k1),(k2),());";
UtFrameUtils.parseAndAnalyzeStmt(selectStmtStr, ctx);
String selectStmtStr2 = "select k1,k4,MAX(k4) from db1.tbl1 GROUP BY GROUPING sets ((k1,k4),(k1),(k4),());";
expectedEx.expect(AnalysisException.class);
expectedEx.expectMessage("column: `k4` cannot both in select list and aggregate functions when using GROUPING"
+ " SETS/CUBE/ROLLUP, please use union instead.");
UtFrameUtils.parseAndAnalyzeStmt(selectStmtStr2, ctx);
String selectStmtStr3 = "select k1,k4,MAX(k4+k4) from db1.tbl1 GROUP BY GROUPING sets ((k1,k4),(k1),(k4),());";
UtFrameUtils.parseAndAnalyzeStmt(selectStmtStr3, ctx);
String selectStmtStr4 = "select k1,k4+k4,MAX(k4+k4) from db1.tbl1 GROUP BY GROUPING sets ((k1,k4),(k1),(k4),()"
+ ");";
UtFrameUtils.parseAndAnalyzeStmt(selectStmtStr4, ctx);
}
}

View File

@ -17,6 +17,7 @@
package org.apache.doris.utframe;
import org.apache.commons.io.FileUtils;
import org.apache.doris.analysis.CreateDbStmt;
import org.apache.doris.analysis.CreateTableStmt;
import org.apache.doris.catalog.Catalog;
@ -41,14 +42,16 @@ import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
/*
@ -69,18 +72,19 @@ public class AnotherDemoTest {
// use a unique dir so that it won't be conflict with other unit test which
// may also start a Mocked Frontend
private static String runningDir = "fe/mocked/AnotherDemoTest/" + UUID.randomUUID().toString() + "/";
private static String runningDirBase = "fe";
private static String runningDir = runningDirBase + "/mocked/AnotherDemoTest/" + UUID.randomUUID().toString() + "/";
@BeforeClass
public static void beforeClass() throws EnvVarNotSetException, IOException,
FeStartException, NotInitException, DdlException, InterruptedException {
// get DORIS_HOME
final String dorisHome = System.getenv("DORIS_HOME");
String dorisHome = System.getenv("DORIS_HOME");
if (Strings.isNullOrEmpty(dorisHome)) {
throw new EnvVarNotSetException("env DORIS_HOME is not set");
dorisHome = Files.createTempDirectory("DORIS_HOME").toAbsolutePath().toString();
}
getRandomPort();
getPorts();
// start fe in "DORIS_HOME/fe/mocked/"
MockedFrontend frontend = MockedFrontend.getInstance();
@ -111,19 +115,25 @@ public class AnotherDemoTest {
Thread.sleep(5000);
}
// generate all port from between 20000 ~ 30000
private static void getRandomPort() {
Random r = new Random(System.currentTimeMillis());
int basePort = 20000 + r.nextInt(9000);
fe_http_port = basePort + 1;
fe_rpc_port = basePort + 2;
fe_query_port = basePort + 3;
fe_edit_log_port = basePort + 4;
@AfterClass
public static void TearDown() {
try {
FileUtils.deleteDirectory(new File(runningDirBase));
} catch (IOException e) {
}
}
be_heartbeat_port = basePort + 5;
be_thrift_port = basePort + 6;
be_brpc_port = basePort + 7;
be_http_port = basePort + 8;
// generate all port from valid ports
private static void getPorts() {
fe_http_port = UtFrameUtils.findValidPort();
fe_rpc_port = UtFrameUtils.findValidPort();
fe_query_port = UtFrameUtils.findValidPort();
fe_edit_log_port = UtFrameUtils.findValidPort();
be_heartbeat_port = UtFrameUtils.findValidPort();
be_thrift_port = UtFrameUtils.findValidPort();
be_brpc_port = UtFrameUtils.findValidPort();
be_http_port = UtFrameUtils.findValidPort();
}
@Test

View File

@ -17,6 +17,7 @@
package org.apache.doris.utframe;
import org.apache.commons.io.FileUtils;
import org.apache.doris.alter.AlterJobV2;
import org.apache.doris.analysis.AlterTableStmt;
import org.apache.doris.analysis.CreateDbStmt;
@ -43,14 +44,16 @@ import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
/*
@ -74,18 +77,19 @@ public class DemoTest {
private static int be_http_port;
// use a unique dir so that it won't be conflict with other unit test which
// may also start a Mocked Frontend
private static String runningDir = "fe/mocked/DemoTest/" + UUID.randomUUID().toString() + "/";
private static String runningDirBase = "fe";
private static String runningDir = runningDirBase + "/mocked/DemoTest/" + UUID.randomUUID().toString() + "/";
@BeforeClass
public static void beforeClass() throws EnvVarNotSetException, IOException,
FeStartException, NotInitException, DdlException, InterruptedException {
// get DORIS_HOME
final String dorisHome = System.getenv("DORIS_HOME");
String dorisHome = System.getenv("DORIS_HOME");
if (Strings.isNullOrEmpty(dorisHome)) {
throw new EnvVarNotSetException("env DORIS_HOME is not set");
dorisHome = Files.createTempDirectory("DORIS_HOME").toAbsolutePath().toString();
}
getRandomPort();
getPorts();
// start fe in "DORIS_HOME/fe/mocked/"
MockedFrontend frontend = MockedFrontend.getInstance();
@ -116,19 +120,26 @@ public class DemoTest {
Thread.sleep(6000);
}
// generate all port from between 20000 ~ 30000
private static void getRandomPort() {
Random r = new Random(System.currentTimeMillis());
int basePort = 20000 + r.nextInt(9000);
fe_http_port = basePort + 1;
fe_rpc_port = basePort + 2;
fe_query_port = basePort + 3;
fe_edit_log_port = basePort + 4;
be_heartbeat_port = basePort + 5;
be_thrift_port = basePort + 6;
be_brpc_port = basePort + 7;
be_http_port = basePort + 8;
@AfterClass
public static void TearDown() {
try {
FileUtils.deleteDirectory(new File(runningDirBase));
} catch (IOException e) {
}
}
// generate all port from valid ports
private static void getPorts() {
fe_http_port = UtFrameUtils.findValidPort();
fe_rpc_port = UtFrameUtils.findValidPort();
fe_query_port = UtFrameUtils.findValidPort();
fe_edit_log_port = UtFrameUtils.findValidPort();
be_heartbeat_port = UtFrameUtils.findValidPort();
be_thrift_port = UtFrameUtils.findValidPort();
be_brpc_port = UtFrameUtils.findValidPort();
be_http_port = UtFrameUtils.findValidPort();
}
@Test

View File

@ -42,10 +42,11 @@ import com.google.common.collect.Maps;
import java.io.IOException;
import java.io.StringReader;
import java.net.ServerSocket;
import java.nio.channels.SocketChannel;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;
import java.util.Random;
public class UtFrameUtils {
@ -77,22 +78,20 @@ public class UtFrameUtils {
public static void createMinDorisCluster(String runningDir) throws EnvVarNotSetException, IOException,
FeStartException, NotInitException, DdlException, InterruptedException {
// get DORIS_HOME
final String dorisHome = System.getenv("DORIS_HOME");
String dorisHome = System.getenv("DORIS_HOME");
if (Strings.isNullOrEmpty(dorisHome)) {
throw new EnvVarNotSetException("env DORIS_HOME is not set");
dorisHome = Files.createTempDirectory("DORIS_HOME").toAbsolutePath().toString();
}
Random r = new Random(System.currentTimeMillis());
int basePort = 20000 + r.nextInt(9000);
int fe_http_port = basePort + 1;
int fe_rpc_port = basePort + 2;
int fe_query_port = basePort + 3;
int fe_edit_log_port = basePort + 4;
int fe_http_port = findValidPort();
int fe_rpc_port = findValidPort();
int fe_query_port = findValidPort();
int fe_edit_log_port = findValidPort();
int be_heartbeat_port = basePort + 5;
int be_thrift_port = basePort + 6;
int be_brpc_port = basePort + 7;
int be_http_port = basePort + 8;
int be_heartbeat_port = findValidPort();
int be_thrift_port = findValidPort();
int be_brpc_port = findValidPort();
int be_http_port = findValidPort();
// start fe in "DORIS_HOME/fe/mocked/"
MockedFrontend frontend = MockedFrontend.getInstance();
@ -122,4 +121,22 @@ public class UtFrameUtils {
// sleep to wait first heartbeat
Thread.sleep(6000);
}
public static int findValidPort() {
ServerSocket socket = null;
try {
socket = new ServerSocket(0);
socket.setReuseAddress(true);
return socket.getLocalPort();
} catch (Exception e) {
throw new IllegalStateException("Could not find a free TCP/IP port to start HTTP Server on");
} finally {
if (socket != null) {
try {
socket.close();
} catch (Exception e) {
}
}
}
}
}