This is an automated email from the ASF dual-hosted git repository.
dmsysolyatin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push:
new 50d124615e [CALCITE-5515] Add keyspace parameter to CassandraSchema
and CassandraTable
50d124615e is described below
commit 50d124615e0b07f7fbe6107b7c440d9737a00836
Author: Tim Nieradzik <[email protected]>
AuthorDate: Mon Jan 30 15:41:35 2023 +0300
[CALCITE-5515] Add keyspace parameter to CassandraSchema and CassandraTable
Currently, it is not possible to construct a `CassandraSchema` instance
if the keyspace is not set in the CQL session.
Queries generated by `CassandraTable` do not include a keyspace and
will fail if the CQL session does not have a keyspace set.
---
.../calcite/adapter/cassandra/CassandraSchema.java | 33 +++++++++++--
.../adapter/cassandra/CassandraSchemaFactory.java | 39 +++++++++------
.../calcite/adapter/cassandra/CassandraTable.java | 36 +++++++++++++-
.../apache/calcite/test/CassandraAdapterTest.java | 3 ++
.../test/CassandraAdapterWithoutKeyspaceTest.java | 57 ++++++++++++++++++++++
.../src/test/resources/model-without-keyspace.json | 31 ++++++++++++
cassandra/src/test/resources/twissandra-small.cql | 36 ++++++++++++++
7 files changed, 213 insertions(+), 22 deletions(-)
diff --git
a/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraSchema.java
b/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraSchema.java
index de774cfa6c..f0291397be 100644
---
a/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraSchema.java
+++
b/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraSchema.java
@@ -90,11 +90,34 @@ public class CassandraSchema extends AbstractSchema {
* @param name the schema name
*/
public CassandraSchema(CqlSession session, SchemaPlus parentSchema, String
name) {
+ this(
+ session,
+ parentSchema,
+ session.getKeyspace()
+ .orElseThrow(() -> new RuntimeException("No keyspace for session " +
session.getName()))
+ .asInternal(),
+ name
+ );
+ }
+
+ /**
+ * Creates a Cassandra schema.
+ *
+ * @param session a Cassandra session
+ * @param parentSchema the parent schema
+ * @param keyspace the keyspace name
+ * @param name the schema name
+ */
+ public CassandraSchema(
+ CqlSession session,
+ SchemaPlus parentSchema,
+ String keyspace,
+ String name
+ ) {
super();
+
this.session = session;
- this.keyspace = session.getKeyspace()
- .orElseThrow(() -> new RuntimeException("No keyspace for session " +
session.getName()))
- .asInternal();
+ this.keyspace = keyspace;
this.parentSchema = parentSchema;
this.name = name;
this.hook = prepareHook();
@@ -323,11 +346,11 @@ public class CassandraSchema extends AbstractSchema {
final ImmutableMap.Builder<String, Table> builder = ImmutableMap.builder();
for (TableMetadata table : getKeyspace().getTables().values()) {
String tableName = table.getName().asInternal();
- builder.put(tableName, new CassandraTable(this, tableName));
+ builder.put(tableName, new CassandraTable(this, keyspace, tableName));
for (ViewMetadata view :
getKeyspace().getViewsOnTable(table.getName()).values()) {
String viewName = view.getName().asInternal();
- builder.put(viewName, new CassandraTable(this, viewName, true));
+ builder.put(viewName, new CassandraTable(this, keyspace, viewName,
true));
}
}
return builder.build();
diff --git
a/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraSchemaFactory.java
b/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraSchemaFactory.java
index 8aa796cbbc..4fba46e0f8 100644
---
a/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraSchemaFactory.java
+++
b/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraSchemaFactory.java
@@ -21,7 +21,9 @@ import org.apache.calcite.schema.SchemaFactory;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.util.trace.CalciteTrace;
+import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.CqlSession;
+import com.datastax.oss.driver.api.core.CqlSessionBuilder;
import com.google.common.collect.ImmutableSet;
import org.slf4j.Logger;
@@ -56,7 +58,6 @@ public class CassandraSchemaFactory implements SchemaFactory {
INFO_TO_SESSION.computeIfAbsent(sessionMap, m -> {
String host = (String) m.get("host");
- String keyspace = (String) m.get("keyspace");
String username = (String) m.get("username");
String password = (String) m.get("password");
int port = getPort(m);
@@ -65,26 +66,34 @@ public class CassandraSchemaFactory implements
SchemaFactory {
LOGGER.debug("Creating session for info {}", m);
}
try {
- if (username != null && password != null) {
- return CqlSession.builder()
- .addContactPoint(new InetSocketAddress(host, port))
- .withAuthCredentials(username, password)
- .withKeyspace(keyspace)
- .withLocalDatacenter("datacenter1")
- .build();
- } else {
- return CqlSession.builder()
- .addContactPoint(new InetSocketAddress(host, port))
- .withKeyspace(keyspace)
- .withLocalDatacenter("datacenter1")
- .build();
+ CqlSessionBuilder builder =
+ username != null && password != null
+ ? CqlSession.builder()
+ .addContactPoint(new InetSocketAddress(host, port))
+ .withAuthCredentials(username, password)
+ : CqlSession.builder()
+ .addContactPoint(new InetSocketAddress(host, port));
+
+ if (m.containsKey("keyspace")) {
+ String keyspace = (String) m.get("keyspace");
+ builder = builder.withKeyspace(keyspace);
}
+
+ return builder
+ .withLocalDatacenter("datacenter1")
+ .build();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
- return new CassandraSchema(INFO_TO_SESSION.get(sessionMap), parentSchema,
name);
+ CqlSession session = INFO_TO_SESSION.get(sessionMap);
+
+ String keyspace = session.getKeyspace()
+ .map(CqlIdentifier::asInternal)
+ .orElse(name);
+
+ return new CassandraSchema(session, parentSchema, keyspace, name);
}
private static Map<String, Object> projectMapOverKeys(
diff --git
a/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraTable.java
b/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraTable.java
index 2cb9519ebe..31550c13e7 100644
---
a/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraTable.java
+++
b/cassandra/src/main/java/org/apache/calcite/adapter/cassandra/CassandraTable.java
@@ -48,6 +48,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
/**
* Table based on a Cassandra column family.
@@ -58,10 +59,13 @@ public class CassandraTable extends AbstractQueryableTable
List<String> partitionKeys;
List<String> clusteringKeys;
List<RelFieldCollation> clusteringOrder;
+ private final Optional<String> keyspace;
private final String columnFamily;
+ @Deprecated // to be removed before 2.0
public CassandraTable(CassandraSchema schema, String columnFamily, boolean
isView) {
super(Object[].class);
+ this.keyspace = Optional.empty();
this.columnFamily = columnFamily;
this.protoRowType = schema.getRelDataType(columnFamily, isView);
this.partitionKeys = schema.getPartitionKeys(columnFamily, isView);
@@ -69,10 +73,30 @@ public class CassandraTable extends AbstractQueryableTable
this.clusteringOrder = schema.getClusteringOrder(columnFamily, isView);
}
+ public CassandraTable(
+ CassandraSchema schema,
+ String keyspace,
+ String columnFamily,
+ boolean isView
+ ) {
+ super(Object[].class);
+ this.keyspace = Optional.of(keyspace);
+ this.columnFamily = columnFamily;
+ this.protoRowType = schema.getRelDataType(columnFamily, isView);
+ this.partitionKeys = schema.getPartitionKeys(columnFamily, isView);
+ this.clusteringKeys = schema.getClusteringKeys(columnFamily, isView);
+ this.clusteringOrder = schema.getClusteringOrder(columnFamily, isView);
+ }
+
+ @Deprecated // to be removed before 2.0
public CassandraTable(CassandraSchema schema, String columnFamily) {
this(schema, columnFamily, false);
}
+ public CassandraTable(CassandraSchema schema, String keyspace, String
columnFamily) {
+ this(schema, keyspace, columnFamily, false);
+ }
+
@Override public String toString() {
return "CassandraTable {" + columnFamily + "}";
}
@@ -168,11 +192,19 @@ public class CassandraTable extends AbstractQueryableTable
// Build and issue the query and return an Enumerator over the results
StringBuilder queryBuilder = new StringBuilder("SELECT ");
- queryBuilder.append(selectString)
- .append(" FROM \"")
+ queryBuilder
+ .append(selectString)
+ .append(" FROM \"");
+
+ keyspace.ifPresent(s ->
+ queryBuilder.append(s).append("\".\"")
+ );
+
+ queryBuilder
.append(columnFamily)
.append("\"")
.append(whereClause);
+
if (!order.isEmpty()) {
queryBuilder.append(Util.toString(order, " ORDER BY ", ", ", ""));
}
diff --git
a/cassandra/src/test/java/org/apache/calcite/test/CassandraAdapterTest.java
b/cassandra/src/test/java/org/apache/calcite/test/CassandraAdapterTest.java
index 411ffdbb65..73e9abe618 100644
--- a/cassandra/src/test/java/org/apache/calcite/test/CassandraAdapterTest.java
+++ b/cassandra/src/test/java/org/apache/calcite/test/CassandraAdapterTest.java
@@ -46,6 +46,9 @@ class CassandraAdapterTest {
private static final ImmutableMap<String, String> TWISSANDRA =
CassandraExtension.getDataset("/model.json");
+ private static final ImmutableMap<String, String>
TWISSANDRA_WITHOUT_KEYSPACE =
+ CassandraExtension.getDataset("/model-without-keyspace.json");
+
@BeforeAll
static void load(CqlSession session) {
new CQLDataLoader(session)
diff --git
a/cassandra/src/test/java/org/apache/calcite/test/CassandraAdapterWithoutKeyspaceTest.java
b/cassandra/src/test/java/org/apache/calcite/test/CassandraAdapterWithoutKeyspaceTest.java
new file mode 100644
index 0000000000..8822e9dbe6
--- /dev/null
+++
b/cassandra/src/test/java/org/apache/calcite/test/CassandraAdapterWithoutKeyspaceTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.test;
+
+import com.datastax.oss.driver.api.core.CqlSession;
+import com.google.common.collect.ImmutableMap;
+
+import org.cassandraunit.CQLDataLoader;
+import org.cassandraunit.dataset.cql.ClassPathCQLDataSet;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
+
+/**
+ * Tests for the {@code org.apache.calcite.adapter.cassandra} package.
+ *
+ *
+ * Instantiates a CQL session without keyspace, but passes it to
+ * {@code org.apache.calcite.adapter.cassandra.CassandraTable}.
+ * All generated CQL queries should still succeed and explicitly
+ * reference the keyspace.
+ */
+@Execution(ExecutionMode.SAME_THREAD)
+@ExtendWith(CassandraExtension.class)
+class CassandraAdapterWithoutKeyspaceTest {
+ private static final ImmutableMap<String, String>
TWISSANDRA_WITHOUT_KEYSPACE =
+ CassandraExtension.getDataset("/model-without-keyspace.json");
+
+ @BeforeAll
+ static void load(CqlSession session) {
+ new CQLDataLoader(session)
+ .load(new ClassPathCQLDataSet("twissandra-small.cql"));
+ }
+
+ @Test void testSelect() {
+ CalciteAssert.that()
+ .with(TWISSANDRA_WITHOUT_KEYSPACE)
+ .query("select * from \"users\"")
+ .returnsCount(10);
+ }
+}
diff --git a/cassandra/src/test/resources/model-without-keyspace.json
b/cassandra/src/test/resources/model-without-keyspace.json
new file mode 100644
index 0000000000..6bcc6a5134
--- /dev/null
+++ b/cassandra/src/test/resources/model-without-keyspace.json
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+{
+ "version": "1.0",
+ "defaultSchema": "twissandra_small",
+ "schemas": [
+ {
+ "name": "twissandra_small",
+ "type": "custom",
+ "factory": "org.apache.calcite.adapter.cassandra.CassandraSchemaFactory",
+ "operand": {
+ "host": "localhost",
+ "port": 9142
+ }
+ }
+ ]
+}
diff --git a/cassandra/src/test/resources/twissandra-small.cql
b/cassandra/src/test/resources/twissandra-small.cql
new file mode 100644
index 0000000000..a324177665
--- /dev/null
+++ b/cassandra/src/test/resources/twissandra-small.cql
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+CREATE KEYSPACE twissandra_small
+WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'};
+
+CREATE TABLE twissandra_small.users (
+ username text PRIMARY KEY,
+ password text
+);
+
+USE twissandra_small;
+
+INSERT INTO users(username, password) VALUES ('fOGctyIDES','cGfDNvOUWH');
+INSERT INTO users(username, password) VALUES ('cWIZrdKQmh','haENHSnBMF');
+INSERT INTO users(username, password) VALUES ('lixvTEUaBj','gmDSxlydEL');
+INSERT INTO users(username, password) VALUES ('PNexGqHdVE','ZSBNHcIrvC');
+INSERT INTO users(username, password) VALUES ('PDKIVoezHs','UINXFlcAod');
+INSERT INTO users(username, password) VALUES ('HuGetcsXbQ','fXwYWMaSjc');
+INSERT INTO users(username, password) VALUES ('MdHQeWbPjB','QlaxOTioNZ');
+INSERT INTO users(username, password) VALUES ('UWzCrfaxQi','EzyQckbKOh');
+INSERT INTO users(username, password) VALUES ('JmuhsAaMdw','SQbIaqvzfW');
+INSERT INTO users(username, password) VALUES ('nFtPHprNOd','CESzsfTALr');