IGNITE-7700: SQL system view for list of nodes. This closes #4185. This closes 
#4424.


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

Branch: refs/heads/ignite-gg-13195-cache-groups
Commit: 6263dbe6c45a0656ba55a0eb04d07941fac27c7c
Parents: e413283
Author: Aleksey Plekhanov <[email protected]>
Authored: Fri Jul 27 12:23:08 2018 +0300
Committer: devozerov <[email protected]>
Committed: Fri Jul 27 12:23:08 2018 +0300

----------------------------------------------------------------------
 .../apache/ignite/IgniteSystemProperties.java   |   3 +
 .../internal/processors/query/QueryUtils.java   |   3 +
 .../processors/query/h2/IgniteH2Indexing.java   |  85 ++++++-
 .../query/h2/ddl/DdlStatementsProcessor.java    |  28 ++-
 .../query/h2/dml/UpdatePlanBuilder.java         |  15 +-
 .../query/h2/sql/GridSqlQuerySplitter.java      |  12 +-
 .../processors/query/h2/sys/SqlSystemIndex.java | 143 +++++++++++
 .../processors/query/h2/sys/SqlSystemTable.java | 208 ++++++++++++++++
 .../query/h2/sys/SqlSystemTableEngine.java      |  60 +++++
 .../h2/sys/view/SqlAbstractLocalSystemView.java | 104 ++++++++
 .../h2/sys/view/SqlAbstractSystemView.java      | 134 ++++++++++
 .../query/h2/sys/view/SqlSystemView.java        |  79 ++++++
 .../sys/view/SqlSystemViewColumnCondition.java  | 102 ++++++++
 .../query/h2/sys/view/SqlSystemViewNodes.java   | 116 +++++++++
 .../query/SqlSystemViewsSelfTest.java           | 247 +++++++++++++++++++
 .../IgniteCacheQuerySelfTestSuite.java          |   2 +
 16 files changed, 1325 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/6263dbe6/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java 
b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
index 6ba44ee..eb60371 100644
--- a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
+++ b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
@@ -477,6 +477,9 @@ public final class IgniteSystemProperties {
     /** Force all SQL queries to be processed lazily regardless of what 
clients request. */
     public static final String IGNITE_SQL_FORCE_LAZY_RESULT_SET = 
"IGNITE_SQL_FORCE_LAZY_RESULT_SET";
 
+    /** Disable SQL system views. */
+    public static final String IGNITE_SQL_DISABLE_SYSTEM_VIEWS = 
"IGNITE_SQL_DISABLE_SYSTEM_VIEWS";
+
     /** Maximum size for affinity assignment history. */
     public static final String IGNITE_AFFINITY_HISTORY_SIZE = 
"IGNITE_AFFINITY_HISTORY_SIZE";
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/6263dbe6/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
index 12eacef..3f40990 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
@@ -75,6 +75,9 @@ public class QueryUtils {
     /** Default schema. */
     public static final String DFLT_SCHEMA = "PUBLIC";
 
+    /** Schema for system view. */
+    public static final String SCHEMA_SYS = "IGNITE";
+
     /** Field name for key. */
     public static final String KEY_FIELD_NAME = "_KEY";
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/6263dbe6/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 14d4333..4f51ca0 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
@@ -113,9 +113,12 @@ import 
org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuery;
 import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryParser;
 import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuerySplitter;
 import org.apache.ignite.internal.processors.query.h2.sql.GridSqlStatement;
+import org.apache.ignite.internal.processors.query.h2.sys.SqlSystemTableEngine;
+import 
org.apache.ignite.internal.processors.query.h2.sys.view.SqlSystemViewNodes;
 import 
org.apache.ignite.internal.processors.query.h2.twostep.GridMapQueryExecutor;
 import 
org.apache.ignite.internal.processors.query.h2.twostep.GridReduceQueryExecutor;
 import 
org.apache.ignite.internal.processors.query.h2.twostep.MapQueryLazyWorker;
+import org.apache.ignite.internal.processors.query.h2.sys.view.SqlSystemView;
 import 
org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitor;
 import 
org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorClosure;
 import 
org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorImpl;
@@ -194,7 +197,6 @@ import static 
org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType
  * {@link GridQueryTypeDescriptor#fields()}.
  * For each table it will create indexes declared in {@link 
GridQueryTypeDescriptor#indexes()}.
  */
-@SuppressWarnings({"UnnecessaryFullyQualifiedName", 
"NonFinalStaticVariableUsedInClassInitialization"})
 public class IgniteH2Indexing implements GridQueryIndexing {
     /** A pattern for commands having internal implementation in Ignite. */
     public static final Pattern INTERNAL_CMD_RE = Pattern.compile(
@@ -239,6 +241,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
     private final Long CLEANUP_STMT_CACHE_PERIOD = 
Long.getLong(IGNITE_H2_INDEXING_CACHE_CLEANUP_PERIOD, 10_000);
 
     /** The period of clean up the {@link #conns}. */
+    @SuppressWarnings("FieldCanBeLocal")
     private final Long CLEANUP_CONNECTIONS_PERIOD = 2000L;
 
     /** The timeout to remove entry from the {@link #stmtCache} if the thread 
doesn't perform any queries. */
@@ -296,7 +299,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
 
     /** */
     private final ThreadLocal<H2ConnectionWrapper> connCache = new 
ThreadLocal<H2ConnectionWrapper>() {
-        @Nullable @Override public H2ConnectionWrapper get() {
+        @Override public H2ConnectionWrapper get() {
             H2ConnectionWrapper c = super.get();
 
             boolean reconnect = true;
@@ -320,7 +323,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
             return c;
         }
 
-        @Nullable @Override protected H2ConnectionWrapper initialValue() {
+        @Override protected H2ConnectionWrapper initialValue() {
             Connection c;
 
             try {
@@ -1110,7 +1113,7 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
     private static List<Long> zeroBatchedStreamedUpdateResult(int size) {
         Long[] res = new Long[size];
 
-        Arrays.fill(res, 0);
+        Arrays.fill(res, 0L);
 
         return Arrays.asList(res);
     }
@@ -1306,6 +1309,7 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
             qry.isEnforceJoinOrder(), qry.getTimeout(), cancel);
 
         QueryCursorImpl<List<?>> cursor = new QueryCursorImpl<>(new 
Iterable<List<?>>() {
+            @SuppressWarnings("NullableProblems")
             @Override public Iterator<List<?>> iterator() {
                 try {
                     return new GridQueryCacheObjectsIterator(res.iterator(), 
objectContext(), keepBinary);
@@ -1336,6 +1340,7 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
             F.asList(params), type, filter, cancel);
 
         return new QueryCursorImpl<>(new Iterable<Cache.Entry<K, V>>() {
+            @SuppressWarnings("NullableProblems")
             @Override public Iterator<Cache.Entry<K, V>> iterator() {
                 return new ClIter<Cache.Entry<K, V>>() {
                     @Override public void close() throws Exception {
@@ -1437,6 +1442,7 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
         final boolean lazy
     ) {
         return new Iterable<List<?>>() {
+            @SuppressWarnings("NullableProblems")
             @Override public Iterator<List<?>> iterator() {
                 return rdcQryExec.query(schemaName, qry, keepCacheObj, 
enforceJoinOrder, timeoutMillis, cancel, params,
                     parts, lazy);
@@ -1732,6 +1738,7 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
      * @param keepBinary Whether binary objects must not be deserialized 
automatically.
      * @param cancel Query cancel state holder.    @return Query result.
      */
+    @SuppressWarnings("unchecked")
     private List<? extends FieldsQueryCursor<List<?>>> doRunPrepared(String 
schemaName, Prepared prepared,
         SqlFieldsQuery qry, GridCacheTwoStepQuery twoStepQry,
         List<GridQueryFieldMetadata> meta, boolean keepBinary, GridQueryCancel 
cancel) {
@@ -1753,6 +1760,7 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
                             dmlProc.updateSqlFieldsLocal(schemaName, conn, 
prepared, qry, filter, cancel);
 
                         return Collections.singletonList(new 
QueryCursorImpl<>(new Iterable<List<?>>() {
+                            @SuppressWarnings("NullableProblems")
                             @Override public Iterator<List<?>> iterator() {
                                 try {
                                     return new 
GridQueryCacheObjectsIterator(updRes.iterator(), objectContext(),
@@ -1793,7 +1801,8 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
                 return Collections.singletonList(resCur);
             }
 
-            throw new IgniteSQLException("Unsupported DDL/DML operation: " + 
prepared.getClass().getName());
+            throw new IgniteSQLException("Unsupported DDL/DML operation: " + 
prepared.getClass().getName(),
+                IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
         }
 
         if (twoStepQry != null) {
@@ -1984,6 +1993,11 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
 
         List<Integer> cacheIds = collectCacheIds(null, res);
 
+        if (!F.isEmpty(cacheIds) && hasSystemViews(res)) {
+            throw new IgniteSQLException("Normal tables and system views 
cannot be used in the same query.",
+                IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+        }
+
         if (F.isEmpty(cacheIds))
             res.local(true);
         else {
@@ -2578,6 +2592,33 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
 
             dmlProc.start(ctx, this);
             ddlProc.start(ctx, this);
+
+            boolean sysViewsEnabled =
+                
!IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_SQL_DISABLE_SYSTEM_VIEWS);
+
+            if (sysViewsEnabled) {
+                try {
+                    synchronized (schemaMux) {
+                        createSchema(QueryUtils.SCHEMA_SYS);
+                    }
+
+                    Connection c = connectionForSchema(QueryUtils.SCHEMA_SYS);
+
+                    for (SqlSystemView view : systemViews(ctx))
+                        SqlSystemTableEngine.registerView(c, view);
+                }
+                catch (SQLException e) {
+                    throw new IgniteCheckedException("Failed to register 
system view.", e);
+                }
+
+                // Caching this connection in ThreadLocal may lead to memory 
leaks.
+                connCache.set(null);
+            }
+            else {
+                if (log.isDebugEnabled())
+                    log.debug("SQL system views will not be created because 
they are disabled (see " +
+                        IgniteSystemProperties.IGNITE_SQL_DISABLE_SYSTEM_VIEWS 
+ " system property)");
+            }
         }
 
         if (JdbcUtils.serializer != null)
@@ -2585,6 +2626,8 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
 
         JdbcUtils.serializer = h2Serializer();
 
+        assert ctx != null;
+
         connCleanupTask = ctx.timeout().schedule(new Runnable() {
             @Override public void run() {
                 cleanupConnections();
@@ -2593,6 +2636,18 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
     }
 
     /**
+     * @param ctx Context.
+     * @return Predefined system views.
+     */
+    public Collection<SqlSystemView> systemViews(GridKernalContext ctx) {
+        Collection<SqlSystemView> views = new ArrayList<>();
+
+        views.add(new SqlSystemViewNodes(ctx));
+
+        return views;
+    }
+
+    /**
      * @return Value object context.
      */
     public CacheObjectValueContext objectContext() {
@@ -3065,9 +3120,11 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
             for (QueryTable tblKey : twoStepQry.tables()) {
                 GridH2Table tbl = dataTable(tblKey);
 
-                int cacheId = tbl.cacheId();
+                if (tbl != null) {
+                    int cacheId = tbl.cacheId();
 
-                caches0.add(cacheId);
+                    caches0.add(cacheId);
+                }
             }
         }
 
@@ -3084,6 +3141,20 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
     }
 
     /**
+     * @return {@code True} is system views exist.
+     */
+    private boolean hasSystemViews(GridCacheTwoStepQuery twoStepQry) {
+        if (twoStepQry.tablesCount() > 0) {
+            for (QueryTable tbl : twoStepQry.tables()) {
+                if (QueryUtils.SCHEMA_SYS.equals(tbl.schema()))
+                    return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
      * Closeable iterator.
      */
     private interface ClIter<X> extends AutoCloseable, Iterator<X> {

http://git-wip-us.apache.org/repos/asf/ignite/blob/6263dbe6/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
index f907138..c617d30 100644
--- 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
@@ -34,8 +34,6 @@ import org.apache.ignite.cache.query.FieldsQueryCursor;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.IgniteInternalFuture;
-import 
org.apache.ignite.internal.processors.authentication.AuthorizationContext;
-import 
org.apache.ignite.internal.processors.authentication.UserManagementOperation;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.QueryCursorImpl;
 import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
@@ -58,7 +56,6 @@ import 
org.apache.ignite.internal.processors.query.h2.sql.GridSqlDropTable;
 import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryParser;
 import org.apache.ignite.internal.processors.query.h2.sql.GridSqlStatement;
 import 
org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
-import org.apache.ignite.internal.processors.security.SecurityContext;
 import org.apache.ignite.internal.processors.security.SecurityContextHolder;
 import org.apache.ignite.internal.sql.command.SqlAlterTableCommand;
 import org.apache.ignite.internal.sql.command.SqlAlterUserCommand;
@@ -121,6 +118,8 @@ public class DdlStatementsProcessor {
         IgniteInternalFuture fut = null;
 
         try {
+            isDdlOnSchemaSupported(cmd.schemaName());
+
             if (cmd instanceof SqlCreateIndexCommand) {
                 SqlCreateIndexCommand cmd0 = (SqlCreateIndexCommand)cmd;
 
@@ -269,6 +268,8 @@ public class DdlStatementsProcessor {
             if (stmt0 instanceof GridSqlCreateIndex) {
                 GridSqlCreateIndex cmd = (GridSqlCreateIndex)stmt0;
 
+                isDdlOnSchemaSupported(cmd.schemaName());
+
                 GridH2Table tbl = idx.dataTable(cmd.schemaName(), 
cmd.tableName());
 
                 if (tbl == null)
@@ -306,6 +307,8 @@ public class DdlStatementsProcessor {
             else if (stmt0 instanceof GridSqlDropIndex) {
                 GridSqlDropIndex cmd = (GridSqlDropIndex) stmt0;
 
+                isDdlOnSchemaSupported(cmd.schemaName());
+
                 GridH2Table tbl = idx.dataTableForIndex(cmd.schemaName(), 
cmd.indexName());
 
                 if (tbl != null) {
@@ -327,6 +330,8 @@ public class DdlStatementsProcessor {
 
                 GridSqlCreateTable cmd = (GridSqlCreateTable)stmt0;
 
+                isDdlOnSchemaSupported(cmd.schemaName());
+
                 if (!F.eq(QueryUtils.DFLT_SCHEMA, cmd.schemaName()))
                     throw new SchemaOperationException("CREATE TABLE can only 
be executed on " +
                         QueryUtils.DFLT_SCHEMA + " schema.");
@@ -362,6 +367,8 @@ public class DdlStatementsProcessor {
 
                 GridSqlDropTable cmd = (GridSqlDropTable)stmt0;
 
+                isDdlOnSchemaSupported(cmd.schemaName());
+
                 if (!F.eq(QueryUtils.DFLT_SCHEMA, cmd.schemaName()))
                     throw new SchemaOperationException("DROP TABLE can only be 
executed on " +
                         QueryUtils.DFLT_SCHEMA + " schema.");
@@ -385,6 +392,8 @@ public class DdlStatementsProcessor {
             else if (stmt0 instanceof GridSqlAlterTableAddColumn) {
                 GridSqlAlterTableAddColumn cmd = 
(GridSqlAlterTableAddColumn)stmt0;
 
+                isDdlOnSchemaSupported(cmd.schemaName());
+
                 GridH2Table tbl = idx.dataTable(cmd.schemaName(), 
cmd.tableName());
 
                 if (tbl == null && cmd.ifTableExists()) {
@@ -444,6 +453,8 @@ public class DdlStatementsProcessor {
             else if (stmt0 instanceof GridSqlAlterTableDropColumn) {
                 GridSqlAlterTableDropColumn cmd = 
(GridSqlAlterTableDropColumn)stmt0;
 
+                isDdlOnSchemaSupported(cmd.schemaName());
+
                 GridH2Table tbl = idx.dataTable(cmd.schemaName(), 
cmd.tableName());
 
                 if (tbl == null && cmd.ifTableExists()) {
@@ -522,6 +533,17 @@ public class DdlStatementsProcessor {
     }
 
     /**
+     * Check if schema supports DDL statement.
+     *
+     * @param schemaName Schema name.
+     */
+    private static void isDdlOnSchemaSupported(String schemaName) {
+        if (F.eq(QueryUtils.SCHEMA_SYS, schemaName))
+            throw new IgniteSQLException("DDL statements are not supported on 
" + schemaName + " schema",
+                IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+    }
+
+    /**
      * Check if table supports DDL statement.
      *
      * @param tbl Table.

http://git-wip-us.apache.org/repos/asf/ignite/blob/6263dbe6/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java
index d897ac7..1079005 100644
--- 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java
@@ -137,7 +137,14 @@ public final class UpdatePlanBuilder {
             target = ins.into();
 
             tbl = DmlAstUtils.gridTableForElement(target);
-            desc = tbl.dataTable().rowDescriptor();
+
+            GridH2Table h2Tbl = tbl.dataTable();
+
+            if (h2Tbl == null)
+                throw new IgniteSQLException("Operation not supported for 
table '" + tbl.tableName() + "'",
+                    IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
+            desc = h2Tbl.rowDescriptor();
 
             cols = ins.columns();
             sel = DmlAstUtils.selectForInsertOrMerge(cols, ins.rows(), 
ins.query());
@@ -312,6 +319,10 @@ public final class UpdatePlanBuilder {
 
         GridH2Table h2Tbl = tbl.dataTable();
 
+        if (h2Tbl == null)
+            throw new IgniteSQLException("Operation not supported for table '" 
+ tbl.tableName() + "'",
+                IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
         GridH2RowDescriptor desc = h2Tbl.rowDescriptor();
 
         if (desc == null)
@@ -659,7 +670,7 @@ public final class UpdatePlanBuilder {
 
         GridH2Table gridTbl = tbl.dataTable();
 
-        if (updateAffectsKeyColumns(gridTbl, update.set().keySet()))
+        if (gridTbl != null && updateAffectsKeyColumns(gridTbl, 
update.set().keySet()))
             throw new IgniteSQLException("SQL UPDATE can't modify key or its 
fields directly",
                 IgniteQueryErrorCode.KEY_UPDATE);
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/6263dbe6/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
index d2fb149..5c32005 100644
--- 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
@@ -1546,8 +1546,12 @@ public class GridSqlQuerySplitter {
      * @return {@code true} If the given AST has partitioned tables.
      */
     private static boolean hasPartitionedTables(GridSqlAst ast) {
-        if (ast instanceof GridSqlTable)
-            return ((GridSqlTable)ast).dataTable().isPartitioned();
+        if (ast instanceof GridSqlTable) {
+            if (((GridSqlTable)ast).dataTable() != null)
+                return ((GridSqlTable)ast).dataTable().isPartitioned();
+            else
+                return false;
+        }
 
         for (int i = 0; i < ast.size(); i++) {
             if (hasPartitionedTables(ast.child(i)))
@@ -1739,8 +1743,8 @@ public class GridSqlQuerySplitter {
         if (from instanceof GridSqlTable) {
             GridSqlTable tbl = (GridSqlTable)from;
 
-            String schemaName = tbl.dataTable().identifier().schema();
-            String tblName = tbl.dataTable().identifier().table();
+            String schemaName = tbl.dataTable() != null ? 
tbl.dataTable().identifier().schema() : tbl.schema();
+            String tblName = tbl.dataTable() != null ? 
tbl.dataTable().identifier().table() : tbl.tableName();
 
             tbls.add(new QueryTable(schemaName, tblName));
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/6263dbe6/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/SqlSystemIndex.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/SqlSystemIndex.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/SqlSystemIndex.java
new file mode 100644
index 0000000..55f5f29
--- /dev/null
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/SqlSystemIndex.java
@@ -0,0 +1,143 @@
+/*
+ * 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.sys;
+
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Cursor;
+import org.h2.engine.Session;
+import org.h2.index.BaseIndex;
+import org.h2.index.Cursor;
+import org.h2.index.IndexType;
+import org.h2.message.DbException;
+import org.h2.result.Row;
+import org.h2.result.SearchRow;
+import org.h2.result.SortOrder;
+import org.h2.table.Column;
+import org.h2.table.IndexColumn;
+import org.h2.table.TableFilter;
+
+import java.util.HashSet;
+
+/**
+ * Meta view H2 index.
+ */
+public class SqlSystemIndex extends BaseIndex {
+    /** Distributed view cost multiplier. */
+    private static final int DISTRIBUTED_MUL = 100;
+
+    /**
+     * @param tbl Table.
+     * @param col Column.
+     */
+    SqlSystemIndex(SqlSystemTable tbl, Column... col) {
+        IndexColumn[] idxCols;
+
+        if (col != null && col.length > 0)
+            idxCols = IndexColumn.wrap(col);
+        else
+            idxCols = new IndexColumn[0];
+
+        initBaseIndex(tbl, 0, null, idxCols, IndexType.createNonUnique(false));
+    }
+
+    /** {@inheritDoc} */
+    @Override public void close(Session ses) {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public void add(Session ses, Row row) {
+        throw DbException.getUnsupportedException("system view is read-only");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void remove(Session ses, Row row) {
+        throw DbException.getUnsupportedException("system view is read-only");
+    }
+
+    /** {@inheritDoc} */
+    @Override public Cursor find(Session ses, SearchRow first, SearchRow last) 
{
+        assert table instanceof SqlSystemTable;
+
+        Iterable<Row> rows = ((SqlSystemTable)table).getRows(ses, first, last);
+
+        return new GridH2Cursor(rows.iterator());
+    }
+
+    /** {@inheritDoc} */
+    @Override public double getCost(Session ses, int[] masks, TableFilter[] 
filters, int filter, SortOrder sortOrder,
+        HashSet<Column> allColsSet) {
+        long rowCnt = getRowCountApproximation();
+
+        double baseCost = getCostRangeIndex(masks, rowCnt, filters, filter, 
sortOrder, false, allColsSet);
+
+        if (((SqlSystemTable)table).view.isDistributed())
+            baseCost = baseCost * DISTRIBUTED_MUL;
+
+        return baseCost;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void truncate(Session ses) {
+        throw DbException.getUnsupportedException("system view cannot be 
truncated");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void remove(Session ses) {
+        throw DbException.getUnsupportedException("system view cannot be 
removed");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void checkRename() {
+        throw DbException.getUnsupportedException("system view cannot be 
renamed");
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean needRebuild() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getCreateSQL() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean canGetFirstOrLast() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Cursor findFirstOrLast(Session ses, boolean first) {
+        throw DbException.getUnsupportedException("system views cannot be used 
to get first or last value");
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getRowCount(Session ses) {
+        return table.getRowCount(ses);
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getRowCountApproximation() {
+        return table.getRowCountApproximation();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getDiskSpaceUsed() {
+        return 0;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6263dbe6/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/SqlSystemTable.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/SqlSystemTable.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/SqlSystemTable.java
new file mode 100644
index 0000000..23106ee
--- /dev/null
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/SqlSystemTable.java
@@ -0,0 +1,208 @@
+/*
+ * 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.sys;
+
+import java.util.ArrayList;
+
+import org.apache.ignite.internal.processors.query.h2.sys.view.SqlSystemView;
+import org.h2.command.ddl.CreateTableData;
+import org.h2.engine.Session;
+import org.h2.index.Index;
+import org.h2.index.IndexType;
+import org.h2.message.DbException;
+import org.h2.result.Row;
+import org.h2.result.SearchRow;
+import org.h2.table.Column;
+import org.h2.table.IndexColumn;
+import org.h2.table.TableBase;
+import org.h2.table.TableType;
+
+/**
+ * System H2 table over a view.
+ */
+public class SqlSystemTable extends TableBase {
+    /** Scan index. */
+    protected final SqlSystemIndex scanIdx;
+
+    /** Meta view. */
+    protected final SqlSystemView view;
+
+    /**
+     * Indexes.
+     *
+     * Note: We need ArrayList here by H2 {@link #getIndexes()} method 
contract.
+     */
+    protected final ArrayList<Index> indexes;
+
+    /**
+     * @param data Data.
+     * @param view Meta view.
+     */
+    public SqlSystemTable(CreateTableData data, SqlSystemView view) {
+        super(data);
+
+        assert view != null;
+
+        this.view = view;
+
+        this.setColumns(view.getColumns());
+
+        scanIdx = new SqlSystemIndex(this);
+
+        indexes = new ArrayList<>();
+        indexes.add(scanIdx);
+
+        for (String index : view.getIndexes()) {
+            String[] indexedCols = index.split(",");
+
+            Column[] cols = new Column[indexedCols.length];
+
+            for (int i = 0; i < indexedCols.length; i++)
+                cols[i] = getColumn(indexedCols[i]);
+
+            SqlSystemIndex idx = new SqlSystemIndex(this, cols);
+
+            indexes.add(idx);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public Index addIndex(Session ses, String idxName, int idxId, 
IndexColumn[] cols,
+        IndexType idxType, boolean create, String idxComment) {
+        throw DbException.getUnsupportedException("META");
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean lock(Session ses, boolean exclusive, boolean 
forceLockEvenInMvcc) {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void unlock(Session ses) {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isLockedExclusively() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void removeRow(Session ses, Row row) {
+        throw DbException.getUnsupportedException("META");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void addRow(Session ses, Row row) {
+        throw DbException.getUnsupportedException("META");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void removeChildrenAndResources(Session ses) {
+        throw DbException.getUnsupportedException("META");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void close(Session ses) {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public void checkRename() {
+        throw DbException.getUnsupportedException("META");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void checkSupportAlter() {
+        throw DbException.getUnsupportedException("META");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void truncate(Session ses) {
+        throw DbException.getUnsupportedException("META");
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getRowCount(Session ses) {
+        return view.getRowCount();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean canGetRowCount() {
+        return view.canGetRowCount();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getRowCountApproximation() {
+        return view.getRowCount();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean canDrop() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public TableType getTableType() {
+        return TableType.SYSTEM_TABLE;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Index getScanIndex(Session ses) {
+        return scanIdx;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Index getUniqueIndex() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public ArrayList<Index> getIndexes() {
+        return indexes;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getMaxDataModificationId() {
+        return Long.MAX_VALUE;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getDiskSpaceUsed() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isDeterministic() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean canReference() {
+        return false;
+    }
+
+    /**
+     * @param ses Session.
+     * @param first First.
+     * @param last Last.
+     */
+    public Iterable<Row> getRows(Session ses, SearchRow first, SearchRow last) 
{
+        return view.getRows(ses, first, last);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6263dbe6/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/SqlSystemTableEngine.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/SqlSystemTableEngine.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/SqlSystemTableEngine.java
new file mode 100644
index 0000000..23d7739
--- /dev/null
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/SqlSystemTableEngine.java
@@ -0,0 +1,60 @@
+/*
+ * 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.sys;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.apache.ignite.internal.processors.query.h2.sys.view.SqlSystemView;
+import org.h2.api.TableEngine;
+import org.h2.command.ddl.CreateTableData;
+import org.h2.table.Table;
+
+/**
+ * H2 table engine for system views.
+ */
+public class SqlSystemTableEngine implements TableEngine {
+    /** View being created. */
+    private static volatile SqlSystemView curView;
+
+    /**
+     * @param conn Connection.
+     * @param view View.
+     */
+    public static synchronized void registerView(Connection conn, 
SqlSystemView view)
+        throws SQLException {
+        curView = view;
+
+        String sql = view.getCreateSQL() + " ENGINE \"" + 
SqlSystemTableEngine.class.getName() + "\"";
+
+        try {
+            try (Statement s = conn.createStatement()) {
+                s.execute(sql);
+            }
+        }
+        finally {
+            curView = null;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public Table createTable(CreateTableData data) {
+        return new SqlSystemTable(data, curView);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6263dbe6/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlAbstractLocalSystemView.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlAbstractLocalSystemView.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlAbstractLocalSystemView.java
new file mode 100644
index 0000000..c601708
--- /dev/null
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlAbstractLocalSystemView.java
@@ -0,0 +1,104 @@
+/*
+ * 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.sys.view;
+
+import org.apache.ignite.internal.GridKernalContext;
+import org.h2.engine.Session;
+import org.h2.result.Row;
+import org.h2.result.SearchRow;
+import org.h2.table.Column;
+import org.h2.value.Value;
+import org.h2.value.ValueNull;
+import org.h2.value.ValueString;
+
+/**
+ * Local meta view base class (which uses only local node data).
+ */
+public abstract class SqlAbstractLocalSystemView extends SqlAbstractSystemView 
{
+    /**
+     * @param tblName Table name.
+     * @param desc Description.
+     * @param ctx Context.
+     * @param indexes Indexed columns.
+     * @param cols Columns.
+     */
+    public SqlAbstractLocalSystemView(String tblName, String desc, 
GridKernalContext ctx, String[] indexes,
+        Column... cols) {
+        super(tblName, desc, ctx, cols, indexes);
+
+        assert tblName != null;
+        assert ctx != null;
+        assert cols != null;
+        assert indexes != null;
+    }
+
+    /**
+     * @param ses Session.
+     * @param key Key.
+     * @param data Data for each column.
+     */
+    protected Row createRow(Session ses, long key, Object... data) {
+        Value[] values = new Value[data.length];
+
+        for (int i = 0; i < data.length; i++) {
+            Object o = data[i];
+
+            Value v = (o == null) ? ValueNull.INSTANCE :
+                (o instanceof Value) ? (Value)o : 
ValueString.get(o.toString());
+
+            values[i] = cols[i].convert(v);
+        }
+
+        Row row = ses.getDatabase().createRow(values, 1);
+
+        row.setKey(key);
+
+        return row;
+    }
+
+    /**
+     * Gets column index by name.
+     *
+     * @param colName Column name.
+     */
+    protected int getColumnIndex(String colName) {
+        assert colName != null;
+
+        for (int i = 0; i < cols.length; i++)
+            if (colName.equalsIgnoreCase(cols[i].getName()))
+                return i;
+
+        return -1;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isDistributed() {
+        return false;
+    }
+
+    /**
+     * Parse condition for column.
+     *
+     * @param colName Column name.
+     * @param first First.
+     * @param last Last.
+     */
+    protected SqlSystemViewColumnCondition conditionForColumn(String colName, 
SearchRow first, SearchRow last) {
+        return SqlSystemViewColumnCondition.forColumn(getColumnIndex(colName), 
first, last);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6263dbe6/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlAbstractSystemView.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlAbstractSystemView.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlAbstractSystemView.java
new file mode 100644
index 0000000..79d6225
--- /dev/null
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlAbstractSystemView.java
@@ -0,0 +1,134 @@
+/*
+ * 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.sys.view;
+
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.GridKernalContext;
+import org.h2.table.Column;
+import org.h2.value.Value;
+
+/**
+ * Meta view base class.
+ */
+public abstract class SqlAbstractSystemView implements SqlSystemView {
+    /** Default row count approximation. */
+    protected static final long DEFAULT_ROW_COUNT_APPROXIMATION = 100L;
+
+    /** Table name. */
+    protected final String tblName;
+
+    /** Description. */
+    protected final String desc;
+
+    /** Grid context. */
+    protected final GridKernalContext ctx;
+
+    /** Logger. */
+    protected final IgniteLogger log;
+
+    /** Columns. */
+    protected final Column[] cols;
+
+    /** Indexed column names. */
+    protected final String[] indexes;
+
+    /**
+     * @param tblName Table name.
+     * @param desc Descriptor.
+     * @param ctx Context.
+     * @param cols Columns.
+     * @param indexes Indexes.
+     */
+    public SqlAbstractSystemView(String tblName, String desc, 
GridKernalContext ctx, Column[] cols,
+        String[] indexes) {
+        this.tblName = tblName;
+        this.desc = desc;
+        this.ctx = ctx;
+        this.cols = cols;
+        this.indexes = indexes;
+        this.log = ctx.log(this.getClass());
+    }
+
+    /**
+     * @param name Name.
+     */
+    protected static Column newColumn(String name) {
+        return newColumn(name, Value.STRING);
+    }
+
+    /**
+     * @param name Name.
+     * @param type Type.
+     */
+    protected static Column newColumn(String name, int type) {
+        return new Column(name, type);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getTableName() {
+        return tblName;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getDescription() {
+        return desc;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Column[] getColumns() {
+        return cols;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String[] getIndexes() {
+        return indexes;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getRowCount() {
+        return DEFAULT_ROW_COUNT_APPROXIMATION;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean canGetRowCount() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("StringConcatenationInsideStringBufferAppend")
+    @Override public String getCreateSQL() {
+        StringBuilder sql = new StringBuilder();
+
+        sql.append("CREATE TABLE " + getTableName() + '(');
+
+        boolean isFirst = true;
+
+        for (Column col : getColumns()) {
+            if (isFirst)
+                isFirst = false;
+            else
+                sql.append(", ");
+
+            sql.append(col.getCreateSQL());
+        }
+
+        sql.append(')');
+
+        return sql.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6263dbe6/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemView.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemView.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemView.java
new file mode 100644
index 0000000..7eab521
--- /dev/null
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemView.java
@@ -0,0 +1,79 @@
+/*
+ * 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.sys.view;
+
+import org.h2.engine.Session;
+import org.h2.result.Row;
+import org.h2.result.SearchRow;
+import org.h2.table.Column;
+
+/**
+ * SQL system view.
+ */
+public interface SqlSystemView {
+    /**
+     * Gets table name.
+     */
+    public String getTableName();
+
+    /**
+     * Gets description.
+     */
+    public String getDescription();
+
+    /**
+     * Gets columns.
+     */
+    public Column[] getColumns();
+
+    /**
+     * Gets indexed column names.
+     */
+    public String[] getIndexes();
+
+    /**
+     * Gets view content.
+     *
+     * @param ses Session.
+     * @param first First.
+     * @param last Last.
+     */
+    public Iterable<Row> getRows(Session ses, SearchRow first, SearchRow last);
+
+    /**
+     * Gets row count for this view (or approximated row count, if real value 
can't be calculated quickly).
+     */
+    public long getRowCount();
+
+    /**
+     * Check if the row count can be retrieved quickly.
+     *
+     * @return true if it can
+     */
+    public boolean canGetRowCount();
+
+    /**
+     * Gets SQL script for creating table.
+     */
+    public String getCreateSQL();
+
+    /**
+     * @return {@code True} if view is distributed.
+     */
+    public boolean isDistributed();
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6263dbe6/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemViewColumnCondition.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemViewColumnCondition.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemViewColumnCondition.java
new file mode 100644
index 0000000..77cf82b
--- /dev/null
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemViewColumnCondition.java
@@ -0,0 +1,102 @@
+/*
+ * 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.sys.view;
+
+import org.h2.result.SearchRow;
+import org.h2.value.Value;
+
+/**
+ * Column condition.
+ */
+public class SqlSystemViewColumnCondition {
+    /** Is equality. */
+    private final boolean isEquality;
+
+    /** Is range. */
+    private final boolean isRange;
+
+    /** Value 1. */
+    private final Value val;
+
+    /**
+     * @param isEquality Is equality.
+     * @param isRange Is range.
+     * @param val Value for equality.
+     */
+    private SqlSystemViewColumnCondition(boolean isEquality, boolean isRange, 
Value val) {
+        this.isEquality = isEquality;
+        this.isRange = isRange;
+        this.val = val;
+    }
+
+    /**
+     * Parse condition for column.
+     *
+     * @param colIdx Column index.
+     * @param start Start row values.
+     * @param end End row values.
+     */
+    public static SqlSystemViewColumnCondition forColumn(int colIdx, SearchRow 
start, SearchRow end) {
+        boolean isEquality = false;
+        boolean isRange = false;
+
+        Value val = null;
+        Value val2 = null;
+
+        if (start != null && colIdx >= 0 && colIdx < start.getColumnCount())
+            val = start.getValue(colIdx);
+
+        if (end != null && colIdx >= 0 && colIdx < end.getColumnCount())
+            val2 = end.getValue(colIdx);
+
+        if (val != null && val2 != null) {
+            if (val.equals(val2))
+                isEquality = true;
+            else
+                isRange = true;
+        }
+        else if (val != null || val2 != null)
+            isRange = true;
+
+        return new SqlSystemViewColumnCondition(isEquality, isRange, val);
+    }
+
+    /**
+     * Checks whether the condition is equality.
+     */
+    public boolean isEquality() {
+        return isEquality;
+    }
+
+    /**
+     * Checks whether the condition is range.
+     */
+    public boolean isRange() {
+        return isRange;
+    }
+
+    /**
+     * Gets value, if condition is equality.
+     */
+    public Value valueForEquality() {
+        if (isEquality)
+            return val;
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6263dbe6/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemViewNodes.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemViewNodes.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemViewNodes.java
new file mode 100644
index 0000000..e944b4f
--- /dev/null
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemViewNodes.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.h2.sys.view;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.util.typedef.F;
+import org.h2.engine.Session;
+import org.h2.result.Row;
+import org.h2.result.SearchRow;
+import org.h2.value.Value;
+
+/**
+ * Meta view: nodes.
+ */
+public class SqlSystemViewNodes extends SqlAbstractLocalSystemView {
+    /**
+     * @param ctx Grid context.
+     */
+    public SqlSystemViewNodes(GridKernalContext ctx) {
+        super("NODES", "Topology nodes", ctx, new String[] {"ID", "IS_LOCAL"},
+            newColumn("ID", Value.UUID),
+            newColumn("CONSISTENT_ID"),
+            newColumn("VERSION"),
+            newColumn("IS_LOCAL", Value.BOOLEAN),
+            newColumn("IS_CLIENT", Value.BOOLEAN),
+            newColumn("IS_DAEMON", Value.BOOLEAN),
+            newColumn("NODE_ORDER", Value.INT),
+            newColumn("ADDRESSES"),
+            newColumn("HOSTNAMES")
+        );
+    }
+
+    /** {@inheritDoc} */
+    @Override public Iterable<Row> getRows(Session ses, SearchRow first, 
SearchRow last) {
+        List<Row> rows = new ArrayList<>();
+
+        Collection<ClusterNode> nodes;
+
+        SqlSystemViewColumnCondition locCond = conditionForColumn("IS_LOCAL", 
first, last);
+        SqlSystemViewColumnCondition idCond = conditionForColumn("ID", first, 
last);
+
+        if (locCond.isEquality() && locCond.valueForEquality().getBoolean())
+            nodes = Collections.singleton(ctx.discovery().localNode());
+        else if (idCond.isEquality()) {
+            UUID nodeId = 
uuidFromString(idCond.valueForEquality().getString());
+
+            nodes = nodeId == null ? Collections.emptySet() : 
Collections.singleton(ctx.discovery().node(nodeId));
+        }
+        else
+            nodes = F.concat(false, ctx.discovery().allNodes(), 
ctx.discovery().daemonNodes());
+
+        for (ClusterNode node : nodes) {
+            if (node != null)
+                rows.add(
+                    createRow(ses, rows.size(),
+                        node.id(),
+                        node.consistentId(),
+                        node.version(),
+                        node.isLocal(),
+                        node.isClient(),
+                        node.isDaemon(),
+                        node.order(),
+                        node.addresses(),
+                        node.hostNames()
+                    )
+                );
+        }
+
+        return rows;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean canGetRowCount() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getRowCount() {
+        return ctx.discovery().allNodes().size() + 
ctx.discovery().daemonNodes().size();
+    }
+
+    /**
+     * Converts string to UUID safe (suppressing exceptions).
+     *
+     * @param val UUID in string format.
+     */
+    private static UUID uuidFromString(String val) {
+        try {
+            return UUID.fromString(val);
+        }
+        catch (RuntimeException e) {
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6263dbe6/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlSystemViewsSelfTest.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlSystemViewsSelfTest.java
 
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlSystemViewsSelfTest.java
new file mode 100644
index 0000000..9c7ce38
--- /dev/null
+++ 
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlSystemViewsSelfTest.java
@@ -0,0 +1,247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+/**
+ * Tests for ignite SQL meta views.
+ */
+public class SqlSystemViewsSelfTest extends GridCommonAbstractTest {
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        stopAllGrids();
+    }
+
+    /**
+     * @param ignite Ignite.
+     * @param sql Sql.
+     * @param args Args.
+     */
+    @SuppressWarnings("unchecked")
+    private List<List<?>> execSql(Ignite ignite, String sql, Object ... args) {
+        IgniteCache cache = ignite.getOrCreateCache(DEFAULT_CACHE_NAME);
+
+        SqlFieldsQuery qry = new SqlFieldsQuery(sql);
+
+        if (args != null && args.length > 0)
+            qry.setArgs(args);
+
+        return cache.query(qry).getAll();
+    }
+
+    /**
+     * @param sql Sql.
+     * @param args Args.
+     */
+    private List<List<?>> execSql(String sql, Object ... args) {
+        return execSql(grid(), sql, args);
+    }
+
+    /**
+     * @param sql Sql.
+     */
+    private void assertSqlError(final String sql) {
+        Throwable t = GridTestUtils.assertThrowsWithCause(new Callable<Void>() 
{
+            @Override public Void call() throws Exception {
+                execSql(sql);
+
+                return null;
+            }
+        }, IgniteSQLException.class);
+
+        IgniteSQLException sqlE = X.cause(t, IgniteSQLException.class);
+
+        assert sqlE != null;
+
+        assertEquals(IgniteQueryErrorCode.UNSUPPORTED_OPERATION, 
sqlE.statusCode());
+    }
+    
+    /**
+     * Test meta views modifications.
+     */
+    public void testModifications() throws Exception {
+        startGrid();
+
+        assertSqlError("DROP TABLE IGNITE.NODES");
+
+        assertSqlError("TRUNCATE TABLE IGNITE.NODES");
+
+        assertSqlError("ALTER TABLE IGNITE.NODES RENAME TO IGNITE.N");
+
+        assertSqlError("ALTER TABLE IGNITE.NODES ADD COLUMN C VARCHAR");
+
+        assertSqlError("ALTER TABLE IGNITE.NODES DROP COLUMN ID");
+
+        assertSqlError("ALTER TABLE IGNITE.NODES RENAME COLUMN ID TO C");
+
+        assertSqlError("CREATE INDEX IDX ON IGNITE.NODES(ID)");
+
+        assertSqlError("INSERT INTO IGNITE.NODES (ID) VALUES ('-')");
+
+        assertSqlError("UPDATE IGNITE.NODES SET ID = '-'");
+
+        assertSqlError("DELETE IGNITE.NODES");
+    }
+
+    /**
+     * Test different query modes.
+     */
+    public void testQueryModes() throws Exception {
+        Ignite ignite = startGrid(0);
+        startGrid(1);
+
+        UUID nodeId = ignite.cluster().localNode().id();
+
+        IgniteCache cache = ignite.getOrCreateCache(DEFAULT_CACHE_NAME);
+
+        String sql = "SELECT ID FROM IGNITE.NODES WHERE IS_LOCAL = true";
+
+        SqlFieldsQuery qry;
+
+        qry = new SqlFieldsQuery(sql).setDistributedJoins(true);
+
+        assertEquals(nodeId, 
((List<?>)cache.query(qry).getAll().get(0)).get(0));
+
+        qry = new SqlFieldsQuery(sql).setReplicatedOnly(true);
+
+        assertEquals(nodeId, 
((List<?>)cache.query(qry).getAll().get(0)).get(0));
+
+        qry = new SqlFieldsQuery(sql).setLocal(true);
+
+        assertEquals(nodeId, 
((List<?>)cache.query(qry).getAll().get(0)).get(0));
+    }
+
+    /**
+     * Test that we can't use cache tables and meta views in the same query.
+     */
+    public void testCacheToViewJoin() throws Exception {
+        Ignite ignite = startGrid();
+
+        ignite.createCache(new 
CacheConfiguration<>().setName(DEFAULT_CACHE_NAME).setQueryEntities(
+            Collections.singleton(new QueryEntity(Integer.class.getName(), 
String.class.getName()))));
+
+        assertSqlError("SELECT * FROM \"" + DEFAULT_CACHE_NAME + "\".String 
JOIN IGNITE.NODES ON 1=1");
+    }
+
+    /**
+     * @param rowData Row data.
+     * @param colTypes Column types.
+     */
+    private void assertColumnTypes(List<?> rowData, Class<?> ... colTypes) {
+        for (int i = 0; i < colTypes.length; i++) {
+            if (rowData.get(i) != null)
+                assertEquals("Column " + i + " type", 
rowData.get(i).getClass(), colTypes[i]);
+        }
+    }
+
+    /**
+     * Test nodes meta view.
+     *
+     * @throws Exception If failed.
+     */
+    public void testNodesViews() throws Exception {
+        Ignite ignite1 = startGrid(getTestIgniteInstanceName(), 
getConfiguration());
+        Ignite ignite2 = startGrid(getTestIgniteInstanceName(1), 
getConfiguration().setClientMode(true));
+        Ignite ignite3 = startGrid(getTestIgniteInstanceName(2), 
getConfiguration().setDaemon(true));
+
+        awaitPartitionMapExchange();
+
+        List<List<?>> resAll = execSql("SELECT ID, CONSISTENT_ID, VERSION, 
IS_LOCAL, IS_CLIENT, IS_DAEMON, " +
+                "NODE_ORDER, ADDRESSES, HOSTNAMES FROM IGNITE.NODES");
+
+        assertColumnTypes(resAll.get(0), UUID.class, String.class, 
String.class, Boolean.class, Boolean.class,
+            Boolean.class, Integer.class, String.class, String.class);
+
+        assertEquals(3, resAll.size());
+
+        List<List<?>> resSrv = execSql(
+            "SELECT ID, IS_LOCAL, NODE_ORDER FROM IGNITE.NODES WHERE IS_CLIENT 
= FALSE AND IS_DAEMON = FALSE"
+        );
+
+        assertEquals(1, resSrv.size());
+
+        assertEquals(ignite1.cluster().localNode().id(), resSrv.get(0).get(0));
+
+        assertEquals(true, resSrv.get(0).get(1));
+
+        assertEquals(1, resSrv.get(0).get(2));
+
+        List<List<?>> resCli = execSql(
+            "SELECT ID, IS_LOCAL, NODE_ORDER FROM IGNITE.NODES WHERE IS_CLIENT 
= TRUE");
+
+        assertEquals(1, resCli.size());
+
+        assertEquals(ignite2.cluster().localNode().id(), resCli.get(0).get(0));
+
+        assertEquals(false, resCli.get(0).get(1));
+
+        assertEquals(2, resCli.get(0).get(2));
+
+        List<List<?>> resDaemon = execSql(
+            "SELECT ID, IS_LOCAL, NODE_ORDER FROM IGNITE.NODES WHERE IS_DAEMON 
= TRUE");
+
+        assertEquals(1, resDaemon.size());
+
+        assertEquals(ignite3.cluster().localNode().id(), 
resDaemon.get(0).get(0));
+
+        assertEquals(false, resDaemon.get(0).get(1));
+
+        assertEquals(3, resDaemon.get(0).get(2));
+
+        // Check index on ID column.
+        assertEquals(0, execSql("SELECT ID FROM IGNITE.NODES WHERE ID = 
'-'").size());
+
+        assertEquals(1, execSql("SELECT ID FROM IGNITE.NODES WHERE ID = ?",
+            ignite1.cluster().localNode().id()).size());
+
+        assertEquals(1, execSql("SELECT ID FROM IGNITE.NODES WHERE ID = ?",
+            ignite3.cluster().localNode().id()).size());
+
+        // Check index on IS_LOCAL column.
+        assertEquals(1, execSql("SELECT ID FROM IGNITE.NODES WHERE IS_LOCAL = 
true").size());
+
+        // Check index on IS_LOCAL column with disjunction.
+        assertEquals(3, execSql("SELECT ID FROM IGNITE.NODES WHERE IS_LOCAL = 
true OR node_order=1 OR node_order=2 OR node_order=3").size());
+
+        // Check quick-count.
+        assertEquals(3L, execSql("SELECT COUNT(*) FROM 
IGNITE.NODES").get(0).get(0));
+
+        // Check joins
+        assertEquals(ignite1.cluster().localNode().id(), execSql("SELECT N1.ID 
FROM IGNITE.NODES N1 JOIN " +
+            "IGNITE.NODES N2 ON N1.IS_LOCAL = N2.IS_LOCAL JOIN IGNITE.NODES N3 
ON N2.ID = N3.ID WHERE N3.IS_LOCAL = true")
+            .get(0).get(0));
+
+        // Check sub-query
+        assertEquals(ignite1.cluster().localNode().id(), execSql("SELECT N1.ID 
FROM IGNITE.NODES N1 " +
+            "WHERE NOT EXISTS (SELECT 1 FROM IGNITE.NODES N2 WHERE N2.ID = 
N1.ID AND N2.IS_LOCAL = false)")
+            .get(0).get(0));
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6263dbe6/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
 
b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
index b4b7e91..39194d7 100644
--- 
a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
+++ 
b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
@@ -161,6 +161,7 @@ import 
org.apache.ignite.internal.processors.query.IgniteSqlSkipReducerOnUpdateD
 import org.apache.ignite.internal.processors.query.IgniteSqlSplitterSelfTest;
 import org.apache.ignite.internal.processors.query.LazyQuerySelfTest;
 import 
org.apache.ignite.internal.processors.query.MultipleStatementsSqlQuerySelfTest;
+import org.apache.ignite.internal.processors.query.SqlSystemViewsSelfTest;
 import org.apache.ignite.internal.processors.query.SqlPushDownFunctionTest;
 import org.apache.ignite.internal.processors.query.SqlSchemaSelfTest;
 import 
org.apache.ignite.internal.processors.query.h2.GridH2IndexingInMemSelfTest;
@@ -405,6 +406,7 @@ public class IgniteCacheQuerySelfTestSuite extends 
TestSuite {
         suite.addTestSuite(H2ConnectionLeaksSelfTest.class);
         
suite.addTestSuite(IgniteCheckClusterStateBeforeExecuteQueryTest.class);
         suite.addTestSuite(OptimizedMarshallerIndexNameTest.class);
+        suite.addTestSuite(SqlSystemViewsSelfTest.class);
 
         suite.addTestSuite(IgniteSqlDefaultValueTest.class);
         suite.addTestSuite(IgniteDecimalSelfTest.class);

Reply via email to