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

Executing insert statement throws a Missing input stream exception

Open chenyongyin opened this issue 2 years ago • 0 comments

Describe the bug

Throw an exception when I execute the "insert into test FORMAT JSONEachRow {"project_name":"jenny","client_type":"iphone","age":11}" statement

exception: ` org.springframework.jdbc.UncategorizedSQLException:

Error updating database. Cause: java.sql.SQLException: Missing input stream

The error may exist in com/cxmdata/das/mapper/EventMapper.java (best guess)

The error may involve com.cxmdata.das.mapper.EventMapper.batchInsertByJson-Inline

The error occurred while setting parameters

SQL: insert into test FORMAT JSONEachRow {"project_name":"jenny","client_type":"iphone","age":11}

Cause: java.sql.SQLException: Missing input stream

; uncategorized SQLException; SQL state [HY000]; error code [0]; Missing input stream; nested exception is java.sql.SQLException: Missing input stream at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:89) at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:88) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:440) at com.sun.proxy.$Proxy124.insert(Unknown Source) at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:271) at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:60) at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:148) at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89) at com.sun.proxy.$Proxy125.batchInsertByJson(Unknown Source) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266) at java.util.concurrent.FutureTask.run(FutureTask.java) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: java.sql.SQLException: Missing input stream at com.clickhouse.jdbc.SqlExceptionUtils.clientError(SqlExceptionUtils.java:73) at com.clickhouse.jdbc.internal.StreamBasedPreparedStatement.ensureParams(StreamBasedPreparedStatement.java:64) at com.clickhouse.jdbc.internal.StreamBasedPreparedStatement.execute(StreamBasedPreparedStatement.java:268) at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3446) at com.alibaba.druid.filter.FilterEventAdapter.preparedStatement_execute(FilterEventAdapter.java:434) at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3444) at com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl.execute(PreparedStatementProxyImpl.java:152) at com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement.java:483) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.ibatis.logging.jdbc.PreparedStatementLogger.invoke(PreparedStatementLogger.java:59) at com.sun.proxy.$Proxy207.execute(Unknown Source) at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:47) at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:74) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:63) at com.sun.proxy.$Proxy205.update(Unknown Source) at com.baomidou.mybatisplus.core.executor.MybatisSimpleExecutor.doUpdate(MybatisSimpleExecutor.java:56) at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117) at com.baomidou.mybatisplus.core.executor.MybatisCachingExecutor.update(MybatisCachingExecutor.java:85) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.ibatis.plugin.Invocation.proceed(Invocation.java:49) at com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor.intercept(MybatisPlusInterceptor.java:106) at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61) at com.sun.proxy.$Proxy204.update(Unknown Source) at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:197) at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:184) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:426) `

Steps to reproduce

clickhouse-jdbc version: 0.4.0~0.4.6 clickhouse-server version: 22.5.1.2079

mapper: ` public interface TestMapper {

@Insert("insert into test FORMAT JSONEachRow ${eventRecord}")
int batchInsertByJson(@Param("eventRecord") String eventRecord);

} ` This exception was thrown by StreamBasedPreparedStatement.ensueParams(), because value is null. I wonder if this execution statement has no parameters, resulting in value being null; But should this situation not validate the value, because the hasParameter was determined in the ClickHouseConnectionImpl. prepareStatement() method, and therefore the StreamBasedPreparedStatement was executed

ClickHouseConnectionImpl.prepareStatement() code: ` public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { ensureOpen();

    ClickHouseConfig config = clientRequest.getConfig();
    // TODO remove the extra parsing
    ClickHouseSqlStatement[] stmts = parse(sql, config, clientRequest.getSettings());
    if (stmts.length != 1) {
        throw SqlExceptionUtils
                .clientError("Prepared statement only supports one query but we got: " + stmts.length);
    }
    ClickHouseSqlStatement parsedStmt = stmts[0];

    ClickHouseParameterizedQuery preparedQuery;
    try {
        preparedQuery = jdbcConf.useNamedParameter()
                ? ClickHouseParameterizedQuery.of(clientRequest.getConfig(), parsedStmt.getSQL())
                : JdbcParameterizedQuery.of(config, parsedStmt.getSQL());
    } catch (RuntimeException e) {
        throw SqlExceptionUtils.clientError(e);
    }

    PreparedStatement ps = null;
    // **preparedQuery.hasParameter() is false**
    if (preparedQuery.hasParameter()) {
        if (parsedStmt.hasTempTable() || parsedStmt.hasInput()) {
            throw SqlExceptionUtils
                    .clientError(
                            "External table, input function, and query parameter cannot be used together in PreparedStatement.");
        } else if (parsedStmt.getStatementType() == StatementType.INSERT &&
                !parsedStmt.containsKeyword("SELECT") && parsedStmt.hasValues() &&
                (!parsedStmt.hasFormat() || clientRequest.getFormat().name().equals(parsedStmt.getFormat()))) {
            String query = parsedStmt.getSQL();
            boolean useStream = false;
            Integer startIndex = parsedStmt.getPositions().get(ClickHouseSqlStatement.KEYWORD_VALUES_START);
            if (startIndex != null) {
                useStream = true;
                int endIndex = parsedStmt.getPositions().get(ClickHouseSqlStatement.KEYWORD_VALUES_END);
                for (int i = startIndex + 1; i < endIndex; i++) {
                    char ch = query.charAt(i);
                    if (ch != '?' && ch != ',' && !Character.isWhitespace(ch)) {
                        useStream = false;
                        break;
                    }
                }
            }

            if (useStream) {
                ps = new InputBasedPreparedStatement(this,
                        clientRequest.write().query(query.substring(0, parsedStmt.getStartPosition("VALUES")),
                                newQueryId()),
                        getTableColumns(parsedStmt.getDatabase(), parsedStmt.getTable(),
                                parsedStmt.getContentBetweenKeywords(
                                        ClickHouseSqlStatement.KEYWORD_TABLE_COLUMNS_START,
                                        ClickHouseSqlStatement.KEYWORD_TABLE_COLUMNS_END)),
                        resultSetType, resultSetConcurrency, resultSetHoldability);
            }
        }
    } else {
        if (parsedStmt.hasTempTable()) {
            // queries using external/temporary table
            ps = new TableBasedPreparedStatement(this,
                    clientRequest.copy().query(parsedStmt.getSQL(), newQueryId()), parsedStmt,
                    resultSetType, resultSetConcurrency, resultSetHoldability);
        } else if (parsedStmt.getStatementType() == StatementType.INSERT) {
            if (!ClickHouseChecker.isNullOrBlank(parsedStmt.getInput())) {
                // an ugly workaround of https://github.com/ClickHouse/ClickHouse/issues/39866
                // would be replace JSON and Object('json') types in the query to String

                Mutation m = clientRequest.write();
                if (parsedStmt.hasFormat()) {
                    m.format(ClickHouseFormat.valueOf(parsedStmt.getFormat()));
                }
                // insert query using input function
                ps = new InputBasedPreparedStatement(this, m.query(parsedStmt.getSQL(), newQueryId()),
                        ClickHouseColumn.parse(parsedStmt.getInput()), resultSetType, resultSetConcurrency,
                        resultSetHoldability);
            } else if (!parsedStmt.containsKeyword("SELECT") && !parsedStmt.hasValues()) {
                // **parsedStmt.hasFormat() is true**
                ps = parsedStmt.hasFormat()
                        ? new StreamBasedPreparedStatement(this,
                                clientRequest.write().query(parsedStmt.getSQL(), newQueryId()), parsedStmt,
                                resultSetType, resultSetConcurrency, resultSetHoldability)
                        : new InputBasedPreparedStatement(this,
                                clientRequest.write().query(parsedStmt.getSQL(), newQueryId()),
                                getTableColumns(parsedStmt.getDatabase(), parsedStmt.getTable(),
                                        parsedStmt.getContentBetweenKeywords(
                                                ClickHouseSqlStatement.KEYWORD_TABLE_COLUMNS_START,
                                                ClickHouseSqlStatement.KEYWORD_TABLE_COLUMNS_END)),
                                resultSetType, resultSetConcurrency, resultSetHoldability);
            }
        }
    }

    return ps != null ? ps
            : new SqlBasedPreparedStatement(this, clientRequest.copy().query(preparedQuery, newQueryId()),
                    stmts[0], resultSetType, resultSetConcurrency, resultSetHoldability);
}

`

StreamBasedPreparedStatement.ensureParams() code: protected void ensureParams() throws SQLException { if (value == null) { throw SqlExceptionUtils.clientError("Missing input stream"); } }

chenyongyin avatar Dec 06 '23 07:12 chenyongyin