Skip to content

Commit 166883a

Browse files
authored
Fix semantic error for unsupported table INSERT time expressions (#18759)
* Fix semantic error for unsupported table INSERT time expressions * Remove rejected insert time parameter integration test
1 parent daf9dd8 commit 166883a

2 files changed

Lines changed: 118 additions & 11 deletions

File tree

  • integration-test/src/test/java/org/apache/iotdb/relational/it/db/it
  • iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser

‎integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBInsertTableIT.java‎

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
import static org.junit.Assert.assertFalse;
7575
import static org.junit.Assert.assertNotNull;
7676
import static org.junit.Assert.assertNull;
77+
import static org.junit.Assert.assertThrows;
7778
import static org.junit.Assert.assertTrue;
7879
import static org.junit.Assert.fail;
7980

@@ -390,6 +391,122 @@ public void testInsertNullIntoTime() {
390391
}
391392
}
392393

394+
@Test
395+
public void testInsertUnsupportedTimeExpressions() throws SQLException {
396+
try (Connection connection = EnvFactory.getEnv().getTableConnection();
397+
Statement statement = connection.createStatement()) {
398+
statement.execute("USE test");
399+
statement.execute(
400+
"CREATE TABLE rejected_time_expression (device_id STRING TAG, value INT64 FIELD)");
401+
statement.execute(
402+
"CREATE TABLE rejected_custom_time_expression "
403+
+ "(device_id STRING TAG, event_time TIMESTAMP TIME, value INT64 FIELD)");
404+
String[] expressions = {"1735660800000 + 1000", "?", "1.5", "true", "abs(1)"};
405+
for (String expression : expressions) {
406+
for (String columns : new String[] {" (time, device_id, value)", ""}) {
407+
assertUnsupportedInsertExpression(
408+
statement,
409+
"INSERT INTO rejected_time_expression"
410+
+ columns
411+
+ " VALUES ("
412+
+ expression
413+
+ ", 'd1', 1)");
414+
}
415+
}
416+
for (String sql :
417+
new String[] {
418+
"INSERT INTO rejected_time_expression (device_id, value, time) VALUES ('d1', 1, ?)",
419+
"INSERT INTO rejected_custom_time_expression (device_id, event_time, value) "
420+
+ "VALUES ('d1', ?, 1)",
421+
"INSERT INTO rejected_custom_time_expression VALUES ('d1', ?, 1)",
422+
"INSERT INTO rejected_time_expression (time, device_id, value) "
423+
+ "VALUES (1735660800000, 'd1', ?)",
424+
"INSERT INTO rejected_time_expression (time, device_id, value) "
425+
+ "VALUES (1735660800000, 'valid', 1), (?, 'invalid', 2)",
426+
"INSERT INTO rejected_time_expression "
427+
+ "VALUES (1735660800000, 'valid', 1), (?, 'invalid', 2)"
428+
}) {
429+
assertUnsupportedInsertExpression(statement, sql);
430+
}
431+
for (String sql :
432+
new String[] {
433+
"INSERT INTO rejected_time_expression (time, device_id, value) VALUES (NULL, 'd1', 1)",
434+
"INSERT INTO rejected_time_expression VALUES (NULL, 'd1', 1)"
435+
}) {
436+
SQLException exception = assertThrows(SQLException.class, () -> statement.execute(sql));
437+
assertEquals(sql, TSStatusCode.SEMANTIC_ERROR.getStatusCode(), exception.getErrorCode());
438+
assertTrue(
439+
exception.getMessage(), exception.getMessage().contains("Timestamp cannot be null"));
440+
}
441+
for (String table :
442+
new String[] {"rejected_time_expression", "rejected_custom_time_expression"}) {
443+
try (ResultSet resultSet = statement.executeQuery("SELECT count(*) FROM " + table)) {
444+
assertTrue(resultSet.next());
445+
assertEquals(0, resultSet.getLong(1));
446+
assertFalse(resultSet.next());
447+
}
448+
}
449+
}
450+
}
451+
452+
private static void assertUnsupportedInsertExpression(Statement statement, String sql) {
453+
SQLException exception = assertThrows(SQLException.class, () -> statement.execute(sql));
454+
assertEquals(sql, TSStatusCode.SEMANTIC_ERROR.getStatusCode(), exception.getErrorCode());
455+
String message = exception.getMessage();
456+
assertTrue(message, message.contains("Unsupported expression:"));
457+
assertFalse(message, message.contains("ClassCastException"));
458+
assertFalse(message, message.contains("cannot be cast"));
459+
assertFalse(message, message.contains("org.apache.iotdb"));
460+
}
461+
462+
@Test
463+
public void testInsertSupportedTimeValues() throws SQLException {
464+
try (Connection connection = EnvFactory.getEnv().getTableConnection();
465+
Statement statement = connection.createStatement()) {
466+
statement.execute("USE test");
467+
statement.execute(
468+
"CREATE TABLE supported_time_expression (device_id STRING TAG, value INT64 FIELD)");
469+
String[] expressions = {
470+
"1735660800000", "'2025-01-01T00:00:00+08:00'", "NOW()", "NOW() + 1d", "NOW() - 1d"
471+
};
472+
for (boolean explicitColumns : new boolean[] {true, false}) {
473+
for (int i = 0; i < expressions.length; i++) {
474+
String deviceId = "d" + explicitColumns + i;
475+
long before = System.currentTimeMillis();
476+
statement.execute(
477+
"INSERT INTO supported_time_expression"
478+
+ (explicitColumns ? " (time, device_id, value)" : "")
479+
+ " VALUES ("
480+
+ expressions[i]
481+
+ ", '"
482+
+ deviceId
483+
+ "', "
484+
+ i
485+
+ ")");
486+
long after = System.currentTimeMillis();
487+
try (ResultSet resultSet =
488+
statement.executeQuery(
489+
"SELECT time, value FROM supported_time_expression WHERE device_id = '"
490+
+ deviceId
491+
+ "'")) {
492+
assertTrue(resultSet.next());
493+
long timestamp = resultSet.getLong(1);
494+
if (i < 2) {
495+
assertEquals(1735660800000L, timestamp);
496+
} else {
497+
long offset = i == 3 ? 86400000L : i == 4 ? -86400000L : 0;
498+
// Allow clock skew between the test client and cluster nodes.
499+
assertTrue(timestamp >= before + offset - 10000);
500+
assertTrue(timestamp <= after + offset + 10000);
501+
}
502+
assertEquals(i, resultSet.getLong(2));
503+
assertFalse(resultSet.next());
504+
}
505+
}
506+
}
507+
}
508+
}
509+
393510
@Test
394511
public void testInsertNaN() throws SQLException {
395512
try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java‎

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,17 +1037,7 @@ private InsertRowStatement toInsertRowStatement(
10371037
}
10381038

10391039
Expression timeExpression = expressions.get(timeColumnIndex);
1040-
if (timeExpression instanceof LongLiteral) {
1041-
timestamp = ((LongLiteral) timeExpression).getParsedValue();
1042-
} else if (timeExpression instanceof NullLiteral) {
1043-
throw new SemanticException(DataNodeQueryMessages.TIMESTAMP_CANNOT_BE_NULL);
1044-
} else {
1045-
timestamp =
1046-
parseDateTimeFormat(
1047-
((StringLiteral) timeExpression).getValue(),
1048-
CommonDateTimeUtils.currentTime(),
1049-
zoneId);
1050-
}
1040+
timestamp = AstUtil.expressionToTimestamp(timeExpression, zoneId);
10511041
nonTimeColCnt = expressions.size() - 1;
10521042
}
10531043

0 commit comments

Comments
 (0)