clickhouse-java icon indicating copy to clipboard operation
clickhouse-java copied to clipboard

INSERT INTO VALUES statement fails to send INSERT clause when preparedStatement SQL is a concatenated string

Open troyjcurt opened this issue 6 months ago • 0 comments

Description

Steps to reproduce

  1. create a table in clickhouse
  2. create a prepared statement from a concatenated insert string containing the table name
  3. add multiple values to the prepared statement in a batch
  4. 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 TABLE statements 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) )

troyjcurt avatar Jun 24 '25 17:06 troyjcurt