This is an automated email from the ASF dual-hosted git repository. JackieTien97 pushed a commit to branch ty/fix-pattern-match-repetition-signs in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 86b651ba5ecb2642c7f1ce1e84aa27934666abea Author: JackieTien97 <[email protected]> AuthorDate: Thu Oct 22 12:27:46 2026 +0800 [Bug] Reject misplaced repetition signs in pattern_match --- .../relational/it/db/it/IoTDBWindowTVFIT.java | 55 ++++++++++++++++------ .../apache/iotdb/commons/i18n/QueryMessages.java | 2 + .../apache/iotdb/commons/i18n/QueryMessages.java | 2 + .../function/tvf/match/QetchAlgorithm.java | 7 +++ .../tvf/PatternMatchTableFunctionTest.java | 45 +++++++++++++++++- 5 files changed, 96 insertions(+), 15 deletions(-) diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBWindowTVFIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBWindowTVFIT.java index 22d099e5bae..0adddc0cf69 100644 --- a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBWindowTVFIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBWindowTVFIT.java @@ -1083,20 +1083,50 @@ public class IoTDBWindowTVFIT { @Test public void testPatternMatchEmptyRepetitionGroups() throws SQLException { + assertInvalidPatternMatchPatterns( + new String[] { + "1,{,}+,1", + "1,{}+,1", + "1,{ , , }*,1", + "{,}+,1,2", + "1,2,{,}+", + "1,{2,{,}+,3}+,1", + "1,{{,}*}+,1" + }, + "Repetition groups must contain at least one numeric data point."); + } + + @Test + public void testPatternMatchMisplacedRepetitionSigns() throws SQLException { + assertInvalidPatternMatchPatterns( + new String[] { + "1,+,2", + "1,*,2", + "1, + ,2", + "1, * ,2", + "+,1,2", + "*,1,2", + "1,2,+", + "1,2,*", + "1+,2", + "1*,2", + "1,{2,+,3}+,1", + "1,{2,*,3}*,1", + "1,{2,3}++,1", + "1,{2,3}+*,1", + "1,{2,3}*+,1", + "1,{2,3}**,1" + }, + "Repetition signs '+' and '*' must immediately follow '}'."); + } + + private void assertInvalidPatternMatchPatterns(String[] patterns, String expectedMessage) + throws SQLException { try (Connection connection = EnvFactory.getEnv().getTableConnection(); Statement statement = connection.createStatement()) { statement.execute("USE " + DATABASE_NAME); for (String data : new String[] {"t1", "(SELECT * FROM t1 WHERE time < 0)"}) { - for (String pattern : - new String[] { - "1,{,}+,1", - "1,{}+,1", - "1,{ , , }*,1", - "{,}+,1,2", - "1,2,{,}+", - "1,{2,{,}+,3}+,1", - "1,{{,}*}+,1" - }) { + for (String pattern : patterns) { SQLException exception = assertThrows( SQLException.class, @@ -1115,10 +1145,7 @@ public class IoTDBWindowTVFIT { exception.getMessage(), exception .getMessage() - .contains( - "Invalid pattern: '" - + pattern - + "'. Repetition groups must contain at least one numeric data point.")); + .contains("Invalid pattern: '" + pattern + "'. " + expectedMessage)); } } } diff --git a/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/QueryMessages.java b/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/QueryMessages.java index 6f8fe1f305f..877bb671f6d 100644 --- a/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/QueryMessages.java +++ b/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/QueryMessages.java @@ -163,6 +163,8 @@ public final class QueryMessages { "height must be a non-negative number, but got: %s"; public static final String INVALID_PATTERN_MISSING_REPEAT_SIGN = "Invalid pattern: %s, missing repeat sign after '}'"; + public static final String EXCEPTION_INVALID_PATTERN_ARG_REPETITION_SIGNS_AND_MUST_IMMEDIATELY_FOLLOW_30FC3998 = + "Invalid pattern: '%s'. Repetition signs '+' and '*' must immediately follow '}'."; public static final String INVALID_PATTERN_MUST_CONTAIN_DATA_POINTS = "Invalid pattern: '%s'. Pattern must contain at least two numeric data points separated by commas, e.g., '1,2,3'"; public static final String EXCEPTION_INVALID_PATTERN_ARG_REPETITION_GROUPS_MUST_CONTAIN_AT_LEAST_ONE_NUMERIC_DATA_POINT_E1880CCB = diff --git a/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/QueryMessages.java b/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/QueryMessages.java index 19c5d8db981..840fb99ec6e 100644 --- a/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/QueryMessages.java +++ b/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/QueryMessages.java @@ -163,6 +163,8 @@ public final class QueryMessages { "height 必须为非负数,但传入了:%s"; public static final String INVALID_PATTERN_MISSING_REPEAT_SIGN = "无效的模式:%s,'}' 后缺少重复符号"; + public static final String EXCEPTION_INVALID_PATTERN_ARG_REPETITION_SIGNS_AND_MUST_IMMEDIATELY_FOLLOW_30FC3998 = + "无效的模式:'%s'。重复符号 '+' 和 '*' 必须紧跟在 '}' 后。"; public static final String INVALID_PATTERN_MUST_CONTAIN_DATA_POINTS = "无效的模式:'%s'。模式必须包含至少两个以逗号分隔的数值数据点,例如 '1,2,3'"; public static final String EXCEPTION_INVALID_PATTERN_ARG_REPETITION_GROUPS_MUST_CONTAIN_AT_LEAST_ONE_NUMERIC_DATA_POINT_E1880CCB = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/function/tvf/match/QetchAlgorithm.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/function/tvf/match/QetchAlgorithm.java index d0ef585197a..af231849631 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/function/tvf/match/QetchAlgorithm.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/function/tvf/match/QetchAlgorithm.java @@ -106,6 +106,13 @@ public class QetchAlgorithm { throw new IllegalArgumentException( String.format(QueryMessages.INVALID_PATTERN_MISSING_REPEAT_SIGN, pattern)); } + } else if (c == '+' || c == '*') { + // Valid repetition signs are consumed together with the preceding '}'. + throw new IllegalArgumentException( + String.format( + QueryMessages + .EXCEPTION_INVALID_PATTERN_ARG_REPETITION_SIGNS_AND_MUST_IMMEDIATELY_FOLLOW_30FC3998, + pattern)); } else if (Character.isDigit(c) || c == '.' || c == '-') { // scan the number, and push it to the list StringBuilder numBuilder = new StringBuilder(); diff --git a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/queryengine/plan/relational/function/tvf/PatternMatchTableFunctionTest.java b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/queryengine/plan/relational/function/tvf/PatternMatchTableFunctionTest.java index 6dd55366ec1..a55813fa599 100644 --- a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/queryengine/plan/relational/function/tvf/PatternMatchTableFunctionTest.java +++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/queryengine/plan/relational/function/tvf/PatternMatchTableFunctionTest.java @@ -78,6 +78,41 @@ public class PatternMatchTableFunctionTest { } } + @Test + public void testRejectMisplacedRepetitionSigns() { + for (String pattern : + new String[] { + "1,+,2", + "1,*,2", + "1, + ,2", + "1, * ,2", + "+,1,2", + "*,1,2", + "1,2,+", + "1,2,*", + "1+,2", + "1*,2", + "1,{2,+,3}+,1", + "1,{2,*,3}*,1", + "1,{2,3}++,1", + "1,{2,3}+*,1", + "1,{2,3}*+,1", + "1,{2,3}**,1" + }) { + String expectedMessage = + String.format( + QueryMessages + .EXCEPTION_INVALID_PATTERN_ARG_REPETITION_SIGNS_AND_MUST_IMMEDIATELY_FOLLOW_30FC3998, + pattern); + assertInvalidPattern(pattern, expectedMessage); + IllegalArgumentException exception = + assertThrows( + IllegalArgumentException.class, + () -> new QetchAlgorithm().parsePattern2Automaton(pattern)); + assertEquals(expectedMessage, exception.getMessage()); + } + } + @Test public void testRejectMalformedPatternsDuringAnalysis() { for (String pattern : new String[] {null, "", "1", "abc,def,ghi"}) { @@ -103,7 +138,15 @@ public class PatternMatchTableFunctionTest { @Test public void testAcceptNonEmptyRepetitionGroups() { for (String pattern : - new String[] {"1,{2,3,2}+,1", "1,{,2,3,2,}*,1", "1,{{2,3,2}+}+,1", "1,{2,{3,2}+,1}+,1"}) { + new String[] { + "1,{2,3,2}+,1", + "1,{2,3,2}*,1", + "1,{,2,3,2,}*,1", + "1,{{2,3,2}+}+,1", + "1,{{2,3,2}*}+,1", + "1,{2,{3,2}+,1}+,1", + "1,{2,{3,2}*,1}+,1" + }) { assertNotNull(createProvider(pattern).getDataProcessor()); } }
