Skip to content

Commit 76cb96e

Browse files
Mysql support for mixed case
1 parent 2bd2c18 commit 76cb96e

File tree

11 files changed

+210
-27
lines changed

11 files changed

+210
-27
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ public class BaseJdbcClient
115115
protected final boolean caseInsensitiveNameMatching;
116116
protected final Cache<JdbcIdentity, Map<String, String>> remoteSchemaNames;
117117
protected final Cache<RemoteTableNameCacheKey, Map<String, String>> remoteTableNames;
118+
protected final boolean isMixedCaseSupportEnabled;
118119

119120
public BaseJdbcClient(JdbcConnectorId connectorId, BaseJdbcConfig config, String identifierQuote, ConnectionFactory connectionFactory)
120121
{
@@ -128,6 +129,7 @@ public BaseJdbcClient(JdbcConnectorId connectorId, BaseJdbcConfig config, String
128129
.expireAfterWrite(config.getCaseInsensitiveNameMatchingCacheTtl().toMillis(), MILLISECONDS);
129130
this.remoteSchemaNames = remoteNamesCacheBuilder.build();
130131
this.remoteTableNames = remoteNamesCacheBuilder.build();
132+
this.isMixedCaseSupportEnabled = config.isMixedCaseSupportEnabled();
131133
}
132134

133135
@PreDestroy

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class BaseJdbcConfig
3232
private String passwordCredentialName;
3333
private boolean caseInsensitiveNameMatching;
3434
private Duration caseInsensitiveNameMatchingCacheTtl = new Duration(1, MINUTES);
35+
private boolean mixedCaseSupportEnabled;
3536

3637
@NotNull
3738
public String getConnectionUrl()
@@ -124,4 +125,16 @@ public BaseJdbcConfig setCaseInsensitiveNameMatchingCacheTtl(Duration caseInsens
124125
this.caseInsensitiveNameMatchingCacheTtl = caseInsensitiveNameMatchingCacheTtl;
125126
return this;
126127
}
128+
129+
public boolean isMixedCaseSupportEnabled()
130+
{
131+
return mixedCaseSupportEnabled;
132+
}
133+
134+
@Config("mixed-case-support")
135+
public BaseJdbcConfig setMixedCaseSupportEnabled(boolean mixedCaseSupportEnabled)
136+
{
137+
this.mixedCaseSupportEnabled = mixedCaseSupportEnabled;
138+
return this;
139+
}
127140
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.facebook.presto.common.type.Type;
1717
import com.facebook.presto.spi.ColumnHandle;
1818
import com.facebook.presto.spi.ColumnMetadata;
19+
import com.facebook.presto.spi.ConnectorSession;
1920
import com.fasterxml.jackson.annotation.JsonCreator;
2021
import com.fasterxml.jackson.annotation.JsonProperty;
2122

@@ -88,10 +89,10 @@ public Optional<String> getComment()
8889
return comment;
8990
}
9091

91-
public ColumnMetadata getColumnMetadata()
92+
public ColumnMetadata getColumnMetadata(ConnectorSession session, JdbcClient jdbcClient)
9293
{
93-
return ColumnMetadata.builder()
94-
.setName(columnName)
94+
return JdbcColumnMetadata.jdbcBuilder()
95+
.setName(jdbcClient.normalizeIdentifier(session, columnName))
9596
.setType(columnType)
9697
.setNullable(nullable)
9798
.setComment(comment.orElse(null))
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
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.presto.common.type.Type;
17+
import com.facebook.presto.spi.ColumnMetadata;
18+
19+
import java.util.Map;
20+
import java.util.Objects;
21+
import java.util.Optional;
22+
23+
import static java.util.Collections.emptyMap;
24+
import static java.util.Objects.requireNonNull;
25+
26+
public class JdbcColumnMetadata
27+
extends ColumnMetadata
28+
{
29+
private final String originalName;
30+
31+
public JdbcColumnMetadata(String name, Type type)
32+
{
33+
this(name, type, true, null, null, false, emptyMap());
34+
}
35+
36+
/**
37+
* @deprecated Use {@link #builder()} instead.
38+
*/
39+
@Deprecated
40+
public JdbcColumnMetadata(String name, Type type, boolean nullable, String comment, String extraInfo, boolean hidden, Map<String, Object> properties)
41+
{
42+
super(ColumnMetadata.builder()
43+
.setName(name)
44+
.setType(type)
45+
.setNullable(nullable)
46+
.setComment(comment)
47+
.setExtraInfo(extraInfo)
48+
.setHidden(hidden)
49+
.setProperties(properties));
50+
this.originalName = requireNonNull(name, "name cannot be null");
51+
}
52+
53+
protected JdbcColumnMetadata(Builder builder)
54+
{
55+
this(builder.name, builder.type, builder.nullable, builder.comment.orElse(null), builder.extraInfo.orElse(null), builder.hidden, builder.properties);
56+
}
57+
58+
public String getName()
59+
{
60+
return originalName;
61+
}
62+
63+
@Override
64+
public boolean equals(Object obj)
65+
{
66+
if (this == obj) {
67+
return true;
68+
}
69+
if (obj == null || getClass() != obj.getClass()) {
70+
return false;
71+
}
72+
JdbcColumnMetadata other = (JdbcColumnMetadata) obj;
73+
return Objects.equals(this.originalName, other.originalName) &&
74+
Objects.equals(this.getName(), other.getName()) &&
75+
Objects.equals(this.getType(), other.getType()) &&
76+
Objects.equals(this.getComment(), other.getComment()) &&
77+
Objects.equals(this.isHidden(), other.isHidden()) &&
78+
Objects.equals(this.isNullable(), other.isNullable()) &&
79+
Objects.equals(this.getProperties(), other.getProperties());
80+
}
81+
82+
@Override
83+
public int hashCode()
84+
{
85+
return Objects.hash(originalName, getType(), getComment(), isHidden(), isNullable(), getProperties());
86+
}
87+
88+
public static Builder jdbcBuilder()
89+
{
90+
return new Builder();
91+
}
92+
93+
public static class Builder
94+
{
95+
private String name;
96+
private Type type;
97+
private boolean nullable = true;
98+
private Optional<String> comment = Optional.empty();
99+
private Optional<String> extraInfo = Optional.empty();
100+
private boolean hidden;
101+
private Map<String, Object> properties = emptyMap();
102+
103+
private Builder() {}
104+
105+
public Builder setName(String name)
106+
{
107+
this.name = requireNonNull(name, "name is null");
108+
return this;
109+
}
110+
111+
public Builder setType(Type type)
112+
{
113+
this.type = requireNonNull(type, "type is null");
114+
return this;
115+
}
116+
117+
public Builder setNullable(boolean nullable)
118+
{
119+
this.nullable = nullable;
120+
return this;
121+
}
122+
123+
public Builder setComment(String comment)
124+
{
125+
this.comment = Optional.ofNullable(comment);
126+
return this;
127+
}
128+
129+
public Builder setExtraInfo(String extraInfo)
130+
{
131+
this.extraInfo = Optional.ofNullable(extraInfo);
132+
return this;
133+
}
134+
135+
public Builder setHidden(boolean hidden)
136+
{
137+
this.hidden = hidden;
138+
return this;
139+
}
140+
141+
public Builder setProperties(Map<String, Object> properties)
142+
{
143+
this.properties = requireNonNull(properties, "properties is null");
144+
return this;
145+
}
146+
147+
public JdbcColumnMetadata build()
148+
{
149+
return new JdbcColumnMetadata(this);
150+
}
151+
}
152+
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public ConnectorTableMetadata getTableMetadata(ConnectorSession session, Connect
104104

105105
ImmutableList.Builder<ColumnMetadata> columnMetadata = ImmutableList.builder();
106106
for (JdbcColumnHandle column : jdbcMetadataCache.getColumns(session, handle)) {
107-
columnMetadata.add(column.getColumnMetadata());
107+
columnMetadata.add(column.getColumnMetadata(session, jdbcClient));
108108
}
109109
return new ConnectorTableMetadata(handle.getSchemaTableName(), columnMetadata.build());
110110
}
@@ -122,7 +122,7 @@ public Map<String, ColumnHandle> getColumnHandles(ConnectorSession session, Conn
122122

123123
ImmutableMap.Builder<String, ColumnHandle> columnHandles = ImmutableMap.builder();
124124
for (JdbcColumnHandle column : jdbcMetadataCache.getColumns(session, jdbcTableHandle)) {
125-
columnHandles.put(column.getColumnMetadata().getName(), column);
125+
columnHandles.put(column.getColumnMetadata(session, jdbcClient).getName(), column);
126126
}
127127
return columnHandles.build();
128128
}
@@ -156,7 +156,7 @@ public Map<SchemaTableName, List<ColumnMetadata>> listTableColumns(ConnectorSess
156156
@Override
157157
public ColumnMetadata getColumnMetadata(ConnectorSession session, ConnectorTableHandle tableHandle, ColumnHandle columnHandle)
158158
{
159-
return ((JdbcColumnHandle) columnHandle).getColumnMetadata();
159+
return ((JdbcColumnHandle) columnHandle).getColumnMetadata(session, jdbcClient);
160160
}
161161

162162
@Override

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ public void testDefaults()
3535
.setUserCredentialName(null)
3636
.setPasswordCredentialName(null)
3737
.setCaseInsensitiveNameMatching(false)
38-
.setCaseInsensitiveNameMatchingCacheTtl(new Duration(1, MINUTES)));
38+
.setCaseInsensitiveNameMatchingCacheTtl(new Duration(1, MINUTES))
39+
.setMixedCaseSupportEnabled(false));
3940
}
4041

4142
@Test
@@ -49,6 +50,7 @@ public void testExplicitPropertyMappings()
4950
.put("password-credential-name", "bar")
5051
.put("case-insensitive-name-matching", "true")
5152
.put("case-insensitive-name-matching.cache-ttl", "1s")
53+
.put("mixed-case-support", "true")
5254
.build();
5355

5456
BaseJdbcConfig expected = new BaseJdbcConfig()
@@ -58,7 +60,8 @@ public void testExplicitPropertyMappings()
5860
.setUserCredentialName("foo")
5961
.setPasswordCredentialName("bar")
6062
.setCaseInsensitiveNameMatching(true)
61-
.setCaseInsensitiveNameMatchingCacheTtl(new Duration(1, SECONDS));
63+
.setCaseInsensitiveNameMatchingCacheTtl(new Duration(1, SECONDS))
64+
.setMixedCaseSupportEnabled(true);
6265

6366
ConfigAssertions.assertFullMapping(properties, expected);
6467
}

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

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import static com.facebook.presto.spi.StandardErrorCode.PERMISSION_DENIED;
4444
import static com.facebook.presto.testing.TestingConnectorSession.SESSION;
4545
import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator;
46+
import static java.util.Collections.emptyMap;
4647
import static java.util.concurrent.Executors.newCachedThreadPool;
4748
import static org.assertj.core.api.Assertions.assertThat;
4849
import static org.assertj.core.data.MapEntry.entry;
@@ -124,17 +125,17 @@ public void getTableMetadata()
124125
ConnectorTableMetadata tableMetadata = metadata.getTableMetadata(SESSION, tableHandle);
125126
assertEquals(tableMetadata.getTable(), new SchemaTableName("example", "numbers"));
126127
assertEquals(tableMetadata.getColumns(), ImmutableList.of(
127-
ColumnMetadata.builder().setName("text").setType(VARCHAR).setNullable(false).build(),
128-
ColumnMetadata.builder().setName("text_short").setType(createVarcharType(32)).build(),
129-
ColumnMetadata.builder().setName("value").setType(BIGINT).build()));
128+
new JdbcColumnMetadata("text", VARCHAR, false, null, null, false, emptyMap()), // primary key is not null in H2
129+
new JdbcColumnMetadata("text_short", createVarcharType(32)),
130+
new JdbcColumnMetadata("value", BIGINT)));
130131

131132
// escaping name patterns
132133
JdbcTableHandle specialTableHandle = metadata.getTableHandle(SESSION, new SchemaTableName("exa_ple", "num_ers"));
133134
ConnectorTableMetadata specialTableMetadata = metadata.getTableMetadata(SESSION, specialTableHandle);
134135
assertEquals(specialTableMetadata.getTable(), new SchemaTableName("exa_ple", "num_ers"));
135136
assertEquals(specialTableMetadata.getColumns(), ImmutableList.of(
136-
ColumnMetadata.builder().setName("te_t").setType(VARCHAR).setNullable(false).build(),
137-
ColumnMetadata.builder().setName("va%ue").setType(BIGINT).build()));
137+
new JdbcColumnMetadata("te_t", VARCHAR, false, null, null, false, emptyMap()), // primary key is not null in H2
138+
new JdbcColumnMetadata("va%ue", BIGINT)));
138139

139140
// unknown tables should produce null
140141
unknownTableMetadata(new JdbcTableHandle(CONNECTOR_ID, new SchemaTableName("u", "numbers"), null, "unknown", "unknown"));
@@ -147,13 +148,13 @@ public void testListTableColumns()
147148
{
148149
SchemaTableName tpchOrders = new SchemaTableName("tpch", "orders");
149150
ImmutableList<ColumnMetadata> tpchOrdersColumnMetadata = ImmutableList.of(
150-
ColumnMetadata.builder().setName("orderkey").setType(BIGINT).setNullable(false).build(),
151-
ColumnMetadata.builder().setName("custkey").setType(BIGINT).setNullable(true).build());
151+
JdbcColumnMetadata.jdbcBuilder().setName("orderkey").setType(BIGINT).setNullable(false).build(),
152+
JdbcColumnMetadata.jdbcBuilder().setName("custkey").setType(BIGINT).setNullable(true).build());
152153

153154
SchemaTableName tpchLineItem = new SchemaTableName("tpch", "lineitem");
154155
ImmutableList<ColumnMetadata> tpchLineItemColumnMetadata = ImmutableList.of(
155-
ColumnMetadata.builder().setName("orderkey").setType(BIGINT).setNullable(false).build(),
156-
ColumnMetadata.builder().setName("partkey").setType(BIGINT).setNullable(true).build());
156+
JdbcColumnMetadata.jdbcBuilder().setName("orderkey").setType(BIGINT).setNullable(false).build(),
157+
JdbcColumnMetadata.jdbcBuilder().setName("partkey").setType(BIGINT).setNullable(true).build());
157158

158159
//List columns for a given schema and table
159160
Map<SchemaTableName, List<ColumnMetadata>> tpchOrdersColumns = metadata.listTableColumns(SESSION, new SchemaTablePrefix("tpch", "orders"));
@@ -213,41 +214,41 @@ public void getColumnMetadata()
213214
{
214215
assertEquals(
215216
metadata.getColumnMetadata(SESSION, tableHandle, new JdbcColumnHandle(CONNECTOR_ID, "text", JDBC_VARCHAR, VARCHAR, true, Optional.empty())),
216-
ColumnMetadata.builder().setName("text").setType(VARCHAR).build());
217+
new JdbcColumnMetadata("text", VARCHAR));
217218
}
218219

219220
@Test
220221
public void testCreateAndAlterTable()
221222
{
222223
SchemaTableName table = new SchemaTableName("example", "foo");
223-
metadata.createTable(SESSION, new ConnectorTableMetadata(table, ImmutableList.of(ColumnMetadata.builder().setName("text").setType(VARCHAR).build())), false);
224+
metadata.createTable(SESSION, new ConnectorTableMetadata(table, ImmutableList.of(new JdbcColumnMetadata("text", VARCHAR))), false);
224225

225226
JdbcTableHandle handle = metadata.getTableHandle(SESSION, table);
226227

227228
ConnectorTableMetadata layout = metadata.getTableMetadata(SESSION, handle);
228229
assertEquals(layout.getTable(), table);
229230
assertEquals(layout.getColumns().size(), 1);
230-
assertEquals(layout.getColumns().get(0), ColumnMetadata.builder().setName("text").setType(VARCHAR).build());
231+
assertEquals(layout.getColumns().get(0), new JdbcColumnMetadata("text", VARCHAR));
231232

232-
metadata.addColumn(SESSION, handle, ColumnMetadata.builder().setName("x").setType(VARCHAR).build());
233+
metadata.addColumn(SESSION, handle, new JdbcColumnMetadata("x", VARCHAR));
233234
layout = metadata.getTableMetadata(SESSION, handle);
234235
assertEquals(layout.getColumns().size(), 2);
235-
assertEquals(layout.getColumns().get(0), ColumnMetadata.builder().setName("text").setType(VARCHAR).build());
236-
assertEquals(layout.getColumns().get(1), ColumnMetadata.builder().setName("x").setType(VARCHAR).build());
236+
assertEquals(layout.getColumns().get(0), new JdbcColumnMetadata("text", VARCHAR));
237+
assertEquals(layout.getColumns().get(1), new JdbcColumnMetadata("x", VARCHAR));
237238

238239
JdbcColumnHandle columnHandle = new JdbcColumnHandle(CONNECTOR_ID, "x", JDBC_VARCHAR, VARCHAR, true, Optional.empty());
239240
metadata.dropColumn(SESSION, handle, columnHandle);
240241
layout = metadata.getTableMetadata(SESSION, handle);
241242
assertEquals(layout.getColumns().size(), 1);
242-
assertEquals(layout.getColumns().get(0), ColumnMetadata.builder().setName("text").setType(VARCHAR).build());
243+
assertEquals(layout.getColumns().get(0), new JdbcColumnMetadata("text", VARCHAR));
243244

244245
SchemaTableName newTableName = new SchemaTableName("example", "bar");
245246
metadata.renameTable(SESSION, handle, newTableName);
246247
handle = metadata.getTableHandle(SESSION, newTableName);
247248
layout = metadata.getTableMetadata(SESSION, handle);
248249
assertEquals(layout.getTable(), newTableName);
249250
assertEquals(layout.getColumns().size(), 1);
250-
assertEquals(layout.getColumns().get(0), ColumnMetadata.builder().setName("text").setType(VARCHAR).build());
251+
assertEquals(layout.getColumns().get(0), new JdbcColumnMetadata("text", VARCHAR));
251252
}
252253

253254
@Test

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public Map<String, JdbcColumnHandle> getColumnHandles(String schemaName, String
117117

118118
ImmutableMap.Builder<String, JdbcColumnHandle> columnHandles = ImmutableMap.builder();
119119
for (JdbcColumnHandle column : columns) {
120-
columnHandles.put(column.getColumnMetadata().getName(), column);
120+
columnHandles.put(column.getColumnMetadata(session, jdbcClient).getName(), column);
121121
}
122122
return columnHandles.build();
123123
}

presto-mysql/src/main/java/com/facebook/presto/plugin/mysql/MySqlClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,6 @@ protected void renameTable(JdbcIdentity identity, String catalogName, SchemaTabl
240240
@Override
241241
public String normalizeIdentifier(ConnectorSession session, String identifier)
242242
{
243-
return identifier;
243+
return isMixedCaseSupportEnabled ? identifier : identifier.toLowerCase(ENGLISH);
244244
}
245245
}

presto-mysql/src/test/java/com/facebook/presto/plugin/mysql/MySqlQueryRunner.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public static QueryRunner createMySqlQueryRunner(String jdbcUrl, Map<String, Str
6969
connectorProperties = new HashMap<>(ImmutableMap.copyOf(connectorProperties));
7070
connectorProperties.putIfAbsent("connection-url", jdbcUrl);
7171
connectorProperties.putIfAbsent("allow-drop-table", "true");
72+
connectorProperties.putIfAbsent("mixed-case-support", "true"); // for testing enable mixed case by default for mysql
7273

7374
queryRunner.installPlugin(new MySqlPlugin());
7475
queryRunner.createCatalog("mysql", "mysql", connectorProperties);

0 commit comments

Comments
 (0)