This is an automated email from the ASF dual-hosted git repository.
qiaojialin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new 393a91f [New feature] Add the alias and show it as column name when
querying (#1621)
393a91f is described below
commit 393a91f48bc016ba2e3690dc1156573309316028
Author: Xiangwei Wei <[email protected]>
AuthorDate: Wed Sep 2 21:56:27 2020 +0800
[New feature] Add the alias and show it as column name when querying (#1621)
---
.../org/apache/iotdb/db/qp/strategy/SqlBase.g4 | 16 +-
.../DML Data Manipulation Language.md | 18 +-
docs/UserGuide/Operation Manual/SQL Reference.md | 47 ++
.../DML Data Manipulation Language.md | 18 +-
.../zh/UserGuide/Operation Manual/SQL Reference.md | 47 ++
.../java/org/apache/iotdb/db/metadata/MTree.java | 2 +-
.../org/apache/iotdb/db/metadata/PartialPath.java | 25 +-
.../db/qp/physical/crud/AlignByDevicePlan.java | 10 +
.../iotdb/db/qp/strategy/LogicalGenerator.java | 57 +-
.../iotdb/db/qp/strategy/PhysicalGenerator.java | 40 +-
.../qp/strategy/optimizer/ConcatPathOptimizer.java | 44 +-
.../iotdb/db/query/executor/LastQueryExecutor.java | 10 +-
.../org/apache/iotdb/db/service/TSServiceImpl.java | 42 +-
.../org/apache/iotdb/db/utils/SchemaUtils.java | 13 +-
.../org/apache/iotdb/db/integration/IoTDBAsIT.java | 603 +++++++++++++++++++++
.../org/apache/iotdb/db/metadata/MTreeTest.java | 4 +-
.../org/apache/iotdb/tsfile/read/common/Path.java | 1 -
17 files changed, 917 insertions(+), 80 deletions(-)
diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/strategy/SqlBase.g4
b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/strategy/SqlBase.g4
old mode 100755
new mode 100644
index f34e32e..8cea342
--- a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/strategy/SqlBase.g4
+++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/strategy/SqlBase.g4
@@ -103,8 +103,9 @@ statement
selectElements
: functionCall (COMMA functionCall)* #functionElement
| suffixPath (COMMA suffixPath)* #selectElement
- | stringLiteral (COMMA stringLiteral)* #selectConstElement
| lastClause #lastElement
+ | asClause (COMMA asClause)* #asElement
+ | functionAsClause (COMMA functionAsClause)* #functionAsElement
;
functionCall
@@ -123,8 +124,17 @@ functionName
| LAST_VALUE
;
+functionAsClause
+ : functionCall (AS ID)?
+ ;
+
lastClause
: LAST suffixPath (COMMA suffixPath)*
+ | LAST asClause (COMMA asClause)*
+ ;
+
+asClause
+ : suffixPath (AS ID)?
;
alias
@@ -1056,6 +1066,10 @@ COMPRESSION
: C O M P R E S S I O N
;
+AS
+ : A S
+ ;
+
TIME
: T I M E
;
diff --git a/docs/UserGuide/Operation Manual/DML Data Manipulation Language.md
b/docs/UserGuide/Operation Manual/DML Data Manipulation Language.md
index f754513..8f0b2b5 100644
--- a/docs/UserGuide/Operation Manual/DML Data Manipulation Language.md
+++ b/docs/UserGuide/Operation Manual/DML Data Manipulation Language.md
@@ -704,6 +704,22 @@ The result is shown below:
<center><img style="width:100%; max-width:800px; max-height:600px;
margin-left:auto; margin-right:auto; display:block;"
src="https://user-images.githubusercontent.com/13203019/51577879-64984680-1ef6-11e9-9d7b-57dd60fab60e.jpg"></center>
+### Use Alias
+
+Since the unique data model of IoTDB, lots of additional information like
device will be carried before each sensor. Sometimes, we want to query just one
specific device, then these prefix information show frequently will be
redundant in this situation, influencing the analysis of result set. At this
time, we can use `AS` function provided by IoTDB, assign an alias to time
series selected in query.
+
+For example:
+
+```
+select s1 as temperature, s2 as speed from root.ln.wf01.wt01;
+```
+
+The result set will be like:
+
+| Time | temperature | speed |
+| ---- | ----------- | ----- |
+| ... | ... | ... |
+
#### Other ResultSet Format
In addition, IoTDB supports two another resultset format: 'align by device'
and 'disable align'.
@@ -713,7 +729,7 @@ The 'align by device' indicates that the deviceId is
considered as a column. The
The SQL statement is:
```
-select s1,s2 from root.sg1.* GROUP BY DEVICE
+select s1,s2 from root.sg1.* ALIGN BY DEVICE
```
For more syntax description, please read SQL REFERENCE.
diff --git a/docs/UserGuide/Operation Manual/SQL Reference.md
b/docs/UserGuide/Operation Manual/SQL Reference.md
index 655be07..ff1b5c2 100644
--- a/docs/UserGuide/Operation Manual/SQL Reference.md
+++ b/docs/UserGuide/Operation Manual/SQL Reference.md
@@ -614,6 +614,53 @@ For example, "select last s1, s2 from root.sg.d1,
root.sg.d2", the query result
```
+* As Statement
+
+As statement assigns an alias to time seires queried in SELECT statement
+
+```
+You can use as statement in all query type.
+
+1. Raw data query
+select s1 as speed, s2 as temperature from root.sg.d1
+
+The result set will be like:
+| Time | speed | temperature |
+| ... | ... | .... |
+
+2. Aggregation query
+select count(s1) as s1_num, max_value(s2) as s2_max from root.sg.d1
+
+3. Down-frequence query
+select count(s1) as s1_num from root.sg.d1 group by ([100,500), 80ms)
+
+4. Align by device query
+select s1 as speed, s2 as temperature from root.sg.d1 align by device
+
+select count(s1) as s1_num, count(s2), count(s3) as s3_num from root.sg.d2
align by device
+
+5. Last query
+select last s1 as speed, s2 from root.sg.d1
+
+Rules:
+1. In addition to Align by device query,each AS statement has to corresponding
to one time series exactly.
+
+E.g. select s1 as temperature from root.sg.*
+
+At this time if `root.sg.*` includes more than one device,then an exception
will be thrown。
+
+2. In align by device query,the prefix path that each AS statement
corresponding to can includes multiple device, but the suffix path can only be
single sensor.
+
+E.g. select s1 as temperature from root.sg.*
+
+In this situation, it will be show correctly even if multiple devices are
selected.
+
+E.g. select * as temperature from root.sg.d1
+
+In this situation, it will throws an exception if * corresponds to multiple
sensors.
+
+```
+
## Database Management Statement
* Create User
diff --git a/docs/zh/UserGuide/Operation Manual/DML Data Manipulation
Language.md b/docs/zh/UserGuide/Operation Manual/DML Data Manipulation
Language.md
index 325941b..860b9e5 100644
--- a/docs/zh/UserGuide/Operation Manual/DML Data Manipulation Language.md
+++ b/docs/zh/UserGuide/Operation Manual/DML Data Manipulation Language.md
@@ -753,6 +753,22 @@ select * from root.ln.wf01.wt01 limit 10 offset 100 slimit
2 soffset 0
<center><img style="width:100%; max-width:800px; max-height:600px;
margin-left:auto; margin-right:auto; display:block;"
src="https://user-images.githubusercontent.com/13203019/51577879-64984680-1ef6-11e9-9d7b-57dd60fab60e.jpg"></center>
+### 使用别名
+
+由于 IoTDB
独特的数据模型,在每个传感器前都附带有设备等诸多额外信息。有时,我们只针对某个具体设备查询,而这些前缀信息频繁显示造成了冗余,影响了结果集的显示与分析。这时我们可以使用
IoTDB 提供的 AS 函数,将查询中出现的时间序列给定一个别名。
+
+例如:
+
+```
+select s1 as temperature, s2 as speed from root.ln.wf01.wt01;
+```
+
+则结果集将显示为:
+
+| Time | temperature | speed |
+| ---- | ----------- | ----- |
+| ... | ... | ... |
+
#### 其他结果集格式
此外,IoTDB支持两种其他结果集格式:“按设备对齐”和“禁用对齐”。
@@ -762,7 +778,7 @@ select * from root.ln.wf01.wt01 limit 10 offset 100 slimit
2 soffset 0
SQL语句是:
```
-select s1,s2 from root.sg1.* GROUP BY DEVICE
+select s1,s2 from root.sg1.* ALIGN BY DEVICE
```
diff --git a/docs/zh/UserGuide/Operation Manual/SQL Reference.md
b/docs/zh/UserGuide/Operation Manual/SQL Reference.md
index 84dd478..b7a4c4b 100644
--- a/docs/zh/UserGuide/Operation Manual/SQL Reference.md
+++ b/docs/zh/UserGuide/Operation Manual/SQL Reference.md
@@ -603,6 +603,53 @@ Eg. SELECT LAST s1 FROM root.sg.d1, root.sg.d2
```
+* As 语句
+
+As 语句为 SELECT 语句中出现的时间序列规定一个别名
+
+```
+在每个查询中都可以使用 As 语句来规定时间序列的别名。
+
+1. 原始数据查询:
+select s1 as speed, s2 as temperature from root.sg.d1
+
+结果集将显示为:
+| Time | speed | temperature |
+| ... | ... | .... |
+
+2. 聚合查询
+select count(s1) as s1_num, max_value(s2) as s2_max from root.sg.d1
+
+3. 降频聚合查询
+select count(s1) as s1_num from root.sg.d1 group by ([100,500), 80ms)
+
+4. 按设备对齐查询
+select s1 as speed, s2 as temperature from root.sg.d1 align by device
+
+select count(s1) as s1_num, count(s2), count(s3) as s3_num from root.sg.d2
align by device
+
+5. Last 查询
+select last s1 as speed, s2 from root.sg.d1
+
+规则:
+1. 除按设备对齐查询外,每一个 AS 语句必须唯一对应一个时间序列。
+
+E.g. select s1 as temperature from root.sg.*
+
+此时如果存储组 root.sg.* 中含有多个设备,则会抛出异常。
+
+2. 按设备对齐查询中,每个 AS 语句对应的前缀路径可以含多个设备,而后缀路径不能含多个传感器。
+
+E.g. select s1 as temperature from root.sg.*
+
+这种情况即使有多个设备,也可以正常显示。
+
+E.g. select * as temperature from root.sg.d1
+
+这种情况如果 * 匹配多个传感器,则无法正常显示。
+
+```
+
## 数据库管理语句
* 创建用户
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/MTree.java
b/server/src/main/java/org/apache/iotdb/db/metadata/MTree.java
index c6dfc2c..0a044e3 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/MTree.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/MTree.java
@@ -585,7 +585,7 @@ public class MTree implements Serializable {
List<PartialPath> paths = new ArrayList<>();
for (Pair<PartialPath, String[]> p : res) {
if (prePath.getMeasurement().equals(p.right[0])) {
- p.left.setAlias(p.right[0]);
+ p.left.setMeasurementAlias(p.right[0]);
}
paths.add(p.left);
}
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/PartialPath.java
b/server/src/main/java/org/apache/iotdb/db/metadata/PartialPath.java
index 59fe393..444795c 100755
--- a/server/src/main/java/org/apache/iotdb/db/metadata/PartialPath.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/PartialPath.java
@@ -31,7 +31,10 @@ import org.apache.iotdb.tsfile.read.common.Path;
public class PartialPath extends Path implements Comparable<Path> {
private String[] nodes;
- private String alias;
+ // alias of measurement
+ private String measurementAlias = null;
+ // alias of time series used in SELECT AS
+ private String tsAlias = null;
public PartialPath(String path) throws IllegalPathException {
this.nodes = MetaUtils.splitPathToDetachedPath(path);
@@ -144,13 +147,23 @@ public class PartialPath extends Path implements
Comparable<Path> {
}
}
- public void setAlias(String alias) {
- this.alias = alias;
+ public String getMeasurementAlias() {
+ return measurementAlias;
+ }
+
+ public void setMeasurementAlias(String measurementAlias) {
this.measurementAlias = measurementAlias; }
+
+ public String getTsAlias() {
+ return tsAlias;
+ }
+
+ public void setTsAlias(String tsAlias) {
+ this.tsAlias = tsAlias;
}
@Override
public String getFullPathWithAlias() {
- return getDevice() + IoTDBConstant.PATH_SEPARATOR + alias;
+ return getDevice() + IoTDBConstant.PATH_SEPARATOR + measurementAlias;
}
@Override
@@ -159,10 +172,6 @@ public class PartialPath extends Path implements
Comparable<Path> {
return this.getFullPath().compareTo(partialPath.getFullPath());
}
- public String getAlias() {
- return alias;
- }
-
public boolean startsWith(String[] otherNodes) {
for (int i = 0; i < otherNodes.length; i++) {
if (!nodes[i].equals(otherNodes[i])) {
diff --git
a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/AlignByDevicePlan.java
b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/AlignByDevicePlan.java
index 1499f92..b5759f7 100644
---
a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/AlignByDevicePlan.java
+++
b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/AlignByDevicePlan.java
@@ -29,6 +29,7 @@ import org.apache.iotdb.tsfile.read.expression.IExpression;
public class AlignByDevicePlan extends QueryPlan {
private List<String> measurements; // to record result measurement columns,
e.g. temperature, status, speed
+ private Map<String, String> measurementAliasMap; // select s1, s2 as speed
from root, then s2 -> speed
// to check data type consistency for the same name sensor of different
devices
private List<PartialPath> devices;
// to record the datatype of the column in the result set
@@ -57,6 +58,15 @@ public class AlignByDevicePlan extends QueryPlan {
return measurements;
}
+ public void setMeasurementAliasMap(
+ Map<String, String> measurementAliasMap) {
+ this.measurementAliasMap = measurementAliasMap;
+ }
+
+ public Map<String, String> getMeasurementAliasMap() {
+ return measurementAliasMap;
+ }
+
public void setDevices(List<PartialPath> devices) {
this.devices = devices;
}
diff --git
a/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java
b/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java
index 21cb7fd..121c51b 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java
@@ -78,6 +78,8 @@ import
org.apache.iotdb.db.qp.strategy.SqlBaseParser.AliasContext;
import
org.apache.iotdb.db.qp.strategy.SqlBaseParser.AlignByDeviceClauseContext;
import org.apache.iotdb.db.qp.strategy.SqlBaseParser.AlterUserContext;
import org.apache.iotdb.db.qp.strategy.SqlBaseParser.AndExpressionContext;
+import org.apache.iotdb.db.qp.strategy.SqlBaseParser.AsClauseContext;
+import org.apache.iotdb.db.qp.strategy.SqlBaseParser.AsElementContext;
import org.apache.iotdb.db.qp.strategy.SqlBaseParser.AttributeClauseContext;
import org.apache.iotdb.db.qp.strategy.SqlBaseParser.AttributeClausesContext;
import org.apache.iotdb.db.qp.strategy.SqlBaseParser.ConstantContext;
@@ -99,6 +101,8 @@ import
org.apache.iotdb.db.qp.strategy.SqlBaseParser.FlushContext;
import org.apache.iotdb.db.qp.strategy.SqlBaseParser.FromClauseContext;
import org.apache.iotdb.db.qp.strategy.SqlBaseParser.FullMergeContext;
import org.apache.iotdb.db.qp.strategy.SqlBaseParser.FullPathContext;
+import org.apache.iotdb.db.qp.strategy.SqlBaseParser.FunctionAsClauseContext;
+import org.apache.iotdb.db.qp.strategy.SqlBaseParser.FunctionAsElementContext;
import org.apache.iotdb.db.qp.strategy.SqlBaseParser.FunctionCallContext;
import org.apache.iotdb.db.qp.strategy.SqlBaseParser.FunctionElementContext;
import org.apache.iotdb.db.qp.strategy.SqlBaseParser.GrantRoleContext;
@@ -140,7 +144,6 @@ import
org.apache.iotdb.db.qp.strategy.SqlBaseParser.RevokeRoleFromUserContext;
import org.apache.iotdb.db.qp.strategy.SqlBaseParser.RevokeUserContext;
import
org.apache.iotdb.db.qp.strategy.SqlBaseParser.RevokeWatermarkEmbeddingContext;
import org.apache.iotdb.db.qp.strategy.SqlBaseParser.RootOrIdContext;
-import org.apache.iotdb.db.qp.strategy.SqlBaseParser.SelectConstElementContext;
import org.apache.iotdb.db.qp.strategy.SqlBaseParser.SelectElementContext;
import org.apache.iotdb.db.qp.strategy.SqlBaseParser.SelectStatementContext;
import org.apache.iotdb.db.qp.strategy.SqlBaseParser.SetColContext;
@@ -1228,14 +1231,6 @@ public class LogicalGenerator extends
SqlBaseBaseListener {
}
@Override
- public void enterSelectConstElement(SelectConstElementContext ctx) {
- super.enterSelectConstElement(ctx);
- operatorType = SQLConstant.TOK_QUERY;
- queryOp = new QueryOperator(SQLConstant.TOK_QUERY);
- initializedOperator = queryOp;
- }
-
- @Override
public void enterFromClause(FromClauseContext ctx) {
super.enterFromClause(ctx);
FromOperator fromOp = new FromOperator(SQLConstant.TOK_FROM);
@@ -1277,11 +1272,49 @@ public class LogicalGenerator extends
SqlBaseBaseListener {
selectOp = new SelectOperator(SQLConstant.TOK_SELECT);
selectOp.setLastQuery();
LastClauseContext lastClauseContext = ctx.lastClause();
- List<SuffixPathContext> suffixPaths = lastClauseContext.suffixPath();
- for (SuffixPathContext suffixPath : suffixPaths) {
- PartialPath path = parseSuffixPath(suffixPath);
+ if (lastClauseContext.asClause().size() != 0) {
+ parseAsClause(lastClauseContext.asClause());
+ } else {
+ List<SuffixPathContext> suffixPaths = lastClauseContext.suffixPath();
+ for (SuffixPathContext suffixPath : suffixPaths) {
+ PartialPath path = parseSuffixPath(suffixPath);
+ selectOp.addSelectPath(path);
+ }
+ }
+ queryOp.setSelectOperator(selectOp);
+ }
+
+ @Override
+ public void enterAsElement(AsElementContext ctx) {
+ super.enterAsElement(ctx);
+ selectOp = new SelectOperator(SQLConstant.TOK_SELECT);
+ parseAsClause(ctx.asClause());
+ queryOp.setSelectOperator(selectOp);
+ }
+
+ public void parseAsClause(List<AsClauseContext> asClauseContexts) {
+ for (AsClauseContext asClauseContext : asClauseContexts) {
+ PartialPath path = parseSuffixPath(asClauseContext.suffixPath());
+ if (asClauseContext.ID() != null) {
+ path.setTsAlias(asClauseContext.ID().toString());
+ }
selectOp.addSelectPath(path);
}
+ }
+
+ @Override
+ public void enterFunctionAsElement(FunctionAsElementContext ctx) {
+ super.enterFunctionAsElement(ctx);
+ selectOp = new SelectOperator(SQLConstant.TOK_SELECT);
+ List<FunctionAsClauseContext> functionAsClauseContexts =
ctx.functionAsClause();
+ for (FunctionAsClauseContext functionAsClauseContext :
functionAsClauseContexts) {
+ FunctionCallContext functionCallContext =
functionAsClauseContext.functionCall();
+ PartialPath path = parseSuffixPath(functionCallContext.suffixPath());
+ if (functionAsClauseContext.ID() != null) {
+ path.setTsAlias(functionAsClauseContext.ID().toString());
+ }
+ selectOp.addClusterPath(path,
functionCallContext.functionName().getText());
+ }
queryOp.setSelectOperator(selectOp);
}
diff --git
a/server/src/main/java/org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java
b/server/src/main/java/org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java
index 36fbd4d..08b3f30 100644
---
a/server/src/main/java/org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java
+++
b/server/src/main/java/org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java
@@ -422,6 +422,7 @@ public class PhysicalGenerator {
// to record result measurement columns
List<String> measurements = new ArrayList<>();
+ Map<String, String> measurementAliasMap = new HashMap<>();
// to check the same measurement of different devices having the same
datatype
// record the data type of each column of result set
Map<String, TSDataType> columnDataTypeMap = new HashMap<>();
@@ -450,6 +451,20 @@ public class PhysicalGenerator {
try {
// remove stars in SELECT to get actual paths
List<PartialPath> actualPaths = getMatchedTimeseries(fullPath);
+ if (suffixPath.getTsAlias() != null) {
+ if (actualPaths.size() == 1) {
+ String columnName = actualPaths.get(0).getMeasurement();
+ if (originAggregations != null &&
!originAggregations.isEmpty()) {
+ measurementAliasMap.put(originAggregations.get(i) + "(" +
columnName + ")", suffixPath.getTsAlias());
+ } else {
+ measurementAliasMap.put(columnName, suffixPath.getTsAlias());
+ }
+ } else if (actualPaths.size() >= 2) {
+ throw new QueryProcessException(
+ "alias '" + suffixPath.getTsAlias() + "' can only be
matched with one time series");
+ }
+ }
+
// for actual non exist path
if (originAggregations != null && actualPaths.isEmpty() &&
originAggregations
.isEmpty()) {
@@ -506,6 +521,7 @@ public class PhysicalGenerator {
|| measurementTypeMap.get(measurementChecked) !=
MeasurementType.Exist) {
measurementTypeMap.put(measurementChecked,
MeasurementType.Exist);
}
+
// update paths
paths.add(path);
}
@@ -537,6 +553,7 @@ public class PhysicalGenerator {
// assigns to alignByDevicePlan
alignByDevicePlan.setMeasurements(measurements);
+ alignByDevicePlan.setMeasurementAliasMap(measurementAliasMap);
alignByDevicePlan.setDevices(devices);
alignByDevicePlan.setColumnDataTypeMap(columnDataTypeMap);
alignByDevicePlan.setMeasurementTypeMap(measurementTypeMap);
@@ -668,11 +685,9 @@ public class PhysicalGenerator {
if (queryPlan instanceof LastQueryPlan) {
for (int i = 0; i < paths.size(); i++) {
PartialPath path = paths.get(i);
- String column;
- if (path.getAlias() != null) {
- column = path.getFullPathWithAlias();
- } else {
- column = path.getFullPath();
+ String column = path.getTsAlias();
+ if (column == null) {
+ column = path.getMeasurementAlias() != null ?
path.getFullPathWithAlias() : path.toString();
}
if (!columnSet.contains(column)) {
TSDataType seriesType = dataTypes.get(i);
@@ -693,14 +708,13 @@ public class PhysicalGenerator {
int index = 0;
for (Pair<PartialPath, Integer> indexedPath : indexedPaths) {
- String column;
- if (indexedPath.left.getAlias() != null) {
- column = indexedPath.left.getFullPathWithAlias();
- } else {
- column = indexedPath.left.getFullPath();
- }
- if (queryPlan instanceof AggregationPlan) {
- column = queryPlan.getAggregations().get(indexedPath.right) + "(" +
column + ")";
+ String column = indexedPath.left.getTsAlias();
+ if (column == null) {
+ column = indexedPath.left.getMeasurementAlias() != null ?
indexedPath.left.getFullPathWithAlias()
+ : indexedPath.left.toString();
+ if (queryPlan instanceof AggregationPlan) {
+ column = queryPlan.getAggregations().get(indexedPath.right) + "(" +
column + ")";
+ }
}
if (!columnSet.contains(column)) {
TSDataType seriesType = dataTypes.get(indexedPath.right);
diff --git
a/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java
b/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java
index 565d04b..05b66de 100644
---
a/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java
+++
b/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java
@@ -18,22 +18,27 @@
*/
package org.apache.iotdb.db.qp.strategy.optimizer;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import org.apache.iotdb.db.exception.metadata.MetadataException;
import org.apache.iotdb.db.exception.query.LogicalOptimizeException;
import org.apache.iotdb.db.exception.runtime.SQLParserException;
import org.apache.iotdb.db.metadata.PartialPath;
import org.apache.iotdb.db.qp.constant.SQLConstant;
import org.apache.iotdb.db.qp.logical.Operator;
-import org.apache.iotdb.db.qp.logical.crud.*;
+import org.apache.iotdb.db.qp.logical.crud.BasicFunctionOperator;
+import org.apache.iotdb.db.qp.logical.crud.FilterOperator;
+import org.apache.iotdb.db.qp.logical.crud.FromOperator;
+import org.apache.iotdb.db.qp.logical.crud.FunctionOperator;
+import org.apache.iotdb.db.qp.logical.crud.QueryOperator;
+import org.apache.iotdb.db.qp.logical.crud.SFWOperator;
+import org.apache.iotdb.db.qp.logical.crud.SelectOperator;
import org.apache.iotdb.db.service.IoTDB;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
/**
* concat paths in select and from clause.
*/
@@ -80,7 +85,8 @@ public class ConcatPathOptimizer implements ILogicalOptimizer
{
boolean isAlignByDevice = false;
if (operator instanceof QueryOperator) {
- if (!((QueryOperator) operator).isAlignByDevice() || ((QueryOperator)
operator).isLastQuery()) {
+ if (!((QueryOperator) operator).isAlignByDevice() || ((QueryOperator)
operator)
+ .isLastQuery()) {
concatSelect(prefixPaths, select); // concat and remove star
if (((QueryOperator) operator).hasSlimit()) {
@@ -94,9 +100,9 @@ public class ConcatPathOptimizer implements
ILogicalOptimizer {
String device = path.getDevice();
if (!device.isEmpty()) {
throw new LogicalOptimizeException(
- "The paths of the SELECT clause can only be single level.
In other words, "
- + "the paths of the SELECT clause can only be
measurements or STAR, without DOT."
- + " For more details please refer to the SQL
document.");
+ "The paths of the SELECT clause can only be single level. In
other words, "
+ + "the paths of the SELECT clause can only be measurements
or STAR, without DOT."
+ + " For more details please refer to the SQL document.");
}
}
// ALIGN_BY_DEVICE leaves the 1) concat, 2) remove star, 3) slimit
tasks to the next phase,
@@ -110,7 +116,7 @@ public class ConcatPathOptimizer implements
ILogicalOptimizer {
if (filter == null) {
return operator;
}
- if(!isAlignByDevice){
+ if (!isAlignByDevice) {
sfwOperator.setFilterOperator(concatFilter(prefixPaths, filter,
filterPaths));
}
sfwOperator.getFilterOperator().setPathSet(filterPaths);
@@ -164,7 +170,11 @@ public class ConcatPathOptimizer implements
ILogicalOptimizer {
// selectPath cannot start with ROOT, which is guaranteed by TSParser
PartialPath selectPath = suffixPaths.get(i);
for (PartialPath fromPath : fromPaths) {
- allPaths.add(fromPath.concatPath(selectPath));
+ PartialPath fullPath = fromPath.concatPath(selectPath);
+ if (selectPath.getTsAlias() != null) {
+ fullPath.setTsAlias(selectPath.getTsAlias());
+ }
+ allPaths.add(fullPath);
extendListSafely(originAggregations, i, afterConcatAggregations);
}
}
@@ -176,7 +186,7 @@ public class ConcatPathOptimizer implements
ILogicalOptimizer {
* Make 'SLIMIT&SOFFSET' take effect by trimming the suffixList and
aggregations of the
* selectOperator.
*
- * @param seriesLimit is ensured to be positive integer
+ * @param seriesLimit is ensured to be positive integer
* @param seriesOffset is ensured to be non-negative integer
*/
private void slimitTrim(SelectOperator select, int seriesLimit, int
seriesOffset)
@@ -297,6 +307,14 @@ public class ConcatPathOptimizer implements
ILogicalOptimizer {
for (int i = 0; i < paths.size(); i++) {
try {
List<PartialPath> actualPaths = removeWildcard(paths.get(i));
+ if (paths.get(i).getTsAlias() != null) {
+ if (actualPaths.size() == 1) {
+ actualPaths.get(0).setTsAlias(paths.get(i).getTsAlias());
+ } else if (actualPaths.size() >= 2) {
+ throw new LogicalOptimizeException(
+ "alias '" + paths.get(i).getTsAlias() + "' can only be matched
with one time series");
+ }
+ }
for (PartialPath actualPath : actualPaths) {
retPaths.add(actualPath);
if (afterConcatAggregations != null &&
!afterConcatAggregations.isEmpty()) {
diff --git
a/server/src/main/java/org/apache/iotdb/db/query/executor/LastQueryExecutor.java
b/server/src/main/java/org/apache/iotdb/db/query/executor/LastQueryExecutor.java
index b6a7039..b935536 100644
---
a/server/src/main/java/org/apache/iotdb/db/query/executor/LastQueryExecutor.java
+++
b/server/src/main/java/org/apache/iotdb/db/query/executor/LastQueryExecutor.java
@@ -90,10 +90,14 @@ public class LastQueryExecutor {
if (lastTimeValuePair.getValue() != null) {
RowRecord resultRecord = new
RowRecord(lastTimeValuePair.getTimestamp());
Field pathField = new Field(TSDataType.TEXT);
- if (selectedSeries.get(i).getAlias() != null) {
- pathField.setBinaryV(new
Binary(selectedSeries.get(i).getFullPathWithAlias()));
+ if (selectedSeries.get(i).getTsAlias() != null) {
+ pathField.setBinaryV(new Binary(selectedSeries.get(i).getTsAlias()));
} else {
- pathField.setBinaryV(new
Binary(selectedSeries.get(i).getFullPath()));
+ if (selectedSeries.get(i).getMeasurementAlias() != null) {
+ pathField.setBinaryV(new
Binary(selectedSeries.get(i).getFullPathWithAlias()));
+ } else {
+ pathField.setBinaryV(new
Binary(selectedSeries.get(i).getFullPath()));
+ }
}
resultRecord.addField(pathField);
diff --git
a/server/src/main/java/org/apache/iotdb/db/service/TSServiceImpl.java
b/server/src/main/java/org/apache/iotdb/db/service/TSServiceImpl.java
index 3c8e4d0..9108b55 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/TSServiceImpl.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/TSServiceImpl.java
@@ -346,9 +346,7 @@ public class TSServiceImpl implements TSIService.Iface,
ServerContext {
status = RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS);
break;
case "COLUMN":
- List<TSDataType> dataTypes =
- getSeriesTypesByString(Collections.singletonList(new
PartialPath(req.getColumnPath())), null);
- resp.setDataType(dataTypes.get(0).toString());
+ resp.setDataType(getSeriesTypeByPath(new
PartialPath(req.getColumnPath())).toString());
status = RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS);
break;
case "ALL_COLUMNS":
@@ -767,18 +765,18 @@ public class TSServiceImpl implements TSIService.Iface,
ServerContext {
// Restore column header of aggregate to func(column_name), only
// support single aggregate function for now
List<PartialPath> paths = plan.getPaths();
- List<TSDataType> seriesTypes;
+ List<TSDataType> seriesTypes = new ArrayList<>();
switch (plan.getOperatorType()) {
case QUERY:
case FILL:
for (PartialPath path : paths) {
- if (path.getAlias() != null) {
- respColumns.add(path.getFullPathWithAlias());
- } else {
- respColumns.add(path.getFullPath());
+ String column = path.getTsAlias();
+ if (column == null) {
+ column = path.getMeasurementAlias() != null ?
path.getFullPathWithAlias() : path.getFullPath();
}
+ respColumns.add(column);
+ seriesTypes.add(getSeriesTypeByPath(path));
}
- seriesTypes = getSeriesTypesByString(paths, null);
break;
case AGGREGATION:
case GROUPBYTIME:
@@ -790,13 +788,16 @@ public class TSServiceImpl implements TSIService.Iface,
ServerContext {
}
}
for (int i = 0; i < paths.size(); i++) {
- if (paths.get(i).getAlias() != null) {
- respColumns.add(aggregations.get(i) + "(" +
paths.get(i).getFullPathWithAlias() + ")");
- } else {
- respColumns.add(aggregations.get(i) + "(" +
paths.get(i).getFullPath() + ")");
+ PartialPath path = paths.get(i);
+ String column = path.getTsAlias();
+ if (column == null) {
+ column = path.getMeasurementAlias() != null
+ ? aggregations.get(i) + "(" +
paths.get(i).getFullPathWithAlias() + ")"
+ : aggregations.get(i) + "(" + paths.get(i).getFullPath() + ")";
}
+ respColumns.add(column);
}
- seriesTypes = getSeriesTypesByPath(paths, aggregations);
+ seriesTypes = getSeriesTypesByPaths(paths, aggregations);
break;
default:
throw new TException("unsupported query type: " +
plan.getOperatorType());
@@ -822,6 +823,7 @@ public class TSServiceImpl implements TSIService.Iface,
ServerContext {
// build column header with constant and non exist column and deduplication
List<String> measurements = plan.getMeasurements();
+ Map<String, String> measurementAliasMap = plan.getMeasurementAliasMap();
Map<String, MeasurementType> measurementTypeMap =
plan.getMeasurementTypeMap();
for (String measurement : measurements) {
TSDataType type = null;
@@ -833,7 +835,7 @@ public class TSServiceImpl implements TSIService.Iface,
ServerContext {
case Constant:
type = TSDataType.TEXT;
}
- respColumns.add(measurement);
+ respColumns.add(measurementAliasMap.getOrDefault(measurement,
measurement));
columnTypes.add(type.toString());
if (!deduplicatedMeasurements.contains(measurement)) {
@@ -1608,13 +1610,13 @@ public class TSServiceImpl implements TSIService.Iface,
ServerContext {
return QueryResourceManager.getInstance().assignQueryId(isDataQuery);
}
- protected List<TSDataType> getSeriesTypesByPath(List<PartialPath> paths,
List<String> aggregations)
+ protected List<TSDataType> getSeriesTypesByPaths(List<PartialPath> paths,
List<String> aggregations)
throws MetadataException {
- return SchemaUtils.getSeriesTypesByPath(paths, aggregations);
+ return SchemaUtils.getSeriesTypesByPaths(paths, aggregations);
}
- protected List<TSDataType> getSeriesTypesByString(List<PartialPath> paths,
String aggregation)
- throws MetadataException {
- return SchemaUtils.getSeriesTypesByString(paths, aggregation);
+
+ protected TSDataType getSeriesTypeByPath(PartialPath path) throws
MetadataException {
+ return SchemaUtils.getSeriesTypeByPath(path);
}
}
diff --git a/server/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java
b/server/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java
index 4cc8162..3a411fb 100644
--- a/server/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java
+++ b/server/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java
@@ -100,7 +100,7 @@ public class SchemaUtils {
}
/**
- * @param paths time series paths
+ * @param paths time series paths
* @param aggregation aggregation function, may be null
* @return The data type of aggregation or (data type of paths if
aggregation is null)
*/
@@ -117,7 +117,11 @@ public class SchemaUtils {
return dataTypes;
}
- public static List<TSDataType> getSeriesTypesByPath(List<PartialPath> paths,
+ public static TSDataType getSeriesTypeByPath(PartialPath path) throws
MetadataException {
+ return IoTDB.metaManager.getSeriesType(path);
+ }
+
+ public static List<TSDataType> getSeriesTypesByPaths(List<PartialPath> paths,
List<String> aggregations) throws MetadataException {
List<TSDataType> tsDataTypes = new ArrayList<>();
for (int i = 0; i < paths.size(); i++) {
@@ -160,8 +164,9 @@ public class SchemaUtils {
public static void checkDataTypeWithEncoding(TSDataType dataType, TSEncoding
encoding)
throws MetadataException {
- if(!schemaChecker.get(dataType).contains(encoding)) {
- throw new MetadataException(String.format("encoding %s does not support
%s", dataType.toString(), encoding.toString()));
+ if (!schemaChecker.get(dataType).contains(encoding)) {
+ throw new MetadataException(String
+ .format("encoding %s does not support %s", dataType.toString(),
encoding.toString()));
}
}
}
diff --git
a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBAsIT.java
b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBAsIT.java
new file mode 100644
index 0000000..4236f5a
--- /dev/null
+++ b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBAsIT.java
@@ -0,0 +1,603 @@
+/*
+ * 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.integration;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.Statement;
+import org.apache.iotdb.db.utils.EnvironmentUtils;
+import org.apache.iotdb.jdbc.Config;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class IoTDBAsIT {
+
+ private static String[] sqls = new String[]{
+ "SET STORAGE GROUP TO root.sg",
+ "CREATE TIMESERIES root.sg.d1.s1 WITH DATATYPE=FLOAT, ENCODING=RLE",
+ "CREATE TIMESERIES root.sg.d1.s2 WITH DATATYPE=FLOAT, ENCODING=RLE",
+ "CREATE TIMESERIES root.sg.d2.s1 WITH DATATYPE=FLOAT, ENCODING=RLE",
+ "CREATE TIMESERIES root.sg.d2.s2 WITH DATATYPE=FLOAT, ENCODING=RLE",
+
+ "CREATE TIMESERIES root.sg.d2.s3 WITH DATATYPE=FLOAT, ENCODING=RLE",
+
+ "INSERT INTO root.sg.d1(timestamp,s1,s2) values(100, 10.1, 20.7)",
+ "INSERT INTO root.sg.d1(timestamp,s1,s2) values(200, 15.2, 22.9)",
+ "INSERT INTO root.sg.d1(timestamp,s1,s2) values(300, 30.3, 25.1)",
+ "INSERT INTO root.sg.d1(timestamp,s1,s2) values(400, 50.4, 28.3)",
+
+ "INSERT INTO root.sg.d2(timestamp,s1,s2,s3) values(100, 11.1, 20.2,
80.0)",
+ "INSERT INTO root.sg.d2(timestamp,s1,s2,s3) values(200, 20.2, 21.8,
81.0)",
+ "INSERT INTO root.sg.d2(timestamp,s1,s2,s3) values(300, 45.3, 23.4,
82.0)",
+ "INSERT INTO root.sg.d2(timestamp,s1,s2,s3) values(400, 73.4, 26.3,
83.0)"
+ };
+
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ EnvironmentUtils.closeStatMonitor();
+ EnvironmentUtils.envSetUp();
+
+ insertData();
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ EnvironmentUtils.cleanEnv();
+ }
+
+ private static void insertData() throws ClassNotFoundException {
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ try (Connection connection = DriverManager
+ .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+
+ for (String sql : sqls) {
+ statement.execute(sql);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void selectWithAsTest() throws ClassNotFoundException {
+ String[] retArray = new String[]{
+ "100,10.1,20.7,",
+ "200,15.2,22.9,",
+ "300,30.3,25.1,",
+ "400,50.4,28.3,"
+ };
+
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ try (Connection connection = DriverManager
+ .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+ boolean hasResultSet = statement
+ .execute("select s1 as speed, s2 as temperature from root.sg.d1");
+ Assert.assertTrue(hasResultSet);
+
+ try (ResultSet resultSet = statement.getResultSet()) {
+ ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+ StringBuilder header = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ header.append(resultSetMetaData.getColumnName(i)).append(",");
+ }
+ assertEquals("Time,speed,temperature,", header.toString());
+
+ int cnt = 0;
+ while (resultSet.next()) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ builder.append(resultSet.getString(i)).append(",");
+ }
+ assertEquals(retArray[cnt], builder.toString());
+ cnt++;
+ }
+ assertEquals(retArray.length, cnt);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void selectWithAsMixedTest() throws ClassNotFoundException {
+ String[] retArray = new String[]{
+ "100,10.1,20.7,",
+ "200,15.2,22.9,",
+ "300,30.3,25.1,",
+ "400,50.4,28.3,"
+ };
+
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ try (Connection connection = DriverManager
+ .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+ boolean hasResultSet = statement.execute("select s1 as speed, s2 from
root.sg.d1");
+ Assert.assertTrue(hasResultSet);
+
+ try (ResultSet resultSet = statement.getResultSet()) {
+ ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+ StringBuilder header = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ header.append(resultSetMetaData.getColumnName(i)).append(",");
+ }
+ assertEquals("Time,speed,root.sg.d1.s2,", header.toString());
+
+ int cnt = 0;
+ while (resultSet.next()) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ builder.append(resultSet.getString(i)).append(",");
+ }
+ assertEquals(retArray[cnt], builder.toString());
+ cnt++;
+ }
+ assertEquals(retArray.length, cnt);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void selectWithAsFailTest() throws ClassNotFoundException {
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ try (Connection connection = DriverManager
+ .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+ // root.sg.*.s1 matches root.sg.d1.s1 and root.sg.d2.s1 both
+ statement.execute("select s1 as speed from root.sg.*");
+ fail();
+ } catch (Exception e) {
+ Assert.assertTrue(
+ e.getMessage().contains("alias 'speed' can only be matched with one
time series"));
+ }
+ }
+
+ @Test
+ public void selectWithAsSingleTest() throws ClassNotFoundException {
+ String[] retArray = new String[]{
+ "100,80.0,",
+ "200,81.0,",
+ "300,82.0,",
+ "400,83.0,"
+ };
+
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ try (Connection connection = DriverManager
+ .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+ // root.sg.*.s3 matches root.sg.d2.s3 exactly
+ boolean hasResultSet = statement.execute("select s3 as power from
root.sg.*");
+ Assert.assertTrue(hasResultSet);
+
+ try (ResultSet resultSet = statement.getResultSet()) {
+ ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+ StringBuilder header = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ header.append(resultSetMetaData.getColumnName(i)).append(",");
+ }
+ assertEquals("Time,power,", header.toString());
+
+ int cnt = 0;
+ while (resultSet.next()) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ builder.append(resultSet.getString(i)).append(",");
+ }
+ assertEquals(retArray[cnt], builder.toString());
+ cnt++;
+ }
+ assertEquals(retArray.length, cnt);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void aggregationWithAsTest() throws ClassNotFoundException {
+ String[] retArray = new String[]{
+ "4,28.3,",
+ };
+
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ try (Connection connection = DriverManager
+ .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+ boolean hasResultSet = statement
+ .execute("select count(s1) as s1_num, max_value(s2) as s2_max from
root.sg.d1");
+ Assert.assertTrue(hasResultSet);
+
+ try (ResultSet resultSet = statement.getResultSet()) {
+ ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+ StringBuilder header = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ header.append(resultSetMetaData.getColumnName(i)).append(",");
+ }
+ assertEquals("s1_num,s2_max,", header.toString());
+
+ int cnt = 0;
+ while (resultSet.next()) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ builder.append(resultSet.getString(i)).append(",");
+ }
+ assertEquals(retArray[cnt], builder.toString());
+ cnt++;
+ }
+ assertEquals(retArray.length, cnt);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void aggregationWithAsFailTest() throws ClassNotFoundException {
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ try (Connection connection = DriverManager
+ .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+ // root.sg.*.s1 matches root.sg.d1.s1 and root.sg.d2.s1 both
+ statement.execute("select count(s1) as s1_num from root.sg.*");
+ fail();
+ } catch (Exception e) {
+ Assert.assertTrue(
+ e.getMessage().contains("alias 's1_num' can only be matched with one
time series"));
+ }
+ }
+
+ @Test
+ public void groupByWithAsTest() throws ClassNotFoundException {
+ String[] retArray = new String[]{
+ "100,1,",
+ "180,1,",
+ "260,1,",
+ "340,1,",
+ "420,0,",
+ };
+
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ try (Connection connection = DriverManager
+ .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+ boolean hasResultSet = statement
+ .execute("select count(s1) as s1_num from root.sg.d1 group by
([100,500), 80ms)");
+ Assert.assertTrue(hasResultSet);
+
+ try (ResultSet resultSet = statement.getResultSet()) {
+ ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+ StringBuilder header = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ header.append(resultSetMetaData.getColumnName(i)).append(",");
+ }
+ assertEquals("Time,s1_num,", header.toString());
+
+ int cnt = 0;
+ while (resultSet.next()) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ builder.append(resultSet.getString(i)).append(",");
+ }
+ assertEquals(retArray[cnt], builder.toString());
+ cnt++;
+ }
+ assertEquals(retArray.length, cnt);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void alignByDeviceWithAsTest() throws ClassNotFoundException {
+ String[] retArray = new String[]{
+ "100,root.sg.d1,10.1,20.7,",
+ "200,root.sg.d1,15.2,22.9,",
+ "300,root.sg.d1,30.3,25.1,",
+ "400,root.sg.d1,50.4,28.3,"
+ };
+
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ try (Connection connection = DriverManager
+ .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+ boolean hasResultSet = statement
+ .execute("select s1 as speed, s2 as temperature from root.sg.d1
align by device");
+ Assert.assertTrue(hasResultSet);
+
+ try (ResultSet resultSet = statement.getResultSet()) {
+ ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+ StringBuilder header = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ header.append(resultSetMetaData.getColumnName(i)).append(",");
+ }
+ assertEquals("Time,Device,speed,temperature,", header.toString());
+
+ int cnt = 0;
+ while (resultSet.next()) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ builder.append(resultSet.getString(i)).append(",");
+ }
+ assertEquals(retArray[cnt], builder.toString());
+ cnt++;
+ }
+ assertEquals(retArray.length, cnt);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void alignByDeviceWithAsMixedTest() throws ClassNotFoundException {
+ String[] retArray = new String[]{
+ "100,root.sg.d1,10.1,20.7,",
+ "200,root.sg.d1,15.2,22.9,",
+ "300,root.sg.d1,30.3,25.1,",
+ "400,root.sg.d1,50.4,28.3,",
+ "100,root.sg.d2,11.1,20.2,",
+ "200,root.sg.d2,20.2,21.8,",
+ "300,root.sg.d2,45.3,23.4,",
+ "400,root.sg.d2,73.4,26.3,"
+ };
+
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ try (Connection connection = DriverManager
+ .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+ boolean hasResultSet = statement
+ .execute("select s1 as speed, s2 from root.sg.* align by device");
+ Assert.assertTrue(hasResultSet);
+
+ try (ResultSet resultSet = statement.getResultSet()) {
+ ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+ StringBuilder header = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ header.append(resultSetMetaData.getColumnName(i)).append(",");
+ }
+ assertEquals("Time,Device,speed,s2,", header.toString());
+
+ int cnt = 0;
+ while (resultSet.next()) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ builder.append(resultSet.getString(i)).append(",");
+ }
+ assertEquals(retArray[cnt], builder.toString());
+ cnt++;
+ }
+ assertEquals(retArray.length, cnt);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void alignByDeviceWithAsFailTest() throws ClassNotFoundException {
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ try (Connection connection = DriverManager
+ .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+ // root.sg.*.s1 matches root.sg.d1.s1 and root.sg.d2.s1 both
+ statement.execute("select * as speed from root.sg.d1 align by device");
+ fail();
+ } catch (Exception e) {
+ Assert.assertTrue(
+ e.getMessage().contains("alias 'speed' can only be matched with one
time series"));
+ }
+ }
+
+ @Test
+ public void alignByDeviceWithAsDuplicatedTest() throws
ClassNotFoundException {
+ String[] retArray = new String[]{
+ "100,root.sg.d1,10.1,10.1,",
+ "200,root.sg.d1,15.2,15.2,",
+ "300,root.sg.d1,30.3,30.3,",
+ "400,root.sg.d1,50.4,50.4,"
+ };
+
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ try (Connection connection = DriverManager
+ .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+ boolean hasResultSet = statement
+ .execute("select s1 as speed, s1 from root.sg.d1 align by device");
+ Assert.assertTrue(hasResultSet);
+
+ try (ResultSet resultSet = statement.getResultSet()) {
+ ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+ StringBuilder header = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ header.append(resultSetMetaData.getColumnName(i)).append(",");
+ }
+ assertEquals("Time,Device,speed,speed,", header.toString());
+
+ int cnt = 0;
+ while (resultSet.next()) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ builder.append(resultSet.getString(i)).append(",");
+ }
+ assertEquals(retArray[cnt], builder.toString());
+ cnt++;
+ }
+ assertEquals(retArray.length, cnt);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void alignByDeviceWithAsAggregationTest() throws
ClassNotFoundException {
+ String[] retArray = new String[]{
+ "root.sg.d2,4,4,4,",
+ };
+
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ try (Connection connection = DriverManager
+ .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+ boolean hasResultSet = statement
+ .execute("select count(s1) as s1_num, count(s2), count(s3) as s3_num
from root.sg.d2 align by device");
+ Assert.assertTrue(hasResultSet);
+
+ try (ResultSet resultSet = statement.getResultSet()) {
+ ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+ StringBuilder header = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ header.append(resultSetMetaData.getColumnName(i)).append(",");
+ }
+ assertEquals("Device,s1_num,count(s2),s3_num,", header.toString());
+
+ int cnt = 0;
+ while (resultSet.next()) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ builder.append(resultSet.getString(i)).append(",");
+ }
+ assertEquals(retArray[cnt], builder.toString());
+ cnt++;
+ }
+ assertEquals(retArray.length, cnt);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void lastWithAsTest() throws ClassNotFoundException {
+ String[] retArray = new String[]{
+ "400,speed,50.4,",
+ "400,root.sg.d1.s2,28.3,"
+ };
+
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ try (Connection connection = DriverManager
+ .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+ boolean hasResultSet = statement
+ .execute("select last s1 as speed, s2 from root.sg.d1");
+ Assert.assertTrue(hasResultSet);
+
+ try (ResultSet resultSet = statement.getResultSet()) {
+ ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+ StringBuilder header = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ header.append(resultSetMetaData.getColumnName(i)).append(",");
+ }
+ assertEquals("Time,timeseries,value,", header.toString());
+
+ int cnt = 0;
+ while (resultSet.next()) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ builder.append(resultSet.getString(i)).append(",");
+ }
+ assertEquals(retArray[cnt], builder.toString());
+ cnt++;
+ }
+ assertEquals(retArray.length, cnt);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void lastWithAsDuplicatedTest() throws ClassNotFoundException {
+ String[] retArray = new String[]{
+ "400,speed,50.4,",
+ "400,root.sg.d1.s1,50.4,",
+ "400,temperature,28.3,"
+ };
+
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ try (Connection connection = DriverManager
+ .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+ boolean hasResultSet = statement
+ .execute("select last s1 as speed, s1, s2 as temperature from
root.sg.d1");
+ Assert.assertTrue(hasResultSet);
+
+ try (ResultSet resultSet = statement.getResultSet()) {
+ ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+ StringBuilder header = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ header.append(resultSetMetaData.getColumnName(i)).append(",");
+ }
+ assertEquals("Time,timeseries,value,", header.toString());
+
+ int cnt = 0;
+ while (resultSet.next()) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+ builder.append(resultSet.getString(i)).append(",");
+ }
+ assertEquals(retArray[cnt], builder.toString());
+ cnt++;
+ }
+ assertEquals(retArray.length, cnt);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void lastWithAsFailTest() throws ClassNotFoundException {
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ try (Connection connection = DriverManager
+ .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+ // root.sg.*.s1 matches root.sg.d1.s1 and root.sg.d2.s1 both
+ statement.execute("select last s1 as speed from root.sg.*");
+ fail();
+ } catch (Exception e) {
+ Assert.assertTrue(
+ e.getMessage().contains("alias 'speed' can only be matched with one
time series"));
+ }
+ }
+
+}
diff --git a/server/src/test/java/org/apache/iotdb/db/metadata/MTreeTest.java
b/server/src/test/java/org/apache/iotdb/db/metadata/MTreeTest.java
index 631aa7e..8d36d18 100644
--- a/server/src/test/java/org/apache/iotdb/db/metadata/MTreeTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/metadata/MTreeTest.java
@@ -186,9 +186,9 @@ public class MTreeTest {
List<PartialPath> result2 = root.getAllTimeseriesPathWithAlias(new
PartialPath("root.a.*.s0"));
assertEquals(2, result2.size());
assertEquals("root.a.d0.s0", result2.get(0).getFullPath());
- assertNull(result2.get(0).getAlias());
+ assertNull(result2.get(0).getMeasurementAlias());
assertEquals("root.a.d1.s0", result2.get(1).getFullPath());
- assertNull(result2.get(1).getAlias());
+ assertNull(result2.get(1).getMeasurementAlias());
result2 = root.getAllTimeseriesPathWithAlias(new
PartialPath("root.a.*.temperature"));
assertEquals(2, result2.size());
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/Path.java
b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/Path.java
index 1bba53b..c2162e4 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/Path.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/Path.java
@@ -35,7 +35,6 @@ public class Path implements Serializable, Comparable<Path> {
protected String fullPath;
private static final String ILLEGAL_PATH_ARGUMENT = "Path parameter is null";
-
public Path() {}
/**