diff --git a/clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/ClickHousePreparedStatement.java b/clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/ClickHousePreparedStatement.java index 7e7788e36..5dddf3608 100644 --- a/clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/ClickHousePreparedStatement.java +++ b/clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/ClickHousePreparedStatement.java @@ -24,6 +24,10 @@ import com.clickhouse.data.format.BinaryStreamUtils; public interface ClickHousePreparedStatement extends PreparedStatement { + default ResultSetMetaData describeQueryResult() throws SQLException { + return null; + } + @Override default void setNull(int parameterIndex, int sqlType) throws SQLException { setNull(parameterIndex, sqlType, null); @@ -109,7 +113,12 @@ default void setClob(int parameterIndex, Clob x) throws SQLException { @Override default ResultSetMetaData getMetaData() throws SQLException { ResultSet currentResult = getResultSet(); - return currentResult != null ? currentResult.getMetaData() : null; + if (currentResult != null) { + return currentResult.getMetaData(); + } else if (getLargeUpdateCount() != -1L) { + return null; + } + return describeQueryResult(); } @Override diff --git a/clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/internal/SqlBasedPreparedStatement.java b/clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/internal/SqlBasedPreparedStatement.java index 0bf2a22ba..aff75d110 100644 --- a/clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/internal/SqlBasedPreparedStatement.java +++ b/clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/internal/SqlBasedPreparedStatement.java @@ -5,7 +5,9 @@ import java.sql.Date; import java.sql.ParameterMetaData; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; import java.time.LocalDate; @@ -35,7 +37,9 @@ import com.clickhouse.data.value.ClickHouseStringValue; import com.clickhouse.logging.Logger; import com.clickhouse.logging.LoggerFactory; +import com.clickhouse.jdbc.ClickHouseConnection; import com.clickhouse.jdbc.ClickHousePreparedStatement; +import com.clickhouse.jdbc.ClickHouseResultSetMetaData; import com.clickhouse.jdbc.JdbcParameterizedQuery; import com.clickhouse.jdbc.SqlExceptionUtils; import com.clickhouse.jdbc.parser.ClickHouseSqlStatement; @@ -239,6 +243,39 @@ protected int getMaxParameterIndex() { return templates.length; } + @Override + public ResultSetMetaData describeQueryResult() throws SQLException { + if (parsedStmt.isRecognized() && !parsedStmt.isQuery()) { + return null; + } + + final String[] vals; + final int len = values.length; + if (batch.isEmpty()) { + vals = new String[len]; + System.arraycopy(this.values, 0, vals, 0, len); + } else { + vals = batch.get(0); + } + for (int i = 0; i < len; i++) { + if (vals[i] == null) { + vals[i] = ClickHouseValues.NULL_EXPR; + } + } + + final ClickHouseConnection conn = getConnection(); + StringBuilder sb = new StringBuilder("desc ("); + preparedQuery.apply(sb, vals); + List columns = new LinkedList<>(); + try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sb.append(')').toString())) { + while (rs.next()) { + columns.add(ClickHouseColumn.of(rs.getString(1), rs.getString(2))); + } + } + return ClickHouseResultSetMetaData.of(conn.getJdbcConfig(), conn.getCurrentDatabase(), "", + Collections.unmodifiableList(new ArrayList<>(columns)), mapper, conn.getTypeMap()); + } + @Override public ResultSet executeQuery() throws SQLException { ensureParams(); diff --git a/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/ClickHousePreparedStatementTest.java b/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/ClickHousePreparedStatementTest.java index 4db1138a4..983c9a010 100644 --- a/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/ClickHousePreparedStatementTest.java +++ b/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/ClickHousePreparedStatementTest.java @@ -4,6 +4,7 @@ import java.io.File; import java.io.IOException; import java.math.BigDecimal; +import java.math.BigInteger; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.MalformedURLException; @@ -16,6 +17,7 @@ import java.sql.ParameterMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; @@ -2005,6 +2007,53 @@ public void testInsertWithSettings() throws SQLException { } } + @Test(groups = "integration") + public void testGetMetaData() throws SQLException { + try (Connection conn = newConnection(new Properties()); + PreparedStatement ps = conn.prepareStatement("select ? a, ? b")) { + ResultSetMetaData md = ps.getMetaData(); + Assert.assertEquals(md.getColumnCount(), 2); + Assert.assertEquals(md.getColumnName(1), "a"); + Assert.assertEquals(md.getColumnTypeName(1), "Nullable(Nothing)"); + Assert.assertEquals(md.getColumnName(2), "b"); + Assert.assertEquals(md.getColumnTypeName(2), "Nullable(Nothing)"); + + ps.setString(1, "x"); + md = ps.getMetaData(); + Assert.assertEquals(md.getColumnCount(), 2); + Assert.assertEquals(md.getColumnName(1), "a"); + Assert.assertEquals(md.getColumnTypeName(1), "String"); + Assert.assertEquals(md.getColumnName(2), "b"); + Assert.assertEquals(md.getColumnTypeName(2), "Nullable(Nothing)"); + + ps.setObject(2, new BigInteger("12345")); + md = ps.getMetaData(); + Assert.assertEquals(md.getColumnCount(), 2); + Assert.assertEquals(md.getColumnName(1), "a"); + Assert.assertEquals(md.getColumnTypeName(1), "String"); + Assert.assertEquals(md.getColumnName(2), "b"); + Assert.assertEquals(md.getColumnTypeName(2), "UInt16"); + + ps.addBatch(); + ps.setInt(1, 2); + md = ps.getMetaData(); + Assert.assertEquals(md.getColumnCount(), 2); + Assert.assertEquals(md.getColumnName(1), "a"); + Assert.assertEquals(md.getColumnTypeName(1), "String"); + Assert.assertEquals(md.getColumnName(2), "b"); + Assert.assertEquals(md.getColumnTypeName(2), "UInt16"); + + ps.clearBatch(); + ps.clearParameters(); + md = ps.getMetaData(); + Assert.assertEquals(md.getColumnCount(), 2); + Assert.assertEquals(md.getColumnName(1), "a"); + Assert.assertEquals(md.getColumnTypeName(1), "Nullable(Nothing)"); + Assert.assertEquals(md.getColumnName(2), "b"); + Assert.assertEquals(md.getColumnTypeName(2), "Nullable(Nothing)"); + } + } + @Test(groups = "integration") public void testGetParameterMetaData() throws SQLException { try (Connection conn = newConnection(new Properties());