blob: 011060184f706d8b225a306de4f3a81968324960 [file] [log] [blame]
/*
* 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.iotdb.db.queryengine.plan.parser;
import org.apache.iotdb.common.rpc.thrift.TAggregationType;
import org.apache.iotdb.commons.auth.entity.PrivilegeType;
import org.apache.iotdb.commons.exception.IllegalPathException;
import org.apache.iotdb.commons.exception.MetadataException;
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.db.exception.query.QueryProcessException;
import org.apache.iotdb.db.exception.sql.SemanticException;
import org.apache.iotdb.db.queryengine.plan.expression.binary.GreaterEqualExpression;
import org.apache.iotdb.db.queryengine.plan.expression.binary.LessThanExpression;
import org.apache.iotdb.db.queryengine.plan.expression.binary.LogicAndExpression;
import org.apache.iotdb.db.queryengine.plan.expression.leaf.ConstantOperand;
import org.apache.iotdb.db.queryengine.plan.expression.leaf.TimeSeriesOperand;
import org.apache.iotdb.db.queryengine.plan.expression.leaf.TimestampOperand;
import org.apache.iotdb.db.queryengine.plan.expression.multi.FunctionExpression;
import org.apache.iotdb.db.queryengine.plan.statement.Statement;
import org.apache.iotdb.db.queryengine.plan.statement.StatementType;
import org.apache.iotdb.db.queryengine.plan.statement.component.ResultColumn;
import org.apache.iotdb.db.queryengine.plan.statement.crud.DeleteDataStatement;
import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertMultiTabletsStatement;
import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement;
import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowsOfOneDeviceStatement;
import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowsStatement;
import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertTabletStatement;
import org.apache.iotdb.db.queryengine.plan.statement.crud.QueryStatement;
import org.apache.iotdb.db.queryengine.plan.statement.metadata.CreateAlignedTimeSeriesStatement;
import org.apache.iotdb.db.queryengine.plan.statement.metadata.CreateMultiTimeSeriesStatement;
import org.apache.iotdb.db.queryengine.plan.statement.metadata.CreateTimeSeriesStatement;
import org.apache.iotdb.db.queryengine.plan.statement.metadata.DatabaseSchemaStatement;
import org.apache.iotdb.db.queryengine.plan.statement.metadata.DeleteDatabaseStatement;
import org.apache.iotdb.db.queryengine.plan.statement.metadata.DeleteTimeSeriesStatement;
import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.BatchActivateTemplateStatement;
import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.CreateSchemaTemplateStatement;
import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.DropSchemaTemplateStatement;
import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.ShowNodesInSchemaTemplateStatement;
import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.UnsetSchemaTemplateStatement;
import org.apache.iotdb.db.queryengine.plan.statement.metadata.view.CreateLogicalViewStatement;
import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement;
import org.apache.iotdb.isession.template.TemplateNode;
import org.apache.iotdb.rpc.StatementExecutionException;
import org.apache.iotdb.service.rpc.thrift.TSAggregationQueryReq;
import org.apache.iotdb.service.rpc.thrift.TSCreateAlignedTimeseriesReq;
import org.apache.iotdb.service.rpc.thrift.TSCreateMultiTimeseriesReq;
import org.apache.iotdb.service.rpc.thrift.TSCreateSchemaTemplateReq;
import org.apache.iotdb.service.rpc.thrift.TSCreateTimeseriesReq;
import org.apache.iotdb.service.rpc.thrift.TSDeleteDataReq;
import org.apache.iotdb.service.rpc.thrift.TSDropSchemaTemplateReq;
import org.apache.iotdb.service.rpc.thrift.TSInsertRecordReq;
import org.apache.iotdb.service.rpc.thrift.TSInsertRecordsOfOneDeviceReq;
import org.apache.iotdb.service.rpc.thrift.TSInsertRecordsReq;
import org.apache.iotdb.service.rpc.thrift.TSInsertStringRecordsOfOneDeviceReq;
import org.apache.iotdb.service.rpc.thrift.TSInsertStringRecordsReq;
import org.apache.iotdb.service.rpc.thrift.TSInsertTabletReq;
import org.apache.iotdb.service.rpc.thrift.TSInsertTabletsReq;
import org.apache.iotdb.service.rpc.thrift.TSLastDataQueryReq;
import org.apache.iotdb.service.rpc.thrift.TSQueryTemplateReq;
import org.apache.iotdb.service.rpc.thrift.TSRawDataQueryReq;
import org.apache.iotdb.service.rpc.thrift.TSUnsetSchemaTemplateReq;
import org.apache.iotdb.session.template.MeasurementNode;
import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType;
import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
import org.junit.Assert;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import static org.apache.iotdb.db.schemaengine.template.TemplateQueryType.SHOW_MEASUREMENTS;
import static org.apache.iotdb.tsfile.file.metadata.enums.CompressionType.SNAPPY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
public class StatementGeneratorTest {
@Test
public void testRawDataQuery() throws IllegalPathException {
TSRawDataQueryReq req =
new TSRawDataQueryReq(
101L, Arrays.asList("root.sg.d1.s1", "root.sg.d1.s2"), 100L, 200L, 102L);
Statement statement = StatementGenerator.createStatement(req);
QueryStatement queryStatement = (QueryStatement) statement;
assertEquals(
Arrays.asList(new PartialPath("root.sg.d1.s2"), new PartialPath("root.sg.d1.s1")),
queryStatement.getPaths());
assertEquals(
new LogicAndExpression(
new GreaterEqualExpression(
new TimestampOperand(), new ConstantOperand(TSDataType.INT64, "100")),
new LessThanExpression(
new TimestampOperand(), new ConstantOperand(TSDataType.INT64, "200"))),
queryStatement.getWhereCondition().getPredicate());
}
@Test
public void testLastDataQuery() throws IllegalPathException {
TSLastDataQueryReq req =
new TSLastDataQueryReq(101L, Arrays.asList("root.sg.d1.s1", "root.sg.d1.s2"), 200L, 102L);
Statement statement = StatementGenerator.createStatement(req);
QueryStatement queryStatement = (QueryStatement) statement;
assertEquals(
Arrays.asList(new PartialPath("root.sg.d1.s2"), new PartialPath("root.sg.d1.s1")),
queryStatement.getPaths());
assertEquals(
new GreaterEqualExpression(
new TimestampOperand(), new ConstantOperand(TSDataType.INT64, "200")),
queryStatement.getWhereCondition().getPredicate());
}
@Test
public void testAggregationQuery() throws IllegalPathException {
TSAggregationQueryReq req =
new TSAggregationQueryReq(
101L,
102L,
Arrays.asList("root.sg.d1.s1", "root.sg.d1.s2"),
Arrays.asList(TAggregationType.AVG, TAggregationType.COUNT));
Statement statement = StatementGenerator.createStatement(req);
QueryStatement queryStatement = (QueryStatement) statement;
assertEquals(
Arrays.asList(new PartialPath("root.sg.d1.s2"), new PartialPath("root.sg.d1.s1")),
queryStatement.getPaths());
assertEquals(
new ResultColumn(
new FunctionExpression(
"AVG",
new LinkedHashMap<>(),
Collections.singletonList(new TimeSeriesOperand(new PartialPath("root.sg.d1.s1")))),
ResultColumn.ColumnType.AGGREGATION),
queryStatement.getSelectComponent().getResultColumns().get(0));
assertEquals(
new ResultColumn(
new FunctionExpression(
"COUNT",
new LinkedHashMap<>(),
Collections.singletonList(new TimeSeriesOperand(new PartialPath("root.sg.d1.s2")))),
ResultColumn.ColumnType.AGGREGATION),
queryStatement.getSelectComponent().getResultColumns().get(1));
}
@Test
public void testInsertRecord() throws QueryProcessException, IllegalPathException {
TSInsertRecordReq req =
new TSInsertRecordReq(
101L,
"root.sg.d1",
Arrays.asList("s1", "s2"),
ByteBuffer.wrap(new byte[] {0, 0, 0, 0}),
1000L);
InsertRowStatement statement = StatementGenerator.createStatement(req);
assertEquals(1000L, statement.getTime());
}
@Test
public void testInsertTablet() throws IllegalPathException {
TSInsertTabletReq req =
new TSInsertTabletReq(
101L,
"root.sg.d1",
Collections.singletonList("s1"),
ByteBuffer.wrap(new byte[] {0, 0, 0, 0}),
ByteBuffer.wrap(new byte[] {0, 0, 0, 0, 0, 0, 0, 0}),
Collections.singletonList(1),
1);
InsertTabletStatement statement = StatementGenerator.createStatement(req);
assertEquals(0L, statement.getMinTime());
}
@Test
public void testInsertTablets() throws IllegalPathException {
TSInsertTabletsReq req =
new TSInsertTabletsReq(
101L,
Collections.singletonList("root.sg.d1"),
Collections.singletonList(Collections.singletonList("s1")),
Collections.singletonList(ByteBuffer.wrap(new byte[] {0, 0, 0, 0})),
Collections.singletonList(ByteBuffer.wrap(new byte[] {0, 0, 0, 0, 0, 0, 0, 0})),
Collections.singletonList(Collections.singletonList(1)),
Collections.singletonList(1));
InsertMultiTabletsStatement statement = StatementGenerator.createStatement(req);
assertEquals(Collections.singletonList(new PartialPath("root.sg.d1.s1")), statement.getPaths());
}
@Test
public void testInsertRecords() throws QueryProcessException, IllegalPathException {
TSInsertRecordsReq req =
new TSInsertRecordsReq(
101L,
Collections.singletonList("root.sg.d1"),
Collections.singletonList(Collections.singletonList("s1")),
Collections.singletonList(ByteBuffer.wrap(new byte[] {0, 0, 0, 0})),
Collections.singletonList(1L));
InsertRowsStatement statement = StatementGenerator.createStatement(req);
assertEquals(Collections.singletonList(new PartialPath("root.sg.d1.s1")), statement.getPaths());
}
@Test
public void testInsertStringRecords() throws IllegalPathException {
TSInsertStringRecordsReq req =
new TSInsertStringRecordsReq(
101L,
Collections.singletonList("root.sg.d1"),
Collections.singletonList(Collections.singletonList("s1")),
Collections.singletonList(Collections.singletonList("1")),
Collections.singletonList(1L));
InsertRowsStatement statement = StatementGenerator.createStatement(req);
assertEquals(Collections.singletonList(new PartialPath("root.sg.d1.s1")), statement.getPaths());
}
@Test
public void testInsertRecordsOfOneDevice() throws IllegalPathException, QueryProcessException {
TSInsertRecordsOfOneDeviceReq req =
new TSInsertRecordsOfOneDeviceReq(
101L,
"root.sg.d1",
Collections.singletonList(Collections.singletonList("s1")),
Collections.singletonList(ByteBuffer.wrap(new byte[] {0, 0, 0, 0})),
Collections.singletonList(1L));
InsertRowsOfOneDeviceStatement statement = StatementGenerator.createStatement(req);
assertEquals(Collections.singletonList(new PartialPath("root.sg.d1.s1")), statement.getPaths());
}
@Test
public void testInsertStringRecordsOfOneDevice() throws IllegalPathException {
TSInsertStringRecordsOfOneDeviceReq req =
new TSInsertStringRecordsOfOneDeviceReq(
101L,
"root.sg.d1",
Collections.singletonList(Collections.singletonList("s1")),
Collections.singletonList(Collections.singletonList("1")),
Collections.singletonList(1L));
InsertRowsOfOneDeviceStatement statement = StatementGenerator.createStatement(req);
assertEquals(Collections.singletonList(new PartialPath("root.sg.d1.s1")), statement.getPaths());
}
@Test
public void testCreateDatabaseSchema() throws IllegalPathException {
DatabaseSchemaStatement statement = StatementGenerator.createStatement("root.db");
assertEquals(new PartialPath("root.db"), statement.getDatabasePath());
}
@Test
public void testCreateTimeSeries() throws IllegalPathException {
TSCreateTimeseriesReq req =
new TSCreateTimeseriesReq(
1L,
"root.db.d1.s1",
TSDataType.INT64.getType(),
TSEncoding.PLAIN.ordinal(),
SNAPPY.ordinal());
CreateTimeSeriesStatement statement = StatementGenerator.createStatement(req);
assertEquals(new PartialPath("root.db.d1.s1"), statement.getPath());
assertEquals(TSDataType.INT64, statement.getDataType());
assertEquals(TSEncoding.PLAIN, statement.getEncoding());
assertEquals(SNAPPY, statement.getCompressor());
}
@Test
public void testCreateAlignedTimeSeries() throws IllegalPathException {
TSCreateAlignedTimeseriesReq req =
new TSCreateAlignedTimeseriesReq(
1L,
"root.db.d1",
Arrays.asList("s1", "s2"),
Arrays.asList(TSDataType.INT64.ordinal(), TSDataType.INT32.ordinal()),
Arrays.asList(TSEncoding.PLAIN.ordinal(), TSEncoding.RLE.ordinal()),
Arrays.asList(SNAPPY.ordinal(), SNAPPY.ordinal()));
CreateAlignedTimeSeriesStatement statement = StatementGenerator.createStatement(req);
assertEquals(
Arrays.asList(new PartialPath("root.db.d1.s1"), new PartialPath("root.db.d1.s2")),
statement.getPaths());
assertEquals(Arrays.asList(TSDataType.INT64, TSDataType.INT32), statement.getDataTypes());
assertEquals(Arrays.asList(TSEncoding.PLAIN, TSEncoding.RLE), statement.getEncodings());
assertEquals(Arrays.asList(SNAPPY, SNAPPY), statement.getCompressors());
}
@Test
public void testCreateMultiTimeSeries() throws IllegalPathException {
TSCreateMultiTimeseriesReq req =
new TSCreateMultiTimeseriesReq(
1L,
Arrays.asList("root.db.d1.s1", "root.db.d1.s2"),
Arrays.asList(TSDataType.INT64.ordinal(), TSDataType.INT32.ordinal()),
Arrays.asList(TSEncoding.PLAIN.ordinal(), TSEncoding.RLE.ordinal()),
Arrays.asList(SNAPPY.ordinal(), SNAPPY.ordinal()));
CreateMultiTimeSeriesStatement statement = StatementGenerator.createStatement(req);
assertEquals(
Arrays.asList(new PartialPath("root.db.d1.s1"), new PartialPath("root.db.d1.s2")),
statement.getPaths());
assertEquals(Arrays.asList(TSDataType.INT64, TSDataType.INT32), statement.getDataTypes());
assertEquals(Arrays.asList(TSEncoding.PLAIN, TSEncoding.RLE), statement.getEncodings());
assertEquals(Arrays.asList(SNAPPY, SNAPPY), statement.getCompressors());
}
@Test
public void testDeleteDatabase() throws IllegalPathException {
DeleteDatabaseStatement statement =
StatementGenerator.createStatement(Collections.singletonList("root.db"));
assertEquals(Collections.singletonList("root.db"), statement.getPrefixPath());
}
@Test
public void testDeleteData() throws IllegalPathException {
TSDeleteDataReq req =
new TSDeleteDataReq(1L, Arrays.asList("root.sg.d1.s1", "root.sg.d1.s2"), 1L, 100L);
DeleteDataStatement statement = StatementGenerator.createStatement(req);
assertEquals(
Arrays.asList(new PartialPath("root.sg.d1.s1"), new PartialPath("root.sg.d1.s2")),
statement.getPaths());
assertEquals(1L, statement.getDeleteStartTime());
assertEquals(100L, statement.getDeleteEndTime());
}
@Test
public void testCreateSchemaTemplate()
throws MetadataException, IOException, StatementExecutionException {
org.apache.iotdb.isession.template.Template template = getTemplate();
TSCreateSchemaTemplateReq req = new TSCreateSchemaTemplateReq();
req.setName(template.getName());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
template.serialize(baos);
req.setSerializedTemplate(baos.toByteArray());
baos.close();
CreateSchemaTemplateStatement statement = StatementGenerator.createStatement(req);
assertEquals("test-template", statement.getName());
assertEquals(Arrays.asList("y", "x"), statement.getMeasurements());
}
@Test
public void testQueryTemplate() {
TSQueryTemplateReq req = new TSQueryTemplateReq(1L, "test", SHOW_MEASUREMENTS.ordinal());
ShowNodesInSchemaTemplateStatement statement =
(ShowNodesInSchemaTemplateStatement) StatementGenerator.createStatement(req);
assertEquals("test", statement.getTemplateName());
}
@Test
public void testUnsetSchemaTemplate() throws IllegalPathException {
TSUnsetSchemaTemplateReq req = new TSUnsetSchemaTemplateReq(1L, "root.sg.d1", "test");
UnsetSchemaTemplateStatement statement = StatementGenerator.createStatement(req);
assertEquals("test", statement.getTemplateName());
}
@Test
public void testDropSchemaTemplate() {
TSDropSchemaTemplateReq req = new TSDropSchemaTemplateReq(1L, "test-template");
DropSchemaTemplateStatement statement = StatementGenerator.createStatement(req);
assertEquals("test-template", statement.getTemplateName());
}
@Test
public void testBatchActivateTemplate() throws IllegalPathException {
BatchActivateTemplateStatement statement =
StatementGenerator.createBatchActivateTemplateStatement(
Collections.singletonList("root.sg.d1"));
assertEquals(
Collections.singletonList(new PartialPath("root.sg.d1")), statement.getDevicePathList());
}
@Test
public void testDeleteTimeSeries() throws IllegalPathException {
DeleteTimeSeriesStatement statement =
StatementGenerator.createDeleteTimeSeriesStatement(Collections.singletonList("root.sg.d1"));
assertEquals(Collections.singletonList(new PartialPath("root.sg.d1")), statement.getPaths());
}
private org.apache.iotdb.isession.template.Template getTemplate()
throws StatementExecutionException {
org.apache.iotdb.isession.template.Template sessionTemplate =
new org.apache.iotdb.isession.template.Template("test-template", false);
TemplateNode mNodeX =
new MeasurementNode("x", TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY);
TemplateNode mNodeY =
new MeasurementNode("y", TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY);
sessionTemplate.addToTemplate(mNodeX);
sessionTemplate.addToTemplate(mNodeY);
return sessionTemplate;
}
@Test
public void rawDataQueryTest() {
String sql = "SELECT s1, s2 FROM root.sg1.d1 WHERE time > 1 and s3 > 2 LIMIT 10 OFFSET 11";
checkQueryStatement(
sql,
Arrays.asList("s1", "s2"),
Collections.singletonList("root.sg1.d1"),
"Time > 1 & s3 > 2",
10,
11);
}
@Test
public void groupByTagWithDuplicatedKeysTest() {
try {
checkQueryStatement(
"SELECT avg(*) FROM root.sg.** GROUP BY TAGS(k1, k2, k1)",
Collections.emptyList(),
Collections.emptyList(),
"",
10,
10);
Assert.fail();
} catch (SemanticException e) {
assertEquals("duplicated key in GROUP BY TAGS: k1", e.getMessage());
}
}
private AuthorStatement createAuthDclStmt(String sql) {
Statement stmt = StatementGenerator.createStatement(sql, ZonedDateTime.now().getOffset());
AuthorStatement roleDcl = (AuthorStatement) stmt;
return roleDcl;
}
@Test
public void testDCLUserOperation() {
// 1. create user and drop user
AuthorStatement userDcl = createAuthDclStmt("create user `user1` 'password1';");
assertEquals("user1", userDcl.getUserName());
assertEquals(Collections.emptyList(), userDcl.getPaths());
assertEquals("password1", userDcl.getPassWord());
assertEquals(StatementType.CREATE_USER, userDcl.getType());
userDcl = createAuthDclStmt("drop user `user1`;");
assertEquals("user1", userDcl.getUserName());
assertEquals(Collections.emptyList(), userDcl.getPaths());
assertEquals(StatementType.DELETE_USER, userDcl.getType());
// 2.update user's password
userDcl = createAuthDclStmt("alter user `user1` set password 'password2';");
assertEquals("user1", userDcl.getUserName());
/**
* [ BUG ] We didn't save the old password in the statement. If userA has logged in with
* session_A. Session_B's alter password operation cannot block session A's alter password
* operation because we only check the user's password before they log in.
*/
// assertEquals("password1", userDcl.getPassWord());
assertEquals("password2", userDcl.getNewPassword());
assertEquals(StatementType.MODIFY_PASSWORD, userDcl.getType());
}
@Test
public void testDCLROLEOperation() {
// 1. create role and drop role.
AuthorStatement roleDcl = createAuthDclStmt("create role role1;");
assertEquals("role1", roleDcl.getRoleName());
assertEquals(StatementType.CREATE_ROLE, roleDcl.getType());
roleDcl = createAuthDclStmt("drop role role1;");
assertEquals(StatementType.DELETE_ROLE, roleDcl.getType());
assertEquals("role1", roleDcl.getRoleName());
// 2. grant and revoke role.
roleDcl = createAuthDclStmt("grant role `role1` to `user1`;");
assertEquals(StatementType.GRANT_USER_ROLE, roleDcl.getType());
assertEquals("role1", roleDcl.getRoleName());
assertEquals("user1", roleDcl.getUserName());
roleDcl = createAuthDclStmt("revoke role `role1` from `user1`;");
assertEquals(StatementType.REVOKE_USER_ROLE, roleDcl.getType());
assertEquals("role1", roleDcl.getRoleName());
assertEquals("user1", roleDcl.getUserName());
}
@FunctionalInterface
interface grantRevokeCheck {
void checkParser(String privilege, String name, boolean isuser, String path, boolean grantOpt);
}
/** This test will check grant/revoke simple privilege on/from paths. */
@Test
public void testNormalGrantRevoke() {
grantRevokeCheck testGrant =
(privilege, name, isuser, path, grantOpt) -> {
String sql = "grant %s on %s to %s %s %s ;";
sql =
String.format(
sql,
privilege,
path,
isuser ? "USER" : "ROLE",
name,
grantOpt ? "with grant option" : "");
AuthorStatement aclStmt = createAuthDclStmt(sql);
assertEquals(
isuser ? StatementType.GRANT_USER_PRIVILEGE : StatementType.GRANT_ROLE_PRIVILEGE,
aclStmt.getType());
assertEquals(path, aclStmt.getPaths().get(0).toString());
assertEquals(name, isuser ? aclStmt.getUserName() : aclStmt.getRoleName());
assertEquals(grantOpt, aclStmt.getGrantOpt());
assertEquals(privilege, aclStmt.getPrivilegeList()[0]);
};
String name = "test1";
String path = "root.**";
String pathErr = "root.t1.**";
String pathsErr = "root.**, root.t1.**";
// 1. check simple privilege grant to user/role with/without grant option.
for (PrivilegeType privilege : PrivilegeType.values()) {
testGrant.checkParser(privilege.toString(), name, true, path, true);
testGrant.checkParser(privilege.toString(), name, true, path, false);
testGrant.checkParser(privilege.toString(), name, false, path, true);
testGrant.checkParser(privilege.toString(), name, false, path, false);
// 2. if grant stmt has system privilege, path should be root.**
if (!privilege.isPathRelevant()) {
assertThrows(
SemanticException.class,
() ->
createAuthDclStmt(
String.format("GRANT %s on %s to USER `user1`;", privilege, pathErr)));
assertThrows(
SemanticException.class,
() ->
createAuthDclStmt(
String.format("GRANT %s on %s to USER `user1`;", privilege, pathsErr)));
}
}
grantRevokeCheck testRevoke =
(privilege, username, isuser, targtePath, grantOpt) -> {
String sql = "revoke %s on %s from %s %s";
sql = String.format(sql, privilege, targtePath, isuser ? "USER" : "ROLE", username);
AuthorStatement aclStmt = createAuthDclStmt(sql);
assertEquals(
isuser ? StatementType.REVOKE_USER_PRIVILEGE : StatementType.REVOKE_ROLE_PRIVILEGE,
aclStmt.getType());
assertEquals(path, aclStmt.getPaths().get(0).toString());
assertEquals(username, isuser ? aclStmt.getUserName() : aclStmt.getRoleName());
assertFalse(aclStmt.getGrantOpt());
assertEquals(privilege, aclStmt.getPrivilegeList()[0]);
};
// 3. check simple privilege revoke from user/role on simple path
for (PrivilegeType type : PrivilegeType.values()) {
testRevoke.checkParser(type.toString(), name, true, path, false);
testRevoke.checkParser(type.toString(), name, false, path, false);
// 4. check system privilege revoke from user on wrong paths.
if (!type.isPathRelevant()) {
assertThrows(
SemanticException.class,
() ->
createAuthDclStmt(
String.format("revoke %s on %s FROM USER `user1`;", type, pathErr)));
assertThrows(
SemanticException.class,
() ->
createAuthDclStmt(
String.format("revoke %s on %s FROM USER `user1`;", type, pathsErr)));
}
}
}
@Test
public void testComplexGrantRevoke() {
// 1. test complex privilege on single path :"root.**"
Set<String> allPriv = new HashSet<>();
for (PrivilegeType type : PrivilegeType.values()) {
allPriv.add(type.toString());
}
for (PrivilegeType type : PrivilegeType.values()) {
{
AuthorStatement stmt =
createAuthDclStmt(
String.format("GRANT ALL,%s on root.** to USER `user1` with grant option", type));
assertEquals(allPriv, new HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
Assert.assertTrue(stmt.getGrantOpt());
assertEquals(StatementType.GRANT_USER_PRIVILEGE, stmt.getType());
}
{
AuthorStatement stmt =
createAuthDclStmt(String.format("REVOKE ALL,%s on root.** from USER `user1`;", type));
assertEquals(allPriv, new HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
assertEquals(StatementType.REVOKE_USER_PRIVILEGE, stmt.getType());
}
{
AuthorStatement stmt =
createAuthDclStmt(String.format("GRANT ALL,%s on root.** to ROLE `role1`;", type));
assertEquals(allPriv, new HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
assertFalse(stmt.getGrantOpt());
assertEquals(StatementType.GRANT_ROLE_PRIVILEGE, stmt.getType());
}
}
AuthorStatement stmt =
createAuthDclStmt("GRANT ALL ON root.** to user `user1` with grant option;");
assertEquals(allPriv, new HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
Assert.assertTrue(stmt.getGrantOpt());
assertEquals(allPriv, new HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
// 2. complex privilege on a single wrong path
assertThrows(
SemanticException.class,
() -> createAuthDclStmt("grant all on root.t1.** to USER `user1` with grant option;"));
assertThrows(
SemanticException.class,
() -> createAuthDclStmt("grant all on root.t1.** to ROLE `user1` with grant option;"));
assertThrows(
SemanticException.class,
() ->
createAuthDclStmt(
"grant all,READ_DATA on root.t1.** to USER `user1` with grant option"));
assertThrows(
SemanticException.class,
() ->
createAuthDclStmt(
"grant all,READ_DATA on root.t1.** to ROLE `user1` with grant option"));
assertThrows(
SemanticException.class,
() -> createAuthDclStmt("grant all on root.t1.** to USER `user1`;"));
assertThrows(
SemanticException.class,
() -> createAuthDclStmt("grant all on root.t1.** to ROLE `user1`;"));
assertThrows(
SemanticException.class,
() -> createAuthDclStmt("grant all,READ_DATA on root.t1.** to USER `user1`;"));
assertThrows(
SemanticException.class,
() -> createAuthDclStmt("grant all,READ_DATA on root.t1.** to ROLE `user1`;"));
assertThrows(
SemanticException.class,
() -> createAuthDclStmt("revoke all on root.t1.** from USER `user1`;"));
assertThrows(
SemanticException.class,
() -> createAuthDclStmt("revoke all on root.t1.** from ROLE `user1`;"));
assertThrows(
SemanticException.class,
() -> createAuthDclStmt("revoke all,READ_DATA on root.t1.** from USER `user1`;"));
assertThrows(
SemanticException.class,
() -> createAuthDclStmt("revoke all,READ_DATA on root.t1.** from ROLE `user1`;"));
try {
createAuthDclStmt("grant all on root.t1.**, root.** to USER `user1` with grant option;");
} catch (SemanticException e) {
assertEquals("[ALL] can only be set on path: root.**", e.getMessage());
}
try {
createAuthDclStmt("grant MANAGE_ROLE on root.t1.** to USER `user1` with grant option;");
} catch (SemanticException e) {
assertEquals("[MANAGE_ROLE] can only be set on path: root.**", e.getMessage());
}
// 3. complex privilege on complex paths.
assertThrows(
SemanticException.class,
() ->
createAuthDclStmt(
"grant all on root.t1.**, root.** to USER `user1` with grant option;"));
assertThrows(
SemanticException.class,
() ->
createAuthDclStmt(
"grant MANAGE_ROLE on root.t1.**, root.** to USER `user1` with grant option;"));
// 4. READ privilege can be parsed successfully.
stmt = createAuthDclStmt("GRANT READ ON root.** TO USER `user1`;");
Set<String> readSet = new HashSet<>();
readSet.add("READ_DATA");
readSet.add("READ_SCHEMA");
assertEquals(readSet, new HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
stmt = createAuthDclStmt("GRANT READ,READ_DATA ON root.** TO USER `user1`;");
assertEquals(readSet, new HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
stmt = createAuthDclStmt("GRANT READ,READ_DATA ON root.**,root.t1.t2 TO USER `user1`;");
assertEquals(readSet, new HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
assertEquals(2, stmt.getPaths().size());
// 5. WRITE privilege can be parsed successfully.
stmt = createAuthDclStmt("GRANT WRITE ON root.** TO USER `user1`;");
Set<String> writeSet = new HashSet<>();
writeSet.add("WRITE_DATA");
writeSet.add("WRITE_SCHEMA");
assertEquals(writeSet, new HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
stmt = createAuthDclStmt("GRANT WRITE,WRITE_DATA ON root.** TO USER `user1`;");
assertEquals(writeSet, new HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
stmt = createAuthDclStmt("GRANT WRITE,WRITE ON root.**,root.t1.t2 TO USER `user1`;");
assertEquals(writeSet, new HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
assertEquals(2, stmt.getPaths().size());
}
@Test
public void testListThings() {
// 1. list user
AuthorStatement stmt = createAuthDclStmt("LIST USER;");
assertEquals(StatementType.LIST_USER, stmt.getType());
assertEquals(null, stmt.getRoleName());
// 2. list user of role
stmt = createAuthDclStmt("LIST USER OF ROLE `role1`;");
assertEquals(StatementType.LIST_USER, stmt.getType());
assertEquals("role1", stmt.getRoleName());
// 3. list role
stmt = createAuthDclStmt("LIST ROLE;");
assertEquals(StatementType.LIST_ROLE, stmt.getType());
assertEquals(null, stmt.getUserName());
// 4. list role of user
stmt = createAuthDclStmt("LIST ROLE OF USER `user1`;");
assertEquals(StatementType.LIST_ROLE, stmt.getType());
assertEquals("user1", stmt.getUserName());
// 5. list privileges of user
stmt = createAuthDclStmt("LIST PRIVILEGES OF USER `user1`;");
assertEquals(StatementType.LIST_USER_PRIVILEGE, stmt.getType());
assertEquals("user1", stmt.getUserName());
// 6. list privileges of role
stmt = createAuthDclStmt("LIST PRIVILEGES OF ROLE `role1`;");
assertEquals(StatementType.LIST_ROLE_PRIVILEGE, stmt.getType());
assertEquals("role1", stmt.getRoleName());
}
private CreateLogicalViewStatement createViewStmt(String sql) {
Statement stmt = StatementGenerator.createStatement(sql, ZonedDateTime.now().getOffset());
CreateLogicalViewStatement viewStmt = (CreateLogicalViewStatement) stmt;
return viewStmt;
}
@Test
public void testCreateView() throws IllegalPathException {
// 1. create with select
CreateLogicalViewStatement stmt =
createViewStmt("create view root.sg.view_dd as select s1 from root.sg.d1;");
List<PartialPath> path = new ArrayList<>();
path.add(new PartialPath("root.sg.d1"));
assertEquals(null, stmt.getSourcePaths().fullPathList);
assertEquals(path, stmt.getQueryStatement().getFromComponent().getPrefixPaths());
// 2. create with path
stmt = createViewStmt("create view root.sg as root.sg.d2;");
List<PartialPath> path2 = new ArrayList<>();
path2.add(new PartialPath("root.sg.d2"));
assertEquals(path2, stmt.getSourcePaths().fullPathList);
assertEquals(null, stmt.getQueryStatement());
}
// TODO: add more tests
private void checkQueryStatement(
String sql,
List<String> selectExprList,
List<String> fromPrefixPaths,
String wherePredicateString,
int rowLimit,
int rowOffset) {
QueryStatement statement =
(QueryStatement) StatementGenerator.createStatement(sql, ZonedDateTime.now().getOffset());
// check SELECT clause
int cnt = 0;
for (ResultColumn resultColumn : statement.getSelectComponent().getResultColumns()) {
String selectExpr = resultColumn.getExpression().toString();
assertEquals(selectExprList.get(cnt++), selectExpr);
}
assertEquals(selectExprList.size(), statement.getSelectComponent().getResultColumns().size());
// check FROM clause
cnt = 0;
for (PartialPath path : statement.getFromComponent().getPrefixPaths()) {
assertEquals(fromPrefixPaths.get(cnt++), path.toString());
}
assertEquals(fromPrefixPaths.size(), statement.getFromComponent().getPrefixPaths().size());
// check WHERE clause
assertEquals(
wherePredicateString, statement.getWhereCondition().getPredicate().getExpressionString());
// check LIMIT & OFFSET clause
assertEquals(rowLimit, statement.getRowLimit());
assertEquals(rowOffset, statement.getRowOffset());
// TODO: add more clause
}
}