diff --git a/clickhouse-client/src/test/java/com/clickhouse/client/BaseIntegrationTest.java b/clickhouse-client/src/test/java/com/clickhouse/client/BaseIntegrationTest.java
index 046ab46ac..c1a0d72a0 100644
--- a/clickhouse-client/src/test/java/com/clickhouse/client/BaseIntegrationTest.java
+++ b/clickhouse-client/src/test/java/com/clickhouse/client/BaseIntegrationTest.java
@@ -1,9 +1,11 @@
package com.clickhouse.client;
import com.clickhouse.client.config.ClickHouseClientOption;
+import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
+import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Locale;
@@ -13,6 +15,9 @@
* Base class for all integration tests.
*/
public abstract class BaseIntegrationTest {
+ private static final int REPLICA_CHECK_ATTEMPTS = 100;
+ private static final long REPLICA_CHECK_INTERVAL_MS = 100L;
+
@BeforeTest(groups = {"integration"})
public static void setupClickHouseContainer() {
ClickHouseServerForTest.beforeSuite();
@@ -86,6 +91,53 @@ protected static String onCluster() {
return ClickHouseServerForTest.isCluster() ? " ON CLUSTER 'test_cluster'" : "";
}
+ /**
+ * Waits until each replica of the test cluster returns {@code expectedRows} rows of
+ * {@code system.
} that match {@code condition}, so that the next request sees a
+ * created or changed user, role, settings profile or grant on any replica.
+ * Does nothing when the tests do not run on a cluster.
+ */
+ protected static void waitForAllReplicas(String table, String condition, int expectedRows) {
+ if (!isCluster()) {
+ return;
+ }
+ String sql = "SELECT hostName() AS host, countIf(" + condition + ") AS matching_rows FROM clusterAllReplicas('"
+ + ClusterClickHouseContainerStarter.CLUSTER_NAME + "', system." + table + ") GROUP BY host";
+ String state = "";
+ for (int attempt = 1; attempt <= REPLICA_CHECK_ATTEMPTS; attempt++) {
+ try {
+ state = ClickHouseServerForTest.queryForResult(sql).trim();
+ if (hasRowsOnAllReplicas(state, expectedRows)) {
+ return;
+ }
+ } catch (IOException e) {
+ state = e.getMessage();
+ }
+ try {
+ Thread.sleep(REPLICA_CHECK_INTERVAL_MS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ break;
+ }
+ }
+ Assert.fail("Expected " + expectedRows + " rows matching [" + condition + "] in system." + table + " on all "
+ + ClusterClickHouseContainerStarter.NODE_COUNT + " replicas, but got [host, rows]:\n" + state);
+ }
+
+ private static boolean hasRowsOnAllReplicas(String result, int expectedRows) {
+ String[] lines = result.split("\n");
+ if (lines.length != ClusterClickHouseContainerStarter.NODE_COUNT) {
+ return false;
+ }
+ for (String line : lines) {
+ String[] columns = line.split("\t");
+ if (columns.length != 2 || !columns[1].equals(String.valueOf(expectedRows))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
protected boolean createDatabase(String dbName) {
return ClickHouseServerForTest.runQuery("CREATE DATABASE IF NOT EXISTS `" + dbName + "`" + onCluster());
}
diff --git a/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseServerForTest.java b/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseServerForTest.java
index bd8bd0871..4e452d087 100644
--- a/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseServerForTest.java
+++ b/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseServerForTest.java
@@ -9,6 +9,9 @@
import com.clickhouse.client.ClickHouseTestEnvironment.ContainerMode;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
@@ -279,6 +282,48 @@ public static boolean runQuery(String sql) {
return false;
}
+ /**
+ * Runs a query once and returns the response body (TabSeparated).
+ *
+ * @throws IOException when the request fails or the server returns an error
+ */
+ public static String queryForResult(String sql) throws IOException {
+ ClickHouseNode server = getClickHouseNode(ClickHouseProtocol.HTTP, isCloud(), ClickHouseNode.builder().build());
+ byte[] postData = sql.getBytes(StandardCharsets.UTF_8);
+ String authorization = Base64.getEncoder()
+ .encodeToString((getUsername() + ":" + getPassword()).getBytes(StandardCharsets.UTF_8));
+ HttpURLConnection httpConn = (HttpURLConnection) new URL(server.getBaseUri()).openConnection();
+ try {
+ httpConn.setConnectTimeout(10000);
+ httpConn.setReadTimeout(10000);
+ httpConn.setRequestMethod("POST");
+ httpConn.setDoOutput(true);
+ httpConn.setRequestProperty("Authorization", "Basic " + authorization);
+ httpConn.setFixedLengthStreamingMode(postData.length);
+ try (OutputStream out = httpConn.getOutputStream()) {
+ out.write(postData, 0, postData.length);
+ }
+
+ boolean ok = httpConn.getResponseCode() == HttpURLConnection.HTTP_OK;
+ ByteArrayOutputStream body = new ByteArrayOutputStream();
+ try (InputStream in = ok ? httpConn.getInputStream() : httpConn.getErrorStream()) {
+ if (in != null) {
+ byte[] buffer = new byte[4096];
+ for (int read; (read = in.read(buffer)) != -1; ) {
+ body.write(buffer, 0, read);
+ }
+ }
+ }
+ String result = new String(body.toByteArray(), StandardCharsets.UTF_8);
+ if (!ok) {
+ throw new IOException("HTTP " + httpConn.getResponseCode() + ": " + result);
+ }
+ return result;
+ } finally {
+ httpConn.disconnect();
+ }
+ }
+
private static Endpoint resolve(ClickHouseProtocol protocol, int port, boolean applyOverride) {
if (starter != null) {
int containerPort = (port == ClickHouseProtocol.HTTP.getDefaultPort())
diff --git a/client-v2/src/test/java/com/clickhouse/client/ClientTests.java b/client-v2/src/test/java/com/clickhouse/client/ClientTests.java
index 2d8f8ad85..e0c7c5d0c 100644
--- a/client-v2/src/test/java/com/clickhouse/client/ClientTests.java
+++ b/client-v2/src/test/java/com/clickhouse/client/ClientTests.java
@@ -434,28 +434,43 @@ private static Object[][] sessionRoles() {
}
@Test(groups = {"integration"}, dataProvider = "sessionRoles")
- public void testOperationCustomRoles(String[] roles) throws Exception {
+ public void testOperationCustomRoles(String[] roleNames) throws Exception {
if (isVersionMatch("(,24.3]", newClient().build())) {
return;
}
+ String suffix = "_" + RandomStringUtils.insecure().next(8, true, true);
+ String user = "some_user" + suffix;
+ String[] roles = Arrays.stream(roleNames).map(role -> role + suffix).toArray(String[]::new);
String password = "^1A" + RandomStringUtils.random(12, true, true) + "3b$";
final String rolesList = "\"" + Strings.join("\",\"", roles) + "\"";
- try (Client client = newClient().build()) {
- client.execute("DROP ROLE IF EXISTS " + rolesList).get().close();
- client.execute("CREATE ROLE " + rolesList).get().close();
- client.execute("DROP USER IF EXISTS some_user").get().close();
- client.execute("CREATE USER some_user IDENTIFIED BY '" + password + "'").get().close();
- client.execute("GRANT " + rolesList + " TO some_user").get().close();
+ try (Client client = newClient().build();
+ Client userClient = newClient().setUsername(user).setPassword(password).build()) {
+ try {
+ createUserWithRoles(client, user, "IDENTIFIED BY '" + password + "'", roles);
+ QuerySettings settings = new QuerySettings().setDBRoles(Arrays.asList(roles));
+ List resp = userClient.queryAll("SELECT currentRoles()", settings);
+ Set roleSet = new HashSet<>(Arrays.asList(roles));
+ Set currentRoles = new HashSet (resp.get(0).getList(1));
+ Assert.assertEquals(currentRoles, roleSet, "Roles " + roleSet + " not found in " + currentRoles);
+ } finally {
+ client.execute("DROP USER IF EXISTS " + user).get().close();
+ client.execute("DROP ROLE IF EXISTS " + rolesList).get().close();
+ }
}
+ }
- try (Client userClient = newClient().setUsername("some_user").setPassword(password).build()) {
- QuerySettings settings = new QuerySettings().setDBRoles(Arrays.asList(roles));
- List resp = userClient.queryAll("SELECT currentRoles()", settings);
- Set roleSet = new HashSet<>(Arrays.asList(roles));
- Set currentRoles = new HashSet (resp.get(0).getList(1));
- Assert.assertEquals(currentRoles, roleSet, "Roles " + roleSet + " not found in " + currentRoles);
- }
+ private static void createUserWithRoles(Client client, String user, String identification, String[] roles)
+ throws Exception {
+ final String rolesList = "\"" + Strings.join("\",\"", roles) + "\"";
+ final String roleNames = "'" + Strings.join("','", roles) + "'";
+ client.execute("CREATE ROLE " + rolesList).get().close();
+ client.execute("CREATE USER " + user + " " + identification).get().close();
+ waitForAllReplicas("roles", "name IN (" + roleNames + ")", roles.length);
+ waitForAllReplicas("users", "name = '" + user + "'", 1);
+ client.execute("GRANT " + rolesList + " TO " + user).get().close();
+ waitForAllReplicas("role_grants", "user_name = '" + user + "' AND granted_role_name IN (" + roleNames + ")",
+ roles.length);
}
@DataProvider(name = "clientSessionRoles")
@@ -466,27 +481,29 @@ private static Object[][] clientSessionRoles() {
};
}
@Test(groups = {"integration"}, dataProvider = "clientSessionRoles")
- public void testClientCustomRoles(String[] roles) throws Exception {
+ public void testClientCustomRoles(String[] roleNames) throws Exception {
if (isVersionMatch("(,24.3]", newClient().build())) {
return;
}
+ String suffix = "_" + RandomStringUtils.insecure().next(8, true, true);
+ String user = "some_user" + suffix;
+ String[] roles = Arrays.stream(roleNames).map(role -> role + suffix).toArray(String[]::new);
String password = "^1A" + RandomStringUtils.insecure().next(12, true, true) + "3B$";
final String rolesList = "\"" + Strings.join("\",\"", roles) + "\"";
- try (Client client = newClient().build()) {
- client.execute("DROP ROLE IF EXISTS " + rolesList).get().close();
- client.execute("CREATE ROLE " + rolesList).get().close();
- client.execute("DROP USER IF EXISTS some_user").get().close();
- client.execute("CREATE USER some_user IDENTIFIED WITH sha256_password BY '" + password + "'").get().close();
- client.execute("GRANT " + rolesList + " TO some_user").get().close();
- }
-
- try (Client userClient = newClient().setUsername("some_user").setPassword(password).build()) {
- userClient.setDBRoles(Arrays.asList(roles));
- List resp = userClient.queryAll("SELECT currentRoles()");
- Set roleSet = new HashSet<>(Arrays.asList(roles));
- Set currentRoles = new HashSet (resp.get(0).getList(1));
- Assert.assertEquals(currentRoles, roleSet, "Roles " + roleSet + " not found in " + currentRoles);
+ try (Client client = newClient().build();
+ Client userClient = newClient().setUsername(user).setPassword(password).build()) {
+ try {
+ createUserWithRoles(client, user, "IDENTIFIED WITH sha256_password BY '" + password + "'", roles);
+ userClient.setDBRoles(Arrays.asList(roles));
+ List resp = userClient.queryAll("SELECT currentRoles()");
+ Set roleSet = new HashSet<>(Arrays.asList(roles));
+ Set currentRoles = new HashSet (resp.get(0).getList(1));
+ Assert.assertEquals(currentRoles, roleSet, "Roles " + roleSet + " not found in " + currentRoles);
+ } finally {
+ client.execute("DROP USER IF EXISTS " + user).get().close();
+ client.execute("DROP ROLE IF EXISTS " + rolesList).get().close();
+ }
}
}
@@ -507,36 +524,15 @@ public void testRuntimeCredentialChange() throws Exception {
adminClient.execute("DROP USER IF EXISTS " + user2).get().close();
adminClient.execute("CREATE USER " + user1 + " IDENTIFIED BY '" + password1 + "'").get().close();
adminClient.execute("CREATE USER " + user2 + " IDENTIFIED BY '" + password2 + "'").get().close();
+ waitForAllReplicas("users", "name IN ('" + user1 + "', '" + user2 + "')", 2);
try (Client userClient = newClient().setUsername(user1).setPassword(password1).build()) {
- List firstResponse = null;
- for (int i = 0; i < 20; i++) {
- try {
- firstResponse = userClient.queryAll("SELECT currentUser() AS user");
- break;
- } catch (ClientException e) {
- if (i == 19) {
- throw e;
- }
- Thread.sleep(250);
- }
- }
+ List firstResponse = userClient.queryAll("SELECT currentUser() AS user");
Assert.assertEquals(firstResponse.get(0).getString("user"), user1);
userClient.updateUserAndPassword(user2, password2);
- List secondResponse = null;
- for (int i = 0; i < 20; i++) {
- try {
- secondResponse = userClient.queryAll("SELECT currentUser() AS user");
- break;
- } catch (ClientException e) {
- if (i == 19) {
- throw e;
- }
- Thread.sleep(250);
- }
- }
+ List secondResponse = userClient.queryAll("SELECT currentUser() AS user");
Assert.assertEquals(secondResponse.get(0).getString("user"), user2);
}
} finally {
diff --git a/client-v2/src/test/java/com/clickhouse/client/HttpTransportTests.java b/client-v2/src/test/java/com/clickhouse/client/HttpTransportTests.java
index 533a43268..8a32fcd9b 100644
--- a/client-v2/src/test/java/com/clickhouse/client/HttpTransportTests.java
+++ b/client-v2/src/test/java/com/clickhouse/client/HttpTransportTests.java
@@ -1044,29 +1044,31 @@ public void testSSLAuthentication() throws Exception {
return; // Current test is working only with local server because of self-signed certificates.
}
ClickHouseNode server = getSecureServer(ClickHouseProtocol.HTTP);
- try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), true)
+ String user = "some_user_" + UUID.randomUUID().toString().replace("-", "");
+ try (Client adminClient = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), true)
.setUsername("dba")
.setPassword("dba")
.setRootCertificate("containers/clickhouse-server/certs/localhost.crt")
.build()) {
- try (CommandResponse resp = client.execute("DROP USER IF EXISTS some_user").get()) {
- }
- try (CommandResponse resp = client.execute("CREATE USER some_user IDENTIFIED WITH ssl_certificate CN 'some_user'").get()) {
- }
- }
+ try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), true)
+ .useSSLAuthentication(true)
+ .setUsername(user)
+ .setRootCertificate("containers/clickhouse-server/certs/localhost.crt")
+ .setClientCertificate("some_user.crt")
+ .setClientKey("some_user.key")
+ .compressServerResponse(false)
+ .build()) {
- try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), true)
- .useSSLAuthentication(true)
- .setUsername("some_user")
- .setRootCertificate("containers/clickhouse-server/certs/localhost.crt")
- .setClientCertificate("some_user.crt")
- .setClientKey("some_user.key")
- .compressServerResponse(false)
- .build()) {
+ try (CommandResponse resp = adminClient.execute("CREATE USER " + user + " IDENTIFIED WITH ssl_certificate CN 'some_user'").get()) {
+ }
+ waitForAllReplicas("users", "name = '" + user + "'", 1);
- try (QueryResponse resp = client.query("SELECT 1").get()) {
- Assert.assertEquals(resp.getReadRows(), 1);
+ try (QueryResponse resp = client.query("SELECT 1").get()) {
+ Assert.assertEquals(resp.getReadRows(), 1);
+ }
+ } finally {
+ adminClient.execute("DROP USER IF EXISTS " + user).get().close();
}
}
}
@@ -1077,43 +1079,46 @@ public void testPasswordAuthentication(String identifyWith, String identifyBy, b
return; // Current test is working only with local server because of self-signed certificates.
}
ClickHouseNode server = getServer(ClickHouseProtocol.HTTP);
+ String user = "some_user_" + UUID.randomUUID().toString().replace("-", "");
- try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), false)
+ try (Client adminClient = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), false)
.setUsername("dba")
.setPassword("dba")
.build()) {
- try (CommandResponse resp = client.execute("DROP USER IF EXISTS some_user").get()) {
- }
- try (CommandResponse resp = client.execute("CREATE USER some_user IDENTIFIED WITH " + identifyWith + " BY '" + identifyBy + "'").get()) {
- }
- } catch (Exception e) {
- Assert.fail("Failed on setup", e);
- }
-
+ try {
+ try (CommandResponse resp = adminClient.execute("CREATE USER " + user + " IDENTIFIED WITH " + identifyWith + " BY '" + identifyBy + "'").get()) {
+ } catch (Exception e) {
+ Assert.fail("Failed on setup", e);
+ }
+ waitForAllReplicas("users", "name = '" + user + "'", 1);
- try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), false)
- .setUsername("some_user")
- .setPassword(identifyBy)
- .build()) {
+ try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), false)
+ .setUsername(user)
+ .setPassword(identifyBy)
+ .build()) {
- Assert.assertEquals(client.queryAll("SELECT user()").get(0).getString(1), "some_user");
- } catch (Exception e) {
- Assert.fail("Failed to authenticate", e);
- }
+ Assert.assertEquals(client.queryAll("SELECT user()").get(0).getString(1), user);
+ } catch (Exception e) {
+ Assert.fail("Failed to authenticate", e);
+ }
- if (failsWithHeaders) {
- try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), false)
- .setUsername("some_user")
- .setPassword(identifyBy)
- .useHTTPBasicAuth(false)
- .build()) {
+ if (failsWithHeaders) {
+ try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), false)
+ .setUsername(user)
+ .setPassword(identifyBy)
+ .useHTTPBasicAuth(false)
+ .build()) {
- Assert.expectThrows(ClientException.class, () ->
- client.queryAll("SELECT user()").get(0).getString(1));
+ Assert.expectThrows(ClientException.class, () ->
+ client.queryAll("SELECT user()").get(0).getString(1));
- } catch (Exception e) {
- Assert.fail("Unexpected exception", e);
+ } catch (Exception e) {
+ Assert.fail("Unexpected exception", e);
+ }
+ }
+ } finally {
+ adminClient.execute("DROP USER IF EXISTS " + user).get().close();
}
}
}
@@ -1141,30 +1146,33 @@ public void testAuthHeaderIsKeptFromUser() throws Exception {
String identifyWith = "sha256_password";
String identifyBy = "123ยง";
- try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), false)
+ String user = "some_user_" + UUID.randomUUID().toString().replace("-", "");
+ try (Client adminClient = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), false)
.setUsername("dba")
.setPassword("dba")
.build()) {
- try (CommandResponse resp = client.execute("DROP USER IF EXISTS some_user").get()) {
- }
- try (CommandResponse resp = client.execute("CREATE USER some_user IDENTIFIED WITH " + identifyWith + " BY '" + identifyBy + "'").get()) {
- }
- } catch (Exception e) {
- Assert.fail("Failed on setup", e);
- }
-
+ try {
+ try (CommandResponse resp = adminClient.execute("CREATE USER " + user + " IDENTIFIED WITH " + identifyWith + " BY '" + identifyBy + "'").get()) {
+ } catch (Exception e) {
+ Assert.fail("Failed on setup", e);
+ }
+ waitForAllReplicas("users", "name = '" + user + "'", 1);
- try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), false)
- .setUsername("some_user")
- .setPassword(identifyBy)
- .useHTTPBasicAuth(false) // disable basic auth to produce CH headers
- .httpHeader(HttpHeaders.AUTHORIZATION, "Basic " + Base64.getEncoder().encodeToString(("some_user:" +identifyBy).getBytes()))
- .build()) {
+ try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost",server.getPort(), false)
+ .setUsername(user)
+ .setPassword(identifyBy)
+ .useHTTPBasicAuth(false) // disable basic auth to produce CH headers
+ .httpHeader(HttpHeaders.AUTHORIZATION, "Basic " + Base64.getEncoder().encodeToString((user + ":" +identifyBy).getBytes()))
+ .build()) {
- Assert.assertEquals(client.queryAll("SELECT user()").get(0).getString(1), "some_user");
- } catch (Exception e) {
- Assert.fail("Failed to authenticate", e);
+ Assert.assertEquals(client.queryAll("SELECT user()").get(0).getString(1), user);
+ } catch (Exception e) {
+ Assert.fail("Failed to authenticate", e);
+ }
+ } finally {
+ adminClient.execute("DROP USER IF EXISTS " + user).get().close();
+ }
}
}
diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ReadonlyProfileTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ReadonlyProfileTest.java
index 2c34ed447..0c3322555 100644
--- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ReadonlyProfileTest.java
+++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ReadonlyProfileTest.java
@@ -18,6 +18,10 @@
public class ReadonlyProfileTest extends JdbcIntegrationTest {
private String password;
+ private String user1;
+ private String user2;
+ private String profile1;
+ private String profile2;
@BeforeClass(groups = { "integration" })
public void setup() throws SQLException {
@@ -27,18 +31,25 @@ public void setup() throws SQLException {
// Append fixed character classes so the random password satisfies server
// password-complexity policies (uppercase, lowercase, digit, special).
password = UUID.randomUUID().toString() + "Aa1!";
+ String suffix = "_" + UUID.randomUUID().toString().replace("-", "");
+ user1 = "jdbc_test_user_readonly_1" + suffix;
+ user2 = "jdbc_test_user_readonly_2" + suffix;
+ profile1 = "jdbc_test_profile_readonly_1" + suffix;
+ profile2 = "jdbc_test_profile_readonly_2" + suffix;
com.clickhouse.client.ClickHouseServerForTest.beforeSuite();
try (Connection conn = getJdbcConnection();
Statement stmt = conn.createStatement()) {
- stmt.execute("CREATE SETTINGS PROFILE IF NOT EXISTS jdbc_test_profile_readonly_1 SETTINGS readonly=1");
- stmt.execute("CREATE SETTINGS PROFILE IF NOT EXISTS jdbc_test_profile_readonly_2 SETTINGS readonly=2");
- stmt.execute("DROP USER IF EXISTS jdbc_test_user_readonly_1");
- stmt.execute("DROP USER IF EXISTS jdbc_test_user_readonly_2");
- stmt.execute("CREATE USER jdbc_test_user_readonly_1 IDENTIFIED WITH plaintext_password BY '" + password + "' SETTINGS PROFILE jdbc_test_profile_readonly_1");
- stmt.execute("CREATE USER jdbc_test_user_readonly_2 IDENTIFIED WITH plaintext_password BY '" + password + "' SETTINGS PROFILE jdbc_test_profile_readonly_2");
+ stmt.execute("CREATE SETTINGS PROFILE " + profile1 + " SETTINGS readonly=1");
+ stmt.execute("CREATE SETTINGS PROFILE " + profile2 + " SETTINGS readonly=2");
+ waitForAllReplicas("settings_profiles", "name IN ('" + profile1 + "', '" + profile2 + "')", 2);
+ stmt.execute("CREATE USER " + user1 + " IDENTIFIED WITH plaintext_password BY '" + password + "' SETTINGS PROFILE " + profile1);
+ stmt.execute("CREATE USER " + user2 + " IDENTIFIED WITH plaintext_password BY '" + password + "' SETTINGS PROFILE " + profile2);
+ String users = "'" + user1 + "', '" + user2 + "'";
+ waitForAllReplicas("users", "name IN (" + users + ")", 2);
String db = getDatabase();
- stmt.execute("GRANT SELECT ON " + db + ".* TO jdbc_test_user_readonly_1");
- stmt.execute("GRANT SELECT ON " + db + ".* TO jdbc_test_user_readonly_2");
+ stmt.execute("GRANT SELECT ON " + db + ".* TO " + user1);
+ stmt.execute("GRANT SELECT ON " + db + ".* TO " + user2);
+ waitForAllReplicas("grants", "user_name IN (" + users + ")", 2);
}
}
@@ -49,10 +60,10 @@ public void teardown() throws SQLException {
}
try (Connection conn = getJdbcConnection();
Statement stmt = conn.createStatement()) {
- stmt.execute("DROP USER IF EXISTS jdbc_test_user_readonly_1");
- stmt.execute("DROP USER IF EXISTS jdbc_test_user_readonly_2");
- stmt.execute("DROP SETTINGS PROFILE IF EXISTS jdbc_test_profile_readonly_1");
- stmt.execute("DROP SETTINGS PROFILE IF EXISTS jdbc_test_profile_readonly_2");
+ stmt.execute("DROP USER IF EXISTS " + user1);
+ stmt.execute("DROP USER IF EXISTS " + user2);
+ stmt.execute("DROP SETTINGS PROFILE IF EXISTS " + profile1);
+ stmt.execute("DROP SETTINGS PROFILE IF EXISTS " + profile2);
}
}
@@ -62,7 +73,7 @@ public void testReadonly1CannotChangeSettings() throws Exception {
throw new SkipException("Should not be tested in cloud because creates users and profiles");
}
Properties properties = new Properties();
- properties.setProperty("user", "jdbc_test_user_readonly_1");
+ properties.setProperty("user", user1);
properties.setProperty("password", password);
properties.put(ClientConfigProperties.serverSetting(ServerSettings.OUTPUT_FORMAT_BINARY_WRITE_JSON_AS_STRING), "1");
@@ -86,7 +97,7 @@ public void testReadonly2CanChangeSettings() throws Exception {
}
Properties properties = new Properties();
- properties.setProperty("user", "jdbc_test_user_readonly_2");
+ properties.setProperty("user", user2);
properties.setProperty("password", password);
properties.put(ClientConfigProperties.serverSetting(ServerSettings.OUTPUT_FORMAT_BINARY_WRITE_JSON_AS_STRING), "1");
properties.put(ClientConfigProperties.serverSetting("allow_experimental_json_type"), "1");
diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java
index 0657558df..d753f466a 100644
--- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java
+++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java
@@ -556,118 +556,130 @@ public void testSettingRole(SqlParserFacade.SQLParser parser) throws SQLExceptio
return;
}
- List roles = Arrays.asList("role1", "role2", "role3");
+ String suffix = "_" + RandomStringUtils.insecure().next(8, true, true);
+ String user = "some_user" + suffix;
+ String role1 = "role1" + suffix;
+ String role2 = "role2" + suffix;
+ String role3 = "role3" + suffix;
+ List roles = Arrays.asList(role1, role2, role3);
+ String roleNames = "'" + String.join("','", roles) + "'";
final String userPass = "^1A" + RandomStringUtils.random(12, true, true) + "3B$";
Properties properties = new Properties();
properties.setProperty(DriverProperties.SQL_PARSER.getKey(), parser.name());
- try (ConnectionImpl conn = (ConnectionImpl) getJdbcConnection(properties)) {
- try (Statement stmt = conn.createStatement()) {
- stmt.execute("DROP ROLE IF EXISTS " + String.join(", ", roles));
- stmt.execute("DROP USER IF EXISTS some_user");
- stmt.execute("CREATE ROLE " + String.join(", ", roles));
- stmt.execute("CREATE USER some_user IDENTIFIED BY '" + userPass + "'");
- stmt.execute("GRANT " + String.join(", ", roles) + " TO some_user");
- stmt.execute("SET DEFAULT ROLE NONE TO some_user");
- }
- }
+ try (ConnectionImpl adminConn = (ConnectionImpl) getJdbcConnection(properties);
+ Statement adminStmt = adminConn.createStatement()) {
+ try {
+ adminStmt.execute("CREATE ROLE " + String.join(", ", roles));
+ adminStmt.execute("CREATE USER " + user + " IDENTIFIED BY '" + userPass + "'");
+ waitForAllReplicas("roles", "name IN (" + roleNames + ")", roles.size());
+ waitForAllReplicas("users", "name = '" + user + "'", 1);
+ adminStmt.execute("GRANT " + String.join(", ", roles) + " TO " + user);
+ adminStmt.execute("SET DEFAULT ROLE NONE TO " + user);
+ waitForAllReplicas("role_grants", "user_name = '" + user + "' AND granted_role_is_default = 0",
+ roles.size());
+
+ Properties info = new Properties();
+ info.setProperty("user", user);
+ info.setProperty("password", userPass);
+ info.setProperty(DriverProperties.SQL_PARSER.getKey(), parser.name());
+ try (ConnectionImpl conn = new ConnectionImpl(getEndpointString(), info)) {
+ GenericRecord dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
+ assertEquals(dataRecord.getList(1).size(), 0);
+
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("SET ROLE " + role1);
+ }
- Properties info = new Properties();
- info.setProperty("user", "some_user");
- info.setProperty("password", userPass);
- info.setProperty(DriverProperties.SQL_PARSER.getKey(), parser.name());
- try (ConnectionImpl conn = new ConnectionImpl(getEndpointString(), info)) {
- GenericRecord dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
- assertEquals(dataRecord.getList(1).size(), 0);
+ dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
+ assertEquals(dataRecord.getList(1).size(), 1);
+ assertEquals(dataRecord.getList(1).get(0), role1);
- try (Statement stmt = conn.createStatement()) {
- stmt.execute("SET ROLE role1");
- }
-
- dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
- assertEquals(dataRecord.getList(1).size(), 1);
- assertEquals(dataRecord.getList(1).get(0), "role1");
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("SET ROLE " + role2);
+ }
- try (Statement stmt = conn.createStatement()) {
- stmt.execute("SET ROLE role2");
- }
+ dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
+ assertEquals(dataRecord.getList(1).size(), 1);
+ assertEquals(dataRecord.getList(1).get(0), role2);
- dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
- assertEquals(dataRecord.getList(1).size(), 1);
- assertEquals(dataRecord.getList(1).get(0), "role2");
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("SET ROLE NONE");
+ }
- try (Statement stmt = conn.createStatement()) {
- stmt.execute("SET ROLE NONE");
- }
+ dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
+ assertEquals(dataRecord.getList(1).size(), 0);
- dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
- assertEquals(dataRecord.getList(1).size(), 0);
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("SET ROLE \"" + role1 + "\",\"" + role2 + "\"");
+ }
- try (Statement stmt = conn.createStatement()) {
- stmt.execute("SET ROLE \"role1\",\"role2\"");
- }
+ dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
+ assertEquals(dataRecord.getList(1).size(), 2);
+ assertEquals(dataRecord.getList(1).get(0), role1);
+ assertEquals(dataRecord.getList(1).get(1), role2);
- dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
- assertEquals(dataRecord.getList(1).size(), 2);
- assertEquals(dataRecord.getList(1).get(0), "role1");
- assertEquals(dataRecord.getList(1).get(1), "role2");
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("SET ROLE \"" + role1 + "\",\"" + role2 + "\",\"" + role3 + "\"");
+ }
- try (Statement stmt = conn.createStatement()) {
- stmt.execute("SET ROLE \"role1\",\"role2\",\"role3\"");
- }
+ dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
+ assertEquals(dataRecord.getList(1).size(), 3);
+ assertEquals(dataRecord.getList(1).get(0), role1);
+ assertEquals(dataRecord.getList(1).get(1), role2);
+ assertEquals(dataRecord.getList(1).get(2), role3);
+ }
- dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
- assertEquals(dataRecord.getList(1).size(), 3);
- assertEquals(dataRecord.getList(1).get(0), "role1");
- assertEquals(dataRecord.getList(1).get(1), "role2");
- assertEquals(dataRecord.getList(1).get(2), "role3");
- }
+ Properties disableSavingRoles = new Properties();
+ disableSavingRoles.setProperty("user", user);
+ disableSavingRoles.setProperty("password", userPass);
+ disableSavingRoles.setProperty(DriverProperties.REMEMBER_LAST_SET_ROLES.getKey(), "false");
+ disableSavingRoles.setProperty(DriverProperties.SQL_PARSER.getKey(), parser.name());
+ try (ConnectionImpl conn = new ConnectionImpl(getEndpointString(), disableSavingRoles)) {
+ GenericRecord dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
+ assertEquals(dataRecord.getList(1).size(), 0);
- Properties disableSavingRoles = new Properties();
- disableSavingRoles.setProperty("user", "some_user");
- disableSavingRoles.setProperty("password", userPass);
- disableSavingRoles.setProperty(DriverProperties.REMEMBER_LAST_SET_ROLES.getKey(), "false");
- disableSavingRoles.setProperty(DriverProperties.SQL_PARSER.getKey(), parser.name());
- try (ConnectionImpl conn = new ConnectionImpl(getEndpointString(), disableSavingRoles)) {
- GenericRecord dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
- assertEquals(dataRecord.getList(1).size(), 0);
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("SET ROLE " + role1);
+ }
- try (Statement stmt = conn.createStatement()) {
- stmt.execute("SET ROLE role1");
- }
+ dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
+ assertEquals(dataRecord.getList(1).size(), 0);
- dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
- assertEquals(dataRecord.getList(1).size(), 0);
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("SET ROLE " + role2);
+ }
- try (Statement stmt = conn.createStatement()) {
- stmt.execute("SET ROLE role2");
- }
+ dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
+ assertEquals(dataRecord.getList(1).size(), 0);
- dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
- assertEquals(dataRecord.getList(1).size(), 0);
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("SET ROLE NONE");
+ }
- try (Statement stmt = conn.createStatement()) {
- stmt.execute("SET ROLE NONE");
- }
+ dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
+ assertEquals(dataRecord.getList(1).size(), 0);
- dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
- assertEquals(dataRecord.getList(1).size(), 0);
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("SET ROLE \"" + role1 + "\",\"" + role2 + "\"");
+ }
- try (Statement stmt = conn.createStatement()) {
- stmt.execute("SET ROLE \"role1\",\"role2\"");
- }
+ dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
+ assertEquals(dataRecord.getList(1).size(), 0);
- dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
- assertEquals(dataRecord.getList(1).size(), 0);
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("SET ROLE \"" + role1 + "\",\"" + role2 + "\",\"" + role3 + "\"");
+ }
- try (Statement stmt = conn.createStatement()) {
- stmt.execute("SET ROLE \"role1\",\"role2\",\"role3\"");
+ dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
+ assertEquals(dataRecord.getList(1).size(), 0);
+ }
+ } finally {
+ adminStmt.execute("DROP USER IF EXISTS " + user);
+ adminStmt.execute("DROP ROLE IF EXISTS " + String.join(", ", roles));
}
-
- dataRecord = conn.getClient().queryAll("SELECT currentRoles()").get(0);
- assertEquals(dataRecord.getList(1).size(), 0);
}
}
@@ -1359,18 +1371,24 @@ public void testDDLStatements() throws Exception {
if (isCloud()) {
return; // skip because we do not want to create extra on cloud instance
}
+ String user = "user011_" + RandomStringUtils.insecure().next(8, true, true);
try (Connection conn = getJdbcConnection()) {
try (Statement stmt = conn.createStatement()) {
- Assert.assertFalse(stmt.execute("CREATE USER IF NOT EXISTS 'user011' IDENTIFIED BY 'password'"));
+ try {
+ Assert.assertFalse(stmt.execute("CREATE USER IF NOT EXISTS '" + user + "' IDENTIFIED BY 'password'"));
+ waitForAllReplicas("users", "name = '" + user + "'", 1);
- try (ResultSet rs = stmt.executeQuery("SHOW USERS")) {
- boolean found = false;
- while (rs.next()) {
- if (rs.getString("name").equals("user011")) {
- found = true;
+ try (ResultSet rs = stmt.executeQuery("SHOW USERS")) {
+ boolean found = false;
+ while (rs.next()) {
+ if (rs.getString("name").equals(user)) {
+ found = true;
+ }
}
+ Assert.assertTrue(found);
}
- Assert.assertTrue(found);
+ } finally {
+ stmt.execute("DROP USER IF EXISTS '" + user + "'");
}
}
}
diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/ShowStatementDatabaseMetaDataTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/ShowStatementDatabaseMetaDataTest.java
index 73674f3cb..8cc9a660f 100644
--- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/ShowStatementDatabaseMetaDataTest.java
+++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/ShowStatementDatabaseMetaDataTest.java
@@ -211,22 +211,23 @@ public void testGetColumnsForReadonlyUserWithColumnGrants(String flag, List