You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by tl...@apache.org on 2022/03/15 10:18:50 UTC

[ignite-3] branch main updated: IGNITE-16591 DDL syntax COLOCATE BY (#711)

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

tledkov 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 8d08ada  IGNITE-16591 DDL syntax COLOCATE BY (#711)
8d08ada is described below

commit 8d08ada8e77a5130778a2f5d04d11e95e0aad91e
Author: Taras Ledkov <tl...@gridgain.com>
AuthorDate: Tue Mar 15 13:18:42 2022 +0300

    IGNITE-16591 DDL syntax COLOCATE BY (#711)
---
 .../sql/engine/AbstractBasicIntegrationTest.java   |  4 +
 .../internal/sql/engine/ItCreateTableDdlTest.java  | 96 ++++++++++++++++++++--
 .../definition/index/PrimaryKeyDefinitionImpl.java | 10 ++-
 .../internal/schema/SchemaConfigurationTest.java   | 55 +++++++------
 modules/sql-engine/src/main/codegen/config.fmpp    |  2 +
 .../src/main/codegen/includes/parserImpls.ftl      | 17 ++--
 .../sql/engine/exec/ddl/DdlCommandHandler.java     |  2 +-
 .../sql/engine/prepare/ddl/CreateTableCommand.java | 25 +++---
 .../prepare/ddl/DdlSqlToCommandConverter.java      |  9 ++
 .../sql/engine/sql/IgniteSqlCreateTable.java       | 33 +++++++-
 .../internal/sql/engine/sql/SqlDdlParserTest.java  | 69 ++++++++++++++++
 11 files changed, 265 insertions(+), 57 deletions(-)

diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/AbstractBasicIntegrationTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/AbstractBasicIntegrationTest.java
index 26a3640..53b8436 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/AbstractBasicIntegrationTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/AbstractBasicIntegrationTest.java
@@ -195,6 +195,10 @@ public class AbstractBasicIntegrationTest extends BaseIgniteAbstractTest {
         );
     }
 
+    protected static Table table(String canonicalName) {
+        return CLUSTER_NODES.get(0).tables().table(canonicalName);
+    }
+
     protected static void insertData(String tblName, String[] columnNames, Object[]... tuples) {
         insertData(CLUSTER_NODES.get(0).tables().table(tblName), columnNames, tuples);
     }
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java
index 45f7a2d..ff70862 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java
@@ -17,15 +17,39 @@
 
 package org.apache.ignite.internal.sql.engine;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+import org.apache.ignite.internal.schema.Column;
+import org.apache.ignite.internal.table.TableImpl;
 import org.apache.ignite.lang.IgniteException;
+import org.apache.ignite.table.Table;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
 
 /**
  * Integration test for set op (EXCEPT, INTERSECT).
  */
 public class ItCreateTableDdlTest extends AbstractBasicIntegrationTest {
+    /**
+     * Clear tables after each test.
+     *
+     * @param testInfo Test information oject.
+     * @throws Exception If failed.
+     */
+    @AfterEach
+    @Override
+    public void tearDown(TestInfo testInfo) throws Exception {
+        for (Table t : CLUSTER_NODES.get(0).tables().tables()) {
+            sql("DROP TABLE " + t.name());
+        }
+
+        super.tearDownBase(testInfo);
+    }
+
     @Test
     public void pkWithNullableColumns() {
         assertThrows(
@@ -42,16 +66,76 @@ public class ItCreateTableDdlTest extends AbstractBasicIntegrationTest {
 
     @Test
     public void undefinedColumnsInPrimaryKey() {
-        assertThrows(
-                IgniteException.class,
-                () -> sql("CREATE TABLE T0(ID INT, VAL INT, PRIMARY KEY (ID1, ID0, ID2))"),
-                " Primary key constrain contains undefined columns: [cols=[ID0, ID2, ID1]]"
+        assertThat(
+                assertThrows(
+                        IgniteException.class,
+                        () -> sql("CREATE TABLE T0(ID INT, VAL INT, PRIMARY KEY (ID1, ID0, ID2))")
+                ).getMessage(),
+                containsString("Primary key constrain contains undefined columns: [cols=[ID0, ID2, ID1]]")
+        );
+    }
+
+    /**
+     * Check invalid colocation columns configuration:
+     * - not PK columns;
+     * - duplicates colocation columns.
+     */
+    @Test
+    public void invalidColocationColumns() {
+        assertThat(
+                assertThrows(
+                        IgniteException.class,
+                        () -> sql("CREATE TABLE T0(ID0 INT, ID1 INT, VAL INT, PRIMARY KEY (ID1, ID0)) COLOCATE (ID0, VAL)")
+                ).getMessage(),
+                containsString("Schema definition error: All colocation columns must be part of primary key")
+        );
+
+        assertThat(
+                assertThrows(
+                        IgniteException.class,
+                        () -> sql("CREATE TABLE T0(ID0 INT, ID1 INT, VAL INT, PRIMARY KEY (ID1, ID0)) COLOCATE (ID1, ID0, ID1)")
+                ).getMessage(),
+                containsString("Schema definition error: Colocation columns must not be duplicated")
         );
     }
 
+    /**
+     * Check implicit colocation columns configuration (defined by PK)..
+     */
     @Test
-    public void dbg() {
-        sql("CREATE TABLE T0(ID INT, VAL INT, PRIMARY KEY (ID))");
+    public void implicitColocationColumns() {
+        sql("CREATE TABLE T0(ID0 INT, ID1 INT, VAL INT, PRIMARY KEY (ID1, ID0))");
+
+        Column[] colocationColumns = ((TableImpl) table("PUBLIC.T0")).schemaView().schema().colocationColumns();
+
+        assertEquals(2, colocationColumns.length);
+        assertEquals("ID1", colocationColumns[0].name());
+        assertEquals("ID0", colocationColumns[1].name());
     }
 
+    /**
+     * Check explicit colocation columns configuration.
+     */
+    @Test
+    public void explicitColocationColumns() {
+        sql("CREATE TABLE T0(ID0 INT, ID1 INT, VAL INT, PRIMARY KEY (ID1, ID0)) COLOCATE BY (id0)");
+
+        Column[] colocationColumns = ((TableImpl) table("PUBLIC.T0")).schemaView().schema().colocationColumns();
+
+        assertEquals(1, colocationColumns.length);
+        assertEquals("ID0", colocationColumns[0].name());
+    }
+
+    /**
+     * Check explicit colocation columns configuration.
+     */
+    @Test
+    public void explicitColocationColumnsCaseSensitive() {
+        sql("CREATE TABLE T0(\"Id0\" INT, ID1 INT, VAL INT, PRIMARY KEY (ID1, \"Id0\")) COLOCATE BY (\"Id0\")");
+
+        Column[] colocationColumns = ((TableImpl) table("PUBLIC.T0")).schemaView().schema().colocationColumns();
+
+        assertEquals(1, colocationColumns.length);
+        assertEquals("Id0", colocationColumns[0].name());
+    }
 }
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/definition/index/PrimaryKeyDefinitionImpl.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/definition/index/PrimaryKeyDefinitionImpl.java
index 79b106d..43304a3 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/definition/index/PrimaryKeyDefinitionImpl.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/definition/index/PrimaryKeyDefinitionImpl.java
@@ -48,9 +48,13 @@ public class PrimaryKeyDefinitionImpl extends AbstractSchemaObject implements Pr
 
         if (CollectionUtils.nullOrEmpty(columns)) {
             throw new IllegalStateException("Primary key column(s) must be configured.");
-        } else if (!columns.containsAll(colocationColumns)) {
-            throw new IllegalStateException("Schema definition error: All colocation columns must be part of key.");
-        } else if (colocationColumns.size() != Set.copyOf(colocationColumns).size()) {
+        }
+
+        Set<String> colocationColumnsSet = Set.copyOf(colocationColumns);
+
+        if (!columns.containsAll(colocationColumnsSet)) {
+            throw new IllegalStateException("Schema definition error: All colocation columns must be part of primary key.");
+        } else if (colocationColumns.size() != colocationColumnsSet.size()) {
             throw new IllegalStateException("Schema definition error: Colocation columns must not be duplicated.");
         }
 
diff --git a/modules/schema/src/test/java/org/apache/ignite/internal/schema/SchemaConfigurationTest.java b/modules/schema/src/test/java/org/apache/ignite/internal/schema/SchemaConfigurationTest.java
index 3143916..4e4644a 100644
--- a/modules/schema/src/test/java/org/apache/ignite/internal/schema/SchemaConfigurationTest.java
+++ b/modules/schema/src/test/java/org/apache/ignite/internal/schema/SchemaConfigurationTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.internal.schema;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.util.Map;
@@ -133,31 +135,38 @@ public class SchemaConfigurationTest {
     }
 
     /**
-     * TestInitialSchema.
-     * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
+     * Check invalid colocation columns configuration:
+     * - not PK columns;
+     * - duplicates colocation columns.
      */
     @Test
     public void invalidColocationColumns() {
-        final TableDefinitionBuilder builder = SchemaBuilders.tableBuilder(SchemaObject.DEFAULT_DATABASE_SCHEMA_NAME, "table1");
-
-        assertThrows(
-                IllegalStateException.class,
-                () ->
-                        SchemaBuilders.primaryKey()  // Declare index column in order.
-                                .withColumns("id0", "id1", "id2")
-                                .withColocationColumns(
-                                        "val")
-                                .build(),
-                "Schema definition error: All colocation columns must be part of key.");
-
-        assertThrows(
-                IllegalStateException.class,
-                () ->
-                        SchemaBuilders.primaryKey()  // Declare index column in order.
-                                .withColumns("id0", "id1", "id2")
-                                .withColocationColumns(
-                                        "id0, id1, id0")
-                                .build(),
-                "Schema definition error: Colocation columns must not be duplicated.");
+        assertThat(
+                assertThrows(
+                        IllegalStateException.class,
+                        () ->
+                                SchemaBuilders.primaryKey()  // Declare index column in order.
+                                        .withColumns("id0", "id1", "id2")
+                                        .withColocationColumns(
+                                                "val")
+                                        .build(),
+                        "Schema definition error: All colocation columns must be part of key."
+                ).getMessage(),
+                containsString("Schema definition error: All colocation columns must be part of primary key")
+        );
+
+        assertThat(
+                assertThrows(
+                        IllegalStateException.class,
+                        () ->
+                                SchemaBuilders.primaryKey()  // Declare index column in order.
+                                        .withColumns("id0", "id1", "id2")
+                                        .withColocationColumns(
+                                                "id0, id1, id0")
+                                        .build(),
+                        "Schema definition error: Colocation columns must not be duplicated."
+                ).getMessage(),
+                containsString("Schema definition error: All colocation columns must be part of primary key")
+        );
     }
 }
diff --git a/modules/sql-engine/src/main/codegen/config.fmpp b/modules/sql-engine/src/main/codegen/config.fmpp
index 57d3f80..40d2ddc 100644
--- a/modules/sql-engine/src/main/codegen/config.fmpp
+++ b/modules/sql-engine/src/main/codegen/config.fmpp
@@ -45,6 +45,7 @@ data: {
     # List of new keywords. Example: "DATABASES", "TABLES". If the keyword is
     # not a reserved keyword, add it to the 'nonReservedKeywords' section.
     keywords: [
+      "COLOCATE"
       "SEMI"
       "IF"
       "REPLICAS"
@@ -136,6 +137,7 @@ data: {
       "COLLATE"
       "COLLATION"
       "COLLECT"
+      "COLOCATE"
       "COLUMN"
       "COMMIT"
       "CONDITION"
diff --git a/modules/sql-engine/src/main/codegen/includes/parserImpls.ftl b/modules/sql-engine/src/main/codegen/includes/parserImpls.ftl
index c95183b..eaa6878 100644
--- a/modules/sql-engine/src/main/codegen/includes/parserImpls.ftl
+++ b/modules/sql-engine/src/main/codegen/includes/parserImpls.ftl
@@ -152,7 +152,7 @@ Boolean NullableOptDefaultNull() :
 }
 
 
-                SqlNodeList TableElementList() :
+SqlNodeList TableElementList() :
 {
     final Span s;
     final List<SqlNode> list = new ArrayList<SqlNode>();
@@ -173,20 +173,23 @@ SqlCreate SqlCreateTable(Span s, boolean replace) :
     final boolean ifNotExists;
     final SqlIdentifier id;
     final SqlNodeList columnList;
-    final SqlNodeList optionList;
+    SqlNodeList optionList = null;
+    SqlNodeList colocationColumns = null;
 }
 {
     <TABLE>
     ifNotExists = IfNotExistsOpt()
     id = CompoundIdentifier()
     columnList = TableElementList()
-    (
+    [
+        <COLOCATE> [<BY>] {s.add(this);}
+            colocationColumns = ParenthesizedSimpleIdentifierList()
+    ]
+    [
         <WITH> { s.add(this); } optionList = CreateTableOptionList()
-    |
-        { optionList = null; }
-    )
+    ]
     {
-        return new IgniteSqlCreateTable(s.end(this), ifNotExists, id, columnList, optionList);
+        return new IgniteSqlCreateTable(s.end(this), ifNotExists, id, columnList, colocationColumns, optionList);
     }
 }
 
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 68bd9c1..775c13f 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
@@ -109,7 +109,7 @@ public class DdlCommandHandler {
         final PrimaryKeyDefinitionBuilder pkeyDef = SchemaBuilders.primaryKey();
 
         pkeyDef.withColumns(IgniteObjectName.quoteNames(cmd.primaryKeyColumns()));
-        pkeyDef.withColocationColumns(IgniteObjectName.quoteNames(cmd.affColumns()));
+        pkeyDef.withColocationColumns(IgniteObjectName.quoteNames(cmd.colocationColumns()));
 
         final IgniteTypeFactory typeFactory = Commons.typeFactory();
 
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/CreateTableCommand.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/CreateTableCommand.java
index c45579e..5543cab 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/CreateTableCommand.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/CreateTableCommand.java
@@ -33,8 +33,8 @@ public class CreateTableCommand extends AbstractTableDdlCommand {
     /** Primary key columns. */
     private List<String> pkCols;
 
-    /** Affinity key columns. */
-    private List<String> affCols;
+    /** Colocation columns. */
+    private List<String> colocationCols;
 
     /** Columns. */
     private List<ColumnDefinition> cols;
@@ -85,7 +85,7 @@ public class CreateTableCommand extends AbstractTableDdlCommand {
     }
 
     /**
-     * Table columns.
+     * Get table columns.
      *
      * @return Columns.
      */
@@ -94,7 +94,7 @@ public class CreateTableCommand extends AbstractTableDdlCommand {
     }
 
     /**
-     * Table columns.
+     * Set table columns.
      *
      * @param cols Columns.
      */
@@ -103,22 +103,21 @@ public class CreateTableCommand extends AbstractTableDdlCommand {
     }
 
     /**
-     * Affinity columns.
+     * Set colocation column names.
      *
-     * @return Affinity key columns.
+     * @return Collocation column names.
      */
     @Nullable
-    public List<String> affColumns() {
-        return affCols;
+    public List<String> colocationColumns() {
+        return colocationCols;
     }
 
     /**
-     * Affinity columns.
+     * Get colocation column names.
      *
-     * @param affCols Set affinity key columns.
+     * @param colocationCols Colocation column names.
      */
-    // todo: support aff columns https://issues.apache.org/jira/browse/IGNITE-16069
-    public void affColumns(List<String> affCols) {
-        this.affCols = affCols;
+    public void colocationColumns(List<String> colocationCols) {
+        this.colocationCols = colocationCols;
     }
 }
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
index 641a5f3..934e64e 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
@@ -169,6 +169,15 @@ public class DdlSqlToCommandConverter {
 
         createTblCmd.primaryKeyColumns(pkCols);
 
+        List<String> colocationCols = createTblNode.colocationColumns() == null
+                ? null
+                : createTblNode.colocationColumns().getList().stream()
+                        .map(SqlIdentifier.class::cast)
+                        .map(SqlIdentifier::getSimple)
+                        .collect(Collectors.toList());
+
+        createTblCmd.colocationColumns(colocationCols);
+
         List<SqlColumnDeclaration> colDeclarations = createTblNode.columnList().getList().stream()
                 .filter(SqlColumnDeclaration.class::isInstance)
                 .map(SqlColumnDeclaration.class::cast)
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlCreateTable.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlCreateTable.java
index d4b0f6c..37aff93 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlCreateTable.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlCreateTable.java
@@ -39,16 +39,26 @@ public class IgniteSqlCreateTable extends SqlCreate {
 
     private final @Nullable SqlNodeList columnList;
 
+    private final @Nullable SqlNodeList colocationColumns;
+
     private final @Nullable SqlNodeList createOptionList;
 
     private static final SqlOperator OPERATOR = new SqlSpecialOperator("CREATE TABLE", SqlKind.CREATE_TABLE);
 
     /** Creates a SqlCreateTable. */
-    public IgniteSqlCreateTable(SqlParserPos pos, boolean ifNotExists,
-            SqlIdentifier name, @Nullable SqlNodeList columnList, @Nullable SqlNodeList createOptionList) {
+    public IgniteSqlCreateTable(
+            SqlParserPos pos,
+            boolean ifNotExists,
+            SqlIdentifier name,
+            @Nullable SqlNodeList columnList,
+            @Nullable SqlNodeList colocationColumns,
+            @Nullable SqlNodeList createOptionList
+    ) {
         super(OPERATOR, pos, false, ifNotExists);
+
         this.name = Objects.requireNonNull(name, "name");
         this.columnList = columnList;
+        this.colocationColumns = colocationColumns;
         this.createOptionList = createOptionList;
     }
 
@@ -78,6 +88,14 @@ public class IgniteSqlCreateTable extends SqlCreate {
             writer.endList(frame);
         }
 
+        if (colocationColumns != null) {
+            writer.keyword("COLOCATE BY");
+
+            SqlWriter.Frame frame = writer.startList("(", ")");
+            colocationColumns.unparse(writer, 0, 0);
+            writer.endList(frame);
+        }
+
         if (createOptionList != null) {
             writer.keyword("WITH");
 
@@ -93,14 +111,21 @@ public class IgniteSqlCreateTable extends SqlCreate {
     }
 
     /**
-     * GEt list of the specified columns and constraints.
+     * Get list of the specified columns and constraints.
      */
     public SqlNodeList columnList() {
         return columnList;
     }
 
     /**
-     * GEt list of the specified options to create table with.
+     * Get list of the colocation columns.
+     */
+    public SqlNodeList colocationColumns() {
+        return colocationColumns;
+    }
+
+    /**
+     * Get list of the specified options to create table with.
      */
     public SqlNodeList createOptionList() {
         return createOptionList;
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 d3c99ce..1fd040f 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
@@ -18,14 +18,18 @@
 package org.apache.ignite.internal.sql.engine.sql;
 
 import static java.util.Collections.singleton;
+import static org.hamcrest.CoreMatchers.endsWith;
 import static org.hamcrest.CoreMatchers.hasItem;
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.jupiter.api.Assertions.assertNull;
 
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 import org.apache.calcite.sql.SqlIdentifier;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlLiteral;
@@ -35,6 +39,7 @@ import org.apache.calcite.sql.ddl.SqlColumnDeclaration;
 import org.apache.calcite.sql.ddl.SqlKeyConstraint;
 import org.apache.calcite.sql.parser.SqlParseException;
 import org.apache.calcite.sql.parser.SqlParser;
+import org.apache.calcite.sql.pretty.SqlPrettyWriter;
 import org.apache.ignite.internal.generated.query.calcite.sql.IgniteSqlParserImpl;
 import org.hamcrest.CustomMatcher;
 import org.hamcrest.Matcher;
@@ -215,6 +220,70 @@ public class SqlDdlParserTest {
     }
 
     /**
+     * Parsing of CREATE TABLE with specified colocation columns.
+     */
+    @Test
+    public void createTableWithColocation() throws SqlParseException {
+        IgniteSqlCreateTable createTable;
+
+        createTable = parseCreateTable(
+                "CREATE TABLE MY_TABLE(ID0 INT, ID1 INT, ID2 INT, VAL INT, PRIMARY KEY (ID0, ID1, ID2))"
+        );
+
+        assertNull(createTable.colocationColumns());
+
+        createTable = parseCreateTable(
+                "CREATE TABLE MY_TABLE(ID0 INT, ID1 INT, ID2 INT, VAL INT, PRIMARY KEY (ID0, ID1, ID2)) COLOCATE BY (ID2, ID1)"
+        );
+
+        assertThat(
+                createTable.colocationColumns().getList().stream()
+                        .map(SqlIdentifier.class::cast)
+                        .map(SqlIdentifier::getSimple)
+                        .collect(Collectors.toList()),
+                equalTo(List.of("ID2", "ID1"))
+        );
+
+        SqlPrettyWriter w = new SqlPrettyWriter();
+        createTable.unparse(w, 0, 0);
+
+        assertThat(w.toString(), endsWith("COLOCATE BY (\"ID2\", \"ID1\")"));
+
+        createTable = parseCreateTable(
+                "CREATE TABLE MY_TABLE(ID0 INT, ID1 INT, ID2 INT, VAL INT, PRIMARY KEY (ID0, ID1, ID2)) COLOCATE (ID0)"
+        );
+
+        assertThat(
+                createTable.colocationColumns().getList().stream()
+                        .map(SqlIdentifier.class::cast)
+                        .map(SqlIdentifier::getSimple)
+                        .collect(Collectors.toList()),
+                equalTo(List.of("ID0"))
+        );
+
+        // Check uparse 'COLOCATE' and 'WITH' together.
+        createTable = parseCreateTable(
+                "CREATE TABLE MY_TABLE(ID0 INT, ID1 INT, ID2 INT, VAL INT, PRIMARY KEY (ID0, ID1, ID2)) COLOCATE (ID0) "
+                        + "with "
+                        + "replicas=2, "
+                        + "partitions=3"
+        );
+
+        w = new SqlPrettyWriter();
+        createTable.unparse(w, 0, 0);
+
+        assertThat(w.toString(), endsWith("COLOCATE BY (\"ID0\") WITH REPLICAS = 2, PARTITIONS = 3"));
+    }
+
+    private IgniteSqlCreateTable parseCreateTable(String stmt) throws SqlParseException {
+        SqlNode node = parse(stmt);
+
+        assertThat(node, instanceOf(IgniteSqlCreateTable.class));
+
+        return (IgniteSqlCreateTable) node;
+    }
+
+    /**
      * Parses a given statement and returns a resulting AST.
      *
      * @param stmt Statement to parse.