Skip to content

Commit 3a572af

Browse files
MariamalmesferMariam-Almesfer
authored andcommitted
Add mixed-case support for BlackHole connector
1 parent e0d3305 commit 3a572af

File tree

6 files changed

+258
-14
lines changed

6 files changed

+258
-14
lines changed

presto-blackhole/pom.xml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,21 @@
2323
<artifactId>guava</artifactId>
2424
</dependency>
2525

26+
<dependency>
27+
<groupId>com.facebook.airlift</groupId>
28+
<artifactId>configuration</artifactId>
29+
</dependency>
30+
31+
<dependency>
32+
<groupId>com.facebook.airlift</groupId>
33+
<artifactId>json</artifactId>
34+
</dependency>
35+
36+
<dependency>
37+
<groupId>com.facebook.airlift</groupId>
38+
<artifactId>bootstrap</artifactId>
39+
</dependency>
40+
2641
<dependency>
2742
<groupId>com.facebook.airlift</groupId>
2843
<artifactId>concurrent</artifactId>
@@ -92,9 +107,8 @@
92107
</dependency>
93108

94109
<dependency>
95-
<groupId>com.facebook.airlift</groupId>
96-
<artifactId>log-manager</artifactId>
97-
<scope>test</scope>
110+
<groupId>com.google.inject</groupId>
111+
<artifactId>guice</artifactId>
98112
</dependency>
99113

100114
<dependency>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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.blackhole;
15+
16+
import com.facebook.airlift.configuration.Config;
17+
import com.facebook.airlift.configuration.ConfigDescription;
18+
19+
public class BlackHoleClientConfig
20+
{
21+
private boolean caseSensitiveNameMatchingEnabled;
22+
23+
public boolean isCaseSensitiveNameMatching()
24+
{
25+
return caseSensitiveNameMatchingEnabled;
26+
}
27+
28+
@Config("case-sensitive-name-matching")
29+
@ConfigDescription("Enable case-sensitive matching of schema, table names across the connector. " +
30+
"When disabled, names are matched case-insensitively using lowercase normalization.")
31+
public BlackHoleClientConfig setCaseSensitiveNameMatching(boolean caseSensitiveNameMatchingEnabled)
32+
{
33+
this.caseSensitiveNameMatchingEnabled = caseSensitiveNameMatchingEnabled;
34+
return this;
35+
}
36+
}

presto-blackhole/src/main/java/com/facebook/presto/plugin/blackhole/BlackHoleConnector.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public class BlackHoleConnector
5555
private final BlackHoleNodePartitioningProvider partitioningProvider;
5656
private final TypeManager typeManager;
5757
private final ExecutorService executorService;
58+
private final boolean caseSensitiveNameMatching;
5859

5960
public BlackHoleConnector(
6061
BlackHoleMetadata metadata,
@@ -63,7 +64,8 @@ public BlackHoleConnector(
6364
BlackHolePageSinkProvider pageSinkProvider,
6465
BlackHoleNodePartitioningProvider partitioningProvider,
6566
TypeManager typeManager,
66-
ExecutorService executorService)
67+
ExecutorService executorService,
68+
BlackHoleClientConfig config)
6769
{
6870
this.metadata = metadata;
6971
this.splitManager = splitManager;
@@ -72,6 +74,12 @@ public BlackHoleConnector(
7274
this.partitioningProvider = partitioningProvider;
7375
this.typeManager = typeManager;
7476
this.executorService = executorService;
77+
this.caseSensitiveNameMatching = config.isCaseSensitiveNameMatching();
78+
}
79+
80+
private String normalizeColumnName(String name)
81+
{
82+
return caseSensitiveNameMatching ? name : name.toLowerCase(ENGLISH);
7583
}
7684

7785
@Override
@@ -143,7 +151,7 @@ public List<PropertyMetadata<?>> getTableProperties()
143151
ImmutableList.of(),
144152
false,
145153
value -> ImmutableList.copyOf(((List<String>) value).stream()
146-
.map(name -> name.toLowerCase(ENGLISH))
154+
.map(this::normalizeColumnName)
147155
.collect(toList())),
148156
List.class::cast),
149157
new PropertyMetadata<>(

presto-blackhole/src/main/java/com/facebook/presto/plugin/blackhole/BlackHoleConnectorFactory.java

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,19 @@
1313
*/
1414
package com.facebook.presto.plugin.blackhole;
1515

16+
import com.facebook.airlift.bootstrap.Bootstrap;
17+
import com.facebook.airlift.json.JsonModule;
1618
import com.facebook.presto.spi.ConnectorHandleResolver;
1719
import com.facebook.presto.spi.connector.Connector;
1820
import com.facebook.presto.spi.connector.ConnectorContext;
1921
import com.facebook.presto.spi.connector.ConnectorFactory;
2022
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
23+
import com.google.inject.Injector;
2124

2225
import java.util.Map;
2326

2427
import static com.facebook.airlift.concurrent.Threads.daemonThreadsNamed;
28+
import static com.facebook.airlift.configuration.ConfigBinder.configBinder;
2529
import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator;
2630
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
2731

@@ -43,14 +47,33 @@ public ConnectorHandleResolver getHandleResolver()
4347
@Override
4448
public Connector create(String catalogName, Map<String, String> requiredConfig, ConnectorContext context)
4549
{
46-
ListeningScheduledExecutorService executorService = listeningDecorator(newSingleThreadScheduledExecutor(daemonThreadsNamed("blackhole")));
47-
return new BlackHoleConnector(
48-
new BlackHoleMetadata(),
49-
new BlackHoleSplitManager(),
50-
new BlackHolePageSourceProvider(executorService),
51-
new BlackHolePageSinkProvider(executorService),
52-
new BlackHoleNodePartitioningProvider(context.getNodeManager()),
53-
context.getTypeManager(),
54-
executorService);
50+
try {
51+
Bootstrap app = new Bootstrap(
52+
new JsonModule(),
53+
binder -> {
54+
configBinder(binder).bindConfig(BlackHoleClientConfig.class);
55+
});
56+
57+
Injector injector = app
58+
.doNotInitializeLogging()
59+
.setRequiredConfigurationProperties(requiredConfig)
60+
.initialize();
61+
62+
BlackHoleClientConfig config = injector.getInstance(BlackHoleClientConfig.class);
63+
64+
ListeningScheduledExecutorService executorService = listeningDecorator(newSingleThreadScheduledExecutor(daemonThreadsNamed("blackhole")));
65+
return new BlackHoleConnector(
66+
new BlackHoleMetadata(),
67+
new BlackHoleSplitManager(),
68+
new BlackHolePageSourceProvider(executorService),
69+
new BlackHolePageSinkProvider(executorService),
70+
new BlackHoleNodePartitioningProvider(context.getNodeManager()),
71+
context.getTypeManager(),
72+
executorService,
73+
config);
74+
}
75+
catch (Exception e) {
76+
throw new RuntimeException(e);
77+
}
5578
}
5679
}

presto-blackhole/src/main/java/com/facebook/presto/plugin/blackhole/BlackHoleMetadata.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
import static com.facebook.presto.spi.StandardErrorCode.INVALID_TABLE_PROPERTY;
5757
import static com.google.common.collect.ImmutableList.toImmutableList;
5858
import static java.lang.String.format;
59+
import static java.util.Locale.ENGLISH;
60+
import static java.util.Objects.requireNonNull;
5961
import static java.util.stream.Collectors.toList;
6062
import static java.util.stream.Collectors.toMap;
6163
import static java.util.stream.Collectors.toSet;
@@ -67,10 +69,23 @@ public class BlackHoleMetadata
6769

6870
private final List<String> schemas = new ArrayList<>();
6971
private final Map<SchemaTableName, BlackHoleTableHandle> tables = new ConcurrentHashMap<>();
72+
private boolean caseSensitiveNameMatchingEnabled;
7073

7174
public BlackHoleMetadata()
75+
{
76+
this(new BlackHoleClientConfig());
77+
}
78+
79+
public BlackHoleMetadata(BlackHoleClientConfig config)
7280
{
7381
schemas.add(SCHEMA_NAME);
82+
this.caseSensitiveNameMatchingEnabled = requireNonNull(config, "config is null").isCaseSensitiveNameMatching();
83+
}
84+
85+
@Override
86+
public String normalizeIdentifier(ConnectorSession session, String identifier)
87+
{
88+
return caseSensitiveNameMatchingEnabled ? identifier : identifier.toLowerCase(ENGLISH);
7489
}
7590

7691
@Override
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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.blackhole;
15+
16+
import com.facebook.presto.spi.ConnectorSession;
17+
import com.facebook.presto.testing.TestingConnectorSession;
18+
import org.testng.annotations.Test;
19+
20+
import static org.testng.Assert.assertEquals;
21+
import static org.testng.Assert.assertFalse;
22+
import static org.testng.Assert.assertTrue;
23+
24+
public class TestBlackHoleCaseSensitivity
25+
{
26+
private static final ConnectorSession SESSION = TestingConnectorSession.SESSION;
27+
28+
@Test
29+
public void testBlackHoleClientConfigDefaults()
30+
{
31+
BlackHoleClientConfig config = new BlackHoleClientConfig();
32+
assertFalse(config.isCaseSensitiveNameMatching(),
33+
"Default should be case-insensitive");
34+
}
35+
36+
@Test
37+
public void testBlackHoleClientConfigCaseSensitive()
38+
{
39+
BlackHoleClientConfig config = new BlackHoleClientConfig()
40+
.setCaseSensitiveNameMatching(true);
41+
42+
assertTrue(config.isCaseSensitiveNameMatching(),
43+
"Should be case-sensitive when enabled");
44+
}
45+
46+
@Test
47+
public void testBlackHoleClientConfigCaseInsensitive()
48+
{
49+
BlackHoleClientConfig config = new BlackHoleClientConfig()
50+
.setCaseSensitiveNameMatching(false);
51+
52+
assertFalse(config.isCaseSensitiveNameMatching(),
53+
"Should be case-insensitive when disabled");
54+
}
55+
56+
@Test
57+
public void testBlackHoleMetadataNormalizeIdentifierCaseSensitive()
58+
{
59+
BlackHoleClientConfig config = new BlackHoleClientConfig()
60+
.setCaseSensitiveNameMatching(true);
61+
BlackHoleMetadata metadata = new BlackHoleMetadata(config);
62+
63+
// Case-sensitive: identifiers should remain unchanged
64+
assertEquals(metadata.normalizeIdentifier(SESSION, "MyTable"), "MyTable");
65+
assertEquals(metadata.normalizeIdentifier(SESSION, "myTable"), "myTable");
66+
assertEquals(metadata.normalizeIdentifier(SESSION, "MYTABLE"), "MYTABLE");
67+
assertEquals(metadata.normalizeIdentifier(SESSION, "MySchema"), "MySchema");
68+
assertEquals(metadata.normalizeIdentifier(SESSION, "Test_Table"), "Test_Table");
69+
}
70+
71+
@Test
72+
public void testBlackHoleMetadataNormalizeIdentifierCaseInsensitive()
73+
{
74+
BlackHoleClientConfig config = new BlackHoleClientConfig()
75+
.setCaseSensitiveNameMatching(false);
76+
BlackHoleMetadata metadata = new BlackHoleMetadata(config);
77+
78+
// Case-insensitive: identifiers should be lowercased
79+
assertEquals(metadata.normalizeIdentifier(SESSION, "MyTable"), "mytable");
80+
assertEquals(metadata.normalizeIdentifier(SESSION, "myTable"), "mytable");
81+
assertEquals(metadata.normalizeIdentifier(SESSION, "MYTABLE"), "mytable");
82+
assertEquals(metadata.normalizeIdentifier(SESSION, "MySchema"), "myschema");
83+
assertEquals(metadata.normalizeIdentifier(SESSION, "Test_Table"), "test_table");
84+
}
85+
86+
@Test
87+
public void testBlackHoleMetadataDefaultBehavior()
88+
{
89+
// Default metadata should be case-insensitive
90+
BlackHoleMetadata metadata = new BlackHoleMetadata();
91+
92+
assertEquals(metadata.normalizeIdentifier(SESSION, "MyTable"), "mytable");
93+
assertEquals(metadata.normalizeIdentifier(SESSION, "TestSchema"), "testschema");
94+
assertEquals(metadata.normalizeIdentifier(SESSION, "UPPERCASE"), "uppercase");
95+
}
96+
97+
@Test
98+
public void testConfigIntegrationWithMetadata()
99+
{
100+
// Test that config changes are properly reflected in metadata behavior
101+
102+
// Case-sensitive config
103+
BlackHoleClientConfig caseSensitiveConfig = new BlackHoleClientConfig()
104+
.setCaseSensitiveNameMatching(true);
105+
BlackHoleMetadata caseSensitiveMetadata = new BlackHoleMetadata(caseSensitiveConfig);
106+
107+
// Case-insensitive config
108+
BlackHoleClientConfig caseInsensitiveConfig = new BlackHoleClientConfig()
109+
.setCaseSensitiveNameMatching(false);
110+
BlackHoleMetadata caseInsensitiveMetadata = new BlackHoleMetadata(caseInsensitiveConfig);
111+
112+
String testIdentifier = "MyTestTable";
113+
114+
assertEquals(caseSensitiveMetadata.normalizeIdentifier(SESSION, testIdentifier), "MyTestTable");
115+
assertEquals(caseInsensitiveMetadata.normalizeIdentifier(SESSION, testIdentifier), "mytesttable");
116+
}
117+
118+
@Test
119+
public void testMultipleConfigInstances()
120+
{
121+
// Test that multiple config instances work independently
122+
BlackHoleClientConfig config1 = new BlackHoleClientConfig().setCaseSensitiveNameMatching(true);
123+
BlackHoleClientConfig config2 = new BlackHoleClientConfig().setCaseSensitiveNameMatching(false);
124+
125+
assertTrue(config1.isCaseSensitiveNameMatching());
126+
assertFalse(config2.isCaseSensitiveNameMatching());
127+
128+
// Test that they don't interfere with each other
129+
BlackHoleMetadata metadata1 = new BlackHoleMetadata(config1);
130+
BlackHoleMetadata metadata2 = new BlackHoleMetadata(config2);
131+
132+
assertEquals(metadata1.normalizeIdentifier(SESSION, "TestCase"), "TestCase");
133+
assertEquals(metadata2.normalizeIdentifier(SESSION, "TestCase"), "testcase");
134+
}
135+
136+
@Test
137+
public void testConfigChaining()
138+
{
139+
// Test that config setter returns the config instance for chaining
140+
BlackHoleClientConfig config = new BlackHoleClientConfig()
141+
.setCaseSensitiveNameMatching(true)
142+
.setCaseSensitiveNameMatching(false)
143+
.setCaseSensitiveNameMatching(true);
144+
145+
assertTrue(config.isCaseSensitiveNameMatching(),
146+
"Final value should be true after chaining");
147+
}
148+
}

0 commit comments

Comments
 (0)