Skip to content

Include connection parameters during the creation of SqlConnectOptions #1225

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

Closed
wants to merge 3 commits into from
Closed
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 @@ -7,6 +7,7 @@

import java.lang.invoke.MethodHandles;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

Expand Down Expand Up @@ -130,6 +131,7 @@ public SqlConnectOptions connectOptions(URI uri) {
//see if the credentials were specified via properties
String username = user;
String password = pass;
Map<String, String> extraProps = new HashMap<>();
if ( username == null || password == null ) {
//if not, look for URI-style user info first
String userInfo = uri.getUserInfo();
Expand Down Expand Up @@ -184,6 +186,10 @@ else if ( param.startsWith( "password=" ) ) {
else if ( param.startsWith( "database=" ) ) {
database = param.substring( 9 );
}
else {
String[] propertySet = param.split( "=" );
extraProps.put( propertySet[0], propertySet[1] );
}
}
}
}
Expand All @@ -203,6 +209,10 @@ else if ( param.startsWith( "database=" ) ) {
connectOptions.setPassword( password );
}

for ( String key : extraProps.keySet() ) {
connectOptions.addProperty( key, extraProps.get( key ) );
}

//enable the prepared statement cache by default
connectOptions.setCachePreparedStatements( true );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,67 +5,130 @@
*/
package org.hibernate.reactive.configuration;

import java.net.URI;
import java.util.HashMap;
import java.util.Map;

import org.hibernate.HibernateError;
import org.hibernate.reactive.pool.impl.DefaultSqlClientPool;
import org.hibernate.reactive.pool.impl.DefaultSqlClientPoolConfiguration;

import org.junit.Test;

import java.net.URI;

import io.vertx.sqlclient.SqlConnectOptions;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.SQLSERVER;
import static org.hibernate.reactive.containers.DatabaseConfiguration.createJdbcUrl;
import static org.hibernate.reactive.containers.DatabaseConfiguration.dbType;
import static org.junit.Assert.assertThrows;

/**
* Test the correct creation of the {@link SqlConnectOptions}
* given a JDBC connection URL.
* <p>
* This test doesn't require docker.
*/
public class JdbcUrlParserTest {

@Test
public void returnsNullForNull() {
assertThrows( HibernateError.class, () -> {
final HibernateError error = assertThrows( HibernateError.class, () -> {
URI uri = DefaultSqlClientPool.parse( null );
assertThat( uri ).isNull();
} );
assertThat( error.getMessage() ).contains( "was not provided" );
}

@Test
public void invalidParameter() {
assertThrows( HibernateError.class, () -> {
URI uri = DefaultSqlClientPool.parse( "jdbc:postgresql://localhost:5432/hreact" );
DefaultSqlClientPoolConfiguration cfg = new DefaultSqlClientPoolConfiguration();
final SqlConnectOptions connectOptions = cfg.connectOptions( uri );
public void missingUser() {
final HibernateError error = assertThrows( HibernateError.class, () -> {
URI uri = DefaultSqlClientPool.parse( defaultUrl() );
new DefaultSqlClientPoolConfiguration().connectOptions( uri );
} );
assertThat( error.getMessage() ).contains( "database username not specified" );
}

@Test
public void parameters() {
URI uri = DefaultSqlClientPool.parse( "jdbc:postgresql://localhost:5432/hreact?user=hello");
DefaultSqlClientPoolConfiguration cfg = new DefaultSqlClientPoolConfiguration();
final SqlConnectOptions connectOptions = cfg.connectOptions( uri );
assertThat(connectOptions).isNotNull();
public void testOptionsWithExtraProperties() {
Map<String, String> params = new HashMap<>();
params.put( "user", "hello" );
params.put( "param1", "value1" );
params.put( "param2", "value2" );
params.put( "param3", "value3" );

assertOptions( createUrl( params ), defaultDb(), params );
}

@Test
public void uriCreation() {
URI uri = DefaultSqlClientPool.parse( "jdbc:postgresql://localhost:5432/hreact");
assertThat(uri).isNotNull();
public void testOptionsWithoutExtraProperties() {
// Without a user we would have an exception
Map<String, String> params = new HashMap<>();
params.put( "user", "PerryThePlatypus" );

assertOptions( createUrl( params ), defaultDb(), params );
}

@Test
public void parsePort() {
URI uri = DefaultSqlClientPool.parse( "jdbc:postgresql://localhost:5432/hreact");
assertThat(uri).hasPort(5432);
public void testOptionsWithPasswordAndProperties() {
Map<String, String> params = new HashMap<>();
params.put( "password", "helloPwd" );
params.put( "user", "username" );
params.put( "param2", "Value2" );

assertOptions( createUrl( params ), defaultDb(), params );
}

@Test
public void parseHost() {
URI uri = DefaultSqlClientPool.parse( "jdbc:postgresql://localhost:5432/hreact");
assertThat(uri).hasHost("localhost");
public void testDatabaseAsProperty() {
Map<String, String> params = new HashMap<>();
params.put( "database", "helloDatabase" );
params.put( "user", "PerryThePlatypus" );
params.put( "password", "XxXxX" );
params.put( "param2", "Value2" );

assertOptions( createUrl( params ), "helloDatabase", params );
}

@Test
public void parseScheme() {
URI uri = DefaultSqlClientPool.parse( "jdbc:postgresql://localhost:5432/hreact");
assertThat(uri).hasScheme("postgresql");
/**
* Create the default {@link SqlConnectOptions} with the given extra properties
* and assert that's correct.
*
* @return the created options in case a test needs custom extra assertions
*/
private SqlConnectOptions assertOptions(String url, String expectedDbName, Map<String, String> parameters) {
URI uri = DefaultSqlClientPool.parse( url );
SqlConnectOptions options = new DefaultSqlClientPoolConfiguration().connectOptions( uri );

// These keys won't be mapped as properties
String username = parameters.remove( "user" );
String password = parameters.remove( "password" );
parameters.remove( "database" );

assertThat( options ).as( "URL: " + url ).isNotNull();
assertThat( options.getUser() ).as( "URL: " + url ).isEqualTo( username );
assertThat( options.getPassword() ).as( "URL: " + url ).isEqualTo( password );
assertThat( options.getDatabase() ).as( "URL: " + url ).isEqualTo( expectedDbName );
assertThat( options.getHost() ).as( "URL: " + url ).isEqualTo( "localhost" );
assertThat( options.getPort() ).as( "URL: " + url ).isEqualTo( dbType().getDefaultPort() );

// Check extra properties
assertThat( options.getProperties() ).as( "URL: " + url ).containsExactlyInAnyOrderEntriesOf( parameters );
return options;
}

private String defaultUrl() {
return createUrl( new HashMap<>() );
}

/**
* Create the JDBC Url with the additional extra properties (if any)
*/
private String createUrl(Map<String, String> properties) {
return createJdbcUrl( "localhost", dbType().getDefaultPort(), defaultDb(), properties );
}

private String defaultDb() {
return dbType() == SQLSERVER ? "" : "hreactDB";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,16 @@ private String address() {
return getRegularJdbcUrl();
}

@Override
public String jdbcStartQuery() {
return ":";
}

@Override
public String jdbcParamDelimiter() {
return ";";
}

private static String buildJdbcUrlWithCredentials(String jdbcUrl) {
return jdbcUrl + ":user=" + db2.getUsername() + ";password=" + db2.getPassword() + ";";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package org.hibernate.reactive.containers;

import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;

Expand Down Expand Up @@ -92,6 +93,10 @@ public static String getUri() {
return dbType().configuration.getUri();
}

public static String createJdbcUrl(String host, int port, String database, Map<String, String> properties) {
return dbType().configuration.createJdbcUrl( host, port, database, properties );
}

public static String getDatatypeQuery(String tableName, String columnName) {
return dbType().configuration.getNativeDatatypeQuery( tableName, columnName );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@ private String address() {
return getRegularJdbcUrl();
}

@Override
public String jdbcStartQuery() {
return ";";
}

@Override
public String jdbcParamDelimiter() {
return ";";
}

private String buildJdbcUrlWithCredentials(String jdbcUrl) {
return jdbcUrl + ";user=" + mssqlserver.getUsername() + ";password=" + mssqlserver.getPassword();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public String getJdbcUrl() {
return buildJdbcUrlWithCredentials( address() );
}

@Override
public String getScheme() {
return "mariadb:";
}

@Override
public String getUri() {
return buildUriWithCredentials( address() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,33 @@ private String getRegularJdbcUrl() {
return "jdbc:oracle:thin:" + getCredentials() + "@localhost:1521/" + oracle.getDatabaseName();
}

@Override
public String createJdbcUrl(String host, int port, String database, Map<String, String> params) {
final StringBuilder paramsBuilder = new StringBuilder();
if ( params != null && !params.isEmpty() ) {
params.forEach( (key, value) -> {
paramsBuilder.append( jdbcParamDelimiter() );
paramsBuilder.append( key );
paramsBuilder.append( "=" );
paramsBuilder.append( value );
} );
}

String url = "jdbc:oracle:thin:@" + host + ":" + port;
if ( database != null && !database.isBlank() ) {
url += "/" + database;
}
if ( paramsBuilder.length() > 0 ) {
url += jdbcStartQuery() + paramsBuilder.substring( 1 );
}
return url;
}

@Override
public String getScheme() {
return "oracle:thin:";
}

@Override
public String getUri() {
// The url is different here because we expect it to work with io.vertx.oracleclient.impl.OracleConnectionUriParser
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class PostgreSQLDatabase implements TestableDatabase {
.withReuse( true );

private String getRegularJdbcUrl() {
return "jdbc:postgresql://localhost:5432/" + postgresql.getDatabaseName() + "?loggerLevel=OFF";
return "jdbc:postgresql://localhost:5432/" + postgresql.getDatabaseName();
}

@Override
Expand Down Expand Up @@ -117,14 +117,16 @@ private String address() {
// Calling start() will start the container (if not already started)
// It is required to call start() before obtaining the JDBC URL because it will contain a randomized port
postgresql.start();
return postgresql.getJdbcUrl();
// Latest Postgres JDBC driver has dropped support for loggerLevel
// and the Vert.x driver throws an exception because it does not recognize it
return postgresql.getJdbcUrl().replace( "?loggerLevel=OFF", "" );
}

return getRegularJdbcUrl();
}

private static String buildJdbcUrlWithCredentials(String jdbcUrl) {
return jdbcUrl + "&user=" + postgresql.getUsername() + "&password=" + postgresql.getPassword();
return jdbcUrl + "?user=" + postgresql.getUsername() + "&password=" + postgresql.getPassword();
}

private static String buildUriWithCredentials(String jdbcUrl) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
*/
package org.hibernate.reactive.containers;

import java.util.Map;

import static org.hibernate.reactive.containers.DatabaseConfiguration.dbType;

/**
* A database that we use for testing.
*/
Expand All @@ -14,9 +18,44 @@ public interface TestableDatabase {

String getUri();

/**
* @return the database scheme for the connection. Example: {@code mysql:}
*/
default String getScheme() {
return dbType().name().toLowerCase() + ":";
}

default String getNativeDatatypeQuery(String tableName, String columnName) {
return "select data_type from information_schema.columns where table_name = '" + tableName + "' and column_name = '" + columnName + "'";
}

String getExpectedNativeDatatype(Class<?> dataType);

default String createJdbcUrl(String host, int port, String database, Map<String, String> params) {
final StringBuilder paramsBuilder = new StringBuilder();
if ( params != null && !params.isEmpty() ) {
params.forEach( (key, value) -> {
paramsBuilder.append( jdbcParamDelimiter() );
paramsBuilder.append( key );
paramsBuilder.append( "=" );
paramsBuilder.append( value );
} );
}
String url = "jdbc:" + getScheme() + "//" + host + ":" + port;
if ( database != null && !database.isBlank() ) {
url += "/" + database;
}
if ( paramsBuilder.length() > 0 ) {
url += jdbcStartQuery() + paramsBuilder.substring( 1 );
}
return url;
}

default String jdbcStartQuery() {
return "?";
}

default String jdbcParamDelimiter() {
return "&";
}
}