Skip to content
Merged
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 @@ -668,7 +668,7 @@ setStmt
// SET ROLE statement

setRoleStmt
: SET (DEFAULT)? ROLE (setRolesList | NONE | ALL (EXCEPT setRolesList)) (TO identifier | CURRENT_USER (COMMA identifier | CURRENT_USER)*)?
: SET (DEFAULT)? ROLE ( NONE | setRolesList | ALL (EXCEPT setRolesList)) (TO identifier | CURRENT_USER (COMMA identifier | CURRENT_USER)*)?
;

setRolesList
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public ConnectionImpl(String url, Properties info) throws SQLException {


this.sqlParser = SqlParserFacade.getParser(config.getDriverProperty(DriverProperties.SQL_PARSER.getKey(),
DriverProperties.SQL_PARSER.getDefaultValue()));
DriverProperties.SQL_PARSER.getDefaultValue()), config);
this.featureManager = new FeatureManager(this.config);
} catch (SQLException e) {
throw e;
Expand Down
46 changes: 46 additions & 0 deletions jdbc-v2/src/main/java/com/clickhouse/jdbc/DriverProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,52 @@ public enum DriverProperties {
*/
QUERY_ID_GENERATOR("jdbc_query_id_generator", null),

/**
* Controls logic of saving roles that were set using {@code SET <role>} statement.
* Default: true - save roles
*/
REMEMBER_LAST_SET_ROLES("remember_last_set_roles", String.valueOf(Boolean.TRUE)),

/**
* Deprecated and will be removed.
* This property is here to keep backward compatibility with {@code com.clickhouse.client.http.config.ClickHouseHttpOption#CUSTOM_PARAMS}.
* Original property is deprecated for {@code com.clickhouse.client.config.ClickHouseClientOption#CUSTOM_SETTINGS}
* This property is expected to be a comma separated list of key-value pair that should be sent to server.
* Pairs will be converted to new properties for client config.
* Use {@link ClientConfigProperties#serverSetting(String)} instead
*
*/
@Deprecated
CUSTOM_HTTP_PARAMS("custom_http_params", null),


/**
* Deprecated and will be removed.
* This property is here to keep backward compatibility with {@code com.clickhouse.client.config.ClickHouseClientOption#CUSTOM_SETTINGS}.
* This property is expected to be a comma separated list of key-value pair that should be sent to server.
* Pairs will be converted to new properties for client config.
* Use {@link ClientConfigProperties#serverSetting(String)} instead
*
*/
@Deprecated
CUSTOM_SETTINGS("custom_settings", null),

/**
* Deprecated as driver do not convert Date values to any timezone. Dates are sent as is.
* Problem having this setting on connection level is that affects all child statements and
* there is no way to control on column basis.
* Driver ignores this setting but will throw exception when it is completely removed.
*/
@Deprecated
USE_TZ_FOR_DATES("use_server_time_zone_for_dates", null),

/**
* Current driver implementation uses only one http provider. So setting it has no effect.
* Deprecated will be removed soon
*/
@Deprecated
HTTP_CONNECTION_PROVIDER("http_connection_provider", null),

;


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ protected long executeUpdateImpl(String sql, QuerySettings settings) throws SQLE
return updateCount;
}

protected void postUpdateActions() throws SQLException {
private void postUpdateActions() throws SQLException {
if (parsedStatement.getUseDatabase() != null) {
this.localSettings.setDatabase(parsedStatement.getUseDatabase());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,11 @@ private void initProperties(Map<String, String> urlProperties, Properties provid
propertyInfos.put(prop.getKey(), propertyInfo);

if (DRIVER_PROP_KEYS.contains(prop.getKey())) {
if (prop.getKey().equalsIgnoreCase(DriverProperties.CUSTOM_HTTP_PARAMS.getKey()) ||
prop.getKey().equalsIgnoreCase(DriverProperties.CUSTOM_SETTINGS.getKey())) {
ClientConfigProperties.toKeyValuePairs(prop.getValue())
.forEach((k, v) -> clientProperties.put(ClientConfigProperties.serverSetting(k), v));
}
driverProperties.put(prop.getKey(), prop.getValue());
} else {
clientProperties.put(prop.getKey(), prop.getValue());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.clickhouse.client.api.sql.SQLUtils;
import com.clickhouse.data.ClickHouseUtils;
import com.clickhouse.jdbc.DriverProperties;
import com.clickhouse.jdbc.internal.parser.antlr4.ClickHouseLexer;
import com.clickhouse.jdbc.internal.parser.antlr4.ClickHouseParser;
import com.clickhouse.jdbc.internal.parser.antlr4.ClickHouseParserBaseListener;
Expand All @@ -25,6 +26,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public abstract class SqlParserFacade {
Expand All @@ -37,6 +39,12 @@ public abstract class SqlParserFacade {

private static class JavaCCParser extends SqlParserFacade {

private final boolean processUseRolesExpr;

public JavaCCParser(boolean saveRoles) {
this.processUseRolesExpr = saveRoles;
}

@Override
public ParsedStatement parsedStatement(String sql) {
ParsedStatement stmt = new ParsedStatement();
Expand All @@ -45,22 +53,11 @@ public ParsedStatement parsedStatement(String sql) {
stmt.setUseDatabase(parsedStmt.getDatabase());
}

String rolesCount = parsedStmt.getSettings().get("_ROLES_COUNT");
if (rolesCount != null) {
int rolesCountInt = Integer.parseInt(rolesCount);
ArrayList<String> roles = new ArrayList<>(rolesCountInt);
boolean resetRoles = false;
for (int i = 0; i < rolesCountInt; i++) {
String role = parsedStmt.getSettings().get("_ROLE_" + i);
if (role.equalsIgnoreCase("NONE")) {
resetRoles = true;
}
roles.add(parsedStmt.getSettings().get("_ROLE_" + i));
}
if (resetRoles) {
roles.clear();
if (processUseRolesExpr) {
List<String> roles = processRoles(parsedStmt.getSettings());
if (roles != null) {
stmt.setRoles(roles);
}
stmt.setRoles(roles);
}

stmt.setInsert(parsedStmt.getStatementType() == StatementType.INSERT);
Expand Down Expand Up @@ -109,11 +106,39 @@ public ParsedPreparedStatement parsePreparedStatement(String sql) {
}
}

if (processUseRolesExpr) {
List<String> roles = processRoles(parsedStmt.getSettings());
if (roles != null) {
stmt.setRoles(roles);
}
}

stmt.setUseFunction(parsedStmt.isFuncUsed());
parseParameters(sql, stmt);
return stmt;
}

private List<String> processRoles(Map<String, String> settings) {
String rolesCount = settings.get("_ROLES_COUNT");
if (rolesCount != null) {
int rolesCountInt = Integer.parseInt(rolesCount);
ArrayList<String> roles = new ArrayList<>(rolesCountInt);
boolean resetRoles = false;
for (int i = 0; i < rolesCountInt; i++) {
String role = settings.get("_ROLE_" + i);
if (role.equalsIgnoreCase("NONE")) {
resetRoles = true;
}
roles.add(settings.get("_ROLE_" + i));
}
if (resetRoles) {
roles.clear();
}
return roles;
}
return null; // no roles present
}


public ClickHouseSqlStatement parse(String sql) {
JdbcParseHandler handler = JdbcParseHandler.getInstance();
Expand All @@ -127,17 +152,23 @@ public ClickHouseSqlStatement parse(String sql) {

private static class ANTLR4Parser extends SqlParserFacade {

protected final boolean processUseRolesExpr;

public ANTLR4Parser(boolean saveRoles) {
this.processUseRolesExpr = saveRoles;
}

@Override
public ParsedStatement parsedStatement(String sql) {
ParsedStatement stmt = new ParsedStatement();
parseSQL(sql, new ParsedStatementListener(stmt));
parseSQL(sql, new ParsedStatementListener(stmt, processUseRolesExpr));
return stmt;
}

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

// Combine database and table like JavaCC does
String tableName = stmt.getTable();
Expand Down Expand Up @@ -177,12 +208,27 @@ static boolean isStmtWithResultSet(ClickHouseParser.QueryStmtContext stmtContext
qCtx.existsStmt() != null || qCtx.checkStmt() != null);
}

static List<String> processRolesExpr(ClickHouseParser.SetRoleStmtContext ctx) {
if (ctx.NONE() != null) {
return Collections.emptyList();
} else {
List<String> roles = new ArrayList<>();
for (ClickHouseParser.IdentifierContext id : ctx.setRolesList().identifier()) {
roles.add(SQLUtils.unquoteIdentifier(id.getText()));
}
return roles;
}
}

private static class ParsedStatementListener extends ClickHouseParserBaseListener {

private final ParsedStatement parsedStatement;

public ParsedStatementListener(ParsedStatement parsedStatement) {
private final boolean processSetRolesExpr;

public ParsedStatementListener(ParsedStatement parsedStatement, boolean processSetRolesExpr) {
this.parsedStatement = parsedStatement;
this.processSetRolesExpr = processSetRolesExpr;
}

@Override
Expand All @@ -206,14 +252,8 @@ public void enterUseStmt(ClickHouseParser.UseStmtContext ctx) {

@Override
public void enterSetRoleStmt(ClickHouseParser.SetRoleStmtContext ctx) {
if (ctx.NONE() != null) {
parsedStatement.setRoles(Collections.emptyList());
} else {
List<String> roles = new ArrayList<>();
for (ClickHouseParser.IdentifierContext id : ctx.setRolesList().identifier()) {
roles.add(SQLUtils.unquoteIdentifier(id.getText()));
}
parsedStatement.setRoles(roles);
if (processSetRolesExpr) {
parsedStatement.setRoles(processRolesExpr(ctx));
}
}
}
Expand All @@ -222,8 +262,11 @@ protected static class ParsedPreparedStatementListener extends ClickHouseParserB

protected final ParsedPreparedStatement parsedStatement;

public ParsedPreparedStatementListener(ParsedPreparedStatement parsedStatement) {
private final boolean processSetRolesExpr;

public ParsedPreparedStatementListener(ParsedPreparedStatement parsedStatement, boolean processSetRolesExpr) {
this.parsedStatement = parsedStatement;
this.processSetRolesExpr = processSetRolesExpr;
}

@Override
Expand All @@ -242,14 +285,8 @@ public void enterUseStmt(ClickHouseParser.UseStmtContext ctx) {

@Override
public void enterSetRoleStmt(ClickHouseParser.SetRoleStmtContext ctx) {
if (ctx.NONE() != null) {
parsedStatement.setRoles(Collections.emptyList());
} else {
List<String> roles = new ArrayList<>();
for (ClickHouseParser.IdentifierContext id : ctx.setRolesList().identifier()) {
roles.add(SQLUtils.unquoteIdentifier(id.getText()));
}
parsedStatement.setRoles(roles);
if (processSetRolesExpr) {
parsedStatement.setRoles(processRolesExpr(ctx));
}
}

Expand Down Expand Up @@ -349,10 +386,15 @@ public void exitInsertParameterFuncExpr(ClickHouseParser.InsertParameterFuncExpr

private static class ANTLR4AndParamsParser extends ANTLR4Parser {


public ANTLR4AndParamsParser(boolean saveRoles) {
super(saveRoles);
}

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

// Combine database and table like JavaCC does
String tableName = stmt.getTable();
Expand All @@ -366,8 +408,8 @@ public ParsedPreparedStatement parsePreparedStatement(String sql) {

private static class ParseStatementAndParamsListener extends ParsedPreparedStatementListener {

public ParseStatementAndParamsListener(ParsedPreparedStatement parsedStatement) {
super(parsedStatement);
public ParseStatementAndParamsListener(ParsedPreparedStatement parsedStatement, boolean processSetRolesExpr) {
super(parsedStatement, processSetRolesExpr);
}

@Override
Expand Down Expand Up @@ -449,16 +491,18 @@ public enum SQLParser {
ANTLR4
}

public static SqlParserFacade getParser(String name) throws SQLException {
public static SqlParserFacade getParser(String name, JdbcConfiguration jdbcConfiguration) throws SQLException {
try {
boolean saveRoles = Boolean.parseBoolean(jdbcConfiguration.getDriverProperty(DriverProperties.REMEMBER_LAST_SET_ROLES.getKey(),
DriverProperties.REMEMBER_LAST_SET_ROLES.getDefaultValue()));
SQLParser parserSelection = SQLParser.valueOf(name);
switch (parserSelection) {
case JAVACC:
return new JavaCCParser();
return new JavaCCParser(saveRoles);
case ANTLR4_PARAMS_PARSER:
return new ANTLR4AndParamsParser();
return new ANTLR4AndParamsParser(saveRoles);
case ANTLR4:
return new ANTLR4Parser();
return new ANTLR4Parser(saveRoles);
}
throw new SQLException("Unsupported parser: " + parserSelection);
} catch (IllegalArgumentException e) {
Expand Down
30 changes: 30 additions & 0 deletions jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1113,4 +1113,34 @@ public void testCustomParameters() throws Exception {
}
}
}

@Test(groups = {"integration"})
public void testOldCustomSettingsParameter() throws Exception {
if (isCloud()) {
return; // no custom settings on cloud instance
}

String sql = "SELECT " +
" getSetting('max_threads') AS max_threads, " +
" getSetting('session_timezone') AS tz ";
Properties properties = new Properties();
properties.put(DriverProperties.CUSTOM_SETTINGS.getKey(), "max_threads=10,session_timezone=Asia/Tokyo");
try (Connection connection = getJdbcConnection(properties)) {
try (Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery(sql)){
assertTrue(rs.next());
assertEquals(rs.getString(1), "10");
assertEquals(rs.getString(2), "Asia/Tokyo");
}
}

properties = new Properties();
properties.put(DriverProperties.CUSTOM_HTTP_PARAMS.getKey(), "max_threads=10,session_timezone=Asia/Tokyo");
try (Connection connection = getJdbcConnection(properties)) {
try (Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery(sql)){
assertTrue(rs.next());
assertEquals(rs.getString(1), "10");
assertEquals(rs.getString(2), "Asia/Tokyo");
}
}
}
}
Loading
Loading