ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From voze...@apache.org
Subject ignite git commit: IGNITE-4570: DDL parsing.
Date Thu, 02 Feb 2017 08:54:24 GMT
Repository: ignite
Updated Branches:
  refs/heads/ignite-4565 739c606aa -> fcadf67cf


IGNITE-4570: DDL parsing.


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/fcadf67c
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/fcadf67c
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/fcadf67c

Branch: refs/heads/ignite-4565
Commit: fcadf67cf134d603f4b8c9364ab4584cc40efed6
Parents: 739c606
Author: devozerov <vozerov@gridgain.com>
Authored: Thu Feb 2 11:54:10 2017 +0300
Committer: devozerov <vozerov@gridgain.com>
Committed: Thu Feb 2 11:54:10 2017 +0300

----------------------------------------------------------------------
 .../cache/query/IgniteQueryErrorCode.java       |   4 +-
 .../query/h2/DdlStatementsProcessor.java        |  67 ++++++
 .../query/h2/DmlStatementsProcessor.java        |  14 ++
 .../processors/query/h2/IgniteH2Indexing.java   |  24 +-
 .../query/h2/sql/GridCreateIndex.java           | 121 ++++++++++
 .../processors/query/h2/sql/GridDropIndex.java  |  82 +++++++
 .../query/h2/sql/GridSqlQueryParser.java        | 117 ++++++++++
 .../query/h2/sql/GridQueryParsingTest.java      | 218 ++++++++++++++++++-
 8 files changed, 638 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/fcadf67c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java
index 93b8d47..2520bf3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java
@@ -33,7 +33,7 @@ public final class IgniteQueryErrorCode {
     /** General parsing error - for the cases when there's no more specific code available.
*/
     public final static int PARSING = 1001;
 
-    /** Code encountered unexpected type of SQL operation - like {@code EXPLAIN MERGE}. */
+    /** Requested operation is not supported. */
     public final static int UNSUPPORTED_OPERATION = 1002;
 
     /* 2xxx - analysis errors */
@@ -58,7 +58,7 @@ public final class IgniteQueryErrorCode {
     /** Statement type does not match that declared by JDBC driver. */
     public final static int STMT_TYPE_MISMATCH = 3003;
 
-    /** Statement type does not match that declared by JDBC driver. */
+    /** DROP TABLE failed. */
     public final static int TABLE_DROP_FAILED = 3004;
 
     /* 4xxx - cache related runtime errors */

http://git-wip-us.apache.org/repos/asf/ignite/blob/fcadf67c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DdlStatementsProcessor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DdlStatementsProcessor.java
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DdlStatementsProcessor.java
new file mode 100644
index 0000000..462ab01
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DdlStatementsProcessor.java
@@ -0,0 +1,67 @@
+/*
+ * 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.h2;
+
+import java.util.List;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.h2.sql.GridCreateIndex;
+import org.apache.ignite.internal.processors.query.h2.sql.GridDropIndex;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryParser;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlStatement;
+import org.h2.command.Prepared;
+
+/**
+ * Logic to execute DDL statements.
+ */
+class DdlStatementsProcessor {
+    /**
+     * Indexing.
+     */
+    private final IgniteH2Indexing indexing;
+
+    /** */
+    DdlStatementsProcessor(IgniteH2Indexing indexing) {
+        this.indexing = indexing;
+    }
+
+    /**
+     * Execute DDL statement.
+     *
+     * @param cctx Cache context.
+     * @param stmt H2 statement to parse and execute.
+     */
+    QueryCursor<List<?>> runDdlStatement(GridCacheContext<?, ?> cctx, Prepared
stmt) throws IgniteCheckedException {
+        GridSqlStatement gridStmt = new GridSqlQueryParser().parse(stmt);
+
+        if (gridStmt instanceof GridCreateIndex) {
+            QueryIndex newIdx = ((GridCreateIndex) gridStmt).index();
+
+            throw new UnsupportedOperationException("CREATE INDEX");
+        }
+        else if (gridStmt instanceof GridDropIndex)
+            throw new UnsupportedOperationException("DROP INDEX");
+        else
+            throw new IgniteSQLException("Unexpected DDL operation [type=" + gridStmt.getClass()
+ ']',
+                IgniteQueryErrorCode.UNEXPECTED_OPERATION);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/fcadf67c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
index 4030758..72cd51f 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
@@ -73,6 +73,10 @@ import org.apache.ignite.lang.IgniteBiTuple;
 import org.apache.ignite.lang.IgniteInClosure;
 import org.apache.ignite.spi.indexing.IndexingQueryFilter;
 import org.h2.command.Prepared;
+import org.h2.command.dml.Delete;
+import org.h2.command.dml.Insert;
+import org.h2.command.dml.Merge;
+import org.h2.command.dml.Update;
 import org.h2.jdbc.JdbcPreparedStatement;
 import org.h2.table.Column;
 import org.h2.value.DataType;
@@ -1015,6 +1019,16 @@ public class DmlStatementsProcessor {
         return res;
     }
 
+    /**
+     * Check whether statement is DML statement.
+     *
+     * @param stmt Statement.
+     * @return {@code True} if this is DML.
+     */
+    static boolean isDmlStatement(Prepared stmt) {
+        return stmt instanceof Merge || stmt instanceof Insert || stmt instanceof Update
|| stmt instanceof Delete;
+    }
+
     /** Update result - modifications count and keys to re-run query with, if needed. */
     private final static class UpdateResult {
         /** Number of processed items. */

http://git-wip-us.apache.org/repos/asf/ignite/blob/fcadf67c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
----------------------------------------------------------------------
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 505a3af..d2aab0b 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
@@ -335,6 +335,9 @@ public class IgniteH2Indexing implements GridQueryIndexing {
     private final DmlStatementsProcessor dmlProc = new DmlStatementsProcessor(this);
 
     /** */
+    private final DdlStatementsProcessor ddlProc = new DdlStatementsProcessor(this);
+
+    /** */
     private final ConcurrentMap<String, GridH2Table> dataTables = new ConcurrentHashMap8<>();
 
     /** Statement cache. */
@@ -1255,12 +1258,23 @@ public class IgniteH2Indexing implements GridQueryIndexing {
                     IgniteQueryErrorCode.STMT_TYPE_MISMATCH);
 
             if (!prepared.isQuery()) {
-                try {
-                    return dmlProc.updateSqlFieldsTwoStep(cctx.namexx(), stmt, qry, cancel);
+                if (dmlProc.isDmlStatement(prepared)) {
+                    try {
+                        return dmlProc.updateSqlFieldsTwoStep(cctx.namexx(), stmt, qry, cancel);
+                    }
+                    catch (IgniteCheckedException e) {
+                        throw new IgniteSQLException("Failed to execute DML statement [stmt="
+ sqlQry + ", params=" +
+                            Arrays.deepToString(qry.getArgs()) + "]", e);
+                    }
                 }
-                catch (IgniteCheckedException e) {
-                    throw new IgniteSQLException("Failed to execute DML statement [qry="
+ sqlQry + ", params=" +
-                        Arrays.deepToString(qry.getArgs()) + "]", e);
+                else {
+                    try {
+                        return ddlProc.runDdlStatement(cctx, prepared);
+                    }
+                    catch (IgniteCheckedException e) {
+                        throw new IgniteSQLException("Failed to execute DDL statement [stmt="
+ sqlQry + ", params=" +
+                            Arrays.deepToString(qry.getArgs()) + "]", e);
+                    }
                 }
             }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/fcadf67c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridCreateIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridCreateIndex.java
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridCreateIndex.java
new file mode 100644
index 0000000..56c7dd3
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridCreateIndex.java
@@ -0,0 +1,121 @@
+/*
+ * 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.h2.sql;
+
+import java.util.Map;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.QueryIndexType;
+import org.h2.command.Parser;
+
+/**
+ * CREATE INDEX statement.
+ */
+public class GridCreateIndex extends GridSqlStatement {
+    /** Schema name. */
+    private String schemaName;
+
+    /** Table name. */
+    private String tblName;
+
+    /** Attempt to create the index only if it does not exist. */
+    private boolean ifNotExists;
+
+    /** Index to create. */
+    private QueryIndex idx;
+
+    /**
+     * @return Schema name for new index.
+     */
+    public String schemaName() {
+        return schemaName;
+    }
+
+    /**
+     * @param schemaName Schema name for new index.
+     */
+    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 whether attempt to create the index should be made only if it does not exist.
+     */
+    public boolean ifNotExists() {
+        return ifNotExists;
+    }
+
+    /**
+     * @param ifNotExists whether attempt to create the index should be made only if it does
not exist.
+     */
+    public void ifNotExists(boolean ifNotExists) {
+        this.ifNotExists = ifNotExists;
+    }
+
+    /**
+     * @return Index to create.
+     */
+    public QueryIndex index() {
+        return idx;
+    }
+
+    /**
+     * @param idx Index to create.
+     */
+    public void index(QueryIndex idx) {
+        this.idx = idx;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getSQL() {
+        StringBuilder sb = new StringBuilder("CREATE ")
+            .append(idx.getIndexType() == QueryIndexType.GEOSPATIAL ? "SPATIAL " : "")
+            .append("INDEX ").append(ifNotExists ? "IF NOT EXISTS " : "")
+            .append(Parser.quoteIdentifier(schemaName)).append('.')
+            .append(Parser.quoteIdentifier(idx.getName())).append(" ON ")
+            .append(Parser.quoteIdentifier(tblName)).append(" (");
+
+        boolean first = true;
+
+        for (Map.Entry<String, Boolean> e : idx.getFields().entrySet()) {
+            if (first)
+                first = false;
+            else
+                sb.append(", ");
+
+            sb.append(Parser.quoteIdentifier(e.getKey())).append(e.getValue() ? " ASC" :
" DESC");
+        }
+
+        sb.append(')');
+
+        return sb.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/fcadf67c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridDropIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridDropIndex.java
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridDropIndex.java
new file mode 100644
index 0000000..07c39df
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridDropIndex.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.h2.sql;
+
+import org.h2.command.Parser;
+
+/**
+ * DROP INDEX statement.
+ */
+public class GridDropIndex extends GridSqlStatement {
+    /** Index name. */
+    private String name;
+
+    /** Schema name. */
+    private String schemaName;
+
+    /** Attempt to drop the index only if it exists. */
+    private boolean ifExists;
+
+    /**
+     * @return Index name.
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * @param name Index name.
+     */
+    public void name(String name) {
+        this.name = name;
+    }
+
+    /**
+     * @return Schema name.
+     */
+    public String schemaName() {
+        return schemaName;
+    }
+
+    /**
+     * @param schemaName Schema name.
+     */
+    public void schemaName(String schemaName) {
+        this.schemaName = schemaName;
+    }
+
+    /**
+     * @return whether attempt to drop the index should be made only if it exists.
+     */
+    public boolean ifExists() {
+        return ifExists;
+    }
+
+    /**
+     * @param ifExists whether attempt to drop the index should be made only if it exists.
+     */
+    public void ifExists(boolean ifExists) {
+        this.ifExists = ifExists;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getSQL() {
+        return "DROP INDEX " + (ifExists ? "IF EXISTS " : "") + Parser.quoteIdentifier(schemaName)
+ '.' +
+            Parser.quoteIdentifier(name);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/fcadf67c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
index d9c546c..d4761a9 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
@@ -27,11 +27,16 @@ import java.util.List;
 import java.util.Map;
 import javax.cache.CacheException;
 import org.apache.ignite.IgniteException;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.QueryIndexType;
 import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.h2.command.Command;
 import org.h2.command.CommandContainer;
 import org.h2.command.Prepared;
+import org.h2.command.ddl.CreateIndex;
+import org.h2.command.ddl.DropIndex;
+import org.h2.command.ddl.SchemaCommand;
 import org.h2.command.dml.Delete;
 import org.h2.command.dml.Explain;
 import org.h2.command.dml.Insert;
@@ -64,8 +69,10 @@ import org.h2.index.Index;
 import org.h2.index.ViewIndex;
 import org.h2.jdbc.JdbcPreparedStatement;
 import org.h2.result.SortOrder;
+import org.h2.schema.Schema;
 import org.h2.table.Column;
 import org.h2.table.FunctionTable;
+import org.h2.table.IndexColumn;
 import org.h2.table.RangeTable;
 import org.h2.table.Table;
 import org.h2.table.TableBase;
@@ -296,6 +303,48 @@ public class GridSqlQueryParser {
         GridSqlQueryParser.<Command, Prepared>getter(CommandContainer.class, "prepared");
 
     /** */
+    private static final Getter<CreateIndex, String> CREATE_INDEX_NAME = getter(CreateIndex.class,
"indexName");
+
+    /** */
+    private static final Getter<CreateIndex, String> CREATE_INDEX_TABLE_NAME = getter(CreateIndex.class,
"tableName");
+
+    /** */
+    private static final Getter<CreateIndex, IndexColumn[]> CREATE_INDEX_COLUMNS =
getter(CreateIndex.class,
+        "indexColumns");
+
+    /** */
+    private static final Getter<CreateIndex, Boolean> CREATE_INDEX_SPATIAL = getter(CreateIndex.class,
"spatial");
+
+    /** */
+    private static final Getter<CreateIndex, Boolean> CREATE_INDEX_PRIMARY_KEY = getter(CreateIndex.class,
+        "primaryKey");
+
+    /** */
+    private static final Getter<CreateIndex, Boolean> CREATE_INDEX_UNIQUE = getter(CreateIndex.class,
"unique");
+
+    /** */
+    private static final Getter<CreateIndex, Boolean> CREATE_INDEX_HASH = getter(CreateIndex.class,
"hash");
+
+    /** */
+    private static final Getter<CreateIndex, Boolean> CREATE_INDEX_IF_NOT_EXISTS =
getter(CreateIndex.class,
+        "ifNotExists");
+
+    /** */
+    private static final Getter<IndexColumn, String> INDEX_COLUMN_NAME = getter(IndexColumn.class,
"columnName");
+
+    /** */
+    private static final Getter<IndexColumn, Integer> INDEX_COLUMN_SORT_TYPE = getter(IndexColumn.class,
"sortType");
+
+    /** */
+    private static final Getter<DropIndex, String> DROP_INDEX_NAME = getter(DropIndex.class,
"indexName");
+
+    /** */
+    private static final Getter<DropIndex, Boolean> DROP_INDEX_IF_EXISTS = getter(DropIndex.class,
"ifExists");
+
+    /** */
+    private static final Getter<SchemaCommand, Schema> SCHEMA_COMMAND_SCHEMA = getter(SchemaCommand.class,
"schema");
+
+    /** */
     private final IdentityHashMap<Object, Object> h2ObjToGridObj = new IdentityHashMap<>();
 
     /**
@@ -594,6 +643,68 @@ public class GridSqlQueryParser {
         return res;
     }
 
+
+
+    /**
+     * Parse {@code DROP INDEX} statement.
+     *
+     * @param dropIdx {@code DROP INDEX} statement.
+     * @see <a href="http://h2database.com/html/grammar.html#drop_index">H2 {@code
DROP INDEX} spec.</a>
+     */
+    private GridDropIndex parseDropIndex(DropIndex dropIdx) {
+        GridDropIndex res = new GridDropIndex();
+
+        res.name(DROP_INDEX_NAME.get(dropIdx));
+        res.schemaName(SCHEMA_COMMAND_SCHEMA.get(dropIdx).getName());
+        res.ifExists(DROP_INDEX_IF_EXISTS.get(dropIdx));
+
+        return res;
+    }
+
+    /**
+     * Parse {@code CREATE INDEX} statement.
+     *
+     * @param createIdx {@code CREATE INDEX} statement.
+     * @see <a href="http://h2database.com/html/grammar.html#create_index">H2 {@code
CREATE INDEX} spec.</a>
+     */
+    private GridCreateIndex parseCreateIndex(CreateIndex createIdx) {
+        if (CREATE_INDEX_HASH.get(createIdx) || CREATE_INDEX_PRIMARY_KEY.get(createIdx) ||
+            CREATE_INDEX_UNIQUE.get(createIdx))
+            throw new IgniteSQLException("Only SPATIAL modifier is supported for CREATE INDEX",
+                IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
+        GridCreateIndex res = new GridCreateIndex();
+
+        res.schemaName(SCHEMA_COMMAND_SCHEMA.get(createIdx).getName());
+        res.tableName(CREATE_INDEX_TABLE_NAME.get(createIdx));
+        res.ifNotExists(CREATE_INDEX_IF_NOT_EXISTS.get(createIdx));
+
+        QueryIndex idx = new QueryIndex();
+
+        idx.setName(CREATE_INDEX_NAME.get(createIdx));
+        idx.setIndexType(CREATE_INDEX_SPATIAL.get(createIdx) ? QueryIndexType.GEOSPATIAL
: QueryIndexType.SORTED);
+
+        IndexColumn[] cols = CREATE_INDEX_COLUMNS.get(createIdx);
+
+        LinkedHashMap<String, Boolean> flds = new LinkedHashMap<>(cols.length);
+
+        for (IndexColumn col : CREATE_INDEX_COLUMNS.get(createIdx)) {
+            int sortType = INDEX_COLUMN_SORT_TYPE.get(col);
+
+            if ((sortType & SortOrder.NULLS_FIRST) != 0 || (sortType & SortOrder.NULLS_LAST)
!= 0)
+                throw new IgniteSQLException("NULLS FIRST and NULLS LAST modifiers are not
supported for index columns",
+                    IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
+            flds.put(INDEX_COLUMN_NAME.get(col), (sortType & SortOrder.DESCENDING) ==
0);
+        }
+
+        idx.setFields(flds);
+
+        res.index(idx);
+
+        return res;
+    }
+
     /**
      * @param sortOrder Sort order.
      * @param qry Query.
@@ -659,6 +770,12 @@ public class GridSqlQueryParser {
         if (qry instanceof Update)
             return parseUpdate((Update)qry);
 
+        if (qry instanceof CreateIndex)
+            return parseCreateIndex((CreateIndex) qry);
+
+        if (qry instanceof DropIndex)
+            return parseDropIndex((DropIndex) qry);
+
         if (qry instanceof Explain) {
             GridSqlStatement stmt = parse(EXPLAIN_COMMAND.get((Explain) qry));
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/fcadf67c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
index b909b36..1f2c364 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
@@ -22,9 +22,15 @@ import java.sql.Connection;
 import java.sql.Date;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.cache.CacheAtomicityMode;
 import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.QueryIndexType;
 import org.apache.ignite.cache.query.annotations.QuerySqlField;
 import org.apache.ignite.cache.query.annotations.QuerySqlFunction;
 import org.apache.ignite.configuration.CacheConfiguration;
@@ -32,15 +38,19 @@ import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.processors.query.GridQueryProcessor;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
+import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.h2.command.Prepared;
 import org.h2.engine.Session;
 import org.h2.jdbc.JdbcConnection;
+import org.h2.message.DbException;
 
 import static org.apache.ignite.cache.CacheRebalanceMode.SYNC;
 import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
@@ -81,7 +91,11 @@ public class GridQueryParsingTest extends GridCommonAbstractTest {
             String.class, Person.class
         );
 
-        c.setCacheConfiguration(cc);
+        CacheConfiguration cc2 = new CacheConfiguration(cc);
+
+        cc2.setName("schema2");
+
+        c.setCacheConfiguration(cc, cc2);
 
         return c;
     }
@@ -367,6 +381,206 @@ public class GridQueryParsingTest extends GridCommonAbstractTest {
     /**
      *
      */
+    public void testParseCreateIndex() throws Exception {
+        assertCreateIndexEquals(
+            buildCreateIndex(null, "Person", "", false, QueryIndexType.SORTED, "name", true),
+            "create index on Person (name)");
+
+        assertCreateIndexEquals(
+            buildCreateIndex("idx", "Person", "", false, QueryIndexType.SORTED, "name", true),
+            "create index idx on Person (name ASC)");
+
+        assertCreateIndexEquals(
+            buildCreateIndex("idx", "Person", "schema2", false, QueryIndexType.GEOSPATIAL,
"name", true),
+            "create spatial index \"schema2\".idx on \"schema2\".Person (name ASC)");
+
+        assertCreateIndexEquals(
+            buildCreateIndex("idx", "Person", "schema2", true, QueryIndexType.SORTED, "name",
true),
+            "create index if not exists \"schema2\".idx on \"schema2\".Person (name)");
+
+        // When we specify schema for the table and don't specify it for the index, resulting
schema is table's
+        assertCreateIndexEquals(
+            buildCreateIndex("idx", "Person", "schema2", true, QueryIndexType.SORTED, "name",
false),
+            "create index if not exists idx on \"schema2\".Person (name dEsC)");
+
+        assertCreateIndexEquals(
+            buildCreateIndex("idx", "Person", "", true, QueryIndexType.GEOSPATIAL, "old",
true, "name", false),
+            "create spatial index if not exists idx on Person (old, name desc)");
+
+        // Schemas for index and table must match (here schema for the table defaults to
empty string)
+        assertParseThrows("create index if not exists \"schema2\".idx on Person (name)",
+            DbException.class, "Schema name must match [90080-191]");
+
+        assertParseThrows("create hash index if not exists idx on Person (name)",
+            IgniteSQLException.class, "Only SPATIAL modifier is supported for CREATE INDEX");
+
+        assertParseThrows("create unique index if not exists idx on Person (name)",
+            IgniteSQLException.class, "Only SPATIAL modifier is supported for CREATE INDEX");
+
+        assertParseThrows("create primary key on Person (name)",
+            IgniteSQLException.class, "Only SPATIAL modifier is supported for CREATE INDEX");
+
+        assertParseThrows("create primary key hash on Person (name)",
+            IgniteSQLException.class, "Only SPATIAL modifier is supported for CREATE INDEX");
+
+        assertParseThrows("create index on Person (name nulls first)",
+            IgniteSQLException.class, "NULLS FIRST and NULLS LAST modifiers are not supported
for index columns");
+
+        assertParseThrows("create index on Person (name desc nulls last)",
+            IgniteSQLException.class, "NULLS FIRST and NULLS LAST modifiers are not supported
for index columns");
+    }
+
+    /**
+     *
+     */
+    public void testParseDropIndex() throws Exception {
+        // Schema that is not set defaults to default schema of connection which is empty
string
+        assertDropIndexEquals(buildDropIndex("idx", "", false), "drop index idx");
+        assertDropIndexEquals(buildDropIndex("idx", "", true), "drop index if exists idx");
+        assertDropIndexEquals(buildDropIndex("idx", "schema2", true), "drop index if exists
\"schema2\".idx");
+        assertDropIndexEquals(buildDropIndex("idx", "schema2", false), "drop index \"schema2\".idx");
+
+        // Message is null as long as it may differ from system to system, so we just check
for exceptions
+        assertParseThrows("drop index schema2.", DbException.class, null);
+        assertParseThrows("drop index", DbException.class, null);
+        assertParseThrows("drop index if exists", DbException.class, null);
+        assertParseThrows("drop index if exists schema2.", DbException.class, null);
+    }
+
+    /**
+     * @param sql Statement.
+     * @param exCls Exception class.
+     * @param msg Expected message.
+     */
+    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+    private void assertParseThrows(final String sql, Class<? extends Exception> exCls,
String msg) {
+        GridTestUtils.assertThrows(null, new Callable<Object>() {
+            @Override public Object call() throws Exception {
+                Prepared p = parse(sql);
+
+                return new GridSqlQueryParser().parse(p);
+            }
+        }, exCls, msg);
+    }
+
+    /**
+     * Parse SQL and compare it to expected instance.
+     */
+    private void assertCreateIndexEquals(GridCreateIndex exp, String sql) throws Exception
{
+        Prepared prepared = parse(sql);
+
+        GridSqlStatement stmt = new GridSqlQueryParser().parse(prepared);
+
+        assertTrue(stmt instanceof GridCreateIndex);
+
+        assertCreateIndexEquals(exp, (GridCreateIndex) stmt);
+    }
+
+    /**
+     * Parse SQL and compare it to expected instance of DROP INDEX.
+     */
+    private void assertDropIndexEquals(GridDropIndex exp, String sql) throws Exception {
+        Prepared prepared = parse(sql);
+
+        GridSqlStatement stmt = new GridSqlQueryParser().parse(prepared);
+
+        assertTrue(stmt instanceof GridDropIndex);
+
+        assertDropIndexEquals(exp, (GridDropIndex) stmt);
+    }
+
+    /**
+     * Test two instances of {@link GridDropIndex} for equality.
+     */
+    private static void assertDropIndexEquals(GridDropIndex exp, GridDropIndex actual) {
+        assertEqualsIgnoreCase(exp.name(), actual.name());
+        assertEqualsIgnoreCase(exp.schemaName(), actual.schemaName());
+        assertEquals(exp.ifExists(), actual.ifExists());
+    }
+
+    /**
+     *
+     */
+    private static GridDropIndex buildDropIndex(String name, String schema, boolean ifExists)
{
+        GridDropIndex res = new GridDropIndex();
+
+        res.name(name);
+        res.schemaName(schema);
+        res.ifExists(ifExists);
+
+        return res;
+    }
+
+    /**
+     * Test two instances of {@link GridCreateIndex} for equality.
+     */
+    private static void assertCreateIndexEquals(GridCreateIndex exp, GridCreateIndex actual)
{
+        assertEquals(exp.ifNotExists(), actual.ifNotExists());
+        assertEqualsIgnoreCase(exp.schemaName(), actual.schemaName());
+        assertEqualsIgnoreCase(exp.tableName(), actual.tableName());
+
+        assertEqualsIgnoreCase(exp.index().getName(), actual.index().getName());
+
+        Iterator<Map.Entry<String, Boolean>> expFldsIt = exp.index().getFields().entrySet().iterator();
+        Iterator<Map.Entry<String, Boolean>> actualFldsIt = actual.index().getFields().entrySet().iterator();
+
+        while (expFldsIt.hasNext()) {
+            assertTrue(actualFldsIt.hasNext());
+
+            Map.Entry<String, Boolean> expEntry = expFldsIt.next();
+            Map.Entry<String, Boolean> actualEntry = actualFldsIt.next();
+
+            assertEqualsIgnoreCase(expEntry.getKey(), actualEntry.getKey());
+            assertEquals(expEntry.getValue(), actualEntry.getValue());
+        }
+
+        assertFalse(actualFldsIt.hasNext());
+
+        assertEquals(exp.index().getIndexType(), actual.index().getIndexType());
+    }
+
+    /**
+     *
+     */
+    private static void assertEqualsIgnoreCase(String exp, String actual) {
+        assertEquals((exp == null), (actual == null));
+
+        if (exp != null)
+            assertTrue(exp.equalsIgnoreCase(actual));
+    }
+
+    /**
+     *
+     */
+    private static GridCreateIndex buildCreateIndex(String name, String tblName, String schemaName,
boolean ifNotExists,
+        QueryIndexType type, Object... flds) {
+        QueryIndex idx = new QueryIndex();
+
+        idx.setName(name);
+
+        assert !F.isEmpty(flds) && flds.length % 2 == 0;
+
+        LinkedHashMap<String, Boolean> trueFlds = new LinkedHashMap<>();
+
+        for (int i = 0; i < flds.length / 2; i++)
+            trueFlds.put((String) flds[i * 2], (Boolean) flds[i * 2 + 1]);
+
+        idx.setFields(trueFlds);
+        idx.setIndexType(type);
+
+        GridCreateIndex res = new GridCreateIndex();
+
+        res.schemaName(schemaName);
+        res.tableName(tblName);
+        res.ifNotExists(ifNotExists);
+        res.index(idx);
+
+        return res;
+    }
+
+    /**
+     *
+     */
     private JdbcConnection connection() throws Exception {
         GridKernalContext ctx = ((IgniteEx)ignite).context();
 
@@ -422,7 +636,7 @@ public class GridQueryParsingTest extends GridCommonAbstractTest {
 
         System.out.println(normalizeSql(res));
 
-        assertSqlEquals(prepared.getPlanSQL(), res);
+        assertSqlEquals(U.firstNotNull(prepared.getPlanSQL(), prepared.getSQL()), res);
     }
 
     @QuerySqlFunction


Mime
View raw message