Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add clickhouse r2dbc support #8434

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 8 additions & 1 deletion modules/clickhouse/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ dependencies {
api project(':testcontainers')
api project(':jdbc')

compileOnly project(':r2dbc')
compileOnly(group: 'com.clickhouse', name: 'clickhouse-r2dbc', version: '0.6.0', classifier: 'http')

testImplementation project(':jdbc-test')
testRuntimeOnly 'ru.yandex.clickhouse:clickhouse-jdbc:0.3.2'
testRuntimeOnly(group: 'com.clickhouse', name: 'clickhouse-jdbc', version: '0.6.0', classifier: 'http')
testImplementation 'org.assertj:assertj-core:3.25.1'
testImplementation testFixtures(project(':r2dbc'))
testRuntimeOnly(group: 'com.clickhouse', name: 'clickhouse-r2dbc', version: '0.6.0', classifier: 'http')

testRuntimeOnly("org.apache.httpcomponents.client5:httpclient5:5.2.1")
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,29 @@ public class ClickHouseContainer extends JdbcDatabaseContainer<ClickHouseContain

private static final String NAME = "clickhouse";

private static final DockerImageName CLICKHOUSE_IMAGE_NAME = DockerImageName.parse("clickhouse/clickhouse-server");
static final String CLICKHOUSE_CLICKHOUSE_SERVER = "clickhouse/clickhouse-server";

private static final Integer HTTP_PORT = 8123;
private static final DockerImageName CLICKHOUSE_IMAGE_NAME = DockerImageName.parse(CLICKHOUSE_CLICKHOUSE_SERVER);

private static final Integer NATIVE_PORT = 9000;
static final Integer HTTP_PORT = 8123;

static final Integer NATIVE_PORT = 9000;

private static final String DRIVER_CLASS_NAME = "com.clickhouse.jdbc.ClickHouseDriver";

private static final String JDBC_URL_PREFIX = "jdbc:" + NAME + "://";

private static final String TEST_QUERY = "SELECT 1";

static final String DEFAULT_USER = "default";

static final String DEFAULT_PASSWORD = "";

private String databaseName = "default";

private String username = "default";
private String username = DEFAULT_USER;

private String password = "";
private String password = DEFAULT_PASSWORD;

public ClickHouseContainer(String dockerImageName) {
this(DockerImageName.parse(dockerImageName));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.testcontainers.clickhouse;

import io.r2dbc.spi.ConnectionFactoryOptions;
import org.testcontainers.r2dbc.R2DBCDatabaseContainer;

/**
* ClickHouse R2DBC support
*/
public class ClickHouseR2DBCDatabaseContainer implements R2DBCDatabaseContainer {

private final ClickHouseContainer container;

public ClickHouseR2DBCDatabaseContainer(ClickHouseContainer container) {
this.container = container;
}

public static ConnectionFactoryOptions getOptions(ClickHouseContainer container) {
ConnectionFactoryOptions options = ConnectionFactoryOptions
.builder()
.option(ConnectionFactoryOptions.DRIVER, ClickHouseR2DBCDatabaseContainerProvider.DRIVER)
.build();

return new ClickHouseR2DBCDatabaseContainer(container).configure(options);
}

@Override
public void start() {
container.start();
}

@Override
public void stop() {
container.stop();
}

@Override
public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) {
return options
.mutate()
.option(ConnectionFactoryOptions.HOST, container.getHost())
.option(ConnectionFactoryOptions.PORT, container.getMappedPort(ClickHouseContainer.HTTP_PORT))
.option(ConnectionFactoryOptions.DATABASE, container.getDatabaseName())
.option(ConnectionFactoryOptions.USER, container.getUsername())
.option(ConnectionFactoryOptions.PASSWORD, container.getPassword())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.testcontainers.clickhouse;

import com.clickhouse.r2dbc.connection.ClickHouseConnectionFactoryProvider;
import io.r2dbc.spi.ConnectionFactoryMetadata;
import io.r2dbc.spi.ConnectionFactoryOptions;
import org.testcontainers.r2dbc.R2DBCDatabaseContainer;
import org.testcontainers.r2dbc.R2DBCDatabaseContainerProvider;

import javax.annotation.Nullable;

public class ClickHouseR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider {

static final String DRIVER = ClickHouseConnectionFactoryProvider.CLICKHOUSE_DRIVER;

@Override
public boolean supports(ConnectionFactoryOptions options) {
return DRIVER.equals(options.getRequiredValue(ConnectionFactoryOptions.DRIVER));
}

@Override
public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) {
String image =
ClickHouseContainer.CLICKHOUSE_CLICKHOUSE_SERVER + ":" + options.getRequiredValue(IMAGE_TAG_OPTION);
ClickHouseContainer container = new ClickHouseContainer(image)
.withDatabaseName((String) options.getRequiredValue(ConnectionFactoryOptions.DATABASE));

if (Boolean.TRUE.equals(options.getValue(REUSABLE_OPTION))) {
container.withReuse(true);
}
return new ClickHouseR2DBCDatabaseContainer(container);
}

@Nullable
@Override
public ConnectionFactoryMetadata getMetadata(ConnectionFactoryOptions options) {
ConnectionFactoryOptions.Builder builder = options.mutate();
if (!options.hasOption(ConnectionFactoryOptions.USER)) {
builder.option(ConnectionFactoryOptions.USER, ClickHouseContainer.DEFAULT_USER);
}
if (!options.hasOption(ConnectionFactoryOptions.PASSWORD)) {
builder.option(ConnectionFactoryOptions.PASSWORD, ClickHouseContainer.DEFAULT_PASSWORD);
}
return R2DBCDatabaseContainerProvider.super.getMetadata(builder.build());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.testcontainers.clickhouse.ClickHouseR2DBCDatabaseContainerProvider
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.testcontainers.clickhouse;

import io.r2dbc.spi.ConnectionFactoryOptions;
import org.testcontainers.r2dbc.AbstractR2DBCDatabaseContainerTest;

public class ClickHouseR2DBCDatabaseContainerTest extends AbstractR2DBCDatabaseContainerTest<ClickHouseContainer> {

@Override
protected ConnectionFactoryOptions getOptions(ClickHouseContainer container) {
ConnectionFactoryOptions options = ClickHouseR2DBCDatabaseContainer.getOptions(container);

return options;
}

@Override
protected String createR2DBCUrl() {
return "r2dbc:tc:clickhouse:///db?TC_IMAGE_TAG=21.9.2-alpine";
}

@Override
protected ClickHouseContainer createContainer() {
return new ClickHouseContainer("clickhouse/clickhouse-server:21.9.2-alpine");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,17 @@ private ConnectionFactoryOptions removeProxying(ConnectionFactoryOptions options
String[] protocols = protocol.split(":", 2);
String driverDelegate = protocols[0];

// when protocol does NOT contain COLON, the length becomes 1
String protocolDelegate = protocols.length == 2 ? protocols[1] : "";

return ConnectionFactoryOptions
ConnectionFactoryOptions.Builder builder = ConnectionFactoryOptions
.builder()
.from(options)
.option(ConnectionFactoryOptions.DRIVER, driverDelegate)
.option(ConnectionFactoryOptions.PROTOCOL, protocolDelegate)
.build();
.option(ConnectionFactoryOptions.DRIVER, driverDelegate);

if (protocols.length == 2) {
// when protocol does NOT contain COLON, the length becomes 1
builder.option(ConnectionFactoryOptions.PROTOCOL, protocols[1]);
}

return builder.build();
}

@Override
Expand Down