Skip to content

Commit 63944ed

Browse files
Mysql support for mixed case
1 parent fac4148 commit 63944ed

File tree

11 files changed

+205
-38
lines changed

11 files changed

+205
-38
lines changed

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

+2
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

+13
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

+5-14
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,23 +89,13 @@ 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)
97-
.setComment(comment.orElse(null))
98-
.build();
99-
}
100-
101-
public ColumnMetadata getColumnMetadata()
102-
{
103-
return ColumnMetadata.builder()
104-
.setName(columnName)
105-
.setType(columnType)
106-
.setNullable(nullable)
107-
.setComment(comment.orElse(null))
98+
.setComment(comment)
10899
.build();
109100
}
110101

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
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(name, type, nullable, comment, extraInfo, hidden, properties);
43+
this.originalName = requireNonNull(name, "name cannot be null");
44+
}
45+
46+
public String getName()
47+
{
48+
return originalName;
49+
}
50+
51+
@Override
52+
public boolean equals(Object obj)
53+
{
54+
if (this == obj) {
55+
return true;
56+
}
57+
if (obj == null || getClass() != obj.getClass()) {
58+
return false;
59+
}
60+
JdbcColumnMetadata other = (JdbcColumnMetadata) obj;
61+
return Objects.equals(this.originalName, other.originalName) &&
62+
Objects.equals(this.getName(), other.getName()) &&
63+
Objects.equals(this.getType(), other.getType()) &&
64+
Objects.equals(this.getComment(), other.getComment()) &&
65+
Objects.equals(this.isHidden(), other.isHidden()) &&
66+
Objects.equals(this.isNullable(), other.isNullable()) &&
67+
Objects.equals(this.getProperties(), other.getProperties());
68+
}
69+
70+
@Override
71+
public int hashCode()
72+
{
73+
return Objects.hash(originalName, getType(), getComment(), isHidden(), isNullable(), getProperties());
74+
}
75+
76+
public static Builder jdbcBuilder()
77+
{
78+
return new Builder();
79+
}
80+
81+
public static class Builder
82+
{
83+
private String name;
84+
private Type type;
85+
private boolean nullable = true;
86+
private Optional<String> comment = Optional.empty();
87+
private Optional<String> extraInfo = Optional.empty();
88+
private boolean hidden;
89+
private Map<String, Object> properties = emptyMap();
90+
91+
private Builder() {}
92+
93+
public Builder setName(String name)
94+
{
95+
this.name = requireNonNull(name, "name is null");
96+
return this;
97+
}
98+
99+
public Builder setType(Type type)
100+
{
101+
this.type = requireNonNull(type, "type is null");
102+
return this;
103+
}
104+
105+
public Builder setNullable(boolean nullable)
106+
{
107+
this.nullable = nullable;
108+
return this;
109+
}
110+
111+
public Builder setComment(Optional<String> comment)
112+
{
113+
this.comment = requireNonNull(comment, "comment is null");
114+
return this;
115+
}
116+
117+
public Builder setExtraInfo(Optional<String> extraInfo)
118+
{
119+
this.extraInfo = requireNonNull(extraInfo, "extraInfo is null");
120+
return this;
121+
}
122+
123+
public Builder setHidden(boolean hidden)
124+
{
125+
this.hidden = hidden;
126+
return this;
127+
}
128+
129+
public Builder setProperties(Map<String, Object> properties)
130+
{
131+
this.properties = requireNonNull(properties, "properties is null");
132+
return this;
133+
}
134+
135+
public JdbcColumnMetadata build()
136+
{
137+
return new JdbcColumnMetadata(
138+
name,
139+
type,
140+
nullable,
141+
comment.orElse(null),
142+
extraInfo.orElse(null),
143+
hidden,
144+
properties);
145+
}
146+
}
147+
}

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

+3-3
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

+5-2
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

+17-17
Original file line numberDiff line numberDiff line change
@@ -125,17 +125,17 @@ public void getTableMetadata()
125125
ConnectorTableMetadata tableMetadata = metadata.getTableMetadata(SESSION, tableHandle);
126126
assertEquals(tableMetadata.getTable(), new SchemaTableName("example", "numbers"));
127127
assertEquals(tableMetadata.getColumns(), ImmutableList.of(
128-
new ColumnMetadata("text", VARCHAR, false, null, null, false, emptyMap()), // primary key is not null in H2
129-
new ColumnMetadata("text_short", createVarcharType(32)),
130-
new ColumnMetadata("value", BIGINT)));
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)));
131131

132132
// escaping name patterns
133133
JdbcTableHandle specialTableHandle = metadata.getTableHandle(SESSION, new SchemaTableName("exa_ple", "num_ers"));
134134
ConnectorTableMetadata specialTableMetadata = metadata.getTableMetadata(SESSION, specialTableHandle);
135135
assertEquals(specialTableMetadata.getTable(), new SchemaTableName("exa_ple", "num_ers"));
136136
assertEquals(specialTableMetadata.getColumns(), ImmutableList.of(
137-
new ColumnMetadata("te_t", VARCHAR, false, null, null, false, emptyMap()), // primary key is not null in H2
138-
new ColumnMetadata("va%ue", BIGINT)));
137+
new JdbcColumnMetadata("te_t", VARCHAR, false, null, null, false, emptyMap()), // primary key is not null in H2
138+
new JdbcColumnMetadata("va%ue", BIGINT)));
139139

140140
// unknown tables should produce null
141141
unknownTableMetadata(new JdbcTableHandle(CONNECTOR_ID, new SchemaTableName("u", "numbers"), null, "unknown", "unknown"));
@@ -148,13 +148,13 @@ public void testListTableColumns()
148148
{
149149
SchemaTableName tpchOrders = new SchemaTableName("tpch", "orders");
150150
ImmutableList<ColumnMetadata> tpchOrdersColumnMetadata = ImmutableList.of(
151-
ColumnMetadata.builder().setName("orderkey").setType(BIGINT).setNullable(false).build(),
152-
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());
153153

154154
SchemaTableName tpchLineItem = new SchemaTableName("tpch", "lineitem");
155155
ImmutableList<ColumnMetadata> tpchLineItemColumnMetadata = ImmutableList.of(
156-
ColumnMetadata.builder().setName("orderkey").setType(BIGINT).setNullable(false).build(),
157-
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());
158158

159159
//List columns for a given schema and table
160160
Map<SchemaTableName, List<ColumnMetadata>> tpchOrdersColumns = metadata.listTableColumns(SESSION, new SchemaTablePrefix("tpch", "orders"));
@@ -214,41 +214,41 @@ public void getColumnMetadata()
214214
{
215215
assertEquals(
216216
metadata.getColumnMetadata(SESSION, tableHandle, new JdbcColumnHandle(CONNECTOR_ID, "text", JDBC_VARCHAR, VARCHAR, true, Optional.empty())),
217-
new ColumnMetadata("text", VARCHAR));
217+
new JdbcColumnMetadata("text", VARCHAR));
218218
}
219219

220220
@Test
221221
public void testCreateAndAlterTable()
222222
{
223223
SchemaTableName table = new SchemaTableName("example", "foo");
224-
metadata.createTable(SESSION, new ConnectorTableMetadata(table, ImmutableList.of(new ColumnMetadata("text", VARCHAR))), false);
224+
metadata.createTable(SESSION, new ConnectorTableMetadata(table, ImmutableList.of(new JdbcColumnMetadata("text", VARCHAR))), false);
225225

226226
JdbcTableHandle handle = metadata.getTableHandle(SESSION, table);
227227

228228
ConnectorTableMetadata layout = metadata.getTableMetadata(SESSION, handle);
229229
assertEquals(layout.getTable(), table);
230230
assertEquals(layout.getColumns().size(), 1);
231-
assertEquals(layout.getColumns().get(0), new ColumnMetadata("text", VARCHAR));
231+
assertEquals(layout.getColumns().get(0), new JdbcColumnMetadata("text", VARCHAR));
232232

233-
metadata.addColumn(SESSION, handle, new ColumnMetadata("x", VARCHAR));
233+
metadata.addColumn(SESSION, handle, new JdbcColumnMetadata("x", VARCHAR));
234234
layout = metadata.getTableMetadata(SESSION, handle);
235235
assertEquals(layout.getColumns().size(), 2);
236-
assertEquals(layout.getColumns().get(0), new ColumnMetadata("text", VARCHAR));
237-
assertEquals(layout.getColumns().get(1), new ColumnMetadata("x", VARCHAR));
236+
assertEquals(layout.getColumns().get(0), new JdbcColumnMetadata("text", VARCHAR));
237+
assertEquals(layout.getColumns().get(1), new JdbcColumnMetadata("x", VARCHAR));
238238

239239
JdbcColumnHandle columnHandle = new JdbcColumnHandle(CONNECTOR_ID, "x", JDBC_VARCHAR, VARCHAR, true, Optional.empty());
240240
metadata.dropColumn(SESSION, handle, columnHandle);
241241
layout = metadata.getTableMetadata(SESSION, handle);
242242
assertEquals(layout.getColumns().size(), 1);
243-
assertEquals(layout.getColumns().get(0), new ColumnMetadata("text", VARCHAR));
243+
assertEquals(layout.getColumns().get(0), new JdbcColumnMetadata("text", VARCHAR));
244244

245245
SchemaTableName newTableName = new SchemaTableName("example", "bar");
246246
metadata.renameTable(SESSION, handle, newTableName);
247247
handle = metadata.getTableHandle(SESSION, newTableName);
248248
layout = metadata.getTableMetadata(SESSION, handle);
249249
assertEquals(layout.getTable(), newTableName);
250250
assertEquals(layout.getColumns().size(), 1);
251-
assertEquals(layout.getColumns().get(0), new ColumnMetadata("text", VARCHAR));
251+
assertEquals(layout.getColumns().get(0), new JdbcColumnMetadata("text", VARCHAR));
252252
}
253253

254254
@Test

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

+1-1
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

+1-1
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

+1
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)