clickhouse-java
clickhouse-java copied to clipboard
INSERT INTO VALUES statement fails to send INSERT clause when preparedStatement SQL is a concatenated string
Description
Steps to reproduce
- create a table in clickhouse
- create a prepared statement from a concatenated insert string containing the table name
- add multiple values to the prepared statement in a batch
- use executeLargeBatch
Error Log or Exception StackTrace
Caused by: java.sql.SQLException: Code: 62. DB::Exception: Syntax error: failed at position 3 ('1'): 1, 'Alice', {'key1': 'val1'}, 1750785420950, 50.75). Expected one of: SELECT query, possibly with UNION, list of union elements, SELECT query, subquery, possibly with UNION, SELECT subquery, SELECT query, WITH, FROM, SELECT, EXPLAIN, EXPLAIN. (SYNTAX_ERROR) (version 24.5.3.5 (official build))
at com.clickhouse.jdbc.internal.ExceptionUtils.toSqlState(ExceptionUtils.java:67) ~[jdbc-v2-0.8.5.jar!/:jdbc-v2 0.8.5 (revision: 547e352)]
at com.clickhouse.jdbc.internal.ExceptionUtils.toSqlState(ExceptionUtils.java:42) ~[jdbc-v2-0.8.5.jar!/:jdbc-v2 0.8.5 (revision: 547e352)]
at com.clickhouse.jdbc.StatementImpl.executeUpdateImpl(StatementImpl.java:250) ~[jdbc-v2-0.8.5.jar!/:clickhouse-jdbc 0.8.5 (revision: 547e352)]
at com.clickhouse.jdbc.PreparedStatementImpl.executeBatchImpl(PreparedStatementImpl.java:294) ~[jdbc-v2-0.8.5.jar!/:clickhouse-jdbc 0.8.5 (revision: 547e352)]
at com.clickhouse.jdbc.PreparedStatementImpl.executeLargeBatch(PreparedStatementImpl.java:287) ~[jdbc-v2-0.8.5.jar!/:clickhouse-jdbc 0.8.5 (revision: 547e352)]
at com.example.clickhouse.service.ClickHouseService.batchInsert(ClickHouseService.java:65) ~[!/:1.0-SNAPSHOT]
at com.example.clickhouse.service.ClickHouseService.run(ClickHouseService.java:43) ~[!/:1.0-SNAPSHOT]
at org.springframework.boot.SpringApplication.lambda$callRunner$4(SpringApplication.java:786) ~[spring-boot-3.2.2.jar!/:3.2.2]
at org.springframework.util.function.ThrowingConsumer$1.acceptWithException(ThrowingConsumer.java:83) ~[spring-core-6.1.3.jar!/:6.1.3]
at org.springframework.util.function.ThrowingConsumer.accept(ThrowingConsumer.java:60) ~[spring-core-6.1.3.jar!/:6.1.3]
... 24 common frames omitted
Caused by: com.clickhouse.client.api.ServerException: Code: 62. DB::Exception: Syntax error: failed at position 3 ('1'): 1, 'Alice', {'key1': 'val1'}, 1750785420950, 50.75). Expected one of: SELECT query, possibly with UNION, list of union elements, SELECT query, subquery, possibly with UNION, SELECT subquery, SELECT query, WITH, FROM, SELECT, EXPLAIN, EXPLAIN. (SYNTAX_ERROR) (version 24.5.3.5 (official build))
at com.clickhouse.client.api.internal.HttpAPIClientHelper.readError(HttpAPIClientHelper.java:371) ~[jdbc-v2-0.8.5.jar!/:jdbc-v2 0.8.5 (revision: 547e352)]
at com.clickhouse.client.api.internal.HttpAPIClientHelper.executeRequest(HttpAPIClientHelper.java:426) ~[jdbc-v2-0.8.5.jar!/:jdbc-v2 0.8.5 (revision: 547e352)]
at com.clickhouse.client.api.Client.lambda$query$10(Client.java:1727) ~[jdbc-v2-0.8.5.jar!/:jdbc-v2 0.8.5 (revision: 547e352)]
at com.clickhouse.client.api.Client.runAsyncOperation(Client.java:2156) ~[jdbc-v2-0.8.5.jar!/:jdbc-v2 0.8.5 (revision: 547e352)]
at com.clickhouse.client.api.Client.query(Client.java:1770) ~[jdbc-v2-0.8.5.jar!/:jdbc-v2 0.8.5 (revision: 547e352)]
at com.clickhouse.client.api.Client.query(Client.java:1671) ~[jdbc-v2-0.8.5.jar!/:jdbc-v2 0.8.5 (revision: 547e352)]
at com.clickhouse.jdbc.StatementImpl.executeUpdateImpl(StatementImpl.java:243) ~[jdbc-v2-0.8.5.jar!/:clickhouse-jdbc 0.8.5 (revision: 547e352)]
... 31 common frames omitted
Expected Behaviour
Data is inserted successfully with the prepared statement made from the given sql string.
Code Example
*SimpleRecord is a very basic POJO containing only a constructor of form (int, String, Map<String,String>, long, Float)
package com.example.clickhouse.service;
import com.example.clickhouse.model.SimpleRecord;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
@Service
public class ClickHouseService implements ApplicationRunner {
private final Connection connection;
private final String TABLE_NAME="test_table";
public ClickHouseService(Connection connection) {
this.connection = connection;
}
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("Application Started. Waiting 1 minute for clickhouse to become stable...");
Thread.sleep(5000);
System.out.println("Creating Table: test_table");
try (Statement stmt = connection.createStatement()) {
stmt.execute("""
CREATE TABLE IF NOT EXISTS test_table (
id UInt64,
NameCol String,
MapCol Map(LowCardinality(String), String),
TimeCol TIMESTAMP,
Value Float
) ENGINE = MergeTree()
ORDER BY id
""");
}
batchInsert(List.of(
new SimpleRecord(1, "Alice", Map.of("key1","val1"), System.currentTimeMillis(), 50.75),
new SimpleRecord(2, "Bob", Map.of("key1","val1", "key2","val2"), System.currentTimeMillis(), 25.15),
new SimpleRecord(3, "Charlie", Map.of("key2","val2"), System.currentTimeMillis(), 35.84),
new SimpleRecord(4, "Darrel", Map.of("key3","val5"), System.currentTimeMillis(), 37.45)
));
}
public void batchInsert(List<SimpleRecord> records) throws Exception {
String sql = "INSERT INTO " + TABLE_NAME + " VALUES (?, ?, ?, ?, ?) ";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
for (SimpleRecord record : records) {
System.out.println("Adding record to batch for: " + record.name());
pstmt.setInt(1, record.id());
pstmt.setString(2, record.name());
pstmt.setObject(3, record.Attributes());
pstmt.setLong(4, record.timeMillis());
pstmt.setDouble(5, record.Value());
pstmt.addBatch();
}
System.out.println("Executing Batch");
pstmt.executeLargeBatch();
}
Thread.sleep(5000); // Wait 5 seconds before fetching
fetchAll();
}
public void fetchAll() throws Exception {
String sql = "SELECT * FROM test_table ORDER BY id";
try (Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
System.out.println("id: " + rs.getInt("id") + ", Name: " + rs.getString("NameCol") + ", Attributes: " + rs.getString("MapCol") + ", TimeStamp: " + rs.getString("TimeCol") + ", Value: " + rs.getDouble("Value"));
}
}
}
}
Configuration
Client Configuration
full pom.xml provided
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.2</version>
</parent>
<groupId>org.example</groupId>
<artifactId>clickhouse-jdbc-bug-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<description>Demonstration of JDBC 0.8.5 Issues</description>
<properties>
<java.version>17</java.version>
<frontend-maven-plugin.version>1.12.1</frontend-maven-plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.clickhouse</groupId>
<artifactId>clickhouse-jdbc</artifactId>
<version>0.8.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Environment
- Client version: 0.8.5
- Language version: Java 17.0.6
- OS: CentOS 8 (also seen on Rocky9)
ClickHouse Server
- ClickHouse Server version: 25.5.2
- ClickHouse Server non-default settings, if any: N/A
CREATE TABLEstatements for tables involved: CREATE TABLE IF NOT EXISTS test_table ( id UInt64, NameCol String, MapCol Map(LowCardinality(String), String), TimeCol TIMESTAMP, Value Float ) ENGINE = MergeTree() ORDER BY id- Sample data for all these tables List.of( new SimpleRecord(1, "Alice", Map.of("key1","val1"), System.currentTimeMillis(), 50.75), new SimpleRecord(2, "Bob", Map.of("key1","val1", "key2","val2"), System.currentTimeMillis(), 25.15), new SimpleRecord(3, "Charlie", Map.of("key2","val2"), System.currentTimeMillis(), 35.84), new SimpleRecord(4, "Darrel", Map.of("key3","val5"), System.currentTimeMillis(), 37.45) )