This is an automated email from the ASF dual-hosted git repository. haonan pushed a commit to branch comma_bug in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 1418da80176e24423ae63e04fc25a71f56c57ce3 Author: wshao08 <[email protected]> AuthorDate: Tue Dec 29 16:44:51 2020 +0800 [IOTDB-1065] Fix deletion bug while trying to delete data from a timeseries which contains comma (#2306) --- .../io/LocalTextModificationAccessor.java | 61 ++++++++++++++++------ .../iotdb/db/integration/IoTDBDeletionIT.java | 35 +++++++++++++ 2 files changed, 81 insertions(+), 15 deletions(-) diff --git a/server/src/main/java/org/apache/iotdb/db/engine/modification/io/LocalTextModificationAccessor.java b/server/src/main/java/org/apache/iotdb/db/engine/modification/io/LocalTextModificationAccessor.java index 7c41115..8e09964 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/modification/io/LocalTextModificationAccessor.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/modification/io/LocalTextModificationAccessor.java @@ -23,6 +23,7 @@ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; import org.apache.iotdb.db.engine.modification.Deletion; @@ -127,33 +128,63 @@ public class LocalTextModificationAccessor implements ModificationReader, Modifi + del.getStartTime() + SEPARATOR + del.getEndTime(); } + /** + * Decode a range deletion record. E.g. "DELETION,root.ln.wf01.wt01.temperature,111,100,300" + * the index of field endTimestamp is length - 1, startTimestamp is length - 2, + * versionNum is length - 3. Fields in index range [1, length -3) all belong + * to a timeseries path in case when the path contains comma. + */ private static Deletion decodeDeletion(String[] fields) throws IOException { - if (fields.length != 5 && fields.length != 4) { + if (fields.length < 4) { throw new IOException("Incorrect deletion fields number: " + fields.length); } - String path = fields[1]; - long versionNum; - long startTimestamp = Long.MIN_VALUE; + String path = ""; + long startTimestamp; long endTimestamp; + long versionNum; try { - versionNum = Long.parseLong(fields[2]); + versionNum = Long.parseLong(fields[fields.length - 3]); } catch (NumberFormatException e) { - throw new IOException("Invalid version number: " + fields[2]); + return decodePointDeletion(fields); } try { - if (fields.length == 4) { - endTimestamp = Long.parseLong(fields[3]); - - } else { - startTimestamp = Long.parseLong(fields[3]); - endTimestamp = Long.parseLong(fields[4]); - } - return new Deletion(new PartialPath(path), versionNum, startTimestamp, endTimestamp); - } catch (NumberFormatException | IllegalPathException e) { + endTimestamp = Long.parseLong(fields[fields.length - 1]); + startTimestamp = Long.parseLong(fields[fields.length - 2]); + } catch (NumberFormatException e) { throw new IOException("Invalid timestamp: " + e.getMessage()); } + try { + String[] pathArray = Arrays.copyOfRange(fields, 1, fields.length - 3); + path = String.join(SEPARATOR, pathArray); + return new Deletion(new PartialPath(path), versionNum, startTimestamp, endTimestamp); + } catch (IllegalPathException e) { + throw new IOException("Invalid series path: " + path); + } + } + /** + * Decode a point deletion record. E.g. "DELETION,root.ln.wf01.wt01.temperature,111,300" + * the index of field endTimestamp is length - 1, versionNum is length - 2. + * Fields in index range [1, length - 2) compose timeseries path. + */ + private static Deletion decodePointDeletion(String[] fields) throws IOException { + String path = ""; + long versionNum; + long endTimestamp; + try { + endTimestamp = Long.parseLong(fields[fields.length - 1]); + versionNum = Long.parseLong(fields[fields.length - 2]); + } catch (NumberFormatException e) { + throw new IOException("Invalid timestamp: " + e.getMessage()); + } + try { + String[] pathArray = Arrays.copyOfRange(fields, 1, fields.length - 2); + path = String.join(SEPARATOR, pathArray); + return new Deletion(new PartialPath(path), versionNum, endTimestamp); + } catch (IllegalPathException e) { + throw new IOException("Invalid series path: " + path); + } } } diff --git a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBDeletionIT.java b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBDeletionIT.java index 1414d13..77c4f90 100644 --- a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBDeletionIT.java +++ b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBDeletionIT.java @@ -32,6 +32,7 @@ import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.utils.EnvironmentUtils; import org.apache.iotdb.jdbc.Config; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -278,6 +279,40 @@ public class IoTDBDeletionIT { IoTDBDescriptor.getInstance().getConfig().setMemtableSizeThreshold(size); } + @Test + public void testDelSeriesWithSpecialSymbol() throws SQLException { + try (Connection connection = DriverManager + .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", + "root"); + Statement statement = connection.createStatement()) { + statement.execute("CREATE TIMESERIES root.ln.d1.\"status,01\" WITH DATATYPE=BOOLEAN," + + " ENCODING=PLAIN"); + statement.execute("INSERT INTO root.ln.d1(timestamp,\"status,01\") " + + "values(300,true)"); + statement.execute("INSERT INTO root.ln.d1(timestamp,\"status,01\") VALUES(500, false)"); + + statement.execute("DELETE FROM root.ln.d1.\"status,01\" WHERE time <= 400"); + + try (ResultSet resultSet = statement.executeQuery("select \"status,01\" from root.ln.d1")) { + int cnt = 0; + while (resultSet.next()) { + cnt++; + } + Assert.assertEquals(1, cnt); + } + + statement.execute("DELETE FROM root.ln.d1.\"status,01\""); + + try (ResultSet resultSet = statement.executeQuery("select \"status,01\" from root.ln.d1")) { + int cnt = 0; + while (resultSet.next()) { + cnt++; + } + Assert.assertEquals(0, cnt); + } + } + } + private static void prepareSeries() { try (Connection connection = DriverManager .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
