IGNITE-4830 Implemented better SQL errors handling.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/fbb99408 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/fbb99408 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/fbb99408 Branch: refs/heads/ignite-3477-master Commit: fbb994081f13091b1442b0f882312341471dcc5a Parents: 83579ce Author: Alexey Kuznetsov <[email protected]> Authored: Tue Mar 21 10:20:39 2017 +0700 Committer: Alexey Kuznetsov <[email protected]> Committed: Tue Mar 21 10:20:39 2017 +0700 ---------------------------------------------------------------------- .../frontend/app/modules/sql/sql.controller.js | 84 +++++++++++++++----- .../web-console/frontend/views/sql/sql.tpl.pug | 9 ++- .../frontend/views/templates/message.tpl.pug | 4 +- 3 files changed, 71 insertions(+), 26 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/fbb99408/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 7ded2d5..a3a5a5f 100644 --- a/modules/web-console/frontend/app/modules/sql/sql.controller.js +++ b/modules/web-console/frontend/app/modules/sql/sql.controller.js @@ -18,7 +18,7 @@ import paragraphRateTemplateUrl from 'views/sql/paragraph-rate.tpl.pug'; import cacheMetadataTemplateUrl from 'views/sql/cache-metadata.tpl.pug'; import chartSettingsTemplateUrl from 'views/sql/chart-settings.tpl.pug'; -import showQueryTemplateUrl from 'views/templates/message.tpl.pug'; +import messageTemplateUrl from 'views/templates/message.tpl.pug'; // Time line X axis descriptor. const TIME_LINE = {value: -1, type: 'java.sql.Date', label: 'TIME_LINE'}; @@ -53,7 +53,7 @@ const _fullColName = (col) => { let paragraphId = 0; class Paragraph { - constructor($animate, $timeout, paragraph) { + constructor($animate, $timeout, JavaTypes, paragraph) { const self = this; self.id = 'paragraph-' + paragraphId++; @@ -140,13 +140,36 @@ class Paragraph { }}); Object.defineProperty(this, 'chartHistory', {value: []}); + + Object.defineProperty(this, 'error', {value: { + root: {}, + message: '' + }}); + + this.setError = (err) => { + this.error.root = err; + this.error.message = err.message; + + let cause = err; + + while (_.nonNil(cause)) { + if (_.nonEmpty(cause.className) && + _.includes(['SQLException', 'JdbcSQLException'], JavaTypes.shortClassName(cause.className))) { + this.error.message = cause.message; + + break; + } + + cause = cause.cause; + } + }; } resultType() { if (_.isNil(this.queryArgs)) return null; - if (!_.isEmpty(this.errMsg)) + if (_.nonEmpty(this.error.message)) return 'error'; if (_.isEmpty(this.rows)) @@ -172,7 +195,7 @@ class Paragraph { } queryExecuted() { - return !_.isEmpty(this.meta) || !_.isEmpty(this.errMsg); + return _.nonEmpty(this.meta) || _.nonEmpty(this.error.message); } scanExplain() { @@ -184,17 +207,17 @@ class Paragraph { } chartColumnsConfigured() { - return !_.isEmpty(this.chartKeyCols) && !_.isEmpty(this.chartValCols); + return _.nonEmpty(this.chartKeyCols) && _.nonEmpty(this.chartValCols); } chartTimeLineEnabled() { - return !_.isEmpty(this.chartKeyCols) && _.eq(this.chartKeyCols[0], TIME_LINE); + return _.nonEmpty(this.chartKeyCols) && _.eq(this.chartKeyCols[0], TIME_LINE); } } // Controller for SQL notebook screen. -export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', '$animate', '$location', '$anchorScroll', '$state', '$filter', '$modal', '$popover', 'IgniteLoading', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteAgentMonitor', 'IgniteChartColors', 'IgniteNotebook', 'IgniteNodes', 'uiGridExporterConstants', 'IgniteVersion', 'IgniteActivitiesData', - function($root, $scope, $http, $q, $timeout, $interval, $animate, $location, $anchorScroll, $state, $filter, $modal, $popover, Loading, LegacyUtils, Messages, Confirm, agentMonitor, IgniteChartColors, Notebook, Nodes, uiGridExporterConstants, Version, ActivitiesData) { +export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', '$animate', '$location', '$anchorScroll', '$state', '$filter', '$modal', '$popover', 'IgniteLoading', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteAgentMonitor', 'IgniteChartColors', 'IgniteNotebook', 'IgniteNodes', 'uiGridExporterConstants', 'IgniteVersion', 'IgniteActivitiesData', 'JavaTypes', + function($root, $scope, $http, $q, $timeout, $interval, $animate, $location, $anchorScroll, $state, $filter, $modal, $popover, Loading, LegacyUtils, Messages, Confirm, agentMonitor, IgniteChartColors, Notebook, Nodes, uiGridExporterConstants, Version, ActivitiesData, JavaTypes) { const $ctrl = this; // Define template urls. @@ -909,7 +932,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', $scope.notebook.paragraphs = []; $scope.notebook.paragraphs = _.map($scope.notebook.paragraphs, - (paragraph) => new Paragraph($animate, $timeout, paragraph)); + (paragraph) => new Paragraph($animate, $timeout, JavaTypes, paragraph)); if (_.isEmpty($scope.notebook.paragraphs)) $scope.addQuery(); @@ -981,7 +1004,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', ActivitiesData.post({ action: '/queries/add/query' }); - const paragraph = new Paragraph($animate, $timeout, { + const paragraph = new Paragraph($animate, $timeout, JavaTypes, { name: 'Query' + (sz === 0 ? '' : sz), query: '', pageSize: $scope.pageSizes[1], @@ -1009,7 +1032,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', ActivitiesData.post({ action: '/queries/add/scan' }); - const paragraph = new Paragraph($animate, $timeout, { + const paragraph = new Paragraph($animate, $timeout, JavaTypes, { name: 'Scan' + (sz === 0 ? '' : sz), query: '', pageSize: $scope.pageSizes[1], @@ -1246,7 +1269,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', paragraph.resNodeId = res.responseNodeId; - delete paragraph.errMsg; + paragraph.setError({message: ''}); // Prepare explain results for display in table. if (paragraph.queryArgs.query && paragraph.queryArgs.query.startsWith('EXPLAIN') && res.rows) { @@ -1341,7 +1364,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', .then((nid) => agentMonitor.query(nid, args.cacheName, args.query, args.nonCollocatedJoins, args.enforceJoinOrder, !!args.localNid, args.pageSize)) .then(_processQueryResult.bind(this, paragraph, false)) - .catch((err) => paragraph.errMsg = err.message); + .catch((err) => paragraph.setError(err)); }; const _tryStartRefresh = function(paragraph) { @@ -1419,7 +1442,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', _tryStartRefresh(paragraph); }) .catch((err) => { - paragraph.errMsg = err.message; + paragraph.setError(err); _showLoading(paragraph, false); @@ -1468,7 +1491,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', }) .then(_processQueryResult.bind(this, paragraph, true)) .catch((err) => { - paragraph.errMsg = err.message; + paragraph.setError(err); _showLoading(paragraph, false); }) @@ -1506,7 +1529,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', }) .then((res) => _processQueryResult(paragraph, true, res)) .catch((err) => { - paragraph.errMsg = err.message; + paragraph.setError(err); _showLoading(paragraph, false); }); @@ -1560,7 +1583,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', delete paragraph.queryId; }) .catch((err) => { - paragraph.errMsg = err.message; + paragraph.setError(err); _showLoading(paragraph, false); }) @@ -1756,15 +1779,36 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', } else if (paragraph.queryArgs.query .startsWith('EXPLAIN ')) { scope.title = 'Explain query'; - scope.content = [paragraph.queryArgs.query]; + scope.content = paragraph.queryArgs.query.split(/\r?\n/); } else { scope.title = 'SQL query'; - scope.content = [paragraph.queryArgs.query]; + scope.content = paragraph.queryArgs.query.split(/\r?\n/); + } + + // Show a basic modal from a controller + $modal({scope, templateUrl: messageTemplateUrl, placement: 'center', show: true}); + } + }; + + $scope.showStackTrace = function(paragraph) { + if (!_.isNil(paragraph)) { + const scope = $scope.$new(); + + scope.title = 'Error details'; + scope.content = []; + + let cause = paragraph.error.root; + + while (_.nonNil(cause)) { + scope.content.push((scope.content.length > 0 ? ' ' : '') + + '[' + JavaTypes.shortClassName(cause.className) + '] ' + cause.message); + + cause = cause.cause; } // Show a basic modal from a controller - $modal({scope, templateUrl: showQueryTemplateUrl, placement: 'center', show: true}); + $modal({scope, templateUrl: messageTemplateUrl, placement: 'center', show: true}); } }; } http://git-wip-us.apache.org/repos/asf/ignite/blob/fbb99408/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 bc134a6..701ee14 100644 --- a/modules/web-console/frontend/views/sql/sql.tpl.pug +++ b/modules/web-console/frontend/views/sql/sql.tpl.pug @@ -199,7 +199,7 @@ mixin paragraph-scan | Scan on selected node .col-sm-12.sql-result(ng-if='paragraph.queryExecuted()' ng-switch='paragraph.resultType()') - .error(ng-switch-when='error') Error: {{paragraph.errMsg}} + .error(ng-switch-when='error') Error: {{paragraph.error.message}} .empty(ng-switch-when='empty') Result set is empty .table(ng-switch-when='table') +table-result-heading-scan @@ -247,7 +247,7 @@ mixin paragraph-query .pull-right +query-settings .col-sm-12.sql-result(ng-if='paragraph.queryExecuted()' ng-switch='paragraph.resultType()') - .error(ng-switch-when='error') Error: {{paragraph.errMsg}} + .error(ng-switch-when='error') Error: {{paragraph.error.message}} .empty(ng-switch-when='empty') Result set is empty .table(ng-switch-when='table') +table-result-heading-query @@ -255,9 +255,10 @@ mixin paragraph-query .chart(ng-switch-when='chart') +chart-result .footer.clearfix - a.pull-left(ng-click='showResultQuery(paragraph)') Show query + a.pull-left(ng-show='paragraph.resultType() === "error"' ng-click='showStackTrace(paragraph)') Show error details + a.pull-left(ng-show='paragraph.resultType() !== "error"' ng-click='showResultQuery(paragraph)') Show query - -var nextVisibleCondition = 'paragraph.resultType() != "error" && paragraph.queryId && paragraph.nonRefresh() && (paragraph.table() || paragraph.chart() && !paragraph.scanExplain())' + -var nextVisibleCondition = 'paragraph.resultType() !== "error" && paragraph.queryId && paragraph.nonRefresh() && (paragraph.table() || paragraph.chart() && !paragraph.scanExplain())' .pull-right(ng-show=`${nextVisibleCondition}` ng-class='{disabled: paragraph.loading}' ng-click='!paragraph.loading && nextPage(paragraph)') i.fa.fa-chevron-circle-right http://git-wip-us.apache.org/repos/asf/ignite/blob/fbb99408/modules/web-console/frontend/views/templates/message.tpl.pug ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/views/templates/message.tpl.pug b/modules/web-console/frontend/views/templates/message.tpl.pug index 6eff74b..aa3615f 100644 --- a/modules/web-console/frontend/views/templates/message.tpl.pug +++ b/modules/web-console/frontend/views/templates/message.tpl.pug @@ -22,7 +22,7 @@ h4.modal-title i.fa.fa-info-circle | {{title}} - .modal-body(ng-show='content') - p(ng-bind-html='content.join("<br/>")' style='text-align: left;') + .modal-body(ng-show='content' style='overflow: auto; max-height: 300px;') + p(ng-bind-html='content.join("<br/>")' style='text-align: left; white-space: nowrap;') .modal-footer button.btn.btn-primary(id='confirm-btn-confirm' ng-click='$hide()') Ok
