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);
