You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by al...@apache.org on 2021/06/22 11:00:38 UTC
[ignite] branch sql-calcite updated: IGNITE-13549 CREATE INDEX/DROP
INDEX commands - Fixes #9176.
This is an automated email from the ASF dual-hosted git repository.
alexpl 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 22a4aa1 IGNITE-13549 CREATE INDEX/DROP INDEX commands - Fixes #9176.
22a4aa1 is described below
commit 22a4aa1fe94d74f46cdef92226ca8aa42994d0cc
Author: Aleksey Plekhanov <pl...@gmail.com>
AuthorDate: Tue Jun 22 13:55:22 2021 +0300
IGNITE-13549 CREATE INDEX/DROP INDEX commands - Fixes #9176.
Signed-off-by: Aleksey Plekhanov <pl...@gmail.com>
---
modules/calcite/src/main/codegen/config.fmpp | 14 +-
.../src/main/codegen/includes/parserImpls.ftl | 82 +++++
.../query/calcite/exec/ExecutionServiceImpl.java | 19 +-
.../query/calcite/exec/ddl/DdlCommandHandler.java | 12 +-
.../calcite/exec/ddl/NativeCommandHandler.java | 116 +++++++
.../prepare/ddl/DdlSqlToCommandConverter.java | 51 +--
.../calcite/prepare/ddl/NativeCommandWrapper.java | 39 +++
.../prepare/ddl/SqlToNativeCommandConverter.java | 117 +++++++
.../query/calcite/sql/IgniteSqlCreateIndex.java | 162 ++++++++++
.../query/calcite/sql/IgniteSqlDropIndex.java | 76 +++++
.../query/calcite/util/IgniteResource.java | 3 +
.../processors/query/calcite/util/PlanUtils.java | 71 +++++
.../integration/AbstractDdlIntegrationTest.java | 88 +++++
.../integration/IndexDdlIntegrationTest.java | 203 ++++++++++++
.../integration/TableDdlIntegrationTest.java | 69 +---
.../query/calcite/sql/SqlDdlParserTest.java | 144 +++++++++
.../ignite/testsuites/IntegrationTestSuite.java | 2 +
.../processors/odbc/jdbc/JdbcRequestHandler.java | 6 +-
.../processors/query/GridQueryProcessor.java | 8 +-
.../processors/query/GridQuerySchemaManager.java | 47 +++
.../ignite/internal/sql/SqlCommandProcessor.java | 355 +++++++++++++++++++++
.../sql/command/SqlCreateIndexCommand.java | 36 +++
.../internal/sql/command/SqlDropIndexCommand.java | 17 +
.../processors/query/h2/CommandProcessor.java | 309 +++---------------
.../processors/query/h2/IgniteH2Indexing.java | 2 +-
.../internal/processors/query/h2/QueryParser.java | 44 +--
.../processors/query/h2/SchemaManager.java | 24 +-
27 files changed, 1684 insertions(+), 432 deletions(-)
diff --git a/modules/calcite/src/main/codegen/config.fmpp b/modules/calcite/src/main/codegen/config.fmpp
index df0c7a8..f40d158 100644
--- a/modules/calcite/src/main/codegen/config.fmpp
+++ b/modules/calcite/src/main/codegen/config.fmpp
@@ -31,8 +31,7 @@ data: {
"org.apache.calcite.sql.SqlDrop",
"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.ignite.internal.processors.query.calcite.util.IgniteResource",
"org.apache.calcite.sql.ddl.SqlDdlNodes",
]
@@ -52,6 +51,9 @@ data: {
# "KEY_TYPE" // already presented in Calcite
"VALUE_TYPE"
"ENCRYPTED"
+ "INDEX"
+ "PARALLEL"
+ "INLINE_SIZE"
]
# List of non-reserved keywords to add;
@@ -69,6 +71,8 @@ data: {
# "KEY_TYPE" // already presented in Calcite
"VALUE_TYPE"
"ENCRYPTED"
+ "PARALLEL"
+ "INLINE_SIZE"
# The following keywords are reserved in core Calcite,
# are reserved in some version of SQL,
@@ -584,14 +588,16 @@ data: {
# Each must accept arguments "(SqlParserPos pos, boolean replace)".
# Example: "SqlCreateForeignSchema".
createStatementParserMethods: [
- "SqlCreateTable"
+ "SqlCreateTable",
+ "SqlCreateIndex"
]
# List of methods for parsing extensions to "DROP" calls.
# Each must accept arguments "(SqlParserPos pos)".
# Example: "SqlDropSchema".
dropStatementParserMethods: [
- "SqlDropTable"
+ "SqlDropTable",
+ "SqlDropIndex"
]
# List of methods for parsing extensions to "DROP" calls.
diff --git a/modules/calcite/src/main/codegen/includes/parserImpls.ftl b/modules/calcite/src/main/codegen/includes/parserImpls.ftl
index 7b43a6d..aad7cfb 100644
--- a/modules/calcite/src/main/codegen/includes/parserImpls.ftl
+++ b/modules/calcite/src/main/codegen/includes/parserImpls.ftl
@@ -164,6 +164,77 @@ SqlCreate SqlCreateTable(Span s, boolean replace) :
}
}
+SqlNode IndexedColumn() :
+{
+ final Span s;
+ SqlNode col;
+}
+{
+ col = SimpleIdentifier()
+ (
+ <ASC>
+ | <DESC> {
+ col = SqlStdOperatorTable.DESC.createCall(getPos(), col);
+ }
+ )?
+ {
+ return col;
+ }
+}
+
+SqlNodeList IndexedColumnList() :
+{
+ final Span s;
+ final List<SqlNode> list = new ArrayList<SqlNode>();
+ SqlNode col = null;
+}
+{
+ <LPAREN> { s = span(); }
+ col = IndexedColumn() { list.add(col); }
+ (
+ <COMMA> col = IndexedColumn() { list.add(col); }
+ )*
+ <RPAREN> {
+ return new SqlNodeList(list, s.end(this));
+ }
+}
+
+SqlCreate SqlCreateIndex(Span s, boolean replace) :
+{
+ final boolean ifNotExists;
+ final SqlIdentifier idxId;
+ final SqlIdentifier tblId;
+ final SqlNodeList columnList;
+ SqlNumericLiteral parallel = null;
+ SqlNumericLiteral inlineSize = null;
+}
+{
+ <INDEX>
+ ifNotExists = IfNotExistsOpt()
+ idxId = SimpleIdentifier()
+ <ON>
+ tblId = CompoundIdentifier()
+ columnList = IndexedColumnList()
+ (
+ <PARALLEL> <UNSIGNED_INTEGER_LITERAL> {
+ if (parallel != null)
+ throw SqlUtil.newContextException(getPos(), IgniteResource.INSTANCE.optionAlreadyDefined("PARALLEL"));
+
+ parallel = SqlLiteral.createExactNumeric(token.image, getPos());
+ }
+ |
+ <INLINE_SIZE> <UNSIGNED_INTEGER_LITERAL> {
+ if (inlineSize != null)
+ throw SqlUtil.newContextException(getPos(), IgniteResource.INSTANCE.optionAlreadyDefined("INLINE_SIZE"));
+
+ inlineSize = SqlLiteral.createExactNumeric(token.image, getPos());
+ }
+ )*
+ {
+ return new IgniteSqlCreateIndex(s.end(this), ifNotExists, idxId, tblId, columnList, parallel, inlineSize);
+ }
+}
+
boolean IfExistsOpt() :
{
}
@@ -184,6 +255,17 @@ SqlDrop SqlDropTable(Span s, boolean replace) :
}
}
+SqlDrop SqlDropIndex(Span s, boolean replace) :
+{
+ final boolean ifExists;
+ final SqlIdentifier id;
+}
+{
+ <INDEX> ifExists = IfExistsOpt() id = CompoundIdentifier() {
+ return new IgniteSqlDropIndex(s.end(this), ifExists, id);
+ }
+}
+
void InfixCast(List<Object> list, ExprContext exprContext, Span s) :
{
final SqlDataTypeSpec dt;
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 c161e89..ac2ed48 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
@@ -27,7 +27,6 @@ import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
-
import com.google.common.collect.ImmutableList;
import org.apache.calcite.plan.Context;
import org.apache.calcite.plan.Contexts;
@@ -39,6 +38,7 @@ 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.SqlKind;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.parser.SqlParseException;
@@ -545,6 +545,9 @@ public class ExecutionServiceImpl<Row> extends AbstractService implements Execut
ctx.planner().reset();
+ if (SqlKind.DDL.contains(sqlNode.getKind()))
+ return prepareDdl(sqlNode, ctx);
+
switch (sqlNode.getKind()) {
case SELECT:
case ORDER_BY:
@@ -563,10 +566,6 @@ public class ExecutionServiceImpl<Row> extends AbstractService implements Execut
case EXPLAIN:
return prepareExplain(sqlNode, ctx);
- case CREATE_TABLE:
- case DROP_TABLE:
- return prepareDdl(sqlNode, ctx);
-
default:
throw new IgniteSQLException("Unsupported operation [" +
"sqlNodeKind=" + sqlNode.getKind() + "; " +
@@ -615,9 +614,7 @@ 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));
+ return new DdlPlan(ddlConverter.convert((SqlDdl)sqlNode, ctx));
}
/** */
@@ -658,7 +655,7 @@ public class ExecutionServiceImpl<Row> extends AbstractService implements Execut
case EXPLAIN:
return executeExplain((ExplainPlan)plan, pctx);
case DDL:
- return executeDdl((DdlPlan)plan, pctx);
+ return executeDdl(qryId, (DdlPlan)plan, pctx);
default:
throw new AssertionError("Unexpected plan type: " + plan);
@@ -666,9 +663,9 @@ public class ExecutionServiceImpl<Row> extends AbstractService implements Execut
}
/** */
- private FieldsQueryCursor<List<?>> executeDdl(DdlPlan plan, PlanningContext pctx) {
+ private FieldsQueryCursor<List<?>> executeDdl(UUID qryId, DdlPlan plan, PlanningContext pctx) {
try {
- ddlCmdHnd.handle(pctx, plan.command());
+ ddlCmdHnd.handle(qryId, plan.command(), pctx);
}
catch (IgniteCheckedException e) {
throw new IgniteSQLException("Failed to execute DDL statement [stmt=" + pctx.query() +
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
index befb58f..405be78 100644
--- 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
@@ -24,8 +24,8 @@ import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
import java.util.function.Supplier;
-
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.schema.Table;
import org.apache.ignite.IgniteCheckedException;
@@ -42,6 +42,7 @@ import org.apache.ignite.internal.processors.query.calcite.prepare.ddl.ColumnDef
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.prepare.ddl.DropTableCommand;
+import org.apache.ignite.internal.processors.query.calcite.prepare.ddl.NativeCommandWrapper;
import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable;
import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
import org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
@@ -68,22 +69,29 @@ public class DdlCommandHandler {
private final Supplier<SchemaPlus> schemaSupp;
/** */
+ private final NativeCommandHandler nativeCmdHandler;
+
+ /** */
public DdlCommandHandler(Supplier<GridQueryProcessor> qryProcessorSupp, GridCacheProcessor cacheProcessor,
IgniteSecurity security, Supplier<SchemaPlus> schemaSupp) {
this.qryProcessorSupp = qryProcessorSupp;
this.cacheProcessor = cacheProcessor;
this.security = security;
this.schemaSupp = schemaSupp;
+ nativeCmdHandler = new NativeCommandHandler(cacheProcessor.context().kernalContext(), schemaSupp);
}
/** */
- public void handle(PlanningContext pctx, DdlCommand cmd) throws IgniteCheckedException {
+ public void handle(UUID qryId, DdlCommand cmd, PlanningContext pctx) throws IgniteCheckedException {
if (cmd instanceof CreateTableCommand)
handle0(pctx, (CreateTableCommand)cmd);
else if (cmd instanceof DropTableCommand)
handle0(pctx, (DropTableCommand)cmd);
+ else if (cmd instanceof NativeCommandWrapper)
+ nativeCmdHandler.handle(qryId, (NativeCommandWrapper)cmd, pctx);
+
else {
throw new IgniteSQLException("Unsupported DDL operation [" +
"cmdName=" + (cmd == null ? null : cmd.getClass().getSimpleName()) + "; " +
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ddl/NativeCommandHandler.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ddl/NativeCommandHandler.java
new file mode 100644
index 0000000..0fc2f1c
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ddl/NativeCommandHandler.java
@@ -0,0 +1,116 @@
+/*
+ * 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.List;
+import java.util.UUID;
+import java.util.function.Supplier;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.schema.Table;
+import org.apache.ignite.cache.query.FieldsQueryCursor;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.processors.cache.GridCacheContextInfo;
+import org.apache.ignite.internal.processors.query.GridQuerySchemaManager;
+import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
+import org.apache.ignite.internal.processors.query.SqlClientContext;
+import org.apache.ignite.internal.processors.query.calcite.prepare.PlanningContext;
+import org.apache.ignite.internal.processors.query.calcite.prepare.ddl.NativeCommandWrapper;
+import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable;
+import org.apache.ignite.internal.sql.SqlCommandProcessor;
+
+/**
+ * Handler for Ignite native (core module) commands.
+ */
+public class NativeCommandHandler {
+ /** Command processor. */
+ private final SqlCommandProcessor proc;
+
+ /**
+ * @param ctx Context.
+ */
+ public NativeCommandHandler(GridKernalContext ctx, Supplier<SchemaPlus> schemaSupp) {
+ proc = new SqlCommandProcessor(ctx, new SchemaManager(schemaSupp));
+ }
+
+ /**
+ * @param qryId Query id.
+ * @param cmd Native command.
+ * @param pctx Planning context.
+ */
+ public FieldsQueryCursor<List<?>> handle(UUID qryId, NativeCommandWrapper cmd, PlanningContext pctx) {
+ assert proc.isCommandSupported(cmd.command()) : cmd.command();
+
+ return proc.runCommand(pctx.query(), cmd.command(), pctx.unwrap(SqlClientContext.class));
+ }
+
+ /**
+ * Schema manager.
+ */
+ private static class SchemaManager implements GridQuerySchemaManager {
+ /** Schema holder. */
+ private final Supplier<SchemaPlus> schemaSupp;
+
+ /**
+ * @param schemaSupp Schema supplier.
+ */
+ private SchemaManager(Supplier<SchemaPlus> schemaSupp) {
+ this.schemaSupp = schemaSupp;
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridQueryTypeDescriptor typeDescriptorForTable(String schemaName, String tableName) {
+ SchemaPlus schema = schemaSupp.get().getSubSchema(schemaName);
+
+ if (schema == null)
+ return null;
+
+ IgniteTable tbl = (IgniteTable)schema.getTable(tableName);
+
+ return tbl == null ? null : tbl.descriptor().typeDescription();
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridQueryTypeDescriptor typeDescriptorForIndex(String schemaName, String idxName) {
+ SchemaPlus schema = schemaSupp.get().getSubSchema(schemaName);
+
+ if (schema == null)
+ return null;
+
+ for (String tableName : schema.getTableNames()) {
+ Table tbl = schema.getTable(tableName);
+
+ if (tbl instanceof IgniteTable && ((IgniteTable)tbl).getIndex(idxName) != null)
+ return ((IgniteTable)tbl).descriptor().typeDescription();
+ }
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public <K, V> GridCacheContextInfo<K, V> cacheInfoForTable(String schemaName, String tableName) {
+ SchemaPlus schema = schemaSupp.get().getSubSchema(schemaName);
+
+ if (schema == null)
+ return null;
+
+ IgniteTable tbl = (IgniteTable)schema.getTable(tableName);
+
+ return tbl == null ? null : (GridCacheContextInfo<K, V>)tbl.descriptor().cacheInfo();
+ }
+ }
+}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlSqlToCommandConverter.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlSqlToCommandConverter.java
index f0c43ba..b5be013 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlSqlToCommandConverter.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlSqlToCommandConverter.java
@@ -28,7 +28,6 @@ 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;
@@ -63,6 +62,8 @@ import static org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlC
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;
+import static org.apache.ignite.internal.processors.query.calcite.util.PlanUtils.deriveObjectName;
+import static org.apache.ignite.internal.processors.query.calcite.util.PlanUtils.deriveSchemaName;
/** */
public class DdlSqlToCommandConverter {
@@ -124,6 +125,9 @@ public class DdlSqlToCommandConverter {
if (ddlNode instanceof SqlDropTable)
return convertDropTable((SqlDropTable)ddlNode, ctx);
+ if (SqlToNativeCommandConverter.isSupported(ddlNode))
+ return SqlToNativeCommandConverter.convert(ddlNode, ctx);
+
throw new IgniteSQLException("Unsupported operation [" +
"sqlNodeKind=" + ddlNode.getKind() + "; " +
"querySql=\"" + ctx.query() + "\"]", IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
@@ -222,51 +226,6 @@ public class DdlSqlToCommandConverter {
return dropTblCmd;
}
- /** Derives a schema name from the compound identifier. */
- private String deriveSchemaName(SqlIdentifier id, PlanningContext ctx) {
- String schemaName;
- if (id.isSimple())
- schemaName = ctx.schemaName();
- else {
- SqlIdentifier schemaId = id.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();
- }
-
- ensureSchemaExists(ctx, schemaName);
-
- return schemaName;
- }
-
- /** Derives an object(a table, an index, etc) name from the compound identifier. */
- private String deriveObjectName(SqlIdentifier id, PlanningContext ctx, String objDesc) {
- if (id.isSimple())
- return id.getSimple();
-
- SqlIdentifier objId = id.getComponent(id.skipLast(1).names.size());
-
- if (!objId.isSimple()) {
- throw new IgniteSQLException("Unexpected value of " + objDesc + " [" +
- "expected a simple identifier, but was " + objId + "; " +
- "querySql=\"" + ctx.query() + "\"]", IgniteQueryErrorCode.PARSING);
- }
-
- return objId.getSimple();
- }
-
- /** */
- 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.
*
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/NativeCommandWrapper.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/NativeCommandWrapper.java
new file mode 100644
index 0000000..85e8ef8
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/NativeCommandWrapper.java
@@ -0,0 +1,39 @@
+/*
+ * 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.ignite.internal.sql.command.SqlCommand;
+
+/**
+ * Wrapper for Ignite core SqlCommand.
+ */
+public class NativeCommandWrapper implements DdlCommand {
+ /** */
+ private final SqlCommand cmd;
+
+ /** */
+ public NativeCommandWrapper(SqlCommand cmd) {
+ this.cmd = Objects.requireNonNull(cmd);
+ }
+
+ /** */
+ public SqlCommand command() {
+ return cmd;
+ }
+}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/SqlToNativeCommandConverter.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/SqlToNativeCommandConverter.java
new file mode 100644
index 0000000..bec218d
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/SqlToNativeCommandConverter.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.prepare.ddl;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlDdl;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.calcite.prepare.PlanningContext;
+import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateIndex;
+import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlDropIndex;
+import org.apache.ignite.internal.sql.command.SqlCommand;
+import org.apache.ignite.internal.sql.command.SqlCreateIndexCommand;
+import org.apache.ignite.internal.sql.command.SqlDropIndexCommand;
+import org.apache.ignite.internal.sql.command.SqlIndexColumn;
+
+import static org.apache.ignite.internal.processors.query.calcite.util.PlanUtils.deriveObjectName;
+import static org.apache.ignite.internal.processors.query.calcite.util.PlanUtils.deriveSchemaName;
+
+/** */
+public class SqlToNativeCommandConverter {
+ /**
+ * Is a given AST can be converted by this class.
+ *
+ * @param sqlCmd Root node of the given AST.
+ */
+ public static boolean isSupported(SqlNode sqlCmd) {
+ return sqlCmd instanceof IgniteSqlCreateIndex
+ || sqlCmd instanceof IgniteSqlDropIndex;
+ }
+
+ /**
+ * Converts a given AST to a native command.
+ *
+ * @param sqlCmd Root node of the given AST.
+ * @param pctx Planning context.
+ */
+ public static NativeCommandWrapper convert(SqlDdl sqlCmd, PlanningContext pctx) {
+ return new NativeCommandWrapper(convertSqlCmd(sqlCmd, pctx));
+ }
+
+ /**
+ * Converts SqlNode to SqlCommand.
+ */
+ private static SqlCommand convertSqlCmd(SqlDdl cmd, PlanningContext pctx) {
+ if (cmd instanceof IgniteSqlCreateIndex)
+ return convertCreateIndex((IgniteSqlCreateIndex)cmd, pctx);
+ else if (cmd instanceof IgniteSqlDropIndex)
+ return convertDropIndex((IgniteSqlDropIndex)cmd, pctx);
+
+ throw new IgniteSQLException("Unsupported native operation [" +
+ "cmdName=" + (cmd == null ? null : cmd.getClass().getSimpleName()) + "; " +
+ "querySql=\"" + pctx.query() + "\"]", IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ }
+
+ /**
+ * Converts CREATE INDEX command.
+ */
+ private static SqlCreateIndexCommand convertCreateIndex(IgniteSqlCreateIndex sqlCmd, PlanningContext ctx) {
+ String schemaName = deriveSchemaName(sqlCmd.tableName(), ctx);
+ String tblName = deriveObjectName(sqlCmd.tableName(), ctx, "table name");
+ String idxName = sqlCmd.indexName().getSimple();
+
+ List<SqlIndexColumn> cols = new ArrayList<>(sqlCmd.columnList().size());
+
+ for (SqlNode col : sqlCmd.columnList().getList()) {
+ boolean desc = false;
+
+ if (col.getKind() == SqlKind.DESCENDING) {
+ col = ((SqlCall)col).getOperandList().get(0);
+
+ desc = true;
+ }
+
+ cols.add(new SqlIndexColumn(((SqlIdentifier)col).getSimple(), desc));
+ }
+
+ int parallel = sqlCmd.parallel() == null ? 0 : sqlCmd.parallel().intValue(true);
+
+ int inlineSize = sqlCmd.inlineSize() == null ? QueryIndex.DFLT_INLINE_SIZE :
+ sqlCmd.inlineSize().intValue(true);
+
+ return new SqlCreateIndexCommand(schemaName, tblName, idxName, sqlCmd.ifNotExists(), cols, false,
+ parallel, inlineSize);
+ }
+
+ /**
+ * Converts DROP INDEX command.
+ */
+ private static SqlDropIndexCommand convertDropIndex(IgniteSqlDropIndex sqlCmd, PlanningContext ctx) {
+ String schemaName = deriveSchemaName(sqlCmd.name(), ctx);
+ String idxName = deriveObjectName(sqlCmd.name(), ctx, "index name");
+
+ return new SqlDropIndexCommand(schemaName, idxName, sqlCmd.ifExists());
+ }
+}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlCreateIndex.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlCreateIndex.java
new file mode 100644
index 0000000..ca472d0
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlCreateIndex.java
@@ -0,0 +1,162 @@
+/*
+ * 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.SqlCall;
+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.SqlNumericLiteral;
+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;
+
+/**
+ * Parse tree for {@code CREATE INDEX} statement
+ */
+public class IgniteSqlCreateIndex extends SqlCreate {
+ /** */
+ private final SqlIdentifier idxName;
+
+ /** */
+ private final SqlIdentifier tblName;
+
+ /** */
+ private final SqlNodeList columnList;
+
+ /** */
+ private final SqlNumericLiteral parallel;
+
+ /** */
+ private final SqlNumericLiteral inlineSize;
+
+ /** */
+ private static final SqlOperator OPERATOR =
+ new SqlSpecialOperator("CREATE INDEX", SqlKind.CREATE_INDEX);
+
+ /** Creates a SqlCreateIndex. */
+ protected IgniteSqlCreateIndex(SqlParserPos pos, boolean ifNotExists, SqlIdentifier idxName, SqlIdentifier tblName,
+ SqlNodeList columnList, SqlNumericLiteral parallel, SqlNumericLiteral inlineSize) {
+ super(OPERATOR, pos, false, ifNotExists);
+ this.idxName = Objects.requireNonNull(idxName, "index name");
+ this.tblName = Objects.requireNonNull(tblName, "table name");
+ this.columnList = columnList;
+ this.parallel = parallel;
+ this.inlineSize = inlineSize;
+ }
+
+ /** {@inheritDoc} */
+ @Override public List<SqlNode> getOperandList() {
+ return ImmutableNullableList.of(idxName, tblName, columnList, parallel, inlineSize);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+ writer.keyword("CREATE");
+
+ writer.keyword("INDEX");
+
+ if (ifNotExists)
+ writer.keyword("IF NOT EXISTS");
+
+ idxName.unparse(writer, 0, 0);
+
+ writer.keyword("ON");
+
+ tblName.unparse(writer, 0, 0);
+
+ SqlWriter.Frame frame = writer.startList("(", ")");
+
+ for (SqlNode c : columnList) {
+ writer.sep(",");
+
+ boolean desc = false;
+
+ if (c.getKind() == SqlKind.DESCENDING) {
+ c = ((SqlCall)c).getOperandList().get(0);
+ desc = true;
+ }
+
+ c.unparse(writer, 0, 0);
+
+ if (desc)
+ writer.keyword("DESC");
+ }
+
+ writer.endList(frame);
+
+ if (parallel != null) {
+ writer.keyword("PARALLEL");
+
+ parallel.unparse(writer, 0, 0);
+ }
+
+ if (inlineSize != null) {
+ writer.keyword("INLINE_SIZE");
+
+ inlineSize.unparse(writer, 0, 0);
+ }
+ }
+
+ /**
+ * @return Name of the index.
+ */
+ public SqlIdentifier indexName() {
+ return idxName;
+ }
+
+ /**
+ * @return Name of the table.
+ */
+ public SqlIdentifier tableName() {
+ return tblName;
+ }
+
+ /**
+ * @return List of the specified columns and constraints.
+ */
+ public SqlNodeList columnList() {
+ return columnList;
+ }
+
+ /**
+ * @return PARALLEL clause.
+ */
+ public SqlNumericLiteral parallel() {
+ return parallel;
+ }
+
+ /**
+ * @return INLINE_SIZE clause.
+ */
+ public SqlNumericLiteral inlineSize() {
+ return inlineSize;
+ }
+
+ /**
+ * @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/IgniteSqlDropIndex.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlDropIndex.java
new file mode 100644
index 0000000..57afc9c
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlDropIndex.java
@@ -0,0 +1,76 @@
+/*
+ * 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.SqlDrop;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlNode;
+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;
+
+/**
+ * Parse tree for {@code DROP INDEX} statement
+ */
+public class IgniteSqlDropIndex extends SqlDrop {
+ /** */
+ private final SqlIdentifier name;
+
+ /** */
+ private static final SqlOperator OPERATOR =
+ new SqlSpecialOperator("DROP INDEX", SqlKind.DROP_INDEX);
+
+ /** */
+ protected IgniteSqlDropIndex(SqlParserPos pos, boolean ifExists, SqlIdentifier idxName) {
+ super(OPERATOR, pos, ifExists);
+ name = Objects.requireNonNull(idxName, "index name");
+ }
+
+ /** {@inheritDoc} */
+ @Override public List<SqlNode> getOperandList() {
+ return ImmutableNullableList.of(name);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+ writer.keyword(getOperator().getName()); // "DROP ..."
+
+ if (ifExists)
+ writer.keyword("IF EXISTS");
+
+ name.unparse(writer, leftPrec, rightPrec);
+ }
+
+ /**
+ * @return Name of the object.
+ */
+ public SqlIdentifier name() {
+ return name;
+ }
+
+ /**
+ * @return Whether the IF EXISTS is specified.
+ */
+ public boolean ifExists() {
+ return ifExists;
+ }
+}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java
index a0dfd8e..ae24367 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java
@@ -43,4 +43,7 @@ public interface IgniteResource {
@Resources.BaseMessage("Illegal value of {0}. The value must be positive and less than Integer.MAX_VALUE " +
"(" + Integer.MAX_VALUE + ")." )
Resources.ExInst<SqlValidatorException> correctIntegerLimit(String a0);
+
+ @Resources.BaseMessage("Option ''{0}'' has already been defined")
+ Resources.ExInst<SqlValidatorException> optionAlreadyDefined(String optName);
}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/PlanUtils.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/PlanUtils.java
new file mode 100644
index 0000000..d444c9e
--- /dev/null
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/PlanUtils.java
@@ -0,0 +1,71 @@
+/*
+ * 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.util;
+
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.calcite.prepare.PlanningContext;
+
+/** */
+public class PlanUtils {
+ /** Derives a schema name from the compound identifier. */
+ public static String deriveSchemaName(SqlIdentifier id, PlanningContext ctx) {
+ String schemaName;
+ if (id.isSimple())
+ schemaName = ctx.schemaName();
+ else {
+ SqlIdentifier schemaId = id.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();
+ }
+
+ ensureSchemaExists(ctx, schemaName);
+
+ return schemaName;
+ }
+
+ /** Derives an object(a table, an index, etc) name from the compound identifier. */
+ public static String deriveObjectName(SqlIdentifier id, PlanningContext ctx, String objDesc) {
+ if (id.isSimple())
+ return id.getSimple();
+
+ SqlIdentifier objId = id.getComponent(id.skipLast(1).names.size());
+
+ if (!objId.isSimple()) {
+ throw new IgniteSQLException("Unexpected value of " + objDesc + " [" +
+ "expected a simple identifier, but was " + objId + "; " +
+ "querySql=\"" + ctx.query() + "\"]", IgniteQueryErrorCode.PARSING);
+ }
+
+ return objId.getSimple();
+ }
+
+ /** */
+ private static 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);
+ }
+}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractDdlIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractDdlIntegrationTest.java
new file mode 100644
index 0000000..1bd3d35
--- /dev/null
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractDdlIntegrationTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.integration;
+
+import java.util.List;
+import org.apache.ignite.cache.query.FieldsQueryCursor;
+import org.apache.ignite.cache.query.QueryCursor;
+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.query.calcite.CalciteQueryProcessor;
+import org.apache.ignite.internal.processors.query.calcite.util.Commons;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.After;
+import org.junit.Before;
+
+/** */
+public class AbstractDdlIntegrationTest extends GridCommonAbstractTest {
+ /** */
+ private static final String CLIENT_NODE_NAME = "client";
+
+ /** */
+ protected static final String DATA_REGION_NAME = "test_data_region";
+
+ /** */
+ protected 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());
+ }
+
+ /** */
+ protected 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);
+ }
+}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IndexDdlIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IndexDdlIntegrationTest.java
new file mode 100644
index 0000000..cff74c5
--- /dev/null
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IndexDdlIntegrationTest.java
@@ -0,0 +1,203 @@
+/*
+ * 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.integration;
+
+import java.util.List;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.cache.query.index.Index;
+import org.apache.ignite.internal.cache.query.index.SortOrder;
+import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyDefinition;
+import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndex;
+import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.lang.IgnitePredicate;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Test;
+
+/** */
+public class IndexDdlIntegrationTest extends AbstractDdlIntegrationTest {
+ /** Cache name. */
+ private static final String CACHE_NAME = "my_cache";
+
+ /** {@inheritDoc} */
+ @Override public void init() {
+ super.init();
+
+ executeSql("create table my_table(id int, val_int int, val_str varchar) with cache_name=\"" + CACHE_NAME + "\"");
+ }
+
+ /**
+ * Creates and drops index.
+ */
+ @Test
+ public void createDropIndexSimpleCase() {
+ assertNull(findIndex(CACHE_NAME, "my_index"));
+
+ executeSql("create index my_index on my_table(id)");
+
+ assertNotNull(findIndex(CACHE_NAME, "my_index"));
+
+ executeSql("drop index my_index");
+
+ assertNull(findIndex(CACHE_NAME, "my_index"));
+ }
+
+ /**
+ * Creates and drops index on not default schema.
+ */
+ @Test
+ public void createDropIndexWithSchema() {
+ String cacheName = "cache2";
+
+ executeSql("create table my_schema.my_table2(id int) with cache_name=\"" + cacheName + "\"");
+
+ assertNull(findIndex(cacheName, "my_index2"));
+
+ executeSql("create index my_index2 on my_schema.my_table2(id)");
+
+ assertNotNull(findIndex(cacheName, "my_index2"));
+
+ GridTestUtils.assertThrowsAnyCause(log, () -> executeSql("drop index my_index2"), IgniteSQLException.class,
+ "Index doesn't exist");
+
+ assertNotNull(findIndex(cacheName, "my_index2"));
+
+ executeSql("drop index my_schema.my_index2");
+
+ assertNull(findIndex(cacheName, "my_index2"));
+ }
+
+ /**
+ * Creates index with "if not exists" clause.
+ */
+ @Test
+ public void createIndexWithIfNotExistsClause() {
+ assertNull(findIndex(CACHE_NAME, "my_index"));
+
+ executeSql("create index if not exists my_index on my_table(id)");
+
+ GridTestUtils.assertThrowsAnyCause(log, () -> executeSql("create index my_index on my_table(val_int)"),
+ IgniteSQLException.class, "Index already exists");
+
+ assertNotNull(findIndex(CACHE_NAME, "my_index"));
+
+ executeSql("create index if not exists my_index on my_table(val_str)");
+
+ Index idx = findIndex(CACHE_NAME, "my_index");
+
+ assertNotNull(idx);
+
+ List<IndexKeyDefinition> keyDefs = indexKeyDefinitions(idx);
+
+ assertEquals("ID", keyDefs.get(0).name());
+ }
+
+ /**
+ * Creates drops index with "if exists" clause.
+ */
+ @Test
+ public void dropIndexWithIfExistsClause() {
+ assertNull(findIndex(CACHE_NAME, "my_index"));
+
+ executeSql("create index my_index on my_table(id)");
+
+ assertNotNull(findIndex(CACHE_NAME, "my_index"));
+
+ executeSql("drop index if exists my_index");
+
+ assertNull(findIndex(CACHE_NAME, "my_index"));
+
+ executeSql("drop index if exists my_index");
+
+ GridTestUtils.assertThrowsAnyCause(log, () -> executeSql("drop index my_index"), IgniteSQLException.class,
+ "Index doesn't exist");
+ }
+
+ /**
+ * Creates index with different columns ordering.
+ */
+ @Test
+ public void createIndexWithColumnsOrdering() {
+ assertNull(findIndex(CACHE_NAME, "my_index"));
+
+ executeSql("create index my_index on my_table(id, val_int asc, val_str desc)");
+
+ Index idx = findIndex(CACHE_NAME, "my_index");
+
+ assertNotNull(idx);
+
+ List<IndexKeyDefinition> keyDefs = indexKeyDefinitions(idx);
+
+ assertEquals("ID", keyDefs.get(0).name());
+ assertEquals(SortOrder.ASC, keyDefs.get(0).order().sortOrder());
+ assertEquals("VAL_INT", keyDefs.get(1).name());
+ assertEquals(SortOrder.ASC, keyDefs.get(1).order().sortOrder());
+ assertEquals("VAL_STR", keyDefs.get(2).name());
+ assertEquals(SortOrder.DESC, keyDefs.get(2).order().sortOrder());
+ }
+
+ /**
+ * Creates index with inline size.
+ */
+ @Test
+ public void createIndexWithInlineSize() {
+ assertNull(findIndex(CACHE_NAME, "my_index"));
+
+ executeSql("create index my_index on my_table(val_str) inline_size 10");
+
+ Index idx = findIndex(CACHE_NAME, "my_index");
+
+ assertNotNull(idx);
+
+ InlineIndex inlineIdx = idx.unwrap(InlineIndex.class);
+
+ assertNotNull(inlineIdx);
+ assertEquals(10, inlineIdx.inlineSize());
+ }
+
+ /**
+ * Creates index with inline size.
+ */
+ @Test
+ public void createIndexWithParallel() {
+ assertNull(findIndex(CACHE_NAME, "my_index"));
+
+ executeSql("create index my_index on my_table(val_str) parallel 10");
+
+ assertNotNull(findIndex(CACHE_NAME, "my_index"));
+ }
+
+ /** */
+ private Index findIndex(String cacheName, String idxName) {
+ IgniteEx node = grid(0);
+
+ IgniteInternalCache<?, ?> cache = node.cachex(cacheName);
+
+ return F.find(node.context().indexProcessor().indexes(cache.context()), null,
+ (IgnitePredicate<Index>)i -> idxName.equalsIgnoreCase(i.name()));
+ }
+
+ /** */
+ private static List<IndexKeyDefinition> indexKeyDefinitions(Index idx) {
+ InlineIndex inlineIdx = idx.unwrap(InlineIndex.class);
+
+ assertNotNull(inlineIdx);
+
+ return inlineIdx.segment(0).rowHandler().indexKeyDefinitions();
+ }
+}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/TableDdlIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/TableDdlIntegrationTest.java
index 4bbfa89..771e485 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/TableDdlIntegrationTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/TableDdlIntegrationTest.java
@@ -27,32 +27,19 @@ import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
-
import org.apache.ignite.IgniteCache;
import org.apache.ignite.cache.CacheAtomicityMode;
import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cache.CachePeekMode;
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.CalciteQueryProcessor;
-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;
@@ -63,47 +50,7 @@ import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
/** */
-public class TableDdlIntegrationTest 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());
- }
-
+public class TableDdlIntegrationTest extends AbstractDdlIntegrationTest {
/**
* Creates table with two columns, where the first column is PK,
* and verifies created cache.
@@ -401,20 +348,6 @@ public class TableDdlIntegrationTest extends GridCommonAbstractTest {
executeSql("drop table if exists my_schema.my_table");
}
- /** */
- 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.
*
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
index 428dc6c..61a1767 100644
--- 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
@@ -21,6 +21,7 @@ import java.util.Objects;
import java.util.function.Predicate;
import com.google.common.collect.ImmutableList;
+import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlLiteral;
@@ -30,6 +31,8 @@ import org.apache.calcite.sql.ddl.SqlColumnDeclaration;
import org.apache.calcite.sql.ddl.SqlKeyConstraint;
import org.apache.calcite.sql.parser.SqlParseException;
import org.apache.calcite.sql.parser.SqlParser;
+import org.apache.calcite.sql.validate.SqlValidatorException;
+import org.apache.ignite.testframework.GridTestUtils;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.hamcrest.CustomMatcher;
import org.hamcrest.Matcher;
@@ -238,6 +241,121 @@ public class SqlDdlParserTest extends GridCommonAbstractTest {
}
/**
+ * Create index with list of indexed columns.
+ */
+ @Test
+ public void createIndexColumns() throws SqlParseException {
+ String qry = "create index my_index on my_table(id, val1 asc, val2 desc)";
+
+ IgniteSqlCreateIndex createIdx = (IgniteSqlCreateIndex) parse(qry);
+
+ assertThat(createIdx.indexName().names, is(ImmutableList.of("MY_INDEX")));
+ assertThat(createIdx.tableName().names, is(ImmutableList.of("MY_TABLE")));
+ assertThat(createIdx.ifNotExists, is(false));
+ assertThat(createIdx.columnList(), hasItem(indexedColumn("ID", false)));
+ assertThat(createIdx.columnList(), hasItem(indexedColumn("VAL1", false)));
+ assertThat(createIdx.columnList(), hasItem(indexedColumn("VAL2", true)));
+ }
+
+ /**
+ * Create index on table with schema.
+ */
+ @Test
+ public void createIndexOnTableWithSchema() throws SqlParseException {
+ String qry = "create index my_index on my_schema.my_table(id)";
+
+ IgniteSqlCreateIndex createIdx = (IgniteSqlCreateIndex)parse(qry);
+
+ assertThat(createIdx.indexName().names, is(ImmutableList.of("MY_INDEX")));
+ assertThat(createIdx.tableName().names, is(ImmutableList.of("MY_SCHEMA", "MY_TABLE")));
+ }
+
+ /**
+ * Create index with "if not exists" clause"
+ */
+ @Test
+ public void createIndexIfNotExists() throws SqlParseException {
+ String qry = "create index if not exists my_index on my_table(id)";
+
+ IgniteSqlCreateIndex createIdx = (IgniteSqlCreateIndex)parse(qry);
+
+ assertThat(createIdx.indexName().names, is(ImmutableList.of("MY_INDEX")));
+ assertThat(createIdx.tableName().names, is(ImmutableList.of("MY_TABLE")));
+ assertThat(createIdx.ifNotExists, is(true));
+ }
+
+ /**
+ * Create index with parallel and inline_size options"
+ */
+ @Test
+ public void createIndexWithOptions() throws SqlParseException {
+ String qry = "create index my_index on my_table(id) parallel 10 inline_size 20";
+
+ IgniteSqlCreateIndex createIdx = (IgniteSqlCreateIndex)parse(qry);
+
+ assertThat(createIdx.indexName().names, is(ImmutableList.of("MY_INDEX")));
+ assertThat(createIdx.tableName().names, is(ImmutableList.of("MY_TABLE")));
+ assertEquals(10, createIdx.parallel().intValue(true));
+ assertEquals(20, createIdx.inlineSize().intValue(true));
+
+ qry = "create index my_index on my_table(id) parallel 10";
+
+ createIdx = (IgniteSqlCreateIndex)parse(qry);
+
+ assertEquals(10, createIdx.parallel().intValue(true));
+ assertNull(createIdx.inlineSize());
+
+ qry = "create index my_index on my_table(id) inline_size 20";
+
+ createIdx = (IgniteSqlCreateIndex)parse(qry);
+
+ assertNull(createIdx.parallel());
+ assertEquals(20, createIdx.inlineSize().intValue(true));
+
+ qry = "create index my_index on my_table(id) inline_size 20 parallel 10";
+
+ createIdx = (IgniteSqlCreateIndex)parse(qry);
+
+ assertEquals(20, createIdx.inlineSize().intValue(true));
+ assertEquals(10, createIdx.parallel().intValue(true));
+ }
+
+ /**
+ * Create index with malformed statements.
+ */
+ @Test
+ public void createIndexMalformed() {
+ assertParserThrows("create index my_index on my_table(id) parallel 10 inline_size 20 parallel 10",
+ SqlValidatorException.class, "Option 'PARALLEL' has already been defined");
+
+ assertParserThrows("create index my_index on my_table(id) inline_size -1", SqlParseException.class);
+
+ assertParserThrows("create index my_index on my_table(id) inline_size = 1", SqlParseException.class);
+
+ assertParserThrows("create index my_index on my_table(id) inline_size", SqlParseException.class);
+
+ assertParserThrows("create index if exists my_index on my_table(id)", SqlParseException.class);
+
+ assertParserThrows("create index my_index on my_table(id asc desc)", SqlParseException.class);
+
+ assertParserThrows("create index my_index on my_table(id nulls first)", SqlParseException.class);
+
+ assertParserThrows("create index my_scheme.my_index on my_table(id)", SqlParseException.class);
+
+ assertParserThrows("create index my_index on my_table(id.id2)", SqlParseException.class);
+ }
+
+ /** */
+ private void assertParserThrows(String sql, Class<? extends Exception> cls) {
+ assertParserThrows(sql, cls, "");
+ }
+
+ /** */
+ private void assertParserThrows(String sql, Class<? extends Exception> cls, String msg) {
+ GridTestUtils.assertThrowsAnyCause(log, () -> parse(sql), cls, msg);
+ }
+
+ /**
* Parses a given statement and returns a resulting AST.
*
* @param stmt Statement to parse.
@@ -322,4 +440,30 @@ public class SqlDdlParserTest extends GridCommonAbstractTest {
}
};
}
+
+ /**
+ * Matcher to verify name and direction of indexed column.
+ *
+ * @param name Expected name.
+ * @param desc Descending order.
+ * @return {@code true} in case name and order of the indexed column equal to expected values.
+ */
+ private static <T extends SqlColumnDeclaration> Matcher<T> indexedColumn(String name, boolean desc) {
+ return new CustomMatcher<T>("column with name=" + name) {
+ @Override public boolean matches(Object item) {
+ SqlNode node = (SqlNode)item;
+
+ if (desc) {
+ if (node.getKind() != SqlKind.DESCENDING)
+ return false;
+
+ SqlIdentifier ident = ((SqlIdentifier)((SqlCall)node).getOperandList().get(0));
+
+ return ident.names.get(0).equals(name);
+ }
+ else
+ return item instanceof SqlIdentifier && ((SqlIdentifier)node).names.get(0).equals(name);
+ }
+ };
+ }
}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
index 03fe094..aa9919c 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
@@ -28,6 +28,7 @@ import org.apache.ignite.internal.processors.query.calcite.SqlFieldsQueryUsageTe
import org.apache.ignite.internal.processors.query.calcite.UnstableTopologyTest;
import org.apache.ignite.internal.processors.query.calcite.integration.AggregatesIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.CalciteErrorHandlilngIntegrationTest;
+import org.apache.ignite.internal.processors.query.calcite.integration.IndexDdlIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.IndexSpoolIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.MetadataIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.SetOpIntegrationTest;
@@ -59,6 +60,7 @@ import org.junit.runners.Suite;
MetadataIntegrationTest.class,
SortAggregateIntegrationTest.class,
TableDdlIntegrationTest.class,
+ IndexDdlIntegrationTest.class,
FunctionsTest.class,
TableDmlIntegrationTest.class,
DataTypesTest.class,
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
index 4c0f1e1..a3d9fc0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
@@ -775,8 +775,10 @@ public class JdbcRequestHandler implements ClientListenerRequestHandler {
/** */
private List<FieldsQueryCursor<List<?>>> querySqlFields(SqlFieldsQueryEx qry, GridQueryCancel cancel) {
if (experimentalQueryEngine != null) {
- if (executeWithExperimentalEngine(qry.getSql()))
- return experimentalQueryEngine.query(QueryContext.of(qry, cancel), qry.getSchema(), qry.getSql(), qry.getArgs());
+ if (executeWithExperimentalEngine(qry.getSql())) {
+ return experimentalQueryEngine.query(QueryContext.of(qry, cliCtx, cancel), qry.getSchema(),
+ qry.getSql(), qry.getArgs());
+ }
}
return connCtx.kernalContext().query().querySqlFields(null, qry,
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
index 1f37129..46227b5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
@@ -173,7 +173,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
/** Pattern to test incoming query to decide whether this query should be executed with Calcite or H2. */
public static final Pattern H2_REDIRECTION_RULES =
- Pattern.compile("\\s*(alter\\s*table|create\\s*index|drop\\s*index)", CASE_INSENSITIVE);
+ Pattern.compile("\\s*(alter\\s+table)", CASE_INSENSITIVE);
/** For tests. */
public static Class<? extends GridQueryIndexing> idxCls;
@@ -2850,8 +2850,10 @@ public class GridQueryProcessor extends GridProcessorAdapter {
throw new CacheException("Execution of local SqlFieldsQuery on client node disallowed.");
if (experimentalQueryEngine != null && useExperimentalSqlEngine) {
- if (executeWithExperimentalEngine(qry.getSql()))
- return experimentalQueryEngine.query(QueryContext.of(qry), qry.getSchema(), qry.getSql(), X.EMPTY_OBJECT_ARRAY);
+ if (executeWithExperimentalEngine(qry.getSql())) {
+ return experimentalQueryEngine.query(QueryContext.of(qry, cliCtx), qry.getSchema(), qry.getSql(),
+ X.EMPTY_OBJECT_ARRAY);
+ }
}
return executeQuerySafe(cctx, () -> {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQuerySchemaManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQuerySchemaManager.java
new file mode 100644
index 0000000..b89d2b8
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQuerySchemaManager.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+import org.apache.ignite.internal.processors.cache.GridCacheContextInfo;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Provides information about query engine schemas.
+ */
+public interface GridQuerySchemaManager {
+ /**
+ * Find type descriptor by schema and table name.
+ *
+ * @return Query type descriptor or {@code null} if descriptor was not found.
+ */
+ public @Nullable GridQueryTypeDescriptor typeDescriptorForTable(String schemaName, String tableName);
+
+ /**
+ * Find type descriptor by schema and index name.
+ *
+ * @return Query type descriptor or {@code null} if descriptor was not found.
+ */
+ public @Nullable GridQueryTypeDescriptor typeDescriptorForIndex(String schemaName, String idxName);
+
+ /**
+ * Find cache info by schema and table name.
+ *
+ * @return Cache info or {@code null} if cache info was not found.
+ */
+ public @Nullable <K, V> GridCacheContextInfo<K, V> cacheInfoForTable(String schemaName, String tableName);
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlCommandProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlCommandProcessor.java
new file mode 100644
index 0000000..f82f022
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlCommandProcessor.java
@@ -0,0 +1,355 @@
+/*
+ * 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.sql;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteCluster;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.QueryIndexType;
+import org.apache.ignite.cache.query.FieldsQueryCursor;
+import org.apache.ignite.internal.ComputeMXBeanImpl;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.QueryMXBeanImpl;
+import org.apache.ignite.internal.ServiceMXBeanImpl;
+import org.apache.ignite.internal.TransactionsMXBeanImpl;
+import org.apache.ignite.internal.processors.cache.GridCacheContextInfo;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.query.GridQueryProperty;
+import org.apache.ignite.internal.processors.query.GridQuerySchemaManager;
+import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.SqlClientContext;
+import org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
+import org.apache.ignite.internal.sql.command.SqlAlterTableCommand;
+import org.apache.ignite.internal.sql.command.SqlAlterUserCommand;
+import org.apache.ignite.internal.sql.command.SqlCommand;
+import org.apache.ignite.internal.sql.command.SqlCreateIndexCommand;
+import org.apache.ignite.internal.sql.command.SqlCreateUserCommand;
+import org.apache.ignite.internal.sql.command.SqlDropIndexCommand;
+import org.apache.ignite.internal.sql.command.SqlDropUserCommand;
+import org.apache.ignite.internal.sql.command.SqlIndexColumn;
+import org.apache.ignite.internal.sql.command.SqlKillComputeTaskCommand;
+import org.apache.ignite.internal.sql.command.SqlKillContinuousQueryCommand;
+import org.apache.ignite.internal.sql.command.SqlKillScanQueryCommand;
+import org.apache.ignite.internal.sql.command.SqlKillServiceCommand;
+import org.apache.ignite.internal.sql.command.SqlKillTransactionCommand;
+import org.apache.ignite.internal.util.future.GridFinishedFuture;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.internal.processors.query.QueryUtils.convert;
+import static org.apache.ignite.internal.processors.query.QueryUtils.isDdlOnSchemaSupported;
+
+/**
+ * Processor responsible for execution of native Ignite commands.
+ */
+public class SqlCommandProcessor {
+ /** Kernal context. */
+ protected final GridKernalContext ctx;
+
+ /** Logger. */
+ protected final IgniteLogger log;
+
+ /** Schema manager. */
+ protected final GridQuerySchemaManager schemaMgr;
+
+ /**
+ * Constructor.
+ *
+ * @param ctx Kernal context.
+ */
+ public SqlCommandProcessor(GridKernalContext ctx, GridQuerySchemaManager schemaMgr) {
+ this.ctx = ctx;
+ this.schemaMgr = schemaMgr;
+ log = ctx.log(getClass());
+ }
+
+ /**
+ * Execute command.
+ *
+ * @param sql SQL.
+ * @param cmdNative Native command.
+ * @param cliCtx Client context.
+ * @return Result.
+ */
+ @Nullable public FieldsQueryCursor<List<?>> runCommand(String sql, SqlCommand cmdNative,
+ @Nullable SqlClientContext cliCtx) {
+ assert cmdNative != null;
+
+ if (isDdl(cmdNative))
+ runCommandNativeDdl(sql, cmdNative);
+ else if (cmdNative instanceof SqlKillComputeTaskCommand)
+ processKillComputeTaskCommand((SqlKillComputeTaskCommand) cmdNative);
+ else if (cmdNative instanceof SqlKillTransactionCommand)
+ processKillTxCommand((SqlKillTransactionCommand) cmdNative);
+ else if (cmdNative instanceof SqlKillServiceCommand)
+ processKillServiceTaskCommand((SqlKillServiceCommand) cmdNative);
+ else if (cmdNative instanceof SqlKillScanQueryCommand)
+ processKillScanQueryCommand((SqlKillScanQueryCommand) cmdNative);
+ else if (cmdNative instanceof SqlKillContinuousQueryCommand)
+ processKillContinuousQueryCommand((SqlKillContinuousQueryCommand) cmdNative);
+
+ return null;
+ }
+
+ /**
+ * @return {@code True} if command is supported by this command processor.
+ */
+ public boolean isCommandSupported(SqlCommand cmd) {
+ return cmd instanceof SqlCreateIndexCommand
+ || cmd instanceof SqlDropIndexCommand
+ || cmd instanceof SqlAlterTableCommand
+ || cmd instanceof SqlCreateUserCommand
+ || cmd instanceof SqlAlterUserCommand
+ || cmd instanceof SqlDropUserCommand
+ || cmd instanceof SqlKillComputeTaskCommand
+ || cmd instanceof SqlKillServiceCommand
+ || cmd instanceof SqlKillTransactionCommand
+ || cmd instanceof SqlKillScanQueryCommand
+ || cmd instanceof SqlKillContinuousQueryCommand;
+ }
+
+ /**
+ * @param cmd Command.
+ * @return {@code True} if this is supported DDL command.
+ */
+ private static boolean isDdl(SqlCommand cmd) {
+ return cmd instanceof SqlCreateIndexCommand
+ || cmd instanceof SqlDropIndexCommand
+ || cmd instanceof SqlAlterTableCommand
+ || cmd instanceof SqlCreateUserCommand
+ || cmd instanceof SqlAlterUserCommand
+ || cmd instanceof SqlDropUserCommand;
+ }
+
+ /**
+ * Process kill scan query cmd.
+ *
+ * @param cmd Command.
+ */
+ private void processKillScanQueryCommand(SqlKillScanQueryCommand cmd) {
+ new QueryMXBeanImpl(ctx)
+ .cancelScan(cmd.getOriginNodeId(), cmd.getCacheName(), cmd.getQryId());
+ }
+
+ /**
+ * Process kill compute task command.
+ *
+ * @param cmd Command.
+ */
+ private void processKillComputeTaskCommand(SqlKillComputeTaskCommand cmd) {
+ new ComputeMXBeanImpl(ctx).cancel(cmd.getSessionId());
+ }
+
+ /**
+ * Process kill transaction cmd.
+ *
+ * @param cmd Command.
+ */
+ private void processKillTxCommand(SqlKillTransactionCommand cmd) {
+ new TransactionsMXBeanImpl(ctx).cancel(cmd.getXid());
+ }
+
+ /**
+ * Process kill service command.
+ *
+ * @param cmd Command.
+ */
+ private void processKillServiceTaskCommand(SqlKillServiceCommand cmd) {
+ new ServiceMXBeanImpl(ctx).cancel(cmd.getName());
+ }
+
+ /**
+ * Process kill continuous query cmd.
+ *
+ * @param cmd Command.
+ */
+ private void processKillContinuousQueryCommand(SqlKillContinuousQueryCommand cmd) {
+ new QueryMXBeanImpl(ctx).cancelContinuous(cmd.getOriginNodeId(), cmd.getRoutineId());
+ }
+
+ /**
+ * Run DDL statement.
+ *
+ * @param sql Original SQL.
+ * @param cmd Command.
+ */
+ private void runCommandNativeDdl(String sql, SqlCommand cmd) {
+ IgniteInternalFuture<?> fut = null;
+
+ try {
+ isDdlOnSchemaSupported(cmd.schemaName());
+
+ finishActiveTxIfNecessary();
+
+ if (cmd instanceof SqlCreateIndexCommand) {
+ SqlCreateIndexCommand cmd0 = (SqlCreateIndexCommand)cmd;
+
+ GridQueryTypeDescriptor typeDesc = schemaMgr.typeDescriptorForTable(cmd0.schemaName(), cmd0.tableName());
+ GridCacheContextInfo<?, ?> cacheInfo = schemaMgr.cacheInfoForTable(cmd0.schemaName(), cmd0.tableName());
+
+ if (typeDesc == null)
+ throw new SchemaOperationException(SchemaOperationException.CODE_TABLE_NOT_FOUND, cmd0.tableName());
+
+ ensureDdlSupported(cacheInfo);
+
+ QueryIndex newIdx = new QueryIndex();
+
+ newIdx.setName(cmd0.indexName());
+
+ newIdx.setIndexType(cmd0.spatial() ? QueryIndexType.GEOSPATIAL : QueryIndexType.SORTED);
+
+ LinkedHashMap<String, Boolean> flds = new LinkedHashMap<>();
+
+ for (SqlIndexColumn col : cmd0.columns()) {
+ GridQueryProperty prop = typeDesc.property(col.name());
+
+ if (prop == null)
+ throw new SchemaOperationException(SchemaOperationException.CODE_COLUMN_NOT_FOUND, col.name());
+
+ flds.put(prop.name(), !col.descending());
+ }
+
+ newIdx.setFields(flds);
+ newIdx.setInlineSize(cmd0.inlineSize());
+
+ fut = ctx.query().dynamicIndexCreate(cacheInfo.name(), cmd.schemaName(), typeDesc.tableName(),
+ newIdx, cmd0.ifNotExists(), cmd0.parallel());
+ }
+ else if (cmd instanceof SqlDropIndexCommand) {
+ SqlDropIndexCommand cmd0 = (SqlDropIndexCommand)cmd;
+
+ GridQueryTypeDescriptor typeDesc = schemaMgr.typeDescriptorForIndex(cmd0.schemaName(), cmd0.indexName());
+
+ if (typeDesc != null) {
+ GridCacheContextInfo<?, ?> cacheInfo = schemaMgr.cacheInfoForTable(typeDesc.schemaName(),
+ typeDesc.tableName());
+
+ ensureDdlSupported(cacheInfo);
+
+ fut = ctx.query().dynamicIndexDrop(cacheInfo.name(), cmd0.schemaName(), cmd0.indexName(),
+ cmd0.ifExists());
+ }
+ else {
+ if (cmd0.ifExists())
+ fut = new GridFinishedFuture<>();
+ else
+ throw new SchemaOperationException(SchemaOperationException.CODE_INDEX_NOT_FOUND,
+ cmd0.indexName());
+ }
+ }
+ else if (cmd instanceof SqlAlterTableCommand) {
+ SqlAlterTableCommand cmd0 = (SqlAlterTableCommand)cmd;
+
+ GridCacheContextInfo<?, ?> cacheInfo = schemaMgr.cacheInfoForTable(cmd0.schemaName(), cmd0.tableName());
+
+ if (cacheInfo == null) {
+ throw new SchemaOperationException(SchemaOperationException.CODE_TABLE_NOT_FOUND,
+ cmd0.tableName());
+ }
+
+ Boolean logging = cmd0.logging();
+
+ assert logging != null : "Only LOGGING/NOLOGGING are supported at the moment.";
+
+ IgniteCluster cluster = ctx.grid().cluster();
+
+ if (logging) {
+ boolean res = cluster.enableWal(cacheInfo.name());
+
+ if (!res)
+ throw new IgniteSQLException("Logging already enabled for table: " + cmd0.tableName());
+ }
+ else {
+ boolean res = cluster.disableWal(cacheInfo.name());
+
+ if (!res)
+ throw new IgniteSQLException("Logging already disabled for table: " + cmd0.tableName());
+ }
+
+ fut = new GridFinishedFuture<>();
+ }
+ else if (cmd instanceof SqlCreateUserCommand) {
+ SqlCreateUserCommand addCmd = (SqlCreateUserCommand)cmd;
+
+ ctx.security().createUser(addCmd.userName(), addCmd.password().toCharArray());
+ }
+ else if (cmd instanceof SqlAlterUserCommand) {
+ SqlAlterUserCommand altCmd = (SqlAlterUserCommand)cmd;
+
+ ctx.security().alterUser(altCmd.userName(), altCmd.password().toCharArray());
+ }
+ else if (cmd instanceof SqlDropUserCommand) {
+ SqlDropUserCommand dropCmd = (SqlDropUserCommand)cmd;
+
+ ctx.security().dropUser(dropCmd.userName());
+ }
+ else
+ throw new IgniteSQLException("Unsupported DDL operation: " + sql,
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
+ if (fut != null)
+ fut.get();
+ }
+ catch (SchemaOperationException e) {
+ throw convert(e);
+ }
+ catch (IgniteSQLException e) {
+ throw e;
+ }
+ catch (Exception e) {
+ throw new IgniteSQLException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Commits active transaction if exists.
+ *
+ * @throws IgniteCheckedException If failed.
+ */
+ protected void finishActiveTxIfNecessary() throws IgniteCheckedException {
+ try (GridNearTxLocal tx = MvccUtils.tx(ctx)) {
+ if (tx == null)
+ return;
+
+ if (!tx.isRollbackOnly())
+ tx.commit();
+ else
+ tx.rollback();
+ }
+ }
+
+ /**
+ * Check if cache supports DDL statement.
+ *
+ * @param cctxInfo Cache context info.
+ * @throws IgniteSQLException If failed.
+ */
+ protected static void ensureDdlSupported(GridCacheContextInfo<?, ?> cctxInfo) throws IgniteSQLException {
+ if (cctxInfo.config().getCacheMode() == CacheMode.LOCAL) {
+ throw new IgniteSQLException("DDL statements are not supported on LOCAL caches",
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ }
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlCreateIndexCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlCreateIndexCommand.java
index 7a2b48b..3190641 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlCreateIndexCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlCreateIndexCommand.java
@@ -83,6 +83,42 @@ public class SqlCreateIndexCommand implements SqlCommand {
/** Inline size. Zero effectively disables inlining. */
private int inlineSize = QueryIndex.DFLT_INLINE_SIZE;
+ /**
+ * Default constructor.
+ */
+ public SqlCreateIndexCommand() {
+ }
+
+ /**
+ * @param schemaName Schema name.
+ * @param tblName Table name.
+ * @param idxName Index name.
+ * @param ifNotExists "If not exists" clause.
+ * @param cols Indexed columns.
+ * @param spatial Spatial flag.
+ * @param parallel Count of threads to rebuild.
+ * @param inlineSize Inline size.
+ */
+ @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
+ public SqlCreateIndexCommand(String schemaName, String tblName, String idxName, boolean ifNotExists,
+ Collection<SqlIndexColumn> cols, boolean spatial, int parallel, int inlineSize) {
+ this.schemaName = schemaName;
+ this.tblName = tblName;
+ this.idxName = idxName;
+ this.ifNotExists = ifNotExists;
+ this.spatial = spatial;
+ this.parallel = parallel;
+ this.inlineSize = inlineSize;
+ this.cols = cols;
+
+ colNames = new HashSet<>();
+
+ for (SqlIndexColumn col : cols) {
+ if (!colNames.add(col.name()))
+ throw new IllegalArgumentException("Column already defined: " + col.name());
+ }
+ }
+
/** {@inheritDoc} */
@Override public String schemaName() {
return schemaName;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlDropIndexCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlDropIndexCommand.java
index 1a1ea87..eed0977 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlDropIndexCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlDropIndexCommand.java
@@ -37,6 +37,23 @@ public class SqlDropIndexCommand implements SqlCommand {
/** IF EXISTS flag. */
private boolean ifExists;
+ /**
+ * Default constructor.
+ */
+ public SqlDropIndexCommand() {
+ }
+
+ /**
+ * @param schemaName Schema name.
+ * @param idxName Index name.
+ * @param ifExists If exists clause.
+ */
+ public SqlDropIndexCommand(String schemaName, String idxName, boolean ifExists) {
+ this.schemaName = schemaName;
+ this.idxName = idxName;
+ this.ifExists = ifExists;
+ }
+
/** {@inheritDoc} */
@Override public String schemaName() {
return schemaName;
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 a1500fa..db5f715 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,16 +32,11 @@ 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;
-import org.apache.ignite.IgniteLogger;
import org.apache.ignite.IgniteSystemProperties;
-import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cache.QueryEntity;
import org.apache.ignite.cache.QueryIndex;
-import org.apache.ignite.cache.QueryIndexType;
import org.apache.ignite.cache.query.BulkLoadContextCursor;
import org.apache.ignite.cache.query.FieldsQueryCursor;
import org.apache.ignite.cluster.ClusterNode;
@@ -49,13 +44,9 @@ import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.events.DiscoveryEvent;
import org.apache.ignite.events.Event;
import org.apache.ignite.events.EventType;
-import org.apache.ignite.internal.ComputeMXBeanImpl;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.GridTopic;
import org.apache.ignite.internal.IgniteInternalFuture;
-import org.apache.ignite.internal.QueryMXBeanImpl;
-import org.apache.ignite.internal.ServiceMXBeanImpl;
-import org.apache.ignite.internal.TransactionsMXBeanImpl;
import org.apache.ignite.internal.managers.communication.GridIoPolicy;
import org.apache.ignite.internal.managers.eventstorage.GridLocalEventListener;
import org.apache.ignite.internal.processors.bulkload.BulkLoadAckClientParameters;
@@ -65,7 +56,6 @@ import org.apache.ignite.internal.processors.bulkload.BulkLoadProcessor;
import org.apache.ignite.internal.processors.bulkload.BulkLoadStreamerWriter;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
-import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
import org.apache.ignite.internal.processors.query.GridQueryProperty;
import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
@@ -91,6 +81,7 @@ import org.apache.ignite.internal.processors.query.h2.sql.GridSqlStatement;
import org.apache.ignite.internal.processors.query.messages.GridQueryKillRequest;
import org.apache.ignite.internal.processors.query.messages.GridQueryKillResponse;
import org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
+import org.apache.ignite.internal.sql.SqlCommandProcessor;
import org.apache.ignite.internal.sql.command.SqlAlterTableCommand;
import org.apache.ignite.internal.sql.command.SqlAlterUserCommand;
import org.apache.ignite.internal.sql.command.SqlBeginTransactionCommand;
@@ -101,13 +92,7 @@ import org.apache.ignite.internal.sql.command.SqlCreateIndexCommand;
import org.apache.ignite.internal.sql.command.SqlCreateUserCommand;
import org.apache.ignite.internal.sql.command.SqlDropIndexCommand;
import org.apache.ignite.internal.sql.command.SqlDropUserCommand;
-import org.apache.ignite.internal.sql.command.SqlIndexColumn;
-import org.apache.ignite.internal.sql.command.SqlKillComputeTaskCommand;
-import org.apache.ignite.internal.sql.command.SqlKillContinuousQueryCommand;
import org.apache.ignite.internal.sql.command.SqlKillQueryCommand;
-import org.apache.ignite.internal.sql.command.SqlKillScanQueryCommand;
-import org.apache.ignite.internal.sql.command.SqlKillServiceCommand;
-import org.apache.ignite.internal.sql.command.SqlKillTransactionCommand;
import org.apache.ignite.internal.sql.command.SqlRollbackTransactionCommand;
import org.apache.ignite.internal.sql.command.SqlSetStreamingCommand;
import org.apache.ignite.internal.util.future.GridFinishedFuture;
@@ -143,19 +128,13 @@ import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryPar
/**
* Processor responsible for execution of all non-SELECT and non-DML commands.
*/
-public class CommandProcessor {
- /** Kernal context. */
- private final GridKernalContext ctx;
-
+public class CommandProcessor extends SqlCommandProcessor {
/** Schema manager. */
private final SchemaManager schemaMgr;
/** H2 Indexing. */
private final IgniteH2Indexing idx;
- /** Logger. */
- private final IgniteLogger log;
-
/** Is backward compatible handling of UUID through DDL enabled. */
private static final boolean handleUuidAsByte =
IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_SQL_UUID_DDL_BYTE_FORMAT, false);
@@ -189,11 +168,10 @@ public class CommandProcessor {
* @param schemaMgr Schema manager.
*/
public CommandProcessor(GridKernalContext ctx, SchemaManager schemaMgr, IgniteH2Indexing idx) {
- this.ctx = ctx;
+ super(ctx, schemaMgr);
+
this.schemaMgr = schemaMgr;
this.idx = idx;
-
- log = ctx.log(CommandProcessor.class);
}
/**
@@ -410,29 +388,16 @@ public class CommandProcessor {
if (cmdNative != null) {
assert cmdH2 == null;
- if (isDdl(cmdNative))
- runCommandNativeDdl(sql, cmdNative);
- else if (cmdNative instanceof SqlBulkLoadCommand) {
- res = processBulkLoadCommand((SqlBulkLoadCommand) cmdNative, qryId);
+ if (isCommandSupported(cmdNative)) {
+ FieldsQueryCursor<List<?>> resNative = runNativeCommand(sql, cmdNative, params, cliCtx, qryId);
- unregister = false;
+ if (resNative != null) {
+ res = resNative;
+ unregister = true;
+ }
}
- else if (cmdNative instanceof SqlSetStreamingCommand)
- processSetStreamingCommand((SqlSetStreamingCommand)cmdNative, cliCtx);
- else if (cmdNative instanceof SqlKillQueryCommand)
- processKillQueryCommand((SqlKillQueryCommand) cmdNative);
- else if (cmdNative instanceof SqlKillComputeTaskCommand)
- processKillComputeTaskCommand((SqlKillComputeTaskCommand) cmdNative);
- else if (cmdNative instanceof SqlKillTransactionCommand)
- processKillTxCommand((SqlKillTransactionCommand) cmdNative);
- else if (cmdNative instanceof SqlKillServiceCommand)
- processKillServiceTaskCommand((SqlKillServiceCommand) cmdNative);
- else if (cmdNative instanceof SqlKillScanQueryCommand)
- processKillScanQueryCommand((SqlKillScanQueryCommand) cmdNative);
- else if (cmdNative instanceof SqlKillContinuousQueryCommand)
- processKillContinuousQueryCommand((SqlKillContinuousQueryCommand) cmdNative);
else
- processTxCommand(cmdNative, params);
+ throw new UnsupportedOperationException("Unsupported command: " + cmdNative);
}
else {
assert cmdH2 != null;
@@ -444,6 +409,44 @@ public class CommandProcessor {
}
/**
+ * Execute native command.
+ *
+ * @param sql SQL.
+ * @param cmdNative Native command.
+ * @param params Parameters.
+ * @param cliCtx Client context.
+ * @param qryId Running query ID.
+ * @return Result.
+ */
+ public FieldsQueryCursor<List<?>> runNativeCommand(String sql, SqlCommand cmdNative,
+ QueryParameters params, @Nullable SqlClientContext cliCtx, Long qryId) throws IgniteCheckedException {
+ if (super.isCommandSupported(cmdNative))
+ return runCommand(sql, cmdNative, cliCtx);
+
+ if (cmdNative instanceof SqlBulkLoadCommand)
+ return processBulkLoadCommand((SqlBulkLoadCommand) cmdNative, qryId);
+ else if (cmdNative instanceof SqlSetStreamingCommand)
+ processSetStreamingCommand((SqlSetStreamingCommand)cmdNative, cliCtx);
+ else if (cmdNative instanceof SqlKillQueryCommand)
+ processKillQueryCommand((SqlKillQueryCommand) cmdNative);
+ else
+ processTxCommand(cmdNative, params);
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isCommandSupported(SqlCommand cmd) {
+ return super.isCommandSupported(cmd)
+ || cmd instanceof SqlBeginTransactionCommand
+ || cmd instanceof SqlCommitTransactionCommand
+ || cmd instanceof SqlRollbackTransactionCommand
+ || cmd instanceof SqlBulkLoadCommand
+ || cmd instanceof SqlSetStreamingCommand
+ || cmd instanceof SqlKillQueryCommand;
+ }
+
+ /**
* Process kill query command
*
* @param cmd Command.
@@ -510,187 +513,6 @@ public class CommandProcessor {
}
/**
- * Process kill scan query cmd.
- *
- * @param cmd Command.
- */
- private void processKillScanQueryCommand(SqlKillScanQueryCommand cmd) {
- new QueryMXBeanImpl(ctx)
- .cancelScan(cmd.getOriginNodeId(), cmd.getCacheName(), cmd.getQryId());
- }
-
- /**
- * Process kill compute task command.
- *
- * @param cmd Command.
- */
- private void processKillComputeTaskCommand(SqlKillComputeTaskCommand cmd) {
- new ComputeMXBeanImpl(ctx).cancel(cmd.getSessionId());
- }
-
- /**
- * Process kill transaction cmd.
- *
- * @param cmd Command.
- */
- private void processKillTxCommand(SqlKillTransactionCommand cmd) {
- new TransactionsMXBeanImpl(ctx).cancel(cmd.getXid());
- }
-
- /**
- * Process kill service command.
- *
- * @param cmd Command.
- */
- private void processKillServiceTaskCommand(SqlKillServiceCommand cmd) {
- new ServiceMXBeanImpl(ctx).cancel(cmd.getName());
- }
-
- /**
- * Process kill continuous query cmd.
- *
- * @param cmd Command.
- */
- private void processKillContinuousQueryCommand(SqlKillContinuousQueryCommand cmd) {
- new QueryMXBeanImpl(ctx).cancelContinuous(cmd.getOriginNodeId(), cmd.getRoutineId());
- }
-
- /**
- * Run DDL statement.
- *
- * @param sql Original SQL.
- * @param cmd Command.
- */
- private void runCommandNativeDdl(String sql, SqlCommand cmd) {
- IgniteInternalFuture fut = null;
-
- try {
- isDdlOnSchemaSupported(cmd.schemaName());
-
- finishActiveTxIfNecessary();
-
- if (cmd instanceof SqlCreateIndexCommand) {
- SqlCreateIndexCommand cmd0 = (SqlCreateIndexCommand)cmd;
-
- GridH2Table tbl = schemaMgr.dataTable(cmd0.schemaName(), cmd0.tableName());
-
- if (tbl == null)
- throw new SchemaOperationException(SchemaOperationException.CODE_TABLE_NOT_FOUND, cmd0.tableName());
-
- assert tbl.rowDescriptor() != null;
-
- ensureDdlSupported(tbl);
-
- QueryIndex newIdx = new QueryIndex();
-
- newIdx.setName(cmd0.indexName());
-
- newIdx.setIndexType(cmd0.spatial() ? QueryIndexType.GEOSPATIAL : QueryIndexType.SORTED);
-
- LinkedHashMap<String, Boolean> flds = new LinkedHashMap<>();
-
- // Let's replace H2's table and property names by those operated by GridQueryProcessor.
- GridQueryTypeDescriptor typeDesc = tbl.rowDescriptor().type();
-
- for (SqlIndexColumn col : cmd0.columns()) {
- GridQueryProperty prop = typeDesc.property(col.name());
-
- if (prop == null)
- throw new SchemaOperationException(SchemaOperationException.CODE_COLUMN_NOT_FOUND, col.name());
-
- flds.put(prop.name(), !col.descending());
- }
-
- newIdx.setFields(flds);
- newIdx.setInlineSize(cmd0.inlineSize());
-
- fut = ctx.query().dynamicIndexCreate(tbl.cacheName(), cmd.schemaName(), typeDesc.tableName(),
- newIdx, cmd0.ifNotExists(), cmd0.parallel());
- }
- else if (cmd instanceof SqlDropIndexCommand) {
- SqlDropIndexCommand cmd0 = (SqlDropIndexCommand)cmd;
-
- GridH2Table tbl = schemaMgr.dataTableForIndex(cmd0.schemaName(), cmd0.indexName());
-
- if (tbl != null) {
- ensureDdlSupported(tbl);
-
- fut = ctx.query().dynamicIndexDrop(tbl.cacheName(), cmd0.schemaName(), cmd0.indexName(),
- cmd0.ifExists());
- }
- else {
- if (cmd0.ifExists())
- fut = new GridFinishedFuture();
- else
- throw new SchemaOperationException(SchemaOperationException.CODE_INDEX_NOT_FOUND,
- cmd0.indexName());
- }
- }
- else if (cmd instanceof SqlAlterTableCommand) {
- SqlAlterTableCommand cmd0 = (SqlAlterTableCommand)cmd;
-
- GridH2Table tbl = schemaMgr.dataTable(cmd0.schemaName(), cmd0.tableName());
-
- if (tbl == null) {
- throw new SchemaOperationException(SchemaOperationException.CODE_TABLE_NOT_FOUND,
- cmd0.tableName());
- }
-
- Boolean logging = cmd0.logging();
-
- assert logging != null : "Only LOGGING/NOLOGGING are supported at the moment.";
-
- IgniteCluster cluster = ctx.grid().cluster();
-
- if (logging) {
- boolean res = cluster.enableWal(tbl.cacheName());
-
- if (!res)
- throw new IgniteSQLException("Logging already enabled for table: " + cmd0.tableName());
- }
- else {
- boolean res = cluster.disableWal(tbl.cacheName());
-
- if (!res)
- throw new IgniteSQLException("Logging already disabled for table: " + cmd0.tableName());
- }
-
- fut = new GridFinishedFuture();
- }
- else if (cmd instanceof SqlCreateUserCommand) {
- SqlCreateUserCommand addCmd = (SqlCreateUserCommand)cmd;
-
- ctx.security().createUser(addCmd.userName(), addCmd.password().toCharArray());
- }
- else if (cmd instanceof SqlAlterUserCommand) {
- SqlAlterUserCommand altCmd = (SqlAlterUserCommand)cmd;
-
- ctx.security().alterUser(altCmd.userName(), altCmd.password().toCharArray());
- }
- else if (cmd instanceof SqlDropUserCommand) {
- SqlDropUserCommand dropCmd = (SqlDropUserCommand)cmd;
-
- ctx.security().dropUser(dropCmd.userName());
- }
- else
- throw new IgniteSQLException("Unsupported DDL operation: " + sql,
- IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
-
- if (fut != null)
- fut.get();
- }
- catch (SchemaOperationException e) {
- throw convert(e);
- }
- catch (IgniteSQLException e) {
- throw e;
- }
- catch (Exception e) {
- throw new IgniteSQLException(e.getMessage(), e);
- }
- }
-
- /**
* Execute DDL statement.
*
* @param sql SQL.
@@ -714,7 +536,7 @@ public class CommandProcessor {
assert tbl.rowDescriptor() != null;
- ensureDdlSupported(tbl);
+ ensureDdlSupported(tbl.cacheInfo());
QueryIndex newIdx = new QueryIndex();
@@ -749,7 +571,7 @@ public class CommandProcessor {
GridH2Table tbl = schemaMgr.dataTableForIndex(cmd.schemaName(), cmd.indexName());
if (tbl != null) {
- ensureDdlSupported(tbl);
+ ensureDdlSupported(tbl.cacheInfo());
fut = ctx.query().dynamicIndexDrop(tbl.cacheName(), cmd.schemaName(), cmd.indexName(),
cmd.ifExists());
@@ -969,35 +791,6 @@ public class CommandProcessor {
}
/**
- * Check if table supports DDL statement.
- *
- * @param tbl Table.
- * @throws IgniteSQLException If failed.
- */
- private static void ensureDdlSupported(GridH2Table tbl) throws IgniteSQLException {
- if (tbl.cacheInfo().config().getCacheMode() == CacheMode.LOCAL)
- throw new IgniteSQLException("DDL statements are not supported on LOCAL caches",
- IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
- }
-
- /**
- * Commits active transaction if exists.
- *
- * @throws IgniteCheckedException If failed.
- */
- private void finishActiveTxIfNecessary() throws IgniteCheckedException {
- try (GridNearTxLocal tx = MvccUtils.tx(ctx)) {
- if (tx == null)
- return;
-
- if (!tx.isRollbackOnly())
- tx.commit();
- else
- tx.rollback();
- }
- }
-
- /**
* Convert this statement to query entity and do Ignite specific sanity checks on the way.
* @return Query entity mimicking this SQL statement.
*/
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
index 7543c27..d1185e3 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
@@ -2101,7 +2101,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
longRunningQryMgr = new LongRunningQueryManager(ctx);
- parser = new QueryParser(this, connMgr);
+ parser = new QueryParser(this, connMgr, cmd -> cmdProc.isCommandSupported(cmd));
schemaMgr = new SchemaManager(ctx, connMgr);
schemaMgr.start(ctx.config().getSqlConfiguration().getSqlSchemas());
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java
index 9f01516..2eec586 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java
@@ -23,6 +23,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.function.Predicate;
import java.util.regex.Pattern;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteLogger;
@@ -59,24 +60,7 @@ import org.apache.ignite.internal.processors.tracing.MTC.TraceSurroundings;
import org.apache.ignite.internal.sql.SqlParseException;
import org.apache.ignite.internal.sql.SqlParser;
import org.apache.ignite.internal.sql.SqlStrictParseException;
-import org.apache.ignite.internal.sql.command.SqlAlterTableCommand;
-import org.apache.ignite.internal.sql.command.SqlAlterUserCommand;
-import org.apache.ignite.internal.sql.command.SqlBeginTransactionCommand;
-import org.apache.ignite.internal.sql.command.SqlBulkLoadCommand;
import org.apache.ignite.internal.sql.command.SqlCommand;
-import org.apache.ignite.internal.sql.command.SqlCommitTransactionCommand;
-import org.apache.ignite.internal.sql.command.SqlCreateIndexCommand;
-import org.apache.ignite.internal.sql.command.SqlCreateUserCommand;
-import org.apache.ignite.internal.sql.command.SqlDropIndexCommand;
-import org.apache.ignite.internal.sql.command.SqlDropUserCommand;
-import org.apache.ignite.internal.sql.command.SqlKillComputeTaskCommand;
-import org.apache.ignite.internal.sql.command.SqlKillContinuousQueryCommand;
-import org.apache.ignite.internal.sql.command.SqlKillQueryCommand;
-import org.apache.ignite.internal.sql.command.SqlKillScanQueryCommand;
-import org.apache.ignite.internal.sql.command.SqlKillServiceCommand;
-import org.apache.ignite.internal.sql.command.SqlKillTransactionCommand;
-import org.apache.ignite.internal.sql.command.SqlRollbackTransactionCommand;
-import org.apache.ignite.internal.sql.command.SqlSetStreamingCommand;
import org.apache.ignite.internal.util.GridBoundedConcurrentLinkedHashMap;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.U;
@@ -112,6 +96,9 @@ public class QueryParser {
/** Query parser metrics holder. */
private final QueryParserMetricsHolder metricsHolder;
+ /** Predicate to filter supported native commands. */
+ private final Predicate<SqlCommand> nativeCmdPredicate;
+
/** */
private volatile GridBoundedConcurrentLinkedHashMap<QueryDescriptor, QueryParserCacheEntry> cache =
new GridBoundedConcurrentLinkedHashMap<>(CACHE_SIZE);
@@ -121,10 +108,12 @@ public class QueryParser {
*
* @param idx Indexing instance.
* @param connMgr Connection manager.
+ * @param nativeCmdPredicate Predicate to filter supported native commands.
*/
- public QueryParser(IgniteH2Indexing idx, ConnectionManager connMgr) {
+ public QueryParser(IgniteH2Indexing idx, ConnectionManager connMgr, Predicate<SqlCommand> nativeCmdPredicate) {
this.idx = idx;
this.connMgr = connMgr;
+ this.nativeCmdPredicate = nativeCmdPredicate;
this.log = idx.kernalContext().log(QueryParser.class);
this.metricsHolder = new QueryParserMetricsHolder(idx.kernalContext().metric());
@@ -264,24 +253,7 @@ public class QueryParser {
assert nativeCmd != null : "Empty query. Parser met end of data";
- if (!(nativeCmd instanceof SqlCreateIndexCommand
- || nativeCmd instanceof SqlDropIndexCommand
- || nativeCmd instanceof SqlBeginTransactionCommand
- || nativeCmd instanceof SqlCommitTransactionCommand
- || nativeCmd instanceof SqlRollbackTransactionCommand
- || nativeCmd instanceof SqlBulkLoadCommand
- || nativeCmd instanceof SqlAlterTableCommand
- || nativeCmd instanceof SqlSetStreamingCommand
- || nativeCmd instanceof SqlCreateUserCommand
- || nativeCmd instanceof SqlAlterUserCommand
- || nativeCmd instanceof SqlDropUserCommand
- || nativeCmd instanceof SqlKillQueryCommand
- || nativeCmd instanceof SqlKillComputeTaskCommand
- || nativeCmd instanceof SqlKillServiceCommand
- || nativeCmd instanceof SqlKillTransactionCommand
- || nativeCmd instanceof SqlKillScanQueryCommand
- || nativeCmd instanceof SqlKillContinuousQueryCommand)
- )
+ if (!nativeCmdPredicate.test(nativeCmd))
return null;
SqlFieldsQuery newQry = cloneFieldsQuery(qry).setSql(parser.lastCommandSql());
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/SchemaManager.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/SchemaManager.java
index 89498ee..67ffad5 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/SchemaManager.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/SchemaManager.java
@@ -49,6 +49,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheContextInfo;
import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
import org.apache.ignite.internal.processors.cache.query.QueryTable;
import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor;
+import org.apache.ignite.internal.processors.query.GridQuerySchemaManager;
import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.QueryField;
@@ -86,7 +87,7 @@ import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.metr
/**
* Schema manager. Responsible for all manipulations on schema objects.
*/
-public class SchemaManager {
+public class SchemaManager implements GridQuerySchemaManager {
/** */
public static final String SQL_SCHEMA_VIEW = "schemas";
@@ -890,6 +891,27 @@ public class SchemaManager {
return null;
}
+ /** {@inheritDoc} */
+ @Override public GridQueryTypeDescriptor typeDescriptorForTable(String schemaName, String tableName) {
+ GridH2Table dataTable = dataTable(schemaName, tableName);
+
+ return dataTable == null ? null : dataTable.rowDescriptor().type();
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridQueryTypeDescriptor typeDescriptorForIndex(String schemaName, String idxName) {
+ GridH2Table dataTable = dataTableForIndex(schemaName, idxName);
+
+ return dataTable == null ? null : dataTable.rowDescriptor().type();
+ }
+
+ /** {@inheritDoc} */
+ @Override public <K, V> GridCacheContextInfo<K, V> cacheInfoForTable(String schemaName, String tableName) {
+ GridH2Table dataTable = dataTable(schemaName, tableName);
+
+ return dataTable == null ? null : (GridCacheContextInfo<K, V>)dataTable.cacheInfo();
+ }
+
/** */
private SchemaChangeListener schemaChangeListener(GridKernalContext ctx) {
List<SchemaChangeListener> subscribers = new ArrayList<>(ctx.internalSubscriptionProcessor().getSchemaChangeSubscribers());