From 07d5d3b74bce8cdb477908dfb757ac320bcf825d Mon Sep 17 00:00:00 2001 From: Simon Schneider <10846939+raynigon@users.noreply.github.com> Date: Wed, 17 Mar 2021 08:57:37 +0100 Subject: [PATCH 1/4] Make TimescaleDB available with JDBC syntax #3890 The Syntax for the use of a TimescaleDB Testcontainer should be jdbc:tc:timescaledb:///databasename or jdbc:tc:timescaledb:2.1.0-pg13:///databasename if a Tag is given. This should allow the user to use the container without any further configuration. --- docs/modules/databases/jdbc.md | 4 ++ .../TimescaleDBContainerProvider.java | 37 ++++++++++++ ...s.containers.JdbcDatabaseContainerProvider | 1 + .../containers/TimescaleDBContainerTest.java | 60 +++++++++++++++++++ .../TimescaleDBJDBCDriverTest.java | 22 +++++++ .../resources/somepath/init_timescaledb.sql | 11 ++++ 6 files changed, 135 insertions(+) create mode 100644 modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java create mode 100644 modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java create mode 100644 modules/postgresql/src/test/java/org/testcontainers/jdbc/timescaledb/TimescaleDBJDBCDriverTest.java create mode 100644 modules/postgresql/src/test/resources/somepath/init_timescaledb.sql diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md index 21393455a9f..eda0fc9900f 100644 --- a/docs/modules/databases/jdbc.md +++ b/docs/modules/databases/jdbc.md @@ -39,6 +39,10 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database `jdbc:tc:postgis:9.6:///databasename` +#### Using TimescaleDB + +`jdbc:tc:timescaldb:2.1.0-pg13:///databasename` + #### Using Presto `jdbc:tc:presto:329://localhost/memory/default` diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java new file mode 100644 index 00000000000..f985bdb3439 --- /dev/null +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java @@ -0,0 +1,37 @@ +package org.testcontainers.containers; + +import org.testcontainers.jdbc.ConnectionUrl; +import org.testcontainers.utility.DockerImageName; + +/** + * Factory for PostGIS containers, which are a special flavour of PostgreSQL. + */ +public class TimescaleDBContainerProvider extends JdbcDatabaseContainerProvider { + + private static final String NAME = "timescaledb"; + private static final String DEFAULT_TAG = "2.1.0-pg11"; + private static final String DEFAULT_IMAGE = "timescale/timescaledb"; + public static final String USER_PARAM = "user"; + public static final String PASSWORD_PARAM = "password"; + + + @Override + public boolean supports(String databaseType) { + return databaseType.equals(NAME); + } + + @Override + public JdbcDatabaseContainer newInstance() { + return newInstance(DEFAULT_TAG); + } + + @Override + public JdbcDatabaseContainer newInstance(String tag) { + return new PostgreSQLContainer(DockerImageName.parse(DEFAULT_IMAGE).withTag(DEFAULT_TAG)); + } + + @Override + public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { + return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); + } +} diff --git a/modules/postgresql/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/postgresql/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider index 79b2f29340a..05df9054522 100644 --- a/modules/postgresql/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider +++ b/modules/postgresql/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -1,2 +1,3 @@ org.testcontainers.containers.PostgreSQLContainerProvider org.testcontainers.containers.PostgisContainerProvider +org.testcontainers.containers.TimescaleDBContainerProvider diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java new file mode 100644 index 00000000000..ae27280a898 --- /dev/null +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java @@ -0,0 +1,60 @@ +package org.testcontainers.containers; + +import org.junit.Test; +import org.testcontainers.db.AbstractContainerDatabaseTest; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.logging.Level; +import java.util.logging.LogManager; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; + +public class TimescaleDBContainerTest extends AbstractContainerDatabaseTest { + + @Test + public void testSimple() throws SQLException { + try (JdbcDatabaseContainer postgres = new TimescaleDBContainerProvider().newInstance()) { + postgres.start(); + + ResultSet resultSet = performQuery(postgres, "SELECT 1"); + int resultSetInt = resultSet.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + } + + @Test + public void testCommandOverride() throws SQLException { + try (GenericContainer postgres = new TimescaleDBContainerProvider().newInstance().withCommand("postgres -c max_connections=42")) { + postgres.start(); + + ResultSet resultSet = performQuery((JdbcDatabaseContainer) postgres, "SELECT current_setting('max_connections')"); + String result = resultSet.getString(1); + assertEquals("max_connections should be overriden", "42", result); + } + } + + @Test + public void testUnsetCommand() throws SQLException { + try (GenericContainer postgres = new TimescaleDBContainerProvider().newInstance().withCommand("postgres -c max_connections=42").withCommand()) { + postgres.start(); + + ResultSet resultSet = performQuery((JdbcDatabaseContainer) postgres, "SELECT current_setting('max_connections')"); + String result = resultSet.getString(1); + assertNotEquals("max_connections should not be overriden", "42", result); + } + } + + @Test + public void testExplicitInitScript() throws SQLException { + try (JdbcDatabaseContainer postgres = new TimescaleDBContainerProvider().newInstance().withInitScript("somepath/init_timescaledb.sql")) { + postgres.start(); + + ResultSet resultSet = performQuery(postgres, "SELECT foo FROM bar"); + + String firstColumnValue = resultSet.getString(1); + assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + } + } +} diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/timescaledb/TimescaleDBJDBCDriverTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/timescaledb/TimescaleDBJDBCDriverTest.java new file mode 100644 index 00000000000..8a6eaa3d04b --- /dev/null +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/timescaledb/TimescaleDBJDBCDriverTest.java @@ -0,0 +1,22 @@ +package org.testcontainers.jdbc.timescaledb; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import java.util.EnumSet; + +import static java.util.Arrays.asList; + +@RunWith(Parameterized.class) +public class TimescaleDBJDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return asList( + new Object[][]{ + {"jdbc:tc:timescaledb://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, + {"jdbc:tc:timescaledb:2.1.0-pg13://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, + }); + } +} diff --git a/modules/postgresql/src/test/resources/somepath/init_timescaledb.sql b/modules/postgresql/src/test/resources/somepath/init_timescaledb.sql new file mode 100644 index 00000000000..b4d80b9ce02 --- /dev/null +++ b/modules/postgresql/src/test/resources/somepath/init_timescaledb.sql @@ -0,0 +1,11 @@ +-- Extend the database with TimescaleDB +CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE; + +CREATE TABLE bar ( + foo VARCHAR(255), + time TIMESTAMPTZ NOT NULL +); + +SELECT create_hypertable('bar', 'time'); + +INSERT INTO bar (time, foo) VALUES (CURRENT_TIMESTAMP, 'hello world'); From ffe29878469465c3457ab68c11d5e8aab268c389 Mon Sep 17 00:00:00 2001 From: Simon Schneider <10846939+raynigon@users.noreply.github.com> Date: Wed, 17 Mar 2021 09:55:13 +0100 Subject: [PATCH 2/4] Fix Docker Image and JavaDoc The DockerImage has to be compatible with PostgreSQL --- .../containers/TimescaleDBContainerProvider.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java index f985bdb3439..9a5034b1455 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java @@ -4,15 +4,17 @@ import org.testcontainers.utility.DockerImageName; /** - * Factory for PostGIS containers, which are a special flavour of PostgreSQL. + * Factory for TimescaleDB containers, which are a special flavour of PostgreSQL. + * + * @see https://docs.timescale.com/latest/introduction */ public class TimescaleDBContainerProvider extends JdbcDatabaseContainerProvider { private static final String NAME = "timescaledb"; private static final String DEFAULT_TAG = "2.1.0-pg11"; - private static final String DEFAULT_IMAGE = "timescale/timescaledb"; + private static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("timescale/timescaledb").asCompatibleSubstituteFor("postgres"); public static final String USER_PARAM = "user"; - public static final String PASSWORD_PARAM = "password"; + public static final String PASSWORD_PARAM = "pTimescaleDBContainerTestassword"; @Override @@ -27,7 +29,7 @@ public JdbcDatabaseContainer newInstance() { @Override public JdbcDatabaseContainer newInstance(String tag) { - return new PostgreSQLContainer(DockerImageName.parse(DEFAULT_IMAGE).withTag(DEFAULT_TAG)); + return new PostgreSQLContainer(DEFAULT_IMAGE.withTag(tag)); } @Override From 0fb5d3c05a21440dab35e68656d0e08f85a3be58 Mon Sep 17 00:00:00 2001 From: Simon Schneider <10846939+raynigon@users.noreply.github.com> Date: Wed, 17 Mar 2021 10:08:52 +0100 Subject: [PATCH 3/4] Fix Docker Image and JavaDoc The DockerImage has to be compatible with PostgreSQL --- .../testcontainers/containers/TimescaleDBContainerProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java index 9a5034b1455..c9b81fccfe4 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java @@ -14,7 +14,7 @@ public class TimescaleDBContainerProvider extends JdbcDatabaseContainerProvider private static final String DEFAULT_TAG = "2.1.0-pg11"; private static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("timescale/timescaledb").asCompatibleSubstituteFor("postgres"); public static final String USER_PARAM = "user"; - public static final String PASSWORD_PARAM = "pTimescaleDBContainerTestassword"; + public static final String PASSWORD_PARAM = "password"; @Override From 5bf76609ac010d68c20228efe70517774ff6486b Mon Sep 17 00:00:00 2001 From: Simon Schneider <10846939+raynigon@users.noreply.github.com> Date: Wed, 17 Mar 2021 11:07:16 +0100 Subject: [PATCH 4/4] Fix Test Case form TimescaleDB --- .../testcontainers/jdbc/AbstractJDBCDriverTest.java | 4 +++- .../containers/TimescaleDBContainerTest.java | 2 -- .../src/test/resources/somepath/init_timescaledb.sql | 10 ++++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java index 38ca2237567..580aad513e3 100644 --- a/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java +++ b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java @@ -117,7 +117,9 @@ private void performTestForJDBCParamUsage(HikariDataSource dataSource) throws SQ String databaseQuery = "SELECT DATABASE()"; // Postgres does not have Database() as a function String databaseType = ConnectionUrl.newInstance(jdbcUrl).getDatabaseType(); - if (databaseType.equalsIgnoreCase("postgresql") || databaseType.equalsIgnoreCase("postgis")) { + if (databaseType.equalsIgnoreCase("postgresql") || + databaseType.equalsIgnoreCase("postgis") || + databaseType.equalsIgnoreCase("timescaledb")) { databaseQuery = "SELECT CURRENT_DATABASE()"; } diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java index ae27280a898..f3c73a4111b 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java @@ -5,8 +5,6 @@ import java.sql.ResultSet; import java.sql.SQLException; -import java.util.logging.Level; -import java.util.logging.LogManager; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; diff --git a/modules/postgresql/src/test/resources/somepath/init_timescaledb.sql b/modules/postgresql/src/test/resources/somepath/init_timescaledb.sql index b4d80b9ce02..da11604cf10 100644 --- a/modules/postgresql/src/test/resources/somepath/init_timescaledb.sql +++ b/modules/postgresql/src/test/resources/somepath/init_timescaledb.sql @@ -1,11 +1,13 @@ -- Extend the database with TimescaleDB CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE; -CREATE TABLE bar ( - foo VARCHAR(255), - time TIMESTAMPTZ NOT NULL +CREATE TABLE bar +( + foo VARCHAR(255), + time TIMESTAMPTZ NOT NULL ); SELECT create_hypertable('bar', 'time'); -INSERT INTO bar (time, foo) VALUES (CURRENT_TIMESTAMP, 'hello world'); +INSERT INTO bar (time, foo) +VALUES (CURRENT_TIMESTAMP, 'hello world');