Skip to content

Commit abe31c6

Browse files
authored
Merge pull request #1700 from sylph-eu/implement_get_metadata_for_non_executed_query
[clickhouse-jdbc] describe SELECT queries to retrieve metadata
2 parents 1b0a40e + 5399034 commit abe31c6

File tree

4 files changed

+130
-1
lines changed

4 files changed

+130
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## Latest
22

3+
### New Features
4+
- Describe non-executed SELECT queries in prepared statements to provide metadata (https://github.com/ClickHouse/clickhouse-java/issues/1430)
5+
36
## 0.6.1
47

58
### New Features

clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/ClickHousePreparedStatement.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,17 @@ default void setClob(int parameterIndex, Clob x) throws SQLException {
109109
@Override
110110
default ResultSetMetaData getMetaData() throws SQLException {
111111
ResultSet currentResult = getResultSet();
112-
return currentResult != null ? currentResult.getMetaData() : null;
112+
if (currentResult != null) {
113+
return currentResult.getMetaData();
114+
} else if (getLargeUpdateCount() != -1L) {
115+
return null; // Update query
116+
}
117+
118+
return describeQueryResult();
119+
}
120+
121+
default ResultSetMetaData describeQueryResult() throws SQLException {
122+
return null;
113123
}
114124

115125
@Override

clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/internal/SqlBasedPreparedStatement.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
import java.sql.Date;
66
import java.sql.ParameterMetaData;
77
import java.sql.ResultSet;
8+
import java.sql.ResultSetMetaData;
89
import java.sql.SQLException;
10+
import java.sql.Statement;
911
import java.sql.Time;
1012
import java.sql.Timestamp;
1113
import java.time.LocalDate;
@@ -35,7 +37,9 @@
3537
import com.clickhouse.data.value.ClickHouseStringValue;
3638
import com.clickhouse.logging.Logger;
3739
import com.clickhouse.logging.LoggerFactory;
40+
import com.clickhouse.jdbc.ClickHouseConnection;
3841
import com.clickhouse.jdbc.ClickHousePreparedStatement;
42+
import com.clickhouse.jdbc.ClickHouseResultSetMetaData;
3943
import com.clickhouse.jdbc.JdbcParameterizedQuery;
4044
import com.clickhouse.jdbc.SqlExceptionUtils;
4145
import com.clickhouse.jdbc.parser.ClickHouseSqlStatement;
@@ -239,6 +243,42 @@ protected int getMaxParameterIndex() {
239243
return templates.length;
240244
}
241245

246+
@Override
247+
public ResultSetMetaData describeQueryResult() throws SQLException {
248+
// No metadata unless query has been recognized as SELECT
249+
if (!parsedStmt.isRecognized() || !parsedStmt.isQuery()) {
250+
return null;
251+
}
252+
253+
final String[] vals;
254+
if (batch.isEmpty()) {
255+
vals = new String[values.length];
256+
System.arraycopy(this.values, 0, vals, 0, values.length);
257+
} else {
258+
vals = batch.get(0);
259+
}
260+
for (int i = 0; i < values.length; i++) {
261+
if (vals[i] == null) {
262+
vals[i] = ClickHouseValues.NULL_EXPR;
263+
}
264+
}
265+
266+
StringBuilder sb = new StringBuilder("desc (");
267+
preparedQuery.apply(sb, vals);
268+
sb.append(')');
269+
270+
List<ClickHouseColumn> columns = new LinkedList<>();
271+
ClickHouseConnection conn = getConnection();
272+
try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sb.toString())) {
273+
while (rs.next()) {
274+
columns.add(ClickHouseColumn.of(rs.getString(1), rs.getString(2)));
275+
}
276+
}
277+
278+
return ClickHouseResultSetMetaData.of(conn.getJdbcConfig(), conn.getCurrentDatabase(), "",
279+
Collections.unmodifiableList(new ArrayList<>(columns)), mapper, conn.getTypeMap());
280+
}
281+
242282
@Override
243283
public ResultSet executeQuery() throws SQLException {
244284
ensureParams();

clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/ClickHousePreparedStatementTest.java

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.io.File;
55
import java.io.IOException;
66
import java.math.BigDecimal;
7+
import java.math.BigInteger;
78
import java.net.Inet4Address;
89
import java.net.Inet6Address;
910
import java.net.MalformedURLException;
@@ -16,6 +17,7 @@
1617
import java.sql.ParameterMetaData;
1718
import java.sql.PreparedStatement;
1819
import java.sql.ResultSet;
20+
import java.sql.ResultSetMetaData;
1921
import java.sql.SQLException;
2022
import java.sql.Statement;
2123
import java.sql.Timestamp;
@@ -2006,6 +2008,80 @@ public void testInsertWithSettings() throws SQLException {
20062008
}
20072009
}
20082010

2011+
@Test(groups = "integration")
2012+
public void testGetMetadataTypes() throws SQLException {
2013+
try (Connection conn = newConnection(new Properties());
2014+
PreparedStatement ps = conn.prepareStatement("select ? a, ? b")) {
2015+
ResultSetMetaData md = ps.getMetaData();
2016+
Assert.assertEquals(md.getColumnCount(), 2);
2017+
Assert.assertEquals(md.getColumnName(1), "a");
2018+
Assert.assertEquals(md.getColumnTypeName(1), "Nullable(Nothing)");
2019+
Assert.assertEquals(md.getColumnName(2), "b");
2020+
Assert.assertEquals(md.getColumnTypeName(2), "Nullable(Nothing)");
2021+
2022+
ps.setString(1, "x");
2023+
md = ps.getMetaData();
2024+
Assert.assertEquals(md.getColumnCount(), 2);
2025+
Assert.assertEquals(md.getColumnName(1), "a");
2026+
Assert.assertEquals(md.getColumnTypeName(1), "String");
2027+
Assert.assertEquals(md.getColumnName(2), "b");
2028+
Assert.assertEquals(md.getColumnTypeName(2), "Nullable(Nothing)");
2029+
2030+
ps.setObject(2, new BigInteger("12345"));
2031+
md = ps.getMetaData();
2032+
Assert.assertEquals(md.getColumnCount(), 2);
2033+
Assert.assertEquals(md.getColumnName(1), "a");
2034+
Assert.assertEquals(md.getColumnTypeName(1), "String");
2035+
Assert.assertEquals(md.getColumnName(2), "b");
2036+
Assert.assertEquals(md.getColumnTypeName(2), "UInt16");
2037+
2038+
ps.addBatch();
2039+
ps.setInt(1, 2);
2040+
md = ps.getMetaData();
2041+
Assert.assertEquals(md.getColumnCount(), 2);
2042+
Assert.assertEquals(md.getColumnName(1), "a");
2043+
Assert.assertEquals(md.getColumnTypeName(1), "String");
2044+
Assert.assertEquals(md.getColumnName(2), "b");
2045+
Assert.assertEquals(md.getColumnTypeName(2), "UInt16");
2046+
2047+
ps.clearBatch();
2048+
ps.clearParameters();
2049+
md = ps.getMetaData();
2050+
Assert.assertEquals(md.getColumnCount(), 2);
2051+
Assert.assertEquals(md.getColumnName(1), "a");
2052+
Assert.assertEquals(md.getColumnTypeName(1), "Nullable(Nothing)");
2053+
Assert.assertEquals(md.getColumnName(2), "b");
2054+
Assert.assertEquals(md.getColumnTypeName(2), "Nullable(Nothing)");
2055+
}
2056+
}
2057+
2058+
@Test(groups = "integration")
2059+
public void testGetMetadataStatements() throws SQLException {
2060+
try (Connection conn = newConnection(new Properties());
2061+
PreparedStatement createPs = conn.prepareStatement("create table test_get_metadata_statements (col String) Engine=Log");
2062+
PreparedStatement selectPs = conn.prepareStatement("select 'Hello, World!'");
2063+
PreparedStatement insertPs = conn.prepareStatement(
2064+
"insert into test_get_metadata_statements select 'Hello, World!'");
2065+
PreparedStatement updatePs = conn.prepareStatement(
2066+
"update test_get_metadata_statements set col = 'Bye, World!'");
2067+
PreparedStatement grantPs = conn.prepareStatement("grant select on * to default");
2068+
PreparedStatement commitPS = conn.prepareStatement("commit");) {
2069+
2070+
// Only select shall have valid metadata
2071+
ResultSetMetaData selectMetaData = selectPs.getMetaData();
2072+
Assert.assertNotNull(selectMetaData);
2073+
Assert.assertEquals(selectMetaData.getColumnCount(), 1);
2074+
Assert.assertEquals(selectMetaData.getColumnTypeName(1), "String");
2075+
2076+
// The rest shall return null
2077+
Assert.assertNull(createPs.getMetaData());
2078+
Assert.assertNull(insertPs.getMetaData());
2079+
Assert.assertNull(updatePs.getMetaData());
2080+
Assert.assertNull(grantPs.getMetaData());
2081+
Assert.assertNull(commitPS.getMetaData());
2082+
}
2083+
}
2084+
20092085
@Test(groups = "integration")
20102086
public void testGetParameterMetaData() throws SQLException {
20112087
try (Connection conn = newConnection(new Properties());

0 commit comments

Comments
 (0)