IGNITE-6287 Web Console: Improved DDL support.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/2410f079 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/2410f079 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/2410f079 Branch: refs/heads/ignite-6305 Commit: 2410f0792fec33725f1b7f74b5b576b353b8fe55 Parents: 49b8358 Author: Vasiliy Sisko <[email protected]> Authored: Fri Oct 6 14:25:42 2017 +0700 Committer: Alexey Kuznetsov <[email protected]> Committed: Fri Oct 6 14:25:42 2017 +0700 ---------------------------------------------------------------------- .../handlers/cache/GridCacheCommandHandler.java | 12 +++++-- .../internal/visor/query/VisorQueryTask.java | 36 ++++++++++++++++++-- .../app/modules/agent/AgentManager.service.js | 7 ++-- .../frontend/app/modules/sql/sql.controller.js | 36 ++++++++++++++------ .../web-console/frontend/views/sql/sql.tpl.pug | 14 ++++---- 5 files changed, 79 insertions(+), 26 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/2410f079/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/cache/GridCacheCommandHandler.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/cache/GridCacheCommandHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/cache/GridCacheCommandHandler.java index 53342c9..d627b20 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/cache/GridCacheCommandHandler.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/cache/GridCacheCommandHandler.java @@ -56,6 +56,7 @@ import org.apache.ignite.internal.processors.cache.CacheInvokeEntry; import org.apache.ignite.internal.processors.cache.GridCacheAdapter; import org.apache.ignite.internal.processors.cache.GridCacheEntryEx; import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException; +import org.apache.ignite.internal.processors.cache.IgniteCacheProxy; import org.apache.ignite.internal.processors.cache.IgniteInternalCache; import org.apache.ignite.internal.processors.cache.query.GridCacheSqlMetadata; import org.apache.ignite.internal.processors.rest.GridRestCommand; @@ -1078,7 +1079,11 @@ public class GridCacheCommandHandler extends GridRestCommandHandlerAdapter { /** {@inheritDoc} */ @Override public Collection<GridCacheSqlMetadata> execute() { String cacheName = null; - IgniteInternalCache<?, ?> cache; + + if (!ignite.active()) + return Collections.emptyList(); + + IgniteInternalCache<?, ?> cache = null; if (!F.isEmpty(arguments())) { cacheName = argument(0); @@ -1088,7 +1093,10 @@ public class GridCacheCommandHandler extends GridRestCommandHandlerAdapter { assert cache != null; } else { - cache = F.first(ignite.context().cache().publicCaches()).internalProxy(); + IgniteCacheProxy<?, ?> pubCache = F.first(ignite.context().cache().publicCaches()); + + if (pubCache != null) + cache = pubCache.internalProxy(); if (cache == null) return Collections.emptyList(); http://git-wip-us.apache.org/repos/asf/ignite/blob/2410f079/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryTask.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryTask.java index c85ceea..a3668c8 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryTask.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/query/VisorQueryTask.java @@ -22,10 +22,15 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.UUID; +import javax.cache.CacheException; import org.apache.ignite.IgniteCache; +import org.apache.ignite.cache.query.FieldsQueryCursor; import org.apache.ignite.cache.query.SqlFieldsQuery; import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata; +import org.apache.ignite.internal.processors.query.IgniteSQLException; import org.apache.ignite.internal.processors.task.GridInternal; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.internal.visor.VisorEither; @@ -70,7 +75,6 @@ public class VisorQueryTask extends VisorOneNodeTask<VisorQueryTaskArg, VisorEit /** {@inheritDoc} */ @Override protected VisorEither<VisorQueryResult> run(final VisorQueryTaskArg arg) { try { - IgniteCache<Object, Object> c = ignite.cache(arg.getCacheName()); UUID nid = ignite.localNode().id(); SqlFieldsQuery qry = new SqlFieldsQuery(arg.getQueryText()); @@ -83,7 +87,35 @@ public class VisorQueryTask extends VisorOneNodeTask<VisorQueryTaskArg, VisorEit long start = U.currentTimeMillis(); - VisorQueryCursor<List<?>> cur = new VisorQueryCursor<>(c.withKeepBinary().query(qry)); + FieldsQueryCursor<List<?>> qryCursor; + + String cacheName = arg.getCacheName(); + + if (F.isEmpty(cacheName)) + qryCursor = ignite.context().query().querySqlFieldsNoCache(qry, true); + else { + IgniteCache<Object, Object> c = ignite.cache(cacheName); + + if (c == null) + throw new SQLException("Fail to execute query. Cache not found: " + cacheName); + + try { + qryCursor = c.withKeepBinary().query(qry); + } + catch (CacheException e) { + // Work around for DDL without explicit schema name. + if (X.hasCause(e, IgniteSQLException.class) + && e.getMessage().contains("can only be executed on PUBLIC schema")) { + qry.setSchema("PUBLIC"); + + qryCursor = c.withKeepBinary().query(qry); + } + else + throw e; + } + } + + VisorQueryCursor<List<?>> cur = new VisorQueryCursor<>(qryCursor); Collection<GridQueryFieldMetadata> meta = cur.fieldsMeta(); http://git-wip-us.apache.org/repos/asf/ignite/blob/2410f079/modules/web-console/frontend/app/modules/agent/AgentManager.service.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/modules/agent/AgentManager.service.js b/modules/web-console/frontend/app/modules/agent/AgentManager.service.js index 20d2976..288ec94 100644 --- a/modules/web-console/frontend/app/modules/agent/AgentManager.service.js +++ b/modules/web-console/frontend/app/modules/agent/AgentManager.service.js @@ -377,11 +377,10 @@ export default class IgniteAgentManager { } /** - * @param {String} [cacheName] Cache name. * @returns {Promise} */ - metadata(cacheName) { - return this._rest('node:rest', {cmd: 'metadata', cacheName: maskNull(cacheName)}) + metadata() { + return this._rest('node:rest', {cmd: 'metadata'}) .then((caches) => { let types = []; @@ -590,7 +589,7 @@ export default class IgniteAgentManager { nid + '=' + queryId); } - return this.visorTask('queryClose', nid, queryId); + return this.visorTask('queryClose', nid, nid, queryId); } /** http://git-wip-us.apache.org/repos/asf/ignite/blob/2410f079/modules/web-console/frontend/app/modules/sql/sql.controller.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/modules/sql/sql.controller.js b/modules/web-console/frontend/app/modules/sql/sql.controller.js index 5f06c1e..8011b0f 100644 --- a/modules/web-console/frontend/app/modules/sql/sql.controller.js +++ b/modules/web-console/frontend/app/modules/sql/sql.controller.js @@ -863,9 +863,6 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', return cachesAcc; }, []), 'label'); - if (_.isEmpty($scope.caches)) - return; - // Reset to first cache in case of stopped selected. const cacheNames = _.map($scope.caches, (cache) => cache.value); @@ -1313,6 +1310,9 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', * @return {String} Nid */ const _chooseNode = (name, local) => { + if (_.isEmpty(name)) + return Promise.resolve(null); + const nodes = cacheNodes(name); if (local) { @@ -1386,7 +1386,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', const enforceJoinOrder = !!paragraph.enforceJoinOrder; const lazy = !!paragraph.lazy; - $scope.actionAvailable(paragraph, true) && _chooseNode(paragraph.cacheName, local) + $scope.queryAvailable(paragraph) && _chooseNode(paragraph.cacheName, local) .then((nid) => { Notebook.save($scope.notebook) .catch(Messages.showError); @@ -1444,7 +1444,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', }; $scope.explain = (paragraph) => { - if (!$scope.actionAvailable(paragraph, true)) + if (!$scope.queryAvailable(paragraph)) return; Notebook.save($scope.notebook) @@ -1483,7 +1483,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', const filter = paragraph.filter; const pageSize = paragraph.pageSize; - $scope.actionAvailable(paragraph, false) && _chooseNode(cacheName, local) + $scope.scanAvailable(paragraph) && _chooseNode(cacheName, local) .then((nid) => { Notebook.save($scope.notebook) .catch(Messages.showError); @@ -1689,18 +1689,32 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', _chartApplySettings(paragraph, true); }; - $scope.actionAvailable = function(paragraph, needQuery) { - return $scope.caches.length > 0 && (!needQuery || paragraph.query) && !paragraph.loading; + $scope.queryAvailable = function(paragraph) { + return paragraph.query && !paragraph.loading; + }; + + $scope.queryTooltip = function(paragraph, action) { + if ($scope.queryAvailable(paragraph)) + return; + + if (paragraph.loading) + return 'Waiting for server response'; + + return 'Input text to ' + action; + }; + + $scope.scanAvailable = function(paragraph) { + return $scope.caches.length && !paragraph.loading; }; - $scope.actionTooltip = function(paragraph, action, needQuery) { - if ($scope.actionAvailable(paragraph, needQuery)) + $scope.scanTooltip = function(paragraph) { + if ($scope.scanAvailable(paragraph)) return; if (paragraph.loading) return 'Waiting for server response'; - return 'To ' + action + ' query select cache' + (needQuery ? ' and input query' : ''); + return 'Select cache to export scan results'; }; $scope.clickableMetadata = function(node) { http://git-wip-us.apache.org/repos/asf/ignite/blob/2410f079/modules/web-console/frontend/views/sql/sql.tpl.pug ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/views/sql/sql.tpl.pug b/modules/web-console/frontend/views/sql/sql.tpl.pug index 1ef2a4c..724c53c 100644 --- a/modules/web-console/frontend/views/sql/sql.tpl.pug +++ b/modules/web-console/frontend/views/sql/sql.tpl.pug @@ -123,10 +123,10 @@ mixin query-settings span Lazy result set mixin query-actions - button.btn.btn-primary(ng-disabled='!actionAvailable(paragraph, true)' ng-click='execute(paragraph)') Execute - button.btn.btn-primary(ng-disabled='!actionAvailable(paragraph, true)' ng-click='execute(paragraph, true)') Execute on selected node + button.btn.btn-primary(ng-disabled='!queryAvailable(paragraph)' ng-click='execute(paragraph)') Execute + button.btn.btn-primary(ng-disabled='!queryAvailable(paragraph)' ng-click='execute(paragraph, true)') Execute on selected node - a.btn.btn-default(ng-disabled='!actionAvailable(paragraph, true)' ng-click='explain(paragraph)' data-placement='bottom' bs-tooltip='' data-title='{{actionTooltip(paragraph, "explain", true)}}') Explain + a.btn.btn-default(ng-disabled='!queryAvailable(paragraph)' ng-click='explain(paragraph)' data-placement='bottom' bs-tooltip='' data-title='{{queryTooltip(paragraph, "explain query")}}') Explain mixin table-result-heading-query .total.row @@ -142,7 +142,7 @@ mixin table-result-heading-query .col-xs-4 .pull-right -var options = [{ text: "Export", click: 'exportCsv(paragraph)' }, { text: 'Export all', click: 'exportCsvAll(paragraph)' }] - +btn-group('paragraph.loading', options, '{{ actionTooltip(paragraph, "export", false) }}') + +btn-group('paragraph.loading', options, '{{ queryTooltip(paragraph, "export query results") }}') mixin table-result-heading-scan .total.row @@ -158,7 +158,7 @@ mixin table-result-heading-scan .col-xs-4 .pull-right -var options = [{ text: "Export", click: 'exportCsv(paragraph)' }, { text: 'Export all', click: 'exportCsvAll(paragraph)' }] - +btn-group('paragraph.loading', options, '{{ actionTooltip(paragraph, "export", false) }}') + +btn-group('paragraph.loading', options, '{{ scanTooltip(paragraph) }}') mixin table-result-body .grid(ui-grid='paragraph.gridOptions' ui-grid-resize-columns ui-grid-exporter) @@ -196,9 +196,9 @@ mixin paragraph-scan button.btn.btn-default.select-toggle.tipLabel(ng-model='paragraph.pageSize' bs-select bs-options='item for item in pageSizes') .col-sm-12.sql-controls - button.btn.btn-primary(ng-disabled='!actionAvailable(paragraph, false)' ng-click='scan(paragraph)') + button.btn.btn-primary(ng-disabled='!scanAvailable(paragraph)' ng-click='scan(paragraph)') | Scan - button.btn.btn-primary(ng-disabled='!actionAvailable(paragraph, false)' ng-click='scan(paragraph, true)') + button.btn.btn-primary(ng-disabled='!scanAvailable(paragraph)' ng-click='scan(paragraph, true)') | Scan on selected node .col-sm-12.sql-result(ng-if='paragraph.queryExecuted()' ng-switch='paragraph.resultType()')
