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

korlov 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 4fafbd01162 IGNITE-28040 Improve test coverage for alter table add 
columns (#7725)
4fafbd01162 is described below

commit 4fafbd01162a230e89d94371908d3d747542c148
Author: Ivan Zlenko <[email protected]>
AuthorDate: Wed Mar 11 11:54:15 2026 +0500

    IGNITE-28040 Improve test coverage for alter table add columns (#7725)
---
 .../ignite/internal/catalog/CatalogTableTest.java  | 417 +++++++++++++++++++++
 .../internal/sql/engine/sql/SqlDdlParserTest.java  | 188 ++++++++++
 2 files changed, 605 insertions(+)

diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogTableTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogTableTest.java
index 8d9f6d1d876..090e38fd8b4 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogTableTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogTableTest.java
@@ -83,11 +83,14 @@ import it.unimi.dsi.fastutil.ints.IntList;
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
+import org.apache.ignite.internal.catalog.commands.AlterTableAddColumnCommand;
 import 
org.apache.ignite.internal.catalog.commands.AlterTableAlterColumnCommand;
 import 
org.apache.ignite.internal.catalog.commands.AlterTableAlterColumnCommandBuilder;
+import org.apache.ignite.internal.catalog.commands.AlterTableDropColumnCommand;
 import 
org.apache.ignite.internal.catalog.commands.AlterTableSetPropertyCommand;
 import org.apache.ignite.internal.catalog.commands.CatalogUtils;
 import org.apache.ignite.internal.catalog.commands.ColumnParams;
@@ -513,6 +516,79 @@ public class CatalogTableTest extends 
BaseCatalogManagerTest {
         );
     }
 
+    @Test
+    public void testDropMultipleColumns() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        CatalogTableDescriptor tableBefore = actualTable(TABLE_NAME);
+        int colCountBefore = tableBefore.columns().size();
+
+        tryApplyAndExpectApplied(dropColumnParams(TABLE_NAME, "VAL", "DEC", 
"STR"));
+
+        CatalogTableDescriptor table = actualTable(TABLE_NAME);
+
+        assertNull(table.column("VAL"));
+        assertNull(table.column("DEC"));
+        assertNull(table.column("STR"));
+        assertEquals(colCountBefore - 3, table.columns().size());
+    }
+
+    @Test
+    public void testDropMultipleColumnsWithMissingColumn() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        assertThat(
+                manager.execute(dropColumnParams(TABLE_NAME, "VAL", "fake")),
+                willThrowFast(CatalogValidationException.class, "Column with 
name 'fake' not found in table 'PUBLIC.test_table'.")
+        );
+
+        // Validate no column was dropped.
+        CatalogTableDescriptor table = actualTable(TABLE_NAME);
+        assertNotNull(table.column("VAL"));
+    }
+
+    @Test
+    public void testDropMultipleColumnsWithPrimaryKeyColumn() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        assertThat(
+                manager.execute(dropColumnParams(TABLE_NAME, "VAL", "ID")),
+                willThrowFast(CatalogValidationException.class, "Deleting 
column `ID` belonging to primary key is not allowed")
+        );
+
+        // Validate no column was dropped.
+        CatalogTableDescriptor table = actualTable(TABLE_NAME);
+        assertNotNull(table.column("VAL"));
+        assertNotNull(table.column("ID"));
+    }
+
+    @Test
+    public void testDropMultipleColumnsWithIndexColumn() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+        tryApplyAndExpectApplied(simpleIndex());
+
+        assertThat(
+                manager.execute(dropColumnParams(TABLE_NAME, "VAL", "DEC")),
+                willThrowFast(
+                        CatalogValidationException.class,
+                        "Deleting column 'VAL' used by index(es) [myIndex], it 
is not allowed"
+                )
+        );
+
+        // Validate no column was dropped.
+        CatalogTableDescriptor table = actualTable(TABLE_NAME);
+        assertNotNull(table.column("VAL"));
+        assertNotNull(table.column("DEC"));
+    }
+
+    @Test
+    public void testDropMultipleColumnsFromNonExistingTable() {
+        assertThat(
+                manager.execute(dropColumnParams(TABLE_NAME, "VAL", "DEC")),
+                willThrowFast(CatalogValidationException.class, "Table with 
name 'PUBLIC.test_table' not found.")
+        );
+    }
+
     @Test
     public void testAddColumnWithNotExistingTable() {
         assertThat(manager.execute(addColumnParams(TABLE_NAME, 
columnParams("key", INT32, true))),
@@ -1534,6 +1610,347 @@ public class CatalogTableTest extends 
BaseCatalogManagerTest {
         }
     }
 
+    @Test
+    public void testAddMultipleColumnsAssignsSequentialIds() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        CatalogTableDescriptor tableBefore = actualTable(TABLE_NAME);
+        assertNotNull(tableBefore);
+        int maxId = 
tableBefore.columns().stream().mapToInt(CatalogTableColumnDescriptor::id).max().orElse(0);
+
+        tryApplyAndExpectApplied(addColumnParams(TABLE_NAME,
+                columnParams("COL_A", INT32, true),
+                columnParams("COL_B", STRING, 100, true),
+                columnParams("COL_C", DECIMAL, true, DFLT_TEST_PRECISION, 2)
+        ));
+
+        CatalogTableDescriptor table = actualTable(TABLE_NAME);
+        assertNotNull(table);
+        assertEquals(maxId + 1, table.column("COL_A").id());
+        assertEquals(maxId + 2, table.column("COL_B").id());
+        assertEquals(maxId + 3, table.column("COL_C").id());
+    }
+
+    @Test
+    public void testAddMultipleColumnsPreservesProperties() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        tryApplyAndExpectApplied(addColumnParams(TABLE_NAME,
+                columnParamsBuilder("COL_INT", INT32, 
true).defaultValue(constant(42)).build(),
+                columnParamsBuilder("COL_STR", 
STRING).length(200).defaultValue(constant("hello")).build(),
+                columnParams("COL_DEC", DECIMAL, true, 15, 5)
+        ));
+
+        CatalogTableDescriptor table = actualTable(TABLE_NAME);
+        assertNotNull(table);
+
+        CatalogTableColumnDescriptor colInt = table.column("COL_INT");
+        assertNotNull(colInt);
+        assertEquals(INT32, colInt.type());
+        assertTrue(colInt.nullable());
+        assertEquals(constant(42), colInt.defaultValue());
+
+        CatalogTableColumnDescriptor colStr = table.column("COL_STR");
+        assertNotNull(colStr);
+        assertEquals(STRING, colStr.type());
+        assertEquals(200, colStr.length());
+        assertEquals(constant("hello"), colStr.defaultValue());
+
+        CatalogTableColumnDescriptor colDec = table.column("COL_DEC");
+        assertNotNull(colDec);
+        assertEquals(DECIMAL, colDec.type());
+        assertTrue(colDec.nullable());
+        assertEquals(15, colDec.precision());
+        assertEquals(5, colDec.scale());
+    }
+
+    @Test
+    public void testAddMultipleColumnsPreservesOrder() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        tryApplyAndExpectApplied(addColumnParams(TABLE_NAME,
+                columnParams("COL_A", INT32, true),
+                columnParams("COL_B", INT32, true),
+                columnParams("COL_C", INT32, true)
+        ));
+
+        CatalogTableDescriptor table = actualTable(TABLE_NAME);
+        assertNotNull(table);
+        List<CatalogTableColumnDescriptor> columns = table.columns();
+
+        // simpleTable creates 6 columns, new ones should be at indices 6, 7, 8
+        assertEquals("COL_A", columns.get(6).name());
+        assertEquals("COL_B", columns.get(7).name());
+        assertEquals("COL_C", columns.get(8).name());
+
+        assertEquals(6, table.columnIndex("COL_A"));
+        assertEquals(7, table.columnIndex("COL_B"));
+        assertEquals(8, table.columnIndex("COL_C"));
+    }
+
+    @Test
+    public void testAddMultipleColumnsIncrementsTableVersionOnce() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        CatalogTableDescriptor tableBefore = actualTable(TABLE_NAME);
+        assertNotNull(tableBefore);
+        assertEquals(1, tableBefore.latestSchemaVersion());
+
+        tryApplyAndExpectApplied(addColumnParams(TABLE_NAME,
+                columnParams("COL_A", INT32, true),
+                columnParams("COL_B", INT32, true),
+                columnParams("COL_C", INT32, true)
+        ));
+
+        CatalogTableDescriptor tableAfter = actualTable(TABLE_NAME);
+        assertNotNull(tableAfter);
+        assertEquals(2, tableAfter.latestSchemaVersion());
+    }
+
+    @Test
+    public void testAddMultipleColumnsTimeTravelVisibility() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        long timestampBeforeAdd = clock.nowLong();
+
+        tryApplyAndExpectApplied(addColumnParams(TABLE_NAME,
+                columnParams("COL_A", INT32, true),
+                columnParams("COL_B", INT32, true)
+        ));
+
+        // At the old timestamp, the new columns should not be visible.
+        CatalogTableDescriptor oldTable = 
manager.activeCatalog(timestampBeforeAdd).table(SCHEMA_NAME, TABLE_NAME);
+        assertNotNull(oldTable);
+        assertNull(oldTable.column("COL_A"));
+        assertNull(oldTable.column("COL_B"));
+
+        // At the latest timestamp, the new columns should be visible.
+        CatalogTableDescriptor newTable = actualTable(TABLE_NAME);
+        assertNotNull(newTable);
+        assertNotNull(newTable.column("COL_A"));
+        assertNotNull(newTable.column("COL_B"));
+    }
+
+    @Test
+    public void testAddMultipleColumnsFiresEventWithAllColumns() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        var fireEventFuture = new CompletableFuture<Void>();
+
+        manager.listen(CatalogEvent.TABLE_ALTER, fromConsumer(fireEventFuture, 
(AddColumnEventParameters parameters) -> {
+            List<CatalogTableColumnDescriptor> descriptors = 
parameters.descriptors();
+
+            assertThat(descriptors, hasSize(2));
+            assertEquals("COL_A", descriptors.get(0).name());
+            assertEquals("COL_B", descriptors.get(1).name());
+        }));
+
+        tryApplyAndExpectApplied(addColumnParams(TABLE_NAME,
+                columnParams("COL_A", INT32, true),
+                columnParams("COL_B", INT32, true)
+        ));
+
+        assertThat(fireEventFuture, willCompleteSuccessfully());
+    }
+
+    @Test
+    public void testAddSingleColumnIfNotExistsColumnExists() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        CatalogCommand command = AlterTableAddColumnCommand.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(List.of(columnParams("VAL", INT32, true)))
+                .ifColumnNotExists(true)
+                .build();
+
+        tryApplyAndExpectNotApplied(command);
+    }
+
+    @Test
+    public void testAddSingleColumnIfNotExistsColumnDoesNotExist() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        CatalogCommand command = AlterTableAddColumnCommand.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(List.of(columnParams(NEW_COLUMN_NAME, INT32, true)))
+                .ifColumnNotExists(true)
+                .build();
+
+        tryApplyAndExpectApplied(command);
+
+        CatalogTableDescriptor table = actualTable(TABLE_NAME);
+        assertNotNull(table);
+        assertNotNull(table.column(NEW_COLUMN_NAME));
+    }
+
+    @Test
+    public void testAddMultipleColumnsIfNotExistsAllNew() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        CatalogCommand command = AlterTableAddColumnCommand.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(List.of(
+                        columnParams(NEW_COLUMN_NAME, INT32, true),
+                        columnParams(NEW_COLUMN_NAME_2, INT32, true)
+                ))
+                .ifColumnNotExists(true)
+                .build();
+
+        tryApplyAndExpectApplied(command);
+
+        CatalogTableDescriptor table = actualTable(TABLE_NAME);
+        assertNotNull(table);
+        assertNotNull(table.column(NEW_COLUMN_NAME));
+        assertNotNull(table.column(NEW_COLUMN_NAME_2));
+    }
+
+    @Test
+    public void testAddMultipleColumnsIfNotExistsPartialOverlap() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        CatalogCommand command = AlterTableAddColumnCommand.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(List.of(
+                        columnParams("VAL", INT32, true),
+                        columnParams(NEW_COLUMN_NAME, INT32, true)
+                ))
+                .ifColumnNotExists(true)
+                .build();
+
+        tryApplyAndExpectApplied(command);
+
+        CatalogTableDescriptor table = actualTable(TABLE_NAME);
+        assertNotNull(table);
+        assertNotNull(table.column(NEW_COLUMN_NAME));
+        // VAL should still have its original type (INT32) and be unchanged.
+        assertNotNull(table.column("VAL"));
+    }
+
+    @Test
+    public void testAddMultipleColumnsIfNotExistsAllExist() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        CatalogCommand command = AlterTableAddColumnCommand.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(List.of(
+                        columnParams("VAL", INT32, true),
+                        columnParams("STR", STRING, 100, true)
+                ))
+                .ifColumnNotExists(true)
+                .build();
+
+        tryApplyAndExpectNotApplied(command);
+    }
+
+    @Test
+    public void testAddMultipleColumnsFailsAtomicallyWhenOneAlreadyExists() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        // Try to add columns where "VAL" already exists — without 
ifColumnNotExists, this should fail.
+        assertThat(
+                manager.execute(addColumnParams(TABLE_NAME,
+                        columnParams(NEW_COLUMN_NAME, INT32, true),
+                        columnParams("VAL", INT32, true)
+                )),
+                willThrow(CatalogValidationException.class)
+        );
+
+        // Verify NEWCOL was NOT added (atomic rollback).
+        CatalogTableDescriptor table = actualTable(TABLE_NAME);
+        assertNotNull(table);
+        assertNull(table.column(NEW_COLUMN_NAME));
+    }
+
+    @Test
+    public void testDropMultipleColumnsIfExistsAllExist() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        CatalogCommand command = AlterTableDropColumnCommand.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(Set.of("VAL", "DEC"))
+                .ifColumnExists(true)
+                .build();
+
+        tryApplyAndExpectApplied(command);
+
+        CatalogTableDescriptor table = actualTable(TABLE_NAME);
+        assertNull(table.column("VAL"));
+        assertNull(table.column("DEC"));
+    }
+
+    @Test
+    public void testDropMultipleColumnsIfExistsPartialOverlap() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        CatalogCommand command = AlterTableDropColumnCommand.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(Set.of("VAL", "fake"))
+                .ifColumnExists(true)
+                .build();
+
+        tryApplyAndExpectApplied(command);
+
+        CatalogTableDescriptor table = actualTable(TABLE_NAME);
+        assertNull(table.column("VAL"));
+    }
+
+    @Test
+    public void testDropMultipleColumnsIfExistsNoneExist() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        CatalogCommand command = AlterTableDropColumnCommand.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(Set.of("fake1", "fake2"))
+                .ifColumnExists(true)
+                .build();
+
+        tryApplyAndExpectNotApplied(command);
+    }
+
+    @Test
+    public void testDropSingleColumnIfExists() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        CatalogCommand command = AlterTableDropColumnCommand.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(Set.of("fake"))
+                .ifColumnExists(true)
+                .build();
+
+        tryApplyAndExpectNotApplied(command);
+
+        // Verify table is unchanged.
+        CatalogTableDescriptor table = actualTable(TABLE_NAME);
+        assertEquals(6, table.columns().size());
+    }
+
+    @Test
+    public void testDropSingleColumnIfExistsColumnExists() {
+        tryApplyAndExpectApplied(simpleTable(TABLE_NAME));
+
+        CatalogCommand command = AlterTableDropColumnCommand.builder()
+                .schemaName(SCHEMA_NAME)
+                .tableName(TABLE_NAME)
+                .columns(Set.of("VAL"))
+                .ifColumnExists(true)
+                .build();
+
+        tryApplyAndExpectApplied(command);
+
+        CatalogTableDescriptor table = actualTable(TABLE_NAME);
+        assertNull(table.column("VAL"));
+        assertEquals(5, table.columns().size());
+    }
+
     private @Nullable CatalogTableDescriptor actualTable(String tableName) {
         return manager.activeCatalog(clock.nowLong()).table(SCHEMA_NAME, 
tableName);
     }
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlDdlParserTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlDdlParserTest.java
index 3eb7745d6fa..3b3914d47d8 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlDdlParserTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlDdlParserTest.java
@@ -796,6 +796,87 @@ public class SqlDdlParserTest extends AbstractParserTest {
                 () -> parse(stmt));
     }
 
+    @ParameterizedTest
+    @CsvSource(delimiter = ';', value = {
+            // ADD COLUMN missing column definition
+            "alter table t add column;"
+                    + " Failed to parse query: Encountered \"<EOF>\" at line 
1, column 24",
+            "alter table t add;"
+                    + " Failed to parse query: Encountered \"<EOF>\" at line 
1, column 17",
+            "alter table t add column if not exists;"
+                    + " Failed to parse query: Encountered \"<EOF>\" at line 
1, column 38",
+            "alter table t add if not exists;"
+                    + " Failed to parse query: Encountered \"<EOF>\" at line 
1, column 31",
+
+            // ADD COLUMN with wrong IF EXISTS (should be IF NOT EXISTS)
+            "alter table t add column if exists c int;"
+                    + " Failed to parse query: Encountered \"exists\" at line 
1, column 29",
+            "alter table if exists t add column if exists c int;"
+                    + " Failed to parse query: Encountered \"exists\" at line 
1, column 39",
+
+            // DROP COLUMN missing column name
+            "alter table t drop column;"
+                    + " Failed to parse query: Encountered \"<EOF>\" at line 
1, column 25",
+            "alter table t drop;"
+                    + " Failed to parse query: Encountered \"<EOF>\" at line 
1, column 18",
+            "alter table t drop column if exists;"
+                    + " Failed to parse query: Encountered \"<EOF>\" at line 
1, column 35",
+            "alter table t drop if exists;"
+                    + " Failed to parse query: Encountered \"<EOF>\" at line 
1, column 28",
+
+            // DROP COLUMN with wrong IF NOT EXISTS (should be IF EXISTS)
+            "alter table t drop column if not exists c;"
+                    + " Failed to parse query: Encountered \"not\" at line 1, 
column 30",
+            "alter table if exists t drop column if not exists c;"
+                    + " Failed to parse query: Encountered \"not\" at line 1, 
column 40",
+
+            // ADD single column with reserved word as name
+            "alter table t add column select int;"
+                    + " Failed to parse query: Encountered \"select\" at line 
1, column 26",
+            "alter table t add select int;"
+                    + " Failed to parse query: Encountered \"select\" at line 
1, column 19",
+            "alter table t add column if not exists select int;"
+                    + " Failed to parse query: Encountered \"select\" at line 
1, column 40",
+
+            // ADD multiple columns with reserved word as name
+            "alter table t add column (select int, b varchar);"
+                    + " Failed to parse query: Encountered \"select\" at line 
1, column 27",
+            "alter table t add column (a int, select varchar);"
+                    + " Failed to parse query: Encountered \"select\" at line 
1, column 34",
+            "alter table t add column if not exists (select int, b varchar);"
+                    + " Failed to parse query: Encountered \"select\" at line 
1, column 41",
+            "alter table t add (select int, b varchar);"
+                    + " Failed to parse query: Encountered \"select\" at line 
1, column 20",
+
+            // ADD column with missing type
+            "alter table t add column a;"
+                    + " Failed to parse query: Encountered \"<EOF>\" at line 
1, column 26",
+            "alter table t add column (a, b varchar);"
+                    + " Failed to parse query: Encountered \",\" at line 1, 
column 28",
+            "alter table t add column (a int, b);"
+                    + " Failed to parse query: Encountered \")\" at line 1, 
column 35",
+
+            // DROP column with reserved word as name
+            "alter table t drop column select;"
+                    + " Failed to parse query: Encountered \"select\" at line 
1, column 27",
+            "alter table t drop select;"
+                    + " Failed to parse query: Encountered \"select\" at line 
1, column 20",
+            "alter table t drop column if exists select;"
+                    + " Failed to parse query: Encountered \"select\" at line 
1, column 37",
+
+            // DROP multiple columns with reserved word as name
+            "alter table t drop column (select, b);"
+                    + " Failed to parse query: Encountered \"select\" at line 
1, column 28",
+            "alter table t drop column (a, select);"
+                    + " Failed to parse query: Encountered \"select\" at line 
1, column 31",
+    })
+    public void alterTableAddDropColumnParsingErrors(String stmt, String 
error) {
+        assertThrowsSqlException(
+                Sql.STMT_PARSE_ERR,
+                error,
+                () -> parse(stmt));
+    }
+
     @Test
     public void createIndexSimpleCase() {
         var query = "create index my_index on my_table (col)";
@@ -1169,6 +1250,113 @@ public class SqlDdlParserTest extends 
AbstractParserTest {
         expectUnparsed(addColumn, "ALTER TABLE \"T\" ADD COLUMN \"C\" INTEGER 
NOT NULL");
     }
 
+    @Test
+    public void alterTableAddMultipleColumns() {
+        SqlNode sqlNode = parse("ALTER TABLE t ADD COLUMN (a INT, b VARCHAR, c 
DECIMAL(10, 2))");
+
+        IgniteSqlAlterTableAddColumn addColumn = 
assertInstanceOf(IgniteSqlAlterTableAddColumn.class, sqlNode);
+
+        assertThat(addColumn.name.names, is(List.of("T")));
+        assertThat(addColumn.ifColumnNotExists(), is(false));
+        assertThat(addColumn.columns().size(), is(3));
+
+        SqlColumnDeclaration col0 = (SqlColumnDeclaration) 
addColumn.columns().get(0);
+        SqlColumnDeclaration col1 = (SqlColumnDeclaration) 
addColumn.columns().get(1);
+        SqlColumnDeclaration col2 = (SqlColumnDeclaration) 
addColumn.columns().get(2);
+
+        expectColumnBasic(col0, "A", ColumnStrategy.NULLABLE, "INTEGER", true);
+        expectColumnBasic(col1, "B", ColumnStrategy.NULLABLE, "VARCHAR", true);
+        expectColumnBasic(col2, "C", ColumnStrategy.NULLABLE, "DECIMAL", true);
+
+        expectUnparsed(addColumn, "ALTER TABLE \"T\" ADD COLUMN \"A\" INTEGER, 
\"B\" VARCHAR, \"C\" DECIMAL(10, 2)");
+    }
+
+    @Test
+    public void alterTableAddMultipleColumnsIfNotExists() {
+        SqlNode sqlNode = parse("ALTER TABLE t ADD COLUMN IF NOT EXISTS (a 
INT, b VARCHAR NOT NULL)");
+
+        IgniteSqlAlterTableAddColumn addColumn = 
assertInstanceOf(IgniteSqlAlterTableAddColumn.class, sqlNode);
+
+        assertThat(addColumn.name.names, is(List.of("T")));
+        assertThat(addColumn.ifColumnNotExists(), is(true));
+        assertThat(addColumn.columns().size(), is(2));
+
+        SqlColumnDeclaration col0 = (SqlColumnDeclaration) 
addColumn.columns().get(0);
+        SqlColumnDeclaration col1 = (SqlColumnDeclaration) 
addColumn.columns().get(1);
+
+        expectColumnBasic(col0, "A", ColumnStrategy.NULLABLE, "INTEGER", true);
+        expectColumnBasic(col1, "B", ColumnStrategy.NOT_NULLABLE, "VARCHAR", 
false);
+
+        expectUnparsed(addColumn, "ALTER TABLE \"T\" ADD COLUMN IF NOT EXISTS 
\"A\" INTEGER, \"B\" VARCHAR NOT NULL");
+    }
+
+    @Test
+    public void alterTableAddMultipleColumnsWithDefaults() {
+        SqlNode sqlNode = parse("ALTER TABLE t ADD COLUMN (a INT DEFAULT 1, b 
VARCHAR DEFAULT 'hello')");
+
+        IgniteSqlAlterTableAddColumn addColumn = 
assertInstanceOf(IgniteSqlAlterTableAddColumn.class, sqlNode);
+
+        assertThat(addColumn.columns().size(), is(2));
+
+        SqlColumnDeclaration col0 = (SqlColumnDeclaration) 
addColumn.columns().get(0);
+        SqlColumnDeclaration col1 = (SqlColumnDeclaration) 
addColumn.columns().get(1);
+
+        assertNotNull(col0.expression);
+        assertNotNull(col1.expression);
+
+        expectUnparsed(addColumn, "ALTER TABLE \"T\" ADD COLUMN \"A\" INTEGER 
DEFAULT (1), \"B\" VARCHAR DEFAULT ('hello')");
+    }
+
+    @Test
+    public void alterTableAddMultipleColumnsWithoutColumnKeyword() {
+        SqlNode sqlNode = parse("ALTER TABLE t ADD (a INT, b VARCHAR)");
+
+        IgniteSqlAlterTableAddColumn addColumn = 
assertInstanceOf(IgniteSqlAlterTableAddColumn.class, sqlNode);
+
+        assertThat(addColumn.name.names, is(List.of("T")));
+        assertThat(addColumn.columns().size(), is(2));
+
+        SqlColumnDeclaration col0 = (SqlColumnDeclaration) 
addColumn.columns().get(0);
+        SqlColumnDeclaration col1 = (SqlColumnDeclaration) 
addColumn.columns().get(1);
+
+        expectColumnBasic(col0, "A", ColumnStrategy.NULLABLE, "INTEGER", true);
+        expectColumnBasic(col1, "B", ColumnStrategy.NULLABLE, "VARCHAR", true);
+
+        expectUnparsed(addColumn, "ALTER TABLE \"T\" ADD COLUMN \"A\" INTEGER, 
\"B\" VARCHAR");
+    }
+
+    @Test
+    public void alterTableAddMultipleColumnsWithoutColumnKeywordIfNotExists() {
+        SqlNode sqlNode = parse("ALTER TABLE t ADD IF NOT EXISTS (a INT, b 
VARCHAR)");
+
+        IgniteSqlAlterTableAddColumn addColumn = 
assertInstanceOf(IgniteSqlAlterTableAddColumn.class, sqlNode);
+
+        assertThat(addColumn.name.names, is(List.of("T")));
+        assertThat(addColumn.ifColumnNotExists(), is(true));
+        assertThat(addColumn.columns().size(), is(2));
+
+        SqlColumnDeclaration col0 = (SqlColumnDeclaration) 
addColumn.columns().get(0);
+        SqlColumnDeclaration col1 = (SqlColumnDeclaration) 
addColumn.columns().get(1);
+
+        expectColumnBasic(col0, "A", ColumnStrategy.NULLABLE, "INTEGER", true);
+        expectColumnBasic(col1, "B", ColumnStrategy.NULLABLE, "VARCHAR", true);
+
+        expectUnparsed(addColumn, "ALTER TABLE \"T\" ADD COLUMN IF NOT EXISTS 
\"A\" INTEGER, \"B\" VARCHAR");
+    }
+
+    @Test
+    public void alterTableAddMultipleColumnsIfTableExists() {
+        SqlNode sqlNode = parse("ALTER TABLE IF EXISTS t ADD COLUMN (a INT, b 
VARCHAR)");
+
+        IgniteSqlAlterTableAddColumn addColumn = 
assertInstanceOf(IgniteSqlAlterTableAddColumn.class, sqlNode);
+
+        assertThat(addColumn.name.names, is(List.of("T")));
+        assertThat(addColumn.ifExists(), is(true));
+        assertThat(addColumn.columns().size(), is(2));
+
+        expectUnparsed(addColumn, "ALTER TABLE IF EXISTS \"T\" ADD COLUMN 
\"A\" INTEGER, \"B\" VARCHAR");
+    }
+
     @Test
     public void alterTableDropColumn() {
         SqlNode sqlNode = parse("ALTER TABLE t DROP COLUMN c");

Reply via email to