Skip to content

Commit d95fca4

Browse files
pradeepvakarschlussel
authored andcommitted
Refactor JdbcMetadata to take in TableLocationProvider (#25582)
Summary: **Issue**: JDBC connectors were tightly coupled to `BaseJdbcConfig.connectionUrl` for table location information, making it difficult for connectors like XDB to use alternative connection management (e.g., SMC tier). **Solution**: Introduced `TableLocationProvider` interface to decouple table location logic from JDBC configuration: - Created `TableLocationProvider` interface for flexible table location resolution - Added `DefaultTableLocationProvider` that uses connection-url from `BaseJdbcConfig` (maintains backward compatibility) - Refactored `JdbcMetadata` and `JdbcMetadataFactory` to accept `TableLocationProvider` instead of `BaseJdbcConfig` This enables connectors to provide custom table location logic while maintaining full backward compatibility for existing JDBC connectors. Added test coverage. Pull Request resolved: #25582 Differential Revision: D78571326
1 parent 3f57283 commit d95fca4

File tree

12 files changed

+545
-8
lines changed

12 files changed

+545
-8
lines changed

presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/BaseJdbcConfig.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@
3333
import static java.util.Locale.ENGLISH;
3434
import static java.util.concurrent.TimeUnit.MINUTES;
3535

36+
/**
37+
* Base configuration class for JDBC connectors.
38+
*
39+
* This class is provided for convenience and contains common configuration properties
40+
* that many JDBC connectors may need. However, core JDBC functionality should not
41+
* depend on this class, as JDBC connectors may choose to use their own mechanisms
42+
* for connection management, authentication, and other configuration needs.
43+
*
44+
* Connectors are free to implement their own configuration classes and connection
45+
* strategies without extending or using this base configuration.
46+
*/
3647
public class BaseJdbcConfig
3748
{
3849
private String connectionUrl;
@@ -176,5 +187,9 @@ public void validateConfig()
176187
throw new ConfigurationException(ImmutableList.of(new Message("Only one of 'case-insensitive-name-matching=true' or 'case-sensitive-name-matching=true' can be set. " +
177188
"These options are mutually exclusive.")));
178189
}
190+
191+
if (connectionUrl == null) {
192+
throw new ConfigurationException(ImmutableList.of(new Message("connection-url is required but was not provided")));
193+
}
179194
}
180195
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package com.facebook.presto.plugin.jdbc;
15+
16+
import javax.inject.Inject;
17+
18+
import static java.util.Objects.requireNonNull;
19+
20+
/**
21+
* Default implementation of TableLocationProvider that uses the connection URL
22+
* from BaseJdbcConfig as the table location.
23+
*/
24+
public class DefaultTableLocationProvider
25+
implements TableLocationProvider
26+
{
27+
private final String connectionUrl;
28+
29+
@Inject
30+
public DefaultTableLocationProvider(BaseJdbcConfig baseJdbcConfig)
31+
{
32+
requireNonNull(baseJdbcConfig, "baseJdbcConfig is null");
33+
this.connectionUrl = requireNonNull(baseJdbcConfig.getConnectionUrl(), "connection-url is null");
34+
}
35+
36+
@Override
37+
public String getTableLocation()
38+
{
39+
return connectionUrl;
40+
}
41+
}

presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcMetadata.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@ public class JdbcMetadata
5858
private final String url;
5959
private final AtomicReference<Runnable> rollbackAction = new AtomicReference<>();
6060

61-
public JdbcMetadata(JdbcMetadataCache jdbcMetadataCache, JdbcClient jdbcClient, boolean allowDropTable, BaseJdbcConfig baseJdbcConfig)
61+
public JdbcMetadata(JdbcMetadataCache jdbcMetadataCache, JdbcClient jdbcClient, boolean allowDropTable, TableLocationProvider tableLocationProvider)
6262
{
6363
this.jdbcMetadataCache = requireNonNull(jdbcMetadataCache, "jdbcMetadataCache is null");
6464
this.jdbcClient = requireNonNull(jdbcClient, "client is null");
6565
this.allowDropTable = allowDropTable;
66-
this.url = baseJdbcConfig.getConnectionUrl();
66+
this.url = requireNonNull(tableLocationProvider, "tableLocationProvider is null").getTableLocation();
6767
}
6868

6969
@Override

presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcMetadataFactory.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,20 @@ public class JdbcMetadataFactory
2222
private final JdbcMetadataCache jdbcMetadataCache;
2323
private final JdbcClient jdbcClient;
2424
private final boolean allowDropTable;
25-
private final BaseJdbcConfig baseJdbcConfig;
25+
private final TableLocationProvider tableLocationProvider;
2626

2727
@Inject
28-
public JdbcMetadataFactory(JdbcMetadataCache jdbcMetadataCache, JdbcClient jdbcClient, JdbcMetadataConfig config, BaseJdbcConfig baseJdbcConfig)
28+
public JdbcMetadataFactory(JdbcMetadataCache jdbcMetadataCache, JdbcClient jdbcClient, JdbcMetadataConfig config, TableLocationProvider tableLocationProvider)
2929
{
3030
this.jdbcMetadataCache = requireNonNull(jdbcMetadataCache, "jdbcMetadataCache is null");
3131
this.jdbcClient = requireNonNull(jdbcClient, "jdbcClient is null");
3232
requireNonNull(config, "config is null");
3333
this.allowDropTable = config.isAllowDropTable();
34-
this.baseJdbcConfig = requireNonNull(baseJdbcConfig, "baseJdbcConfig is null");
34+
this.tableLocationProvider = requireNonNull(tableLocationProvider, "tableLocationProvider is null");
3535
}
3636

3737
public JdbcMetadata create()
3838
{
39-
return new JdbcMetadata(jdbcMetadataCache, jdbcClient, allowDropTable, baseJdbcConfig);
39+
return new JdbcMetadata(jdbcMetadataCache, jdbcClient, allowDropTable, tableLocationProvider);
4040
}
4141
}

presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcModule.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,7 @@ public void configure(Binder binder)
5454
newOptionalBinder(binder, JdbcSessionPropertiesProvider.class);
5555
binder.bind(JdbcConnector.class).in(Scopes.SINGLETON);
5656
configBinder(binder).bindConfig(JdbcMetadataConfig.class);
57+
configBinder(binder).bindConfig(BaseJdbcConfig.class);
58+
binder.bind(TableLocationProvider.class).to(DefaultTableLocationProvider.class).in(Scopes.SINGLETON);
5759
}
5860
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package com.facebook.presto.plugin.jdbc;
15+
16+
/**
17+
* Provides table location information for JDBC connectors.
18+
* Different implementations can provide location information based on
19+
* different sources (e.g., connection URL, service discovery, etc.)
20+
*/
21+
public interface TableLocationProvider
22+
{
23+
/**
24+
* Returns the location/URL for the table.
25+
* This could be a connection URL, service endpoint, or any other
26+
* location identifier relevant to the specific connector.
27+
*/
28+
String getTableLocation();
29+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package com.facebook.presto.plugin.jdbc;
15+
16+
import com.facebook.airlift.configuration.Config;
17+
18+
import javax.validation.constraints.NotNull;
19+
20+
/**
21+
* Simple JDBC configuration that doesn't extend BaseJdbcConfig.
22+
* This demonstrates that JDBC connectors can implement their own
23+
* configuration mechanisms without depending on BaseJdbcConfig.
24+
*/
25+
public class SimpleTestJdbcConfig
26+
{
27+
private String jdbcUrl;
28+
private String username;
29+
private String password;
30+
31+
@NotNull
32+
public String getJdbcUrl()
33+
{
34+
return jdbcUrl;
35+
}
36+
37+
@Config("jdbc-url")
38+
public SimpleTestJdbcConfig setJdbcUrl(String jdbcUrl)
39+
{
40+
this.jdbcUrl = jdbcUrl;
41+
return this;
42+
}
43+
44+
public String getUsername()
45+
{
46+
return username;
47+
}
48+
49+
@Config("username")
50+
public SimpleTestJdbcConfig setUsername(String username)
51+
{
52+
this.username = username;
53+
return this;
54+
}
55+
56+
public String getPassword()
57+
{
58+
return password;
59+
}
60+
61+
@Config("password")
62+
public SimpleTestJdbcConfig setPassword(String password)
63+
{
64+
this.password = password;
65+
return this;
66+
}
67+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package com.facebook.presto.plugin.jdbc;
15+
16+
import javax.inject.Inject;
17+
18+
import static java.util.Objects.requireNonNull;
19+
20+
/**
21+
* Simple TableLocationProvider implementation that doesn't depend on BaseJdbcConfig.
22+
* This demonstrates that TableLocationProvider implementations can use their own
23+
* configuration mechanisms without depending on BaseJdbcConfig.
24+
*/
25+
public class SimpleTestTableLocationProvider
26+
implements TableLocationProvider
27+
{
28+
private final String jdbcUrl;
29+
30+
@Inject
31+
public SimpleTestTableLocationProvider(SimpleTestJdbcConfig config)
32+
{
33+
requireNonNull(config, "config is null");
34+
this.jdbcUrl = config.getJdbcUrl();
35+
}
36+
37+
@Override
38+
public String getTableLocation()
39+
{
40+
return jdbcUrl;
41+
}
42+
}

presto-base-jdbc/src/test/java/com/facebook/presto/plugin/jdbc/TestBaseJdbcConfig.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@
1515

1616
import com.facebook.airlift.configuration.testing.ConfigAssertions;
1717
import com.google.common.collect.ImmutableMap;
18+
import com.google.inject.ConfigurationException;
1819
import io.airlift.units.Duration;
1920
import org.testng.annotations.Test;
2021

2122
import java.util.Map;
2223

2324
import static java.util.concurrent.TimeUnit.MINUTES;
2425
import static java.util.concurrent.TimeUnit.SECONDS;
26+
import static org.testng.Assert.assertEquals;
27+
import static org.testng.Assert.expectThrows;
2528

2629
public class TestBaseJdbcConfig
2730
{
@@ -68,4 +71,69 @@ public void testExplicitPropertyMappings()
6871

6972
ConfigAssertions.assertFullMapping(properties, expected);
7073
}
74+
75+
@Test
76+
public void testValidConfigValidation()
77+
{
78+
BaseJdbcConfig config = new BaseJdbcConfig();
79+
config.setConnectionUrl("jdbc:mysql://localhost:3306/test");
80+
81+
// Should not throw any exception
82+
config.validateConfig();
83+
84+
assertEquals(config.getConnectionUrl(), "jdbc:mysql://localhost:3306/test");
85+
}
86+
87+
@Test
88+
public void testNullConnectionUrlValidation()
89+
{
90+
BaseJdbcConfig config = new BaseJdbcConfig();
91+
// connectionUrl is null by default
92+
93+
ConfigurationException exception = expectThrows(
94+
ConfigurationException.class,
95+
config::validateConfig);
96+
assertEquals(exception.getErrorMessages().iterator().next().getMessage(),
97+
"connection-url is required but was not provided");
98+
}
99+
100+
@Test
101+
public void testMutuallyExclusiveNameMatchingOptions()
102+
{
103+
BaseJdbcConfig config = new BaseJdbcConfig();
104+
config.setConnectionUrl("jdbc:mysql://localhost:3306/test");
105+
config.setCaseInsensitiveNameMatching(true);
106+
config.setCaseSensitiveNameMatching(true);
107+
108+
ConfigurationException exception = expectThrows(
109+
ConfigurationException.class,
110+
config::validateConfig);
111+
assertEquals(exception.getErrorMessages().iterator().next().getMessage(),
112+
"Only one of 'case-insensitive-name-matching=true' or 'case-sensitive-name-matching=true' can be set. " +
113+
"These options are mutually exclusive.");
114+
}
115+
116+
@Test
117+
public void testCaseInsensitiveNameMatchingOnly()
118+
{
119+
BaseJdbcConfig config = new BaseJdbcConfig();
120+
config.setConnectionUrl("jdbc:mysql://localhost:3306/test");
121+
config.setCaseInsensitiveNameMatching(true);
122+
config.setCaseSensitiveNameMatching(false);
123+
124+
// Should not throw any exception
125+
config.validateConfig();
126+
}
127+
128+
@Test
129+
public void testCaseSensitiveNameMatchingOnly()
130+
{
131+
BaseJdbcConfig config = new BaseJdbcConfig();
132+
config.setConnectionUrl("jdbc:mysql://localhost:3306/test");
133+
config.setCaseInsensitiveNameMatching(false);
134+
config.setCaseSensitiveNameMatching(true);
135+
136+
// Should not throw any exception
137+
config.validateConfig();
138+
}
71139
}

0 commit comments

Comments
 (0)