Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
/*
* 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.it.schema;

import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant;
import org.apache.iotdb.it.env.EnvFactory;
import org.apache.iotdb.itbase.category.ClusterIT;
import org.apache.iotdb.itbase.category.LocalStandaloneIT;
import org.apache.iotdb.util.AbstractSchemaIT;

import org.junit.After;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runners.Parameterized;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

@Category({LocalStandaloneIT.class, ClusterIT.class})
public class IoTDBShowTimeseriesOrderByTimeseriesIT extends AbstractSchemaIT {

private static final List<String> BASE_TIMESERIES =
Arrays.asList(
"root.db1.devA.m1",
"root.db1.devA.m2",
"root.db1.devB.m1",
"root.db1.devB.x",
"root.db2.devA.m1",
"root.db2.devC.m0",
"root.db2.devC.m3",
"root.db3.z.m1",
"root.db3.z.m10",
"root.db3.z.m2");

public IoTDBShowTimeseriesOrderByTimeseriesIT(SchemaTestMode schemaTestMode) {
super(schemaTestMode);
}

@Parameterized.BeforeParam
public static void before() throws Exception {
setUpEnvironment();
EnvFactory.getEnv().initClusterEnvironment();
}

@Parameterized.AfterParam
public static void after() throws Exception {
EnvFactory.getEnv().cleanClusterEnvironment();
tearDownEnvironment();
}

@After
public void tearDown() throws Exception {
clearSchema();
}

private void prepareComplexSchema() throws Exception {
try (Connection connection = EnvFactory.getEnv().getConnection();
Statement statement = connection.createStatement()) {
statement.execute("CREATE DATABASE root.db1");
statement.execute("CREATE DATABASE root.db2");
statement.execute("CREATE DATABASE root.db3");

for (String ts : BASE_TIMESERIES) {
statement.execute(
String.format(
"create timeseries %s with datatype=INT32, encoding=RLE, compression=SNAPPY", ts));
}
}
}

private List<String> queryTimeseries(final String sql) throws Exception {
try (Connection connection = EnvFactory.getEnv().getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql)) {
List<String> result = new ArrayList<>();
while (resultSet.next()) {
result.add(resultSet.getString(ColumnHeaderConstant.TIMESERIES));
}
return result;
}
}

@Test
public void testOrderAscWithoutLimit() throws Exception {
prepareComplexSchema();
List<String> expected = new ArrayList<>(BASE_TIMESERIES);
Collections.sort(expected);

List<String> actual = queryTimeseries("show timeseries root.db*.** order by timeseries");
assertEquals(expected, actual);
}

@Test
public void testOrderDescWithOffsetLimit() throws Exception {
prepareComplexSchema();
List<String> expected = new ArrayList<>(BASE_TIMESERIES);
Collections.sort(expected);
Collections.reverse(expected);
expected = expected.subList(2, 6); // offset 2 limit 4

List<String> actual =
queryTimeseries("show timeseries root.db*.** order by timeseries desc offset 2 limit 4");
assertEquals(expected, actual);
}

@Test
public void testInsertThenQueryOrder() throws Exception {
prepareComplexSchema();
try (Connection connection = EnvFactory.getEnv().getConnection();
Statement statement = connection.createStatement()) {
statement.execute(
"create timeseries root.db0.devX.a with datatype=INT32, encoding=RLE, compression=SNAPPY");
}

List<String> expected = new ArrayList<>(BASE_TIMESERIES);
expected.add("root.db0.devX.a");
Collections.sort(expected);

List<String> actual = queryTimeseries("show timeseries root.db*.** order by timeseries");
assertEquals(expected, actual);
}

@Test
public void testDeleteSubtreeThenQueryOrder() throws Exception {
prepareComplexSchema();
try (Connection connection = EnvFactory.getEnv().getConnection();
Statement statement = connection.createStatement()) {
statement.execute("delete timeseries root.db2.devC.**");
}

List<String> expected = new ArrayList<>(BASE_TIMESERIES);
expected.remove("root.db2.devC.m0");
expected.remove("root.db2.devC.m3");
Collections.sort(expected);

List<String> actual = queryTimeseries("show timeseries root.db*.** order by timeseries");
assertEquals(expected, actual);
}

@Test
public void testOffsetLimitAfterDeletesAndAdds() throws Exception {
prepareComplexSchema();
try (Connection connection = EnvFactory.getEnv().getConnection();
Statement statement = connection.createStatement()) {
statement.execute("delete timeseries root.db1.devB.x");
statement.execute(
"create timeseries root.db1.devC.m0 with datatype=INT32, encoding=RLE, compression=SNAPPY");
statement.execute(
"create timeseries root.db4.devZ.z with datatype=INT32, encoding=RLE, compression=SNAPPY");
}

List<String> expected = new ArrayList<>(BASE_TIMESERIES);
expected.remove("root.db1.devB.x");
expected.add("root.db1.devC.m0");
expected.add("root.db4.devZ.z");
Collections.sort(expected);
expected = expected.subList(5, 10); // offset 5 limit 5

List<String> actual =
queryTimeseries("show timeseries root.db*.** order by timeseries offset 5 limit 5");
assertEquals(expected, actual);
}

@Test
public void testConflictWithLatest() throws Exception {
prepareComplexSchema();
try (Connection connection = EnvFactory.getEnv().getConnection();
Statement statement = connection.createStatement()) {
try (ResultSet ignored =
statement.executeQuery("show latest timeseries order by timeseries")) {
fail("Expected exception for conflict between LATEST and ORDER BY TIMESERIES");
} catch (SQLException e) {
assertTrue(
e.getMessage().toLowerCase().contains("latest")
&& e.getMessage().toLowerCase().contains("order by timeseries"));
}
}
}

@Test
public void testConflictWithTimeCondition() throws Exception {
prepareComplexSchema();
try (Connection connection = EnvFactory.getEnv().getConnection();
Statement statement = connection.createStatement()) {
try (ResultSet ignored =
statement.executeQuery("show timeseries where time > 0 order by timeseries")) {
fail("Expected exception for conflict between TIME condition and ORDER BY TIMESERIES");
} catch (SQLException e) {
assertTrue(
e.getMessage().toLowerCase().contains("time condition")
&& e.getMessage().toLowerCase().contains("order by timeseries"));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,12 @@ showDevices

// ---- Show Timeseries
showTimeseries
: SHOW LATEST? TIMESERIES prefixPath? timeseriesWhereClause? timeConditionClause? rowPaginationClause?
: SHOW LATEST? TIMESERIES prefixPath? timeseriesWhereClause? timeConditionClause? orderByTimeseriesClause? rowPaginationClause?
;

// order by timeseries for SHOW TIMESERIES
orderByTimeseriesClause
: ORDER BY TIMESERIES (ASC | DESC)?
;

// ---- Show Child Paths
Expand Down Expand Up @@ -1586,4 +1591,4 @@ subStringExpression

signedIntegerLiteral
: (PLUS|MINUS)?INTEGER_LITERAL
;
;
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ public ISchemaReader<ITimeSeriesSchemaInfo> getSchemaReader(ISchemaRegion schema
SchemaFilterFactory.and(
schemaFilter, SchemaFilterFactory.createViewTypeFilter(ViewType.VIEW)),
true,
scope));
scope,
false,
false));
} catch (MetadataException e) {
throw new SchemaExecutionException(e.getMessage(), e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public static ISchemaSource<ITimeSeriesSchemaInfo> getTimeSeriesSchemaCountSourc
Map<Integer, Template> templateMap,
PathPatternTree scope) {
return new TimeSeriesSchemaSource(
pathPattern, isPrefixMatch, 0, 0, schemaFilter, templateMap, false, scope);
pathPattern, isPrefixMatch, 0, 0, schemaFilter, templateMap, false, scope, false, false);
}

// show time series
Expand All @@ -58,9 +58,20 @@ public static ISchemaSource<ITimeSeriesSchemaInfo> getTimeSeriesSchemaScanSource
long offset,
SchemaFilter schemaFilter,
Map<Integer, Template> templateMap,
PathPatternTree scope) {
PathPatternTree scope,
boolean orderByTimeseries,
boolean orderByTimeseriesDesc) {
return new TimeSeriesSchemaSource(
pathPattern, isPrefixMatch, limit, offset, schemaFilter, templateMap, true, scope);
pathPattern,
isPrefixMatch,
limit,
offset,
schemaFilter,
templateMap,
true,
scope,
orderByTimeseries,
orderByTimeseriesDesc);
}

// count device
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public class TimeSeriesSchemaSource implements ISchemaSource<ITimeSeriesSchemaIn
private final SchemaFilter schemaFilter;
private final Map<Integer, Template> templateMap;
private final boolean needViewDetail;
private final boolean orderByTimeseries;
private final boolean orderByTimeseriesDesc;

TimeSeriesSchemaSource(
PartialPath pathPattern,
Expand All @@ -63,7 +65,9 @@ public class TimeSeriesSchemaSource implements ISchemaSource<ITimeSeriesSchemaIn
SchemaFilter schemaFilter,
Map<Integer, Template> templateMap,
boolean needViewDetail,
PathPatternTree scope) {
PathPatternTree scope,
boolean orderByTimeseries,
boolean orderByTimeseriesDesc) {
this.pathPattern = pathPattern;
this.isPrefixMatch = isPrefixMatch;
this.limit = limit;
Expand All @@ -72,6 +76,8 @@ public class TimeSeriesSchemaSource implements ISchemaSource<ITimeSeriesSchemaIn
this.templateMap = templateMap;
this.needViewDetail = needViewDetail;
this.scope = scope;
this.orderByTimeseries = orderByTimeseries;
this.orderByTimeseriesDesc = orderByTimeseriesDesc;
}

@Override
Expand All @@ -86,7 +92,9 @@ public ISchemaReader<ITimeSeriesSchemaInfo> getSchemaReader(ISchemaRegion schema
isPrefixMatch,
schemaFilter,
needViewDetail,
scope));
scope,
orderByTimeseries,
orderByTimeseriesDesc));
} catch (MetadataException e) {
throw new SchemaExecutionException(e.getMessage(), e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,22 @@ public Statement visitShowTimeseries(IoTDBSqlParser.ShowTimeseriesContext ctx) {
showTimeSeriesStatement.setTimeCondition(
parseWhereClause(ctx.timeConditionClause().whereClause()));
}

// ORDER BY TIMESERIES [ASC|DESC]
if (ctx.orderByTimeseriesClause() != null) {
if (orderByHeat) {
throw new SemanticException(
"LATEST and ORDER BY TIMESERIES cannot be used at the same time.");
}
if (ctx.timeConditionClause() != null) {
throw new SemanticException("ORDER BY TIMESERIES does not support TIME condition.");
}
Ordering ordering = Ordering.ASC;
if (ctx.orderByTimeseriesClause().DESC() != null) {
ordering = Ordering.DESC;
}
showTimeSeriesStatement.setOrderByTimeseries(true, ordering);
}
if (ctx.rowPaginationClause() != null) {
if (ctx.rowPaginationClause().limitClause() != null) {
showTimeSeriesStatement.setLimit(parseLimitClause(ctx.rowPaginationClause().limitClause()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ public LogicalPlanBuilder planFill(FillDescriptor fillDescriptor, Ordering scanO
}

public LogicalPlanBuilder planLimit(long rowLimit) {
if (rowLimit == 0) {
if (rowLimit <= 0) {
return this;
}

Expand Down Expand Up @@ -995,7 +995,9 @@ public LogicalPlanBuilder planTimeSeriesSchemaSource(
boolean orderByHeat,
boolean prefixPath,
Map<Integer, Template> templateMap,
PathPatternTree scope) {
PathPatternTree scope,
boolean orderByTimeseries,
boolean orderByTimeseriesDesc) {
this.root =
new TimeSeriesSchemaScanNode(
context.getQueryId().genPlanNodeId(),
Expand All @@ -1006,7 +1008,9 @@ public LogicalPlanBuilder planTimeSeriesSchemaSource(
orderByHeat,
prefixPath,
templateMap,
scope);
scope,
orderByTimeseries,
orderByTimeseriesDesc);
return this;
}

Expand Down
Loading