This is an automated email from the ASF dual-hosted git repository.

zabetak 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 e440e51545 [CALCITE-6724] MockTable support for multiple (individual & 
composite) keys
e440e51545 is described below

commit e440e5154576de18e3f4b9076d70c13bf0fffe88
Author: Stamatis Zampetakis <[email protected]>
AuthorDate: Wed Nov 27 17:59:36 2024 +0100

    [CALCITE-6724] MockTable support for multiple (individual & composite) keys
    
    MockTable currently allows only one key to be specified; the key can be 
either simple or composite. In real scenarios though a table can have multiple 
keys.
---
 .../org/apache/calcite/test/RelMetadataTest.java   |   5 +-
 .../calcite/test/catalog/MockCatalogReader.java    |  48 ++++++++--
 .../org/apache/calcite/test/MockTableTest.java     | 104 +++++++++++++++++++++
 3 files changed, 145 insertions(+), 12 deletions(-)

diff --git a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java 
b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
index bc1809a41a..1e0b2be72c 100644
--- a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
@@ -4198,9 +4198,10 @@ public class RelMetadataTest {
       // Register "T1" table.
       final MockTable t1 =
           MockTable.create(this, tSchema, "composite_keys_table", false, 7.0, 
null);
-      t1.addColumn("key1", typeFactory.createSqlType(SqlTypeName.VARCHAR), 
true);
-      t1.addColumn("key2", typeFactory.createSqlType(SqlTypeName.VARCHAR), 
true);
+      t1.addColumn("key1", typeFactory.createSqlType(SqlTypeName.VARCHAR));
+      t1.addColumn("key2", typeFactory.createSqlType(SqlTypeName.VARCHAR));
       t1.addColumn("value1", typeFactory.createSqlType(SqlTypeName.INTEGER));
+      t1.addKey("key1", "key2");
       addSizeHandler(t1);
       addDistinctRowcountHandler(t1);
       addUniqueKeyHandler(t1);
diff --git 
a/testkit/src/main/java/org/apache/calcite/test/catalog/MockCatalogReader.java 
b/testkit/src/main/java/org/apache/calcite/test/catalog/MockCatalogReader.java
index 01aeb5b8b2..164c2825ef 100644
--- 
a/testkit/src/main/java/org/apache/calcite/test/catalog/MockCatalogReader.java
+++ 
b/testkit/src/main/java/org/apache/calcite/test/catalog/MockCatalogReader.java
@@ -316,7 +316,7 @@ public abstract class MockCatalogReader extends 
CalciteCatalogReader {
     protected final double rowCount;
     protected final List<Map.Entry<String, RelDataType>> columnList =
         new ArrayList<>();
-    protected final List<Integer> keyList = new ArrayList<>();
+    protected final List<ImmutableBitSet> keyList = new ArrayList<>();
     protected final List<RelReferentialConstraint> referentialConstraints =
         new ArrayList<>();
     protected RelDataType rowType;
@@ -378,7 +378,7 @@ public abstract class MockCatalogReader extends 
CalciteCatalogReader {
      */
     protected MockTable(MockCatalogReader catalogReader, boolean stream,
         boolean temporal, double rowCount,
-        List<Map.Entry<String, RelDataType>> columnList, List<Integer> keyList,
+        List<Map.Entry<String, RelDataType>> columnList, List<ImmutableBitSet> 
keyList,
         RelDataType rowType, List<RelCollation> collationList, List<String> 
names,
         Set<String> monotonicColumnSet, StructKind kind,
         @Nullable ColumnResolver resolver,
@@ -592,15 +592,11 @@ public abstract class MockCatalogReader extends 
CalciteCatalogReader {
     }
 
     @Override public boolean isKey(ImmutableBitSet columns) {
-      return !keyList.isEmpty()
-          && columns.contains(ImmutableBitSet.of(keyList));
+      return keyList.stream().anyMatch(columns::contains);
     }
 
     @Override public List<ImmutableBitSet> getKeys() {
-      if (keyList.isEmpty()) {
-        return ImmutableList.of();
-      }
-      return ImmutableList.of(ImmutableBitSet.of(keyList));
+      return keyList;
     }
 
     @Override public List<RelReferentialConstraint> 
getReferentialConstraints() {
@@ -651,11 +647,43 @@ public abstract class MockCatalogReader extends 
CalciteCatalogReader {
 
     public void addColumn(String name, RelDataType type, boolean isKey) {
       if (isKey) {
-        keyList.add(columnList.size());
+        keyList.add(ImmutableBitSet.of(columnList.size()));
       }
       columnList.add(Pair.of(name, type));
     }
 
+    public void addKey(String... columns) {
+      ImmutableBitSet.Builder keyBuilder = ImmutableBitSet.builder();
+      for (String c : columns) {
+        int i = columnIndex(c);
+        if (i < 0) {
+          throw new IllegalArgumentException("Column " + c + " not found in 
the table");
+        }
+        keyBuilder.set(i);
+      }
+      keyList.add(keyBuilder.build());
+    }
+
+    public void addKey(ImmutableBitSet key) {
+      for (Integer columnIndex : key) {
+        if (columnIndex >= columnList.size()) {
+          throw new IllegalArgumentException(
+              "Column index " + columnIndex + " exceeds the number of 
columns");
+        }
+      }
+      keyList.add(key);
+    }
+
+    private int columnIndex(String colName) {
+      for (int i = 0; i < columnList.size(); i++) {
+        Map.Entry<String, RelDataType> col = columnList.get(i);
+        if (colName.equals(col.getKey())) {
+          return i;
+        }
+      }
+      return -1;
+    }
+
     public void addMonotonic(String name) {
       monotonicColumnSet.add(name);
       assert Pair.left(columnList).contains(name);
@@ -712,7 +740,7 @@ public abstract class MockCatalogReader extends 
CalciteCatalogReader {
      */
     private MockModifiableViewRelOptTable(MockModifiableViewTable 
modifiableViewTable,
         MockCatalogReader catalogReader, boolean stream, double rowCount,
-        List<Map.Entry<String, RelDataType>> columnList, List<Integer> keyList,
+        List<Map.Entry<String, RelDataType>> columnList, List<ImmutableBitSet> 
keyList,
         RelDataType rowType, List<RelCollation> collationList, List<String> 
names,
         Set<String> monotonicColumnSet, StructKind kind,
         @Nullable ColumnResolver resolver,
diff --git a/testkit/src/test/java/org/apache/calcite/test/MockTableTest.java 
b/testkit/src/test/java/org/apache/calcite/test/MockTableTest.java
new file mode 100644
index 0000000000..8becd8a9ca
--- /dev/null
+++ b/testkit/src/test/java/org/apache/calcite/test/MockTableTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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 org.apache.calcite.rel.type.RelDataTypeSystem;
+import org.apache.calcite.sql.type.SqlTypeFactoryImpl;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql2rel.NullInitializerExpressionFactory;
+import org.apache.calcite.test.catalog.MockCatalogReader;
+import org.apache.calcite.util.ImmutableBitSet;
+
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasToString;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Tests for {@link 
org.apache.calcite.test.catalog.MockCatalogReader.MockTable}.
+ */
+public class MockTableTest {
+  private static final SqlTypeFactoryImpl TYPE_FACTORY =
+      new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
+
+  @Test void testAddColumnCreatesIndividualKeys() {
+    MockCatalogReader.MockTable t = newTable();
+    t.addColumn("k1", TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER), true);
+    t.addColumn("k2", TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER), true);
+    assertThat(t.getKeys(), hasToString("[{0}, {1}]"));
+  }
+
+  @Test void testAddKeyWithOneEntryCreatesSimpleKey() {
+    MockCatalogReader.MockTable t = newTable();
+    t.addColumn("k1", TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER));
+    t.addColumn("k2", TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER));
+    t.addKey("k1");
+    assertThat(t.getKeys(), hasToString("[{0}]"));
+  }
+
+  @Test void testAddKeyWithMultipleEntriesCreatesCompositeKey() {
+    MockCatalogReader.MockTable t = newTable();
+    t.addColumn("k1", TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER));
+    t.addColumn("k2", TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER));
+    t.addKey("k1", "k2");
+    assertThat(t.getKeys(), hasToString("[{0, 1}]"));
+  }
+
+  @Test void testAddKeyWithMissingColumnNameThrowsException() {
+    MockCatalogReader.MockTable t = newTable();
+    t.addColumn("k1", TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER));
+    t.addColumn("k2", TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER));
+    assertThrows(IllegalArgumentException.class, () -> t.addKey("k1", "k3"));
+  }
+
+  @Test void testAddKeyUsingColumnIndex() {
+    MockCatalogReader.MockTable t = newTable();
+    t.addColumn("k1", TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER));
+    t.addColumn("k2", TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER));
+    t.addKey(ImmutableBitSet.of(0, 1));
+    assertThat(t.getKeys(), hasToString("[{0, 1}]"));
+  }
+
+  @Test void testAddKeyUsingWrongIndexThrowsException() {
+    MockCatalogReader.MockTable t = newTable();
+    t.addColumn("k1", TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER));
+    t.addColumn("k2", TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER));
+    assertThrows(IllegalArgumentException.class, () -> 
t.addKey(ImmutableBitSet.of(0, 2)));
+  }
+
+  @Test void testAddKeyMultipleTimes() {
+    MockCatalogReader.MockTable t = newTable();
+    t.addColumn("k1", TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER));
+    t.addColumn("k2", TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER));
+    t.addColumn("k3", TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER));
+    t.addKey("k1");
+    t.addKey("k2", "k3");
+    assertThat(t.getKeys(), hasToString("[{0}, {1, 2}]"));
+  }
+
+  private static MockCatalogReader.MockTable newTable() {
+
+    MockCatalogReader catalogReader = new MockCatalogReader(TYPE_FACTORY, 
false) {
+      @Override public MockCatalogReader init() {
+        return this;
+      }
+    };
+    return new MockCatalogReader.MockTable(catalogReader, "catalog", "schema", 
"table", false,
+        false, 0.0, null, NullInitializerExpressionFactory.INSTANCE);
+  }
+}

Reply via email to