From 643d7b40ceca51b2c8d037e7d59b9212c3e6582e Mon Sep 17 00:00:00 2001 From: Alima777 <526213833@qq.com> Date: Wed, 9 Sep 2020 20:31:15 +0800 Subject: [PATCH 1/4] fix double quotes bug --- .../apache/iotdb/db/metadata/MetaUtils.java | 6 +- .../db/qp/strategy/PhysicalGenerator.java | 97 +++++------ .../db/integration/IoTDBAlignByDeviceIT.java | 164 +++++++++--------- .../db/integration/IoTDBQuotedPathIT.java | 93 ++++++---- .../iotdb/db/metadata/MetaUtilsTest.java | 3 + 5 files changed, 184 insertions(+), 179 deletions(-) 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 703237b85b3e8..5242641d5e88c 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 @@ -37,7 +37,7 @@ static String getNodeRegByIdx(int idx, String[] nodes) { } /** - * @param path the path will split. ex, root.ln. note: doesn't support escape character + * @param path the path will split. ex, root.ln. * @return string array. ex, [root, ln] * @throws IllegalPathException if path isn't correct, the exception will throw */ @@ -50,6 +50,10 @@ public static String[] splitPathToDetachedPath(String path) throws IllegalPathEx startIndex = i + 1; } else if (path.charAt(i) == '"') { int endIndex = path.indexOf('"', i + 1); + // if a double quotes with escape character + while (endIndex != -1 && path.charAt(endIndex - 1) == '\\') { + endIndex = path.indexOf('"', endIndex + 1); + } if (endIndex != -1 && (endIndex == path.length() - 1 || path.charAt(endIndex + 1) == '.')) { nodes.add(path.substring(startIndex, endIndex + 1)); i = endIndex + 1; 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 523295890faac..97f543884389e 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 @@ -327,47 +327,43 @@ protected List getSeriesTypes(List paths) throws Metada private PhysicalPlan transformQuery(QueryOperator queryOperator) throws QueryProcessException { QueryPlan queryPlan; - if (queryOperator.isGroupByTime() && queryOperator.isFill()) { - queryPlan = new GroupByTimeFillPlan(); - ((GroupByTimeFillPlan) queryPlan).setInterval(queryOperator.getUnit()); - ((GroupByTimeFillPlan) queryPlan).setSlidingStep(queryOperator.getSlidingStep()); - ((GroupByTimeFillPlan) queryPlan).setLeftCRightO(queryOperator.isLeftCRightO()); - if (!queryOperator.isLeftCRightO()) { - ((GroupByTimePlan) queryPlan).setStartTime(queryOperator.getStartTime() + 1); - ((GroupByTimePlan) queryPlan).setEndTime(queryOperator.getEndTime() + 1); + if (queryOperator.hasAggregation()) { + if (queryOperator.isGroupByTime() && queryOperator.isFill()) { + queryPlan = new GroupByTimeFillPlan(); + } else if (queryOperator.isGroupByTime()) { + queryPlan = new GroupByTimePlan(); } else { - ((GroupByTimePlan) queryPlan).setStartTime(queryOperator.getStartTime()); - ((GroupByTimePlan) queryPlan).setEndTime(queryOperator.getEndTime()); + queryPlan = new AggregationPlan(); } - ((GroupByTimeFillPlan) queryPlan) + ((AggregationPlan) queryPlan) .setAggregations(queryOperator.getSelectOperator().getAggregations()); - for (String aggregation : queryPlan.getAggregations()) { - if (!SQLConstant.LAST_VALUE.equals(aggregation)) { - throw new QueryProcessException("Group By Fill only support last_value function"); + + if (queryOperator.isGroupByTime()) { + ((GroupByTimePlan) queryPlan).setInterval(queryOperator.getUnit()); + ((GroupByTimePlan) queryPlan).setSlidingStep(queryOperator.getSlidingStep()); + ((GroupByTimePlan) queryPlan).setLeftCRightO(queryOperator.isLeftCRightO()); + if (!queryOperator.isLeftCRightO()) { + ((GroupByTimePlan) queryPlan).setStartTime(queryOperator.getStartTime() + 1); + ((GroupByTimePlan) queryPlan).setEndTime(queryOperator.getEndTime() + 1); + } else { + ((GroupByTimePlan) queryPlan).setStartTime(queryOperator.getStartTime()); + ((GroupByTimePlan) queryPlan).setEndTime(queryOperator.getEndTime()); } } - ((GroupByTimeFillPlan) queryPlan).setFillType(queryOperator.getFillTypes()); - } else if (queryOperator.isGroupByTime()) { - queryPlan = new GroupByTimePlan(); - ((GroupByTimePlan) queryPlan).setInterval(queryOperator.getUnit()); - ((GroupByTimePlan) queryPlan).setSlidingStep(queryOperator.getSlidingStep()); - ((GroupByTimePlan) queryPlan).setLeftCRightO(queryOperator.isLeftCRightO()); - if (!queryOperator.isLeftCRightO()) { - ((GroupByTimePlan) queryPlan).setStartTime(queryOperator.getStartTime() + 1); - ((GroupByTimePlan) queryPlan).setEndTime(queryOperator.getEndTime() + 1); + if (queryOperator.isFill()) { + ((GroupByTimeFillPlan) queryPlan).setFillType(queryOperator.getFillTypes()); + for (String aggregation : queryPlan.getAggregations()) { + if (!SQLConstant.LAST_VALUE.equals(aggregation)) { + throw new QueryProcessException("Group By Fill only support last_value function."); + } + } } else { - ((GroupByTimePlan) queryPlan).setStartTime(queryOperator.getStartTime()); - ((GroupByTimePlan) queryPlan).setEndTime(queryOperator.getEndTime()); - } - ((GroupByTimePlan) queryPlan) - .setAggregations(queryOperator.getSelectOperator().getAggregations()); - - ((GroupByTimePlan) queryPlan).setLevel(queryOperator.getLevel()); - if (queryOperator.getLevel() >= 0) { - for (int i = 0; i < queryOperator.getSelectOperator().getAggregations().size(); i++) { - if (!SQLConstant.COUNT - .equals(queryOperator.getSelectOperator().getAggregations().get(i))) { - throw new QueryProcessException("group by level only support count now."); + ((AggregationPlan) queryPlan).setLevel(queryOperator.getLevel()); + if (queryOperator.getLevel() >= 0) { + for (String aggregation : queryPlan.getAggregations()) { + if (!SQLConstant.COUNT.equals(aggregation)) { + throw new QueryProcessException("Group By Level only support count now."); + } } } } @@ -380,32 +376,13 @@ private PhysicalPlan transformQuery(QueryOperator queryOperator) throws QueryPro long time = Long.parseLong(((BasicFunctionOperator) timeFilter).getValue()); ((FillQueryPlan) queryPlan).setQueryTime(time); ((FillQueryPlan) queryPlan).setFillType(queryOperator.getFillTypes()); - } else if (queryOperator.hasAggregation()) { - queryPlan = new AggregationPlan(); - ((AggregationPlan) queryPlan).setLevel(queryOperator.getLevel()); - ((AggregationPlan) queryPlan) - .setAggregations(queryOperator.getSelectOperator().getAggregations()); - if (queryOperator.getLevel() >= 0) { - for (int i = 0; i < queryOperator.getSelectOperator().getAggregations().size(); i++) { - if (!SQLConstant.COUNT - .equals(queryOperator.getSelectOperator().getAggregations().get(i))) { - throw new QueryProcessException("group by level only support count now."); - } - } - } } else if (queryOperator.isLastQuery()) { queryPlan = new LastQueryPlan(); } else { queryPlan = new RawDataQueryPlan(); } - if (queryPlan instanceof LastQueryPlan) { - // Last query result set will not be affected by alignment - if (!queryOperator.isAlignByTime()) { - throw new QueryProcessException("Disable align cannot be applied to LAST query."); - } - List paths = queryOperator.getSelectedPaths(); - queryPlan.setPaths(paths); - } else if (queryOperator.isAlignByDevice()) { + + if (queryOperator.isAlignByDevice()) { // below is the core realization of ALIGN_BY_DEVICE sql logic AlignByDevicePlan alignByDevicePlan = new AlignByDevicePlan(); if (queryPlan instanceof GroupByTimePlan) { @@ -444,7 +421,7 @@ private PhysicalPlan transformQuery(QueryOperator queryOperator) throws QueryPro Set measurementSetOfGivenSuffix = new LinkedHashSet<>(); // if const measurement - if (suffixPath.getMeasurement().startsWith("'") || suffixPath.getMeasurement().startsWith("\"")) { + if (suffixPath.getMeasurement().startsWith("'")) { measurements.add(suffixPath.getMeasurement()); measurementTypeMap.put(suffixPath.getMeasurement(), MeasurementType.Constant); continue; @@ -573,9 +550,13 @@ private PhysicalPlan transformQuery(QueryOperator queryOperator) throws QueryPro queryPlan = alignByDevicePlan; } else { - queryPlan.setAlignByTime(queryOperator.isAlignByTime()); List paths = queryOperator.getSelectedPaths(); queryPlan.setPaths(paths); + // Last query result set will not be affected by alignment + if (queryPlan instanceof LastQueryPlan && !queryOperator.isAlignByTime()) { + throw new QueryProcessException("Disable align cannot be applied to LAST query."); + } + queryPlan.setAlignByTime(queryOperator.isAlignByTime()); // transform filter operator to expression FilterOperator filterOperator = queryOperator.getFilterOperator(); diff --git a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBAlignByDeviceIT.java b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBAlignByDeviceIT.java index 7c108185fb80a..5fb5356ad8149 100644 --- a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBAlignByDeviceIT.java +++ b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBAlignByDeviceIT.java @@ -702,25 +702,25 @@ public void unusualCaseTest2() throws ClassNotFoundException { @Test public void selectConstantTest() throws ClassNotFoundException { String[] retArray = new String[]{ - "1,root.vehicle.d0,101,1101,null,null,null,\"11\",", - "2,root.vehicle.d0,10000,40000,2.22,null,null,\"11\",", - "3,root.vehicle.d0,null,null,3.33,null,null,\"11\",", - "4,root.vehicle.d0,null,null,4.44,null,null,\"11\",", - "50,root.vehicle.d0,10000,50000,null,null,null,\"11\",", - "60,root.vehicle.d0,null,null,null,aaaaa,null,\"11\",", - "70,root.vehicle.d0,null,null,null,bbbbb,null,\"11\",", - "80,root.vehicle.d0,null,null,null,ccccc,null,\"11\",", - "100,root.vehicle.d0,99,199,null,null,true,\"11\",", - "101,root.vehicle.d0,99,199,null,ddddd,null,\"11\",", - "102,root.vehicle.d0,80,180,10.0,fffff,null,\"11\",", - "103,root.vehicle.d0,99,199,null,null,null,\"11\",", - "104,root.vehicle.d0,90,190,null,null,null,\"11\",", - "105,root.vehicle.d0,99,199,11.11,null,null,\"11\",", - "106,root.vehicle.d0,99,null,null,null,null,\"11\",", - "1000,root.vehicle.d0,22222,55555,1000.11,null,null,\"11\",", - "946684800000,root.vehicle.d0,null,100,null,good,null,\"11\",", - "1,root.vehicle.d1,999,null,null,null,null,\"11\",", - "1000,root.vehicle.d1,888,null,null,null,null,\"11\",", + "1,root.vehicle.d0,101,1101,null,null,null,'11',", + "2,root.vehicle.d0,10000,40000,2.22,null,null,'11',", + "3,root.vehicle.d0,null,null,3.33,null,null,'11',", + "4,root.vehicle.d0,null,null,4.44,null,null,'11',", + "50,root.vehicle.d0,10000,50000,null,null,null,'11',", + "60,root.vehicle.d0,null,null,null,aaaaa,null,'11',", + "70,root.vehicle.d0,null,null,null,bbbbb,null,'11',", + "80,root.vehicle.d0,null,null,null,ccccc,null,'11',", + "100,root.vehicle.d0,99,199,null,null,true,'11',", + "101,root.vehicle.d0,99,199,null,ddddd,null,'11',", + "102,root.vehicle.d0,80,180,10.0,fffff,null,'11',", + "103,root.vehicle.d0,99,199,null,null,null,'11',", + "104,root.vehicle.d0,90,190,null,null,null,'11',", + "105,root.vehicle.d0,99,199,11.11,null,null,'11',", + "106,root.vehicle.d0,99,null,null,null,null,'11',", + "1000,root.vehicle.d0,22222,55555,1000.11,null,null,'11',", + "946684800000,root.vehicle.d0,null,100,null,good,null,'11',", + "1,root.vehicle.d1,999,null,null,null,null,'11',", + "1000,root.vehicle.d1,888,null,null,null,null,'11',", }; Class.forName(Config.JDBC_DRIVER_NAME); @@ -728,7 +728,7 @@ public void selectConstantTest() throws ClassNotFoundException { .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root"); Statement statement = connection.createStatement()) { boolean hasResultSet = statement.execute( - "select *, \"11\" from root.vehicle align by device"); + "select *, '11' from root.vehicle align by device"); Assert.assertTrue(hasResultSet); try (ResultSet resultSet = statement.getResultSet()) { @@ -737,7 +737,7 @@ public void selectConstantTest() throws ClassNotFoundException { for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) { header.append(resultSetMetaData.getColumnName(i)).append(","); } - Assert.assertEquals("Time,Device,s0,s1,s2,s3,s4,\"11\",", header.toString()); + Assert.assertEquals("Time,Device,s0,s1,s2,s3,s4,'11',", header.toString()); Assert.assertEquals(Types.TIMESTAMP, resultSetMetaData.getColumnType(1)); Assert.assertEquals(Types.VARCHAR, resultSetMetaData.getColumnType(2)); Assert.assertEquals(Types.INTEGER, resultSetMetaData.getColumnType(3)); @@ -835,23 +835,23 @@ public void selectNonExistTest() throws ClassNotFoundException { @Test public void selectConstantTestUnorder() throws ClassNotFoundException { String[] retArray = new String[]{ - "1,root.vehicle.d0,101,1101,\"11\",null,\"22\",null,null,", - "2,root.vehicle.d0,10000,40000,\"11\",2.22,\"22\",null,null,", - "3,root.vehicle.d0,null,null,\"11\",3.33,\"22\",null,null,", - "4,root.vehicle.d0,null,null,\"11\",4.44,\"22\",null,null,", - "50,root.vehicle.d0,10000,50000,\"11\",null,\"22\",null,null,", - "60,root.vehicle.d0,null,null,\"11\",null,\"22\",aaaaa,null,", - "70,root.vehicle.d0,null,null,\"11\",null,\"22\",bbbbb,null,", - "80,root.vehicle.d0,null,null,\"11\",null,\"22\",ccccc,null,", - "100,root.vehicle.d0,99,199,\"11\",null,\"22\",null,true,", - "101,root.vehicle.d0,99,199,\"11\",null,\"22\",ddddd,null,", - "102,root.vehicle.d0,80,180,\"11\",10.0,\"22\",fffff,null,", - "103,root.vehicle.d0,99,199,\"11\",null,\"22\",null,null,", - "104,root.vehicle.d0,90,190,\"11\",null,\"22\",null,null,", - "105,root.vehicle.d0,99,199,\"11\",11.11,\"22\",null,null,", - "106,root.vehicle.d0,99,null,\"11\",null,\"22\",null,null,", - "1000,root.vehicle.d0,22222,55555,\"11\",1000.11,\"22\",null,null,", - "946684800000,root.vehicle.d0,null,100,\"11\",null,\"22\",good,null,", + "1,root.vehicle.d0,101,1101,'11',null,'22',null,null,", + "2,root.vehicle.d0,10000,40000,'11',2.22,'22',null,null,", + "3,root.vehicle.d0,null,null,'11',3.33,'22',null,null,", + "4,root.vehicle.d0,null,null,'11',4.44,'22',null,null,", + "50,root.vehicle.d0,10000,50000,'11',null,'22',null,null,", + "60,root.vehicle.d0,null,null,'11',null,'22',aaaaa,null,", + "70,root.vehicle.d0,null,null,'11',null,'22',bbbbb,null,", + "80,root.vehicle.d0,null,null,'11',null,'22',ccccc,null,", + "100,root.vehicle.d0,99,199,'11',null,'22',null,true,", + "101,root.vehicle.d0,99,199,'11',null,'22',ddddd,null,", + "102,root.vehicle.d0,80,180,'11',10.0,'22',fffff,null,", + "103,root.vehicle.d0,99,199,'11',null,'22',null,null,", + "104,root.vehicle.d0,90,190,'11',null,'22',null,null,", + "105,root.vehicle.d0,99,199,'11',11.11,'22',null,null,", + "106,root.vehicle.d0,99,null,'11',null,'22',null,null,", + "1000,root.vehicle.d0,22222,55555,'11',1000.11,'22',null,null,", + "946684800000,root.vehicle.d0,null,100,'11',null,'22',good,null,", }; Class.forName(Config.JDBC_DRIVER_NAME); @@ -859,7 +859,7 @@ public void selectConstantTestUnorder() throws ClassNotFoundException { .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root"); Statement statement = connection.createStatement()) { boolean hasResultSet = statement.execute( - "select s0, s1,\"11\", s2, \"22\", s3, s4 from root.vehicle.d0 align by device"); + "select s0, s1,'11', s2, '22', s3, s4 from root.vehicle.d0 align by device"); Assert.assertTrue(hasResultSet); try (ResultSet resultSet = statement.getResultSet()) { @@ -868,7 +868,7 @@ public void selectConstantTestUnorder() throws ClassNotFoundException { for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) { header.append(resultSetMetaData.getColumnName(i)).append(","); } - Assert.assertEquals("Time,Device,s0,s1,\"11\",s2,\"22\",s3,s4,", header.toString()); + Assert.assertEquals("Time,Device,s0,s1,'11',s2,'22',s3,s4,", header.toString()); Assert.assertEquals(Types.TIMESTAMP, resultSetMetaData.getColumnType(1)); Assert.assertEquals(Types.VARCHAR, resultSetMetaData.getColumnType(2)); Assert.assertEquals(Types.INTEGER, resultSetMetaData.getColumnType(3)); @@ -902,25 +902,25 @@ public void selectConstantTestUnorder() throws ClassNotFoundException { @Test public void selectConstantAndNonExistTestUnorder() throws ClassNotFoundException { String[] retArray = new String[]{ - "1,root.vehicle.d0,101,1101,\"11\",null,\"22\",null,null,null,", - "2,root.vehicle.d0,10000,40000,\"11\",2.22,\"22\",null,null,null,", - "3,root.vehicle.d0,null,null,\"11\",3.33,\"22\",null,null,null,", - "4,root.vehicle.d0,null,null,\"11\",4.44,\"22\",null,null,null,", - "50,root.vehicle.d0,10000,50000,\"11\",null,\"22\",null,null,null,", - "60,root.vehicle.d0,null,null,\"11\",null,\"22\",null,aaaaa,null,", - "70,root.vehicle.d0,null,null,\"11\",null,\"22\",null,bbbbb,null,", - "80,root.vehicle.d0,null,null,\"11\",null,\"22\",null,ccccc,null,", - "100,root.vehicle.d0,99,199,\"11\",null,\"22\",null,null,true,", - "101,root.vehicle.d0,99,199,\"11\",null,\"22\",null,ddddd,null,", - "102,root.vehicle.d0,80,180,\"11\",10.0,\"22\",null,fffff,null,", - "103,root.vehicle.d0,99,199,\"11\",null,\"22\",null,null,null,", - "104,root.vehicle.d0,90,190,\"11\",null,\"22\",null,null,null,", - "105,root.vehicle.d0,99,199,\"11\",11.11,\"22\",null,null,null,", - "106,root.vehicle.d0,99,null,\"11\",null,\"22\",null,null,null,", - "1000,root.vehicle.d0,22222,55555,\"11\",1000.11,\"22\",null,null,null,", - "946684800000,root.vehicle.d0,null,100,\"11\",null,\"22\",null,good,null,", - "1,root.vehicle.d1,999,null,\"11\",null,\"22\",null,null,null,", - "1000,root.vehicle.d1,888,null,\"11\",null,\"22\",null,null,null,", + "1,root.vehicle.d0,101,1101,'11',null,'22',null,null,null,", + "2,root.vehicle.d0,10000,40000,'11',2.22,'22',null,null,null,", + "3,root.vehicle.d0,null,null,'11',3.33,'22',null,null,null,", + "4,root.vehicle.d0,null,null,'11',4.44,'22',null,null,null,", + "50,root.vehicle.d0,10000,50000,'11',null,'22',null,null,null,", + "60,root.vehicle.d0,null,null,'11',null,'22',null,aaaaa,null,", + "70,root.vehicle.d0,null,null,'11',null,'22',null,bbbbb,null,", + "80,root.vehicle.d0,null,null,'11',null,'22',null,ccccc,null,", + "100,root.vehicle.d0,99,199,'11',null,'22',null,null,true,", + "101,root.vehicle.d0,99,199,'11',null,'22',null,ddddd,null,", + "102,root.vehicle.d0,80,180,'11',10.0,'22',null,fffff,null,", + "103,root.vehicle.d0,99,199,'11',null,'22',null,null,null,", + "104,root.vehicle.d0,90,190,'11',null,'22',null,null,null,", + "105,root.vehicle.d0,99,199,'11',11.11,'22',null,null,null,", + "106,root.vehicle.d0,99,null,'11',null,'22',null,null,null,", + "1000,root.vehicle.d0,22222,55555,'11',1000.11,'22',null,null,null,", + "946684800000,root.vehicle.d0,null,100,'11',null,'22',null,good,null,", + "1,root.vehicle.d1,999,null,'11',null,'22',null,null,null,", + "1000,root.vehicle.d1,888,null,'11',null,'22',null,null,null,", }; Class.forName(Config.JDBC_DRIVER_NAME); @@ -928,7 +928,7 @@ public void selectConstantAndNonExistTestUnorder() throws ClassNotFoundException .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root"); Statement statement = connection.createStatement()) { boolean hasResultSet = statement.execute( - "select s0, s1,\"11\", s2, \"22\", s5, s3, s4 from root.vehicle.* align by device"); + "select s0, s1,'11', s2, '22', s5, s3, s4 from root.vehicle.* align by device"); Assert.assertTrue(hasResultSet); try (ResultSet resultSet = statement.getResultSet()) { @@ -937,7 +937,7 @@ public void selectConstantAndNonExistTestUnorder() throws ClassNotFoundException for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) { header.append(resultSetMetaData.getColumnName(i)).append(","); } - Assert.assertEquals("Time,Device,s0,s1,\"11\",s2,\"22\",s5,s3,s4,", header.toString()); + Assert.assertEquals("Time,Device,s0,s1,'11',s2,'22',s5,s3,s4,", header.toString()); Assert.assertEquals(Types.TIMESTAMP, resultSetMetaData.getColumnType(1)); Assert.assertEquals(Types.VARCHAR, resultSetMetaData.getColumnType(2)); Assert.assertEquals(Types.INTEGER, resultSetMetaData.getColumnType(3)); @@ -973,25 +973,25 @@ public void selectConstantAndNonExistTestUnorder() throws ClassNotFoundException @Test public void selectConstantAndNonExistTestWithUnorderedDevice() throws ClassNotFoundException { String[] retArray = new String[]{ - "1,root.vehicle.d1,null,999,null,\"11\",null,\"11\",\"22\",null,null,999,", - "1000,root.vehicle.d1,null,888,null,\"11\",null,\"11\",\"22\",null,null,888,", - "1,root.vehicle.d0,null,101,null,\"11\",null,\"11\",\"22\",null,null,101,", - "2,root.vehicle.d0,null,10000,null,\"11\",2.22,\"11\",\"22\",null,null,10000,", - "3,root.vehicle.d0,null,null,null,\"11\",3.33,\"11\",\"22\",null,null,null,", - "4,root.vehicle.d0,null,null,null,\"11\",4.44,\"11\",\"22\",null,null,null,", - "50,root.vehicle.d0,null,10000,null,\"11\",null,\"11\",\"22\",null,null,10000,", - "60,root.vehicle.d0,null,null,null,\"11\",null,\"11\",\"22\",null,aaaaa,null,", - "70,root.vehicle.d0,null,null,null,\"11\",null,\"11\",\"22\",null,bbbbb,null,", - "80,root.vehicle.d0,null,null,null,\"11\",null,\"11\",\"22\",null,ccccc,null,", - "100,root.vehicle.d0,null,99,null,\"11\",null,\"11\",\"22\",null,null,99,", - "101,root.vehicle.d0,null,99,null,\"11\",null,\"11\",\"22\",null,ddddd,99,", - "102,root.vehicle.d0,null,80,null,\"11\",10.0,\"11\",\"22\",null,fffff,80,", - "103,root.vehicle.d0,null,99,null,\"11\",null,\"11\",\"22\",null,null,99,", - "104,root.vehicle.d0,null,90,null,\"11\",null,\"11\",\"22\",null,null,90,", - "105,root.vehicle.d0,null,99,null,\"11\",11.11,\"11\",\"22\",null,null,99,", - "106,root.vehicle.d0,null,99,null,\"11\",null,\"11\",\"22\",null,null,99,", - "1000,root.vehicle.d0,null,22222,null,\"11\",1000.11,\"11\",\"22\",null,null,22222,", - "946684800000,root.vehicle.d0,null,null,null,\"11\",null,\"11\",\"22\",null,good,null,", + "1,root.vehicle.d1,null,999,null,'11',null,'11','22',null,null,999,", + "1000,root.vehicle.d1,null,888,null,'11',null,'11','22',null,null,888,", + "1,root.vehicle.d0,null,101,null,'11',null,'11','22',null,null,101,", + "2,root.vehicle.d0,null,10000,null,'11',2.22,'11','22',null,null,10000,", + "3,root.vehicle.d0,null,null,null,'11',3.33,'11','22',null,null,null,", + "4,root.vehicle.d0,null,null,null,'11',4.44,'11','22',null,null,null,", + "50,root.vehicle.d0,null,10000,null,'11',null,'11','22',null,null,10000,", + "60,root.vehicle.d0,null,null,null,'11',null,'11','22',null,aaaaa,null,", + "70,root.vehicle.d0,null,null,null,'11',null,'11','22',null,bbbbb,null,", + "80,root.vehicle.d0,null,null,null,'11',null,'11','22',null,ccccc,null,", + "100,root.vehicle.d0,null,99,null,'11',null,'11','22',null,null,99,", + "101,root.vehicle.d0,null,99,null,'11',null,'11','22',null,ddddd,99,", + "102,root.vehicle.d0,null,80,null,'11',10.0,'11','22',null,fffff,80,", + "103,root.vehicle.d0,null,99,null,'11',null,'11','22',null,null,99,", + "104,root.vehicle.d0,null,90,null,'11',null,'11','22',null,null,90,", + "105,root.vehicle.d0,null,99,null,'11',11.11,'11','22',null,null,99,", + "106,root.vehicle.d0,null,99,null,'11',null,'11','22',null,null,99,", + "1000,root.vehicle.d0,null,22222,null,'11',1000.11,'11','22',null,null,22222,", + "946684800000,root.vehicle.d0,null,null,null,'11',null,'11','22',null,good,null,", }; Class.forName(Config.JDBC_DRIVER_NAME); @@ -999,7 +999,7 @@ public void selectConstantAndNonExistTestWithUnorderedDevice() throws ClassNotFo .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root"); Statement statement = connection.createStatement()) { boolean hasResultSet = statement.execute( - "select s5, s0, s5, \"11\", s2, \"11\", \"22\", s5, s3, s0 from root.vehicle.d1, root.vehicle.d0 align by device"); + "select s5, s0, s5, '11', s2, '11', '22', s5, s3, s0 from root.vehicle.d1, root.vehicle.d0 align by device"); Assert.assertTrue(hasResultSet); try (ResultSet resultSet = statement.getResultSet()) { @@ -1008,7 +1008,7 @@ public void selectConstantAndNonExistTestWithUnorderedDevice() throws ClassNotFo for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) { header.append(resultSetMetaData.getColumnName(i)).append(","); } - Assert.assertEquals("Time,Device,s5,s0,s5,\"11\",s2,\"11\",\"22\",s5,s3,s0,", header.toString()); + Assert.assertEquals("Time,Device,s5,s0,s5,'11',s2,'11','22',s5,s3,s0,", header.toString()); Assert.assertEquals(Types.TIMESTAMP, resultSetMetaData.getColumnType(1)); Assert.assertEquals(Types.VARCHAR, resultSetMetaData.getColumnType(2)); Assert.assertEquals(Types.VARCHAR, resultSetMetaData.getColumnType(3)); diff --git a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBQuotedPathIT.java b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBQuotedPathIT.java index 276bd73af91f1..0d7c8169f420a 100644 --- a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBQuotedPathIT.java +++ b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBQuotedPathIT.java @@ -27,7 +27,6 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.util.ArrayList; import org.apache.iotdb.db.utils.EnvironmentUtils; import org.apache.iotdb.jdbc.Config; import org.apache.iotdb.jdbc.IoTDBSQLException; @@ -52,62 +51,80 @@ public void tearDown() throws Exception { @Test public void test() throws SQLException { - try(Connection connection = DriverManager - .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", - "root"); - Statement statement = connection.createStatement()){ + try (Connection connection = DriverManager + .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", + "root"); + Statement statement = connection.createStatement()) { String[] exp = new String[]{ - "1509465600000,true", - "1509465600001,true", - "1509465600002,false", - "1509465600003,false" + "1509465600000,true", + "1509465600001,true", + "1509465600002,false", + "1509465600003,false" }; statement.execute("SET STORAGE GROUP TO root.ln"); - statement.execute("CREATE TIMESERIES root.ln.\"wf.01\".wt01.\"status.2.3\" WITH DATATYPE=BOOLEAN, ENCODING=PLAIN"); - statement.execute("INSERT INTO root.ln.\"wf.01\".wt01(timestamp,\"status.2.3\") values(1509465600000,true)"); - statement.execute("INSERT INTO root.ln.\"wf.01\".wt01(timestamp,\"status.2.3\") values(1509465600001,true)"); - statement.execute("INSERT INTO root.ln.\"wf.01\".wt01(timestamp,\"status.2.3\") values(1509465600002,false)"); - statement.execute("INSERT INTO root.ln.\"wf.01\".wt01(timestamp,\"status.2.3\") values(1509465600003,false)"); - statement.execute("CREATE TIMESERIES root.ln.\"wf.01\".wt02.\"abd\" WITH DATATYPE=BOOLEAN, ENCODING=PLAIN"); - statement.execute("CREATE TIMESERIES root.ln.\"wf.01\".wt02.\"asf.asd.sdf\" WITH DATATYPE=BOOLEAN, ENCODING=PLAIN"); - statement.execute("CREATE TIMESERIES root.ln.\"wf.01\".wt02.\"asd12\" WITH DATATYPE=BOOLEAN, ENCODING=PLAIN"); - boolean hasResultSet = statement.execute("SELECT * FROM root.ln.\"wf.01\".wt01"); + statement.execute( + "CREATE TIMESERIES root.ln.\"wf.01\".wt01.\"status.2.3\" WITH DATATYPE=BOOLEAN, ENCODING=PLAIN"); + statement.execute( + "INSERT INTO root.ln.\"wf.01\".wt01(timestamp,\"status.2.3\") values(1509465600000,true)"); + statement.execute( + "INSERT INTO root.ln.\"wf.01\".wt01(timestamp,\"status.2.3\") values(1509465600001,true)"); + statement.execute( + "INSERT INTO root.ln.\"wf.01\".wt01(timestamp,\"status.2.3\") values(1509465600002,false)"); + statement.execute( + "INSERT INTO root.ln.\"wf.01\".wt01(timestamp,\"status.2.3\") values(1509465600003,false)"); + statement.execute( + "CREATE TIMESERIES root.ln.\"wf.01\".wt02.\"abd\" WITH DATATYPE=BOOLEAN, ENCODING=PLAIN"); + statement.execute( + "CREATE TIMESERIES root.ln.\"wf.01\".wt02.\"asf.asd.sdf\" WITH DATATYPE=BOOLEAN, ENCODING=PLAIN"); + statement.execute( + "CREATE TIMESERIES root.ln.\"wf.01\".wt02.\"asd12\" WITH DATATYPE=BOOLEAN, ENCODING=PLAIN"); + boolean hasResultSet = statement.execute("SELECT * FROM root.ln.\"wf.01\".wt01"); assertTrue(hasResultSet); - int cnt; - ArrayList ans = new ArrayList<>(); ResultSet resultSet = statement.getResultSet(); - cnt = 0; try { + int cnt = 0; while (resultSet.next()) { String result = resultSet.getString(TIMESTAMP_STR) + "," + resultSet.getString(2); - ans.add(result); - cnt++; - } - - for (int i = 0; i < ans.size(); i++) { - assertEquals(exp[i], ans.get(i)); + assertEquals(exp[cnt++], result); } - hasResultSet = statement.execute("SELECT * FROM root.ln.\"wf.01\".wt01 WHERE \"status.2.3\" = false"); + hasResultSet = statement + .execute("SELECT * FROM root.ln.\"wf.01\".wt01 WHERE \"status.2.3\" = false"); assertTrue(hasResultSet); exp = new String[]{ - "1509465600002,false", - "1509465600003,false" + "1509465600002,false", + "1509465600003,false" }; - ans = new ArrayList<>(); - resultSet = statement.getResultSet(); cnt = 0; + resultSet = statement.getResultSet(); while (resultSet.next()) { String result = resultSet.getString(TIMESTAMP_STR) + "," + resultSet.getString(2); - ans.add(result); - cnt++; + assertEquals(exp[cnt++], result); } - for (int i = 0; i < exp.length; i++) { - assertEquals(exp[i], ans.get(i)); + hasResultSet = statement + .execute( + "select \"status.2.3\", 'status.2.3' from root.ln.\"wf.01\".wt01 align by device"); + assertTrue(hasResultSet); + exp = new String[]{ + "1509465600000,root.ln.\"wf.01\".wt01,true,'status.2.3',", + "1509465600001,root.ln.\"wf.01\".wt01,true,'status.2.3',", + "1509465600002,root.ln.\"wf.01\".wt01,false,'status.2.3',", + "1509465600003,root.ln.\"wf.01\".wt01,false,'status.2.3'," + }; + cnt = 0; + resultSet = statement.getResultSet(); + while (resultSet.next()) { + StringBuilder builder = new StringBuilder(); + for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) { + builder.append(resultSet.getString(i)).append(","); + } + assertEquals(exp[cnt++], builder.toString()); } - statement.execute("DELETE FROM root.ln.\"wf.01\".wt01.\"status.2.3\" WHERE time < 1509465600001"); + + statement.execute( + "DELETE FROM root.ln.\"wf.01\".wt01.\"status.2.3\" WHERE time < 1509465600001"); statement.execute("DELETE TIMESERIES root.ln.\"wf.01\".wt01.\"status.2.3\""); } finally { resultSet.close(); @@ -120,7 +137,7 @@ public void test() throws SQLException { @Test public void testIllegalStorageGroup() throws SQLException { - try(Connection connection = DriverManager + try (Connection connection = DriverManager .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root"); Statement statement = connection.createStatement()) { 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 ed63228b51dcf..18f260b9e3531 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 @@ -35,6 +35,9 @@ public void testSplitPathToNodes() throws IllegalPathException { assertEquals(Arrays.asList("root", "sg", "d1", "\"s.1\"").toArray(), MetaUtils.splitPathToDetachedPath("root.sg.d1.\"s.1\"")); + assertEquals(Arrays.asList("root", "sg", "d1", "\"s\\\".1\"").toArray(), + MetaUtils.splitPathToDetachedPath("root.sg.d1.\"s\\\".1\"")); + assertEquals(Arrays.asList("root", "\"s g\"", "d1", "\"s.1\"").toArray(), MetaUtils.splitPathToDetachedPath("root.\"s g\".d1.\"s.1\"")); From a09d998297090b87630f9c8e65249ce8f190718f Mon Sep 17 00:00:00 2001 From: Alima777 <526213833@qq.com> Date: Thu, 10 Sep 2020 10:00:25 +0800 Subject: [PATCH 2/4] fix exception bug --- .../org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 97f543884389e..c0ec29ebe4f19 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 @@ -354,7 +354,7 @@ private PhysicalPlan transformQuery(QueryOperator queryOperator) throws QueryPro ((GroupByTimeFillPlan) queryPlan).setFillType(queryOperator.getFillTypes()); for (String aggregation : queryPlan.getAggregations()) { if (!SQLConstant.LAST_VALUE.equals(aggregation)) { - throw new QueryProcessException("Group By Fill only support last_value function."); + throw new QueryProcessException("Group By Fill only support last_value function"); } } } else { @@ -362,7 +362,7 @@ private PhysicalPlan transformQuery(QueryOperator queryOperator) throws QueryPro if (queryOperator.getLevel() >= 0) { for (String aggregation : queryPlan.getAggregations()) { if (!SQLConstant.COUNT.equals(aggregation)) { - throw new QueryProcessException("Group By Level only support count now."); + throw new QueryProcessException("group by level only support count now."); } } } From c7dadba0e763cbb43559b463c30970e3be739825 Mon Sep 17 00:00:00 2001 From: Alima777 <526213833@qq.com> Date: Fri, 11 Sep 2020 17:03:59 +0800 Subject: [PATCH 3/4] update path and doc --- docs/SystemDesign/DataQuery/AlignByDeviceQuery.md | 10 +++++----- docs/zh/SystemDesign/DataQuery/AlignByDeviceQuery.md | 10 +++++----- .../java/org/apache/iotdb/tsfile/read/common/Path.java | 4 ++++ .../org/apache/iotdb/tsfile/read/common/PathTest.java | 3 +++ 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/docs/SystemDesign/DataQuery/AlignByDeviceQuery.md b/docs/SystemDesign/DataQuery/AlignByDeviceQuery.md index 39367c63ae7f7..3a0f24ea3eea5 100644 --- a/docs/SystemDesign/DataQuery/AlignByDeviceQuery.md +++ b/docs/SystemDesign/DataQuery/AlignByDeviceQuery.md @@ -46,7 +46,7 @@ First explain the meaning of some important fields in AlignByDevicePlan: Before explaining the specific implementation process, a relatively complete example is given first, and the following explanation will be used in conjunction with this example. ```sql -SELECT s1, "1", *, s2, s5 FROM root.sg.d1, root.sg.* WHERE time = 1 AND s1 < 25 ALIGN BY DEVICE +SELECT s1, '1', *, s2, s5 FROM root.sg.d1, root.sg.* WHERE time = 1 AND s1 < 25 ALIGN BY DEVICE ``` Among them, the time series in the system is: @@ -87,7 +87,7 @@ It splices the suffix paths obtained in the SELECT statement with the prefix pat // See the following for an example Set measurementSetOfGivenSuffix = new LinkedHashSet<>(); // If a constant. Recording, continue to the next suffix path - if (suffixPath.startWith("'") || suffixPath.startWith("\"")) { + if (suffixPath.startWith("'"))) { ... continue; } @@ -174,11 +174,11 @@ In the example, the result of splicing the filter conditions of device 1 is `tim The following example summarizes the variable information calculated through this stage: -- measurement list `measurements`:`[s1, "1", s1, s2, s2, s5]` +- measurement list `measurements`:`[s1, '1', s1, s2, s2, s5]` - measurement type `measurementTypeMap`: - `s1 -> Exist` - `s2 -> Exist` - - `"1" -> Constant` + - `'1' -> Constant` - `s5 -> NonExist` - Filter condition `deviceToFilterMap` for each device: - `root.sg.d1 -> time = 1 AND root.sg.d1.s1 < 25` @@ -213,7 +213,7 @@ The resulting header is: | ---- | ------ | --- | --- | --- | --- | --- | --- | | | | | | | | | | -The deduplicated `measurements` are `[s1, "1", s2, s5]`. +The deduplicated `measurements` are `[s1, '1', s2, s5]`. ### Result set generation diff --git a/docs/zh/SystemDesign/DataQuery/AlignByDeviceQuery.md b/docs/zh/SystemDesign/DataQuery/AlignByDeviceQuery.md index c61c81241bd90..2305eec030bb5 100644 --- a/docs/zh/SystemDesign/DataQuery/AlignByDeviceQuery.md +++ b/docs/zh/SystemDesign/DataQuery/AlignByDeviceQuery.md @@ -47,7 +47,7 @@ AlignByDevicePlan 即按设备对齐查询对应的表结构为: 在进行具体实现过程的讲解前,先给出一个覆盖较为完整的例子,下面的解释过程中将结合该示例进行说明。 ```sql -SELECT s1, "1", *, s2, s5 FROM root.sg.d1, root.sg.* WHERE time = 1 AND s1 < 25 ALIGN BY DEVICE +SELECT s1, '1', *, s2, s5 FROM root.sg.d1, root.sg.* WHERE time = 1 AND s1 < 25 ALIGN BY DEVICE ``` 其中,系统中的时间序列为: @@ -87,7 +87,7 @@ SELECT s1, "1", *, s2, s5 FROM root.sg.d1, root.sg.* WHERE time = 1 AND s1 < 25 // 用于记录此后缀路径对应的所有 measurement,示例见下文 Set measurementSetOfGivenSuffix = new LinkedHashSet<>(); // 该后缀路径为常量,记录后继续遍历下一后缀路径 - if (suffixPath.startWith("'") || suffixPath.startWith("\"")) { + if (suffixPath.startWith("'")) { ... continue; } @@ -172,11 +172,11 @@ Map concatFilterByDevice(List devices, 下面用示例总结一下通过该阶段计算得到的变量信息: -- measurement 列表 `measurements`:`[s1, "1", s1, s2, s2, s5]` +- measurement 列表 `measurements`:`[s1, '1', s1, s2, s2, s5]` - measurement 类型 `measurementTypeMap`: - `s1 -> Exist` - `s2 -> Exist` - - `"1" -> Constant` + - `'1' -> Constant` - `s5 -> NonExist` - 每个设备的过滤条件 `deviceToFilterMap`: - `root.sg.d1 -> time = 1 AND root.sg.d1.s1 < 25` @@ -211,7 +211,7 @@ private void getAlignByDeviceQueryHeaders( | ---- | ------ | --- | --- | --- | --- | --- | --- | | | | | | | | | | -去重后的 `measurements` 为 `[s1, "1", s2, s5]`。 +去重后的 `measurements` 为 `[s1, '1', s2, s5]`。 ### 结果集生成 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 fa4fae6eb0e58..3b81e673682ac 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 @@ -61,6 +61,10 @@ public Path(String pathSc, boolean needSplit) { if (pathSc.length() > 0) { if (pathSc.charAt(pathSc.length() - 1) == TsFileConstant.DOUBLE_QUOTE) { int endIndex = pathSc.lastIndexOf('"', pathSc.length() - 2); + // if a double quotes with escape character + while (endIndex != -1 && pathSc.charAt(endIndex - 1) == '\\') { + endIndex = pathSc.lastIndexOf('"', endIndex - 2); + } if (endIndex != -1 && (endIndex == 0 || pathSc.charAt(endIndex - 1) == '.')) { fullPath = pathSc; device = pathSc.substring(0, endIndex - 1); diff --git a/tsfile/src/test/java/org/apache/iotdb/tsfile/read/common/PathTest.java b/tsfile/src/test/java/org/apache/iotdb/tsfile/read/common/PathTest.java index 856f6a05aa867..f253967cf4602 100644 --- a/tsfile/src/test/java/org/apache/iotdb/tsfile/read/common/PathTest.java +++ b/tsfile/src/test/java/org/apache/iotdb/tsfile/read/common/PathTest.java @@ -36,6 +36,9 @@ public void testPath() { Path d = new Path("s1", true); Assert.assertEquals("s1", d.getMeasurement()); Assert.assertEquals("", d.getDevice()); + Path e = new Path("root.\"s.g\".d1.\"s..\\\"s1\"", true); + Assert.assertEquals("root.\"s.g\".d1", e.getDevice()); + Assert.assertEquals("\"s..\\\"s1\"", e.getMeasurement()); } @Test(expected = IllegalArgumentException.class) From 8f222e947cb2b969ce895397062e9ad1175c538a Mon Sep 17 00:00:00 2001 From: Alima777 <526213833@qq.com> Date: Fri, 11 Sep 2020 17:14:08 +0800 Subject: [PATCH 4/4] add user guide doc --- docs/UserGuide/Concept/Data Model and Terminology.md | 2 +- docs/zh/UserGuide/Concept/Data Model and Terminology.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/UserGuide/Concept/Data Model and Terminology.md b/docs/UserGuide/Concept/Data Model and Terminology.md index 9941f65edc75f..f07c35e7a2ad9 100644 --- a/docs/UserGuide/Concept/Data Model and Terminology.md +++ b/docs/UserGuide/Concept/Data Model and Terminology.md @@ -84,7 +84,7 @@ Single quotes are not allowed in the path. If you want to use special characters The timeseries path is the core concept in IoTDB. A timeseries path can be thought of as the complete path of a sensor that produces the time series data. All timeseries paths in IoTDB must start with root and end with the sensor. A timeseries path can also be called a full path. -For example, if device1 of the vehicle type has a sensor named sensor1, its timeseries path can be expressed as: `root.vehicle.device1.sensor1`. +For example, if device1 of the vehicle type has a sensor named sensor1, its timeseries path can be expressed as: `root.vehicle.device1.sensor1`. Double quotes can be nested with escape characters, e.g. `root.sg.d1."s.\"t\"1"`. > Note: The layer of timeseries paths supported by the current IoTDB must be greater than or equal to four (it will be changed to two in the future). diff --git a/docs/zh/UserGuide/Concept/Data Model and Terminology.md b/docs/zh/UserGuide/Concept/Data Model and Terminology.md index faddc2e4bc3d6..35ac550bac4ae 100644 --- a/docs/zh/UserGuide/Concept/Data Model and Terminology.md +++ b/docs/zh/UserGuide/Concept/Data Model and Terminology.md @@ -74,7 +74,7 @@ LayerName: Identifier | STAR 值得说明的是,在路径中,root为一个保留字符,它只允许出现在下文提到的时间序列的开头,若其他层级出现root,则无法解析,提示报错。 -在路径中,不允许使用单引号。如果你想在LayerName中使用“.”等特殊字符,请使用双引号。例如,`root.sg."d.1"."s.1"`。 +在路径中,不允许使用单引号。如果你想在LayerName中使用“.”等特殊字符,请使用双引号。例如,`root.sg."d.1"."s.1"`。双引号内支持使用转义符进行双引号的嵌套,如 `root.sg.d1."s.\"t\"1"`。 > 注意: storage group中的LayerName只支持数字,字母,和下划线。