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

alexpl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 149e094ffac IGNITE-20353 SQL Calcite: Fix CREATE TABLE ... WITH 
affinity_key option validation - Fixes #10925.
149e094ffac is described below

commit 149e094ffac37bf4a42e4a62f6eac98185a9857b
Author: Aleksey Plekhanov <[email protected]>
AuthorDate: Thu Sep 14 12:28:54 2023 +0300

    IGNITE-20353 SQL Calcite: Fix CREATE TABLE ... WITH affinity_key option 
validation - Fixes #10925.
    
    Signed-off-by: Aleksey Plekhanov <[email protected]>
---
 .../prepare/ddl/DdlSqlToCommandConverter.java      | 46 +++++++++++++++
 .../integration/TableDdlIntegrationTest.java       | 66 +++++++++++++++++++++-
 2 files changed, 111 insertions(+), 1 deletion(-)

diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlSqlToCommandConverter.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlSqlToCommandConverter.java
index 1c8121445ff..c2c4f521d14 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlSqlToCommandConverter.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlSqlToCommandConverter.java
@@ -316,6 +316,52 @@ public class DdlSqlToCommandConverter {
                 IgniteQueryErrorCode.PARSING);
         }
 
+        // Validate affinity key.
+        if (createTblCmd.affinityKey() != null) {
+            String affColName = null;
+            String val = createTblCmd.affinityKey();
+
+            if (val.startsWith("'")) {
+                if (val.length() == 1 || !val.endsWith("'")) {
+                    throw new IgniteSQLException("Affinity key column name 
does not have trailing quote: " + val,
+                        IgniteQueryErrorCode.PARSING);
+                }
+
+                val = val.substring(1, val.length() - 1);
+
+                if (F.isEmpty(val))
+                    throw new IgniteSQLException("Affinity key cannot be 
empty", IgniteQueryErrorCode.PARSING);
+
+                affColName = val;
+            }
+            else {
+                for (ColumnDefinition col : createTblCmd.columns()) {
+                    if (col.name().equalsIgnoreCase(val)) {
+                        if (affColName != null) {
+                            throw new IgniteSQLException("Ambiguous affinity 
column name, use single quotes " +
+                                "for case sensitivity: " + val, 
IgniteQueryErrorCode.PARSING);
+                        }
+
+                        affColName = col.name();
+                    }
+                }
+            }
+
+            String affColFinal = affColName;
+
+            if (affColName == null || 
createTblCmd.columns().stream().noneMatch(col -> 
affColFinal.equals(col.name()))) {
+                throw new IgniteSQLException("Affinity key column with given 
name not found: " + val,
+                    IgniteQueryErrorCode.PARSING);
+            }
+
+            if (!createTblCmd.primaryKeyColumns().contains(affColName)) {
+                throw new IgniteSQLException("Affinity key column must be one 
of key columns: " + affColName,
+                    IgniteQueryErrorCode.PARSING);
+            }
+
+            createTblCmd.affinityKey(affColName);
+        }
+
         return createTblCmd;
     }
 
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/TableDdlIntegrationTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/TableDdlIntegrationTest.java
index dc51b74eb75..f385132a7ca 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/TableDdlIntegrationTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/TableDdlIntegrationTest.java
@@ -37,7 +37,10 @@ import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.processors.query.QueryUtils;
+import 
org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessor;
 import org.apache.ignite.internal.processors.query.calcite.QueryChecker;
+import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable;
+import 
org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistribution;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.hamcrest.CustomMatcher;
@@ -229,7 +232,7 @@ public class TableDdlIntegrationTest extends 
AbstractDdlIntegrationTest {
         assertThat(ccfg.getGroupName(), equalTo("my_cache_group"));
         assertThat(ccfg.getName(), equalTo("my_cache_name"));
         assertThat(ccfg.getDataRegionName(), equalTo(DATA_REGION_NAME));
-        assertThat(ccfg.getKeyConfiguration()[0].getAffinityKeyFieldName(), 
equalTo("id2"));
+        assertThat(ccfg.getKeyConfiguration()[0].getAffinityKeyFieldName(), 
equalTo("ID2"));
 
         QueryEntity ent = ccfg.getQueryEntities().iterator().next();
 
@@ -267,6 +270,67 @@ public class TableDdlIntegrationTest extends 
AbstractDdlIntegrationTest {
             ccfg -> "PART".equals(ccfg.getName()) && ccfg.getCacheMode() == 
CacheMode.PARTITIONED)));
     }
 
+    /**
+     * Creates table with affinity key.
+     */
+    @Test
+    public void createTableWithAffinity() {
+        assertThrows("create table TBL0 (id1 int, id2 int, val varchar, 
primary key(id1, id2)) with " +
+            "\"affinity_key=''\"", IgniteSQLException.class, "Affinity key 
cannot be empty");
+
+        assertThrows("create table TBL0 (id1 int, id2 int, val varchar, 
primary key(id1, id2)) with " +
+            "\"affinity_key='id\"", IgniteSQLException.class, "Affinity key 
column name does not have trailing quote");
+
+        assertThrows("create table TBL0 (id1 int, id2 int, val varchar, 
primary key(id1, id2)) with " +
+            "affinity_key=\"id3\"", IgniteSQLException.class, "Affinity key 
column with given name not found: id3");
+
+        assertThrows("create table TBL0 (id1 int, id2 int, val varchar, 
primary key(id1, id2)) with " +
+            "affinity_key=val", IgniteSQLException.class, "Affinity key column 
must be one of key columns: VAL");
+
+        // Wrong case of quoted 'id1'.
+        assertThrows("create table TBL0 (id1 int, id2 int, val varchar, 
primary key(id1, id2)) with " +
+            "\"affinity_key='id1'\"", IgniteSQLException.class, "Affinity key 
column with given name not found: id1");
+
+        assertThrows("create table TBL0 (\"id\" int, \"iD\" int, val varchar, 
primary key(\"id\", \"iD\")) with " +
+            " \"affinity_key=id\"", IgniteSQLException.class, "Ambiguous 
affinity column name");
+
+        sql("create table TBL1 (id1 int, id2 int, val varchar, primary 
key(id1, id2)) with " +
+            " affinity_key=id1");
+
+        checkAffinity("TBL1", "ID1");
+
+        sql("create table TBL2 (\"id1\" int, id2 int, val varchar, primary 
key(\"id1\", id2)) with " +
+            " affinity_key=\"id1\"");
+
+        checkAffinity("TBL2", "id1");
+
+        sql("create table TBL3 (\"id1\" int, id2 int, val varchar, primary 
key(\"id1\", id2)) with " +
+            " \"affinity_key='id1'\"");
+
+        checkAffinity("TBL3", "id1");
+
+        sql("create table TBL4 (id1 int, \"iD2\" int, val varchar, primary 
key(id1, \"iD2\")) with " +
+            " affinity_key=id2");
+
+        checkAffinity("TBL4", "iD2");
+
+        sql("create table TBL5 (\"id\" int, \"iD\" int, val varchar, primary 
key(\"id\", \"iD\")) with " +
+            " \"affinity_key='iD'\"");
+
+        checkAffinity("TBL5", "iD");
+    }
+
+    /** */
+    private void checkAffinity(String tableName, String affCol) {
+        CalciteQueryProcessor proc = queryProcessor(client);
+
+        IgniteTable tbl = 
(IgniteTable)proc.schemaHolder().schema(QueryUtils.DFLT_SCHEMA).getTable(tableName);
+        IgniteDistribution distr = tbl.distribution();
+
+        assertEquals(1, distr.getKeys().size());
+        assertEquals(tbl.descriptor().columnDescriptor(affCol).fieldIndex(), 
(int)distr.getKeys().get(0));
+    }
+
     /**
      * Tries to create several tables with the same name.
      */

Reply via email to