This is an automated email from the ASF dual-hosted git repository.
haonan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new 56ffbcf [ISSUE-2484] Fix creating timeseries error by using "create"
or "insert" statement (#2468)
56ffbcf is described below
commit 56ffbcf77ec59e5c3065a81a1a2086178b94cecd
Author: Al Wei <[email protected]>
AuthorDate: Wed Feb 24 09:24:40 2021 +0800
[ISSUE-2484] Fix creating timeseries error by using "create" or "insert"
statement (#2468)
Co-authored-by: weizihan0110 <[email protected]>
---
.../apache/iotdb/cluster/metadata/CMManager.java | 9 +-
.../org/apache/iotdb/db/conf/IoTDBDescriptor.java | 2 +-
.../org/apache/iotdb/db/metadata/MManager.java | 62 ++++----
.../java/org/apache/iotdb/db/metadata/MTree.java | 26 ++--
.../org/apache/iotdb/db/metadata/MetaUtils.java | 37 +++++
.../org/apache/iotdb/db/metadata/mnode/MNode.java | 49 ++++++-
.../apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java | 2 +-
.../db/integration/IoTDBAutoCreateSchemaIT.java | 106 ++++++++++++++
.../db/integration/IoTDBCreateTimeseriesIT.java | 157 +++++++++++++++++++++
.../org/apache/iotdb/db/metadata/MTreeTest.java | 27 ++++
.../apache/iotdb/db/metadata/MetaUtilsTest.java | 35 +++++
.../apache/iotdb/db/metadata/mnode/MNodeTest.java | 71 ++++++++++
12 files changed, 529 insertions(+), 54 deletions(-)
diff --git
a/cluster/src/main/java/org/apache/iotdb/cluster/metadata/CMManager.java
b/cluster/src/main/java/org/apache/iotdb/cluster/metadata/CMManager.java
index e3a58f1..c5094c8 100644
--- a/cluster/src/main/java/org/apache/iotdb/cluster/metadata/CMManager.java
+++ b/cluster/src/main/java/org/apache/iotdb/cluster/metadata/CMManager.java
@@ -1423,13 +1423,12 @@ public class CMManager extends MManager {
}
@Override
- protected MeasurementMNode getMeasurementMNode(MNode deviceMNode, String
measurement) {
- MNode child;
- child = deviceMNode.getChild(measurement);
+ public MNode getMNode(MNode deviceMNode, String measurementName) {
+ MNode child = deviceMNode.getChild(measurementName);
if (child == null) {
- child =
mRemoteMetaCache.get(deviceMNode.getPartialPath().concatNode(measurement));
+ child =
mRemoteMetaCache.get(deviceMNode.getPartialPath().concatNode(measurementName));
}
- return child != null ? (MeasurementMNode) child : null;
+ return child;
}
public List<ShowTimeSeriesResult> showLocalTimeseries(
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
index a1a4e58..f26a1b5 100644
--- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
+++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
@@ -1146,7 +1146,7 @@ public class IoTDBDescriptor {
}
/** Get default encode algorithm by data type */
- public TSEncoding getDefualtEncodingByType(TSDataType dataType) {
+ public TSEncoding getDefaultEncodingByType(TSDataType dataType) {
switch (dataType) {
case BOOLEAN:
return conf.getDefaultBooleanEncoding();
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/MManager.java
b/server/src/main/java/org/apache/iotdb/db/metadata/MManager.java
index 6c7e5de..844d023 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/MManager.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/MManager.java
@@ -470,7 +470,12 @@ public class MManager {
createTimeseries(
new CreateTimeSeriesPlan(path, dataType, encoding, compressor,
props, null, null, null));
} catch (PathAlreadyExistException | AliasAlreadyExistException e) {
- // ignore
+ if (logger.isDebugEnabled()) {
+ logger.debug(
+ "Ignore PathAlreadyExistException and AliasAlreadyExistException
when Concurrent inserting"
+ + " a non-exist time series {}",
+ path);
+ }
}
}
@@ -991,10 +996,6 @@ public class MManager {
return res;
}
- protected MeasurementMNode getMeasurementMNode(MNode deviceMNode, String
measurement) {
- return (MeasurementMNode) deviceMNode.getChild(measurement);
- }
-
public MeasurementSchema getSeriesSchema(PartialPath device, String
measurement)
throws MetadataException {
MNode node = mtree.getNodeByPath(device);
@@ -1828,28 +1829,26 @@ public class MManager {
MNode deviceMNode = getDeviceNodeWithAutoCreate(deviceId);
// 2. get schema of each measurement
+ // if do not has measurement
+ MeasurementMNode measurementMNode;
+ TSDataType dataType;
for (int i = 0; i < measurementList.length; i++) {
try {
- // if do not has measurement
- MeasurementMNode measurementMNode;
- if (!deviceMNode.hasChild(measurementList[i])) {
- // could not create it
+ MNode child = getMNode(deviceMNode, measurementList[i]);
+ if (child instanceof MeasurementMNode) {
+ measurementMNode = (MeasurementMNode) child;
+ } else if (child instanceof StorageGroupMNode) {
+ throw new PathAlreadyExistException(deviceId + PATH_SEPARATOR +
measurementList[i]);
+ } else {
if (!config.isAutoCreateSchemaEnabled()) {
- // but measurement not in MTree and cannot auto-create, try the
cache
- measurementMNode = getMeasurementMNode(deviceMNode,
measurementList[i]);
- if (measurementMNode == null) {
- throw new PathNotExistException(deviceId + PATH_SEPARATOR +
measurementList[i]);
- }
+ throw new PathNotExistException(deviceId + PATH_SEPARATOR +
measurementList[i]);
} else {
- // create it
-
- TSDataType dataType = getTypeInLoc(plan, i);
+ // child is null or child is type of MNode
+ dataType = getTypeInLoc(plan, i);
// create it, may concurrent created by multiple thread
internalCreateTimeseries(deviceId.concatNode(measurementList[i]),
dataType);
measurementMNode = (MeasurementMNode)
deviceMNode.getChild(measurementList[i]);
}
- } else {
- measurementMNode = getMeasurementMNode(deviceMNode,
measurementList[i]);
}
// check type is match
@@ -1906,24 +1905,19 @@ public class MManager {
return deviceMNode;
}
+ public MNode getMNode(MNode deviceMNode, String measurementName) {
+ return deviceMNode.getChild(measurementName);
+ }
+
/** create timeseries with ignore PathAlreadyExistException */
private void internalCreateTimeseries(PartialPath path, TSDataType dataType)
throws MetadataException {
- try {
- createTimeseries(
- path,
- dataType,
- getDefaultEncoding(dataType),
- TSFileDescriptor.getInstance().getConfig().getCompressor(),
- Collections.emptyMap());
- } catch (PathAlreadyExistException | AliasAlreadyExistException e) {
- if (logger.isDebugEnabled()) {
- logger.debug(
- "Ignore PathAlreadyExistException and AliasAlreadyExistException
when Concurrent inserting"
- + " a non-exist time series {}",
- path);
- }
- }
+ createTimeseries(
+ path,
+ dataType,
+ getDefaultEncoding(dataType),
+ TSFileDescriptor.getInstance().getConfig().getCompressor(),
+ Collections.emptyMap());
}
/** get dataType of plan, in loc measurements only support InsertRowPlan and
InsertTabletPlan */
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 f628f86..a9bc505 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
@@ -214,23 +214,33 @@ public class MTree implements Serializable {
// synchronize check and add, we need addChild and add Alias become atomic
operation
// only write on mtree will be synchronized
synchronized (this) {
- if (cur.hasChild(leafName)) {
+ MNode child = cur.getChild(leafName);
+ if (child instanceof MeasurementMNode || child instanceof
StorageGroupMNode) {
throw new PathAlreadyExistException(path.getFullPath());
}
- if (alias != null && cur.hasChild(alias)) {
- throw new AliasAlreadyExistException(path.getFullPath(), alias);
+
+ if (alias != null) {
+ MNode childByAlias = cur.getChild(alias);
+ if (childByAlias instanceof MeasurementMNode) {
+ throw new AliasAlreadyExistException(path.getFullPath(), alias);
+ }
}
- MeasurementMNode leaf =
- new MeasurementMNode(cur, leafName, alias, dataType, encoding,
compressor, props);
- cur.addChild(leafName, leaf);
+ // this measurementMNode could be a leaf or not.
+ MeasurementMNode measurementMNode =
+ new MeasurementMNode(cur, leafName, alias, dataType, encoding,
compressor, props);
+ if (child != null) {
+ cur.replaceChild(measurementMNode.getName(), measurementMNode);
+ } else {
+ cur.addChild(leafName, measurementMNode);
+ }
// link alias to LeafMNode
if (alias != null) {
- cur.addAlias(alias, leaf);
+ cur.addAlias(alias, measurementMNode);
}
- return leaf;
+ return measurementMNode;
}
}
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/MetaUtils.java
b/server/src/main/java/org/apache/iotdb/db/metadata/MetaUtils.java
index 61c8bec..7b9fe11 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/MetaUtils.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/MetaUtils.java
@@ -21,9 +21,14 @@ package org.apache.iotdb.db.metadata;
import org.apache.iotdb.db.conf.IoTDBConstant;
import org.apache.iotdb.db.exception.metadata.IllegalPathException;
import org.apache.iotdb.db.exception.metadata.MetadataException;
+import org.apache.iotdb.db.metadata.mnode.MNode;
+import org.apache.iotdb.db.utils.TestOnly;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import static org.apache.iotdb.db.conf.IoTDBConstant.PATH_WILDCARD;
@@ -100,4 +105,36 @@ public class MetaUtils {
System.arraycopy(nodeNames, 0, storageGroupNodes, 0, level + 1);
return new PartialPath(storageGroupNodes);
}
+
+ @TestOnly
+ public static List<String> getMultiFullPaths(MNode node) {
+ if (node == null) {
+ return Collections.emptyList();
+ }
+
+ List<MNode> lastNodeList = new ArrayList<>();
+ collectLastNode(node, lastNodeList);
+
+ List<String> result = new ArrayList<>();
+ for (MNode mNode : lastNodeList) {
+ result.add(mNode.getFullPath());
+ }
+
+ return result;
+ }
+
+ @TestOnly
+ public static void collectLastNode(MNode node, List<MNode> lastNodeList) {
+ if (node != null) {
+ Map<String, MNode> children = node.getChildren();
+ if (children.isEmpty()) {
+ lastNodeList.add(node);
+ }
+
+ for (Entry<String, MNode> entry : children.entrySet()) {
+ MNode childNode = entry.getValue();
+ collectLastNode(childNode, lastNodeList);
+ }
+ }
+ }
}
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mnode/MNode.java
b/server/src/main/java/org/apache/iotdb/db/metadata/mnode/MNode.java
index 0f898c8..23f201c 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mnode/MNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mnode/MNode.java
@@ -31,7 +31,6 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
/**
* This class is the implementation of Metadata Node. One MNode instance
represents one node in the
@@ -54,13 +53,19 @@ public class MNode implements Serializable {
/**
* use in Measurement Node so it's protected suppress warnings reason:
volatile for double
* synchronized check
+ *
+ * <p>This will be a ConcurrentHashMap instance
*/
@SuppressWarnings("squid:S3077")
- protected transient volatile ConcurrentMap<String, MNode> children = null;
+ protected transient volatile Map<String, MNode> children = null;
- /** suppress warnings reason: volatile for double synchronized check */
+ /**
+ * suppress warnings reason: volatile for double synchronized check
+ *
+ * <p>This will be a ConcurrentHashMap instance
+ */
@SuppressWarnings("squid:S3077")
- private transient volatile ConcurrentMap<String, MNode> aliasChildren = null;
+ private transient volatile Map<String, MNode> aliasChildren = null;
/** Constructor of MNode. */
public MNode(MNode parent, String name) {
@@ -207,10 +212,21 @@ public class MNode implements Serializable {
return children;
}
- public void setChildren(ConcurrentMap<String, MNode> children) {
+ public Map<String, MNode> getAliasChildren() {
+ if (aliasChildren == null) {
+ return Collections.emptyMap();
+ }
+ return aliasChildren;
+ }
+
+ public void setChildren(Map<String, MNode> children) {
this.children = children;
}
+ private void setAliasChildren(Map<String, MNode> aliasChildren) {
+ this.aliasChildren = aliasChildren;
+ }
+
public String getName() {
return name;
}
@@ -233,4 +249,27 @@ public class MNode implements Serializable {
entry.getValue().serializeTo(logWriter);
}
}
+
+ public void replaceChild(String measurement, MNode newChildNode) {
+ MNode oldChildNode = this.getChild(measurement);
+ if (oldChildNode == null) {
+ return;
+ }
+
+ // newChildNode builds parent-child relationship
+ Map<String, MNode> grandChildren = oldChildNode.getChildren();
+ newChildNode.setChildren(grandChildren);
+ grandChildren.forEach(
+ (grandChildName, grandChildNode) ->
grandChildNode.setParent(newChildNode));
+
+ Map<String, MNode> grandAliasChildren = oldChildNode.getAliasChildren();
+ newChildNode.setAliasChildren(grandAliasChildren);
+ grandAliasChildren.forEach(
+ (grandAliasChildName, grandAliasChild) ->
grandAliasChild.setParent(newChildNode));
+
+ newChildNode.setParent(this);
+
+ this.deleteChild(measurement);
+ this.addChild(newChildNode.getName(), newChildNode);
+ }
}
diff --git
a/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java
b/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java
index 1c100fa..83a6802 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java
@@ -1907,7 +1907,7 @@ public class IoTDBSqlVisitor extends
SqlBaseBaseVisitor<Operator> {
createTimeSeriesOperator.setDataType(tsDataType);
final IoTDBDescriptor ioTDBDescriptor = IoTDBDescriptor.getInstance();
- TSEncoding encoding = ioTDBDescriptor.getDefualtEncodingByType(tsDataType);
+ TSEncoding encoding = ioTDBDescriptor.getDefaultEncodingByType(tsDataType);
if (Objects.nonNull(ctx.encoding())) {
String encodingString =
ctx.encoding().getChild(0).getText().toUpperCase();
encoding = TSEncoding.valueOf(encodingString);
diff --git
a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBAutoCreateSchemaIT.java
b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBAutoCreateSchemaIT.java
index 8f9b7ce..d1651e3 100644
---
a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBAutoCreateSchemaIT.java
+++
b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBAutoCreateSchemaIT.java
@@ -22,6 +22,7 @@ package org.apache.iotdb.db.integration;
import org.apache.iotdb.db.constant.TestConstant;
import org.apache.iotdb.db.utils.EnvironmentUtils;
import org.apache.iotdb.jdbc.Config;
+import org.apache.iotdb.jdbc.IoTDBSQLException;
import org.junit.After;
import org.junit.Assert;
@@ -33,22 +34,34 @@ import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
import java.sql.Statement;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
/**
* Notice that, all test begins with "IoTDB" is integration test. All test
which will start the
* IoTDB server should be defined as integration test.
*/
public class IoTDBAutoCreateSchemaIT {
+ private Statement statement;
+ private Connection connection;
@Before
public void setUp() {
EnvironmentUtils.closeStatMonitor();
EnvironmentUtils.envSetUp();
+
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ connection = DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/",
"root", "root");
+ statement = connection.createStatement();
}
@After
public void tearDown() throws Exception {
+ statement.close();
+ connection.close();
EnvironmentUtils.cleanEnv();
}
@@ -141,4 +154,97 @@ public class IoTDBAutoCreateSchemaIT {
e.printStackTrace();
}
}
+
+ /**
+ * insert data when the time series that is a prefix path of an existing
time series hasn't been
+ * created
+ */
+ @Test
+ public void testInsertAutoCreate1() throws Exception {
+ String[] timeSeriesArray = {"root.sg1.a.a", "root.sg1.a",
"root.sg1.a.a.a"};
+
+ for (String timeSeries : timeSeriesArray) {
+ statement.execute(
+ String.format("INSERT INTO %s(timestamp, a) values(123, \"aabb\")",
timeSeries));
+ }
+
+ // ensure that insert data in cache is right.
+ insertAutoCreate1Tool();
+
+ EnvironmentUtils.stopDaemon();
+ setUp();
+
+ // ensure that insert data in cache is right after recovering.
+ insertAutoCreate1Tool();
+ }
+
+ private void insertAutoCreate1Tool() throws SQLException {
+ boolean hasResult = statement.execute("select * from root.sg1");
+ Assert.assertTrue(hasResult);
+
+ Set<String> strSet = new HashSet<>();
+ String[] valueList = {};
+ try (ResultSet resultSet = statement.getResultSet()) {
+ while (resultSet.next()) {
+ valueList =
+ new String[] {
+ resultSet.getString("root.sg1.a.a"),
+ resultSet.getString("root.sg1.a.a.a"),
+ resultSet.getString("root.sg1.a.a.a.a")
+ };
+ strSet = new HashSet<>(Arrays.asList(valueList));
+ }
+ }
+ Assert.assertEquals(3, valueList.length);
+ Assert.assertEquals(1, strSet.size());
+ Assert.assertTrue(strSet.contains("aabb"));
+ }
+
+ /**
+ * test if automatically creating a time series will cause the storage group
with same name to
+ * disappear
+ */
+ @Test
+ public void testInsertAutoCreate2() throws Exception {
+ String storageGroup = "root.sg2.a.b.c";
+ String timeSeriesPrefix = "root.sg2.a.b";
+
+ statement.execute(String.format("SET storage group TO %s", storageGroup));
+ try {
+ statement.execute(
+ String.format("INSERT INTO %s(timestamp, c) values(123, \"aabb\")",
timeSeriesPrefix));
+ } catch (IoTDBSQLException ignored) {
+ }
+
+ // ensure that current storage group in cache is right.
+ InsertAutoCreate2Tool(storageGroup, timeSeriesPrefix);
+
+ EnvironmentUtils.stopDaemon();
+ setUp();
+
+ // ensure that storage group in cache is right after recovering.
+ InsertAutoCreate2Tool(storageGroup, timeSeriesPrefix);
+ }
+
+ private void InsertAutoCreate2Tool(String storageGroup, String
timeSeriesPrefix)
+ throws SQLException {
+ statement.execute("show timeseries");
+ Set<String> resultList = new HashSet<>();
+ try (ResultSet resultSet = statement.getResultSet()) {
+ while (resultSet.next()) {
+ String str = resultSet.getString("timeseries");
+ resultList.add(str);
+ }
+ }
+ Assert.assertFalse(resultList.contains(timeSeriesPrefix + "c"));
+
+ statement.execute("show storage group");
+ resultList.clear();
+ try (ResultSet resultSet = statement.getResultSet()) {
+ while (resultSet.next()) {
+ resultList.add(resultSet.getString("storage group"));
+ }
+ }
+ Assert.assertTrue(resultList.contains(storageGroup));
+ }
}
diff --git
a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBCreateTimeseriesIT.java
b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBCreateTimeseriesIT.java
new file mode 100644
index 0000000..5174419
--- /dev/null
+++
b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBCreateTimeseriesIT.java
@@ -0,0 +1,157 @@
+/*
+ * 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 org.apache.iotdb.db.utils.EnvironmentUtils;
+import org.apache.iotdb.jdbc.Config;
+import org.apache.iotdb.jdbc.IoTDBSQLException;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Notice that, all test begins with "IoTDB" is integration test. All test
which will start the
+ * IoTDB server should be defined as integration test.
+ */
+public class IoTDBCreateTimeseriesIT {
+ private Statement statement;
+ private Connection connection;
+
+ @Before
+ public void setUp() throws Exception {
+ EnvironmentUtils.envSetUp();
+
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ connection = DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/",
"root", "root");
+ statement = connection.createStatement();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ statement.close();
+ connection.close();
+ EnvironmentUtils.cleanEnv();
+ }
+
+ /** Test creating a time series that is a prefix path of an existing time
series */
+ @Test
+ public void testCreateTimeseries1() throws Exception {
+ String[] timeSeriesArray = {"root.sg1.aa.bb", "root.sg1.aa.bb.cc",
"root.sg1.aa"};
+
+ for (String timeSeries : timeSeriesArray) {
+ statement.execute(
+ String.format(
+ "create timeseries %s with datatype=INT64, encoding=PLAIN,
compression=SNAPPY",
+ timeSeries));
+ }
+
+ // ensure that current timeseries in cache is right.
+ createTimeSeries1Tool(timeSeriesArray);
+
+ EnvironmentUtils.stopDaemon();
+ setUp();
+
+ // ensure timeseries in cache is right after recovering.
+ createTimeSeries1Tool(timeSeriesArray);
+ }
+
+ private void createTimeSeries1Tool(String[] timeSeriesArray) throws
SQLException {
+ boolean hasResult = statement.execute("show timeseries");
+ Assert.assertTrue(hasResult);
+
+ List<String> resultList = new ArrayList<>();
+ try (ResultSet resultSet = statement.getResultSet()) {
+ while (resultSet.next()) {
+ String timeseries = resultSet.getString("timeseries");
+ resultList.add(timeseries);
+ }
+ }
+ Assert.assertEquals(3, resultList.size());
+
+ List<String> collect =
+ resultList.stream()
+ .sorted(Comparator.comparingInt(e -> e.split("\\.").length))
+ .collect(Collectors.toList());
+
+ Assert.assertEquals(timeSeriesArray[2], collect.get(0));
+ Assert.assertEquals(timeSeriesArray[0], collect.get(1));
+ Assert.assertEquals(timeSeriesArray[1], collect.get(2));
+ }
+
+ /** Test if creating a time series will cause the storage group with same
name to disappear */
+ @Test
+ public void testCreateTimeseries2() throws Exception {
+ String storageGroup = "root.sg1.a.b.c";
+
+ statement.execute(String.format("SET storage group TO %s", storageGroup));
+ try {
+ statement.execute(
+ String.format(
+ "create timeseries %s with datatype=INT64, encoding=PLAIN,
compression=SNAPPY",
+ storageGroup));
+ } catch (IoTDBSQLException ignored) {
+ }
+
+ // ensure that current storage group in cache is right.
+ createTimeSeries2Tool(storageGroup);
+
+ EnvironmentUtils.stopDaemon();
+ setUp();
+
+ // ensure storage group in cache is right after recovering.
+ createTimeSeries2Tool(storageGroup);
+ }
+
+ private void createTimeSeries2Tool(String storageGroup) throws SQLException {
+ statement.execute("show timeseries");
+ Set<String> resultList = new HashSet<>();
+ try (ResultSet resultSet = statement.getResultSet()) {
+ while (resultSet.next()) {
+ String str = resultSet.getString("timeseries");
+ resultList.add(str);
+ }
+ }
+ Assert.assertFalse(resultList.contains(storageGroup));
+
+ statement.execute("show storage group");
+ resultList.clear();
+ try (ResultSet resultSet = statement.getResultSet()) {
+ while (resultSet.next()) {
+ String res = resultSet.getString("storage group");
+ resultList.add(res);
+ }
+ }
+ Assert.assertTrue(resultList.contains(storageGroup));
+ }
+}
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 e7c0b25..14f5e39 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
@@ -22,6 +22,7 @@ import
org.apache.iotdb.db.exception.metadata.AliasAlreadyExistException;
import org.apache.iotdb.db.exception.metadata.IllegalPathException;
import org.apache.iotdb.db.exception.metadata.MetadataException;
import org.apache.iotdb.db.metadata.mnode.MNode;
+import org.apache.iotdb.db.metadata.mnode.MeasurementMNode;
import org.apache.iotdb.db.utils.EnvironmentUtils;
import org.apache.iotdb.tsfile.common.conf.TSFileDescriptor;
import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType;
@@ -767,4 +768,30 @@ public class MTreeTest {
fail(e1.getMessage());
}
}
+
+ @Test
+ public void testCreateTimeseries() throws MetadataException {
+ MTree root = new MTree();
+ String sgPath = "root.sg1";
+ root.setStorageGroup(new PartialPath(sgPath));
+
+ root.createTimeseries(
+ new PartialPath("root.sg1.a.b.c"),
+ TSDataType.INT32,
+ TSEncoding.RLE,
+ TSFileDescriptor.getInstance().getConfig().getCompressor(),
+ Collections.emptyMap(),
+ null);
+
+ root.createTimeseries(
+ new PartialPath("root.sg1.a.b"),
+ TSDataType.INT32,
+ TSEncoding.RLE,
+ TSFileDescriptor.getInstance().getConfig().getCompressor(),
+ Collections.emptyMap(),
+ null);
+
+ MNode node = root.getNodeByPath(new PartialPath("root.sg1.a.b"));
+ Assert.assertTrue(node instanceof MeasurementMNode);
+ }
}
diff --git
a/server/src/test/java/org/apache/iotdb/db/metadata/MetaUtilsTest.java
b/server/src/test/java/org/apache/iotdb/db/metadata/MetaUtilsTest.java
index ddd99ac..49d85a2 100644
--- a/server/src/test/java/org/apache/iotdb/db/metadata/MetaUtilsTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/metadata/MetaUtilsTest.java
@@ -19,11 +19,13 @@
package org.apache.iotdb.db.metadata;
import org.apache.iotdb.db.exception.metadata.IllegalPathException;
+import org.apache.iotdb.db.metadata.mnode.MNode;
import org.junit.Assert;
import org.junit.Test;
import java.util.Arrays;
+import java.util.List;
import static org.junit.Assert.assertArrayEquals;
@@ -76,4 +78,37 @@ public class MetaUtilsTest {
Assert.assertEquals("root.sg.d1.'s1' is not a legal path",
e.getMessage());
}
}
+
+ @Test
+ public void testGetMultiFullPaths() {
+ MNode rootNode = new MNode(null, "root");
+
+ // builds the relationship of root.a and root.aa
+ MNode aNode = new MNode(rootNode, "a");
+ rootNode.addChild(aNode.getName(), aNode);
+ MNode aaNode = new MNode(rootNode, "aa");
+ rootNode.addChild(aaNode.getName(), aaNode);
+
+ // builds the relationship of root.a.b and root.aa.bb
+ MNode bNode = new MNode(aNode, "b");
+ aNode.addChild(bNode.getName(), bNode);
+ MNode bbNode = new MNode(aaNode, "bb");
+ aaNode.addChild(bbNode.getName(), bbNode);
+
+ // builds the relationship of root.aa.bb.cc
+ MNode ccNode = new MNode(bbNode, "cc");
+ bbNode.addChild(ccNode.getName(), ccNode);
+
+ List<String> multiFullPaths = MetaUtils.getMultiFullPaths(rootNode);
+ Assert.assertSame(2, multiFullPaths.size());
+
+ multiFullPaths.forEach(
+ fullPath -> {
+ if (fullPath.contains("aa")) {
+ Assert.assertEquals("root.aa.bb.cc", fullPath);
+ } else {
+ Assert.assertEquals("root.a.b", fullPath);
+ }
+ });
+ }
}
diff --git
a/server/src/test/java/org/apache/iotdb/db/metadata/mnode/MNodeTest.java
b/server/src/test/java/org/apache/iotdb/db/metadata/mnode/MNodeTest.java
new file mode 100644
index 0000000..1a58bda
--- /dev/null
+++ b/server/src/test/java/org/apache/iotdb/db/metadata/mnode/MNodeTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.metadata.mnode;
+
+import org.apache.iotdb.db.metadata.MetaUtils;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertEquals;
+
+public class MNodeTest {
+ private static ExecutorService service;
+
+ @Before
+ public void setUp() throws Exception {
+ service =
+ Executors.newFixedThreadPool(
+ Runtime.getRuntime().availableProcessors(),
+ new
ThreadFactoryBuilder().setDaemon(false).setNameFormat("replaceChild-%d").build());
+ }
+
+ @Test
+ public void testReplaceChild() throws InterruptedException {
+ // after replacing a with c, the timeseries root.a.b becomes root.c.b
+ MNode rootNode = new MNode(null, "root");
+
+ MNode aNode = new MNode(rootNode, "a");
+ rootNode.addChild(aNode.getName(), aNode);
+
+ MNode bNode = new MNode(aNode, "b");
+ aNode.addChild(bNode.getName(), bNode);
+ aNode.addAlias("aliasOfb", bNode);
+
+ for (int i = 0; i < 500; i++) {
+ service.submit(
+ new Thread(() -> rootNode.replaceChild(aNode.getName(), new
MNode(null, "c"))));
+ }
+
+ if (!service.isShutdown()) {
+ service.shutdown();
+ service.awaitTermination(30, TimeUnit.SECONDS);
+ }
+
+ List<String> multiFullPaths = MetaUtils.getMultiFullPaths(rootNode);
+ assertEquals("root.c.b", multiFullPaths.get(0));
+ assertEquals("root.c.b",
rootNode.getChild("c").getChild("aliasOfb").getFullPath());
+ }
+}