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 11ef1ab IGNITE-14857 ALTER TABLE command - Fixes #9185.
11ef1ab is described below
commit 11ef1abda9852db2f4f11c7233a54b09b57e7a95
Author: Aleksey Plekhanov <[email protected]>
AuthorDate: Tue Jul 20 10:58:29 2021 +0300
IGNITE-14857 ALTER TABLE command - Fixes #9185.
Signed-off-by: Aleksey Plekhanov <[email protected]>
---
modules/calcite/src/main/codegen/config.fmpp | 11 +-
.../src/main/codegen/includes/parserImpls.ftl | 75 +++++
.../query/calcite/exec/ddl/DdlCommandHandler.java | 158 +++++++++-
.../calcite/exec/ddl/NativeCommandHandler.java | 66 +----
.../query/calcite/exec/ddl/SchemaManager.java | 82 ++++++
.../prepare/ddl/AbstractAlterTableCommand.java | 74 +++++
.../calcite/prepare/ddl/AlterTableAddCommand.java | 61 ++++
.../calcite/prepare/ddl/AlterTableDropCommand.java | 61 ++++
.../prepare/ddl/DdlSqlToCommandConverter.java | 66 +++++
.../prepare/ddl/SqlToNativeCommandConverter.java | 17 +-
.../calcite/sql/IgniteAbstractSqlAlterTable.java | 84 ++++++
.../query/calcite/sql/IgniteSqlAlterTable.java | 55 ++++
.../calcite/sql/IgniteSqlAlterTableAddColumn.java | 75 +++++
.../calcite/sql/IgniteSqlAlterTableDropColumn.java | 75 +++++
.../integration/AbstractDdlIntegrationTest.java | 20 +-
.../integration/TableDdlIntegrationTest.java | 323 ++++++++++++++++++++-
.../query/calcite/sql/SqlDdlParserTest.java | 210 +++++++++++++-
.../processors/odbc/jdbc/JdbcRequestHandler.java | 8 +-
.../processors/query/GridQueryProcessor.java | 20 +-
.../internal/sql/command/SqlAlterTableCommand.java | 18 ++
20 files changed, 1440 insertions(+), 119 deletions(-)
diff --git a/modules/calcite/src/main/codegen/config.fmpp
b/modules/calcite/src/main/codegen/config.fmpp
index f40d158..376aa2f 100644
--- a/modules/calcite/src/main/codegen/config.fmpp
+++ b/modules/calcite/src/main/codegen/config.fmpp
@@ -54,6 +54,8 @@ data: {
"INDEX"
"PARALLEL"
"INLINE_SIZE"
+ "LOGGING"
+ "NOLOGGING"
]
# List of non-reserved keywords to add;
@@ -73,6 +75,8 @@ data: {
"ENCRYPTED"
"PARALLEL"
"INLINE_SIZE"
+ "LOGGING"
+ "NOLOGGING"
# The following keywords are reserved in core Calcite,
# are reserved in some version of SQL,
@@ -582,6 +586,7 @@ data: {
# Return type of method implementation should be 'SqlNode'.
# Example: "SqlShowDatabases()", "SqlShowTables()".
statementParserMethods: [
+ "SqlAlterTable()"
]
# List of methods for parsing extensions to "CREATE [OR REPLACE]" calls.
@@ -600,9 +605,9 @@ data: {
"SqlDropIndex"
]
- # List of methods for parsing extensions to "DROP" calls.
- # Each must accept arguments "(SqlParserPos pos)".
- # Example: "SqlDropSchema".
+ # List of methods for parsing extensions to "ALTER <scope>" calls.
+ # Where scope is SYSTEM or SESSION.
+ # Each must accept arguments "(SqlParserPos pos, String scope)".
alterStatementParserMethods: [
]
diff --git a/modules/calcite/src/main/codegen/includes/parserImpls.ftl
b/modules/calcite/src/main/codegen/includes/parserImpls.ftl
index aad7cfb..811f4f7 100644
--- a/modules/calcite/src/main/codegen/includes/parserImpls.ftl
+++ b/modules/calcite/src/main/codegen/includes/parserImpls.ftl
@@ -281,3 +281,78 @@ void InfixCast(List<Object> list, ExprContext exprContext,
Span s) :
list.add(dt);
}
}
+
+SqlNodeList ColumnWithTypeList() :
+{
+ final Span s;
+ List<SqlNode> list = new ArrayList<SqlNode>();
+ SqlNode col;
+}
+{
+ <LPAREN> { s = span(); }
+ col = ColumnWithType() { list.add(col); }
+ (
+ <COMMA> col = ColumnWithType() { list.add(col); }
+ )*
+ <RPAREN> {
+ return new SqlNodeList(list, s.end(this));
+ }
+}
+
+SqlNode ColumnWithType() :
+{
+ SqlIdentifier id;
+ SqlDataTypeSpec type;
+ boolean nullable = true;
+ final Span s = Span.of();
+}
+{
+ id = SimpleIdentifier()
+ type = DataType()
+ [
+ <NOT> <NULL> {
+ nullable = false;
+ }
+ ]
+ {
+ return SqlDdlNodes.column(s.add(id).end(this), id,
type.withNullable(nullable), null, null);
+ }
+}
+
+SqlNodeList ColumnWithTypeOrList() :
+{
+ SqlNode col;
+ SqlNodeList list;
+}
+{
+ col = ColumnWithType() { return new
SqlNodeList(Collections.singletonList(col), col.getParserPosition()); }
+|
+ list = ColumnWithTypeList() { return list; }
+}
+
+SqlNode SqlAlterTable() :
+{
+ final Span s;
+ final boolean ifExists;
+ final SqlIdentifier id;
+ boolean colIgnoreErr;
+ SqlNode col;
+ SqlNodeList cols;
+}
+{
+ <ALTER> { s = span(); }
+ <TABLE> ifExists = IfExistsOpt() id = CompoundIdentifier()
+ (
+ <LOGGING> { return new IgniteSqlAlterTable(s.end(this), ifExists, id,
true); }
+ |
+ <NOLOGGING> { return new IgniteSqlAlterTable(s.end(this), ifExists,
id, false); }
+ |
+ <ADD> [<COLUMN>] colIgnoreErr = IfNotExistsOpt() cols =
ColumnWithTypeOrList() {
+ return new IgniteSqlAlterTableAddColumn(s.end(this), ifExists, id,
colIgnoreErr, cols);
+ }
+ |
+ <DROP> [<COLUMN>] colIgnoreErr = IfExistsOpt() cols =
SimpleIdentifierOrList() {
+ return new IgniteSqlAlterTableDropColumn(s.end(this), ifExists,
id, colIgnoreErr, cols);
+ }
+ )
+}
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 405be78..eedd0bc 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
@@ -18,10 +18,12 @@
package org.apache.ignite.internal.processors.query.calcite.exec.ddl;
import java.lang.reflect.Type;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -31,13 +33,20 @@ import org.apache.calcite.schema.Table;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.cache.QueryEntity;
import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheContextInfo;
import org.apache.ignite.internal.processors.cache.GridCacheProcessor;
import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
import org.apache.ignite.internal.processors.query.GridQueryProcessor;
+import org.apache.ignite.internal.processors.query.GridQuerySchemaManager;
+import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.QueryEntityEx;
+import org.apache.ignite.internal.processors.query.QueryField;
import org.apache.ignite.internal.processors.query.QueryUtils;
import
org.apache.ignite.internal.processors.query.calcite.prepare.PlanningContext;
+import
org.apache.ignite.internal.processors.query.calcite.prepare.ddl.AlterTableAddCommand;
+import
org.apache.ignite.internal.processors.query.calcite.prepare.ddl.AlterTableDropCommand;
import
org.apache.ignite.internal.processors.query.calcite.prepare.ddl.ColumnDefinition;
import
org.apache.ignite.internal.processors.query.calcite.prepare.ddl.CreateTableCommand;
import
org.apache.ignite.internal.processors.query.calcite.prepare.ddl.DdlCommand;
@@ -69,7 +78,10 @@ public class DdlCommandHandler {
private final Supplier<SchemaPlus> schemaSupp;
/** */
- private final NativeCommandHandler nativeCmdHandler;
+ private final NativeCommandHandler nativeCmdHnd;
+
+ /** */
+ private final GridQuerySchemaManager schemaMgr;
/** */
public DdlCommandHandler(Supplier<GridQueryProcessor> qryProcessorSupp,
GridCacheProcessor cacheProcessor,
@@ -78,24 +90,36 @@ public class DdlCommandHandler {
this.cacheProcessor = cacheProcessor;
this.security = security;
this.schemaSupp = schemaSupp;
- nativeCmdHandler = new
NativeCommandHandler(cacheProcessor.context().kernalContext(), schemaSupp);
+ schemaMgr = new SchemaManager(schemaSupp);
+ nativeCmdHnd = new
NativeCommandHandler(cacheProcessor.context().kernalContext(), schemaMgr);
}
/** */
public void handle(UUID qryId, DdlCommand cmd, PlanningContext pctx)
throws IgniteCheckedException {
- if (cmd instanceof CreateTableCommand)
- handle0(pctx, (CreateTableCommand)cmd);
+ try {
+ if (cmd instanceof CreateTableCommand)
+ handle0(pctx, (CreateTableCommand)cmd);
- else if (cmd instanceof DropTableCommand)
- handle0(pctx, (DropTableCommand)cmd);
+ else if (cmd instanceof DropTableCommand)
+ handle0(pctx, (DropTableCommand)cmd);
- else if (cmd instanceof NativeCommandWrapper)
- nativeCmdHandler.handle(qryId, (NativeCommandWrapper)cmd, pctx);
+ else if (cmd instanceof AlterTableAddCommand)
+ handle0(pctx, (AlterTableAddCommand)cmd);
- else {
- throw new IgniteSQLException("Unsupported DDL operation [" +
- "cmdName=" + (cmd == null ? null :
cmd.getClass().getSimpleName()) + "; " +
- "querySql=\"" + pctx.query() + "\"]",
IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ else if (cmd instanceof AlterTableDropCommand)
+ handle0(pctx, (AlterTableDropCommand)cmd);
+
+ else if (cmd instanceof NativeCommandWrapper)
+ nativeCmdHnd.handle(qryId, (NativeCommandWrapper)cmd, pctx);
+
+ else {
+ throw new IgniteSQLException("Unsupported DDL operation [" +
+ "cmdName=" + (cmd == null ? null :
cmd.getClass().getSimpleName()) + "; " +
+ "querySql=\"" + pctx.query() + "\"]",
IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ }
+ }
+ catch (SchemaOperationException e) {
+ throw convert(e);
}
}
@@ -174,6 +198,116 @@ public class DdlCommandHandler {
}
/** */
+ private void handle0(PlanningContext pctx, AlterTableAddCommand cmd)
throws IgniteCheckedException {
+ isDdlOnSchemaSupported(cmd.schemaName());
+
+ GridQueryTypeDescriptor typeDesc =
schemaMgr.typeDescriptorForTable(cmd.schemaName(), cmd.tableName());
+
+ if (typeDesc == null) {
+ if (!cmd.ifTableExists())
+ throw new
SchemaOperationException(SchemaOperationException.CODE_TABLE_NOT_FOUND,
cmd.tableName());
+ }
+ else {
+ if (QueryUtils.isSqlType(typeDesc.valueClass())) {
+ throw new SchemaOperationException("Cannot add column(s)
because table was created " +
+ "with WRAP_VALUE=false option.");
+ }
+
+ List<QueryField> cols = new ArrayList<>(cmd.columns().size());
+
+ boolean allFieldsNullable = true;
+
+ for (ColumnDefinition col : cmd.columns()) {
+ if (typeDesc.fields().containsKey(col.name())) {
+ if (!cmd.ifColumnNotExists())
+ throw new
SchemaOperationException(SchemaOperationException.CODE_COLUMN_EXISTS,
col.name());
+ else
+ continue;
+ }
+
+ Type javaType = pctx.typeFactory().getResultClass(col.type());
+
+ String typeName = javaType instanceof Class ?
((Class<?>)javaType).getName() : javaType.getTypeName();
+
+ Integer precession = col.precision();
+ Integer scale = col.scale();
+
+ QueryField field = new QueryField(col.name(), typeName,
+ col.type().isNullable(), col.defaultValue(),
+ precession == null ? -1 : precession, scale == null ? -1 :
scale);
+
+ cols.add(field);
+
+ allFieldsNullable &= field.isNullable();
+ }
+
+ if (!F.isEmpty(cols)) {
+ GridCacheContextInfo<?, ?> ctxInfo =
schemaMgr.cacheInfoForTable(cmd.schemaName(), cmd.tableName());
+
+ assert ctxInfo != null;
+
+ if (!allFieldsNullable)
+ QueryUtils.checkNotNullAllowed(ctxInfo.config());
+
+ qryProcessorSupp.get().dynamicColumnAdd(ctxInfo.name(),
cmd.schemaName(),
+ typeDesc.tableName(), cols, cmd.ifTableExists(),
cmd.ifColumnNotExists()).get();
+ }
+ }
+ }
+
+ /** */
+ private void handle0(PlanningContext pctx, AlterTableDropCommand cmd)
throws IgniteCheckedException {
+ isDdlOnSchemaSupported(cmd.schemaName());
+
+ GridQueryTypeDescriptor typeDesc =
schemaMgr.typeDescriptorForTable(cmd.schemaName(), cmd.tableName());
+
+ if (typeDesc == null) {
+ if (!cmd.ifTableExists())
+ throw new
SchemaOperationException(SchemaOperationException.CODE_TABLE_NOT_FOUND,
cmd.tableName());
+ }
+ else {
+ GridCacheContextInfo<?, ?> ctxInfo =
schemaMgr.cacheInfoForTable(cmd.schemaName(), cmd.tableName());
+
+ GridCacheContext<?, ?> cctx = ctxInfo.cacheContext();
+
+ assert cctx != null;
+
+ if (cctx.mvccEnabled()) {
+ throw new IgniteSQLException("Cannot drop column(s) with
enabled MVCC. " +
+ "Operation is unsupported at the moment.",
IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ }
+
+ if (QueryUtils.isSqlType(typeDesc.valueClass())) {
+ throw new SchemaOperationException("Cannot drop column(s)
because table was created " +
+ "with WRAP_VALUE=false option.");
+ }
+
+ List<String> cols = new ArrayList<>(cmd.columns().size());
+
+ for (String colName : cmd.columns()) {
+ if (!typeDesc.fields().containsKey(colName)) {
+ if (!cmd.ifColumnExists())
+ throw new
SchemaOperationException(SchemaOperationException.CODE_COLUMN_NOT_FOUND,
colName);
+ else
+ continue;
+ }
+
+ SchemaOperationException err =
QueryUtils.validateDropColumn(typeDesc, colName);
+
+ if (err != null)
+ throw err;
+
+ cols.add(colName);
+ }
+
+ if (!F.isEmpty(cols)) {
+ qryProcessorSupp.get().dynamicColumnRemove(ctxInfo.name(),
cmd.schemaName(),
+ typeDesc.tableName(), cols, cmd.ifTableExists(),
cmd.ifColumnExists()).get();
+ }
+ }
+ }
+
+ /** */
private QueryEntity toQueryEntity(CreateTableCommand cmd, PlanningContext
pctx) {
QueryEntity res = new QueryEntity();
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
index 0fc2f1c..d2b0738 100644
---
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
@@ -19,18 +19,12 @@ 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;
/**
@@ -43,8 +37,8 @@ public class NativeCommandHandler {
/**
* @param ctx Context.
*/
- public NativeCommandHandler(GridKernalContext ctx, Supplier<SchemaPlus>
schemaSupp) {
- proc = new SqlCommandProcessor(ctx, new SchemaManager(schemaSupp));
+ public NativeCommandHandler(GridKernalContext ctx, GridQuerySchemaManager
schemaMgr) {
+ proc = new SqlCommandProcessor(ctx, schemaMgr);
}
/**
@@ -57,60 +51,4 @@ public class NativeCommandHandler {
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/exec/ddl/SchemaManager.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ddl/SchemaManager.java
new file mode 100644
index 0000000..cbf375f
--- /dev/null
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ddl/SchemaManager.java
@@ -0,0 +1,82 @@
+/*
+ * 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.function.Supplier;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.schema.Table;
+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.calcite.schema.IgniteTable;
+
+/**
+ * Schema manager.
+ */
+class SchemaManager implements GridQuerySchemaManager {
+ /** Schema holder. */
+ private final Supplier<SchemaPlus> schemaSupp;
+
+ /**
+ * @param schemaSupp Schema supplier.
+ */
+ 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/AbstractAlterTableCommand.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/AbstractAlterTableCommand.java
new file mode 100644
index 0000000..f721385
--- /dev/null
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/AbstractAlterTableCommand.java
@@ -0,0 +1,74 @@
+/*
+ * 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;
+
+/**
+ * ALTER TABLE ... ADD/DROP COLUMN statement.
+ */
+public abstract class AbstractAlterTableCommand implements DdlCommand {
+ /** Schema name. */
+ protected String schemaName;
+
+ /** Table name. */
+ protected String tblName;
+
+ /** Quietly ignore this command if table is not exists. */
+ protected boolean ifTableExists;
+
+ /**
+ * @return Schema name.
+ */
+ public String schemaName() {
+ return schemaName;
+ }
+
+ /**
+ * @param schemaName Schema name.
+ */
+ public void schemaName(String schemaName) {
+ this.schemaName = schemaName;
+ }
+
+ /**
+ * @return Table name.
+ */
+ public String tableName() {
+ return tblName;
+ }
+
+ /**
+ * @param tblName Table name.
+ */
+ public void tableName(String tblName) {
+ this.tblName = tblName;
+ }
+
+ /**
+ * @return Quietly ignore this command if table is not exists.
+ */
+ public boolean ifTableExists() {
+ return ifTableExists;
+ }
+
+ /**
+ * @param ifTableExists Quietly ignore this command if table is not exists.
+ */
+ public void ifTableExists(boolean ifTableExists) {
+ this.ifTableExists = ifTableExists;
+ }
+}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/AlterTableAddCommand.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/AlterTableAddCommand.java
new file mode 100644
index 0000000..78a4ff7
--- /dev/null
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/AlterTableAddCommand.java
@@ -0,0 +1,61 @@
+/*
+ * 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.Collections;
+import java.util.List;
+
+/**
+ * ALTER TABLE ... ADD COLUMN statement.
+ */
+@SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
+public class AlterTableAddCommand extends AbstractAlterTableCommand {
+ /** Quietly ignore this command if column already exists. */
+ private boolean ifColumnNotExists;
+
+ /** Columns. */
+ private List<ColumnDefinition> cols;
+
+ /**
+ * @return Columns.
+ */
+ public List<ColumnDefinition> columns() {
+ return Collections.unmodifiableList(cols);
+ }
+
+ /**
+ * @param cols Columns.
+ */
+ public void columns(List<ColumnDefinition> cols) {
+ this.cols = cols;
+ }
+
+ /**
+ * @return Quietly ignore this command if column already exists.
+ */
+ public boolean ifColumnNotExists() {
+ return ifColumnNotExists;
+ }
+
+ /**
+ * @param ifColumnNotExists Quietly ignore this command if column already
exists.
+ */
+ public void ifColumnNotExists(boolean ifColumnNotExists) {
+ this.ifColumnNotExists = ifColumnNotExists;
+ }
+}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/AlterTableDropCommand.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/AlterTableDropCommand.java
new file mode 100644
index 0000000..5bf2b3e
--- /dev/null
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/AlterTableDropCommand.java
@@ -0,0 +1,61 @@
+/*
+ * 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.Collections;
+import java.util.List;
+
+/**
+ * ALTER TABLE ... DROP COLUMN statement.
+ */
+@SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
+public class AlterTableDropCommand extends AbstractAlterTableCommand {
+ /** Quietly ignore this command if column is not exists. */
+ private boolean ifColumnExists;
+
+ /** Columns. */
+ private List<String> cols;
+
+ /**
+ * @return Columns.
+ */
+ public List<String> columns() {
+ return Collections.unmodifiableList(cols);
+ }
+
+ /**
+ * @param cols Columns.
+ */
+ public void columns(List<String> cols) {
+ this.cols = cols;
+ }
+
+ /**
+ * @return Quietly ignore this command if column is not exists.
+ */
+ public boolean ifColumnExists() {
+ return ifColumnExists;
+ }
+
+ /**
+ * @param ifColumnExists Quietly ignore this command if column is not
exists.
+ */
+ public void ifColumnExists(boolean ifColumnExists) {
+ this.ifColumnExists = ifColumnExists;
+ }
+}
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 b5be013..9d1f981 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
@@ -45,6 +45,8 @@ import
org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.QueryUtils;
import
org.apache.ignite.internal.processors.query.calcite.prepare.IgnitePlanner;
import
org.apache.ignite.internal.processors.query.calcite.prepare.PlanningContext;
+import
org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlAlterTableAddColumn;
+import
org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlAlterTableDropColumn;
import
org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTable;
import
org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOption;
import
org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOptionEnum;
@@ -125,6 +127,12 @@ public class DdlSqlToCommandConverter {
if (ddlNode instanceof SqlDropTable)
return convertDropTable((SqlDropTable)ddlNode, ctx);
+ if (ddlNode instanceof IgniteSqlAlterTableAddColumn)
+ return convertAlterTableAdd((IgniteSqlAlterTableAddColumn)ddlNode,
ctx);
+
+ if (ddlNode instanceof IgniteSqlAlterTableDropColumn)
+ return
convertAlterTableDrop((IgniteSqlAlterTableDropColumn)ddlNode, ctx);
+
if (SqlToNativeCommandConverter.isSupported(ddlNode))
return SqlToNativeCommandConverter.convert(ddlNode, ctx);
@@ -227,6 +235,64 @@ public class DdlSqlToCommandConverter {
}
/**
+ * Converts a given IgniteSqlAlterTableAddColumn AST to a
AlterTableAddCommand.
+ *
+ * @param alterTblNode Root node of the given AST.
+ * @param ctx Planning context.
+ */
+ private AlterTableAddCommand
convertAlterTableAdd(IgniteSqlAlterTableAddColumn alterTblNode, PlanningContext
ctx) {
+ AlterTableAddCommand alterTblCmd = new AlterTableAddCommand();
+
+ alterTblCmd.schemaName(deriveSchemaName(alterTblNode.name(), ctx));
+ alterTblCmd.tableName(deriveObjectName(alterTblNode.name(), ctx,
"table name"));
+ alterTblCmd.ifTableExists(alterTblNode.ifExists());
+ alterTblCmd.ifColumnNotExists(alterTblNode.ifNotExistsColumn());
+
+ List<ColumnDefinition> cols = new
ArrayList<>(alterTblNode.columns().size());
+
+ for (SqlNode colNode : alterTblNode.columns()) {
+ assert colNode instanceof SqlColumnDeclaration :
colNode.getClass();
+
+ SqlColumnDeclaration col = (SqlColumnDeclaration)colNode;
+
+ assert col.name.isSimple();
+
+ String name = col.name.getSimple();
+ RelDataType type = ctx.planner().convert(col.dataType);
+
+ assert col.expression == null : "Unexpected column default value"
+ col.expression;
+
+ cols.add(new ColumnDefinition(name, type, null));
+ }
+
+ alterTblCmd.columns(cols);
+
+ return alterTblCmd;
+ }
+
+ /**
+ * Converts a given IgniteSqlAlterTableDropColumn AST to a
AlterTableDropCommand.
+ *
+ * @param alterTblNode Root node of the given AST.
+ * @param ctx Planning context.
+ */
+ private AlterTableDropCommand
convertAlterTableDrop(IgniteSqlAlterTableDropColumn alterTblNode,
PlanningContext ctx) {
+ AlterTableDropCommand alterTblCmd = new AlterTableDropCommand();
+
+ alterTblCmd.schemaName(deriveSchemaName(alterTblNode.name(), ctx));
+ alterTblCmd.tableName(deriveObjectName(alterTblNode.name(), ctx,
"table name"));
+ alterTblCmd.ifTableExists(alterTblNode.ifExists());
+ alterTblCmd.ifColumnExists(alterTblNode.ifExistsColumn());
+
+ List<String> cols = new ArrayList<>(alterTblNode.columns().size());
+ alterTblNode.columns().forEach(c ->
cols.add(((SqlIdentifier)c).getSimple()));
+
+ alterTblCmd.columns(cols);
+
+ return alterTblCmd;
+ }
+
+ /**
* Short cut for validating that option value is a simple identifier.
*
* @param opt An option to validate.
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
index bec218d..4f6d0ad 100644
---
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
@@ -28,8 +28,10 @@ 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.IgniteSqlAlterTable;
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.SqlAlterTableCommand;
import org.apache.ignite.internal.sql.command.SqlCommand;
import org.apache.ignite.internal.sql.command.SqlCreateIndexCommand;
import org.apache.ignite.internal.sql.command.SqlDropIndexCommand;
@@ -47,7 +49,8 @@ public class SqlToNativeCommandConverter {
*/
public static boolean isSupported(SqlNode sqlCmd) {
return sqlCmd instanceof IgniteSqlCreateIndex
- || sqlCmd instanceof IgniteSqlDropIndex;
+ || sqlCmd instanceof IgniteSqlDropIndex
+ || sqlCmd instanceof IgniteSqlAlterTable;
}
/**
@@ -68,6 +71,8 @@ public class SqlToNativeCommandConverter {
return convertCreateIndex((IgniteSqlCreateIndex)cmd, pctx);
else if (cmd instanceof IgniteSqlDropIndex)
return convertDropIndex((IgniteSqlDropIndex)cmd, pctx);
+ else if (cmd instanceof IgniteSqlAlterTable)
+ return convertAlterTable((IgniteSqlAlterTable)cmd, pctx);
throw new IgniteSQLException("Unsupported native operation [" +
"cmdName=" + (cmd == null ? null : cmd.getClass().getSimpleName())
+ "; " +
@@ -114,4 +119,14 @@ public class SqlToNativeCommandConverter {
return new SqlDropIndexCommand(schemaName, idxName, sqlCmd.ifExists());
}
+
+ /**
+ * Converts ALTER TABLE ... LOGGING/NOLOGGING command.
+ */
+ private static SqlAlterTableCommand convertAlterTable(IgniteSqlAlterTable
sqlCmd, PlanningContext ctx) {
+ String schemaName = deriveSchemaName(sqlCmd.name(), ctx);
+ String tblName = deriveObjectName(sqlCmd.name(), ctx, "table name");
+
+ return new SqlAlterTableCommand(schemaName, tblName,
sqlCmd.ifExists(), sqlCmd.logging());
+ }
}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteAbstractSqlAlterTable.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteAbstractSqlAlterTable.java
new file mode 100644
index 0000000..630e671
--- /dev/null
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteAbstractSqlAlterTable.java
@@ -0,0 +1,84 @@
+/*
+ * 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.Objects;
+import org.apache.calcite.sql.SqlDdl;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+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;
+
+/**
+ * Parse tree for {@code ALTER TABLE } statement
+ */
+public abstract class IgniteAbstractSqlAlterTable extends SqlDdl {
+ /** */
+ protected final SqlIdentifier name;
+
+ /** */
+ protected final boolean ifExists;
+
+ /** */
+ private static final SqlOperator OPERATOR =
+ new SqlSpecialOperator("ALTER TABLE", SqlKind.ALTER_TABLE);
+
+ /** */
+ protected IgniteAbstractSqlAlterTable(SqlParserPos pos, boolean ifExists,
SqlIdentifier tblName) {
+ super(OPERATOR, pos);
+ this.ifExists = ifExists;
+ name = Objects.requireNonNull(tblName, "table name");
+ }
+
+ /** {@inheritDoc} */
+ @Override public SqlOperator getOperator() {
+ return OPERATOR;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void unparse(SqlWriter writer, int leftPrec, int
rightPrec) {
+ writer.keyword(getOperator().getName());
+
+ if (ifExists)
+ writer.keyword("IF EXISTS");
+
+ name.unparse(writer, leftPrec, rightPrec);
+
+ unparseAlterTableOperation(writer, leftPrec, rightPrec);
+ }
+
+ /**
+ * Unparse rest of the ALTER TABLE command.
+ */
+ protected abstract void unparseAlterTableOperation(SqlWriter writer, int
leftPrec, int 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/sql/IgniteSqlAlterTable.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlAlterTable.java
new file mode 100644
index 0000000..67e7031
--- /dev/null
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlAlterTable.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ignite.internal.processors.query.calcite.sql;
+
+import java.util.List;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.util.ImmutableNullableList;
+
+/**
+ * Parse tree for {@code ALTER TABLE ... LOGGING/NOLOGGING} statement
+ */
+public class IgniteSqlAlterTable extends IgniteAbstractSqlAlterTable {
+ /** */
+ private final boolean logging;
+
+ /** */
+ protected IgniteSqlAlterTable(SqlParserPos pos, boolean ifExists,
SqlIdentifier tblName, boolean logging) {
+ super(pos, ifExists, tblName);
+ this.logging = logging;
+ }
+
+ /** {@inheritDoc} */
+ @Override public List<SqlNode> getOperandList() {
+ return ImmutableNullableList.of(name);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void unparseAlterTableOperation(SqlWriter writer, int
leftPrec, int rightPrec) {
+ writer.keyword(logging ? "LOGGING" : "NOLOGGING");
+ }
+
+ /**
+ * @return If LOGGING or NOLOGGING clause specified.
+ */
+ public boolean logging() {
+ return logging;
+ }
+}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlAlterTableAddColumn.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlAlterTableAddColumn.java
new file mode 100644
index 0000000..0687bbb
--- /dev/null
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlAlterTableAddColumn.java
@@ -0,0 +1,75 @@
+/*
+ * 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.SqlIdentifier;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlNodeList;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.util.ImmutableNullableList;
+
+/**
+ * Parse tree for {@code ALTER TABLE ... ADD COLUMN} statement
+ */
+public class IgniteSqlAlterTableAddColumn extends IgniteAbstractSqlAlterTable {
+ /** */
+ private final boolean ifNotExistsColumn;
+
+ /** */
+ private final SqlNodeList columns;
+
+ /** */
+ protected IgniteSqlAlterTableAddColumn(SqlParserPos pos, boolean ifExists,
SqlIdentifier tblName,
+ boolean ifNotExistsColumn, SqlNodeList columns) {
+ super(pos, ifExists, tblName);
+ this.ifNotExistsColumn = ifNotExistsColumn;
+ this.columns = Objects.requireNonNull(columns, "columns list");
+ }
+
+ /** {@inheritDoc} */
+ @Override public List<SqlNode> getOperandList() {
+ return ImmutableNullableList.of(name, columns);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void unparseAlterTableOperation(SqlWriter writer, int
leftPrec, int rightPrec) {
+ writer.keyword("ADD");
+ writer.keyword("COLUMN");
+
+ if (ifNotExistsColumn)
+ writer.keyword("IF NOT EXISTS");
+
+ columns.unparse(writer, leftPrec, rightPrec);
+ }
+
+ /**
+ * @return Whether the IF NOT EXISTS is specified for columns.
+ */
+ public boolean ifNotExistsColumn() {
+ return ifNotExistsColumn;
+ }
+
+ /**
+ * @return Columns list.
+ */
+ public SqlNodeList columns() {
+ return columns;
+ }
+}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlAlterTableDropColumn.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlAlterTableDropColumn.java
new file mode 100644
index 0000000..e7a1b7e
--- /dev/null
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlAlterTableDropColumn.java
@@ -0,0 +1,75 @@
+/*
+ * 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.SqlIdentifier;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlNodeList;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.util.ImmutableNullableList;
+
+/**
+ * Parse tree for {@code ALTER TABLE ... DROP COLUMN} statement
+ */
+public class IgniteSqlAlterTableDropColumn extends IgniteAbstractSqlAlterTable
{
+ /** */
+ private final boolean ifExistsColumn;
+
+ /** */
+ private final SqlNodeList columns;
+
+ /** */
+ protected IgniteSqlAlterTableDropColumn(SqlParserPos pos, boolean
ifExists, SqlIdentifier tblName,
+ boolean ifExistsColumn, SqlNodeList columns) {
+ super(pos, ifExists, tblName);
+ this.ifExistsColumn = ifExistsColumn;
+ this.columns = Objects.requireNonNull(columns, "columns list");
+ }
+
+ /** {@inheritDoc} */
+ @Override public List<SqlNode> getOperandList() {
+ return ImmutableNullableList.of(name, columns);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void unparseAlterTableOperation(SqlWriter writer, int
leftPrec, int rightPrec) {
+ writer.keyword("DROP");
+ writer.keyword("COLUMN");
+
+ if (ifExistsColumn)
+ writer.keyword("IF EXISTS");
+
+ columns.unparse(writer, leftPrec, rightPrec);
+ }
+
+ /**
+ * @return Whether the IF EXISTS is specified for columns.
+ */
+ public boolean ifExistsColumn() {
+ return ifExistsColumn;
+ }
+
+ /**
+ * @return Columns list.
+ */
+ public SqlNodeList columns() {
+ return columns;
+ }
+}
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
index 1bd3d35..f851b67 100644
---
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
@@ -19,6 +19,7 @@ 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.cluster.ClusterState;
import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
@@ -39,6 +40,9 @@ public class AbstractDdlIntegrationTest extends
GridCommonAbstractTest {
protected static final String DATA_REGION_NAME = "test_data_region";
/** */
+ protected static final String PERSISTENT_DATA_REGION = "pds_data_region";
+
+ /** */
protected IgniteEx client;
/** {@inheritDoc} */
@@ -46,6 +50,8 @@ public class AbstractDdlIntegrationTest extends
GridCommonAbstractTest {
startGrids(1);
client = startClientGrid(CLIENT_NODE_NAME);
+
+ client.cluster().state(ClusterState.ACTIVE);
}
/** {@inheritDoc} */
@@ -56,7 +62,8 @@ public class AbstractDdlIntegrationTest extends
GridCommonAbstractTest {
)
.setDataStorageConfiguration(
new DataStorageConfiguration()
- .setDataRegionConfigurations(new
DataRegionConfiguration().setName(DATA_REGION_NAME))
+ .setDataRegionConfigurations(new
DataRegionConfiguration().setName(DATA_REGION_NAME),
+ new
DataRegionConfiguration().setName(PERSISTENT_DATA_REGION).setPersistenceEnabled(true))
);
}
@@ -74,7 +81,12 @@ public class AbstractDdlIntegrationTest extends
GridCommonAbstractTest {
/** */
protected List<List<?>> executeSql(String sql) {
- List<FieldsQueryCursor<List<?>>> cur = queryProcessor().query(null,
"PUBLIC", sql);
+ return executeSql(client, sql);
+ }
+
+ /** */
+ protected List<List<?>> executeSql(IgniteEx ignite, String sql) {
+ List<FieldsQueryCursor<List<?>>> cur =
queryProcessor(ignite).query(null, "PUBLIC", sql);
try (QueryCursor<List<?>> srvCursor = cur.get(0)) {
return srvCursor.getAll();
@@ -82,7 +94,7 @@ public class AbstractDdlIntegrationTest extends
GridCommonAbstractTest {
}
/** */
- private CalciteQueryProcessor queryProcessor() {
- return Commons.lookupComponent(client.context(),
CalciteQueryProcessor.class);
+ private CalciteQueryProcessor queryProcessor(IgniteEx ignite) {
+ return Commons.lookupComponent(ignite.context(),
CalciteQueryProcessor.class);
}
}
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 771e485..9fb0c2c 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
@@ -43,6 +43,7 @@ import org.hamcrest.Matcher;
import org.junit.Test;
import static org.apache.ignite.internal.util.IgniteUtils.map;
+import static
org.apache.ignite.testframework.GridTestUtils.assertThrowsAnyCause;
import static org.apache.ignite.testframework.GridTestUtils.hasSize;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.hasItem;
@@ -337,18 +338,336 @@ public class TableDdlIntegrationTest extends
AbstractDdlIntegrationTest {
GridTestUtils.assertThrows(log,
() -> executeSql("drop table my_table"),
- IgniteSQLException.class, "Table doesn't exist: MY_TABLE]");
+ IgniteSQLException.class, "Table doesn't exist: MY_TABLE");
executeSql("drop table my_schema.my_table");
GridTestUtils.assertThrows(log,
() -> executeSql("drop table my_schema.my_table"),
- IgniteSQLException.class, "Table doesn't exist: MY_TABLE]");
+ IgniteSQLException.class, "Table doesn't exist: MY_TABLE");
executeSql("drop table if exists my_schema.my_table");
}
/**
+ * Add/drop column simple case.
+ */
+ @Test
+ public void alterTableAddDropSimpleCase() {
+ executeSql("create table my_table (id int primary key, val varchar)");
+
+ executeSql("alter table my_table add column val2 varchar");
+
+ executeSql("insert into my_table (id, val, val2) values (0, '1',
'2')");
+
+ List<List<?>> res = executeSql("select * from my_table ");
+
+ assertEquals(1, res.size());
+ assertEquals("2", res.get(0).get(2));
+
+ executeSql("alter table my_table drop column val2");
+
+ res = executeSql("select * from my_table ");
+
+ assertEquals(1, res.size());
+ assertEquals(2, res.get(0).size());
+ }
+
+ /**
+ * Add/drop two columns at the same time.
+ */
+ @Test
+ public void alterTableAddDropTwoColumns() {
+ executeSql("create table my_table (id int primary key, val varchar)");
+
+ executeSql("alter table my_table add column (val2 varchar, val3 int)");
+
+ executeSql("insert into my_table (id, val, val2, val3) values (0, '1',
'2', 3)");
+
+ List<List<?>> res = executeSql("select * from my_table ");
+
+ assertEquals(1, res.size());
+ assertEquals("2", res.get(0).get(2));
+ assertEquals(3, res.get(0).get(3));
+
+ executeSql("alter table my_table drop column (val2, val3)");
+
+ res = executeSql("select * from my_table ");
+
+ assertEquals(1, res.size());
+ assertEquals(2, res.get(0).size());
+ }
+
+ /**
+ * Add/drop column for table in custom schema.
+ */
+ @Test
+ public void alterTableAddDropCustomSchema() {
+ executeSql("create table my_schema.my_table (id int primary key, val
varchar)");
+
+ executeSql("alter table my_schema.my_table add column val2 varchar");
+
+ executeSql("insert into my_schema.my_table (id, val, val2) values (0,
'1', '2')");
+
+ List<List<?>> res = executeSql("select * from my_schema.my_table ");
+
+ assertEquals(1, res.size());
+ assertEquals("2", res.get(0).get(2));
+
+ executeSql("alter table my_schema.my_table drop column val2");
+
+ res = executeSql("select * from my_schema.my_table ");
+
+ assertEquals(1, res.size());
+ assertEquals(2, res.get(0).size());
+ }
+
+ /**
+ * Add/drop column if table exists.
+ */
+ @Test
+ public void alterTableAddDropIfTableExists() {
+ assertThrows("alter table my_table add val2 varchar",
IgniteSQLException.class, "Table doesn't exist");
+
+ executeSql("alter table if exists my_table add column val2 varchar");
+
+ assertThrows("alter table my_table drop column val2",
IgniteSQLException.class, "Table doesn't exist");
+
+ executeSql("alter table if exists my_table drop column val2");
+
+ executeSql("create table my_table (id int primary key, val varchar)");
+
+ executeSql("alter table if exists my_table add column val3 varchar");
+
+ executeSql("insert into my_table (id, val, val3) values (0, '1',
'2')");
+
+ List<List<?>> res = executeSql("select * from my_table ");
+
+ assertEquals(1, res.size());
+ assertEquals("2", res.get(0).get(2));
+
+ executeSql("alter table if exists my_table drop column val3");
+
+ assertThrows("alter table if exists my_table drop column val3",
IgniteSQLException.class,
+ "Column doesn't exist");
+
+ res = executeSql("select * from my_table ");
+
+ assertEquals(1, res.size());
+ assertEquals(2, res.get(0).size());
+ }
+
+ /**
+ * Add/drop column if column not exists/exists.
+ */
+ @Test
+ public void alterTableAddDropIfColumnExists() {
+ executeSql("create table my_table (id int primary key, val varchar)");
+
+ executeSql("insert into my_table (id, val) values (0, '1')");
+
+ assertThrows("alter table my_table add column val varchar",
IgniteSQLException.class,
+ "Column already exist");
+
+ executeSql("alter table my_table add column if not exists val
varchar");
+
+ List<List<?>> res = executeSql("select * from my_table ");
+ assertEquals(1, res.size());
+ assertEquals(2, res.get(0).size());
+
+ assertThrows("alter table my_table drop column val2",
IgniteSQLException.class,
+ "Column doesn't exist");
+
+ executeSql("alter table my_table drop column if exists val2");
+
+ res = executeSql("select * from my_table ");
+ assertEquals(1, res.size());
+ assertEquals(2, res.get(0).size());
+
+ executeSql("alter table my_table add column if not exists val3
varchar");
+
+ res = executeSql("select * from my_table ");
+ assertEquals(1, res.size());
+ assertEquals(3, res.get(0).size());
+
+ executeSql("alter table my_table drop column if exists val3");
+
+ res = executeSql("select * from my_table ");
+ assertEquals(1, res.size());
+ assertEquals(2, res.get(0).size());
+
+ // Mixing existsing and not existsing columns
+ executeSql("alter table my_table add column if not exists (val
varchar, val4 varchar)");
+
+ res = executeSql("select * from my_table ");
+ assertEquals(1, res.size());
+ assertEquals(3, res.get(0).size());
+
+ executeSql("alter table my_table drop column if exists (val4, val5)");
+
+ res = executeSql("select * from my_table ");
+ assertEquals(1, res.size());
+ assertEquals(2, res.get(0).size());
+ }
+
+ /**
+ * Add not null column.
+ */
+ @Test
+ public void alterTableAddNotNullColumn() {
+ executeSql("create table my_table (id int primary key, val varchar)");
+
+ executeSql("alter table my_table add column val2 varchar not null");
+
+ assertThrows("insert into my_table (id, val, val2) values (0, '1',
null)", IgniteSQLException.class,
+ "Null value is not allowed");
+
+ executeSql("insert into my_table (id, val, val2) values (0, '1',
'2')");
+
+ List<List<?>> res = executeSql("select * from my_table ");
+
+ assertEquals(1, res.size());
+ assertEquals("2", res.get(0).get(2));
+ }
+
+ /**
+ * Drop forbidden column.
+ */
+ @Test
+ public void alterTableDropForbiddenColumn() {
+ executeSql("create table my_table (id int primary key, val varchar,
val2 varchar)");
+
+ executeSql("create index my_index on my_table(val)");
+
+ executeSql("insert into my_table (id, val, val2) values (0, '1',
'2')");
+
+ assertThrows("alter table my_table drop column id",
IgniteSQLException.class,
+ "Cannot drop column");
+
+ assertThrows("alter table my_table drop column val",
IgniteSQLException.class,
+ "Cannot drop column");
+
+ List<List<?>> res = executeSql("select * from my_table ");
+
+ assertEquals(1, res.size());
+ assertEquals(3, res.get(0).size());
+
+ executeSql("alter table my_table drop column val2");
+
+ res = executeSql("select * from my_table ");
+
+ assertEquals(1, res.size());
+ assertEquals(2, res.get(0).size());
+ }
+
+ /**
+ * Alter table from server and client nodes.
+ */
+ @Test
+ public void alterTableServerAndClient() throws Exception {
+ executeSql(grid(0), "create table my_table (id int primary key, val
varchar)");
+
+ executeSql(grid(0), "alter table my_table add column val2 varchar");
+
+ executeSql(grid(0), "insert into my_table (id, val, val2) values (0,
'1', '2')");
+
+ List<List<?>> res = executeSql(grid(0), "select * from my_table ");
+
+ assertEquals(1, res.size());
+ assertEquals(3, res.get(0).size());
+
+ executeSql(grid(0), "drop table my_table");
+
+ awaitPartitionMapExchange();
+
+ executeSql("create table my_table (id int primary key, val varchar)");
+
+ executeSql("alter table my_table add column val2 varchar");
+
+ executeSql("insert into my_table (id, val, val2) values (0, '1',
'2')");
+
+ res = executeSql("select * from my_table ");
+
+ assertEquals(1, res.size());
+ assertEquals(3, res.get(0).size());
+
+ awaitPartitionMapExchange();
+
+ executeSql(grid(0), "alter table my_table drop column val2");
+
+ awaitPartitionMapExchange();
+
+ res = executeSql("select * from my_table ");
+
+ assertEquals(1, res.size());
+ assertEquals(2, res.get(0).size());
+ }
+
+ /**
+ * Drop and add the same column with different NOT NULL modificator.
+ */
+ @Test
+ public void alterTableDropAddColumn() {
+ executeSql("create table my_table (id int primary key, val varchar,
val2 varchar)");
+
+ executeSql("insert into my_table (id, val, val2) values (0, '1',
'2')");
+
+ executeSql("alter table my_table drop column val2");
+
+ List<List<?>> res = executeSql("select * from my_table ");
+
+ assertEquals(1, res.size());
+ assertEquals(2, res.get(0).size());
+
+ executeSql("alter table my_table add column val2 varchar not null");
+
+ res = executeSql("select * from my_table ");
+ assertEquals(1, res.size());
+ assertEquals(3, res.get(0).size());
+ // The command DROP COLUMN does not remove actual data from the
cluster, it's a known and documented limitation.
+ assertEquals("2", res.get(0).get(2));
+
+ assertThrows("insert into my_table (id, val, val2) values (1, '2',
null)", IgniteSQLException.class,
+ "Null value is not allowed");
+
+ executeSql("insert into my_table (id, val, val2) values (1, '2',
'3')");
+
+ assertEquals(2, executeSql("select * from my_table").size());
+ }
+
+ /**
+ * Alter table logging/nologing.
+ */
+ @Test
+ public void alterTableLogging() {
+ String cacheName = "cache";
+
+ IgniteCache<Object, Object> cache = client.getOrCreateCache(new
CacheConfiguration<>(cacheName)
+
.setDataRegionName(PERSISTENT_DATA_REGION).setIndexedTypes(Integer.class,
Integer.class));
+
+ assertTrue(client.cluster().isWalEnabled(cacheName));
+
+ executeSql("alter table \"" + cacheName + "\".Integer nologging");
+
+ assertFalse(client.cluster().isWalEnabled(cacheName));
+
+ executeSql("alter table \"" + cacheName + "\".Integer logging");
+
+ assertTrue(client.cluster().isWalEnabled(cacheName));
+ }
+
+ /**
+ * Asserts that executeSql throws an exception.
+ *
+ * @param sql Query.
+ * @param cls Exception class.
+ * @param msg Error message.
+ */
+ private void assertThrows(String sql, Class<? extends Exception> cls,
String msg) {
+ assertThrowsAnyCause(log, () -> executeSql(sql), cls, msg);
+ }
+
+ /**
* Matcher to verify that an object of the expected type and matches the
given predicat.
*
* @param desc Description for this matcher.
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 61a1767..7e4843e 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
@@ -247,7 +247,7 @@ public class SqlDdlParserTest extends
GridCommonAbstractTest {
public void createIndexColumns() throws SqlParseException {
String qry = "create index my_index on my_table(id, val1 asc, val2
desc)";
- IgniteSqlCreateIndex createIdx = (IgniteSqlCreateIndex) parse(qry);
+ IgniteSqlCreateIndex createIdx = parse(qry);
assertThat(createIdx.indexName().names,
is(ImmutableList.of("MY_INDEX")));
assertThat(createIdx.tableName().names,
is(ImmutableList.of("MY_TABLE")));
@@ -264,7 +264,7 @@ public class SqlDdlParserTest extends
GridCommonAbstractTest {
public void createIndexOnTableWithSchema() throws SqlParseException {
String qry = "create index my_index on my_schema.my_table(id)";
- IgniteSqlCreateIndex createIdx = (IgniteSqlCreateIndex)parse(qry);
+ IgniteSqlCreateIndex createIdx = parse(qry);
assertThat(createIdx.indexName().names,
is(ImmutableList.of("MY_INDEX")));
assertThat(createIdx.tableName().names,
is(ImmutableList.of("MY_SCHEMA", "MY_TABLE")));
@@ -277,7 +277,7 @@ public class SqlDdlParserTest extends
GridCommonAbstractTest {
public void createIndexIfNotExists() throws SqlParseException {
String qry = "create index if not exists my_index on my_table(id)";
- IgniteSqlCreateIndex createIdx = (IgniteSqlCreateIndex)parse(qry);
+ IgniteSqlCreateIndex createIdx = parse(qry);
assertThat(createIdx.indexName().names,
is(ImmutableList.of("MY_INDEX")));
assertThat(createIdx.tableName().names,
is(ImmutableList.of("MY_TABLE")));
@@ -291,7 +291,7 @@ public class SqlDdlParserTest extends
GridCommonAbstractTest {
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);
+ IgniteSqlCreateIndex createIdx = parse(qry);
assertThat(createIdx.indexName().names,
is(ImmutableList.of("MY_INDEX")));
assertThat(createIdx.tableName().names,
is(ImmutableList.of("MY_TABLE")));
@@ -300,21 +300,21 @@ public class SqlDdlParserTest extends
GridCommonAbstractTest {
qry = "create index my_index on my_table(id) parallel 10";
- createIdx = (IgniteSqlCreateIndex)parse(qry);
+ createIdx = 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);
+ createIdx = 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);
+ createIdx = parse(qry);
assertEquals(20, createIdx.inlineSize().intValue(true));
assertEquals(10, createIdx.parallel().intValue(true));
@@ -345,6 +345,171 @@ public class SqlDdlParserTest extends
GridCommonAbstractTest {
assertParserThrows("create index my_index on my_table(id.id2)",
SqlParseException.class);
}
+ /**
+ * Alter table with LOGGING/NOLOGING clause.
+ */
+ @Test
+ public void alterTableLoggingNologging() throws SqlParseException {
+ IgniteSqlAlterTable alterTbl = parse("alter table my_table logging");
+
+ assertThat(alterTbl.name().names, is(ImmutableList.of("MY_TABLE")));
+ assertEquals(false, alterTbl.ifExists());
+ assertEquals(true, alterTbl.logging());
+
+ alterTbl = parse("alter table if exists my_table nologging");
+
+ assertThat(alterTbl.name().names, is(ImmutableList.of("MY_TABLE")));
+ assertEquals(true, alterTbl.ifExists());
+ assertEquals(false, alterTbl.logging());
+ }
+
+ /**
+ * Alter table with schema.
+ */
+ @Test
+ public void alterTableWithSchema() throws SqlParseException {
+ IgniteSqlAlterTable alterTbl1 = parse("alter table my_schema.my_table
logging");
+ assertThat(alterTbl1.name().names, is(ImmutableList.of("MY_SCHEMA",
"MY_TABLE")));
+
+ IgniteSqlAlterTableAddColumn alterTbl2 = parse("alter table
my_schema.my_table add column a int");
+
+ assertThat(alterTbl2.name().names, is(ImmutableList.of("MY_SCHEMA",
"MY_TABLE")));
+
+ IgniteSqlAlterTableDropColumn alterTbl3 = parse("alter table
my_schema.my_table drop column a");
+
+ assertThat(alterTbl3.name().names, is(ImmutableList.of("MY_SCHEMA",
"MY_TABLE")));
+ }
+
+ /**
+ * Alter table add column.
+ */
+ @Test
+ public void alterTableAddColumn() throws SqlParseException {
+ IgniteSqlAlterTableAddColumn alterTbl;
+
+ alterTbl = parse("alter table my_table add column a int");
+
+ assertThat(alterTbl.name().names, is(ImmutableList.of("MY_TABLE")));
+ assertEquals(false, alterTbl.ifNotExistsColumn());
+ assertEquals(1, alterTbl.columns().size());
+ assertThat(alterTbl.columns(), hasItem(columnWithName("A")));
+
+ alterTbl = parse("alter table my_table add column if not exists a
int");
+
+ assertEquals(true, alterTbl.ifNotExistsColumn());
+ assertEquals(1, alterTbl.columns().size());
+ assertThat(alterTbl.columns(), hasItem(columnWithName("A")));
+
+ alterTbl = parse("alter table my_table add a int");
+
+ assertEquals(false, alterTbl.ifNotExistsColumn());
+ assertEquals(1, alterTbl.columns().size());
+ assertThat(alterTbl.columns(), hasItem(columnWithName("A")));
+
+ alterTbl = parse("alter table my_table add if not exists a int");
+
+ assertEquals(true, alterTbl.ifNotExistsColumn());
+ assertEquals(1, alterTbl.columns().size());
+ assertThat(alterTbl.columns(), hasItem(columnWithName("A")));
+
+ alterTbl = parse("alter table my_table add column (a int)");
+
+ assertEquals(false, alterTbl.ifNotExistsColumn());
+ assertEquals(1, alterTbl.columns().size());
+ assertThat(alterTbl.columns(), hasItem(columnWithName("A")));
+
+ alterTbl = parse("alter table my_table add column if not exists (a
int)");
+
+ assertEquals(true, alterTbl.ifNotExistsColumn());
+ assertEquals(1, alterTbl.columns().size());
+ assertThat(alterTbl.columns(), hasItem(columnWithName("A")));
+
+ alterTbl = parse("alter table my_table add column (a int, \"b\"
varchar, c date not null)");
+
+ assertEquals(false, alterTbl.ifNotExistsColumn());
+ assertEquals(3, alterTbl.columns().size());
+ assertThat(alterTbl.columns(), hasItem(columnDeclaration("A",
"INTEGER", true)));
+ assertThat(alterTbl.columns(), hasItem(columnDeclaration("b",
"VARCHAR", true)));
+ assertThat(alterTbl.columns(), hasItem(columnDeclaration("C", "DATE",
false)));
+ }
+
+ /**
+ * Alter table drop column.
+ */
+ @Test
+ public void alterTableDropColumn() throws SqlParseException {
+ IgniteSqlAlterTableDropColumn alterTbl;
+
+ alterTbl = parse("alter table my_table drop column a");
+
+ assertThat(alterTbl.name().names, is(ImmutableList.of("MY_TABLE")));
+ assertEquals(false, alterTbl.ifExistsColumn());
+ assertEquals(1, alterTbl.columns().size());
+ assertThat(alterTbl.columns(), hasItem(identifierWithName("A")));
+
+ alterTbl = parse("alter table my_table drop column if exists a");
+
+ assertEquals(true, alterTbl.ifExistsColumn());
+ assertEquals(1, alterTbl.columns().size());
+ assertThat(alterTbl.columns(), hasItem(identifierWithName("A")));
+
+ alterTbl = parse("alter table my_table drop a");
+
+ assertEquals(false, alterTbl.ifExistsColumn());
+ assertEquals(1, alterTbl.columns().size());
+ assertThat(alterTbl.columns(), hasItem(identifierWithName("A")));
+
+ alterTbl = parse("alter table my_table drop if exists a");
+
+ assertEquals(true, alterTbl.ifExistsColumn());
+ assertEquals(1, alterTbl.columns().size());
+ assertThat(alterTbl.columns(), hasItem(identifierWithName("A")));
+
+ alterTbl = parse("alter table my_table drop column (a)");
+
+ assertEquals(false, alterTbl.ifExistsColumn());
+ assertEquals(1, alterTbl.columns().size());
+ assertThat(alterTbl.columns(), hasItem(identifierWithName("A")));
+
+ alterTbl = parse("alter table my_table drop column if exists (a)");
+
+ assertEquals(true, alterTbl.ifExistsColumn());
+ assertEquals(1, alterTbl.columns().size());
+ assertThat(alterTbl.columns(), hasItem(identifierWithName("A")));
+
+ alterTbl = parse("alter table my_table drop column (a, \"b\", c)");
+
+ assertEquals(false, alterTbl.ifExistsColumn());
+ assertEquals(3, alterTbl.columns().size());
+ assertThat(alterTbl.columns(), hasItem(identifierWithName("A")));
+ assertThat(alterTbl.columns(), hasItem(identifierWithName("b")));
+ assertThat(alterTbl.columns(), hasItem(identifierWithName("C")));
+ }
+
+ /**
+ * Malformed alter table statements.
+ */
+ @Test
+ public void alterTableMalformed() {
+ assertParserThrows("alter table my_table logging nologging",
SqlParseException.class);
+
+ assertParserThrows("alter table my_table logging add column a int",
SqlParseException.class);
+
+ assertParserThrows("alter table if not exists my_table logging",
SqlParseException.class);
+
+ assertParserThrows("alter table my_table add column if exists (a
int)", SqlParseException.class);
+
+ assertParserThrows("alter table my_table add column (a)",
SqlParseException.class);
+
+ assertParserThrows("alter table my_table drop column if not exists
(a)", SqlParseException.class);
+
+ assertParserThrows("alter table my_table drop column (a int)",
SqlParseException.class);
+
+ assertParserThrows("alter table my_table add column (a.b int)",
SqlParseException.class);
+
+ assertParserThrows("alter table my_table drop column (a.b)",
SqlParseException.class);
+ }
+
/** */
private void assertParserThrows(String sql, Class<? extends Exception>
cls) {
assertParserThrows(sql, cls, "");
@@ -361,10 +526,10 @@ public class SqlDdlParserTest extends
GridCommonAbstractTest {
* @param stmt Statement to parse.
* @return An AST.
*/
- private static SqlNode parse(String stmt) throws SqlParseException {
+ private static <T extends SqlNode> T parse(String stmt) throws
SqlParseException {
SqlParser parser = SqlParser.create(stmt,
SqlParser.config().withParserFactory(IgniteSqlParserImpl.FACTORY));
- return parser.parseStmt();
+ return (T)parser.parseStmt();
}
/**
@@ -426,6 +591,33 @@ public class SqlDdlParserTest extends
GridCommonAbstractTest {
}
/**
+ * Matcher to verify identifier.
+ */
+ private static <T extends SqlIdentifier> Matcher<T>
identifierWithName(String name) {
+ return new CustomMatcher<T>("identifier with name=" + name) {
+ @Override public boolean matches(Object item) {
+ return item instanceof SqlIdentifier
+ && ((SqlIdentifier)item).names.get(0).equals(name);
+ }
+ };
+ }
+
+ /**
+ * Matcher to verify column declaration.
+ */
+ private static <T extends SqlColumnDeclaration> Matcher<T>
columnDeclaration(String name, String type,
+ boolean nullable) {
+ return new CustomMatcher<T>("column declaration [name=" + name + ",
type=" + type + ']') {
+ @Override public boolean matches(Object item) {
+ return item instanceof SqlColumnDeclaration
+ &&
((SqlColumnDeclaration)item).name.names.get(0).equals(name)
+ &&
((SqlColumnDeclaration)item).dataType.getTypeName().names.get(0).equals(type)
+ && ((SqlColumnDeclaration)item).dataType.getNullable() ==
nullable;
+ }
+ };
+ }
+
+ /**
* Matcher to verify that an object of the expected type and matches the
given predicat.
*
* @param desc Description for this matcher.
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 a3d9fc0..20aa512 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
@@ -31,7 +31,6 @@ import java.util.SortedSet;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
-
import javax.cache.configuration.Factory;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteLogger;
@@ -115,7 +114,6 @@ import static
org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.QRY_CL
import static
org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.QRY_EXEC;
import static
org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.QRY_FETCH;
import static
org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.QRY_META;
-import static
org.apache.ignite.internal.processors.query.GridQueryProcessor.executeWithExperimentalEngine;
/**
* JDBC request handler.
@@ -775,10 +773,8 @@ 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,
cliCtx, cancel), qry.getSchema(),
- qry.getSql(), qry.getArgs());
- }
+ 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 eec8ccb..29ca5da 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
@@ -34,7 +34,6 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.regex.Pattern;
import javax.cache.Cache;
import javax.cache.CacheException;
import org.apache.ignite.IgniteCheckedException;
@@ -142,7 +141,6 @@ import static java.util.Collections.newSetFromMap;
import static java.util.Collections.singleton;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
-import static java.util.regex.Pattern.CASE_INSENSITIVE;
import static java.util.stream.Collectors.toSet;
import static
org.apache.ignite.IgniteSystemProperties.IGNITE_EXPERIMENTAL_SQL_ENGINE;
import static org.apache.ignite.IgniteSystemProperties.getBoolean;
@@ -173,10 +171,6 @@ public class GridQueryProcessor extends
GridProcessorAdapter {
/** */
private static final ThreadLocal<AffinityTopologyVersion> requestTopVer =
new ThreadLocal<>();
- /** 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)", CASE_INSENSITIVE);
-
/** For tests. */
public static Class<? extends GridQueryIndexing> idxCls;
@@ -2871,10 +2865,8 @@ 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,
cliCtx), qry.getSchema(), qry.getSql(),
- X.EMPTY_OBJECT_ARRAY);
- }
+ return experimentalQueryEngine.query(QueryContext.of(qry, cliCtx),
qry.getSchema(), qry.getSql(),
+ X.EMPTY_OBJECT_ARRAY);
}
return executeQuerySafe(cctx, () -> {
@@ -3662,14 +3654,6 @@ public class GridQueryProcessor extends
GridProcessorAdapter {
}
/**
- * @param sql Query to execute.
- * @return {@code true} if the given query should be executed with
Calcite-based engine.
- */
- public static boolean executeWithExperimentalEngine(String sql) {
- return !H2_REDIRECTION_RULES.matcher(sql).find();
- }
-
- /**
* @return Affinity topology version of the current request.
*/
public static AffinityTopologyVersion getRequestAffinityTopologyVersion() {
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlAlterTableCommand.java
b/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlAlterTableCommand.java
index b6f762c..491bdc3 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlAlterTableCommand.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlAlterTableCommand.java
@@ -41,6 +41,24 @@ public class SqlAlterTableCommand implements SqlCommand {
/** Logging flag. */
private Boolean logging;
+ /**
+ * Default constructor.
+ */
+ public SqlAlterTableCommand() {
+ }
+
+ /**
+ * @param schemaName Schema name.
+ * @param tblName Table name.
+ * @param ifExists If exists clause.
+ * @param logging LOGGING/NOLOGGING.
+ */
+ public SqlAlterTableCommand(String schemaName, String tblName, boolean
ifExists, boolean logging) {
+ this.schemaName = schemaName;
+ this.tblName = tblName;
+ this.logging = logging;
+ }
+
/** {@inheritDoc} */
@Override public String schemaName() {
return schemaName;