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');

Reply via email to