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 2021/04/15 12:48:48 UTC
[ignite] branch sql-calcite updated: IGNITE-13547 Calcite
integration. CREATE TABLE support
This is an automated email from the ASF dual-hosted git repository.
tledkov pushed a commit to branch sql-calcite
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/sql-calcite by this push:
new 5ba33d3 IGNITE-13547 Calcite integration. CREATE TABLE support
5ba33d3 is described below
commit 5ba33d3f98a4421f32df62880c17426a7f5b8b22
Author: korlov42 <ko...@gridgain.com>
AuthorDate: Thu Apr 15 15:48:29 2021 +0300
IGNITE-13547 Calcite integration. CREATE TABLE support
---
modules/calcite/pom.xml | 96 ++-
modules/calcite/src/main/codegen/config.fmpp | 642 +++++++++++++++++++++
.../src/main/codegen/includes/parserImpls.ftl | 181 ++++++
.../java/org/apache/calcite/sql/IgniteSqlNode.java | 31 +
.../query/calcite/CalciteQueryProcessor.java | 5 +-
.../query/calcite/exec/ExecutionServiceImpl.java | 46 +-
.../query/calcite/exec/ddl/DdlCommandHandler.java | 215 +++++++
.../processors/query/calcite/prepare/DdlPlan.java | 46 ++
.../query/calcite/prepare/IgnitePlanner.java | 6 +
.../calcite/prepare/ddl/ColumnDefinition.java | 83 +++
.../calcite/prepare/ddl/CreateTableCommand.java | 304 ++++++++++
.../query/calcite/prepare/ddl/DdlCommand.java | 21 +
.../prepare/ddl/DdlSqlToCommandConverter.java | 344 +++++++++++
.../query/calcite/schema/TableDescriptorImpl.java | 47 +-
.../query/calcite/sql/IgniteSqlCreateTable.java | 117 ++++
.../calcite/sql/IgniteSqlCreateTableOption.java | 91 +++
.../sql/IgniteSqlCreateTableOptionEnum.java | 55 ++
.../query/calcite/CalciteQueryProcessorTest.java | 28 +-
.../query/calcite/CreateTableIntegrationTest.java | 276 +++++++++
.../calcite/planner/JoinColocationPlannerTest.java | 18 +-
.../query/calcite/sql/SqlDdlParserTest.java | 325 +++++++++++
.../ignite/testsuites/IgniteCalciteTestSuite.java | 5 +
.../cache/query/IgniteQueryErrorCode.java | 3 +
.../processors/query/GridQueryTypeDescriptor.java | 12 +
.../internal/processors/query/QueryEntityEx.java | 20 +-
.../processors/query/QueryTypeDescriptorImpl.java | 13 +
.../internal/processors/query/QueryUtils.java | 68 ++-
.../apache/ignite/testframework/GridTestUtils.java | 16 +
.../processors/query/h2/CommandProcessor.java | 63 +-
29 files changed, 3073 insertions(+), 104 deletions(-)
diff --git a/modules/calcite/pom.xml b/modules/calcite/pom.xml
index 1491b62..81171e7 100644
--- a/modules/calcite/pom.xml
+++ b/modules/calcite/pom.xml
@@ -20,7 +20,8 @@
<!--
POM file.
-->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- Module specific package versions -->
@@ -70,12 +71,6 @@
<dependency>
<groupId>org.apache.calcite</groupId>
- <artifactId>calcite-babel</artifactId>
- <version>${calcite.version}</version>
- </dependency>
-
- <dependency>
- <groupId>org.apache.calcite</groupId>
<artifactId>calcite-linq4j</artifactId>
<version>${calcite.version}</version>
</dependency>
@@ -203,6 +198,91 @@
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
- </plugins>
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-fmpp-resources</id>
+ <phase>validate</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/codegen</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/codegen</directory>
+ <filtering>false</filtering>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-parser-template</id>
+ <phase>validate</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.apache.calcite</groupId>
+ <artifactId>calcite-core</artifactId>
+ <type>jar</type>
+ <overWrite>true</overWrite>
+ <outputDirectory>${project.build.directory}/</outputDirectory>
+ <includes>codegen/templates/Parser.jj</includes>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>com.googlecode.fmpp-maven-plugin</groupId>
+ <artifactId>fmpp-maven-plugin</artifactId>
+ <version>1.0</version>
+ <configuration>
+ <cfgFile>${project.build.directory}/codegen/config.fmpp</cfgFile>
+ <templateDirectory>${project.build.directory}/codegen/templates</templateDirectory>
+ </configuration>
+ <executions>
+ <execution>
+ <id>generate-fmpp-sources</id>
+ <phase>validate</phase>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>javacc-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>javacc</id>
+ <goals>
+ <goal>javacc</goal>
+ </goals>
+ <configuration>
+ <sourceDirectory>${project.build.directory}/generated-sources/fmpp</sourceDirectory>
+ <outputDirectory>${project.build.directory}/generated-sources/javacc</outputDirectory>
+ <includes>
+ <include>**/Parser.jj</include>
+ </includes>
+ <lookAhead>2</lookAhead>
+ <isStatic>false</isStatic>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
</build>
</project>
diff --git a/modules/calcite/src/main/codegen/config.fmpp b/modules/calcite/src/main/codegen/config.fmpp
new file mode 100644
index 0000000..a7db7a4
--- /dev/null
+++ b/modules/calcite/src/main/codegen/config.fmpp
@@ -0,0 +1,642 @@
+# 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.
+
+data: {
+ # Data declarations for this parser.
+ #
+ # Default declarations are in default_config.fmpp; if you do not include a
+ # declaration ('imports' or 'nonReservedKeywords', for example) in this file,
+ # FMPP will use the declaration from default_config.fmpp.
+ parser: {
+ # Generated parser implementation class package and name
+ package: "org.apache.ignite.internal.processors.query.calcite.sql",
+ class: "IgniteSqlParserImpl",
+
+ # List of additional classes and packages to import.
+ # Example: "org.apache.calcite.sql.*", "java.util.List".
+ imports: [
+ "org.apache.calcite.sql.SqlCreate",
+ "org.apache.calcite.sql.SqlLiteral",
+ "org.apache.calcite.schema.ColumnStrategy",
+ "org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTable",
+ "org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOptionEnum",
+ "org.apache.calcite.sql.ddl.SqlDdlNodes",
+ ]
+
+ # List of new keywords. Example: "DATABASES", "TABLES". If the keyword is
+ # not a reserved keyword, add it to the 'nonReservedKeywords' section.
+ keywords: [
+ "SEMI"
+ "IF"
+ "TEMPLATE"
+ "BACKUPS"
+ "AFFINITY_KEY"
+ "ATOMICITY"
+ "WRITE_SYNCHRONIZATION_MODE"
+ "CACHE_GROUP"
+ "CACHE_NAME"
+ "DATA_REGION"
+# "KEY_TYPE" // already presented in Calcite
+ "VALUE_TYPE"
+ "ENCRYPTED"
+ ]
+
+ # List of non-reserved keywords to add;
+ # items in this list become non-reserved
+ nonReservedKeywords: [
+ "SEMI"
+ "TEMPLATE"
+ "BACKUPS"
+ "AFFINITY_KEY"
+ "ATOMICITY"
+ "WRITE_SYNCHRONIZATION_MODE"
+ "CACHE_GROUP"
+ "CACHE_NAME"
+ "DATA_REGION"
+# "KEY_TYPE" // already presented in Calcite
+ "VALUE_TYPE"
+ "ENCRYPTED"
+
+ # The following keywords are reserved in core Calcite,
+ # are reserved in some version of SQL,
+ # but are not reserved in Babel.
+ #
+ # Words that are commented out (e.g. "AND") are still reserved.
+ # These are the most important reserved words, and SQL cannot be
+ # unambiguously parsed if they are not reserved. For example, if
+ # "INNER" is not reserved then in the query
+ #
+ # select * from emp inner join dept using (deptno)"
+ #
+ # "inner" could be a table alias for "emp".
+ #
+ "A"
+ "ABS"
+ "ABSOLUTE"
+ "ACTION"
+ "ADD"
+ "AFTER"
+# "ALL"
+ "ALLOCATE"
+ "ALLOW"
+ "ALTER"
+ "AND"
+# "ANY"
+ "ARE"
+ "ARRAY"
+# "ARRAY_AGG" # not a keyword in Calcite
+ "ARRAY_MAX_CARDINALITY"
+ "AS"
+ "ASC"
+ "ASENSITIVE"
+ "ASSERTION"
+ "ASYMMETRIC"
+ "AT"
+ "ATOMIC"
+ "AUTHORIZATION"
+ "AVG"
+ "BEFORE"
+ "BEGIN"
+ "BEGIN_FRAME"
+ "BEGIN_PARTITION"
+ "BETWEEN"
+ "BIGINT"
+ "BINARY"
+ "BIT"
+# "BIT_LENGTH" # not a keyword in Calcite
+ "BLOB"
+ "BOOLEAN"
+ "BOTH"
+ "BREADTH"
+ "BY"
+ "C"
+# "CALL"
+ "CALLED"
+ "CARDINALITY"
+ "CASCADE"
+ "CASCADED"
+# "CASE"
+ "CAST"
+ "CATALOG"
+ "CEIL"
+ "CEILING"
+ "CHAR"
+ "CHARACTER"
+ "CHARACTER_LENGTH"
+ "CHAR_LENGTH"
+ "CHECK"
+ "CLASSIFIER"
+ "CLOB"
+ "CLOSE"
+ "COALESCE"
+ "COLLATE"
+ "COLLATION"
+ "COLLECT"
+ "COLUMN"
+ "COMMIT"
+ "CONDITION"
+ "CONNECT"
+ "CONNECTION"
+# "CONSTRAINT"
+ "CONSTRAINTS"
+ "CONSTRUCTOR"
+ "CONTAINS"
+ "CONTINUE"
+ "CONVERT"
+ "CORR"
+ "CORRESPONDING"
+ "COUNT"
+ "COVAR_POP"
+ "COVAR_SAMP"
+# "CREATE"
+# "CROSS"
+ "CUBE"
+ "CUME_DIST"
+# "CURRENT"
+ "CURRENT_CATALOG"
+ "CURRENT_DATE"
+ "CURRENT_DEFAULT_TRANSFORM_GROUP"
+ "CURRENT_PATH"
+ "CURRENT_ROLE"
+ "CURRENT_ROW"
+ "CURRENT_SCHEMA"
+ "CURRENT_TIME"
+ "CURRENT_TIMESTAMP"
+ "CURRENT_TRANSFORM_GROUP_FOR_TYPE"
+ "CURRENT_USER"
+# "CURSOR"
+ "CYCLE"
+ "DATA"
+# "DATE"
+ "DAY"
+ "DEALLOCATE"
+ "DEC"
+ "DECIMAL"
+ "DECLARE"
+# "DEFAULT"
+ "DEFERRABLE"
+ "DEFERRED"
+# "DEFINE"
+# "DELETE"
+ "DENSE_RANK"
+ "DEPTH"
+ "DEREF"
+ "DESC"
+# "DESCRIBE" # must be reserved
+ "DESCRIPTOR"
+ "DETERMINISTIC"
+ "DIAGNOSTICS"
+ "DISALLOW"
+ "DISCONNECT"
+# "DISTINCT"
+# "DO" # not a keyword in Calcite
+ "DOMAIN"
+ "DOUBLE"
+# "DROP" # probably must be reserved
+ "DYNAMIC"
+ "EACH"
+ "ELEMENT"
+ "ELSE"
+# "ELSEIF" # not a keyword in Calcite
+ "EMPTY"
+ "END"
+# "END-EXEC" # not a keyword in Calcite, and contains '-'
+ "END_FRAME"
+ "END_PARTITION"
+ "EQUALS"
+ "ESCAPE"
+ "EVERY"
+# "EXCEPT" # must be reserved
+ "EXCEPTION"
+ "EXEC"
+ "EXECUTE"
+ "EXISTS"
+# "EXIT" # not a keyword in Calcite
+ "EXP"
+# "EXPLAIN" # must be reserved
+ "EXTEND"
+ "EXTERNAL"
+ "EXTRACT"
+ "FALSE"
+# "FETCH"
+ "FILTER"
+ "FIRST"
+ "FIRST_VALUE"
+ "FLOAT"
+ "FLOOR"
+ "FOR"
+ "FOREIGN"
+# "FOREVER" # not a keyword in Calcite
+ "FOUND"
+ "FRAME_ROW"
+ "FREE"
+# "FROM" # must be reserved
+# "FULL" # must be reserved
+ "FUNCTION"
+ "FUSION"
+ "G"
+ "GENERAL"
+ "GET"
+ "GLOBAL"
+ "GO"
+ "GOTO"
+# "GRANT"
+# "GROUP"
+# "GROUPING"
+ "GROUPS"
+# "HANDLER" # not a keyword in Calcite
+# "HAVING"
+ "HOLD"
+ "HOUR"
+ "IDENTITY"
+# "IF" # not a keyword in Calcite
+ # "ILIKE"
+ "IMMEDIATE"
+ "IMMEDIATELY"
+ "IMPORT"
+# "IN"
+ "INDICATOR"
+ "INITIAL"
+ "INITIALLY"
+# "INNER"
+ "INOUT"
+ "INPUT"
+ "INSENSITIVE"
+# "INSERT"
+ "INT"
+ "INTEGER"
+# "INTERSECT"
+ "INTERSECTION"
+# "INTERVAL"
+# "INTO"
+ "IS"
+ "ISOLATION"
+# "ITERATE" # not a keyword in Calcite
+# "JOIN"
+ "JSON_ARRAY"
+ "JSON_ARRAYAGG"
+ "JSON_EXISTS"
+ "JSON_OBJECT"
+ "JSON_OBJECTAGG"
+ "JSON_QUERY"
+ "JSON_VALUE"
+ "K"
+# "KEEP" # not a keyword in Calcite
+ "KEY"
+ "LAG"
+ "LANGUAGE"
+ "LARGE"
+ "LAST"
+ "LAST_VALUE"
+# "LATERAL"
+ "LEAD"
+ "LEADING"
+# "LEAVE" # not a keyword in Calcite
+# "LEFT"
+ "LEVEL"
+ "LIKE"
+ "LIKE_REGEX"
+# "LIMIT"
+ "LN"
+ "LOCAL"
+ "LOCALTIME"
+ "LOCALTIMESTAMP"
+ "LOCATOR"
+# "LOOP" # not a keyword in Calcite
+ "LOWER"
+ "M"
+ "MAP"
+ "MATCH"
+ "MATCHES"
+ "MATCH_NUMBER"
+# "MATCH_RECOGNIZE"
+ "MAX"
+# "MAX_CARDINALITY" # not a keyword in Calcite
+ "MEASURES"
+ "MEMBER"
+# "MERGE"
+ "METHOD"
+ "MIN"
+# "MINUS"
+ "MINUTE"
+ "MOD"
+ "MODIFIES"
+ "MODULE"
+ "MONTH"
+ "MULTISET"
+ "NAME"
+ "NAMES"
+ "NATIONAL"
+# "NATURAL"
+ "NCHAR"
+ "NCLOB"
+# "NEW"
+# "NEXT"
+ "NO"
+ "NONE"
+ "NORMALIZE"
+ "NOT"
+ "NTH_VALUE"
+ "NTILE"
+# "NULL"
+ "NULLIF"
+ "NUMERIC"
+ "OBJECT"
+ "OCCURRENCES_REGEX"
+ "OCTET_LENGTH"
+ "OF"
+# "OFFSET"
+ "OLD"
+ "OMIT"
+# "ON"
+ "ONE"
+ "ONLY"
+ "OPEN"
+ "OPTION"
+ "OR"
+# "ORDER"
+ "ORDINALITY"
+ "OUT"
+# "OUTER"
+ "OUTPUT"
+# "OVER"
+ "OVERLAPS"
+ "OVERLAY"
+ "PAD"
+ "PARAMETER"
+ "PARTIAL"
+# "PARTITION"
+ "PATH"
+# "PATTERN"
+ "PER"
+ "PERCENT"
+ "PERCENTILE_CONT"
+ "PERCENTILE_DISC"
+ "PERCENT_RANK"
+ "PERIOD"
+ "PERMUTE"
+ "PORTION"
+ "POSITION"
+ "POSITION_REGEX"
+ "POWER"
+ "PRECEDES"
+ "PRECISION"
+ "PREPARE"
+ "PRESERVE"
+ "PREV"
+# "PRIMARY"
+ "PRIOR"
+ "PRIVILEGES"
+ "PROCEDURE"
+ "PUBLIC"
+# "RANGE"
+ "RANK"
+ "READ"
+ "READS"
+ "REAL"
+ "RECURSIVE"
+ "REF"
+ "REFERENCES"
+ "REFERENCING"
+ "REGR_AVGX"
+ "REGR_AVGY"
+ "REGR_COUNT"
+ "REGR_INTERCEPT"
+ "REGR_R2"
+ "REGR_SLOPE"
+ "REGR_SXX"
+ "REGR_SXY"
+ "REGR_SYY"
+ "RELATIVE"
+ "RELEASE"
+# "REPEAT" # not a keyword in Calcite
+ "RESET"
+# "RESIGNAL" # not a keyword in Calcite
+ "RESTRICT"
+ "RESULT"
+ "RETURN"
+ "RETURNS"
+ "REVOKE"
+# "RIGHT"
+ # "RLIKE"
+ "ROLE"
+ "ROLLBACK"
+# "ROLLUP"
+ "ROUTINE"
+# "ROW"
+# "ROWS"
+ "ROW_NUMBER"
+ "RUNNING"
+ "SAVEPOINT"
+ "SCHEMA"
+ "SCOPE"
+ "SCROLL"
+ "SEARCH"
+ "SECOND"
+ "SECTION"
+ "SEEK"
+# "SELECT"
+ "SENSITIVE"
+ "SESSION"
+ "SESSION_USER"
+# "SET"
+# "SETS"
+ "SHOW"
+# "SIGNAL" # not a keyword in Calcite
+ "SIMILAR"
+ "SIZE"
+# "SKIP" # messes with JavaCC's <SKIP> token
+ "SMALLINT"
+# "SOME"
+ "SPACE"
+ "SPECIFIC"
+ "SPECIFICTYPE"
+ "SQL"
+# "SQLCODE" # not a keyword in Calcite
+# "SQLERROR" # not a keyword in Calcite
+ "SQLEXCEPTION"
+ "SQLSTATE"
+ "SQLWARNING"
+ "SQRT"
+ "START"
+ "STATE"
+ "STATIC"
+ "STDDEV_POP"
+ "STDDEV_SAMP"
+# "STREAM"
+ "SUBMULTISET"
+ "SUBSET"
+ "SUBSTRING"
+ "SUBSTRING_REGEX"
+ "SUCCEEDS"
+ "SUM"
+ "SYMMETRIC"
+ "SYSTEM"
+ "SYSTEM_TIME"
+ "SYSTEM_USER"
+# "TABLE"
+# "TABLESAMPLE"
+ "TEMPORARY"
+# "THEN"
+# "TIME"
+# "TIMESTAMP"
+ "TIMEZONE_HOUR"
+ "TIMEZONE_MINUTE"
+ "TINYINT"
+ "TO"
+ "TRAILING"
+ "TRANSACTION"
+ "TRANSLATE"
+ "TRANSLATE_REGEX"
+ "TRANSLATION"
+ "TREAT"
+ "TRIGGER"
+ "TRIM"
+ "TRIM_ARRAY"
+ "TRUE"
+ "TRUNCATE"
+ "UESCAPE"
+ "UNDER"
+# "UNDO" # not a keyword in Calcite
+# "UNION"
+ "UNIQUE"
+ "UNKNOWN"
+# "UNNEST"
+# "UNTIL" # not a keyword in Calcite
+# "UPDATE"
+ "UPPER"
+ "UPSERT"
+ "USAGE"
+ "USER"
+# "USING"
+ "VALUE"
+# "VALUES"
+ "VALUE_OF"
+ "VARBINARY"
+ "VARCHAR"
+ "VARYING"
+ "VAR_POP"
+ "VAR_SAMP"
+ "VERSION"
+ "VERSIONING"
+# "VERSIONS" # not a keyword in Calcite
+ "VIEW"
+# "WHEN"
+ "WHENEVER"
+# "WHERE"
+# "WHILE" # not a keyword in Calcite
+ "WIDTH_BUCKET"
+# "WINDOW"
+# "WITH"
+ "WITHIN"
+ "WITHOUT"
+ "WORK"
+ "WRITE"
+ "YEAR"
+ "ZONE"
+ ]
+
+ # List of non-reserved keywords to add;
+ # items in this list become non-reserved.
+ nonReservedKeywordsToAdd: [
+ ]
+
+ # List of non-reserved keywords to remove;
+ # items in this list become reserved.
+ nonReservedKeywordsToRemove: [
+ ]
+
+ # List of additional join types. Each is a method with no arguments.
+ # Example: "LeftSemiJoin".
+ joinTypes: [
+ # "LeftSemiJoin"
+ ]
+
+ # List of methods for parsing builtin function calls.
+ # Return type of method implementation should be "SqlNode".
+ # Example: "DateFunctionCall()".
+ builtinFunctionCallMethods: [
+ # "DateFunctionCall()"
+ # "DateaddFunctionCall()"
+ ]
+
+ # List of methods for parsing custom SQL statements.
+ # Return type of method implementation should be 'SqlNode'.
+ # Example: "SqlShowDatabases()", "SqlShowTables()".
+ statementParserMethods: [
+ ]
+
+ # List of methods for parsing extensions to "CREATE [OR REPLACE]" calls.
+ # Each must accept arguments "(SqlParserPos pos, boolean replace)".
+ # Example: "SqlCreateForeignSchema".
+ createStatementParserMethods: [
+ "SqlCreateTable"
+ ]
+
+ # List of methods for parsing extensions to "DROP" calls.
+ # Each must accept arguments "(SqlParserPos pos)".
+ # Example: "SqlDropSchema".
+ dropStatementParserMethods: [
+ ]
+
+ # List of methods for parsing extensions to "DROP" calls.
+ # Each must accept arguments "(SqlParserPos pos)".
+ # Example: "SqlDropSchema".
+ alterStatementParserMethods: [
+ ]
+
+ # List of methods for parsing custom literals.
+ # Return type of method implementation should be "SqlNode".
+ # Example: ParseJsonLiteral().
+ literalParserMethods: [
+ ]
+
+ # List of methods for parsing custom data types.
+ # Return type of method implementation should be "SqlTypeNameSpec".
+ # Example: SqlParseTimeStampZ().
+ dataTypeParserMethods: [
+ ]
+
+ # Binary operators tokens.
+ # Example: "< INFIX_CAST: \"::\" >".
+ binaryOperatorsTokens: [
+ "< INFIX_CAST: \"::\" >"
+ ]
+
+ # Binary operators initialization.
+ # Example: "InfixCast".
+ extraBinaryExpressions: [
+ "InfixCast"
+ ]
+
+ # List of files in @includes directory that have parser method
+ # implementations for parsing custom SQL statements, literals or types
+ # given as part of "statementParserMethods", "literalParserMethods" or
+ # "dataTypeParserMethods".
+ # Example: "parserImpls.ftl".
+ implementationFiles: [
+ "parserImpls.ftl"
+ ]
+
+ includePosixOperators: false
+ includeCompoundIdentifier: true
+ includeBraces: true
+ includeAdditionalDeclarations: false
+ }
+}
+
+freemarkerLinks: {
+ includes: includes/
+}
diff --git a/modules/calcite/src/main/codegen/includes/parserImpls.ftl b/modules/calcite/src/main/codegen/includes/parserImpls.ftl
new file mode 100644
index 0000000..6583b7d
--- /dev/null
+++ b/modules/calcite/src/main/codegen/includes/parserImpls.ftl
@@ -0,0 +1,181 @@
+<#--
+// 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.
+-->
+
+boolean IfNotExistsOpt() :
+{
+}
+{
+ <IF> <NOT> <EXISTS> { return true; }
+|
+ { return false; }
+}
+
+SqlNodeList CreateTableOptionList() :
+{
+ List<SqlNode> list = new ArrayList<SqlNode>();
+ final Span s = Span.of();
+}
+{
+ CreateTableOption(list)
+ (
+ <COMMA> { s.add(this); } CreateTableOption(list)
+ )*
+ {
+ return new SqlNodeList(list, s.end(this));
+ }
+}
+
+IgniteSqlCreateTableOptionEnum CreateTableOptionEnumOpt() :
+{
+}
+{
+ <TEMPLATE> { return IgniteSqlCreateTableOptionEnum.TEMPLATE; }
+|
+ <BACKUPS> { return IgniteSqlCreateTableOptionEnum.BACKUPS; }
+|
+ <AFFINITY_KEY> { return IgniteSqlCreateTableOptionEnum.AFFINITY_KEY; }
+|
+ <ATOMICITY> { return IgniteSqlCreateTableOptionEnum.ATOMICITY; }
+|
+ <WRITE_SYNCHRONIZATION_MODE> { return IgniteSqlCreateTableOptionEnum.WRITE_SYNCHRONIZATION_MODE; }
+|
+ <CACHE_GROUP> { return IgniteSqlCreateTableOptionEnum.CACHE_GROUP; }
+|
+ <CACHE_NAME> { return IgniteSqlCreateTableOptionEnum.CACHE_NAME; }
+|
+ <DATA_REGION> { return IgniteSqlCreateTableOptionEnum.DATA_REGION; }
+|
+ <KEY_TYPE> { return IgniteSqlCreateTableOptionEnum.KEY_TYPE; }
+|
+ <VALUE_TYPE> { return IgniteSqlCreateTableOptionEnum.VALUE_TYPE; }
+|
+ <ENCRYPTED> { return IgniteSqlCreateTableOptionEnum.ENCRYPTED; }
+}
+
+void CreateTableOption(List<SqlNode> list) :
+{
+ final Span s;
+ final IgniteSqlCreateTableOptionEnum key;
+ final SqlNode val;
+}
+{
+ key = CreateTableOptionEnumOpt() { s = span(); }
+ <EQ>
+ (
+ val = Literal()
+ |
+ val = SimpleIdentifier()
+ ) {
+ list.add(new IgniteSqlCreateTableOption(key, val, s.end(this)));
+ }
+}
+
+void TableElement(List<SqlNode> list) :
+{
+ final SqlDataTypeSpec type;
+ final boolean nullable;
+ final SqlNodeList columnList;
+ final Span s = Span.of();
+ final ColumnStrategy strategy;
+ final SqlNode dflt;
+ SqlIdentifier id = null;
+}
+{
+ id = SimpleIdentifier() type = DataType() nullable = NullableOptDefaultTrue()
+ (
+ <DEFAULT_> { s.add(this); } dflt = Literal() {
+ strategy = ColumnStrategy.DEFAULT;
+ }
+ |
+ {
+ dflt = null;
+ strategy = nullable ? ColumnStrategy.NULLABLE
+ : ColumnStrategy.NOT_NULLABLE;
+ }
+ )
+ [
+ <PRIMARY> { s.add(this); } <KEY> {
+ columnList = SqlNodeList.of(id);
+ list.add(SqlDdlNodes.primary(s.end(columnList), null, columnList));
+ }
+ ]
+ {
+ list.add(
+ SqlDdlNodes.column(s.add(id).end(this), id,
+ type.withNullable(nullable), dflt, strategy));
+ }
+|
+ [ <CONSTRAINT> { s.add(this); } id = SimpleIdentifier() ]
+ <PRIMARY> { s.add(this); } <KEY>
+ columnList = ParenthesizedSimpleIdentifierList() {
+ list.add(SqlDdlNodes.primary(s.end(columnList), id, columnList));
+ }
+}
+
+SqlNodeList TableElementList() :
+{
+ final Span s;
+ final List<SqlNode> list = new ArrayList<SqlNode>();
+}
+{
+ <LPAREN> { s = span(); }
+ TableElement(list)
+ (
+ <COMMA> TableElement(list)
+ )*
+ <RPAREN> {
+ return new SqlNodeList(list, s.end(this));
+ }
+}
+
+SqlCreate SqlCreateTable(Span s, boolean replace) :
+{
+ final boolean ifNotExists;
+ final SqlIdentifier id;
+ final SqlNodeList columnList;
+ final SqlNodeList optionList;
+}
+{
+ <TABLE>
+ ifNotExists = IfNotExistsOpt()
+ id = CompoundIdentifier()
+ columnList = TableElementList()
+ (
+ <WITH> { s.add(this); } optionList = CreateTableOptionList()
+ |
+ { optionList = null; }
+ )
+ {
+ return new IgniteSqlCreateTable(s.end(this), ifNotExists, id, columnList, optionList);
+ }
+}
+
+void InfixCast(List<Object> list, ExprContext exprContext, Span s) :
+{
+ final SqlDataTypeSpec dt;
+}
+{
+ <INFIX_CAST> {
+ checkNonQueryExpression(exprContext);
+ }
+ dt = DataType() {
+ list.add(
+ new SqlParserUtil.ToTreeListItem(SqlLibraryOperators.INFIX_CAST,
+ s.pos()));
+ list.add(dt);
+ }
+}
diff --git a/modules/calcite/src/main/java/org/apache/calcite/sql/IgniteSqlNode.java b/modules/calcite/src/main/java/org/apache/calcite/sql/IgniteSqlNode.java
new file mode 100644
index 0000000..5ce6fb0
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/calcite/sql/IgniteSqlNode.java
@@ -0,0 +1,31 @@
+/*
+ * 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.sql;
+
+import org.apache.calcite.sql.parser.SqlParserPos;
+
+/** A {@link SqlNode} that exposes constructor for descendant classes. */
+public abstract class IgniteSqlNode extends SqlNode {
+ /**
+ * Creates a node.
+ *
+ * @param pos Parser position, must not be null.
+ */
+ protected IgniteSqlNode(SqlParserPos pos) {
+ super(pos);
+ }
+}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
index 53184da..9a52947 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
@@ -24,7 +24,6 @@ import org.apache.calcite.plan.Contexts;
import org.apache.calcite.sql.fun.SqlLibrary;
import org.apache.calcite.sql.fun.SqlLibraryOperatorTableFactory;
import org.apache.calcite.sql.parser.SqlParser;
-import org.apache.calcite.sql.parser.babel.SqlBabelParserImpl;
import org.apache.calcite.sql.validate.SqlConformanceEnum;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql2rel.SqlToRelConverter;
@@ -57,6 +56,7 @@ import org.apache.ignite.internal.processors.query.calcite.prepare.QueryPlanCach
import org.apache.ignite.internal.processors.query.calcite.prepare.QueryPlanCacheImpl;
import org.apache.ignite.internal.processors.query.calcite.schema.SchemaHolder;
import org.apache.ignite.internal.processors.query.calcite.schema.SchemaHolderImpl;
+import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlParserImpl;
import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeSystem;
import org.apache.ignite.internal.processors.query.calcite.util.LifecycleAware;
import org.apache.ignite.internal.processors.query.calcite.util.Service;
@@ -78,7 +78,7 @@ public class CalciteQueryProcessor extends GridProcessorAdapter implements Query
.withDecorrelationEnabled(true))
.parserConfig(
SqlParser.config()
- .withParserFactory(SqlBabelParserImpl.FACTORY)
+ .withParserFactory(IgniteSqlParserImpl.FACTORY)
.withLex(Lex.ORACLE)
.withConformance(SqlConformanceEnum.DEFAULT))
.sqlValidatorConfig(SqlValidator.Config.DEFAULT
@@ -88,6 +88,7 @@ public class CalciteQueryProcessor extends GridProcessorAdapter implements Query
.operatorTable(SqlLibraryOperatorTableFactory.INSTANCE
.getOperatorTable(
SqlLibrary.STANDARD,
+ SqlLibrary.POSTGRESQL,
SqlLibrary.ORACLE,
SqlLibrary.MYSQL))
// Context provides a way to store data within the planner session that can be accessed in planner rules.
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java
index fa97dbd..b9ca57d 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java
@@ -41,6 +41,7 @@ import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelRoot;
import org.apache.calcite.rel.hint.Hintable;
import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.sql.SqlDdl;
import org.apache.calcite.sql.SqlExplain;
import org.apache.calcite.sql.SqlExplainLevel;
import org.apache.calcite.sql.SqlNode;
@@ -68,6 +69,7 @@ import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.QueryCancellable;
import org.apache.ignite.internal.processors.query.QueryContext;
import org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessor;
+import org.apache.ignite.internal.processors.query.calcite.exec.ddl.DdlCommandHandler;
import org.apache.ignite.internal.processors.query.calcite.exec.rel.Inbox;
import org.apache.ignite.internal.processors.query.calcite.exec.rel.Node;
import org.apache.ignite.internal.processors.query.calcite.exec.rel.Outbox;
@@ -83,6 +85,7 @@ import org.apache.ignite.internal.processors.query.calcite.metadata.FragmentMapp
import org.apache.ignite.internal.processors.query.calcite.metadata.MappingService;
import org.apache.ignite.internal.processors.query.calcite.metadata.RemoteException;
import org.apache.ignite.internal.processors.query.calcite.prepare.CacheKey;
+import org.apache.ignite.internal.processors.query.calcite.prepare.DdlPlan;
import org.apache.ignite.internal.processors.query.calcite.prepare.ExplainPlan;
import org.apache.ignite.internal.processors.query.calcite.prepare.FieldsMetadata;
import org.apache.ignite.internal.processors.query.calcite.prepare.FieldsMetadataImpl;
@@ -99,6 +102,7 @@ import org.apache.ignite.internal.processors.query.calcite.prepare.QueryPlanCach
import org.apache.ignite.internal.processors.query.calcite.prepare.QueryTemplate;
import org.apache.ignite.internal.processors.query.calcite.prepare.Splitter;
import org.apache.ignite.internal.processors.query.calcite.prepare.ValidationResult;
+import org.apache.ignite.internal.processors.query.calcite.prepare.ddl.DdlSqlToCommandConverter;
import org.apache.ignite.internal.processors.query.calcite.rel.IgniteConvention;
import org.apache.ignite.internal.processors.query.calcite.rel.IgniteRel;
import org.apache.ignite.internal.processors.query.calcite.schema.SchemaHolder;
@@ -112,6 +116,7 @@ import org.apache.ignite.internal.processors.query.calcite.util.Commons;
import org.apache.ignite.internal.processors.query.calcite.util.HintUtils;
import org.apache.ignite.internal.processors.query.calcite.util.ListFieldsQueryCursor;
import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils;
+import org.apache.ignite.internal.processors.query.h2.H2Utils;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.T2;
import org.apache.ignite.internal.util.typedef.X;
@@ -120,6 +125,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static java.util.Collections.singletonList;
+import static org.apache.calcite.rel.type.RelDataType.PRECISION_NOT_SPECIFIED;
import static org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessor.FRAMEWORK_CONFIG;
import static org.apache.ignite.internal.processors.query.calcite.externalize.RelJsonReader.fromJson;
@@ -176,6 +182,12 @@ public class ExecutionServiceImpl<Row> extends AbstractService implements Execut
/** */
private final RowHandler<Row> handler;
+ /** */
+ private final DdlCommandHandler ddlCmdHnd;
+
+ /** */
+ private final DdlSqlToCommandConverter ddlConverter;
+
/**
* @param ctx Kernal.
*/
@@ -185,6 +197,11 @@ public class ExecutionServiceImpl<Row> extends AbstractService implements Execut
discoLsnr = (e, c) -> onNodeLeft(e.eventNode().id());
running = new ConcurrentHashMap<>();
+ ddlConverter = new DdlSqlToCommandConverter();
+
+ ddlCmdHnd = new DdlCommandHandler(
+ ctx::query, ctx.cache(), ctx.security(), () -> schemaHolder().schema()
+ );
}
/**
@@ -552,6 +569,9 @@ public class ExecutionServiceImpl<Row> extends AbstractService implements Execut
case EXPLAIN:
return prepareExplain(sqlNode, ctx);
+ case CREATE_TABLE:
+ return prepareDdl(sqlNode, ctx);
+
default:
throw new IgniteSQLException("Unsupported operation [" +
"sqlNodeKind=" + sqlNode.getKind() + "; " +
@@ -597,6 +617,15 @@ public class ExecutionServiceImpl<Row> extends AbstractService implements Execut
}
/** */
+ private QueryPlan prepareDdl(SqlNode sqlNode, PlanningContext ctx) {
+ assert sqlNode instanceof SqlDdl : sqlNode == null ? "null" : sqlNode.getClass().getName();
+
+ SqlDdl ddlNode = (SqlDdl)sqlNode;
+
+ return new DdlPlan(ddlConverter.convert(ddlNode, ctx));
+ }
+
+ /** */
private IgniteRel optimize(SqlNode sqlNode, IgnitePlanner planner) {
try {
// Convert to Relational operators graph
@@ -647,7 +676,7 @@ public class ExecutionServiceImpl<Row> extends AbstractService implements Execut
private FieldsMetadata explainFieldsMetadata(PlanningContext ctx) {
IgniteTypeFactory factory = ctx.typeFactory();
RelDataType planStrDataType =
- factory.createSqlType(SqlTypeName.VARCHAR, RelDataType.PRECISION_NOT_SPECIFIED);
+ factory.createSqlType(SqlTypeName.VARCHAR, PRECISION_NOT_SPECIFIED);
T2<String, RelDataType> planField = new T2<>(ExplainPlan.PLAN_COL_NAME, planStrDataType);
RelDataType planDataType = factory.createStructType(singletonList(planField));
@@ -663,6 +692,8 @@ public class ExecutionServiceImpl<Row> extends AbstractService implements Execut
return executeQuery(qryId, (MultiStepPlan) plan, pctx);
case EXPLAIN:
return executeExplain((ExplainPlan)plan, pctx);
+ case DDL:
+ return executeDdl((DdlPlan)plan, pctx);
default:
throw new AssertionError("Unexpected plan type: " + plan);
@@ -670,6 +701,19 @@ public class ExecutionServiceImpl<Row> extends AbstractService implements Execut
}
/** */
+ private FieldsQueryCursor<List<?>> executeDdl(DdlPlan plan, PlanningContext pctx) {
+ try {
+ ddlCmdHnd.handle(pctx, plan.command());
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteSQLException("Failed to execute DDL statement [stmt=" + pctx.query() +
+ ", err=" + e.getMessage() + ']', e);
+ }
+
+ return H2Utils.zeroCursor();
+ }
+
+ /** */
private FieldsQueryCursor<List<?>> executeQuery(UUID qryId, MultiStepPlan plan, PlanningContext pctx) {
plan.init(pctx);
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ddl/DdlCommandHandler.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ddl/DdlCommandHandler.java
new file mode 100644
index 0000000..529bcae
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ddl/DdlCommandHandler.java
@@ -0,0 +1,215 @@
+/*
+ * 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.ignite.internal.processors.query.calcite.exec.ddl;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
+
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.processors.cache.GridCacheProcessor;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.query.GridQueryProcessor;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.QueryEntityEx;
+import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.internal.processors.query.calcite.prepare.PlanningContext;
+import org.apache.ignite.internal.processors.query.calcite.prepare.ddl.ColumnDefinition;
+import org.apache.ignite.internal.processors.query.calcite.prepare.ddl.CreateTableCommand;
+import org.apache.ignite.internal.processors.query.calcite.prepare.ddl.DdlCommand;
+import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
+import org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
+import org.apache.ignite.internal.processors.security.IgniteSecurity;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.plugin.security.SecurityPermission;
+
+import static org.apache.ignite.internal.processors.query.QueryUtils.convert;
+import static org.apache.ignite.internal.processors.query.QueryUtils.isDdlOnSchemaSupported;
+
+/** */
+public class DdlCommandHandler {
+ /** */
+ private final Supplier<GridQueryProcessor> qryProcessorSupp;
+
+ /** */
+ private final GridCacheProcessor cacheProcessor;
+
+ /** */
+ private final IgniteSecurity security;
+
+ /** */
+ private final Supplier<SchemaPlus> schemaSupp;
+
+ /** */
+ public DdlCommandHandler(Supplier<GridQueryProcessor> qryProcessorSupp, GridCacheProcessor cacheProcessor,
+ IgniteSecurity security, Supplier<SchemaPlus> schemaSupp) {
+ this.qryProcessorSupp = qryProcessorSupp;
+ this.cacheProcessor = cacheProcessor;
+ this.security = security;
+ this.schemaSupp = schemaSupp;
+ }
+
+ /** */
+ public void handle(PlanningContext pctx, DdlCommand cmd) throws IgniteCheckedException {
+ if (cmd instanceof CreateTableCommand)
+ handle0(pctx, (CreateTableCommand)cmd);
+
+ else {
+ throw new IgniteSQLException("Unsupported DDL operation [" +
+ "cmdName=" + (cmd == null ? null : cmd.getClass().getSimpleName()) + "; " +
+ "querySql=\"" + pctx.query() + "\"]", IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ }
+ }
+
+ /** */
+ private void handle0(PlanningContext pctx, CreateTableCommand cmd) throws IgniteCheckedException {
+ security.authorize(cmd.cacheName(), SecurityPermission.CACHE_CREATE);
+
+ isDdlOnSchemaSupported(cmd.schemaName());
+
+ if (schemaSupp.get().getSubSchema(cmd.schemaName()).getTable(cmd.tableName()) != null) {
+ if (cmd.ifNotExists())
+ return;
+
+ throw new SchemaOperationException(SchemaOperationException.CODE_TABLE_EXISTS, cmd.tableName());
+ }
+
+ CacheConfiguration<?, ?> ccfg = new CacheConfiguration<>(cmd.tableName());
+
+ QueryEntity e = toQueryEntity(cmd, pctx);
+
+ ccfg.setQueryEntities(Collections.singleton(e));
+ ccfg.setSqlSchema(cmd.schemaName());
+
+ SchemaOperationException err =
+ QueryUtils.checkQueryEntityConflicts(ccfg, cacheProcessor.cacheDescriptors().values());
+
+ if (err != null)
+ throw convert(err);
+
+ qryProcessorSupp.get().dynamicTableCreate(
+ cmd.schemaName(),
+ e,
+ cmd.templateName(),
+ cmd.cacheName(),
+ cmd.cacheGroup(),
+ cmd.dataRegionName(),
+ cmd.affinityKey(),
+ cmd.atomicityMode(),
+ cmd.writeSynchronizationMode(),
+ cmd.backups(),
+ cmd.ifNotExists(),
+ cmd.encrypted(),
+ null
+ );
+ }
+
+ /** */
+ private QueryEntity toQueryEntity(CreateTableCommand cmd, PlanningContext pctx) {
+ QueryEntity res = new QueryEntity();
+
+ res.setTableName(cmd.tableName());
+
+ Set<String> notNullFields = null;
+
+ HashMap<String, Object> dfltValues = new HashMap<>();
+
+ Map<String, Integer> precision = new HashMap<>();
+ Map<String, Integer> scale = new HashMap<>();
+
+ IgniteTypeFactory tf = pctx.typeFactory();
+
+ for (ColumnDefinition col : cmd.columns()) {
+ String name = col.name();
+
+ res.addQueryField(name, tf.getJavaClass(col.type()).getTypeName(), null);
+
+ if (!col.nullable()) {
+ if (notNullFields == null)
+ notNullFields = new HashSet<>();
+
+ notNullFields.add(name);
+ }
+
+ if (col.defaultValue() != null)
+ dfltValues.put(name, col.defaultValue());
+
+ if (col.precision() != null)
+ precision.put(name, col.precision());
+
+ if (col.scale() != null)
+ scale.put(name, col.scale());
+ }
+
+ if (!F.isEmpty(dfltValues))
+ res.setDefaultFieldValues(dfltValues);
+
+ if (!F.isEmpty(precision))
+ res.setFieldsPrecision(precision);
+
+ if (!F.isEmpty(scale))
+ res.setFieldsScale(scale);
+
+ String valTypeName = QueryUtils.createTableValueTypeName(cmd.schemaName(), cmd.tableName());
+
+ String keyTypeName;
+ if ((!F.isEmpty(cmd.primaryKeyColumns()) && cmd.primaryKeyColumns().size() > 1) || !F.isEmpty(cmd.keyTypeName())) {
+ keyTypeName = cmd.keyTypeName();
+
+ if (F.isEmpty(keyTypeName))
+ keyTypeName = QueryUtils.createTableKeyTypeName(valTypeName);
+
+ if (!F.isEmpty(cmd.primaryKeyColumns()))
+ res.setKeyFields(new LinkedHashSet<>(cmd.primaryKeyColumns()));
+ }
+ else if (!F.isEmpty(cmd.primaryKeyColumns()) && cmd.primaryKeyColumns().size() == 1) {
+ String pkFieldName = cmd.primaryKeyColumns().get(0);
+
+ keyTypeName = res.getFields().get(pkFieldName);
+
+ res.setKeyFieldName(pkFieldName);
+ }
+ else {
+ // if pk is not explicitly set, we create it ourselves
+ keyTypeName = IgniteUuid.class.getName();
+
+ res = new QueryEntityEx(res).implicitPk(true);
+ }
+
+ res.setValueType(F.isEmpty(cmd.valueTypeName()) ? valTypeName : cmd.valueTypeName());
+ res.setKeyType(keyTypeName);
+
+ if (!F.isEmpty(notNullFields)) {
+ QueryEntityEx res0 = new QueryEntityEx(res);
+
+ res0.setNotNullFields(notNullFields);
+
+ res = res0;
+ }
+
+ return res;
+ }
+}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/DdlPlan.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/DdlPlan.java
new file mode 100644
index 0000000..79d3880
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/DdlPlan.java
@@ -0,0 +1,46 @@
+/*
+ * 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.ignite.internal.processors.query.calcite.prepare;
+
+import org.apache.ignite.internal.processors.query.calcite.prepare.ddl.DdlCommand;
+
+/** */
+public class DdlPlan implements QueryPlan {
+ /** */
+ private final DdlCommand cmd;
+
+ /** */
+ public DdlPlan(DdlCommand cmd) {
+ this.cmd = cmd;
+ }
+
+ /** */
+ public DdlCommand command() {
+ return cmd;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Type type() {
+ return Type.DDL;
+ }
+
+ /** {@inheritDoc} */
+ @Override public QueryPlan copy() {
+ return this;
+ }
+}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java
index bd584b9..ec7daaf 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java
@@ -23,6 +23,7 @@ import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+
import com.google.common.collect.ImmutableList;
import org.apache.calcite.plan.Context;
import org.apache.calcite.plan.RelOptCluster;
@@ -46,6 +47,7 @@ import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexExecutor;
+import org.apache.calcite.sql.SqlDataTypeSpec;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlNodeList;
@@ -186,6 +188,10 @@ public class IgnitePlanner implements Planner, RelOptTable.ViewExpander {
return Pair.of(validatedNode, type);
}
+ public RelDataType conver(SqlDataTypeSpec typeSpec) {
+ return typeSpec.deriveType(validator());
+ }
+
/**
* Validates a SQL statement.
*
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/ColumnDefinition.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/ColumnDefinition.java
new file mode 100644
index 0000000..b865078
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/ColumnDefinition.java
@@ -0,0 +1,83 @@
+/*
+ * 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.ignite.internal.processors.query.calcite.prepare.ddl;
+
+import java.util.Objects;
+
+import org.apache.calcite.rel.type.RelDataType;
+import org.jetbrains.annotations.Nullable;
+
+/** Definies a particular column within table. */
+public class ColumnDefinition {
+ /** */
+ private final String name;
+
+ /** */
+ private final RelDataType type;
+
+ /** */
+ private final Object dflt;
+
+ /** Creates a column definition. */
+ public ColumnDefinition(String name, RelDataType type, @Nullable Object dflt) {
+ this.name = Objects.requireNonNull(name, "name");
+ this.type = Objects.requireNonNull(type, "type");
+ this.dflt = dflt;
+ }
+
+ /**
+ * @return Column's name.
+ */
+ public String name() {
+ return name;
+ }
+
+ /**
+ * @return Column's type.
+ */
+ public RelDataType type() {
+ return type;
+ }
+
+ /**
+ * @return Column's default value.
+ */
+ public @Nullable Object defaultValue() {
+ return dflt;
+ }
+
+ /**
+ * @return {@code true} if this column accepts nulls.
+ */
+ public boolean nullable() {
+ return type.isNullable();
+ }
+
+ /**
+ * @return Column's precision.
+ */
+ public Integer precision() {
+ return type.getPrecision() != RelDataType.PRECISION_NOT_SPECIFIED ? type.getPrecision() : null;
+ }
+
+ /**
+ * @return Column's scale.
+ */
+ public Integer scale() {
+ return type.getScale() != RelDataType.SCALE_NOT_SPECIFIED ? type.getScale() : null;
+ }
+}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/CreateTableCommand.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/CreateTableCommand.java
new file mode 100644
index 0000000..f474f49
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/CreateTableCommand.java
@@ -0,0 +1,304 @@
+/*
+ * 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.ignite.internal.processors.query.calcite.prepare.ddl;
+
+import java.util.List;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * CREATE TABLE statement.
+ */
+public class CreateTableCommand implements DdlCommand {
+ /**
+ * Schema name upon which this statement has been issued - <b>not</b> the name of the schema where this new table
+ * will be created.
+ */
+ private String schemaName;
+
+ /** Table name. */
+ private String tblName;
+
+ /** Cache name upon which new cache configuration for this table must be based. */
+ private String templateName;
+
+ /** Name of new cache associated with this table. */
+ private String cacheName;
+
+ /** Name of cache key type. */
+ private String keyTypeName;
+
+ /** Name of cache value type. */
+ private String valTypeName;
+
+ /** Group to put new cache into. */
+ private String cacheGrp;
+
+ /** Atomicity mode for new cache. */
+ private CacheAtomicityMode atomicityMode;
+
+ /** Write sync mode. */
+ private CacheWriteSynchronizationMode writeSyncMode;
+
+ /** Backups number for new cache. */
+ private Integer backups;
+
+ /** Quietly ignore this command if table already exists. */
+ private boolean ifNotExists;
+
+ /** Columns. */
+ private List<ColumnDefinition> cols;
+
+ /** Primary key columns. */
+ private List<String> pkCols;
+
+ /** Name of the column that represents affinity key. */
+ private String affinityKey;
+
+ /** Data region. */
+ private String dataRegionName;
+
+ /** Encrypted flag. */
+ private boolean encrypted;
+
+ /**
+ * @return Cache name upon which new cache configuration for this table must be based.
+ */
+ public String templateName() {
+ return templateName;
+ }
+
+ /**
+ * @param templateName Cache name upon which new cache configuration for this table must be based.
+ */
+ public void templateName(String templateName) {
+ this.templateName = templateName;
+ }
+
+ /**
+ * @return Name of new cache associated with this table.
+ */
+ public String cacheName() {
+ return cacheName;
+ }
+
+ /**
+ * @param cacheName Name of new cache associated with this table.
+ */
+ public void cacheName(String cacheName) {
+ this.cacheName = cacheName;
+ }
+
+ /**
+ * @return Name of cache key type.
+ */
+ public String keyTypeName() {
+ return keyTypeName;
+ }
+
+ /**
+ * @param keyTypeName Name of cache key type.
+ */
+ public void keyTypeName(String keyTypeName) {
+ this.keyTypeName = keyTypeName;
+ }
+
+ /**
+ * @return Name of cache value type.
+ */
+ public String valueTypeName() {
+ return valTypeName;
+ }
+
+ /**
+ * @param valTypeName Name of cache value type.
+ */
+ public void valueTypeName(String valTypeName) {
+ this.valTypeName = valTypeName;
+ }
+
+ /**
+ * @return Group to put new cache into.
+ */
+ public String cacheGroup() {
+ return cacheGrp;
+ }
+
+ /**
+ * @param cacheGrp Group to put new cache into.
+ */
+ public void cacheGroup(String cacheGrp) {
+ this.cacheGrp = cacheGrp;
+ }
+
+ /**
+ * @return Atomicity mode for new cache.
+ */
+ public CacheAtomicityMode atomicityMode() {
+ return atomicityMode;
+ }
+
+ /**
+ * @param atomicityMode Atomicity mode for new cache.
+ */
+ public void atomicityMode(CacheAtomicityMode atomicityMode) {
+ this.atomicityMode = atomicityMode;
+ }
+
+ /**
+ * @return Write sync mode for new cache.
+ */
+ public CacheWriteSynchronizationMode writeSynchronizationMode() {
+ return writeSyncMode;
+ }
+
+ /**
+ * @param writeSyncMode Write sync mode for new cache.
+ */
+ public void writeSynchronizationMode(CacheWriteSynchronizationMode writeSyncMode) {
+ this.writeSyncMode = writeSyncMode;
+ }
+
+ /**
+ * @return Backups number for new cache.
+ */
+ @Nullable public Integer backups() {
+ return backups;
+ }
+
+ /**
+ * @param backups Backups number for new cache.
+ */
+ public void backups(Integer backups) {
+ this.backups = backups;
+ }
+
+ /**
+ * @return Columns.
+ */
+ public List<ColumnDefinition> columns() {
+ return cols;
+ }
+
+ /**
+ * @param cols Columns.
+ */
+ public void columns(List<ColumnDefinition> cols) {
+ this.cols = cols;
+ }
+
+ /**
+ * @return Primary key columns.
+ */
+ public List<String> primaryKeyColumns() {
+ return pkCols;
+ }
+
+ /**
+ * @param pkCols Primary key columns.
+ */
+ public void primaryKeyColumns(List<String> pkCols) {
+ this.pkCols = pkCols;
+ }
+
+ /**
+ * @return Name of the column that represents affinity key.
+ */
+ public String affinityKey() {
+ return affinityKey;
+ }
+
+ /**
+ * @param affinityKey Name of the column that represents affinity key.
+ */
+ public void affinityKey(String affinityKey) {
+ this.affinityKey = affinityKey;
+ }
+
+ /**
+ * @return Schema name upon which this statement has been issued.
+ */
+ public String schemaName() {
+ return schemaName;
+ }
+
+ /**
+ * @param schemaName Schema name upon which this statement has been issued.
+ */
+ public void schemaName(String schemaName) {
+ this.schemaName = schemaName;
+ }
+
+ /**
+ * @return Table name.
+ */
+ public String tableName() {
+ return tblName;
+ }
+
+ /**
+ * @param tblName Table name.
+ */
+ public void tableName(String tblName) {
+ this.tblName = tblName;
+ }
+
+ /**
+ * @return Quietly ignore this command if table already exists.
+ */
+ public boolean ifNotExists() {
+ return ifNotExists;
+ }
+
+ /**
+ * @param ifNotExists Quietly ignore this command if table already exists.
+ */
+ public void ifNotExists(boolean ifNotExists) {
+ this.ifNotExists = ifNotExists;
+ }
+
+ /**
+ * @return Data region name.
+ */
+ public String dataRegionName() {
+ return dataRegionName;
+ }
+
+ /**
+ * @param dataRegionName Data region name.
+ */
+ public void dataRegionName(String dataRegionName) {
+ this.dataRegionName = dataRegionName;
+ }
+
+ /**
+ * @return Encrypted flag.
+ */
+ public boolean encrypted() {
+ return encrypted;
+ }
+
+ /**
+ * @param encrypted Encrypted flag.
+ */
+ public void encrypted(boolean encrypted) {
+ this.encrypted = encrypted;
+ }
+}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlCommand.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlCommand.java
new file mode 100644
index 0000000..0d46ad4
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlCommand.java
@@ -0,0 +1,21 @@
+/*
+ * 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.ignite.internal.processors.query.calcite.prepare.ddl;
+
+/** Common interface to group all DDL operations. */
+public interface DdlCommand {
+}
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
new file mode 100644
index 0000000..4d63e76
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlSqlToCommandConverter.java
@@ -0,0 +1,344 @@
+/*
+ * 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.ignite.internal.processors.query.calcite.prepare.ddl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.sql.SqlDdl;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlNodeList;
+import org.apache.calcite.sql.SqlNumericLiteral;
+import org.apache.calcite.sql.ddl.SqlColumnDeclaration;
+import org.apache.calcite.sql.ddl.SqlKeyConstraint;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.internal.processors.query.calcite.prepare.IgnitePlanner;
+import org.apache.ignite.internal.processors.query.calcite.prepare.PlanningContext;
+import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTable;
+import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOption;
+import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOptionEnum;
+import org.apache.ignite.internal.util.typedef.F;
+
+import static org.apache.calcite.sql.type.SqlTypeName.BOOLEAN;
+import static org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOptionEnum.AFFINITY_KEY;
+import static org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOptionEnum.ATOMICITY;
+import static org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOptionEnum.BACKUPS;
+import static org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOptionEnum.CACHE_GROUP;
+import static org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOptionEnum.CACHE_NAME;
+import static org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOptionEnum.DATA_REGION;
+import static org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOptionEnum.ENCRYPTED;
+import static org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOptionEnum.KEY_TYPE;
+import static org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOptionEnum.TEMPLATE;
+import static org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOptionEnum.VALUE_TYPE;
+import static org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOptionEnum.WRITE_SYNCHRONIZATION_MODE;
+
+/** */
+public class DdlSqlToCommandConverter {
+ /** Processor that validates a value is a Sql Identifier. */
+ private static final BiFunction<IgniteSqlCreateTableOption, PlanningContext, String> VALUE_IS_IDENTIFIER_VALIDATOR = (opt, ctx) -> {
+ if (!(opt.value() instanceof SqlIdentifier) || !((SqlIdentifier)opt.value()).isSimple())
+ throwOptionParsingException(opt, "a simple identifier", ctx.query());
+
+ return ((SqlIdentifier)opt.value()).getSimple();
+ };
+
+ /** Processor that unconditionally throws an AssertionException. */
+ private static final TableOptionProcessor<Void> UNSUPPORTED_OPTION_PROCESSOR = new TableOptionProcessor<>(
+ null,
+ (opt, ctx) -> {
+ throw new AssertionError("Unsupported option " + opt.key());
+ },
+ null);
+
+ /** Map of the supported table option processors. */
+ private final Map<IgniteSqlCreateTableOptionEnum, TableOptionProcessor<?>> tblOptionProcessors = Stream.of(
+ new TableOptionProcessor<>(TEMPLATE, VALUE_IS_IDENTIFIER_VALIDATOR, CreateTableCommand::templateName),
+ new TableOptionProcessor<>(AFFINITY_KEY, VALUE_IS_IDENTIFIER_VALIDATOR, CreateTableCommand::affinityKey),
+ new TableOptionProcessor<>(CACHE_GROUP, VALUE_IS_IDENTIFIER_VALIDATOR, CreateTableCommand::cacheGroup),
+ new TableOptionProcessor<>(CACHE_NAME, VALUE_IS_IDENTIFIER_VALIDATOR, CreateTableCommand::cacheName),
+ new TableOptionProcessor<>(DATA_REGION, VALUE_IS_IDENTIFIER_VALIDATOR, CreateTableCommand::dataRegionName),
+ new TableOptionProcessor<>(KEY_TYPE, VALUE_IS_IDENTIFIER_VALIDATOR, CreateTableCommand::keyTypeName),
+ new TableOptionProcessor<>(VALUE_TYPE, VALUE_IS_IDENTIFIER_VALIDATOR, CreateTableCommand::valueTypeName),
+ new TableOptionProcessor<>(ATOMICITY, validatorForEnumValue(CacheAtomicityMode.class), CreateTableCommand::atomicityMode),
+ new TableOptionProcessor<>(WRITE_SYNCHRONIZATION_MODE, validatorForEnumValue(CacheWriteSynchronizationMode.class), CreateTableCommand::writeSynchronizationMode),
+ new TableOptionProcessor<>(BACKUPS, (opt, ctx) -> {
+ if (!(opt.value() instanceof SqlNumericLiteral)
+ || !((SqlNumericLiteral)opt.value()).isInteger()
+ || ((SqlLiteral)opt.value()).intValue(true) < 0
+ )
+ throwOptionParsingException(opt, "a non-negative integer", ctx.query());
+
+ return ((SqlLiteral)opt.value()).intValue(true);
+ }, CreateTableCommand::backups),
+ new TableOptionProcessor<>(ENCRYPTED, (opt, ctx) -> {
+ if (!(opt.value() instanceof SqlLiteral) && ((SqlLiteral)opt.value()).getTypeName() != BOOLEAN)
+ throwOptionParsingException(opt, "a boolean", ctx.query());
+
+ return ((SqlLiteral)opt.value()).booleanValue();
+ }, CreateTableCommand::encrypted)
+ ).collect(Collectors.toMap(TableOptionProcessor::key, Function.identity()));
+
+ /**
+ * Converts a given ddl AST to a ddl command.
+ *
+ * @param ddlNode Root node of the given AST.
+ * @param ctx Planning context.
+ */
+ public DdlCommand convert(SqlDdl ddlNode, PlanningContext ctx) {
+ if (ddlNode instanceof IgniteSqlCreateTable)
+ return convertCreateTable((IgniteSqlCreateTable)ddlNode, ctx);
+
+ throw new IgniteSQLException("Unsupported operation [" +
+ "sqlNodeKind=" + ddlNode.getKind() + "; " +
+ "querySql=\"" + ctx.query() + "\"]", IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ }
+
+ /**
+ * Converts a given CreateTable AST to a CreateTable command.
+ *
+ * @param createTblNode Root node of the given AST.
+ * @param ctx Planning context.
+ */
+ private CreateTableCommand convertCreateTable(IgniteSqlCreateTable createTblNode, PlanningContext ctx) {
+ String schemaName, tableName;
+
+ if (createTblNode.name().isSimple()) {
+ schemaName = ctx.schemaName();
+ tableName = createTblNode.name().getSimple();
+ }
+ else {
+ SqlIdentifier schemaId = createTblNode.name().skipLast(1);
+
+ if (!schemaId.isSimple()) {
+ throw new IgniteSQLException("Unexpected value of schemaName [" +
+ "expected a simple identifier, but was " + schemaId + "; " +
+ "querySql=\"" + ctx.query() + "\"]", IgniteQueryErrorCode.PARSING);
+ }
+
+ schemaName = schemaId.getSimple();
+
+ SqlIdentifier tableId = createTblNode.name().getComponent(schemaId.names.size());
+
+ if (!tableId.isSimple()) {
+ throw new IgniteSQLException("Unexpected value of tableName [" +
+ "expected a simple identifier, but was " + tableId + "; " +
+ "querySql=\"" + ctx.query() + "\"]", IgniteQueryErrorCode.PARSING);
+ }
+
+ tableName = tableId.getSimple();
+ }
+
+ ensureSchemaExists(ctx, schemaName);
+
+ CreateTableCommand createTblCmd = new CreateTableCommand();
+
+ createTblCmd.schemaName(schemaName);
+ createTblCmd.tableName(tableName);
+ createTblCmd.ifNotExists(createTblNode.ifNotExists());
+ createTblCmd.templateName(QueryUtils.TEMPLATE_PARTITIONED);
+
+ if (createTblNode.createOptionList() != null) {
+ for (SqlNode optNode : createTblNode.createOptionList().getList()) {
+ IgniteSqlCreateTableOption opt = (IgniteSqlCreateTableOption)optNode;
+
+ tblOptionProcessors.getOrDefault(opt.key(), UNSUPPORTED_OPTION_PROCESSOR).process(opt, ctx, createTblCmd);
+ }
+ }
+
+ List<SqlColumnDeclaration> colDeclarations = createTblNode.columnList().getList().stream()
+ .filter(SqlColumnDeclaration.class::isInstance)
+ .map(SqlColumnDeclaration.class::cast)
+ .collect(Collectors.toList());
+
+ IgnitePlanner planner = ctx.planner();
+
+ List<ColumnDefinition> cols = new ArrayList<>();
+
+ for (SqlColumnDeclaration col : colDeclarations) {
+ if (!col.name.isSimple())
+ throw new IgniteSQLException("Unexpected value of columnName [" +
+ "expected a simple identifier, but was " + col.name + "; " +
+ "querySql=\"" + ctx.query() + "\"]", IgniteQueryErrorCode.PARSING);
+
+ String name = col.name.getSimple();
+ RelDataType type = planner.conver(col.dataType);
+
+ Object dflt = null;
+ if (col.expression != null)
+ dflt = ((SqlLiteral)col.expression).getValue();
+
+ cols.add(new ColumnDefinition(name, type, dflt));
+ }
+
+ createTblCmd.columns(cols);
+
+ List<SqlKeyConstraint> pkConstraints = createTblNode.columnList().getList().stream()
+ .filter(SqlKeyConstraint.class::isInstance)
+ .map(SqlKeyConstraint.class::cast)
+ .collect(Collectors.toList());
+
+ if (pkConstraints.size() > 1)
+ throw new IgniteSQLException("Unexpected amount of primary key constraints [" +
+ "expected at most one, but was " + pkConstraints.size() + "; " +
+ "querySql=\"" + ctx.query() + "\"]", IgniteQueryErrorCode.PARSING);
+
+ if (!F.isEmpty(pkConstraints)) {
+ Set<String> dedupSet = new HashSet<>();
+
+ List<String> pkCols = pkConstraints.stream()
+ .map(pk -> pk.getOperandList().get(1))
+ .map(SqlNodeList.class::cast)
+ .flatMap(l -> l.getList().stream())
+ .map(SqlIdentifier.class::cast)
+ .map(SqlIdentifier::getSimple)
+ .filter(dedupSet::add)
+ .collect(Collectors.toList());
+
+ createTblCmd.primaryKeyColumns(pkCols);
+ }
+
+ return createTblCmd;
+ }
+
+ /** */
+ private void ensureSchemaExists(PlanningContext ctx, String schemaName) {
+ if (ctx.catalogReader().getRootSchema().getSubSchema(schemaName, true) == null)
+ throw new IgniteSQLException("Schema with name " + schemaName + " not found",
+ IgniteQueryErrorCode.SCHEMA_NOT_FOUND);
+ }
+
+ /**
+ * Short cut for validating that option value is a simple identifier.
+ *
+ * @param opt An option to validate.
+ * @param ctx Planning context.
+ * @throws IgniteSQLException In case the validation was failed.
+ */
+ private String paramIsSqlIdentifierValidator(IgniteSqlCreateTableOption opt, PlanningContext ctx) {
+ if (!(opt.value() instanceof SqlIdentifier) || !((SqlIdentifier)opt.value()).isSimple())
+ throwOptionParsingException(opt, "a simple identifier", ctx.query());
+
+ return ((SqlIdentifier)opt.value()).getSimple();
+ }
+
+ /**
+ * Creates a validator for an option which value should be value of given enumeration.
+ *
+ * @param clz Enumeration class to create validator for.
+ */
+ private static <T extends Enum<T>> BiFunction<IgniteSqlCreateTableOption, PlanningContext, T> validatorForEnumValue(
+ Class<T> clz
+ ) {
+ return (opt, ctx) -> {
+ T val = null;
+
+ if (opt.value() instanceof SqlIdentifier) {
+ val = Arrays.stream(clz.getEnumConstants())
+ .filter(m -> m.name().equalsIgnoreCase(opt.value().toString()))
+ .findFirst()
+ .orElse(null);
+ }
+
+ if (val == null)
+ throwOptionParsingException(opt, "values are "
+ + Arrays.toString(clz.getEnumConstants()), ctx.query());
+
+ return val;
+ };
+ }
+
+ /**
+ * Throws exception with message relates to validation of create table option.
+ *
+ * @param opt An option which validation was failed.
+ * @param exp A string representing expected values.
+ * @param qry A query the validation was failed for.
+ */
+ private static void throwOptionParsingException(IgniteSqlCreateTableOption opt, String exp, String qry) {
+ throw new IgniteSQLException("Unexpected value for param " + opt.key() + " [" +
+ "expected " + exp + ", but was " + opt.value() + "; " +
+ "querySql=\"" + qry + "\"]", IgniteQueryErrorCode.PARSING);
+ }
+
+ /** */
+ private static class TableOptionProcessor<T> {
+ /** */
+ private final IgniteSqlCreateTableOptionEnum key;
+
+ /** */
+ private final BiFunction<IgniteSqlCreateTableOption, PlanningContext, T> validator;
+
+ /** */
+ private final BiConsumer<CreateTableCommand, T> valSetter;
+
+ /**
+ * @param key Option key this processor is supopsed to handle.
+ * @param validator Validator that derives a value from a {@link SqlNode},
+ * validates it and then returns if validation passed,
+ * throws an exeption otherwise.
+ * @param valSetter Setter sets the value recived from the validator
+ * to the given {@link CreateTableCommand}.
+ */
+ private TableOptionProcessor(
+ IgniteSqlCreateTableOptionEnum key,
+ BiFunction<IgniteSqlCreateTableOption, PlanningContext, T> validator,
+ BiConsumer<CreateTableCommand, T> valSetter
+ ) {
+ this.key = key;
+ this.validator = validator;
+ this.valSetter = valSetter;
+ }
+
+ /**
+ * Processes the given option, validates it's value and then sets the appropriate
+ * field in a given command, throws an exception if the validation failed.
+ *
+ * @param opt Option to validate.
+ * @param ctx Planning context.
+ * @param cmd Command instance to set a validation result.
+ */
+ private void process(IgniteSqlCreateTableOption opt, PlanningContext ctx, CreateTableCommand cmd) {
+ assert key == null || key == opt.key() : "Unexpected create table option [expected=" + key + ", actual=" + opt.key() + "]";
+
+ valSetter.accept(cmd, validator.apply(opt, ctx));
+ }
+
+ /**
+ * @return Key this processor is supposed to handle.
+ */
+ private IgniteSqlCreateTableOptionEnum key() {
+ return key;
+ }
+ }
+}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/TableDescriptorImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/TableDescriptorImpl.java
index 2d3881c..44c8437 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/TableDescriptorImpl.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/TableDescriptorImpl.java
@@ -68,6 +68,7 @@ import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.lang.IgniteUuid;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -123,8 +124,23 @@ public class TableDescriptorImpl extends NullInitializerExpressionFactory
// A _key/_val field is virtual in case there is an alias or a property(es) mapped to the _key/_val field.
BitSet virtualFields = new BitSet();
- descriptors.add(
- new KeyValDescriptor(QueryUtils.KEY_FIELD_NAME, typeDesc.keyClass(), true, QueryUtils.KEY_COL));
+ if (typeDesc.implicitPk()) {
+ // pk is not set, thus we need to provide default value for autogenerated key
+ descriptors.add(
+ new KeyValDescriptor(QueryUtils.KEY_FIELD_NAME, typeDesc.keyClass(), true, QueryUtils.KEY_COL) {
+ @Override public Object defaultValue() {
+ return IgniteUuid.randomUuid();
+ }
+ }
+ );
+
+ virtualFields.set(0);
+ }
+ else {
+ descriptors.add(
+ new KeyValDescriptor(QueryUtils.KEY_FIELD_NAME, typeDesc.keyClass(), true, QueryUtils.KEY_COL));
+ }
+
descriptors.add(
new KeyValDescriptor(QueryUtils.VAL_FIELD_NAME, typeDesc.valueClass(), false, QueryUtils.VAL_COL));
@@ -323,21 +339,28 @@ public class TableDescriptorImpl extends NullInitializerExpressionFactory
Object key = handler.get(keyField, row);
- if (key == null) {
- key = newVal(typeDesc.keyTypeName(), typeDesc.keyClass());
+ if (key != null)
+ return TypeUtils.fromInternal(ectx, key, descriptors[QueryUtils.KEY_COL].storageType());
- // skip _key and _val
- for (int i = 2; i < descriptors.length; i++) {
- final ColumnDescriptor desc = descriptors[i];
+ // skip _key and _val
+ for (int i = 2; i < descriptors.length; i++) {
+ final ColumnDescriptor desc = descriptors[i];
- Object fieldVal = handler.get(i, row);
+ if (!desc.field() || !desc.key())
+ continue;
+
+ Object fieldVal = handler.get(i, row);
- if (desc.field() && desc.key() && fieldVal != null)
- desc.set(key, TypeUtils.fromInternal(ectx, fieldVal, desc.storageType()));
+ if (fieldVal != null) {
+ if (key == null)
+ key = newVal(typeDesc.keyTypeName(), typeDesc.keyClass());
+
+ desc.set(key, TypeUtils.fromInternal(ectx, fieldVal, desc.storageType()));
}
}
- else
- key = TypeUtils.fromInternal(ectx, key, descriptors[QueryUtils.KEY_COL].storageType());
+
+ if (key == null)
+ key = descriptors[QueryUtils.KEY_COL].defaultValue();
return key;
}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlCreateTable.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlCreateTable.java
new file mode 100644
index 0000000..e6a28f4
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlCreateTable.java
@@ -0,0 +1,117 @@
+/*
+ * 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.ignite.internal.processors.query.calcite.sql;
+
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.calcite.sql.SqlCreate;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlNodeList;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.util.ImmutableNullableList;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/**
+ * Parse tree for {@code CREATE TABLE} statement with Ignite specific features.
+ */
+public class IgniteSqlCreateTable extends SqlCreate {
+ /** */
+ private final SqlIdentifier name;
+
+ /** */
+ private final @Nullable SqlNodeList columnList;
+
+ /** */
+ private final @Nullable SqlNodeList createOptionList;
+
+ /** */
+ private static final SqlOperator OPERATOR =
+ new SqlSpecialOperator("CREATE TABLE", SqlKind.CREATE_TABLE);
+
+ /** Creates a SqlCreateTable. */
+ protected IgniteSqlCreateTable(SqlParserPos pos, boolean ifNotExists,
+ SqlIdentifier name, @Nullable SqlNodeList columnList, @Nullable SqlNodeList createOptionList) {
+ super(OPERATOR, pos, false, ifNotExists);
+ this.name = Objects.requireNonNull(name, "name");
+ this.columnList = columnList;
+ this.createOptionList = createOptionList;
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("nullness")
+ @Override public List<SqlNode> getOperandList() {
+ return ImmutableNullableList.of(name, columnList, createOptionList);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+ writer.keyword("CREATE");
+ writer.keyword("TABLE");
+ if (ifNotExists)
+ writer.keyword("IF NOT EXISTS");
+
+ name.unparse(writer, leftPrec, rightPrec);
+ if (columnList != null) {
+ SqlWriter.Frame frame = writer.startList("(", ")");
+ for (SqlNode c : columnList) {
+ writer.sep(",");
+ c.unparse(writer, 0, 0);
+ }
+ writer.endList(frame);
+ }
+
+ if (createOptionList != null) {
+ writer.keyword("WITH");
+
+ createOptionList.unparse(writer, 0, 0);
+ }
+ }
+
+ /**
+ * @return Name of the table.
+ */
+ public SqlIdentifier name() {
+ return name;
+ }
+
+ /**
+ * @return List of the specified columns and constraints.
+ */
+ public SqlNodeList columnList() {
+ return columnList;
+ }
+
+ /**
+ * @return List of the specified options to create table with.
+ */
+ public SqlNodeList createOptionList() {
+ return createOptionList;
+ }
+
+ /**
+ * @return Whether the IF NOT EXISTS is specified.
+ */
+ public boolean ifNotExists() {
+ return ifNotExists;
+ }
+}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlCreateTableOption.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlCreateTableOption.java
new file mode 100644
index 0000000..6191568
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlCreateTableOption.java
@@ -0,0 +1,91 @@
+/*
+ * 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.ignite.internal.processors.query.calcite.sql;
+
+import org.apache.calcite.sql.IgniteSqlNode;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.util.SqlVisitor;
+import org.apache.calcite.sql.validate.SqlValidator;
+import org.apache.calcite.sql.validate.SqlValidatorScope;
+import org.apache.calcite.util.Litmus;
+
+/** An AST node representing option to create table with. */
+public class IgniteSqlCreateTableOption extends IgniteSqlNode {
+ /** Option key. */
+ private final IgniteSqlCreateTableOptionEnum key;
+
+ /** Option value. */
+ private final SqlNode value;
+
+ /** Creates IgniteSqlCreateTableOption. */
+ public IgniteSqlCreateTableOption(IgniteSqlCreateTableOptionEnum key, SqlNode value, SqlParserPos pos) {
+ super(pos);
+
+ this.key = key;
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ @Override public SqlNode clone(SqlParserPos pos) {
+ return new IgniteSqlCreateTableOption(key, value, pos);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+ writer.keyword(key.name());
+ writer.keyword("=");
+ value.unparse(writer, leftPrec, rightPrec);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void validate(SqlValidator validator, SqlValidatorScope scope) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public <R> R accept(SqlVisitor<R> visitor) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equalsDeep(SqlNode node, Litmus litmus) {
+ if (!(node instanceof IgniteSqlCreateTableOption))
+ return litmus.fail("{} != {}", this, node);
+
+ IgniteSqlCreateTableOption that = (IgniteSqlCreateTableOption)node;
+ if (key != that.key)
+ return litmus.fail("{} != {}", this, node);
+
+ return value.equalsDeep(that.value, litmus);
+ }
+
+ /**
+ * @return Option's key.
+ */
+ public IgniteSqlCreateTableOptionEnum key() {
+ return key;
+ }
+
+ /**
+ * @return Option's value.
+ */
+ public SqlNode value() {
+ return value;
+ }
+}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlCreateTableOptionEnum.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlCreateTableOptionEnum.java
new file mode 100644
index 0000000..e1d71ad
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlCreateTableOptionEnum.java
@@ -0,0 +1,55 @@
+/*
+ * 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.ignite.internal.processors.query.calcite.sql;
+
+/**
+ * Enumerates the Ignite specific options for CREATE TABLE statement.
+ */
+public enum IgniteSqlCreateTableOptionEnum {
+ /** A name of the required cache template. */
+ TEMPLATE,
+
+ /** A number of partition backups. */
+ BACKUPS,
+
+ /** A name of the desired affinity key column. */
+ AFFINITY_KEY,
+
+ /** An atomicity mode for the underlying cache. */
+ ATOMICITY,
+
+ /** A write synchronization mode for the underlying cache. */
+ WRITE_SYNCHRONIZATION_MODE,
+
+ /** A name the group the underlying cache belongs to. */
+ CACHE_GROUP,
+
+ /** A name of the underlying cache created by the command. */
+ CACHE_NAME,
+
+ /** A name of the data region where table entries should be stored. */
+ DATA_REGION,
+
+ /** A name of the custom key type that is used from the key-value and other non-SQL APIs in Ignite. */
+ KEY_TYPE,
+
+ /** A name of the custom value type that is used from the key-value and other non-SQL APIs in Ignite. */
+ VALUE_TYPE,
+
+ /** This flag specified whether the encryption should be enabled for the underlying cache. */
+ ENCRYPTED,
+}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessorTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessorTest.java
index bee1930..e2a8617 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessorTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessorTest.java
@@ -42,6 +42,7 @@ import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.IgniteInterruptedCheckedException;
+import org.apache.ignite.internal.processors.cache.query.QueryCursorEx;
import org.apache.ignite.internal.processors.query.QueryEngine;
import org.apache.ignite.internal.processors.query.calcite.exec.MailboxRegistryImpl;
import org.apache.ignite.internal.processors.query.calcite.exec.rel.Inbox;
@@ -56,6 +57,7 @@ import org.apache.ignite.testframework.ListeningTestLogger;
import org.apache.ignite.testframework.LogListener;
import org.apache.ignite.testframework.junits.WithSystemProperty;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.hamcrest.CoreMatchers;
import org.junit.Test;
import static org.apache.ignite.internal.processors.query.calcite.QueryChecker.awaitReservationsRelease;
@@ -754,7 +756,8 @@ public class CalciteQueryProcessorTest extends GridCommonAbstractTest {
QueryEngine engine = Commons.lookupComponent(grid(1).context(), QueryEngine.class);
- List<FieldsQueryCursor<List<?>>> query = engine.query(null, "PUBLIC", "INSERT INTO DEVELOPER VALUES (?, ?, ?)", 0, "Igor", 1);
+ List<FieldsQueryCursor<List<?>>> query = engine.query(null, "PUBLIC",
+ "INSERT INTO DEVELOPER(_key, name, projectId) VALUES (?, ?, ?)", 0, "Igor", 1);
assertEquals(1, query.size());
@@ -996,6 +999,29 @@ public class CalciteQueryProcessorTest extends GridCommonAbstractTest {
}
}
+ /**
+ * Verifies infix cast operator.
+ */
+ @Test
+ public void testInfixTypeCast() {
+ execute(client, "drop table if exists test_tbl");
+ execute(client, "create table test_tbl(id int primary key, val varchar)");
+
+ QueryEngine engineSrv = Commons.lookupComponent(grid(1).context(), QueryEngine.class);
+
+ FieldsQueryCursor<List<?>> cur = engineSrv.query(null, "PUBLIC",
+ "select id, id::tinyint as tid, id::smallint as sid, id::varchar as vid from test_tbl").get(0);
+
+ assertThat(cur, CoreMatchers.instanceOf(QueryCursorEx.class));
+
+ QueryCursorEx<?> qCur = (QueryCursorEx<?>)cur;
+
+ assertThat(qCur.fieldsMeta().get(0).fieldTypeName(), equalTo(Integer.class.getName()));
+ assertThat(qCur.fieldsMeta().get(1).fieldTypeName(), equalTo(Byte.class.getName()));
+ assertThat(qCur.fieldsMeta().get(2).fieldTypeName(), equalTo(Short.class.getName()));
+ assertThat(qCur.fieldsMeta().get(3).fieldTypeName(), equalTo(String.class.getName()));
+ }
+
/** */
private static List<String> deriveColumnNamesFromCursor(FieldsQueryCursor cursor) {
List<String> names = new ArrayList<>(cursor.getColumnsCount());
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/CreateTableIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/CreateTableIntegrationTest.java
new file mode 100644
index 0000000..80d61e0
--- /dev/null
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/CreateTableIntegrationTest.java
@@ -0,0 +1,276 @@
+/*
+ * 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.ignite.internal.processors.query.calcite;
+
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.query.FieldsQueryCursor;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.SqlConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.calcite.util.Commons;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.hamcrest.CustomMatcher;
+import org.hamcrest.Matcher;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.apache.ignite.internal.util.IgniteUtils.map;
+import static org.apache.ignite.testframework.GridTestUtils.hasSize;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.junit.Assert.assertThat;
+
+/** */
+public class CreateTableIntegrationTest extends GridCommonAbstractTest {
+ /** */
+ private static final String CLIENT_NODE_NAME = "client";
+
+ /** */
+ private static final String DATA_REGION_NAME = "test_data_region";
+
+ /** */
+ private IgniteEx client;
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ startGrids(1);
+
+ client = startClientGrid(CLIENT_NODE_NAME);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+ return super.getConfiguration(igniteInstanceName)
+ .setSqlConfiguration(
+ new SqlConfiguration().setSqlSchemas("MY_SCHEMA")
+ )
+ .setDataStorageConfiguration(
+ new DataStorageConfiguration()
+ .setDataRegionConfigurations(new DataRegionConfiguration().setName(DATA_REGION_NAME))
+ );
+ }
+
+ /** */
+ @Before
+ public void init() {
+ client = grid(CLIENT_NODE_NAME);
+ }
+
+ /** */
+ @After
+ public void cleanUp() {
+ client.destroyCaches(client.cacheNames());
+ }
+
+ /**
+ * Creates table with two columns, where the first column is PK,
+ * and verifies created cache.
+ */
+ @Test
+ public void createTableSimpleCase() {
+ Set<String> cachesBefore = new HashSet<>(client.cacheNames());
+
+ executeSql("create table my_table (id int primary key, val varchar)");
+
+ Set<String> newCaches = new HashSet<>(client.cacheNames());
+ newCaches.removeAll(cachesBefore);
+
+ assertThat(newCaches, hasSize(1));
+
+ CacheConfiguration<?, ?> ccfg = client.cachex(newCaches.iterator().next()).configuration();
+
+ assertThat(ccfg.getQueryEntities(), hasSize(1));
+
+ QueryEntity ent = ccfg.getQueryEntities().iterator().next();
+
+ assertThat(ent.getTableName(), equalTo("MY_TABLE"));
+ assertThat(ent.getKeyFieldName(), equalTo("ID"));
+ assertThat(
+ ent.getFields(),
+ equalTo(new LinkedHashMap<>(
+ map(
+ "ID", Integer.class.getName(),
+ "VAL", String.class.getName()
+ )
+ ))
+ );
+ }
+
+ /**
+ * Creates table with all possible options except template,
+ * and verifies created cache.
+ */
+ @Test
+ public void createTableWithOptions() {
+ Set<String> cachesBefore = new HashSet<>(client.cacheNames());
+
+ executeSql("create table my_table (id1 int, id2 int, val varchar, primary key(id1, id2)) with " +
+ " backups=2," +
+ " affinity_key=id2," +
+ " atomicity=transactional," +
+ " write_synchronization_mode=full_async," +
+ " cache_group=my_cache_group," +
+ " cache_name=my_cache_name," +
+ " data_region=\"" + DATA_REGION_NAME + "\"," +
+ " key_type=my_key_type," +
+ " value_type=my_value_type"
+// due to uncertain reason a test hangs if encryption is enabled
+// " encrypted=true"
+ );
+
+ Set<String> newCaches = new HashSet<>(client.cacheNames());
+ newCaches.removeAll(cachesBefore);
+
+ assertThat(newCaches, hasSize(1));
+
+ CacheConfiguration<?, ?> ccfg = client.cachex(newCaches.iterator().next()).configuration();
+
+ assertThat(ccfg.getQueryEntities(), hasSize(1));
+ assertThat(ccfg.getBackups(), equalTo(2));
+ assertThat(ccfg.getAtomicityMode(), equalTo(CacheAtomicityMode.TRANSACTIONAL));
+ assertThat(ccfg.getWriteSynchronizationMode(), equalTo(CacheWriteSynchronizationMode.FULL_ASYNC));
+ 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"));
+
+ QueryEntity ent = ccfg.getQueryEntities().iterator().next();
+
+ assertThat(ent.getTableName(), equalTo("MY_TABLE"));
+ assertThat(ent.getKeyType(), equalTo("MY_KEY_TYPE"));
+ assertThat(ent.getValueType(), equalTo("MY_VALUE_TYPE"));
+ assertThat(ent.getKeyFields(), equalTo(new LinkedHashSet<>(F.asList("ID1", "ID2"))));
+ }
+
+ /**
+ * Creates several tables with specified templates,
+ * and verifies created caches.
+ */
+ @Test
+ @SuppressWarnings("rawtypes")
+ public void createTableWithTemplate() {
+ Set<String> cachesBefore = new HashSet<>(client.cacheNames());
+
+ executeSql("create table my_table_replicated (id int, val varchar) with template=replicated, cache_name=repl");
+ executeSql("create table my_table_partitioned (id int, val varchar) with template=partitioned, cache_name=part");
+
+ Set<String> newCaches = new HashSet<>(client.cacheNames());
+ newCaches.removeAll(cachesBefore);
+
+ assertThat(newCaches, hasSize(2));
+
+ List<CacheConfiguration> ccfgs = newCaches.stream()
+ .map(client::cachex)
+ .map(IgniteInternalCache::configuration)
+ .collect(Collectors.toList());
+
+ assertThat(ccfgs, hasItem(matches("replicated cache",
+ ccfg -> "REPL".equals(ccfg.getName()) && ccfg.getCacheMode() == CacheMode.REPLICATED)));
+ assertThat(ccfgs, hasItem(matches("partitioned cache",
+ ccfg -> "PART".equals(ccfg.getName()) && ccfg.getCacheMode() == CacheMode.PARTITIONED)));
+ }
+
+ /**
+ * Tries to create several tables with the same name.
+ */
+ @Test
+ @SuppressWarnings("ThrowableNotThrown")
+ public void createTableIfNotExists() {
+ executeSql("create table my_table (id int, val varchar)");
+
+ GridTestUtils.assertThrows(log,
+ () -> executeSql("create table my_table (id int, val varchar)"),
+ IgniteSQLException.class, "Table already exists: MY_TABLE");
+
+ executeSql("create table if not exists my_table (id int, val varchar)");
+ }
+
+ /**
+ * Creates a table without a primary key and then insert a few rows.
+ */
+ @Test
+ public void createTableWithoutPk() {
+ executeSql("create table my_table (f1 int, f2 varchar)");
+
+ executeSql("insert into my_table(f1, f2) values (1, '1'),(2, '2')");
+ executeSql("insert into my_table values (1, '1'),(2, '2')");
+
+ assertThat(executeSql("select * from my_table"), hasSize(4));
+ }
+
+ /**
+ * Creates a table in a different schema.
+ */
+ @Test
+ public void createTableCustomSchema() {
+ executeSql("create table my_schema.my_table (f1 int, f2 varchar)");
+
+ executeSql("insert into my_schema.my_table(f1, f2) values (1, '1'),(2, '2')");
+ executeSql("insert into my_schema.my_table values (1, '1'),(2, '2')");
+
+ assertThat(executeSql("select * from my_schema.my_table"), hasSize(4));
+ }
+
+ /** */
+ private List<List<?>> executeSql(String sql) {
+ List<FieldsQueryCursor<List<?>>> cur = queryProcessor().query(null, "PUBLIC", sql);
+
+ try (QueryCursor<List<?>> srvCursor = cur.get(0)) {
+ return srvCursor.getAll();
+ }
+ }
+
+ /** */
+ private CalciteQueryProcessor queryProcessor() {
+ return Commons.lookupComponent(client.context(), CalciteQueryProcessor.class);
+ }
+
+ /**
+ * Matcher to verify that an object of the expected type and matches the given predicat.
+ *
+ * @param desc Description for this matcher.
+ * @param pred Addition check that would be applied to the object.
+ * @return {@code true} in case the object if instance of the given class and matches the predicat.
+ */
+ private static Matcher<CacheConfiguration<?, ?>> matches(String desc, Predicate<CacheConfiguration<?, ?>> pred) {
+ return new CustomMatcher<CacheConfiguration<?, ?>>(desc) {
+ @Override public boolean matches(Object item) {
+ return item instanceof CacheConfiguration && pred.test((CacheConfiguration<?, ?>)item);
+ }
+ };
+ }
+}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/JoinColocationPlannerTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/JoinColocationPlannerTest.java
index e378766..d833245 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/JoinColocationPlannerTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/JoinColocationPlannerTest.java
@@ -17,7 +17,6 @@
package org.apache.ignite.internal.processors.query.calcite.planner;
-import java.util.Collection;
import java.util.List;
import org.apache.calcite.plan.RelOptUtil;
@@ -38,10 +37,9 @@ import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactor
import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeSystem;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.CU;
-import org.hamcrest.CustomMatcher;
-import org.hamcrest.Matcher;
import org.junit.Test;
+import static org.apache.ignite.testframework.GridTestUtils.hasSize;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
@@ -271,18 +269,4 @@ public class JoinColocationPlannerTest extends AbstractPlannerTest {
return schema;
}
-
- /**
- * Matcher to verify size of the collection.
- *
- * @param size Required size.
- * @return {@code true} in case collection is not null and has an exactly the same size.
- */
- private static <T extends Collection<?>> Matcher<T> hasSize(int size) {
- return new CustomMatcher<T>("should be non empty with size=" + size) {
- @Override public boolean matches(Object item) {
- return item instanceof Collection && ((Collection<?>)item).size() == size;
- }
- };
- }
}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/sql/SqlDdlParserTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/sql/SqlDdlParserTest.java
new file mode 100644
index 0000000..428dc6c
--- /dev/null
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/sql/SqlDdlParserTest.java
@@ -0,0 +1,325 @@
+/*
+ * 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.ignite.internal.processors.query.calcite.sql;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlNumericLiteral;
+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.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.hamcrest.CustomMatcher;
+import org.hamcrest.Matcher;
+import org.junit.Test;
+
+import static java.util.Collections.singleton;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test suite to verify parsing of the DDL command.
+ */
+public class SqlDdlParserTest extends GridCommonAbstractTest {
+ /**
+ * Very simple case where only table name and a few columns are presented.
+ */
+ @Test
+ public void createTableSimpleCase() throws SqlParseException {
+ String query = "create table my_table(id int, val varchar)";
+
+ SqlNode node = parse(query);
+
+ assertThat(node, instanceOf(IgniteSqlCreateTable.class));
+
+ IgniteSqlCreateTable createTable = (IgniteSqlCreateTable)node;
+
+ assertThat(createTable.name().names, is(ImmutableList.of("MY_TABLE")));
+ assertThat(createTable.ifNotExists, is(false));
+ assertThat(createTable.columnList(), hasItem(columnWithName("ID")));
+ assertThat(createTable.columnList(), hasItem(columnWithName("VAL")));
+ }
+
+ /**
+ * Parsing of CREATE TABLE statement with quoted identifiers.
+ */
+ @Test
+ public void createTableQuotedIdentifiers() throws SqlParseException {
+ String query = "create table \"My_Table\"(\"Id\" int, \"Val\" varchar)";
+
+ SqlNode node = parse(query);
+
+ assertThat(node, instanceOf(IgniteSqlCreateTable.class));
+
+ IgniteSqlCreateTable createTable = (IgniteSqlCreateTable)node;
+
+ assertThat(createTable.name().names, is(ImmutableList.of("My_Table")));
+ assertThat(createTable.ifNotExists, is(false));
+ assertThat(createTable.columnList(), hasItem(columnWithName("Id")));
+ assertThat(createTable.columnList(), hasItem(columnWithName("Val")));
+ }
+
+ /**
+ * Parsing of CREATE TABLE statement with IF NOT EXISTS.
+ */
+ @Test
+ public void createTableIfNotExists() throws SqlParseException {
+ String query = "create table if not exists my_table(id int, val varchar)";
+
+ SqlNode node = parse(query);
+
+ assertThat(node, instanceOf(IgniteSqlCreateTable.class));
+
+ IgniteSqlCreateTable createTable = (IgniteSqlCreateTable)node;
+
+ assertThat(createTable.name().names, is(ImmutableList.of("MY_TABLE")));
+ assertThat(createTable.ifNotExists, is(true));
+ assertThat(createTable.columnList(), hasItem(columnWithName("ID")));
+ assertThat(createTable.columnList(), hasItem(columnWithName("VAL")));
+ }
+
+ /**
+ * Parsing of CREATE TABLE with specified PK constraint where constraint
+ * is a shortcut within a column definition.
+ */
+ @Test
+ public void createTableWithPkCase1() throws SqlParseException {
+ String query = "create table my_table(id int primary key, val varchar)";
+
+ SqlNode node = parse(query);
+
+ assertThat(node, instanceOf(IgniteSqlCreateTable.class));
+
+ IgniteSqlCreateTable createTable = (IgniteSqlCreateTable)node;
+
+ assertThat(createTable.name().names, is(ImmutableList.of("MY_TABLE")));
+ assertThat(createTable.ifNotExists, is(false));
+ assertThat(createTable.columnList(), hasItem(ofTypeMatching(
+ "PK constraint with name \"ID\"", SqlKeyConstraint.class,
+ constraint -> hasItem(ofTypeMatching("identifier \"ID\"", SqlIdentifier.class, id -> "ID".equals(id.names.get(0))))
+ .matches(constraint.getOperandList().get(1))
+ && constraint.getOperandList().get(0) == null
+ && constraint.isA(singleton(SqlKind.PRIMARY_KEY)))));
+ }
+
+ /**
+ * Parsing of CREATE TABLE with specified PK constraint where constraint
+ * is set explicitly and has no name.
+ */
+ @Test
+ public void createTableWithPkCase2() throws SqlParseException {
+ String query = "create table my_table(id int, val varchar, primary key(id))";
+
+ SqlNode node = parse(query);
+
+ assertThat(node, instanceOf(IgniteSqlCreateTable.class));
+
+ IgniteSqlCreateTable createTable = (IgniteSqlCreateTable)node;
+
+ assertThat(createTable.name().names, is(ImmutableList.of("MY_TABLE")));
+ assertThat(createTable.ifNotExists, is(false));
+ assertThat(createTable.columnList(), hasItem(ofTypeMatching(
+ "PK constraint without name containing column \"ID\"", SqlKeyConstraint.class,
+ constraint -> hasItem(ofTypeMatching("identifier \"ID\"", SqlIdentifier.class, id -> "ID".equals(id.names.get(0))))
+ .matches(constraint.getOperandList().get(1))
+ && constraint.getOperandList().get(0) == null
+ && constraint.isA(singleton(SqlKind.PRIMARY_KEY)))));
+ }
+
+ /**
+ * Parsing of CREATE TABLE with specified PK constraint where constraint
+ * is set explicitly and has a name.
+ */
+ @Test
+ public void createTableWithPkCase3() throws SqlParseException {
+ String query = "create table my_table(id int, val varchar, constraint pk_key primary key(id))";
+
+ SqlNode node = parse(query);
+
+ assertThat(node, instanceOf(IgniteSqlCreateTable.class));
+
+ IgniteSqlCreateTable createTable = (IgniteSqlCreateTable)node;
+
+ assertThat(createTable.name().names, is(ImmutableList.of("MY_TABLE")));
+ assertThat(createTable.ifNotExists, is(false));
+ assertThat(createTable.columnList(), hasItem(ofTypeMatching(
+ "PK constraint with name \"PK_KEY\" containing column \"ID\"", SqlKeyConstraint.class,
+ constraint -> hasItem(ofTypeMatching("identifier \"ID\"", SqlIdentifier.class, id -> "ID".equals(id.names.get(0))))
+ .matches(constraint.getOperandList().get(1))
+ && "PK_KEY".equals(((SqlIdentifier)constraint.getOperandList().get(0)).names.get(0))
+ && constraint.isA(singleton(SqlKind.PRIMARY_KEY)))));
+ }
+
+ /**
+ * Parsing of CREATE TABLE with specified PK constraint where constraint
+ * consists of several columns.
+ */
+ @Test
+ public void createTableWithPkCase4() throws SqlParseException {
+ String query = "create table my_table(id1 int, id2 int, val varchar, primary key(id1, id2))";
+
+ SqlNode node = parse(query);
+
+ assertThat(node, instanceOf(IgniteSqlCreateTable.class));
+
+ IgniteSqlCreateTable createTable = (IgniteSqlCreateTable)node;
+
+ assertThat(createTable.name().names, is(ImmutableList.of("MY_TABLE")));
+ assertThat(createTable.ifNotExists, is(false));
+ assertThat(createTable.columnList(), hasItem(ofTypeMatching(
+ "PK constraint with two columns", SqlKeyConstraint.class,
+ constraint -> hasItem(ofTypeMatching("identifier \"ID1\"", SqlIdentifier.class, id -> "ID1".equals(id.names.get(0))))
+ .matches(constraint.getOperandList().get(1))
+ && hasItem(ofTypeMatching("identifier \"ID2\"", SqlIdentifier.class, id -> "ID2".equals(id.names.get(0))))
+ .matches(constraint.getOperandList().get(1))
+ && constraint.getOperandList().get(0) == null
+ && constraint.isA(singleton(SqlKind.PRIMARY_KEY)))));
+ }
+
+ /**
+ * Parsing of CREATE TABLE with specified table options.
+ */
+ @Test
+ public void createTableWithOptions() throws SqlParseException {
+ String query = "create table my_table(id int) with" +
+ " template=\"my_template\"," +
+ " backups=2," +
+ " affinity_key=my_aff," +
+ " atomicity=atomic," +
+ " write_synchronization_mode=transactional," +
+ " cache_group=my_cache_group," +
+ " cache_name=my_cache_name," +
+ " data_region=my_data_region," +
+ " key_type=my_key_type," +
+ " value_type=my_value_type," +
+ " encrypted=true";
+
+ SqlNode node = parse(query);
+
+ assertThat(node, instanceOf(IgniteSqlCreateTable.class));
+
+ IgniteSqlCreateTable createTable = (IgniteSqlCreateTable)node;
+
+ assertThatStringOptionPresent(createTable.createOptionList().getList(), "TEMPLATE", "my_template");
+ assertThatIntegerOptionPresent(createTable.createOptionList().getList(), "BACKUPS", 2);
+ assertThatStringOptionPresent(createTable.createOptionList().getList(), "AFFINITY_KEY", "MY_AFF");
+ assertThatStringOptionPresent(createTable.createOptionList().getList(), "ATOMICITY", "ATOMIC");
+ assertThatStringOptionPresent(createTable.createOptionList().getList(), "WRITE_SYNCHRONIZATION_MODE", "TRANSACTIONAL");
+ assertThatStringOptionPresent(createTable.createOptionList().getList(), "CACHE_GROUP", "MY_CACHE_GROUP");
+ assertThatStringOptionPresent(createTable.createOptionList().getList(), "CACHE_NAME", "MY_CACHE_NAME");
+ assertThatStringOptionPresent(createTable.createOptionList().getList(), "DATA_REGION", "MY_DATA_REGION");
+ assertThatStringOptionPresent(createTable.createOptionList().getList(), "KEY_TYPE", "MY_KEY_TYPE");
+ assertThatStringOptionPresent(createTable.createOptionList().getList(), "VALUE_TYPE", "MY_VALUE_TYPE");
+ assertThatBooleanOptionPresent(createTable.createOptionList().getList(), "ENCRYPTED", true);
+ }
+
+ /**
+ * Parses a given statement and returns a resulting AST.
+ *
+ * @param stmt Statement to parse.
+ * @return An AST.
+ */
+ private static SqlNode parse(String stmt) throws SqlParseException {
+ SqlParser parser = SqlParser.create(stmt, SqlParser.config().withParserFactory(IgniteSqlParserImpl.FACTORY));
+
+ return parser.parseStmt();
+ }
+
+ /**
+ * Shortcut to verify that there is an option with a particular string value.
+ *
+ * @param optionList Option list from parsed AST.
+ * @param option An option key of interest.
+ * @param expVal Expected value.
+ */
+ private static void assertThatStringOptionPresent(List<SqlNode> optionList, String option, String expVal) {
+ assertThat(optionList, hasItem(ofTypeMatching(
+ "option " + option + "=" + expVal, IgniteSqlCreateTableOption.class,
+ opt -> opt.key().name().equals(option) && opt.value() instanceof SqlIdentifier
+ && Objects.equals(expVal, ((SqlIdentifier)opt.value()).names.get(0)))));
+ }
+
+ /**
+ * Shortcut to verify that there is an option with a particular boolean value.
+ *
+ * @param optionList Option list from parsed AST.
+ * @param option An option key of interest.
+ * @param expVal Expected value.
+ */
+ private static void assertThatBooleanOptionPresent(List<SqlNode> optionList, String option, boolean expVal) {
+ assertThat(optionList, hasItem(ofTypeMatching(
+ "option" + option + "=" + expVal, IgniteSqlCreateTableOption.class,
+ opt -> opt.key().name().equals(option) && opt.value() instanceof SqlLiteral
+ && Objects.equals(expVal, ((SqlLiteral)opt.value()).booleanValue()))));
+ }
+
+ /**
+ * Shortcut to verify that there is an option with a particular integer value.
+ *
+ * @param optionList Option list from parsed AST.
+ * @param option An option key of interest.
+ * @param expVal Expected value.
+ */
+ private static void assertThatIntegerOptionPresent(List<SqlNode> optionList, String option, int expVal) {
+ assertThat(optionList, hasItem(ofTypeMatching(
+ "option" + option + "=" + expVal, IgniteSqlCreateTableOption.class,
+ opt -> opt.key().name().equals(option) && opt.value() instanceof SqlNumericLiteral
+ && ((SqlNumericLiteral)opt.value()).isInteger()
+ && Objects.equals(expVal, ((SqlLiteral)opt.value()).intValue(true)))));
+ }
+
+ /**
+ * Matcher to verify name in the column declaration.
+ *
+ * @param name Expected name.
+ * @return {@code true} in case name in the column declaration equals to the expected one.
+ */
+ private static <T extends SqlColumnDeclaration> Matcher<T> columnWithName(String name) {
+ return new CustomMatcher<T>("column with name=" + name) {
+ @Override public boolean matches(Object item) {
+ return item instanceof SqlColumnDeclaration
+ && ((SqlColumnDeclaration)item).name.names.get(0).equals(name);
+ }
+ };
+ }
+
+ /**
+ * Matcher to verify that an object of the expected type and matches the given predicat.
+ *
+ * @param desc Description for this matcher.
+ * @param cls Expected class to verify the object is instance of.
+ * @param pred Addition check that would be applied to the object.
+ * @return {@code true} in case the object if instance of the given class and matches the predicat.
+ */
+ private static <T> Matcher<T> ofTypeMatching(String desc, Class<T> cls, Predicate<T> pred) {
+ return new CustomMatcher<T>(desc) {
+ @Override public boolean matches(Object item) {
+ return item != null && cls.isAssignableFrom(item.getClass()) && pred.test((T)item);
+ }
+ };
+ }
+}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java
index 9c656ca..498178a 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java
@@ -22,6 +22,7 @@ import org.apache.ignite.internal.processors.query.calcite.CalciteBasicSecondary
import org.apache.ignite.internal.processors.query.calcite.CalciteErrorHandlilngIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessorTest;
import org.apache.ignite.internal.processors.query.calcite.CancelTest;
+import org.apache.ignite.internal.processors.query.calcite.CreateTableIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.DateTimeTest;
import org.apache.ignite.internal.processors.query.calcite.LimitOffsetTest;
import org.apache.ignite.internal.processors.query.calcite.MetadataIntegrationTest;
@@ -33,6 +34,7 @@ import org.apache.ignite.internal.processors.query.calcite.exec.rel.ContinuousEx
import org.apache.ignite.internal.processors.query.calcite.jdbc.JdbcQueryTest;
import org.apache.ignite.internal.processors.query.calcite.rules.OrToUnionRuleTest;
import org.apache.ignite.internal.processors.query.calcite.rules.ProjectScanMergeRuleTest;
+import org.apache.ignite.internal.processors.query.calcite.sql.SqlDdlParserTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@@ -59,6 +61,9 @@ import org.junit.runners.Suite;
AggregatesIntegrationTest.class,
MetadataIntegrationTest.class,
SortAggregateIntegrationTest.class,
+
+ SqlDdlParserTest.class,
+ CreateTableIntegrationTest.class,
})
public class IgniteCalciteTestSuite {
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java
index 705be74..9a98419 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java
@@ -83,6 +83,9 @@ public final class IgniteQueryErrorCode {
/** Query canceled. */
public static final int QUERY_CANCELED = 3014;
+ /** Required schema not found. */
+ public static final int SCHEMA_NOT_FOUND = 3015;
+
/* 4xxx - cache related runtime errors */
/** Attempt to INSERT a key that is already in cache. */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java
index 722cf31..b112ed2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java
@@ -216,4 +216,16 @@ public interface GridQueryTypeDescriptor {
* @param keys Primary keys.
*/
public void primaryKeyFields(Set<String> keys);
+
+ /**
+ * Gets whether a primary key should be autogenerated.
+ */
+ public boolean implicitPk();
+
+ /**
+ * Sets whether a primary key should be autogenerated.
+ *
+ * @param implicitPk Whether a PK should be autogenerated.
+ */
+ public void implicitPk(boolean implicitPk);
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryEntityEx.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryEntityEx.java
index 655cf52..c43bc3a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryEntityEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryEntityEx.java
@@ -34,6 +34,9 @@ public class QueryEntityEx extends QueryEntity {
/** Fields that must have non-null value. */
private Set<String> notNullFields;
+ /** Whether a primary key should be autocreated or not. */
+ private boolean implicitPk;
+
/**
* Default constructor.
*/
@@ -53,6 +56,7 @@ public class QueryEntityEx extends QueryEntity {
QueryEntityEx other0 = (QueryEntityEx)other;
notNullFields = other0.notNullFields != null ? new HashSet<>(other0.notNullFields) : null;
+ implicitPk = other0.implicitPk;
}
}
@@ -68,6 +72,18 @@ public class QueryEntityEx extends QueryEntity {
return this;
}
+ /** */
+ public boolean implicitPk() {
+ return implicitPk;
+ }
+
+ /** */
+ public QueryEntity implicitPk(boolean implicitPk) {
+ this.implicitPk = implicitPk;
+
+ return this;
+ }
+
/** {@inheritDoc} */
@Override public boolean equals(Object o) {
if (this == o)
@@ -78,7 +94,8 @@ public class QueryEntityEx extends QueryEntity {
QueryEntityEx entity = (QueryEntityEx)o;
- return super.equals(entity) && F.eq(notNullFields, entity.notNullFields);
+ return super.equals(entity) && F.eq(notNullFields, entity.notNullFields)
+ && implicitPk == entity.implicitPk;
}
/** {@inheritDoc} */
@@ -86,6 +103,7 @@ public class QueryEntityEx extends QueryEntity {
int res = super.hashCode();
res = 31 * res + (notNullFields != null ? notNullFields.hashCode() : 0);
+ res = 31 * res + (implicitPk ? 1 : 0);
return res;
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java
index 9a0c260..305b91a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java
@@ -135,6 +135,9 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
/** Primary key fields. */
private Set<String> pkFields;
+ /** */
+ private boolean implicitPk;
+
/**
* Constructor.
*
@@ -746,4 +749,14 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
@Override public void primaryKeyFields(Set<String> keys) {
pkFields = keys;
}
+
+ /** {@inheritDoc} */
+ @Override public boolean implicitPk() {
+ return implicitPk;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void implicitPk(boolean implicitPk) {
+ this.implicitPk = implicitPk;
+ }
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
index c561a5e..30da8b5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
@@ -38,6 +38,7 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
+
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteSystemProperties;
@@ -633,6 +634,9 @@ public class QueryUtils {
if (!isKeyClsSqlType)
d.primaryKeyFields(keyFields);
+ if (qryEntity instanceof QueryEntityEx)
+ d.implicitPk(((QueryEntityEx)qryEntity).implicitPk());
+
// Sql-typed key/value doesn't have field property, but they may have precision and scale constraints.
// Also if fields are not set then _KEY and _VAL will be created as visible,
// so we have to add binary properties for them
@@ -1221,7 +1225,7 @@ public class QueryUtils {
*/
public static SchemaOperationException checkQueryEntityConflicts(CacheConfiguration<?, ?> ccfg,
Collection<DynamicCacheDescriptor> descs) {
- String schema = QueryUtils.normalizeSchemaName(ccfg.getName(), ccfg.getSqlSchema());
+ String schema = normalizeSchemaName(ccfg.getName(), ccfg.getSqlSchema());
Set<String> idxNames = new HashSet<>();
@@ -1231,7 +1235,7 @@ public class QueryUtils {
if (F.eq(ccfg.getName(), desc.cacheName()))
continue;
- String descSchema = QueryUtils.normalizeSchemaName(desc.cacheName(),
+ String descSchema = normalizeSchemaName(desc.cacheName(),
desc.cacheConfiguration().getSqlSchema());
if (!F.eq(schema, descSchema))
@@ -1583,6 +1587,66 @@ public class QueryUtils {
}
/**
+ * @return {@link IgniteSQLException} with the message same as of {@code this}'s and
+ */
+ public static IgniteSQLException convert(SchemaOperationException e) {
+ int sqlCode;
+
+ switch (e.code()) {
+ case SchemaOperationException.CODE_CACHE_NOT_FOUND:
+ sqlCode = IgniteQueryErrorCode.CACHE_NOT_FOUND;
+
+ break;
+
+ case SchemaOperationException.CODE_TABLE_NOT_FOUND:
+ sqlCode = IgniteQueryErrorCode.TABLE_NOT_FOUND;
+
+ break;
+
+ case SchemaOperationException.CODE_TABLE_EXISTS:
+ sqlCode = IgniteQueryErrorCode.TABLE_ALREADY_EXISTS;
+
+ break;
+
+ case SchemaOperationException.CODE_COLUMN_NOT_FOUND:
+ sqlCode = IgniteQueryErrorCode.COLUMN_NOT_FOUND;
+
+ break;
+
+ case SchemaOperationException.CODE_COLUMN_EXISTS:
+ sqlCode = IgniteQueryErrorCode.COLUMN_ALREADY_EXISTS;
+
+ break;
+
+ case SchemaOperationException.CODE_INDEX_NOT_FOUND:
+ sqlCode = IgniteQueryErrorCode.INDEX_NOT_FOUND;
+
+ break;
+
+ case SchemaOperationException.CODE_INDEX_EXISTS:
+ sqlCode = IgniteQueryErrorCode.INDEX_ALREADY_EXISTS;
+
+ break;
+
+ default:
+ sqlCode = IgniteQueryErrorCode.UNKNOWN;
+ }
+
+ return new IgniteSQLException(e.getMessage(), sqlCode, e);
+ }
+
+ /**
+ * Check if schema supports DDL statement.
+ *
+ * @param schemaName Schema name.
+ */
+ public static void isDdlOnSchemaSupported(String schemaName) {
+ if (F.eq(SCHEMA_SYS, schemaName))
+ throw new IgniteSQLException("DDL statements are not supported on " + schemaName + " schema",
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ }
+
+ /**
* Remove field by alias.
*
* @param entity Query entity.
diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java b/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java
index 0b19056..d7e528b 100644
--- a/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java
+++ b/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java
@@ -119,6 +119,8 @@ import org.apache.ignite.spi.discovery.DiscoverySpiListener;
import org.apache.ignite.ssl.SslContextFactory;
import org.apache.ignite.testframework.config.GridTestProperties;
import org.apache.ignite.testframework.junits.GridAbstractTest;
+import org.hamcrest.CustomMatcher;
+import org.hamcrest.Matcher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -2525,4 +2527,18 @@ public final class GridTestUtils {
public static void suppressException(RunnableX runnableX) {
runnableX.run();
}
+
+ /**
+ * Matcher to verify size of the collection.
+ *
+ * @param size Required size.
+ * @return {@code true} in case collection is not null and has an exactly the same size.
+ */
+ public static <T extends Collection<?>> Matcher<T> hasSize(int size) {
+ return new CustomMatcher<T>("should be non empty with size=" + size) {
+ @Override public boolean matches(Object item) {
+ return item instanceof Collection && ((Collection<?>)item).size() == size;
+ }
+ };
+ }
}
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/CommandProcessor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/CommandProcessor.java
index fb314e6..a577530 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/CommandProcessor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/CommandProcessor.java
@@ -32,6 +32,7 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
+
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteCluster;
import org.apache.ignite.IgniteDataStreamer;
@@ -135,6 +136,8 @@ import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.mvccEnabled;
import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.tx;
import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.txStart;
+import static org.apache.ignite.internal.processors.query.QueryUtils.convert;
+import static org.apache.ignite.internal.processors.query.QueryUtils.isDdlOnSchemaSupported;
import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryParser.PARAM_WRAP_VALUE;
/**
@@ -966,17 +969,6 @@ public class CommandProcessor {
}
/**
- * Check if schema supports DDL statement.
- *
- * @param schemaName Schema name.
- */
- private static void isDdlOnSchemaSupported(String schemaName) {
- if (F.eq(QueryUtils.SCHEMA_SYS, schemaName))
- throw new IgniteSQLException("DDL statements are not supported on " + schemaName + " schema",
- IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
- }
-
- /**
* Check if table supports DDL statement.
*
* @param tbl Table.
@@ -1006,55 +998,6 @@ public class CommandProcessor {
}
/**
- * @return {@link IgniteSQLException} with the message same as of {@code this}'s and
- */
- private IgniteSQLException convert(SchemaOperationException e) {
- int sqlCode;
-
- switch (e.code()) {
- case SchemaOperationException.CODE_CACHE_NOT_FOUND:
- sqlCode = IgniteQueryErrorCode.CACHE_NOT_FOUND;
-
- break;
-
- case SchemaOperationException.CODE_TABLE_NOT_FOUND:
- sqlCode = IgniteQueryErrorCode.TABLE_NOT_FOUND;
-
- break;
-
- case SchemaOperationException.CODE_TABLE_EXISTS:
- sqlCode = IgniteQueryErrorCode.TABLE_ALREADY_EXISTS;
-
- break;
-
- case SchemaOperationException.CODE_COLUMN_NOT_FOUND:
- sqlCode = IgniteQueryErrorCode.COLUMN_NOT_FOUND;
-
- break;
-
- case SchemaOperationException.CODE_COLUMN_EXISTS:
- sqlCode = IgniteQueryErrorCode.COLUMN_ALREADY_EXISTS;
-
- break;
-
- case SchemaOperationException.CODE_INDEX_NOT_FOUND:
- sqlCode = IgniteQueryErrorCode.INDEX_NOT_FOUND;
-
- break;
-
- case SchemaOperationException.CODE_INDEX_EXISTS:
- sqlCode = IgniteQueryErrorCode.INDEX_ALREADY_EXISTS;
-
- break;
-
- default:
- sqlCode = IgniteQueryErrorCode.UNKNOWN;
- }
-
- return new IgniteSQLException(e.getMessage(), sqlCode, e);
- }
-
- /**
* Convert this statement to query entity and do Ignite specific sanity checks on the way.
* @return Query entity mimicking this SQL statement.
*/