Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ private String buildSQL() throws SQLException {
public ResultSet executeQuery() throws SQLException {
ensureOpen();
String buildSQL = buildSQL();
return super.executeQueryImpl(buildSQL, localSettings);
return super.executeQuery(buildSQL);
}

@Override
Expand Down Expand Up @@ -293,9 +293,10 @@ public void setObject(int parameterIndex, Object x) throws SQLException {
@Override
public boolean execute() throws SQLException {
ensureOpen();
currentUpdateCount = -1;
if (parsedPreparedStatement.isHasResultSet()) {
currentResultSet = super.executeQueryImpl(buildSQL(), localSettings);
return true;
return currentResultSet != null;
} else {
currentUpdateCount = super.executeUpdateImpl(buildSQL(), localSettings);
return false;
Expand Down
29 changes: 25 additions & 4 deletions jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@
ensureOpen();
currentUpdateCount = -1;
currentResultSet = executeQueryImpl(sql, localSettings);
if (currentResultSet == null) {
throw new SQLException("executeQuery() requires a ResultSet; use execute() for statements that do not produce one.",
ExceptionUtils.SQL_STATE_CLIENT_ERROR);
}
return currentResultSet;
}

Expand Down Expand Up @@ -147,7 +151,7 @@
return queryId;
}

protected ResultSetImpl executeQueryImpl(String sql, QuerySettings settings) throws SQLException {

Check failure on line 154 in jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 17 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=ClickHouse_clickhouse-java&issues=AZz4wvHtACzsL9rDxbqx&open=AZz4wvHtACzsL9rDxbqx&pullRequest=2785
ensureOpen();

// Closing before trying to do next request. Otherwise, deadlock because previous connection will not be
Expand Down Expand Up @@ -179,8 +183,26 @@
}
ClickHouseBinaryFormatReader reader = connection.getClient().newBinaryFormatReader(response);
if (reader.getSchema() == null) {
reader.close();
throw new SQLException("Called method expects empty or filled result set but query has returned none. Consider using `java.sql.Statement.execute(java.lang.String)`", ExceptionUtils.SQL_STATE_CLIENT_ERROR);
long writtenRows = 0L;
try {

Check warning on line 187 in jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested try block into a separate method.

See more on https://sonarcloud.io/project/issues?id=ClickHouse_clickhouse-java&issues=AZz4tJ3ks3vzZqbPoTpI&open=AZz4tJ3ks3vzZqbPoTpI&pullRequest=2785
writtenRows = response.getWrittenRows();
} catch (Exception ignore) {
// Best effort: leave writtenRows as 0 if we can't obtain it.
}
this.currentUpdateCount = (int) Math.min(writtenRows, Integer.MAX_VALUE);
try {

Check warning on line 193 in jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested try block into a separate method.

See more on https://sonarcloud.io/project/issues?id=ClickHouse_clickhouse-java&issues=AZz4wvHtACzsL9rDxbqv&open=AZz4wvHtACzsL9rDxbqv&pullRequest=2785
reader.close();
} catch (Exception closeEx) {
LOG.warn("Failed to close reader when schema is null", closeEx);
} finally {
try {

Check warning on line 198 in jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested try block into a separate method.

See more on https://sonarcloud.io/project/issues?id=ClickHouse_clickhouse-java&issues=AZz4wvHtACzsL9rDxbqw&open=AZz4wvHtACzsL9rDxbqw&pullRequest=2785
response.close();
} catch (Exception closeRespEx) {
LOG.warn("Failed to close response when schema is null", closeRespEx);
}
}
onResultSetClosed(null);
return null;
}
return new ResultSetImpl(this, response, reader, this::handleSocketTimeoutException);
} catch (Exception e) {
Expand Down Expand Up @@ -342,10 +364,9 @@
ensureOpen();
parsedStatement = connection.getSqlParser().parsedStatement(sql);
currentUpdateCount = -1;
currentResultSet = null;
if (parsedStatement.isHasResultSet()) {
currentResultSet = executeQueryImpl(sql, localSettings);
return true;
return currentResultSet != null;
} else {
currentUpdateCount = executeUpdateImpl(sql, localSettings);
postUpdateActions();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,18 @@
}

private boolean isStmtWithResultSet(ClickHouseSqlStatement parsedStmt) {
return parsedStmt.getStatementType() == StatementType.SELECT || parsedStmt.getStatementType() == StatementType.SHOW
|| parsedStmt.getStatementType() == StatementType.EXPLAIN || parsedStmt.getStatementType() == StatementType.DESCRIBE
|| parsedStmt.getStatementType() == StatementType.EXISTS || parsedStmt.getStatementType() == StatementType.CHECK;

switch (parsedStmt.getStatementType()) {
case SELECT:
case SHOW:
case EXPLAIN:
case DESCRIBE:
case EXISTS:
case CHECK:
case UNKNOWN:

Check warning on line 77 in jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SqlParserFacade.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Merge the previous cases into this one using comma-separated label.

See more on https://sonarcloud.io/project/issues?id=ClickHouse_clickhouse-java&issues=AZzg7RdpVeL0hYeMcUU-&open=AZzg7RdpVeL0hYeMcUU-&pullRequest=2785
return true;
default:
return false;
}
}

@Override
Expand Down Expand Up @@ -162,14 +170,19 @@
public ParsedStatement parsedStatement(String sql) {
ParsedStatement stmt = new ParsedStatement();
parseSQL(sql, new ParsedStatementListener(stmt, processUseRolesExpr));
if (stmt.isHasErrors()) {
stmt.setHasResultSet(true);
}
return stmt;
}

@Override
public ParsedPreparedStatement parsePreparedStatement(String sql) {
ParsedPreparedStatement stmt = new ParsedPreparedStatement();
parseSQL(sql, new ParsedPreparedStatementListener(stmt, processUseRolesExpr));

if (stmt.isHasErrors()) {
stmt.setHasResultSet(true);
}
// Combine database and table like JavaCC does
String tableName = stmt.getTable();
if (stmt.getDatabase() != null && stmt.getTable() != null) {
Expand Down Expand Up @@ -395,7 +408,9 @@
public ParsedPreparedStatement parsePreparedStatement(String sql) {
ParsedPreparedStatement stmt = new ParsedPreparedStatement();
parseSQL(sql, new ParseStatementAndParamsListener(stmt, processUseRolesExpr));

if (stmt.isHasErrors()) {
stmt.setHasResultSet(true);
}
// Combine database and table like JavaCC does
String tableName = stmt.getTable();
if (stmt.getDatabase() != null && stmt.getTable() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1785,4 +1785,51 @@
}
}
}

@Test(groups = {"integration"})
public void testUnknownStatement() throws Exception {
try (Connection conn = getJdbcConnection()) {
try (PreparedStatement stmt = conn.prepareStatement("SELECT number, FROM system.numbers LIMIT 3")) {
Assert.assertTrue(stmt.execute());

try (ResultSet rs = stmt.getResultSet()) {
for (int i = 0; i < 3; i++) {
Assert.assertTrue(rs.next());
Assert.assertEquals(rs.getLong(1), i);
}
}
}

try (Statement stmt = conn.createStatement()) {
Assert.assertTrue(stmt.execute("SELECT number, FROM system.numbers LIMIT 3"));

try (ResultSet rs = stmt.getResultSet()) {
for (int i = 0; i < 3; i++) {
Assert.assertTrue(rs.next());
Assert.assertEquals(rs.getLong(1), i);
}
}
}

// No-ResultSet path: DDL should not produce a ResultSet
String tmpTable = "tmp_no_result_" + RandomStringUtils.randomAlphanumeric(8);

Check warning on line 1815 in jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "randomAlphanumeric"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=ClickHouse_clickhouse-java&issues=AZz4wvGrACzsL9rDxbqt&open=AZz4wvGrACzsL9rDxbqt&pullRequest=2785
// PreparedStatement: execute() should return false, executeQuery() should throw
try (PreparedStatement stmt = conn.prepareStatement(
"CREATE TABLE " + tmpTable + " (x Int32) Engine MergeTree ORDER BY()")) {
Assert.assertFalse(stmt.execute(), "DDL should not produce a ResultSet");
Assert.assertNull(stmt.getResultSet(), "ResultSet should be null for DDL");
assertThrows(SQLException.class, stmt::executeQuery);
}
// Statement: execute() should return false, executeQuery() should throw
String tmpTable2 = "tmp_no_result_" + RandomStringUtils.randomAlphanumeric(8);

Check warning on line 1824 in jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "randomAlphanumeric"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=ClickHouse_clickhouse-java&issues=AZz4wvGrACzsL9rDxbqu&open=AZz4wvGrACzsL9rDxbqu&pullRequest=2785
try (Statement stmt = conn.createStatement()) {
Assert.assertFalse(
stmt.execute("CREATE TABLE " + tmpTable2 + " (x Int32) Engine MergeTree ORDER BY()"),
"DDL should not produce a ResultSet");
Assert.assertNull(stmt.getResultSet(), "ResultSet should be null for DDL");
assertThrows(SQLException.class,
() -> stmt.executeQuery("CREATE TABLE " + tmpTable2 + " (x Int32) Engine MergeTree ORDER BY()"));
}
}
}
}
44 changes: 44 additions & 0 deletions jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,50 @@ public void testDescribeStatement() throws Exception {
}
}

@Test(groups = {"integration"}, dataProvider = "testUnknownStatementTest_DP")
public void testUnknownStatement(String parserName) throws Exception {
Properties properties = new Properties();
properties.setProperty(DriverProperties.SQL_PARSER.getKey(), parserName);
try (Connection conn = getJdbcConnection(properties)) {

try (Statement stmt = conn.createStatement()) {
Assert.assertTrue(stmt.execute("SELECT number, FROM system.numbers LIMIT 3"));

try (ResultSet rs = stmt.getResultSet()) {
for (int i = 0; i < 3; i++) {
Assert.assertTrue(rs.next());
Assert.assertEquals(rs.getLong(1), i);
}
}


stmt.execute("DROP TABLE IF EXISTS test_unknown_statement_test");
stmt.execute("CREATE TABLE test_unknown_statement_test (v Int32) Engine MergeTree ORDER BY ()");

// INSERT via execute(...) must not produce a ResultSet and should return false
boolean hasResultSet = stmt.execute("INSERT INTO test_unknown_statement_test VALUES (1);");
assertFalse(hasResultSet);
assertEquals(stmt.getUpdateCount(), 1);

stmt.executeUpdate("INSERT INTO test_unknown_statement_test VALUES (2);");
assertEquals(stmt.getUpdateCount(), 1);

assertThrows(SQLException.class,
() -> stmt.executeQuery("INSERT INTO test_unknown_statement_test VALUES (3);"));

}
}
}

@DataProvider
public static Object[][] testUnknownStatementTest_DP() {
return new Object[][] {
{SqlParserFacade.SQLParser.ANTLR4.name()},
{SqlParserFacade.SQLParser.ANTLR4_PARAMS_PARSER.name()},
{SqlParserFacade.SQLParser.JAVACC.name()},
};
}

private static String getDBName(Statement stmt) throws SQLException {
try (ResultSet rs = stmt.executeQuery("SELECT database()")) {
rs.next();
Expand Down
Loading