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

amashenkov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 19d1ff5a17 IGNITE-19304 Forbid creating index on the duplicate columns 
(#2174)
19d1ff5a17 is described below

commit 19d1ff5a17cff8b1b21c37fd8531fe671b574329
Author: Andrew V. Mashenkov <[email protected]>
AuthorDate: Mon Jun 12 17:17:15 2023 +0300

    IGNITE-19304 Forbid creating index on the duplicate columns (#2174)
---
 .../internal/catalog/CatalogServiceImpl.java       | 15 +++++++---
 .../internal/catalog/CatalogServiceSelfTest.java   | 32 ++++++++++++++++++++++
 .../java/org/apache/ignite/lang/ErrorGroups.java   |  3 ++
 .../internal/sql/api/ItSqlAsynchronousApiTest.java | 12 ++++++++
 .../internal/sql/api/ItSqlSynchronousApiTest.java  | 12 ++++++++
 .../sql/engine/exec/ddl/DdlCommandHandler.java     | 32 ++++++++++++++++++----
 6 files changed, 96 insertions(+), 10 deletions(-)

diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogServiceImpl.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogServiceImpl.java
index 133904021e..2852a1e652 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogServiceImpl.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogServiceImpl.java
@@ -288,6 +288,14 @@ public class CatalogServiceImpl extends 
Producer<CatalogEvent, CatalogEventParam
                 throw new TableAlreadyExistsException(schemaName, 
params.tableName());
             }
 
+            
params.columns().stream().map(ColumnParams::name).filter(Predicate.not(new 
HashSet<>()::add))
+                    .findAny().ifPresent(columnName -> {
+                        throw new IgniteInternalException(
+                                
ErrorGroups.Index.INVALID_INDEX_DEFINITION_ERR, "Can't create table with 
duplicate columns: "
+                                + 
params.columns().stream().map(ColumnParams::name).collect(Collectors.joining(", 
"))
+                        );
+                    });
+
             CatalogTableDescriptor table = 
CatalogUtils.fromParams(catalog.objectIdGenState(), params);
 
             return List.of(
@@ -395,8 +403,7 @@ public class CatalogServiceImpl extends 
Producer<CatalogEvent, CatalogEventParam
                             .ifPresent(columnName -> {
                                 throw new SqlException(
                                         Sql.DROP_IDX_COLUMN_CONSTRAINT_ERR,
-                                        "Can't drop indexed column: 
columnName=" + columnName + ", indexName="
-                                                + index.name()
+                                        "Can't drop indexed column: 
columnName=" + columnName + ", indexName=" + index.name()
                                 );
                             }));
 
@@ -520,7 +527,7 @@ public class CatalogServiceImpl extends 
Producer<CatalogEvent, CatalogEventParam
                 } else if (duplicateValidator.test(columnName)) {
                     throw new IgniteInternalException(
                             ErrorGroups.Index.INVALID_INDEX_DEFINITION_ERR,
-                            "Can't create index on duplicate columns: 
columnName=" + columnName
+                            "Can't create index on duplicate columns: " + 
String.join(", ", params.columns())
                     );
                 }
             }
@@ -574,7 +581,7 @@ public class CatalogServiceImpl extends 
Producer<CatalogEvent, CatalogEventParam
                 } else if (duplicateValidator.test(columnName)) {
                     throw new IgniteInternalException(
                             ErrorGroups.Index.INVALID_INDEX_DEFINITION_ERR,
-                            "Can't create index on duplicate columns: 
columnName=" + columnName
+                            "Can't create index on duplicate columns: " + 
String.join(", ", params.columns())
                     );
                 }
             }
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogServiceSelfTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogServiceSelfTest.java
index 2d09e3f26c..1894a49db5 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogServiceSelfTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogServiceSelfTest.java
@@ -267,6 +267,24 @@ public class CatalogServiceSelfTest {
         assertSame(schema, service.activeSchema(clock.nowLong()));
     }
 
+    @Test
+    public void testCreateTableWithDuplicateColumns() {
+        CreateTableParams params = CreateTableParams.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .zone(ZONE_NAME)
+                .columns(List.of(
+                        
ColumnParams.builder().name("key").type(ColumnType.INT32).build(),
+                        
ColumnParams.builder().name("val").type(ColumnType.INT32).build(),
+                        
ColumnParams.builder().name("val").type(ColumnType.INT32).nullable(true).build()
+                ))
+                .primaryKeyColumns(List.of("key"))
+                .build();
+
+        assertThat(service.createTable(params), 
willThrowFast(IgniteInternalException.class,
+                "Can't create table with duplicate columns: key, val, val"));
+    }
+
     @Test
     public void testDropTable() {
         assertThat(service.createTable(simpleTable(TABLE_NAME)), 
willBe((Object) null));
@@ -1045,6 +1063,20 @@ public class CatalogServiceSelfTest {
         assertThat(service.createIndex(params), 
willThrow(IndexAlreadyExistsException.class));
     }
 
+    @Test
+    public void testCreateIndexOnDuplicateColumns() {
+        assertThat(service.createTable(simpleTable(TABLE_NAME)), 
willBe((Object) null));
+
+        CreateHashIndexParams params = CreateHashIndexParams.builder()
+                .indexName(INDEX_NAME)
+                .tableName(TABLE_NAME)
+                .columns(List.of("VAL", "VAL"))
+                .build();
+
+        assertThat(service.createIndex(params),
+                willThrow(IgniteInternalException.class, "Can't create index 
on duplicate columns: VAL, VAL"));
+    }
+
     @Test
     public void operationWillBeRetriedFiniteAmountOfTimes() {
         UpdateLog updateLogMock = Mockito.mock(UpdateLog.class);
diff --git a/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java 
b/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java
index 9c14a0032a..d924c06b6d 100755
--- a/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java
+++ b/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java
@@ -65,6 +65,9 @@ public class ErrorGroups {
 
         /** Table is stopping. */
         public static final int TABLE_STOPPING_ERR = 
TABLE_ERR_GROUP.registerErrorCode(5);
+
+        /** Table definition is incorrect. */
+        public static final int TABLE_DEFINITION_ERR = 
TABLE_ERR_GROUP.registerErrorCode(6);
     }
 
     /** Client error group. */
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlAsynchronousApiTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlAsynchronousApiTest.java
index 6c70d570f8..cef5e436d1 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlAsynchronousApiTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlAsynchronousApiTest.java
@@ -132,6 +132,12 @@ public class ItSqlAsynchronousApiTest extends 
ClusterPerClassIntegrationTest {
                 ses,
                 "CREATE TABLE TEST(ID INT PRIMARY KEY, VAL0 INT)"
         );
+        checkError(
+                IgniteException.class,
+                "Can't create table with duplicate columns: ID, VAL, VAL",
+                ses,
+                "CREATE TABLE TEST1(ID INT PRIMARY KEY, VAL INT, VAL INT)"
+        );
         checkDdl(false, ses, "CREATE TABLE IF NOT EXISTS TEST(ID INT PRIMARY 
KEY, VAL VARCHAR)");
 
         // ADD COLUMN
@@ -167,6 +173,12 @@ public class ItSqlAsynchronousApiTest extends 
ClusterPerClassIntegrationTest {
         checkDdl(true, ses, "CREATE INDEX TEST_IDX1 ON TEST(VAL0)");
         checkDdl(true, ses, "CREATE INDEX TEST_IDX2 ON TEST(VAL0)");
         checkDdl(true, ses, "CREATE INDEX TEST_IDX3 ON TEST(ID, VAL0, VAL1)");
+        checkError(
+                SqlException.class,
+                "Can't create index on duplicate columns: VAL0, VAL0",
+                ses,
+                "CREATE INDEX TEST_IDX4 ON TEST(VAL0, VAL0)"
+        );
 
         checkError(
                 SqlException.class,
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlSynchronousApiTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlSynchronousApiTest.java
index 976ea3c716..13533f4825 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlSynchronousApiTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlSynchronousApiTest.java
@@ -114,6 +114,12 @@ public class ItSqlSynchronousApiTest extends 
ClusterPerClassIntegrationTest {
                 ses,
                 "CREATE TABLE TEST(ID INT PRIMARY KEY, VAL0 INT)"
         );
+        checkError(
+                SqlException.class,
+                "Can't create table with duplicate columns: ID, VAL, VAL",
+                ses,
+                "CREATE TABLE TEST1(ID INT PRIMARY KEY, VAL INT, VAL INT)"
+        );
         checkDdl(false, ses, "CREATE TABLE IF NOT EXISTS TEST(ID INT PRIMARY 
KEY, VAL VARCHAR)");
 
         // ADD COLUMN
@@ -149,6 +155,12 @@ public class ItSqlSynchronousApiTest extends 
ClusterPerClassIntegrationTest {
         checkDdl(true, ses, "CREATE INDEX TEST_IDX1 ON TEST(VAL0)");
         checkDdl(true, ses, "CREATE INDEX TEST_IDX2 ON TEST(VAL0)");
         checkDdl(true, ses, "CREATE INDEX TEST_IDX3 ON TEST(ID, VAL0, VAL1)");
+        checkError(
+                SqlException.class,
+                "Can't create index on duplicate columns: VAL0, VAL0",
+                ses,
+                "CREATE INDEX TEST_IDX4 ON TEST(VAL0, VAL0)"
+        );
 
         checkError(
                 SqlException.class,
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandler.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandler.java
index b5fe932de4..8f8b0b6b6d 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandler.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ddl/DdlCommandHandler.java
@@ -38,6 +38,7 @@ import java.util.concurrent.CompletionException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import org.apache.ignite.configuration.NamedListView;
 import 
org.apache.ignite.internal.distributionzones.DistributionZoneConfigurationParameters;
@@ -92,6 +93,8 @@ import org.apache.ignite.lang.ColumnAlreadyExistsException;
 import org.apache.ignite.lang.ColumnNotFoundException;
 import org.apache.ignite.lang.DistributionZoneAlreadyExistsException;
 import org.apache.ignite.lang.DistributionZoneNotFoundException;
+import org.apache.ignite.lang.ErrorGroups;
+import org.apache.ignite.lang.ErrorGroups.Table;
 import org.apache.ignite.lang.IgniteInternalCheckedException;
 import org.apache.ignite.lang.IgniteStringBuilder;
 import org.apache.ignite.lang.IgniteStringFormatter;
@@ -257,6 +260,15 @@ public class DdlCommandHandler {
 
     /** Handles create table command. */
     private CompletableFuture<Boolean> handleCreateTable(CreateTableCommand 
cmd) {
+        cmd.columns().stream()
+                .map(ColumnDefinition::name)
+                .filter(Predicate.not(new HashSet<>()::add))
+                .findAny()
+                .ifPresent(col -> {
+                    throw new SqlException(Table.TABLE_DEFINITION_ERR, "Can't 
create table with duplicate columns: "
+                            + 
cmd.columns().stream().map(ColumnDefinition::name).collect(Collectors.joining(",
 ")));
+                });
+
         Consumer<TableChange> tblChanger = tableChange -> {
             tableChange.changeColumns(columnsChange -> {
                 for (var col : cmd.columns()) {
@@ -284,7 +296,7 @@ public class DdlCommandHandler {
             zoneName = DEFAULT_ZONE_NAME;
         }
 
-        return tableManager.createTableAsync(cmd.tableName(), zoneName,  
tblChanger)
+        return tableManager.createTableAsync(cmd.tableName(), zoneName, 
tblChanger)
                 .thenApply(Objects::nonNull)
                 .handle(handleModificationResult(cmd.ifTableExists(), 
TableAlreadyExistsException.class));
     }
@@ -334,6 +346,14 @@ public class DdlCommandHandler {
 
     /** Handles create index command. */
     private CompletableFuture<Boolean> handleCreateIndex(CreateIndexCommand 
cmd) {
+        cmd.columns().stream()
+                .filter(Predicate.not(new HashSet<>()::add))
+                .findAny()
+                .ifPresent(col -> {
+                    throw new 
SqlException(ErrorGroups.Index.INVALID_INDEX_DEFINITION_ERR,
+                            "Can't create index on duplicate columns: " + 
String.join(", ", cmd.columns()));
+                });
+
         Consumer<TableIndexChange> indexChanger = tableIndexChange -> {
             switch (cmd.type()) {
                 case SORTED:
@@ -350,11 +370,11 @@ public class DdlCommandHandler {
         };
 
         return indexManager.createIndexAsync(
-                        cmd.schemaName(),
-                        cmd.indexName(),
-                        cmd.tableName(),
-                        !cmd.ifNotExists(),
-                        indexChanger);
+                cmd.schemaName(),
+                cmd.indexName(),
+                cmd.tableName(),
+                !cmd.ifNotExists(),
+                indexChanger);
     }
 
     /** Handles drop index command. */

Reply via email to