Skip to content

Commit b37c151

Browse files
committed
chore: Use domain name from JDBC URL for Postgres and Mysql, Part of #2043.
When connecting to a Cloud SQL database using a domain name like `db.example.com`, you may now set the domain name in the JDBC URL. For example, you can use a URL like "jdbc:mysql://db.example.com/my-schema?socketFactory=socketFactory=com.google.cloud.sql.mysql.SocketFactory" The socket factory will detect "db.example.com" and look up the TXT record to resolve the instance name. See #2043 for the whole feature definition.
1 parent c2210d8 commit b37c151

File tree

7 files changed

+220
-5
lines changed

7 files changed

+220
-5
lines changed

jdbc/mariadb/src/main/java/com/google/cloud/sql/mariadb/SocketFactory.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class SocketFactory extends ConfigurableSocketFactory {
3737
}
3838

3939
private Configuration conf;
40+
private String host;
4041

4142
/** Instantiate a socket factory. */
4243
public SocketFactory() {}
@@ -45,13 +46,14 @@ public SocketFactory() {}
4546
public void setConfiguration(Configuration conf, String host) {
4647
// Ignore the hostname
4748
this.conf = conf;
49+
this.host = host;
4850
}
4951

5052
@Override
5153
public Socket createSocket() throws IOException {
5254
try {
5355
return InternalConnectorRegistry.getInstance()
54-
.connect(ConnectionConfig.fromConnectionProperties(conf.nonMappedOptions()));
56+
.connect(ConnectionConfig.fromConnectionProperties(conf.nonMappedOptions(), host));
5557
} catch (InterruptedException e) {
5658
throw new RuntimeException(e);
5759
}

jdbc/mysql-j-8/src/main/java/com/google/cloud/sql/mysql/SocketFactory.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public <T extends Closeable> T connect(
6060
T socket =
6161
(T)
6262
InternalConnectorRegistry.getInstance()
63-
.connect(ConnectionConfig.fromConnectionProperties(props));
63+
.connect(ConnectionConfig.fromConnectionProperties(props, host));
6464
return socket;
6565
}
6666

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.sql.mysql;
18+
19+
import static com.google.common.truth.Truth.assertThat;
20+
import static com.google.common.truth.Truth.assertWithMessage;
21+
22+
import com.google.cloud.sql.ConnectorConfig;
23+
import com.google.cloud.sql.ConnectorRegistry;
24+
import com.google.common.collect.ImmutableList;
25+
import com.zaxxer.hikari.HikariConfig;
26+
import com.zaxxer.hikari.HikariDataSource;
27+
import java.sql.*;
28+
import java.util.ArrayList;
29+
import java.util.List;
30+
import java.util.Properties;
31+
import java.util.concurrent.TimeUnit;
32+
import org.junit.Before;
33+
import org.junit.BeforeClass;
34+
import org.junit.Rule;
35+
import org.junit.Test;
36+
import org.junit.rules.Timeout;
37+
import org.junit.runner.RunWith;
38+
import org.junit.runners.JUnit4;
39+
40+
@RunWith(JUnit4.class)
41+
public class JdbcMysqlJ8DomainNameIntegrationTests {
42+
43+
private static final String CONNECTION_NAME = System.getenv("MYSQL_CONNECTION_NAME");
44+
private static final String DB_NAME = System.getenv("MYSQL_DB");
45+
private static final String DB_USER = System.getenv("MYSQL_USER");
46+
private static final String DB_PASSWORD = System.getenv("MYSQL_PASS");
47+
private static final ImmutableList<String> requiredEnvVars =
48+
ImmutableList.of("MYSQL_USER", "MYSQL_PASS", "MYSQL_DB", "MYSQL_CONNECTION_NAME");
49+
@Rule public Timeout globalTimeout = new Timeout(80, TimeUnit.SECONDS);
50+
private HikariDataSource connectionPool;
51+
52+
@BeforeClass
53+
public static void checkEnvVars() {
54+
// Check that required env vars are set
55+
requiredEnvVars.forEach(
56+
(varName) ->
57+
assertWithMessage(
58+
String.format(
59+
"Environment variable '%s' must be set to perform these tests.", varName))
60+
.that(System.getenv(varName))
61+
.isNotEmpty());
62+
}
63+
64+
@Before
65+
public void setUpPool() throws SQLException {
66+
// Set up URL parameters
67+
String jdbcURL = String.format("jdbc:mysql://db.example.com/%s", DB_NAME);
68+
Properties connProps = new Properties();
69+
connProps.setProperty("user", DB_USER);
70+
connProps.setProperty("password", DB_PASSWORD);
71+
connProps.setProperty("socketFactory", "com.google.cloud.sql.mysql.SocketFactory");
72+
73+
// Register a resolver that resolves `db.example.com` to the connection name
74+
connProps.setProperty("cloudSqlNamedConnector", "resolver-test");
75+
ConnectorRegistry.register(
76+
"resolver-test",
77+
new ConnectorConfig.Builder()
78+
.withInstanceNameResolver((n) -> "db.example.com".equals(n) ? CONNECTION_NAME : null)
79+
.build());
80+
81+
// Initialize connection pool
82+
HikariConfig config = new HikariConfig();
83+
config.setJdbcUrl(jdbcURL);
84+
config.setDataSourceProperties(connProps);
85+
config.setConnectionTimeout(10000); // 10s
86+
87+
this.connectionPool = new HikariDataSource(config);
88+
}
89+
90+
@Test
91+
public void pooledConnectionTest() throws SQLException {
92+
93+
List<Timestamp> rows = new ArrayList<>();
94+
try (Connection conn = connectionPool.getConnection()) {
95+
try (PreparedStatement selectStmt = conn.prepareStatement("SELECT NOW() as TS")) {
96+
ResultSet rs = selectStmt.executeQuery();
97+
while (rs.next()) {
98+
rows.add(rs.getTimestamp("TS"));
99+
}
100+
}
101+
}
102+
assertThat(rows.size()).isEqualTo(1);
103+
}
104+
}

jdbc/mysql-j-8/src/test/java/com/google/cloud/sql/mysql/JdbcMysqlJ8IntegrationTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public static void checkEnvVars() {
6262
@Before
6363
public void setUpPool() throws SQLException {
6464
// Set up URL parameters
65-
String jdbcURL = String.format("jdbc:mysql:///%s", DB_NAME);
65+
String jdbcURL = String.format("jdbc:mysql://db.example.com/%s", DB_NAME);
6666
Properties connProps = new Properties();
6767
connProps.setProperty("user", DB_USER);
6868
connProps.setProperty("password", DB_PASSWORD);

jdbc/postgres/src/main/java/com/google/cloud/sql/postgres/SocketFactory.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ public class SocketFactory extends javax.net.SocketFactory {
3838
private static final String DEPRECATED_SOCKET_ARG = "SocketFactoryArg";
3939
private static final String POSTGRES_SUFFIX = "/.s.PGSQL.5432";
4040

41+
/** The connection property containing the hostname from the JDBC url. */
42+
private static final String POSTGRES_HOST_PROP = "PGHOST";
43+
4144
private final Properties props;
4245

4346
static {
@@ -83,7 +86,9 @@ private static Properties createDefaultProperties(String instanceName) {
8386
public Socket createSocket() throws IOException {
8487
try {
8588
return InternalConnectorRegistry.getInstance()
86-
.connect(ConnectionConfig.fromConnectionProperties(props));
89+
.connect(
90+
ConnectionConfig.fromConnectionProperties(
91+
props, props.getProperty(POSTGRES_HOST_PROP)));
8792
} catch (InterruptedException e) {
8893
throw new RuntimeException(e);
8994
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.sql.postgres;
18+
19+
import static com.google.common.truth.Truth.assertThat;
20+
import static com.google.common.truth.Truth.assertWithMessage;
21+
22+
import com.google.cloud.sql.ConnectorConfig;
23+
import com.google.cloud.sql.ConnectorRegistry;
24+
import com.google.common.collect.ImmutableList;
25+
import com.zaxxer.hikari.HikariConfig;
26+
import com.zaxxer.hikari.HikariDataSource;
27+
import java.sql.*;
28+
import java.util.ArrayList;
29+
import java.util.List;
30+
import java.util.Properties;
31+
import java.util.concurrent.TimeUnit;
32+
import org.junit.Before;
33+
import org.junit.BeforeClass;
34+
import org.junit.Rule;
35+
import org.junit.Test;
36+
import org.junit.rules.Timeout;
37+
import org.junit.runner.RunWith;
38+
import org.junit.runners.JUnit4;
39+
40+
@RunWith(JUnit4.class)
41+
public class JdbcPostgresDomainNameIntegrationTests {
42+
43+
private static final String CONNECTION_NAME = System.getenv("POSTGRES_CONNECTION_NAME");
44+
private static final String DB_NAME = System.getenv("POSTGRES_DB");
45+
private static final String DB_USER = System.getenv("POSTGRES_USER");
46+
private static final String DB_PASSWORD = System.getenv("POSTGRES_PASS");
47+
private static final ImmutableList<String> requiredEnvVars =
48+
ImmutableList.of("POSTGRES_USER", "POSTGRES_PASS", "POSTGRES_DB", "POSTGRES_CONNECTION_NAME");
49+
@Rule public Timeout globalTimeout = new Timeout(80, TimeUnit.SECONDS);
50+
51+
private HikariDataSource connectionPool;
52+
53+
@BeforeClass
54+
public static void checkEnvVars() {
55+
// Check that required env vars are set
56+
requiredEnvVars.forEach(
57+
(varName) ->
58+
assertWithMessage(
59+
String.format(
60+
"Environment variable '%s' must be set to perform these tests.", varName))
61+
.that(System.getenv(varName))
62+
.isNotEmpty());
63+
}
64+
65+
@Before
66+
public void setUpPool() throws SQLException {
67+
// Set up URL parameters
68+
String jdbcURL = String.format("jdbc:postgresql://db.example.com/%s", DB_NAME);
69+
Properties connProps = new Properties();
70+
connProps.setProperty("user", DB_USER);
71+
connProps.setProperty("password", DB_PASSWORD);
72+
connProps.setProperty("socketFactory", "com.google.cloud.sql.postgres.SocketFactory");
73+
connProps.setProperty("cloudSqlNamedConnector", "resolver-test");
74+
75+
ConnectorRegistry.register(
76+
"resolver-test",
77+
new ConnectorConfig.Builder()
78+
.withInstanceNameResolver((n) -> "db.example.com".equals(n) ? CONNECTION_NAME : null)
79+
.build());
80+
81+
// Initialize connection pool
82+
HikariConfig config = new HikariConfig();
83+
config.setJdbcUrl(jdbcURL);
84+
config.setDataSourceProperties(connProps);
85+
config.setConnectionTimeout(10000); // 10s
86+
87+
this.connectionPool = new HikariDataSource(config);
88+
}
89+
90+
@Test
91+
public void pooledConnectionTest() throws SQLException {
92+
93+
List<Timestamp> rows = new ArrayList<>();
94+
try (Connection conn = connectionPool.getConnection()) {
95+
try (PreparedStatement selectStmt = conn.prepareStatement("SELECT NOW() as TS")) {
96+
ResultSet rs = selectStmt.executeQuery();
97+
while (rs.next()) {
98+
rows.add(rs.getTimestamp("TS"));
99+
}
100+
}
101+
}
102+
assertThat(rows.size()).isEqualTo(1);
103+
}
104+
}

jdbc/postgres/src/test/java/com/google/cloud/sql/postgres/JdbcPostgresIntegrationTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public static void checkEnvVars() {
6363
@Before
6464
public void setUpPool() throws SQLException {
6565
// Set up URL parameters
66-
String jdbcURL = String.format("jdbc:postgresql:///%s", DB_NAME);
66+
String jdbcURL = String.format("jdbc:postgresql://db.example.com/%s", DB_NAME);
6767
Properties connProps = new Properties();
6868
connProps.setProperty("user", DB_USER);
6969
connProps.setProperty("password", DB_PASSWORD);

0 commit comments

Comments
 (0)