Skip to content

Commit 44c3dcc

Browse files
committed
Mixedcase support for cassandra
Adding mixed case test to ci
1 parent e0d3305 commit 44c3dcc

File tree

11 files changed

+216
-24
lines changed

11 files changed

+216
-24
lines changed

presto-cassandra/pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@
250250
<configuration>
251251
<excludes>
252252
<exclude>**/TestCassandraIntegrationSmokeTest.java</exclude>
253+
<exclude>**/TestCassandraIntergrationMixedCase.java</exclude>
253254
</excludes>
254255
</configuration>
255256
</plugin>
@@ -266,6 +267,7 @@
266267
<configuration>
267268
<includes>
268269
<include>**/TestCassandraIntegrationSmokeTest.java</include>
270+
<include>**/TestCassandraIntergrationMixedCase.java</include>
269271
</includes>
270272
</configuration>
271273
</plugin>

presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraClientConfig.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public class CassandraClientConfig
7676
private String truststorePassword;
7777
private File keystorePath;
7878
private String keystorePassword;
79+
private boolean caseSensitiveNameMatchingEnabled;
7980

8081
@NotNull
8182
@Size(min = 1)
@@ -476,4 +477,16 @@ public CassandraClientConfig setTruststorePassword(String truststorePassword)
476477
this.truststorePassword = truststorePassword;
477478
return this;
478479
}
480+
481+
public boolean isCaseSensitiveNameMatchingEnabled()
482+
{
483+
return caseSensitiveNameMatchingEnabled;
484+
}
485+
486+
@Config("case-sensitive-name-matching")
487+
public CassandraClientConfig setCaseSensitiveNameMatchingEnabled(boolean caseSensitiveNameMatchingEnabled)
488+
{
489+
this.caseSensitiveNameMatchingEnabled = caseSensitiveNameMatchingEnabled;
490+
return this;
491+
}
479492
}

presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraClientModule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,6 @@ public static CassandraSession createCassandraSession(
156156
contactPoints.forEach(clusterBuilder::addContactPoint);
157157
return clusterBuilder.build();
158158
}),
159-
config.getNoHostAvailableRetryTimeout());
159+
config.getNoHostAvailableRetryTimeout(), config.isCaseSensitiveNameMatchingEnabled());
160160
}
161161
}

presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraMetadata.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454

5555
import static com.facebook.presto.cassandra.CassandraType.toCassandraType;
5656
import static com.facebook.presto.cassandra.util.CassandraCqlUtils.cqlNameToSqlName;
57+
import static com.facebook.presto.cassandra.util.CassandraCqlUtils.validColumnName;
5758
import static com.facebook.presto.cassandra.util.CassandraCqlUtils.validSchemaName;
5859
import static com.facebook.presto.cassandra.util.CassandraCqlUtils.validTableName;
5960
import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED;
@@ -72,6 +73,7 @@ public class CassandraMetadata
7273
private final CassandraPartitionManager partitionManager;
7374
private final boolean allowDropTable;
7475
private final ProtocolVersion protocolVersion;
76+
private boolean caseSensitiveNameMatchingEnabled;
7577

7678
private final JsonCodec<List<ExtraColumnMetadata>> extraColumnMetadataCodec;
7779

@@ -89,6 +91,7 @@ public CassandraMetadata(
8991
this.allowDropTable = requireNonNull(config, "config is null").getAllowDropTable();
9092
this.extraColumnMetadataCodec = requireNonNull(extraColumnMetadataCodec, "extraColumnMetadataCodec is null");
9193
this.protocolVersion = requireNonNull(config, "config is null").getProtocolVersion();
94+
this.caseSensitiveNameMatchingEnabled = requireNonNull(config, "config is null").isCaseSensitiveNameMatchingEnabled();
9295
}
9396

9497
@Override
@@ -140,7 +143,8 @@ public List<SchemaTableName> listTables(ConnectorSession session, String schemaN
140143
for (String schemaName : listSchemas(session, schemaNameOrNull)) {
141144
try {
142145
for (String tableName : cassandraSession.getCaseSensitiveTableNames(schemaName)) {
143-
tableNames.add(new SchemaTableName(schemaName, tableName.toLowerCase(ENGLISH)));
146+
String finalTableName = normalizeIdentifier(session, tableName);
147+
tableNames.add(new SchemaTableName(schemaName, finalTableName));
144148
}
145149
}
146150
catch (SchemaNotFoundException e) {
@@ -166,7 +170,9 @@ public Map<String, ColumnHandle> getColumnHandles(ConnectorSession session, Conn
166170
CassandraTable table = cassandraSession.getTable(getTableName(tableHandle));
167171
ImmutableMap.Builder<String, ColumnHandle> columnHandles = ImmutableMap.builder();
168172
for (CassandraColumnHandle columnHandle : table.getColumns()) {
169-
columnHandles.put(CassandraCqlUtils.cqlNameToSqlName(columnHandle.getName()).toLowerCase(ENGLISH), columnHandle);
173+
String columnName = CassandraCqlUtils.cqlNameToSqlName(columnHandle.getName());
174+
String columnNameKey = normalizeIdentifier(session, columnName);
175+
columnHandles.put(columnNameKey, columnHandle);
170176
}
171177
return columnHandles.build();
172178
}
@@ -245,7 +251,7 @@ public String toString()
245251
@Override
246252
public void createTable(ConnectorSession session, ConnectorTableMetadata tableMetadata, boolean ignoreExisting)
247253
{
248-
createTable(tableMetadata);
254+
createTable(session, tableMetadata);
249255
}
250256

251257
@Override
@@ -274,10 +280,10 @@ public void renameTable(ConnectorSession session, ConnectorTableHandle tableHand
274280
@Override
275281
public ConnectorOutputTableHandle beginCreateTable(ConnectorSession session, ConnectorTableMetadata tableMetadata, Optional<ConnectorNewTableLayout> layout)
276282
{
277-
return createTable(tableMetadata);
283+
return createTable(session, tableMetadata);
278284
}
279285

280-
private CassandraOutputTableHandle createTable(ConnectorTableMetadata tableMetadata)
286+
private CassandraOutputTableHandle createTable(ConnectorSession session, ConnectorTableMetadata tableMetadata)
281287
{
282288
ImmutableList.Builder<String> columnNames = ImmutableList.builder();
283289
ImmutableList.Builder<Type> columnTypes = ImmutableList.builder();
@@ -298,9 +304,10 @@ private CassandraOutputTableHandle createTable(ConnectorTableMetadata tableMetad
298304
StringBuilder queryBuilder = new StringBuilder(String.format("CREATE TABLE \"%s\".\"%s\"(id uuid primary key", schemaName, tableName));
299305
for (int i = 0; i < columns.size(); i++) {
300306
String name = columns.get(i);
307+
String columnName = validColumnName(normalizeIdentifier(session, name));
301308
Type type = types.get(i);
302309
queryBuilder.append(", ")
303-
.append(name)
310+
.append(columnName)
304311
.append(" ")
305312
.append(toCassandraType(type, protocolVersion).name().toLowerCase(ENGLISH));
306313
}
@@ -352,4 +359,10 @@ public Optional<ConnectorOutputMetadata> finishInsert(ConnectorSession session,
352359
{
353360
return Optional.empty();
354361
}
362+
363+
@Override
364+
public String normalizeIdentifier(ConnectorSession session, String identifier)
365+
{
366+
return caseSensitiveNameMatchingEnabled ? identifier : identifier.toLowerCase(ENGLISH);
367+
}
355368
}

presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraPageSink.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939

4040
import static com.datastax.driver.core.querybuilder.QueryBuilder.bindMarker;
4141
import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
42+
import static com.facebook.presto.cassandra.util.CassandraCqlUtils.validColumnName;
43+
import static com.facebook.presto.cassandra.util.CassandraCqlUtils.validSchemaName;
44+
import static com.facebook.presto.cassandra.util.CassandraCqlUtils.validTableName;
4245
import static com.facebook.presto.common.type.BigintType.BIGINT;
4346
import static com.facebook.presto.common.type.BooleanType.BOOLEAN;
4447
import static com.facebook.presto.common.type.DateType.DATE;
@@ -92,14 +95,14 @@ public CassandraPageSink(
9295
toCassandraDate = value -> LocalDate.fromDaysSinceEpoch(toIntExact(value));
9396
}
9497

95-
Insert insert = insertInto(schemaName, tableName);
98+
Insert insert = insertInto(validSchemaName(schemaName), validTableName(tableName));
9699
if (generateUUID) {
97100
insert.value("id", bindMarker());
98101
}
99102
for (int i = 0; i < columnNames.size(); i++) {
100103
String columnName = columnNames.get(i);
101104
checkArgument(columnName != null, "columnName is null at position: %d", i);
102-
insert.value(columnName, bindMarker());
105+
insert.value(validColumnName(columnName), bindMarker());
103106
}
104107
this.insert = cassandraSession.prepare(insert);
105108
}

presto-cassandra/src/main/java/com/facebook/presto/cassandra/NativeCassandraSession.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,16 @@ public KeyspaceMetadata load(String key)
118118
private final Cluster cluster;
119119
private final Supplier<Session> session;
120120
private final Duration noHostAvailableRetryTimeout;
121+
private static boolean caseSensitiveNameMatchingEnabled;
121122

122-
public NativeCassandraSession(String connectorId, JsonCodec<List<ExtraColumnMetadata>> extraColumnMetadataCodec, Cluster cluster, Duration noHostAvailableRetryTimeout)
123+
public NativeCassandraSession(String connectorId, JsonCodec<List<ExtraColumnMetadata>> extraColumnMetadataCodec, Cluster cluster, Duration noHostAvailableRetryTimeout, boolean caseSensitiveNameMatchingEnabled)
123124
{
124125
this.connectorId = requireNonNull(connectorId, "connectorId is null");
125126
this.extraColumnMetadataCodec = requireNonNull(extraColumnMetadataCodec, "extraColumnMetadataCodec is null");
126127
this.cluster = requireNonNull(cluster, "cluster is null");
127128
this.noHostAvailableRetryTimeout = requireNonNull(noHostAvailableRetryTimeout, "noHostAvailableRetryTimeout is null");
128129
this.session = memoize(cluster::connect);
130+
this.caseSensitiveNameMatchingEnabled = requireNonNull(caseSensitiveNameMatchingEnabled, "caseSensitiveNameMatchingEnabled is null");
129131
}
130132

131133
@Override
@@ -320,7 +322,7 @@ private static AbstractTableMetadata getTableMetadata(KeyspaceMetadata keyspace,
320322
List<AbstractTableMetadata> tables = Stream.concat(
321323
keyspace.getTables().stream(),
322324
keyspace.getMaterializedViews().stream())
323-
.filter(table -> table.getName().equalsIgnoreCase(caseInsensitiveTableName))
325+
.filter(table -> caseSensitiveNameMatchingEnabled ? table.getName().equals(caseInsensitiveTableName) : table.getName().equalsIgnoreCase(caseInsensitiveTableName))
324326
.collect(toImmutableList());
325327
if (tables.size() == 0) {
326328
throw new TableNotFoundException(new SchemaTableName(keyspace.getName(), caseInsensitiveTableName));
@@ -348,14 +350,14 @@ private static void checkColumnNames(List<ColumnMetadata> columns)
348350
{
349351
Map<String, ColumnMetadata> lowercaseNameToColumnMap = new HashMap<>();
350352
for (ColumnMetadata column : columns) {
351-
String lowercaseName = column.getName().toLowerCase(ENGLISH);
352-
if (lowercaseNameToColumnMap.containsKey(lowercaseName)) {
353+
String columnNameKey = caseSensitiveNameMatchingEnabled ? column.getName() : column.getName().toLowerCase(ENGLISH);
354+
if (lowercaseNameToColumnMap.containsKey(columnNameKey)) {
353355
throw new PrestoException(
354356
NOT_SUPPORTED,
355357
format("More than one column has been found for the case insensitive column name: %s -> (%s, %s)",
356-
lowercaseName, lowercaseNameToColumnMap.get(lowercaseName).getName(), column.getName()));
358+
columnNameKey, lowercaseNameToColumnMap.get(columnNameKey).getName(), column.getName()));
357359
}
358-
lowercaseNameToColumnMap.put(lowercaseName, column);
360+
lowercaseNameToColumnMap.put(columnNameKey, column);
359361
}
360362
}
361363

presto-cassandra/src/test/java/com/facebook/presto/cassandra/CassandraQueryRunner.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
import com.google.common.collect.ImmutableMap;
2020
import io.airlift.tpch.TpchTable;
2121

22+
import java.util.HashMap;
2223
import java.util.List;
24+
import java.util.Map;
2325

2426
import static com.facebook.presto.cassandra.CassandraTestingUtils.createKeyspace;
2527
import static com.facebook.presto.testing.TestingSession.testSessionBuilder;
@@ -34,19 +36,21 @@ private CassandraQueryRunner()
3436

3537
private static boolean tpchLoaded;
3638

37-
public static DistributedQueryRunner createCassandraQueryRunner(CassandraServer server)
39+
public static DistributedQueryRunner createCassandraQueryRunner(CassandraServer server, Map<String, String> connectorProperties)
3840
throws Exception
3941
{
4042
DistributedQueryRunner queryRunner = new DistributedQueryRunner(createCassandraSession("tpch"), 4);
4143

4244
queryRunner.installPlugin(new TpchPlugin());
4345
queryRunner.createCatalog("tpch", "tpch");
4446

47+
connectorProperties = new HashMap<>(ImmutableMap.copyOf(connectorProperties));
48+
connectorProperties.putIfAbsent("cassandra.contact-points", server.getHost());
49+
connectorProperties.putIfAbsent("cassandra.native-protocol-port", Integer.toString(server.getPort()));
50+
connectorProperties.putIfAbsent("cassandra.allow-drop-table", "true");
51+
4552
queryRunner.installPlugin(new CassandraPlugin());
46-
queryRunner.createCatalog("cassandra", "cassandra", ImmutableMap.of(
47-
"cassandra.contact-points", server.getHost(),
48-
"cassandra.native-protocol-port", Integer.toString(server.getPort()),
49-
"cassandra.allow-drop-table", "true"));
53+
queryRunner.createCatalog("cassandra", "cassandra", connectorProperties);
5054

5155
createKeyspace(server.getSession(), "tpch");
5256
List<TpchTable<?>> tables = TpchTable.getTables();

presto-cassandra/src/test/java/com/facebook/presto/cassandra/CassandraServer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public CassandraServer()
8383
"EmbeddedCassandra",
8484
JsonCodec.listJsonCodec(ExtraColumnMetadata.class),
8585
cluster,
86-
new Duration(1, MINUTES));
86+
new Duration(1, MINUTES), false);
8787
this.metadata = cluster.getMetadata();
8888

8989
try {

presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraDistributed.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.facebook.presto.testing.MaterializedResult;
1717
import com.facebook.presto.testing.QueryRunner;
1818
import com.facebook.presto.tests.AbstractTestDistributedQueries;
19+
import com.google.common.collect.ImmutableMap;
1920
import org.testng.annotations.AfterClass;
2021
import org.testng.annotations.Optional;
2122

@@ -35,7 +36,7 @@ protected QueryRunner createQueryRunner()
3536
throws Exception
3637
{
3738
this.server = new CassandraServer();
38-
return CassandraQueryRunner.createCassandraQueryRunner(server);
39+
return CassandraQueryRunner.createCassandraQueryRunner(server, ImmutableMap.of());
3940
}
4041

4142
@AfterClass(alwaysRun = true)

presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraIntegrationSmokeTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.facebook.presto.testing.QueryRunner;
2121
import com.facebook.presto.tests.AbstractTestIntegrationSmokeTest;
2222
import com.google.common.collect.ImmutableList;
23+
import com.google.common.collect.ImmutableMap;
2324
import io.airlift.units.Duration;
2425
import org.testng.annotations.AfterClass;
2526
import org.testng.annotations.Test;
@@ -97,7 +98,7 @@ protected QueryRunner createQueryRunner()
9798
this.server = server;
9899
this.session = server.getSession();
99100
createTestTables(session, server.getMetadata(), KEYSPACE, DATE_TIME_LOCAL);
100-
return createCassandraQueryRunner(server);
101+
return createCassandraQueryRunner(server, ImmutableMap.of());
101102
}
102103

103104
@Test
@@ -312,7 +313,7 @@ public void testUppercaseNameEscaped()
312313
.row("column_2", "bigint", "", "")
313314
.build());
314315

315-
execute("INSERT INTO \"KEYSPACE_2\".\"TABLE_2\" (\"COLUMN_2\") VALUES (1)");
316+
session.execute("INSERT INTO \"KEYSPACE_2\".\"TABLE_2\" (\"COLUMN_2\") VALUES (1)");
316317

317318
assertEquals(execute("SELECT column_2 FROM cassandra.keyspace_2.table_2").getRowCount(), 1);
318319
assertUpdate("DROP TABLE cassandra.keyspace_2.table_2");

0 commit comments

Comments
 (0)