diff --git a/core/trino-main/src/main/java/io/trino/execution/SqlStage.java b/core/trino-main/src/main/java/io/trino/execution/SqlStage.java index 128d52e10a03f..c553e29e38a4d 100644 --- a/core/trino-main/src/main/java/io/trino/execution/SqlStage.java +++ b/core/trino-main/src/main/java/io/trino/execution/SqlStage.java @@ -254,9 +254,9 @@ public void recordGetSplitTime(long start) stateMachine.recordGetSplitTime(start); } - public void updateConnectorMetrics(Metrics newConnectorMetrics) + public void updateConnectorMetrics(Metrics newConnectorMetrics, PlanNodeId planNodeId) { - stateMachine.updateConnectorMetrics(newConnectorMetrics); + stateMachine.updateConnectorMetrics(newConnectorMetrics, planNodeId); } private synchronized void updateTaskStatus(TaskStatus status) diff --git a/core/trino-main/src/main/java/io/trino/execution/StageStateMachine.java b/core/trino-main/src/main/java/io/trino/execution/StageStateMachine.java index 6a3263eb09e31..b5beb9e237aff 100644 --- a/core/trino-main/src/main/java/io/trino/execution/StageStateMachine.java +++ b/core/trino-main/src/main/java/io/trino/execution/StageStateMachine.java @@ -23,10 +23,14 @@ import io.trino.operator.BlockedReason; import io.trino.operator.OperatorStats; import io.trino.operator.PipelineStats; +import io.trino.operator.ScanFilterAndProjectOperator; import io.trino.operator.TaskStats; import io.trino.spi.eventlistener.StageGcStatistics; +import io.trino.spi.metrics.DataSkippingMetrics; import io.trino.spi.metrics.Metrics; +import io.trino.spi.metrics.TableLevelDataSkippingMetrics; import io.trino.sql.planner.PlanFragment; +import io.trino.sql.planner.plan.PlanNode; import io.trino.sql.planner.plan.PlanNodeId; import io.trino.sql.planner.plan.TableScanNode; import io.trino.util.Failures; @@ -41,6 +45,8 @@ import java.util.Optional; import java.util.OptionalDouble; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; @@ -50,6 +56,7 @@ import static com.google.common.base.MoreObjects.toStringHelper; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.ImmutableMap.toImmutableMap; import static io.airlift.units.DataSize.succinctBytes; import static io.airlift.units.Duration.succinctDuration; import static io.trino.execution.StageState.ABORTED; @@ -60,6 +67,7 @@ import static io.trino.execution.StageState.RUNNING; import static io.trino.execution.StageState.SCHEDULING; import static io.trino.execution.StageState.TERMINAL_STAGE_STATES; +import static io.trino.sql.planner.optimizations.PlanNodeSearcher.searchFrom; import static java.lang.Math.max; import static java.lang.Math.min; import static java.lang.Math.toIntExact; @@ -71,6 +79,7 @@ public class StageStateMachine { private static final Logger log = Logger.get(StageStateMachine.class); + private static final String TABLE_LEVEL_METRICS_SUFFIX = "_table_level"; private final StageId stageId; private final PlanFragment fragment; @@ -91,6 +100,7 @@ public class StageStateMachine private final AtomicLong currentTotalMemory = new AtomicLong(); private final AtomicReference connectorMetrics = new AtomicReference<>(Metrics.EMPTY); + private final ConcurrentMap projectOrFilterNodeToTableScan = new ConcurrentHashMap<>(); public StageStateMachine( StageId stageId, @@ -221,9 +231,27 @@ public void updateMemoryUsage(long deltaUserMemoryInBytes, long deltaRevocableMe peakRevocableMemory.updateAndGet(currentPeakValue -> max(currentRevocableMemory.get(), currentPeakValue)); } - public void updateConnectorMetrics(Metrics newConnectorMetrics) + public void updateConnectorMetrics(Metrics newConnectorMetrics, PlanNodeId planNodeId) { connectorMetrics.updateAndGet(metrics -> metrics.mergeWith(newConnectorMetrics)); + if (isValidTableScanNodeId(planNodeId)) { + connectorMetrics.updateAndGet(metrics -> metrics.mergeWith(processTableLevelMetrics(newConnectorMetrics, planNodeId))); + } + } + + private boolean isValidTableScanNodeId(PlanNodeId planNodeId) + { + return planNodeId != null && tables.containsKey(planNodeId); + } + + private Metrics processTableLevelMetrics(Metrics metrics, PlanNodeId planNodeId) + { + String table = stageId.getId() + ":" + planNodeId + ":" + tables.get(planNodeId).getTableName().asSchemaTableName().toString(); + return new Metrics(metrics.getMetrics().entrySet().stream() + .filter(entry -> entry.getValue() instanceof DataSkippingMetrics) + .collect(toImmutableMap( + entry -> entry.getKey() + TABLE_LEVEL_METRICS_SUFFIX, + entry -> new TableLevelDataSkippingMetrics(ImmutableMap.of(table, (DataSkippingMetrics) entry.getValue()))))); } public BasicStageStats getBasicStageStats(Supplier> taskInfosSupplier) @@ -472,8 +500,6 @@ public StageInfo getStageInfo(Supplier> taskInfosSupplier) physicalWrittenDataSize += taskStats.getPhysicalWrittenDataSize().toBytes(); - connectorMetricsAccumulator.add(taskStats.getConnectorMetrics()); - fullGcCount += taskStats.getFullGcCount(); fullGcTaskCount += taskStats.getFullGcCount() > 0 ? 1 : 0; @@ -486,6 +512,18 @@ public StageInfo getStageInfo(Supplier> taskInfosSupplier) for (OperatorStats operatorStats : pipeline.getOperatorSummaries()) { String id = pipeline.getPipelineId() + "." + operatorStats.getOperatorId(); operatorToStats.compute(id, (k, v) -> v == null ? operatorStats : v.add(operatorStats)); + + connectorMetricsAccumulator.add(operatorStats.getConnectorMetrics()); + + // generate table level data skipping metrics for TableScan node + PlanNodeId planNodeId = operatorStats.getPlanNodeId(); + if (planNodeId != null && ScanFilterAndProjectOperator.class.getSimpleName().equals(operatorStats.getOperatorType())) { + // need to map Project/Filter node to TableScan node for ScanFilterAndProjectOperator + planNodeId = getTableScanNodeFromProjectOrFilter(planNodeId); + } + if (isValidTableScanNodeId(planNodeId)) { + connectorMetricsAccumulator.add(processTableLevelMetrics(operatorStats.getConnectorMetrics(), planNodeId)); + } } } } @@ -565,6 +603,25 @@ public StageInfo getStageInfo(Supplier> taskInfosSupplier) failureInfo); } + private PlanNodeId getTableScanNodeFromProjectOrFilter(PlanNodeId planNodeId) + { + if (projectOrFilterNodeToTableScan.containsKey(planNodeId)) { + return projectOrFilterNodeToTableScan.get(planNodeId); + } + + PlanNode planNode = searchFrom(fragment.getRoot()) + .where(node -> node.getId().equals(planNodeId)) + .findSingle() + .get(); + PlanNodeId tableScanNodeId = searchFrom(planNode) + .where(TableScanNode.class::isInstance) + .findSingle() + .get() + .getId(); + projectOrFilterNodeToTableScan.put(planNodeId, tableScanNodeId); + return tableScanNodeId; + } + public void recordGetSplitTime(long startNanos) { long elapsedNanos = System.nanoTime() - startNanos; diff --git a/core/trino-main/src/main/java/io/trino/execution/scheduler/PipelinedStageExecution.java b/core/trino-main/src/main/java/io/trino/execution/scheduler/PipelinedStageExecution.java index 6499f1b1243d0..f81ff9237306f 100644 --- a/core/trino-main/src/main/java/io/trino/execution/scheduler/PipelinedStageExecution.java +++ b/core/trino-main/src/main/java/io/trino/execution/scheduler/PipelinedStageExecution.java @@ -515,9 +515,9 @@ public void recordGetSplitTime(long start) stage.recordGetSplitTime(start); } - public void updateConnectorMetrics(Metrics newConnectorMetrics) + public void updateConnectorMetrics(Metrics newConnectorMetrics, PlanNodeId planNodeId) { - stage.updateConnectorMetrics(newConnectorMetrics); + stage.updateConnectorMetrics(newConnectorMetrics, planNodeId); } public StageId getStageId() diff --git a/core/trino-main/src/main/java/io/trino/execution/scheduler/SourcePartitionedScheduler.java b/core/trino-main/src/main/java/io/trino/execution/scheduler/SourcePartitionedScheduler.java index 8a2644feb1716..af46d8980446f 100644 --- a/core/trino-main/src/main/java/io/trino/execution/scheduler/SourcePartitionedScheduler.java +++ b/core/trino-main/src/main/java/io/trino/execution/scheduler/SourcePartitionedScheduler.java @@ -394,7 +394,8 @@ else if (pendingSplits.isEmpty()) { whenFinishedOrNewLifespanAdded.set(null); // fall through case FINISHED: - splitSource.getMetrics().ifPresent(stageExecution::updateConnectorMetrics); + splitSource.getMetrics().ifPresent(metrics -> + stageExecution.updateConnectorMetrics(metrics, partitionedNode)); return new ScheduleResult( true, overallNewTasks.build(), diff --git a/core/trino-main/src/main/java/io/trino/operator/TaskContext.java b/core/trino-main/src/main/java/io/trino/operator/TaskContext.java index 4bb65ce2dbda2..2dfbf1414f43a 100644 --- a/core/trino-main/src/main/java/io/trino/operator/TaskContext.java +++ b/core/trino-main/src/main/java/io/trino/operator/TaskContext.java @@ -34,7 +34,6 @@ import io.trino.memory.QueryContextVisitor; import io.trino.memory.context.LocalMemoryContext; import io.trino.memory.context.MemoryTrackingContext; -import io.trino.spi.metrics.Metrics; import io.trino.spi.predicate.Domain; import io.trino.sql.planner.LocalDynamicFiltersCollector; import io.trino.sql.planner.plan.DynamicFilterId; @@ -90,9 +89,6 @@ public class TaskContext private final List pipelineContexts = new CopyOnWriteArrayList<>(); - // update only if task is done to avoid redundant computation - private final AtomicReference connectorMetrics = new AtomicReference<>(Metrics.EMPTY); - private final boolean perOperatorCpuTimerEnabled; private final boolean cpuTimerEnabled; @@ -238,15 +234,6 @@ private void updateStatsIfDone(TaskState newState) endNanos.compareAndSet(0, System.nanoTime()); endFullGcCount.compareAndSet(-1, majorGcCount); endFullGcTimeNanos.compareAndSet(-1, majorGcTime); - - // collect connector metrics directly from operators - Metrics.Accumulator connectorMetricsAccumulator = Metrics.accumulator(); - pipelineContexts.stream() - .map(PipelineContext::getPipelineStats) - .flatMap(pipelineStats -> pipelineStats.getOperatorSummaries().stream()) - .map(OperatorStats::getConnectorMetrics) - .forEach(connectorMetricsAccumulator::add); - connectorMetrics.compareAndSet(Metrics.EMPTY, connectorMetricsAccumulator.get()); } } @@ -596,7 +583,6 @@ public TaskStats getTaskStats() succinctBytes(outputDataSize), outputPositions, succinctBytes(physicalWrittenDataSize), - connectorMetrics.get(), fullGcCount, fullGcTime, pipelineStats); diff --git a/core/trino-main/src/main/java/io/trino/operator/TaskStats.java b/core/trino-main/src/main/java/io/trino/operator/TaskStats.java index 9c4139e6d5d0e..fb4adf30c724b 100644 --- a/core/trino-main/src/main/java/io/trino/operator/TaskStats.java +++ b/core/trino-main/src/main/java/io/trino/operator/TaskStats.java @@ -19,7 +19,6 @@ import com.google.common.collect.ImmutableSet; import io.airlift.units.DataSize; import io.airlift.units.Duration; -import io.trino.spi.metrics.Metrics; import org.joda.time.DateTime; import javax.annotation.Nullable; @@ -85,8 +84,6 @@ public class TaskStats private final DataSize physicalWrittenDataSize; - private final Metrics connectorMetrics; - private final int fullGcCount; private final Duration fullGcTime; @@ -134,7 +131,6 @@ public TaskStats(DateTime createTime, DateTime endTime) DataSize.ofBytes(0), 0, DataSize.ofBytes(0), - Metrics.EMPTY, 0, new Duration(0, MILLISECONDS), ImmutableList.of()); @@ -193,8 +189,6 @@ public TaskStats( @JsonProperty("physicalWrittenDataSize") DataSize physicalWrittenDataSize, - @JsonProperty("connectorMetrics") Metrics connectorMetrics, - @JsonProperty("fullGcCount") int fullGcCount, @JsonProperty("fullGcTime") Duration fullGcTime, @@ -268,8 +262,6 @@ public TaskStats( this.physicalWrittenDataSize = requireNonNull(physicalWrittenDataSize, "physicalWrittenDataSize is null"); - this.connectorMetrics = requireNonNull(connectorMetrics, "connectorMetrics is null"); - checkArgument(fullGcCount >= 0, "fullGcCount is negative"); this.fullGcCount = fullGcCount; this.fullGcTime = requireNonNull(fullGcTime, "fullGcTime is null"); @@ -497,12 +489,6 @@ public DataSize getPhysicalWrittenDataSize() return physicalWrittenDataSize; } - @JsonProperty - public Metrics getConnectorMetrics() - { - return connectorMetrics; - } - @JsonProperty public List getPipelines() { @@ -588,7 +574,6 @@ public TaskStats summarize() outputDataSize, outputPositions, physicalWrittenDataSize, - connectorMetrics, fullGcCount, fullGcTime, ImmutableList.of()); @@ -637,7 +622,6 @@ public TaskStats summarizeFinal() outputDataSize, outputPositions, physicalWrittenDataSize, - connectorMetrics, fullGcCount, fullGcTime, summarizePipelineStats(pipelines)); diff --git a/core/trino-main/src/main/resources/webapp/dist/embedded_plan.js b/core/trino-main/src/main/resources/webapp/dist/embedded_plan.js index baba6da2248ae..9cf42b9eba6bf 100644 --- a/core/trino-main/src/main/resources/webapp/dist/embedded_plan.js +++ b/core/trino-main/src/main/resources/webapp/dist/embedded_plan.js @@ -20652,7 +20652,7 @@ eval("module.exports = function(module) {\n\tif (!module.webpackPolyfill) {\n\t\ /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.getTotalFromLongCountMetrics = getTotalFromLongCountMetrics;\nexports.getCountWithPercentage = getCountWithPercentage;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n break;\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n break;\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n break;\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\nfunction getTotalFromLongCountMetrics(longCount) {\n if (longCount == null || longCount.total == null) {\n return 0;\n }\n return longCount.total;\n}\n\nfunction getCountWithPercentage(numerator, denominator) {\n if (denominator !== 0) {\n var percentage = numerator / denominator * 100;\n return numerator.toString() + ' (' + precisionRound(percentage) + '%)';\n }\n return numerator.toString();\n}\n\n//# sourceURL=webpack:///./utils.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.getTotalFromLongCountMetrics = getTotalFromLongCountMetrics;\nexports.formatPercentage = formatPercentage;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n break;\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n break;\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n break;\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\nfunction getTotalFromLongCountMetrics(longCount) {\n if (longCount == null || longCount.total == null) {\n return 0;\n }\n return longCount.total;\n}\n\nfunction formatPercentage(numerator, denominator) {\n var percentage = denominator === 0 ? 0 : numerator / denominator * 100;\n return precisionRound(percentage) + \"%\";\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/core/trino-main/src/main/resources/webapp/dist/index.js b/core/trino-main/src/main/resources/webapp/dist/index.js index 1be9ac843985a..f81480e4d1792 100644 --- a/core/trino-main/src/main/resources/webapp/dist/index.js +++ b/core/trino-main/src/main/resources/webapp/dist/index.js @@ -20628,7 +20628,7 @@ eval("module.exports = function(module) {\n\tif (!module.webpackPolyfill) {\n\t\ /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.getTotalFromLongCountMetrics = getTotalFromLongCountMetrics;\nexports.getCountWithPercentage = getCountWithPercentage;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n break;\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n break;\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n break;\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\nfunction getTotalFromLongCountMetrics(longCount) {\n if (longCount == null || longCount.total == null) {\n return 0;\n }\n return longCount.total;\n}\n\nfunction getCountWithPercentage(numerator, denominator) {\n if (denominator !== 0) {\n var percentage = numerator / denominator * 100;\n return numerator.toString() + ' (' + precisionRound(percentage) + '%)';\n }\n return numerator.toString();\n}\n\n//# sourceURL=webpack:///./utils.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.getTotalFromLongCountMetrics = getTotalFromLongCountMetrics;\nexports.formatPercentage = formatPercentage;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n break;\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n break;\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n break;\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\nfunction getTotalFromLongCountMetrics(longCount) {\n if (longCount == null || longCount.total == null) {\n return 0;\n }\n return longCount.total;\n}\n\nfunction formatPercentage(numerator, denominator) {\n var percentage = denominator === 0 ? 0 : numerator / denominator * 100;\n return precisionRound(percentage) + \"%\";\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/core/trino-main/src/main/resources/webapp/dist/plan.js b/core/trino-main/src/main/resources/webapp/dist/plan.js index 026f2d3f19bc3..86590c1026a0e 100644 --- a/core/trino-main/src/main/resources/webapp/dist/plan.js +++ b/core/trino-main/src/main/resources/webapp/dist/plan.js @@ -20664,7 +20664,7 @@ eval("\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/i /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.getTotalFromLongCountMetrics = getTotalFromLongCountMetrics;\nexports.getCountWithPercentage = getCountWithPercentage;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n break;\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n break;\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n break;\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\nfunction getTotalFromLongCountMetrics(longCount) {\n if (longCount == null || longCount.total == null) {\n return 0;\n }\n return longCount.total;\n}\n\nfunction getCountWithPercentage(numerator, denominator) {\n if (denominator !== 0) {\n var percentage = numerator / denominator * 100;\n return numerator.toString() + ' (' + precisionRound(percentage) + '%)';\n }\n return numerator.toString();\n}\n\n//# sourceURL=webpack:///./utils.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.getTotalFromLongCountMetrics = getTotalFromLongCountMetrics;\nexports.formatPercentage = formatPercentage;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n break;\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n break;\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n break;\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\nfunction getTotalFromLongCountMetrics(longCount) {\n if (longCount == null || longCount.total == null) {\n return 0;\n }\n return longCount.total;\n}\n\nfunction formatPercentage(numerator, denominator) {\n var percentage = denominator === 0 ? 0 : numerator / denominator * 100;\n return precisionRound(percentage) + \"%\";\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/core/trino-main/src/main/resources/webapp/dist/query.js b/core/trino-main/src/main/resources/webapp/dist/query.js index 7734bb1ce8c8c..a346abbae5546 100644 --- a/core/trino-main/src/main/resources/webapp/dist/query.js +++ b/core/trino-main/src/main/resources/webapp/dist/query.js @@ -106,7 +106,7 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n}); /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.QueryDetail = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _reactable = __webpack_require__(/*! reactable */ \"./node_modules/reactable/lib/reactable.js\");\n\nvar _reactable2 = _interopRequireDefault(_reactable);\n\nvar _utils = __webpack_require__(/*! ../utils */ \"./utils.js\");\n\nvar _QueryHeader = __webpack_require__(/*! ./QueryHeader */ \"./components/QueryHeader.jsx\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nvar Table = _reactable2.default.Table,\n Thead = _reactable2.default.Thead,\n Th = _reactable2.default.Th,\n Tr = _reactable2.default.Tr,\n Td = _reactable2.default.Td;\n\nvar TaskList = function (_React$Component) {\n _inherits(TaskList, _React$Component);\n\n function TaskList() {\n _classCallCheck(this, TaskList);\n\n return _possibleConstructorReturn(this, (TaskList.__proto__ || Object.getPrototypeOf(TaskList)).apply(this, arguments));\n }\n\n _createClass(TaskList, [{\n key: \"render\",\n value: function render() {\n var tasks = this.props.tasks;\n\n if (tasks === undefined || tasks.length === 0) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n \"No threads in the selected group\"\n )\n )\n );\n }\n\n var showPortNumbers = TaskList.showPortNumbers(tasks);\n\n var renderedTasks = tasks.map(function (task) {\n var elapsedTime = (0, _utils.parseDuration)(task.stats.elapsedTime);\n if (elapsedTime === 0) {\n elapsedTime = Date.now() - Date.parse(task.stats.createTime);\n }\n\n return _react2.default.createElement(\n Tr,\n { key: task.taskStatus.taskId },\n _react2.default.createElement(\n Td,\n { column: \"id\", value: task.taskStatus.taskId },\n _react2.default.createElement(\n \"a\",\n { href: \"/ui/api/worker/\" + task.taskStatus.nodeId + \"/task/\" + task.taskStatus.taskId + \"?pretty\" },\n (0, _utils.getTaskIdSuffix)(task.taskStatus.taskId)\n )\n ),\n _react2.default.createElement(\n Td,\n { column: \"host\", value: (0, _utils.getHostname)(task.taskStatus.self) },\n _react2.default.createElement(\n \"a\",\n { href: \"worker.html?\" + task.taskStatus.nodeId, className: \"font-light\", target: \"_blank\" },\n showPortNumbers ? (0, _utils.getHostAndPort)(task.taskStatus.self) : (0, _utils.getHostname)(task.taskStatus.self)\n )\n ),\n _react2.default.createElement(\n Td,\n { column: \"state\", value: TaskList.formatState(task.taskStatus.state, task.stats.fullyBlocked) },\n TaskList.formatState(task.taskStatus.state, task.stats.fullyBlocked)\n ),\n _react2.default.createElement(\n Td,\n { column: \"rows\", value: task.stats.rawInputPositions },\n (0, _utils.formatCount)(task.stats.rawInputPositions)\n ),\n _react2.default.createElement(\n Td,\n { column: \"rowsSec\", value: (0, _utils.computeRate)(task.stats.rawInputPositions, elapsedTime) },\n (0, _utils.formatCount)((0, _utils.computeRate)(task.stats.rawInputPositions, elapsedTime))\n ),\n _react2.default.createElement(\n Td,\n { column: \"bytes\", value: (0, _utils.parseDataSize)(task.stats.rawInputDataSize) },\n (0, _utils.formatDataSizeBytes)((0, _utils.parseDataSize)(task.stats.rawInputDataSize))\n ),\n _react2.default.createElement(\n Td,\n { column: \"bytesSec\", value: (0, _utils.computeRate)((0, _utils.parseDataSize)(task.stats.rawInputDataSize), elapsedTime) },\n (0, _utils.formatDataSizeBytes)((0, _utils.computeRate)((0, _utils.parseDataSize)(task.stats.rawInputDataSize), elapsedTime))\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsPending\", value: task.stats.queuedDrivers },\n task.stats.queuedDrivers\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsRunning\", value: task.stats.runningDrivers },\n task.stats.runningDrivers\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsBlocked\", value: task.stats.blockedDrivers },\n task.stats.blockedDrivers\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsDone\", value: task.stats.completedDrivers },\n task.stats.completedDrivers\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsSkippedByIndex\", value: task.stats.skippedSplitsByIndex },\n task.stats.skippedSplitsByIndex\n ),\n _react2.default.createElement(\n Td,\n { column: \"avgIndexReadTime\", value: (0, _utils.parseDuration)((0, _utils.computeAvgDuration)(task.stats.totalIndexReadTime, task.stats.completedDrivers)) },\n (0, _utils.computeAvgDuration)(task.stats.totalIndexReadTime, task.stats.completedDrivers)\n ),\n _react2.default.createElement(\n Td,\n { column: \"elapsedTime\", value: (0, _utils.parseDuration)(task.stats.elapsedTime) },\n task.stats.elapsedTime\n ),\n _react2.default.createElement(\n Td,\n { column: \"cpuTime\", value: (0, _utils.parseDuration)(task.stats.totalCpuTime) },\n task.stats.totalCpuTime\n ),\n _react2.default.createElement(\n Td,\n { column: \"bufferedBytes\", value: task.outputBuffers.totalBufferedBytes },\n (0, _utils.formatDataSizeBytes)(task.outputBuffers.totalBufferedBytes)\n )\n );\n });\n\n return _react2.default.createElement(\n Table,\n { id: \"tasks\", className: \"table table-striped sortable\", sortable: [{\n column: 'id',\n sortFunction: TaskList.compareTaskId\n }, 'host', 'state', 'splitsPending', 'splitsRunning', 'splitsBlocked', 'splitsDone', 'splitsSkippedByIndex', 'avgIndexReadTime', 'rows', 'rowsSec', 'bytes', 'bytesSec', 'elapsedTime', 'cpuTime', 'bufferedBytes'],\n defaultSort: { column: 'id', direction: 'asc' } },\n _react2.default.createElement(\n Thead,\n null,\n _react2.default.createElement(\n Th,\n { column: \"id\" },\n \"ID\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"host\" },\n \"Host\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"state\" },\n \"State\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsPending\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-pause\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Pending splits\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsRunning\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-play\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Running splits\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsBlocked\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-bookmark\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Blocked splits\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsDone\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-ok\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Completed splits\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsSkippedByIndex\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-share-alt\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Skipped splits by index\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"avgIndexReadTime\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-align-justify\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Average index read time per driver/split\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"rows\" },\n \"Rows\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"rowsSec\" },\n \"Rows/s\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"bytes\" },\n \"Bytes\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"bytesSec\" },\n \"Bytes/s\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"elapsedTime\" },\n \"Elapsed\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"cpuTime\" },\n \"CPU Time\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"bufferedBytes\" },\n \"Buffered\"\n )\n ),\n renderedTasks\n );\n }\n }], [{\n key: \"removeQueryId\",\n value: function removeQueryId(id) {\n var pos = id.indexOf('.');\n if (pos !== -1) {\n return id.substring(pos + 1);\n }\n return id;\n }\n }, {\n key: \"compareTaskId\",\n value: function compareTaskId(taskA, taskB) {\n var taskIdArrA = TaskList.removeQueryId(taskA).split(\".\");\n var taskIdArrB = TaskList.removeQueryId(taskB).split(\".\");\n\n if (taskIdArrA.length > taskIdArrB.length) {\n return 1;\n }\n for (var i = 0; i < taskIdArrA.length; i++) {\n var anum = Number.parseInt(taskIdArrA[i]);\n var bnum = Number.parseInt(taskIdArrB[i]);\n if (anum !== bnum) {\n return anum > bnum ? 1 : -1;\n }\n }\n\n return 0;\n }\n }, {\n key: \"showPortNumbers\",\n value: function showPortNumbers(tasks) {\n // check if any host has multiple port numbers\n var hostToPortNumber = {};\n for (var i = 0; i < tasks.length; i++) {\n var taskUri = tasks[i].taskStatus.self;\n var hostname = (0, _utils.getHostname)(taskUri);\n var port = (0, _utils.getPort)(taskUri);\n if (hostname in hostToPortNumber && hostToPortNumber[hostname] !== port) {\n return true;\n }\n hostToPortNumber[hostname] = port;\n }\n\n return false;\n }\n }, {\n key: \"formatState\",\n value: function formatState(state, fullyBlocked) {\n if (fullyBlocked && state === \"RUNNING\") {\n return \"BLOCKED\";\n } else {\n return state;\n }\n }\n }]);\n\n return TaskList;\n}(_react2.default.Component);\n\nvar BAR_CHART_WIDTH = 800;\n\nvar BAR_CHART_PROPERTIES = {\n type: 'bar',\n barSpacing: '0',\n height: '80px',\n barColor: '#747F96',\n zeroColor: '#8997B3',\n chartRangeMin: 0,\n tooltipClassname: 'sparkline-tooltip',\n tooltipFormat: 'Task {{offset:offset}} - {{value}}',\n disableHiddenCheck: true\n};\n\nvar HISTOGRAM_WIDTH = 175;\n\nvar HISTOGRAM_PROPERTIES = {\n type: 'bar',\n barSpacing: '0',\n height: '80px',\n barColor: '#747F96',\n zeroColor: '#747F96',\n zeroAxis: true,\n chartRangeMin: 0,\n tooltipClassname: 'sparkline-tooltip',\n tooltipFormat: '{{offset:offset}} -- {{value}} tasks',\n disableHiddenCheck: true\n};\n\nvar StageSummary = function (_React$Component2) {\n _inherits(StageSummary, _React$Component2);\n\n function StageSummary(props) {\n _classCallCheck(this, StageSummary);\n\n var _this2 = _possibleConstructorReturn(this, (StageSummary.__proto__ || Object.getPrototypeOf(StageSummary)).call(this, props));\n\n _this2.state = {\n expanded: false,\n lastRender: null\n };\n return _this2;\n }\n\n _createClass(StageSummary, [{\n key: \"getExpandedIcon\",\n value: function getExpandedIcon() {\n return this.state.expanded ? \"glyphicon-chevron-up\" : \"glyphicon-chevron-down\";\n }\n }, {\n key: \"getExpandedStyle\",\n value: function getExpandedStyle() {\n return this.state.expanded ? {} : { display: \"none\" };\n }\n }, {\n key: \"toggleExpanded\",\n value: function toggleExpanded() {\n this.setState({\n expanded: !this.state.expanded\n });\n }\n }, {\n key: \"componentDidUpdate\",\n value: function componentDidUpdate() {\n var stage = this.props.stage;\n var numTasks = stage.tasks.length;\n\n // sort the x-axis\n stage.tasks.sort(function (taskA, taskB) {\n return (0, _utils.getTaskNumber)(taskA.taskStatus.taskId) - (0, _utils.getTaskNumber)(taskB.taskStatus.taskId);\n });\n\n var scheduledTimes = stage.tasks.map(function (task) {\n return (0, _utils.parseDuration)(task.stats.totalScheduledTime);\n });\n var cpuTimes = stage.tasks.map(function (task) {\n return (0, _utils.parseDuration)(task.stats.totalCpuTime);\n });\n\n // prevent multiple calls to componentDidUpdate (resulting from calls to setState or otherwise) within the refresh interval from re-rendering sparklines/charts\n if (this.state.lastRender === null || Date.now() - this.state.lastRender >= 1000) {\n var renderTimestamp = Date.now();\n var stageId = (0, _utils.getStageNumber)(stage.stageId);\n\n StageSummary.renderHistogram('#scheduled-time-histogram-' + stageId, scheduledTimes, _utils.formatDuration);\n StageSummary.renderHistogram('#cpu-time-histogram-' + stageId, cpuTimes, _utils.formatDuration);\n\n if (this.state.expanded) {\n // this needs to be a string otherwise it will also be passed to numberFormatter\n var tooltipValueLookups = { 'offset': {} };\n for (var i = 0; i < numTasks; i++) {\n tooltipValueLookups['offset'][i] = (0, _utils.getStageNumber)(stage.stageId) + \".\" + i;\n }\n\n var stageBarChartProperties = $.extend({}, BAR_CHART_PROPERTIES, { barWidth: BAR_CHART_WIDTH / numTasks, tooltipValueLookups: tooltipValueLookups });\n\n $('#scheduled-time-bar-chart-' + stageId).sparkline(scheduledTimes, $.extend({}, stageBarChartProperties, { numberFormatter: _utils.formatDuration }));\n $('#cpu-time-bar-chart-' + stageId).sparkline(cpuTimes, $.extend({}, stageBarChartProperties, { numberFormatter: _utils.formatDuration }));\n }\n\n this.setState({\n lastRender: renderTimestamp\n });\n }\n }\n }, {\n key: \"render\",\n value: function render() {\n var stage = this.props.stage;\n if (stage === undefined || !stage.hasOwnProperty('plan')) {\n return _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Information about this stage is unavailable.\"\n )\n );\n }\n\n var totalBufferedBytes = stage.tasks.map(function (task) {\n return task.outputBuffers.totalBufferedBytes;\n }).reduce(function (a, b) {\n return a + b;\n }, 0);\n\n var stageId = (0, _utils.getStageNumber)(stage.stageId);\n\n return _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-id\" },\n _react2.default.createElement(\n \"div\",\n { className: \"stage-state-color\", style: { borderLeftColor: (0, _utils.getStageStateColor)(stage) } },\n stageId\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"table single-stage-table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table stage-table-time\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-stat-header\" },\n \"Time\"\n ),\n _react2.default.createElement(\"th\", null)\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Scheduled\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.stageStats.totalScheduledTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Blocked\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.stageStats.totalBlockedTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"CPU\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.stageStats.totalCpuTime\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table stage-table-memory\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-stat-header\" },\n \"Memory\"\n ),\n _react2.default.createElement(\"th\", null)\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Cumulative\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n (0, _utils.formatDataSizeBytes)(stage.stageStats.cumulativeUserMemory / 1000)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Current\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n (0, _utils.parseAndFormatDataSize)(stage.stageStats.userMemoryReservation)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Buffers\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n (0, _utils.formatDataSize)(totalBufferedBytes)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Peak\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n (0, _utils.parseAndFormatDataSize)(stage.stageStats.peakUserMemoryReservation)\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table stage-table-tasks\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-stat-header\" },\n \"Tasks\"\n ),\n _react2.default.createElement(\"th\", null)\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Pending\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.tasks.filter(function (task) {\n return task.taskStatus.state === \"PLANNED\";\n }).length\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Running\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.tasks.filter(function (task) {\n return task.taskStatus.state === \"RUNNING\";\n }).length\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Blocked\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.tasks.filter(function (task) {\n return task.stats.fullyBlocked;\n }).length\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Total\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.tasks.length\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table histogram-table\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-chart-header\" },\n \"Scheduled Time Skew\"\n )\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"histogram-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"histogram\", id: \"scheduled-time-histogram-\" + stageId },\n _react2.default.createElement(\"div\", { className: \"loader\" })\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table histogram-table\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-chart-header\" },\n \"CPU Time Skew\"\n )\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"histogram-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"histogram\", id: \"cpu-time-histogram-\" + stageId },\n _react2.default.createElement(\"div\", { className: \"loader\" })\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"expand-charts-container\" },\n _react2.default.createElement(\n \"a\",\n { onClick: this.toggleExpanded.bind(this), className: \"expand-charts-button\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon \" + this.getExpandedIcon(), style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\", title: \"More\" })\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { style: this.getExpandedStyle() },\n _react2.default.createElement(\n \"td\",\n { colSpan: \"6\" },\n _react2.default.createElement(\n \"table\",\n { className: \"expanded-chart\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title expanded-chart-title\" },\n \"Task Scheduled Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"bar-chart-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"bar-chart\", id: \"scheduled-time-bar-chart-\" + stageId },\n _react2.default.createElement(\"div\", { className: \"loader\" })\n )\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { style: this.getExpandedStyle() },\n _react2.default.createElement(\n \"td\",\n { colSpan: \"6\" },\n _react2.default.createElement(\n \"table\",\n { className: \"expanded-chart\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title expanded-chart-title\" },\n \"Task CPU Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"bar-chart-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"bar-chart\", id: \"cpu-time-bar-chart-\" + stageId },\n _react2.default.createElement(\"div\", { className: \"loader\" })\n )\n )\n )\n )\n )\n )\n )\n )\n )\n )\n );\n }\n }], [{\n key: \"renderHistogram\",\n value: function renderHistogram(histogramId, inputData, numberFormatter) {\n var numBuckets = Math.min(HISTOGRAM_WIDTH, Math.sqrt(inputData.length));\n var dataMin = Math.min.apply(null, inputData);\n var dataMax = Math.max.apply(null, inputData);\n var bucketSize = (dataMax - dataMin) / numBuckets;\n\n var histogramData = [];\n if (bucketSize === 0) {\n histogramData = [inputData.length];\n } else {\n for (var i = 0; i < numBuckets + 1; i++) {\n histogramData.push(0);\n }\n\n for (var _i in inputData) {\n var dataPoint = inputData[_i];\n var bucket = Math.floor((dataPoint - dataMin) / bucketSize);\n histogramData[bucket] = histogramData[bucket] + 1;\n }\n }\n\n var tooltipValueLookups = { 'offset': {} };\n for (var _i2 = 0; _i2 < histogramData.length; _i2++) {\n tooltipValueLookups['offset'][_i2] = numberFormatter(dataMin + _i2 * bucketSize) + \"-\" + numberFormatter(dataMin + (_i2 + 1) * bucketSize);\n }\n\n var stageHistogramProperties = $.extend({}, HISTOGRAM_PROPERTIES, { barWidth: HISTOGRAM_WIDTH / histogramData.length, tooltipValueLookups: tooltipValueLookups });\n $(histogramId).sparkline(histogramData, stageHistogramProperties);\n }\n }]);\n\n return StageSummary;\n}(_react2.default.Component);\n\nvar StageList = function (_React$Component3) {\n _inherits(StageList, _React$Component3);\n\n function StageList() {\n _classCallCheck(this, StageList);\n\n return _possibleConstructorReturn(this, (StageList.__proto__ || Object.getPrototypeOf(StageList)).apply(this, arguments));\n }\n\n _createClass(StageList, [{\n key: \"getStages\",\n value: function getStages(stage) {\n if (stage === undefined || !stage.hasOwnProperty('subStages')) {\n return [];\n }\n\n return [].concat.apply(stage, stage.subStages.map(this.getStages, this));\n }\n }, {\n key: \"render\",\n value: function render() {\n var stages = this.getStages(this.props.outputStage);\n\n if (stages === undefined || stages.length === 0) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n \"No stage information available.\"\n )\n );\n }\n\n var renderedStages = stages.map(function (stage) {\n return _react2.default.createElement(StageSummary, { key: stage.stageId, stage: stage });\n });\n\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"table\",\n { className: \"table\", id: \"stage-list\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n renderedStages\n )\n )\n )\n );\n }\n }]);\n\n return StageList;\n}(_react2.default.Component);\n\nvar SMALL_SPARKLINE_PROPERTIES = {\n width: '100%',\n height: '57px',\n fillColor: '#3F4552',\n lineColor: '#747F96',\n spotColor: '#1EDCFF',\n tooltipClassname: 'sparkline-tooltip',\n disableHiddenCheck: true\n};\n\nvar TASK_FILTER = {\n ALL: function ALL() {\n return true;\n },\n PLANNED: function PLANNED(state) {\n return state === 'PLANNED';\n },\n RUNNING: function RUNNING(state) {\n return state === 'RUNNING';\n },\n FINISHED: function FINISHED(state) {\n return state === 'FINISHED';\n },\n FAILED: function FAILED(state) {\n return state === 'FAILED' || state === 'ABORTED' || state === 'CANCELED';\n }\n};\n\nvar QueryDetail = exports.QueryDetail = function (_React$Component4) {\n _inherits(QueryDetail, _React$Component4);\n\n function QueryDetail(props) {\n _classCallCheck(this, QueryDetail);\n\n var _this4 = _possibleConstructorReturn(this, (QueryDetail.__proto__ || Object.getPrototypeOf(QueryDetail)).call(this, props));\n\n _this4.state = {\n query: null,\n lastSnapshotStages: null,\n lastSnapshotTasks: null,\n\n lastScheduledTime: 0,\n lastCpuTime: 0,\n lastRowInput: 0,\n lastByteInput: 0,\n lastPhysicalInput: 0,\n lastPhysicalTime: 0,\n\n scheduledTimeRate: [],\n cpuTimeRate: [],\n rowInputRate: [],\n byteInputRate: [],\n physicalInputRate: [],\n\n reservedMemory: [],\n\n initialized: false,\n queryEnded: false,\n renderingEnded: false,\n\n lastRefresh: null,\n lastRender: null,\n\n stageRefresh: true,\n taskRefresh: true,\n\n taskFilter: TASK_FILTER.ALL\n };\n\n _this4.refreshLoop = _this4.refreshLoop.bind(_this4);\n return _this4;\n }\n\n _createClass(QueryDetail, [{\n key: \"resetTimer\",\n value: function resetTimer() {\n clearTimeout(this.timeoutId);\n // stop refreshing when query finishes or fails\n if (this.state.query === null || !this.state.queryEnded) {\n // task.info-update-interval is set to 3 seconds by default\n this.timeoutId = setTimeout(this.refreshLoop, 3000);\n }\n }\n }, {\n key: \"refreshLoop\",\n value: function refreshLoop() {\n var _this5 = this;\n\n clearTimeout(this.timeoutId); // to stop multiple series of refreshLoop from going on simultaneously\n var queryId = (0, _utils.getFirstParameter)(window.location.search);\n $.get('/ui/api/query/' + queryId, function (query) {\n var lastSnapshotStages = this.state.lastSnapshotStage;\n if (this.state.stageRefresh) {\n lastSnapshotStages = query.outputStage;\n }\n var lastSnapshotTasks = this.state.lastSnapshotTasks;\n if (this.state.taskRefresh) {\n lastSnapshotTasks = query.outputStage;\n }\n\n var lastRefresh = this.state.lastRefresh;\n var lastScheduledTime = this.state.lastScheduledTime;\n var lastCpuTime = this.state.lastCpuTime;\n var lastRowInput = this.state.lastRowInput;\n var lastByteInput = this.state.lastByteInput;\n var lastPhysicalInput = this.state.lastPhysicalInput;\n var lastPhysicalTime = this.state.lastPhysicalTime;\n var alreadyEnded = this.state.queryEnded;\n var nowMillis = Date.now();\n\n this.setState({\n query: query,\n lastSnapshotStage: lastSnapshotStages,\n lastSnapshotTasks: lastSnapshotTasks,\n\n lastPhysicalTime: (0, _utils.parseDuration)(query.queryStats.physicalInputReadTime),\n lastScheduledTime: (0, _utils.parseDuration)(query.queryStats.totalScheduledTime),\n lastCpuTime: (0, _utils.parseDuration)(query.queryStats.totalCpuTime),\n lastRowInput: query.queryStats.processedInputPositions,\n lastByteInput: (0, _utils.parseDataSize)(query.queryStats.processedInputDataSize),\n lastPhysicalInput: (0, _utils.parseDataSize)(query.queryStats.physicalInputDataSize),\n\n initialized: true,\n queryEnded: !!query.finalQueryInfo,\n\n lastRefresh: nowMillis\n });\n\n // i.e. don't show sparklines if we've already decided not to update or if we don't have one previous measurement\n if (alreadyEnded || lastRefresh === null && query.state === \"RUNNING\") {\n this.resetTimer();\n return;\n }\n\n if (lastRefresh === null) {\n lastRefresh = nowMillis - (0, _utils.parseDuration)(query.queryStats.elapsedTime);\n }\n\n var elapsedSecsSinceLastRefresh = (nowMillis - lastRefresh) / 1000.0;\n if (elapsedSecsSinceLastRefresh >= 0) {\n var currentScheduledTimeRate = ((0, _utils.parseDuration)(query.queryStats.totalScheduledTime) - lastScheduledTime) / (elapsedSecsSinceLastRefresh * 1000);\n var currentCpuTimeRate = ((0, _utils.parseDuration)(query.queryStats.totalCpuTime) - lastCpuTime) / (elapsedSecsSinceLastRefresh * 1000);\n var currentPhysicalReadTime = ((0, _utils.parseDuration)(query.queryStats.physicalInputReadTime) - lastPhysicalTime) / 1000;\n var currentRowInputRate = (query.queryStats.processedInputPositions - lastRowInput) / elapsedSecsSinceLastRefresh;\n var currentByteInputRate = ((0, _utils.parseDataSize)(query.queryStats.processedInputDataSize) - lastByteInput) / elapsedSecsSinceLastRefresh;\n var currentPhysicalInputRate = currentPhysicalReadTime > 0 ? ((0, _utils.parseDataSize)(query.queryStats.physicalInputDataSize) - lastPhysicalInput) / currentPhysicalReadTime : 0;\n\n this.setState({\n scheduledTimeRate: (0, _utils.addToHistory)(currentScheduledTimeRate, this.state.scheduledTimeRate),\n cpuTimeRate: (0, _utils.addToHistory)(currentCpuTimeRate, this.state.cpuTimeRate),\n rowInputRate: (0, _utils.addToHistory)(currentRowInputRate, this.state.rowInputRate),\n byteInputRate: (0, _utils.addToHistory)(currentByteInputRate, this.state.byteInputRate),\n reservedMemory: (0, _utils.addToHistory)((0, _utils.parseDataSize)(query.queryStats.totalMemoryReservation), this.state.reservedMemory),\n physicalInputRate: (0, _utils.addToHistory)(currentPhysicalInputRate, this.state.physicalInputRate)\n });\n }\n this.resetTimer();\n }.bind(this)).fail(function () {\n _this5.setState({\n initialized: true\n });\n _this5.resetTimer();\n });\n }\n }, {\n key: \"handleTaskRefreshClick\",\n value: function handleTaskRefreshClick() {\n if (this.state.taskRefresh) {\n this.setState({\n taskRefresh: false,\n lastSnapshotTasks: this.state.query.outputStage\n });\n } else {\n this.setState({\n taskRefresh: true\n });\n }\n }\n }, {\n key: \"renderTaskRefreshButton\",\n value: function renderTaskRefreshButton() {\n if (this.state.taskRefresh) {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleTaskRefreshClick.bind(this) },\n \"Auto-Refresh: On\"\n );\n } else {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleTaskRefreshClick.bind(this) },\n \"Auto-Refresh: Off\"\n );\n }\n }\n }, {\n key: \"handleStageRefreshClick\",\n value: function handleStageRefreshClick() {\n if (this.state.stageRefresh) {\n this.setState({\n stageRefresh: false,\n lastSnapshotStages: this.state.query.outputStage\n });\n } else {\n this.setState({\n stageRefresh: true\n });\n }\n }\n }, {\n key: \"renderStageRefreshButton\",\n value: function renderStageRefreshButton() {\n if (this.state.stageRefresh) {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleStageRefreshClick.bind(this) },\n \"Auto-Refresh: On\"\n );\n } else {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleStageRefreshClick.bind(this) },\n \"Auto-Refresh: Off\"\n );\n }\n }\n }, {\n key: \"renderTaskFilterListItem\",\n value: function renderTaskFilterListItem(taskFilter, taskFilterText) {\n return _react2.default.createElement(\n \"li\",\n null,\n _react2.default.createElement(\n \"a\",\n { href: \"#\", className: this.state.taskFilter === taskFilter ? \"selected\" : \"\", onClick: this.handleTaskFilterClick.bind(this, taskFilter) },\n taskFilterText\n )\n );\n }\n }, {\n key: \"handleTaskFilterClick\",\n value: function handleTaskFilterClick(filter, event) {\n this.setState({\n taskFilter: filter\n });\n event.preventDefault();\n }\n }, {\n key: \"getTasksFromStage\",\n value: function getTasksFromStage(stage) {\n if (stage === undefined || !stage.hasOwnProperty('subStages') || !stage.hasOwnProperty('tasks')) {\n return [];\n }\n\n return [].concat.apply(stage.tasks, stage.subStages.map(this.getTasksFromStage, this));\n }\n }, {\n key: \"componentDidMount\",\n value: function componentDidMount() {\n this.refreshLoop();\n }\n }, {\n key: \"componentDidUpdate\",\n value: function componentDidUpdate() {\n // prevent multiple calls to componentDidUpdate (resulting from calls to setState or otherwise) within the refresh interval from re-rendering sparklines/charts\n if (this.state.lastRender === null || Date.now() - this.state.lastRender >= 1000 || this.state.ended && !this.state.renderingEnded) {\n var renderTimestamp = Date.now();\n $('#scheduled-time-rate-sparkline').sparkline(this.state.scheduledTimeRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, {\n chartRangeMin: 0,\n numberFormatter: _utils.precisionRound\n }));\n $('#cpu-time-rate-sparkline').sparkline(this.state.cpuTimeRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { chartRangeMin: 0, numberFormatter: _utils.precisionRound }));\n $('#row-input-rate-sparkline').sparkline(this.state.rowInputRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { numberFormatter: _utils.formatCount }));\n $('#byte-input-rate-sparkline').sparkline(this.state.byteInputRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { numberFormatter: _utils.formatDataSize }));\n $('#reserved-memory-sparkline').sparkline(this.state.reservedMemory, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { numberFormatter: _utils.formatDataSize }));\n $('#physical-input-rate-sparkline').sparkline(this.state.physicalInputRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { numberFormatter: _utils.formatDataSize }));\n\n if (this.state.lastRender === null) {\n $('#query').each(function (i, block) {\n hljs.highlightBlock(block);\n });\n\n $('#prepared-query').each(function (i, block) {\n hljs.highlightBlock(block);\n });\n }\n\n this.setState({\n renderingEnded: this.state.ended,\n lastRender: renderTimestamp\n });\n }\n\n $('[data-toggle=\"tooltip\"]').tooltip();\n new window.ClipboardJS('.copy-button');\n }\n }, {\n key: \"renderTasks\",\n value: function renderTasks() {\n var _this6 = this;\n\n if (this.state.lastSnapshotTasks === null) {\n return;\n }\n\n var tasks = this.getTasksFromStage(this.state.lastSnapshotTasks).filter(function (task) {\n return _this6.state.taskFilter(task.taskStatus.state);\n }, this);\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-9\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Tasks\"\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-3\" },\n _react2.default.createElement(\n \"table\",\n { className: \"header-inline-links\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"div\",\n { className: \"input-group-btn text-right\" },\n _react2.default.createElement(\n \"button\",\n { type: \"button\", className: \"btn btn-default dropdown-toggle pull-right text-right\", \"data-toggle\": \"dropdown\", \"aria-haspopup\": \"true\",\n \"aria-expanded\": \"false\" },\n \"Show \",\n _react2.default.createElement(\"span\", { className: \"caret\" })\n ),\n _react2.default.createElement(\n \"ul\",\n { className: \"dropdown-menu\" },\n this.renderTaskFilterListItem(TASK_FILTER.ALL, \"All\"),\n this.renderTaskFilterListItem(TASK_FILTER.PLANNED, \"Planned\"),\n this.renderTaskFilterListItem(TASK_FILTER.RUNNING, \"Running\"),\n this.renderTaskFilterListItem(TASK_FILTER.FINISHED, \"Finished\"),\n this.renderTaskFilterListItem(TASK_FILTER.FAILED, \"Aborted/Canceled/Failed\")\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n \"\\xA0\\xA0\",\n this.renderTaskRefreshButton()\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(TaskList, { key: this.state.query.queryId, tasks: tasks })\n )\n )\n );\n }\n }, {\n key: \"renderStages\",\n value: function renderStages() {\n if (this.state.lastSnapshotStage === null) {\n return;\n }\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-9\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Stages\"\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-3\" },\n _react2.default.createElement(\n \"table\",\n { className: \"header-inline-links\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n this.renderStageRefreshButton()\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(StageList, { key: this.state.query.queryId, outputStage: this.state.lastSnapshotStage })\n )\n )\n );\n }\n }, {\n key: \"renderPreparedQuery\",\n value: function renderPreparedQuery() {\n var query = this.state.query;\n if (!query.hasOwnProperty('preparedQuery') || query.preparedQuery === null) {\n return;\n }\n\n return _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Prepared Query\",\n _react2.default.createElement(\n \"a\",\n { className: \"btn copy-button\", \"data-clipboard-target\": \"#prepared-query-text\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n ),\n _react2.default.createElement(\n \"pre\",\n { id: \"prepared-query\" },\n _react2.default.createElement(\n \"code\",\n { className: \"lang-sql\", id: \"prepared-query-text\" },\n query.preparedQuery\n )\n )\n );\n }\n }, {\n key: \"renderSessionProperties\",\n value: function renderSessionProperties() {\n var query = this.state.query;\n\n var properties = [];\n for (var property in query.session.systemProperties) {\n if (query.session.systemProperties.hasOwnProperty(property)) {\n properties.push(_react2.default.createElement(\n \"span\",\n null,\n \"- \",\n property + \"=\" + query.session.systemProperties[property],\n \" \",\n _react2.default.createElement(\"br\", null)\n ));\n }\n }\n\n for (var catalog in query.session.catalogProperties) {\n if (query.session.catalogProperties.hasOwnProperty(catalog)) {\n for (var _property in query.session.catalogProperties[catalog]) {\n if (query.session.catalogProperties[catalog].hasOwnProperty(_property)) {\n properties.push(_react2.default.createElement(\n \"span\",\n null,\n \"- \",\n catalog + \".\" + _property + \"=\" + query.session.catalogProperties[catalog][_property],\n \" \",\n _react2.default.createElement(\"br\", null)\n ));\n }\n }\n }\n }\n\n return properties;\n }\n }, {\n key: \"renderResourceEstimates\",\n value: function renderResourceEstimates() {\n var query = this.state.query;\n var estimates = query.session.resourceEstimates;\n var renderedEstimates = [];\n\n for (var resource in estimates) {\n if (estimates.hasOwnProperty(resource)) {\n var upperChars = resource.match(/([A-Z])/g) || [];\n var snakeCased = resource;\n for (var i = 0, n = upperChars.length; i < n; i++) {\n snakeCased = snakeCased.replace(new RegExp(upperChars[i]), '_' + upperChars[i].toLowerCase());\n }\n\n renderedEstimates.push(_react2.default.createElement(\n \"span\",\n null,\n \"- \",\n snakeCased + \"=\" + query.session.resourceEstimates[resource],\n \" \",\n _react2.default.createElement(\"br\", null)\n ));\n }\n }\n\n return renderedEstimates;\n }\n }, {\n key: \"renderSplitFilterMetrics\",\n value: function renderSplitFilterMetrics() {\n var metrics = this.state.query.queryStats.connectorMetrics;\n if (metrics) {\n var totalSplitsInPartitions = (0, _utils.getTotalFromLongCountMetrics)(metrics.iceberg_total_splits_in_partitions);\n var totalSplitsRead = (0, _utils.getTotalFromLongCountMetrics)(metrics.iceberg_total_splits_read);\n var skippedSplitsByIndex = (0, _utils.getTotalFromLongCountMetrics)(metrics.iceberg_skipped_splits_by_index);\n var skippedSplitsByMinMax = (0, _utils.getTotalFromLongCountMetrics)(metrics.iceberg_skipped_splits_by_minmax);\n var skippedSplitsByDFInCoordinator = (0, _utils.getTotalFromLongCountMetrics)(metrics.iceberg_skipped_splits_by_df_in_coordinator);\n var skippedSplitsByDFInWorker = (0, _utils.getTotalFromLongCountMetrics)(metrics.iceberg_skipped_splits_by_df_in_worker);\n return _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Source Split Filter Metrics (Iceberg Only)\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\", style: { width: '50%' } },\n \"Total Splits in Target Partitions\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n totalSplitsInPartitions\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Total Splits Read\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.getCountWithPercentage)(totalSplitsRead, totalSplitsInPartitions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Skipped Splits by Index\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.getCountWithPercentage)(skippedSplitsByIndex, totalSplitsInPartitions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Skipped Splits by MinMax Statistics\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.getCountWithPercentage)(skippedSplitsByMinMax, totalSplitsInPartitions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Skipped Splits by Dynamic Filter in Coordinator\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.getCountWithPercentage)(skippedSplitsByDFInCoordinator, totalSplitsInPartitions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Skipped Splits by Dynamic Filter in Worker\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.getCountWithPercentage)(skippedSplitsByDFInWorker, totalSplitsInPartitions)\n )\n )\n )\n )\n );\n } else {\n return null;\n }\n }\n }, {\n key: \"renderWarningInfo\",\n value: function renderWarningInfo() {\n var query = this.state.query;\n if (query.warnings.length > 0) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Warnings\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\", id: \"warnings-table\" },\n query.warnings.map(function (warning) {\n return _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n warning.warningCode.name\n ),\n _react2.default.createElement(\n \"td\",\n null,\n warning.message\n )\n );\n })\n )\n )\n );\n } else {\n return null;\n }\n }\n }, {\n key: \"renderFailureInfo\",\n value: function renderFailureInfo() {\n var query = this.state.query;\n if (query.failureInfo) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Error Information\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Error Type\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.errorType\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Error Code\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.errorCode.name + \" (\" + this.state.query.errorCode.code + \")\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Stack Trace\",\n _react2.default.createElement(\n \"a\",\n { className: \"btn copy-button\", \"data-clipboard-target\": \"#stack-trace\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n _react2.default.createElement(\n \"pre\",\n { id: \"stack-trace\" },\n QueryDetail.formatStackTrace(query.failureInfo)\n )\n )\n )\n )\n )\n )\n );\n } else {\n return \"\";\n }\n }\n }, {\n key: \"render\",\n value: function render() {\n var query = this.state.query;\n\n if (query === null || this.state.initialized === false) {\n var label = _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading...\"\n );\n if (this.state.initialized) {\n label = \"Query not found\";\n }\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n label\n )\n )\n );\n }\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(_QueryHeader.QueryHeader, { query: query }),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Session\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"User\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n _react2.default.createElement(\n \"span\",\n { id: \"query-user\" },\n query.session.user\n ),\n \"\\xA0\\xA0\",\n _react2.default.createElement(\n \"a\",\n { href: \"#\", className: \"copy-button\", \"data-clipboard-target\": \"#query-user\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Principal\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n query.session.principal\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Source\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n query.session.source\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Catalog\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.catalog\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Schema\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.schema\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Time zone\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.timeZone\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Client Address\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.remoteUserAddress\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Client Tags\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.clientTags.join(\", \")\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Session Properties\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n this.renderSessionProperties()\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Resource Estimates\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n this.renderResourceEstimates()\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Execution\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Resource Group\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n query.resourceGroupId ? query.resourceGroupId.join(\".\") : \"n/a\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Submission Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatShortDateTime)(new Date(query.queryStats.createTime))\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Completion Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.endTime ? (0, _utils.formatShortDateTime)(new Date(query.queryStats.endTime)) : \"\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Elapsed Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.elapsedTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Queued Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.queuedTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Analysis Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.analysisTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Planning Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.planningTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Execution Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.executionTime\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Resource Utilization Summary\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"CPU Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.totalCpuTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Scheduled Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.totalScheduledTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Input Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.processedInputPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Input Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.processedInputDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Input Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.physicalInputPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Input Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.physicalInputDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Input Read Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.physicalInputReadTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Internal Network Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.internalNetworkInputPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Internal Network Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.internalNetworkInputDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Peak User Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.peakUserMemoryReservation)\n )\n ),\n (0, _utils.parseDataSize)(query.queryStats.peakRevocableMemoryReservation) > 0 && _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Peak Revocable Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.peakRevocableMemoryReservation)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Peak Total Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.peakTotalMemoryReservation)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Memory Pool\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.memoryPool\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Cumulative User Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatDataSizeBytes)(query.queryStats.cumulativeUserMemory / 1000.0) + \" seconds\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Cumulative System Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatDataSizeBytes)(query.queryStats.cumulativeSystemMemory / 1000.0) + \" seconds\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Output Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.outputPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Output Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.outputDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Written Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.writtenPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Logical Written Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.logicalWrittenDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Written Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.physicalWrittenDataSize)\n )\n ),\n (0, _utils.parseDataSize)(query.queryStats.spilledDataSize) > 0 && _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Spilled Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.spilledDataSize)\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Timeline\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Parallelism\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"cpu-time-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatCount)(this.state.cpuTimeRate[this.state.cpuTimeRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Scheduled Time/s\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"scheduled-time-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatCount)(this.state.scheduledTimeRate[this.state.scheduledTimeRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Input Rows/s\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"row-input-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatCount)(this.state.rowInputRate[this.state.rowInputRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Input Bytes/s\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"byte-input-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatDataSize)(this.state.byteInputRate[this.state.byteInputRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Input Bytes/s\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"physical-input-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatDataSize)(this.state.physicalInputRate[this.state.physicalInputRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Memory Utilization\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"reserved-memory-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatDataSize)(this.state.reservedMemory[this.state.reservedMemory.length - 1])\n )\n )\n )\n )\n )\n )\n )\n ),\n this.renderSplitFilterMetrics(),\n this.renderWarningInfo(),\n this.renderFailureInfo(),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Query\",\n _react2.default.createElement(\n \"a\",\n { className: \"btn copy-button\", \"data-clipboard-target\": \"#query-text\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n ),\n _react2.default.createElement(\n \"pre\",\n { id: \"query\" },\n _react2.default.createElement(\n \"code\",\n { className: \"lang-sql\", id: \"query-text\" },\n query.query\n )\n )\n ),\n this.renderPreparedQuery()\n ),\n this.renderStages(),\n this.renderTasks()\n );\n }\n }], [{\n key: \"formatStackTrace\",\n value: function formatStackTrace(info) {\n return QueryDetail.formatStackTraceHelper(info, [], \"\", \"\");\n }\n }, {\n key: \"formatStackTraceHelper\",\n value: function formatStackTraceHelper(info, parentStack, prefix, linePrefix) {\n var s = linePrefix + prefix + QueryDetail.failureInfoToString(info) + \"\\n\";\n\n if (info.stack) {\n var sharedStackFrames = 0;\n if (parentStack !== null) {\n sharedStackFrames = QueryDetail.countSharedStackFrames(info.stack, parentStack);\n }\n\n for (var i = 0; i < info.stack.length - sharedStackFrames; i++) {\n s += linePrefix + \"\\tat \" + info.stack[i] + \"\\n\";\n }\n if (sharedStackFrames !== 0) {\n s += linePrefix + \"\\t... \" + sharedStackFrames + \" more\" + \"\\n\";\n }\n }\n\n if (info.suppressed) {\n for (var _i3 = 0; _i3 < info.suppressed.length; _i3++) {\n s += QueryDetail.formatStackTraceHelper(info.suppressed[_i3], info.stack, \"Suppressed: \", linePrefix + \"\\t\");\n }\n }\n\n if (info.cause) {\n s += QueryDetail.formatStackTraceHelper(info.cause, info.stack, \"Caused by: \", linePrefix);\n }\n\n return s;\n }\n }, {\n key: \"countSharedStackFrames\",\n value: function countSharedStackFrames(stack, parentStack) {\n var n = 0;\n var minStackLength = Math.min(stack.length, parentStack.length);\n while (n < minStackLength && stack[stack.length - 1 - n] === parentStack[parentStack.length - 1 - n]) {\n n++;\n }\n return n;\n }\n }, {\n key: \"failureInfoToString\",\n value: function failureInfoToString(t) {\n return t.message !== null ? t.type + \": \" + t.message : t.type;\n }\n }]);\n\n return QueryDetail;\n}(_react2.default.Component);\n\n//# sourceURL=webpack:///./components/QueryDetail.jsx?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.QueryDetail = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _reactable = __webpack_require__(/*! reactable */ \"./node_modules/reactable/lib/reactable.js\");\n\nvar _reactable2 = _interopRequireDefault(_reactable);\n\nvar _utils = __webpack_require__(/*! ../utils */ \"./utils.js\");\n\nvar _QueryHeader = __webpack_require__(/*! ./QueryHeader */ \"./components/QueryHeader.jsx\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nvar Table = _reactable2.default.Table,\n Thead = _reactable2.default.Thead,\n Th = _reactable2.default.Th,\n Tr = _reactable2.default.Tr,\n Td = _reactable2.default.Td;\n\nvar TaskList = function (_React$Component) {\n _inherits(TaskList, _React$Component);\n\n function TaskList() {\n _classCallCheck(this, TaskList);\n\n return _possibleConstructorReturn(this, (TaskList.__proto__ || Object.getPrototypeOf(TaskList)).apply(this, arguments));\n }\n\n _createClass(TaskList, [{\n key: \"render\",\n value: function render() {\n var tasks = this.props.tasks;\n\n if (tasks === undefined || tasks.length === 0) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n \"No threads in the selected group\"\n )\n )\n );\n }\n\n var showPortNumbers = TaskList.showPortNumbers(tasks);\n\n var renderedTasks = tasks.map(function (task) {\n var elapsedTime = (0, _utils.parseDuration)(task.stats.elapsedTime);\n if (elapsedTime === 0) {\n elapsedTime = Date.now() - Date.parse(task.stats.createTime);\n }\n\n return _react2.default.createElement(\n Tr,\n { key: task.taskStatus.taskId },\n _react2.default.createElement(\n Td,\n { column: \"id\", value: task.taskStatus.taskId },\n _react2.default.createElement(\n \"a\",\n { href: \"/ui/api/worker/\" + task.taskStatus.nodeId + \"/task/\" + task.taskStatus.taskId + \"?pretty\" },\n (0, _utils.getTaskIdSuffix)(task.taskStatus.taskId)\n )\n ),\n _react2.default.createElement(\n Td,\n { column: \"host\", value: (0, _utils.getHostname)(task.taskStatus.self) },\n _react2.default.createElement(\n \"a\",\n { href: \"worker.html?\" + task.taskStatus.nodeId, className: \"font-light\", target: \"_blank\" },\n showPortNumbers ? (0, _utils.getHostAndPort)(task.taskStatus.self) : (0, _utils.getHostname)(task.taskStatus.self)\n )\n ),\n _react2.default.createElement(\n Td,\n { column: \"state\", value: TaskList.formatState(task.taskStatus.state, task.stats.fullyBlocked) },\n TaskList.formatState(task.taskStatus.state, task.stats.fullyBlocked)\n ),\n _react2.default.createElement(\n Td,\n { column: \"rows\", value: task.stats.rawInputPositions },\n (0, _utils.formatCount)(task.stats.rawInputPositions)\n ),\n _react2.default.createElement(\n Td,\n { column: \"rowsSec\", value: (0, _utils.computeRate)(task.stats.rawInputPositions, elapsedTime) },\n (0, _utils.formatCount)((0, _utils.computeRate)(task.stats.rawInputPositions, elapsedTime))\n ),\n _react2.default.createElement(\n Td,\n { column: \"bytes\", value: (0, _utils.parseDataSize)(task.stats.rawInputDataSize) },\n (0, _utils.formatDataSizeBytes)((0, _utils.parseDataSize)(task.stats.rawInputDataSize))\n ),\n _react2.default.createElement(\n Td,\n { column: \"bytesSec\", value: (0, _utils.computeRate)((0, _utils.parseDataSize)(task.stats.rawInputDataSize), elapsedTime) },\n (0, _utils.formatDataSizeBytes)((0, _utils.computeRate)((0, _utils.parseDataSize)(task.stats.rawInputDataSize), elapsedTime))\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsPending\", value: task.stats.queuedDrivers },\n task.stats.queuedDrivers\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsRunning\", value: task.stats.runningDrivers },\n task.stats.runningDrivers\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsBlocked\", value: task.stats.blockedDrivers },\n task.stats.blockedDrivers\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsDone\", value: task.stats.completedDrivers },\n task.stats.completedDrivers\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsSkippedByIndex\", value: task.stats.skippedSplitsByIndex },\n task.stats.skippedSplitsByIndex\n ),\n _react2.default.createElement(\n Td,\n { column: \"avgIndexReadTime\", value: (0, _utils.parseDuration)((0, _utils.computeAvgDuration)(task.stats.totalIndexReadTime, task.stats.completedDrivers)) },\n (0, _utils.computeAvgDuration)(task.stats.totalIndexReadTime, task.stats.completedDrivers)\n ),\n _react2.default.createElement(\n Td,\n { column: \"elapsedTime\", value: (0, _utils.parseDuration)(task.stats.elapsedTime) },\n task.stats.elapsedTime\n ),\n _react2.default.createElement(\n Td,\n { column: \"cpuTime\", value: (0, _utils.parseDuration)(task.stats.totalCpuTime) },\n task.stats.totalCpuTime\n ),\n _react2.default.createElement(\n Td,\n { column: \"bufferedBytes\", value: task.outputBuffers.totalBufferedBytes },\n (0, _utils.formatDataSizeBytes)(task.outputBuffers.totalBufferedBytes)\n )\n );\n });\n\n return _react2.default.createElement(\n Table,\n { id: \"tasks\", className: \"table table-striped sortable\", sortable: [{\n column: 'id',\n sortFunction: TaskList.compareTaskId\n }, 'host', 'state', 'splitsPending', 'splitsRunning', 'splitsBlocked', 'splitsDone', 'splitsSkippedByIndex', 'avgIndexReadTime', 'rows', 'rowsSec', 'bytes', 'bytesSec', 'elapsedTime', 'cpuTime', 'bufferedBytes'],\n defaultSort: { column: 'id', direction: 'asc' } },\n _react2.default.createElement(\n Thead,\n null,\n _react2.default.createElement(\n Th,\n { column: \"id\" },\n \"ID\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"host\" },\n \"Host\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"state\" },\n \"State\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsPending\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-pause\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Pending splits\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsRunning\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-play\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Running splits\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsBlocked\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-bookmark\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Blocked splits\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsDone\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-ok\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Completed splits\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsSkippedByIndex\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-share-alt\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Skipped splits by index\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"avgIndexReadTime\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-align-justify\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Average index read time per driver/split\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"rows\" },\n \"Rows\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"rowsSec\" },\n \"Rows/s\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"bytes\" },\n \"Bytes\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"bytesSec\" },\n \"Bytes/s\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"elapsedTime\" },\n \"Elapsed\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"cpuTime\" },\n \"CPU Time\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"bufferedBytes\" },\n \"Buffered\"\n )\n ),\n renderedTasks\n );\n }\n }], [{\n key: \"removeQueryId\",\n value: function removeQueryId(id) {\n var pos = id.indexOf('.');\n if (pos !== -1) {\n return id.substring(pos + 1);\n }\n return id;\n }\n }, {\n key: \"compareTaskId\",\n value: function compareTaskId(taskA, taskB) {\n var taskIdArrA = TaskList.removeQueryId(taskA).split(\".\");\n var taskIdArrB = TaskList.removeQueryId(taskB).split(\".\");\n\n if (taskIdArrA.length > taskIdArrB.length) {\n return 1;\n }\n for (var i = 0; i < taskIdArrA.length; i++) {\n var anum = Number.parseInt(taskIdArrA[i]);\n var bnum = Number.parseInt(taskIdArrB[i]);\n if (anum !== bnum) {\n return anum > bnum ? 1 : -1;\n }\n }\n\n return 0;\n }\n }, {\n key: \"showPortNumbers\",\n value: function showPortNumbers(tasks) {\n // check if any host has multiple port numbers\n var hostToPortNumber = {};\n for (var i = 0; i < tasks.length; i++) {\n var taskUri = tasks[i].taskStatus.self;\n var hostname = (0, _utils.getHostname)(taskUri);\n var port = (0, _utils.getPort)(taskUri);\n if (hostname in hostToPortNumber && hostToPortNumber[hostname] !== port) {\n return true;\n }\n hostToPortNumber[hostname] = port;\n }\n\n return false;\n }\n }, {\n key: \"formatState\",\n value: function formatState(state, fullyBlocked) {\n if (fullyBlocked && state === \"RUNNING\") {\n return \"BLOCKED\";\n } else {\n return state;\n }\n }\n }]);\n\n return TaskList;\n}(_react2.default.Component);\n\nvar DataSkippingMetricsList = function (_React$Component2) {\n _inherits(DataSkippingMetricsList, _React$Component2);\n\n function DataSkippingMetricsList() {\n _classCallCheck(this, DataSkippingMetricsList);\n\n return _possibleConstructorReturn(this, (DataSkippingMetricsList.__proto__ || Object.getPrototypeOf(DataSkippingMetricsList)).apply(this, arguments));\n }\n\n _createClass(DataSkippingMetricsList, [{\n key: \"formatMetricHeader\",\n value: function formatMetricHeader(metrics) {\n var header = [];\n if (metrics.isSum) {\n header.push(_react2.default.createElement(\n \"td\",\n { colSpan: \"2\" },\n \"SUM\"\n ));\n } else {\n header.push(_react2.default.createElement(\n \"td\",\n null,\n metrics.stageId + \"-\" + metrics.planNodeId\n ));\n header.push(_react2.default.createElement(\n \"td\",\n null,\n metrics.schemaName,\n \". \",\n _react2.default.createElement(\"br\", null),\n metrics.tableName\n ));\n }\n return header;\n }\n }, {\n key: \"formatMetricWithOption\",\n value: function formatMetricWithOption(metric, totalMetric) {\n switch (this.props.displayOption) {\n case \"DataSize\":\n return _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatDataSize)(metric.dataSize),\n \" \",\n _react2.default.createElement(\"br\", null),\n \"(\",\n (0, _utils.formatPercentage)(metric.dataSize, totalMetric.dataSize),\n \")\"\n );\n case \"SplitCount\":\n return _react2.default.createElement(\n \"td\",\n null,\n metric.splitCount,\n \" \",\n _react2.default.createElement(\"br\", null),\n \"(\",\n (0, _utils.formatPercentage)(metric.splitCount, totalMetric.splitCount),\n \")\"\n );\n default:\n return _react2.default.createElement(\n \"td\",\n null,\n \"N/A\"\n );\n }\n }\n }, {\n key: \"render\",\n value: function render() {\n var _this3 = this;\n\n var renderedMetricsList = this.props.metricsList.map(function (metrics) {\n return _react2.default.createElement(\n \"tr\",\n null,\n _this3.formatMetricHeader(metrics),\n _this3.formatMetricWithOption(metrics.total, metrics.total),\n _this3.formatMetricWithOption(metrics.read, metrics.total),\n _this3.formatMetricWithOption(metrics.skippedByMinmaxStats, metrics.total),\n _this3.formatMetricWithOption(metrics.skippedByDfInCoordinator, metrics.total),\n _this3.formatMetricWithOption(metrics.skippedByPartFilter, metrics.total),\n _this3.formatMetricWithOption(metrics.skippedByIndexInCoordinator, metrics.total),\n _this3.formatMetricWithOption(metrics.skippedByDfInWorker, metrics.total),\n _this3.formatMetricWithOption(metrics.skippedByIndexInWorker, metrics.total)\n );\n });\n\n return _react2.default.createElement(\n \"table\",\n { className: \"table\", id: \"data-skipping-metrics-list\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n { className: \"data-skipping-metrics-list-header-group\" },\n _react2.default.createElement(\n \"th\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"span\",\n { \"data-toggle\": \"tooltip\", \"data-placement\": \"top\", title: \"StageId-PlanNodeId\" },\n \"ID\"\n )\n ),\n _react2.default.createElement(\n \"th\",\n { rowSpan: \"2\" },\n \"Table\"\n ),\n _react2.default.createElement(\n \"th\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"span\",\n { \"data-toggle\": \"tooltip\", \"data-placement\": \"top\", title: \"Total in target partitions\" },\n \"Total\"\n )\n ),\n _react2.default.createElement(\n \"th\",\n { rowSpan: \"2\" },\n \"Read\"\n ),\n _react2.default.createElement(\n \"th\",\n { colSpan: \"4\" },\n \"Skipped In Coordinator\"\n ),\n _react2.default.createElement(\n \"th\",\n { colSpan: \"2\" },\n \"Skipped In Worker\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n null,\n \"Minmax\"\n ),\n _react2.default.createElement(\n \"th\",\n null,\n _react2.default.createElement(\n \"span\",\n { \"data-toggle\": \"tooltip\", \"data-placement\": \"top\", title: \"Dynamic Filter\" },\n \"Dyn. Filter\"\n )\n ),\n _react2.default.createElement(\n \"th\",\n null,\n _react2.default.createElement(\n \"span\",\n { \"data-toggle\": \"tooltip\", \"data-placement\": \"top\", title: \"Partition Filter For Complicated Constraints\" },\n \"Part. Filter\"\n )\n ),\n _react2.default.createElement(\n \"th\",\n null,\n _react2.default.createElement(\n \"span\",\n { \"data-toggle\": \"tooltip\", \"data-placement\": \"top\", title: \"In-place Index\" },\n \"Index\"\n )\n ),\n _react2.default.createElement(\n \"th\",\n null,\n _react2.default.createElement(\n \"span\",\n { \"data-toggle\": \"tooltip\", \"data-placement\": \"top\", title: \"Dynamic Filter\" },\n \"Dyn. Filter\"\n )\n ),\n _react2.default.createElement(\n \"th\",\n null,\n _react2.default.createElement(\n \"span\",\n { \"data-toggle\": \"tooltip\", \"data-placement\": \"top\", title: \"Non In-place Index\" },\n \"Index\"\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n renderedMetricsList\n )\n );\n }\n }]);\n\n return DataSkippingMetricsList;\n}(_react2.default.Component);\n\nvar BAR_CHART_WIDTH = 800;\n\nvar BAR_CHART_PROPERTIES = {\n type: 'bar',\n barSpacing: '0',\n height: '80px',\n barColor: '#747F96',\n zeroColor: '#8997B3',\n chartRangeMin: 0,\n tooltipClassname: 'sparkline-tooltip',\n tooltipFormat: 'Task {{offset:offset}} - {{value}}',\n disableHiddenCheck: true\n};\n\nvar HISTOGRAM_WIDTH = 175;\n\nvar HISTOGRAM_PROPERTIES = {\n type: 'bar',\n barSpacing: '0',\n height: '80px',\n barColor: '#747F96',\n zeroColor: '#747F96',\n zeroAxis: true,\n chartRangeMin: 0,\n tooltipClassname: 'sparkline-tooltip',\n tooltipFormat: '{{offset:offset}} -- {{value}} tasks',\n disableHiddenCheck: true\n};\n\nvar StageSummary = function (_React$Component3) {\n _inherits(StageSummary, _React$Component3);\n\n function StageSummary(props) {\n _classCallCheck(this, StageSummary);\n\n var _this4 = _possibleConstructorReturn(this, (StageSummary.__proto__ || Object.getPrototypeOf(StageSummary)).call(this, props));\n\n _this4.state = {\n expanded: false,\n lastRender: null\n };\n return _this4;\n }\n\n _createClass(StageSummary, [{\n key: \"getExpandedIcon\",\n value: function getExpandedIcon() {\n return this.state.expanded ? \"glyphicon-chevron-up\" : \"glyphicon-chevron-down\";\n }\n }, {\n key: \"getExpandedStyle\",\n value: function getExpandedStyle() {\n return this.state.expanded ? {} : { display: \"none\" };\n }\n }, {\n key: \"toggleExpanded\",\n value: function toggleExpanded() {\n this.setState({\n expanded: !this.state.expanded\n });\n }\n }, {\n key: \"componentDidUpdate\",\n value: function componentDidUpdate() {\n var stage = this.props.stage;\n var numTasks = stage.tasks.length;\n\n // sort the x-axis\n stage.tasks.sort(function (taskA, taskB) {\n return (0, _utils.getTaskNumber)(taskA.taskStatus.taskId) - (0, _utils.getTaskNumber)(taskB.taskStatus.taskId);\n });\n\n var scheduledTimes = stage.tasks.map(function (task) {\n return (0, _utils.parseDuration)(task.stats.totalScheduledTime);\n });\n var cpuTimes = stage.tasks.map(function (task) {\n return (0, _utils.parseDuration)(task.stats.totalCpuTime);\n });\n\n // prevent multiple calls to componentDidUpdate (resulting from calls to setState or otherwise) within the refresh interval from re-rendering sparklines/charts\n if (this.state.lastRender === null || Date.now() - this.state.lastRender >= 1000) {\n var renderTimestamp = Date.now();\n var stageId = (0, _utils.getStageNumber)(stage.stageId);\n\n StageSummary.renderHistogram('#scheduled-time-histogram-' + stageId, scheduledTimes, _utils.formatDuration);\n StageSummary.renderHistogram('#cpu-time-histogram-' + stageId, cpuTimes, _utils.formatDuration);\n\n if (this.state.expanded) {\n // this needs to be a string otherwise it will also be passed to numberFormatter\n var tooltipValueLookups = { 'offset': {} };\n for (var i = 0; i < numTasks; i++) {\n tooltipValueLookups['offset'][i] = (0, _utils.getStageNumber)(stage.stageId) + \".\" + i;\n }\n\n var stageBarChartProperties = $.extend({}, BAR_CHART_PROPERTIES, { barWidth: BAR_CHART_WIDTH / numTasks, tooltipValueLookups: tooltipValueLookups });\n\n $('#scheduled-time-bar-chart-' + stageId).sparkline(scheduledTimes, $.extend({}, stageBarChartProperties, { numberFormatter: _utils.formatDuration }));\n $('#cpu-time-bar-chart-' + stageId).sparkline(cpuTimes, $.extend({}, stageBarChartProperties, { numberFormatter: _utils.formatDuration }));\n }\n\n this.setState({\n lastRender: renderTimestamp\n });\n }\n }\n }, {\n key: \"render\",\n value: function render() {\n var stage = this.props.stage;\n if (stage === undefined || !stage.hasOwnProperty('plan')) {\n return _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Information about this stage is unavailable.\"\n )\n );\n }\n\n var totalBufferedBytes = stage.tasks.map(function (task) {\n return task.outputBuffers.totalBufferedBytes;\n }).reduce(function (a, b) {\n return a + b;\n }, 0);\n\n var stageId = (0, _utils.getStageNumber)(stage.stageId);\n\n return _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-id\" },\n _react2.default.createElement(\n \"div\",\n { className: \"stage-state-color\", style: { borderLeftColor: (0, _utils.getStageStateColor)(stage) } },\n stageId\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"table single-stage-table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table stage-table-time\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-stat-header\" },\n \"Time\"\n ),\n _react2.default.createElement(\"th\", null)\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Scheduled\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.stageStats.totalScheduledTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Blocked\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.stageStats.totalBlockedTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"CPU\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.stageStats.totalCpuTime\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table stage-table-memory\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-stat-header\" },\n \"Memory\"\n ),\n _react2.default.createElement(\"th\", null)\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Cumulative\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n (0, _utils.formatDataSizeBytes)(stage.stageStats.cumulativeUserMemory / 1000)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Current\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n (0, _utils.parseAndFormatDataSize)(stage.stageStats.userMemoryReservation)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Buffers\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n (0, _utils.formatDataSize)(totalBufferedBytes)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Peak\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n (0, _utils.parseAndFormatDataSize)(stage.stageStats.peakUserMemoryReservation)\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table stage-table-tasks\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-stat-header\" },\n \"Tasks\"\n ),\n _react2.default.createElement(\"th\", null)\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Pending\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.tasks.filter(function (task) {\n return task.taskStatus.state === \"PLANNED\";\n }).length\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Running\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.tasks.filter(function (task) {\n return task.taskStatus.state === \"RUNNING\";\n }).length\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Blocked\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.tasks.filter(function (task) {\n return task.stats.fullyBlocked;\n }).length\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Total\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.tasks.length\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table histogram-table\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-chart-header\" },\n \"Scheduled Time Skew\"\n )\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"histogram-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"histogram\", id: \"scheduled-time-histogram-\" + stageId },\n _react2.default.createElement(\"div\", { className: \"loader\" })\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table histogram-table\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-chart-header\" },\n \"CPU Time Skew\"\n )\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"histogram-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"histogram\", id: \"cpu-time-histogram-\" + stageId },\n _react2.default.createElement(\"div\", { className: \"loader\" })\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"expand-charts-container\" },\n _react2.default.createElement(\n \"a\",\n { onClick: this.toggleExpanded.bind(this), className: \"expand-charts-button\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon \" + this.getExpandedIcon(), style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\", title: \"More\" })\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { style: this.getExpandedStyle() },\n _react2.default.createElement(\n \"td\",\n { colSpan: \"6\" },\n _react2.default.createElement(\n \"table\",\n { className: \"expanded-chart\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title expanded-chart-title\" },\n \"Task Scheduled Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"bar-chart-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"bar-chart\", id: \"scheduled-time-bar-chart-\" + stageId },\n _react2.default.createElement(\"div\", { className: \"loader\" })\n )\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { style: this.getExpandedStyle() },\n _react2.default.createElement(\n \"td\",\n { colSpan: \"6\" },\n _react2.default.createElement(\n \"table\",\n { className: \"expanded-chart\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title expanded-chart-title\" },\n \"Task CPU Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"bar-chart-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"bar-chart\", id: \"cpu-time-bar-chart-\" + stageId },\n _react2.default.createElement(\"div\", { className: \"loader\" })\n )\n )\n )\n )\n )\n )\n )\n )\n )\n )\n );\n }\n }], [{\n key: \"renderHistogram\",\n value: function renderHistogram(histogramId, inputData, numberFormatter) {\n var numBuckets = Math.min(HISTOGRAM_WIDTH, Math.sqrt(inputData.length));\n var dataMin = Math.min.apply(null, inputData);\n var dataMax = Math.max.apply(null, inputData);\n var bucketSize = (dataMax - dataMin) / numBuckets;\n\n var histogramData = [];\n if (bucketSize === 0) {\n histogramData = [inputData.length];\n } else {\n for (var i = 0; i < numBuckets + 1; i++) {\n histogramData.push(0);\n }\n\n for (var _i in inputData) {\n var dataPoint = inputData[_i];\n var bucket = Math.floor((dataPoint - dataMin) / bucketSize);\n histogramData[bucket] = histogramData[bucket] + 1;\n }\n }\n\n var tooltipValueLookups = { 'offset': {} };\n for (var _i2 = 0; _i2 < histogramData.length; _i2++) {\n tooltipValueLookups['offset'][_i2] = numberFormatter(dataMin + _i2 * bucketSize) + \"-\" + numberFormatter(dataMin + (_i2 + 1) * bucketSize);\n }\n\n var stageHistogramProperties = $.extend({}, HISTOGRAM_PROPERTIES, { barWidth: HISTOGRAM_WIDTH / histogramData.length, tooltipValueLookups: tooltipValueLookups });\n $(histogramId).sparkline(histogramData, stageHistogramProperties);\n }\n }]);\n\n return StageSummary;\n}(_react2.default.Component);\n\nvar StageList = function (_React$Component4) {\n _inherits(StageList, _React$Component4);\n\n function StageList() {\n _classCallCheck(this, StageList);\n\n return _possibleConstructorReturn(this, (StageList.__proto__ || Object.getPrototypeOf(StageList)).apply(this, arguments));\n }\n\n _createClass(StageList, [{\n key: \"getStages\",\n value: function getStages(stage) {\n if (stage === undefined || !stage.hasOwnProperty('subStages')) {\n return [];\n }\n\n return [].concat.apply(stage, stage.subStages.map(this.getStages, this));\n }\n }, {\n key: \"render\",\n value: function render() {\n var stages = this.getStages(this.props.outputStage);\n\n if (stages === undefined || stages.length === 0) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n \"No stage information available.\"\n )\n );\n }\n\n var renderedStages = stages.map(function (stage) {\n return _react2.default.createElement(StageSummary, { key: stage.stageId, stage: stage });\n });\n\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"table\",\n { className: \"table\", id: \"stage-list\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n renderedStages\n )\n )\n )\n );\n }\n }]);\n\n return StageList;\n}(_react2.default.Component);\n\nvar SMALL_SPARKLINE_PROPERTIES = {\n width: '100%',\n height: '57px',\n fillColor: '#3F4552',\n lineColor: '#747F96',\n spotColor: '#1EDCFF',\n tooltipClassname: 'sparkline-tooltip',\n disableHiddenCheck: true\n};\n\nvar TASK_FILTER = {\n ALL: function ALL() {\n return true;\n },\n PLANNED: function PLANNED(state) {\n return state === 'PLANNED';\n },\n RUNNING: function RUNNING(state) {\n return state === 'RUNNING';\n },\n FINISHED: function FINISHED(state) {\n return state === 'FINISHED';\n },\n FAILED: function FAILED(state) {\n return state === 'FAILED' || state === 'ABORTED' || state === 'CANCELED';\n }\n};\n\nvar QueryDetail = exports.QueryDetail = function (_React$Component5) {\n _inherits(QueryDetail, _React$Component5);\n\n function QueryDetail(props) {\n _classCallCheck(this, QueryDetail);\n\n var _this6 = _possibleConstructorReturn(this, (QueryDetail.__proto__ || Object.getPrototypeOf(QueryDetail)).call(this, props));\n\n _this6.state = {\n query: null,\n lastSnapshotStages: null,\n lastSnapshotTasks: null,\n\n lastScheduledTime: 0,\n lastCpuTime: 0,\n lastRowInput: 0,\n lastByteInput: 0,\n lastPhysicalInput: 0,\n lastPhysicalTime: 0,\n\n scheduledTimeRate: [],\n cpuTimeRate: [],\n rowInputRate: [],\n byteInputRate: [],\n physicalInputRate: [],\n\n reservedMemory: [],\n\n initialized: false,\n queryEnded: false,\n renderingEnded: false,\n\n lastRefresh: null,\n lastRender: null,\n\n stageRefresh: true,\n taskRefresh: true,\n\n dataSkippingMetricsList: null,\n metricsDisplayOption: \"DataSize\",\n\n taskFilter: TASK_FILTER.ALL\n };\n\n _this6.refreshLoop = _this6.refreshLoop.bind(_this6);\n return _this6;\n }\n\n _createClass(QueryDetail, [{\n key: \"resetTimer\",\n value: function resetTimer() {\n clearTimeout(this.timeoutId);\n // stop refreshing when query finishes or fails\n if (this.state.query === null || !this.state.queryEnded) {\n // task.info-update-interval is set to 3 seconds by default\n this.timeoutId = setTimeout(this.refreshLoop, 3000);\n }\n }\n }, {\n key: \"refreshLoop\",\n value: function refreshLoop() {\n var _this7 = this;\n\n clearTimeout(this.timeoutId); // to stop multiple series of refreshLoop from going on simultaneously\n var queryId = (0, _utils.getFirstParameter)(window.location.search);\n $.get('/ui/api/query/' + queryId, function (query) {\n var lastSnapshotStages = this.state.lastSnapshotStage;\n if (this.state.stageRefresh) {\n lastSnapshotStages = query.outputStage;\n }\n var lastSnapshotTasks = this.state.lastSnapshotTasks;\n if (this.state.taskRefresh) {\n lastSnapshotTasks = query.outputStage;\n }\n\n var lastRefresh = this.state.lastRefresh;\n var lastScheduledTime = this.state.lastScheduledTime;\n var lastCpuTime = this.state.lastCpuTime;\n var lastRowInput = this.state.lastRowInput;\n var lastByteInput = this.state.lastByteInput;\n var lastPhysicalInput = this.state.lastPhysicalInput;\n var lastPhysicalTime = this.state.lastPhysicalTime;\n var alreadyEnded = this.state.queryEnded;\n var nowMillis = Date.now();\n\n this.setState({\n query: query,\n lastSnapshotStage: lastSnapshotStages,\n lastSnapshotTasks: lastSnapshotTasks,\n\n lastPhysicalTime: (0, _utils.parseDuration)(query.queryStats.physicalInputReadTime),\n lastScheduledTime: (0, _utils.parseDuration)(query.queryStats.totalScheduledTime),\n lastCpuTime: (0, _utils.parseDuration)(query.queryStats.totalCpuTime),\n lastRowInput: query.queryStats.processedInputPositions,\n lastByteInput: (0, _utils.parseDataSize)(query.queryStats.processedInputDataSize),\n lastPhysicalInput: (0, _utils.parseDataSize)(query.queryStats.physicalInputDataSize),\n\n initialized: true,\n queryEnded: !!query.finalQueryInfo,\n\n lastRefresh: nowMillis\n });\n\n // i.e. don't show sparklines if we've already decided not to update or if we don't have one previous measurement\n if (alreadyEnded || lastRefresh === null && query.state === \"RUNNING\") {\n this.resetTimer();\n return;\n }\n\n if (lastRefresh === null) {\n lastRefresh = nowMillis - (0, _utils.parseDuration)(query.queryStats.elapsedTime);\n }\n\n var elapsedSecsSinceLastRefresh = (nowMillis - lastRefresh) / 1000.0;\n if (elapsedSecsSinceLastRefresh >= 0) {\n var currentScheduledTimeRate = ((0, _utils.parseDuration)(query.queryStats.totalScheduledTime) - lastScheduledTime) / (elapsedSecsSinceLastRefresh * 1000);\n var currentCpuTimeRate = ((0, _utils.parseDuration)(query.queryStats.totalCpuTime) - lastCpuTime) / (elapsedSecsSinceLastRefresh * 1000);\n var currentPhysicalReadTime = ((0, _utils.parseDuration)(query.queryStats.physicalInputReadTime) - lastPhysicalTime) / 1000;\n var currentRowInputRate = (query.queryStats.processedInputPositions - lastRowInput) / elapsedSecsSinceLastRefresh;\n var currentByteInputRate = ((0, _utils.parseDataSize)(query.queryStats.processedInputDataSize) - lastByteInput) / elapsedSecsSinceLastRefresh;\n var currentPhysicalInputRate = currentPhysicalReadTime > 0 ? ((0, _utils.parseDataSize)(query.queryStats.physicalInputDataSize) - lastPhysicalInput) / currentPhysicalReadTime : 0;\n\n this.setState({\n scheduledTimeRate: (0, _utils.addToHistory)(currentScheduledTimeRate, this.state.scheduledTimeRate),\n cpuTimeRate: (0, _utils.addToHistory)(currentCpuTimeRate, this.state.cpuTimeRate),\n rowInputRate: (0, _utils.addToHistory)(currentRowInputRate, this.state.rowInputRate),\n byteInputRate: (0, _utils.addToHistory)(currentByteInputRate, this.state.byteInputRate),\n reservedMemory: (0, _utils.addToHistory)((0, _utils.parseDataSize)(query.queryStats.totalMemoryReservation), this.state.reservedMemory),\n physicalInputRate: (0, _utils.addToHistory)(currentPhysicalInputRate, this.state.physicalInputRate)\n });\n }\n this.resetTimer();\n }.bind(this)).fail(function () {\n _this7.setState({\n initialized: true\n });\n _this7.resetTimer();\n });\n }\n }, {\n key: \"handleTaskRefreshClick\",\n value: function handleTaskRefreshClick() {\n if (this.state.taskRefresh) {\n this.setState({\n taskRefresh: false,\n lastSnapshotTasks: this.state.query.outputStage\n });\n } else {\n this.setState({\n taskRefresh: true\n });\n }\n }\n }, {\n key: \"renderTaskRefreshButton\",\n value: function renderTaskRefreshButton() {\n if (this.state.taskRefresh) {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleTaskRefreshClick.bind(this) },\n \"Auto-Refresh: On\"\n );\n } else {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleTaskRefreshClick.bind(this) },\n \"Auto-Refresh: Off\"\n );\n }\n }\n }, {\n key: \"handleStageRefreshClick\",\n value: function handleStageRefreshClick() {\n if (this.state.stageRefresh) {\n this.setState({\n stageRefresh: false,\n lastSnapshotStages: this.state.query.outputStage\n });\n } else {\n this.setState({\n stageRefresh: true\n });\n }\n }\n }, {\n key: \"renderStageRefreshButton\",\n value: function renderStageRefreshButton() {\n if (this.state.stageRefresh) {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleStageRefreshClick.bind(this) },\n \"Auto-Refresh: On\"\n );\n } else {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleStageRefreshClick.bind(this) },\n \"Auto-Refresh: Off\"\n );\n }\n }\n }, {\n key: \"renderTaskFilterListItem\",\n value: function renderTaskFilterListItem(taskFilter, taskFilterText) {\n return _react2.default.createElement(\n \"li\",\n null,\n _react2.default.createElement(\n \"a\",\n { href: \"#\", className: this.state.taskFilter === taskFilter ? \"selected\" : \"\", onClick: this.handleTaskFilterClick.bind(this, taskFilter) },\n taskFilterText\n )\n );\n }\n }, {\n key: \"handleTaskFilterClick\",\n value: function handleTaskFilterClick(filter, event) {\n this.setState({\n taskFilter: filter\n });\n event.preventDefault();\n }\n }, {\n key: \"getTasksFromStage\",\n value: function getTasksFromStage(stage) {\n if (stage === undefined || !stage.hasOwnProperty('subStages') || !stage.hasOwnProperty('tasks')) {\n return [];\n }\n\n return [].concat.apply(stage.tasks, stage.subStages.map(this.getTasksFromStage, this));\n }\n }, {\n key: \"componentDidMount\",\n value: function componentDidMount() {\n this.refreshLoop();\n }\n }, {\n key: \"componentDidUpdate\",\n value: function componentDidUpdate() {\n // prevent multiple calls to componentDidUpdate (resulting from calls to setState or otherwise) within the refresh interval from re-rendering sparklines/charts\n if (this.state.lastRender === null || Date.now() - this.state.lastRender >= 1000 || this.state.ended && !this.state.renderingEnded) {\n var renderTimestamp = Date.now();\n $('#scheduled-time-rate-sparkline').sparkline(this.state.scheduledTimeRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, {\n chartRangeMin: 0,\n numberFormatter: _utils.precisionRound\n }));\n $('#cpu-time-rate-sparkline').sparkline(this.state.cpuTimeRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { chartRangeMin: 0, numberFormatter: _utils.precisionRound }));\n $('#row-input-rate-sparkline').sparkline(this.state.rowInputRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { numberFormatter: _utils.formatCount }));\n $('#byte-input-rate-sparkline').sparkline(this.state.byteInputRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { numberFormatter: _utils.formatDataSize }));\n $('#reserved-memory-sparkline').sparkline(this.state.reservedMemory, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { numberFormatter: _utils.formatDataSize }));\n $('#physical-input-rate-sparkline').sparkline(this.state.physicalInputRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { numberFormatter: _utils.formatDataSize }));\n\n if (this.state.lastRender === null) {\n $('#query').each(function (i, block) {\n hljs.highlightBlock(block);\n });\n\n $('#prepared-query').each(function (i, block) {\n hljs.highlightBlock(block);\n });\n }\n\n this.setState({\n renderingEnded: this.state.ended,\n lastRender: renderTimestamp\n });\n }\n\n $('[data-toggle=\"tooltip\"]').tooltip();\n new window.ClipboardJS('.copy-button');\n }\n }, {\n key: \"renderTasks\",\n value: function renderTasks() {\n var _this8 = this;\n\n if (this.state.lastSnapshotTasks === null) {\n return;\n }\n\n var tasks = this.getTasksFromStage(this.state.lastSnapshotTasks).filter(function (task) {\n return _this8.state.taskFilter(task.taskStatus.state);\n }, this);\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-9\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Tasks\"\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-3\" },\n _react2.default.createElement(\n \"table\",\n { className: \"header-inline-links\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"div\",\n { className: \"input-group-btn text-right\" },\n _react2.default.createElement(\n \"button\",\n { type: \"button\", className: \"btn btn-default dropdown-toggle pull-right text-right\", \"data-toggle\": \"dropdown\", \"aria-haspopup\": \"true\",\n \"aria-expanded\": \"false\" },\n \"Show \",\n _react2.default.createElement(\"span\", { className: \"caret\" })\n ),\n _react2.default.createElement(\n \"ul\",\n { className: \"dropdown-menu\" },\n this.renderTaskFilterListItem(TASK_FILTER.ALL, \"All\"),\n this.renderTaskFilterListItem(TASK_FILTER.PLANNED, \"Planned\"),\n this.renderTaskFilterListItem(TASK_FILTER.RUNNING, \"Running\"),\n this.renderTaskFilterListItem(TASK_FILTER.FINISHED, \"Finished\"),\n this.renderTaskFilterListItem(TASK_FILTER.FAILED, \"Aborted/Canceled/Failed\")\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n \"\\xA0\\xA0\",\n this.renderTaskRefreshButton()\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(TaskList, { key: this.state.query.queryId, tasks: tasks })\n )\n )\n );\n }\n }, {\n key: \"renderStages\",\n value: function renderStages() {\n if (this.state.lastSnapshotStage === null) {\n return;\n }\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-9\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Stages\"\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-3\" },\n _react2.default.createElement(\n \"table\",\n { className: \"header-inline-links\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n this.renderStageRefreshButton()\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(StageList, { key: this.state.query.queryId, outputStage: this.state.lastSnapshotStage })\n )\n )\n );\n }\n }, {\n key: \"renderPreparedQuery\",\n value: function renderPreparedQuery() {\n var query = this.state.query;\n if (!query.hasOwnProperty('preparedQuery') || query.preparedQuery === null) {\n return;\n }\n\n return _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Prepared Query\",\n _react2.default.createElement(\n \"a\",\n { className: \"btn copy-button\", \"data-clipboard-target\": \"#prepared-query-text\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n ),\n _react2.default.createElement(\n \"pre\",\n { id: \"prepared-query\" },\n _react2.default.createElement(\n \"code\",\n { className: \"lang-sql\", id: \"prepared-query-text\" },\n query.preparedQuery\n )\n )\n );\n }\n }, {\n key: \"renderSessionProperties\",\n value: function renderSessionProperties() {\n var query = this.state.query;\n\n var properties = [];\n for (var property in query.session.systemProperties) {\n if (query.session.systemProperties.hasOwnProperty(property)) {\n properties.push(_react2.default.createElement(\n \"span\",\n null,\n \"- \",\n property + \"=\" + query.session.systemProperties[property],\n \" \",\n _react2.default.createElement(\"br\", null)\n ));\n }\n }\n\n for (var catalog in query.session.catalogProperties) {\n if (query.session.catalogProperties.hasOwnProperty(catalog)) {\n for (var _property in query.session.catalogProperties[catalog]) {\n if (query.session.catalogProperties[catalog].hasOwnProperty(_property)) {\n properties.push(_react2.default.createElement(\n \"span\",\n null,\n \"- \",\n catalog + \".\" + _property + \"=\" + query.session.catalogProperties[catalog][_property],\n \" \",\n _react2.default.createElement(\"br\", null)\n ));\n }\n }\n }\n }\n\n return properties;\n }\n }, {\n key: \"renderResourceEstimates\",\n value: function renderResourceEstimates() {\n var query = this.state.query;\n var estimates = query.session.resourceEstimates;\n var renderedEstimates = [];\n\n for (var resource in estimates) {\n if (estimates.hasOwnProperty(resource)) {\n var upperChars = resource.match(/([A-Z])/g) || [];\n var snakeCased = resource;\n for (var i = 0, n = upperChars.length; i < n; i++) {\n snakeCased = snakeCased.replace(new RegExp(upperChars[i]), '_' + upperChars[i].toLowerCase());\n }\n\n renderedEstimates.push(_react2.default.createElement(\n \"span\",\n null,\n \"- \",\n snakeCased + \"=\" + query.session.resourceEstimates[resource],\n \" \",\n _react2.default.createElement(\"br\", null)\n ));\n }\n }\n\n return renderedEstimates;\n }\n }, {\n key: \"renderDataSkippingMetrics\",\n value: function renderDataSkippingMetrics() {\n if (this.state.dataSkippingMetricsList === null) {\n var connectorMetrics = this.state.query.queryStats.connectorMetrics;\n var defaultMetric = { splitCount: 0, dataSize: 0 };\n this.state.dataSkippingMetricsList = [];\n if (connectorMetrics && connectorMetrics.hasOwnProperty('iceberg_data_skipping_metrics_table_level')) {\n var metricsMap = connectorMetrics.iceberg_data_skipping_metrics_table_level;\n if (metricsMap) {\n for (var tableKey in metricsMap) {\n if (tableKey !== \"@class\" && metricsMap.hasOwnProperty(tableKey)) {\n var tableKeyArray = tableKey.split(\":\");\n var tableNameArray = tableKeyArray[2].split(\".\");\n this.state.dataSkippingMetricsList.push({\n isSum: false,\n stageId: parseInt(tableKeyArray[0]),\n planNodeId: parseInt(tableKeyArray[1]),\n schemaName: tableNameArray[0],\n tableName: tableNameArray[1],\n total: metricsMap[tableKey]['TOTAL'] || defaultMetric,\n read: metricsMap[tableKey]['READ'] || defaultMetric,\n skippedByMinmaxStats: metricsMap[tableKey]['SKIPPED_BY_MINMAX_STATS'] || defaultMetric,\n skippedByIndexInCoordinator: metricsMap[tableKey]['SKIPPED_BY_INDEX_IN_COORDINATOR'] || defaultMetric,\n skippedByIndexInWorker: metricsMap[tableKey]['SKIPPED_BY_INDEX_IN_WORKER'] || defaultMetric,\n skippedByDfInCoordinator: metricsMap[tableKey]['SKIPPED_BY_DF_IN_COORDINATOR'] || defaultMetric,\n skippedByDfInWorker: metricsMap[tableKey]['SKIPPED_BY_DF_IN_WORKER'] || defaultMetric,\n skippedByPartFilter: metricsMap[tableKey]['SKIPPED_BY_PART_FILTER'] || defaultMetric\n });\n }\n }\n }\n this.state.dataSkippingMetricsList.sort(function (metricsA, metricsB) {\n if (metricsA.stageId !== metricsB.stageId) {\n return metricsA.stageId > metricsB.stageId ? 1 : -1;\n }\n if (metricsA.planNodeId !== metricsB.planNodeId) {\n return metricsA.planNodeId > metricsB.planNodeId ? 1 : -1;\n }\n return 0;\n });\n }\n\n if (connectorMetrics && connectorMetrics.hasOwnProperty('iceberg_data_skipping_metrics')) {\n var sumMetrics = connectorMetrics.iceberg_data_skipping_metrics;\n if (sumMetrics) {\n this.state.dataSkippingMetricsList.push({\n isSum: true,\n total: sumMetrics['TOTAL'] || defaultMetric,\n read: sumMetrics['READ'] || defaultMetric,\n skippedByMinmaxStats: sumMetrics['SKIPPED_BY_MINMAX_STATS'] || defaultMetric,\n skippedByIndexInCoordinator: sumMetrics['SKIPPED_BY_INDEX_IN_COORDINATOR'] || defaultMetric,\n skippedByIndexInWorker: sumMetrics['SKIPPED_BY_INDEX_IN_WORKER'] || defaultMetric,\n skippedByDfInCoordinator: sumMetrics['SKIPPED_BY_DF_IN_COORDINATOR'] || defaultMetric,\n skippedByDfInWorker: sumMetrics['SKIPPED_BY_DF_IN_WORKER'] || defaultMetric,\n skippedByPartFilter: sumMetrics['SKIPPED_BY_PART_FILTER'] || defaultMetric\n });\n }\n }\n }\n\n if (this.state.dataSkippingMetricsList.length === 0) {\n return;\n }\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-9\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Data Skipping Metrics (Iceberg Only)\"\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-3\" },\n _react2.default.createElement(\n \"table\",\n { className: \"header-inline-links\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n this.renderMetricsDisplayOptionButton()\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(DataSkippingMetricsList, {\n key: this.state.query.queryId,\n metricsList: this.state.dataSkippingMetricsList,\n displayOption: this.state.metricsDisplayOption })\n )\n )\n );\n }\n }, {\n key: \"renderMetricsDisplayOptionButton\",\n value: function renderMetricsDisplayOptionButton() {\n if (this.state.metricsDisplayOption === \"DataSize\") {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleMetricsDisplayOptionClick.bind(this) },\n \"Display Metrics In Split Count\"\n );\n } else if (this.state.metricsDisplayOption === \"SplitCount\") {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleMetricsDisplayOptionClick.bind(this) },\n \"Display Metrics In Data Size\"\n );\n }\n }\n }, {\n key: \"handleMetricsDisplayOptionClick\",\n value: function handleMetricsDisplayOptionClick() {\n if (this.state.metricsDisplayOption === \"DataSize\") {\n this.setState({\n metricsDisplayOption: \"SplitCount\"\n });\n } else {\n this.setState({\n metricsDisplayOption: \"DataSize\"\n });\n }\n }\n }, {\n key: \"renderWarningInfo\",\n value: function renderWarningInfo() {\n var query = this.state.query;\n if (query.warnings.length > 0) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Warnings\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\", id: \"warnings-table\" },\n query.warnings.map(function (warning) {\n return _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n warning.warningCode.name\n ),\n _react2.default.createElement(\n \"td\",\n null,\n warning.message\n )\n );\n })\n )\n )\n );\n } else {\n return null;\n }\n }\n }, {\n key: \"renderFailureInfo\",\n value: function renderFailureInfo() {\n var query = this.state.query;\n if (query.failureInfo) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Error Information\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Error Type\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.errorType\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Error Code\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.errorCode.name + \" (\" + this.state.query.errorCode.code + \")\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Stack Trace\",\n _react2.default.createElement(\n \"a\",\n { className: \"btn copy-button\", \"data-clipboard-target\": \"#stack-trace\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n _react2.default.createElement(\n \"pre\",\n { id: \"stack-trace\" },\n QueryDetail.formatStackTrace(query.failureInfo)\n )\n )\n )\n )\n )\n )\n );\n } else {\n return \"\";\n }\n }\n }, {\n key: \"render\",\n value: function render() {\n var query = this.state.query;\n\n if (query === null || this.state.initialized === false) {\n var label = _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading...\"\n );\n if (this.state.initialized) {\n label = \"Query not found\";\n }\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n label\n )\n )\n );\n }\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(_QueryHeader.QueryHeader, { query: query }),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Session\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"User\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n _react2.default.createElement(\n \"span\",\n { id: \"query-user\" },\n query.session.user\n ),\n \"\\xA0\\xA0\",\n _react2.default.createElement(\n \"a\",\n { href: \"#\", className: \"copy-button\", \"data-clipboard-target\": \"#query-user\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Principal\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n query.session.principal\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Source\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n query.session.source\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Catalog\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.catalog\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Schema\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.schema\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Time zone\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.timeZone\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Client Address\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.remoteUserAddress\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Client Tags\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.clientTags.join(\", \")\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Session Properties\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n this.renderSessionProperties()\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Resource Estimates\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n this.renderResourceEstimates()\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Execution\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Resource Group\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n query.resourceGroupId ? query.resourceGroupId.join(\".\") : \"n/a\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Submission Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatShortDateTime)(new Date(query.queryStats.createTime))\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Completion Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.endTime ? (0, _utils.formatShortDateTime)(new Date(query.queryStats.endTime)) : \"\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Elapsed Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.elapsedTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Queued Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.queuedTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Analysis Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.analysisTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Planning Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.planningTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Execution Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.executionTime\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Resource Utilization Summary\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"CPU Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.totalCpuTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Scheduled Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.totalScheduledTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Input Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.processedInputPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Input Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.processedInputDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Input Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.physicalInputPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Input Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.physicalInputDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Input Read Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.physicalInputReadTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Internal Network Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.internalNetworkInputPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Internal Network Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.internalNetworkInputDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Peak User Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.peakUserMemoryReservation)\n )\n ),\n (0, _utils.parseDataSize)(query.queryStats.peakRevocableMemoryReservation) > 0 && _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Peak Revocable Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.peakRevocableMemoryReservation)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Peak Total Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.peakTotalMemoryReservation)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Memory Pool\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.memoryPool\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Cumulative User Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatDataSizeBytes)(query.queryStats.cumulativeUserMemory / 1000.0) + \" seconds\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Cumulative System Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatDataSizeBytes)(query.queryStats.cumulativeSystemMemory / 1000.0) + \" seconds\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Output Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.outputPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Output Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.outputDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Written Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.writtenPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Logical Written Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.logicalWrittenDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Written Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.physicalWrittenDataSize)\n )\n ),\n (0, _utils.parseDataSize)(query.queryStats.spilledDataSize) > 0 && _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Spilled Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.spilledDataSize)\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Timeline\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Parallelism\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"cpu-time-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatCount)(this.state.cpuTimeRate[this.state.cpuTimeRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Scheduled Time/s\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"scheduled-time-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatCount)(this.state.scheduledTimeRate[this.state.scheduledTimeRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Input Rows/s\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"row-input-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatCount)(this.state.rowInputRate[this.state.rowInputRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Input Bytes/s\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"byte-input-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatDataSize)(this.state.byteInputRate[this.state.byteInputRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Input Bytes/s\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"physical-input-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatDataSize)(this.state.physicalInputRate[this.state.physicalInputRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Memory Utilization\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"reserved-memory-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatDataSize)(this.state.reservedMemory[this.state.reservedMemory.length - 1])\n )\n )\n )\n )\n )\n )\n )\n ),\n this.renderDataSkippingMetrics(),\n this.renderWarningInfo(),\n this.renderFailureInfo(),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Query\",\n _react2.default.createElement(\n \"a\",\n { className: \"btn copy-button\", \"data-clipboard-target\": \"#query-text\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n ),\n _react2.default.createElement(\n \"pre\",\n { id: \"query\" },\n _react2.default.createElement(\n \"code\",\n { className: \"lang-sql\", id: \"query-text\" },\n query.query\n )\n )\n ),\n this.renderPreparedQuery()\n ),\n this.renderStages(),\n this.renderTasks()\n );\n }\n }], [{\n key: \"formatStackTrace\",\n value: function formatStackTrace(info) {\n return QueryDetail.formatStackTraceHelper(info, [], \"\", \"\");\n }\n }, {\n key: \"formatStackTraceHelper\",\n value: function formatStackTraceHelper(info, parentStack, prefix, linePrefix) {\n var s = linePrefix + prefix + QueryDetail.failureInfoToString(info) + \"\\n\";\n\n if (info.stack) {\n var sharedStackFrames = 0;\n if (parentStack !== null) {\n sharedStackFrames = QueryDetail.countSharedStackFrames(info.stack, parentStack);\n }\n\n for (var i = 0; i < info.stack.length - sharedStackFrames; i++) {\n s += linePrefix + \"\\tat \" + info.stack[i] + \"\\n\";\n }\n if (sharedStackFrames !== 0) {\n s += linePrefix + \"\\t... \" + sharedStackFrames + \" more\" + \"\\n\";\n }\n }\n\n if (info.suppressed) {\n for (var _i3 = 0; _i3 < info.suppressed.length; _i3++) {\n s += QueryDetail.formatStackTraceHelper(info.suppressed[_i3], info.stack, \"Suppressed: \", linePrefix + \"\\t\");\n }\n }\n\n if (info.cause) {\n s += QueryDetail.formatStackTraceHelper(info.cause, info.stack, \"Caused by: \", linePrefix);\n }\n\n return s;\n }\n }, {\n key: \"countSharedStackFrames\",\n value: function countSharedStackFrames(stack, parentStack) {\n var n = 0;\n var minStackLength = Math.min(stack.length, parentStack.length);\n while (n < minStackLength && stack[stack.length - 1 - n] === parentStack[parentStack.length - 1 - n]) {\n n++;\n }\n return n;\n }\n }, {\n key: \"failureInfoToString\",\n value: function failureInfoToString(t) {\n return t.message !== null ? t.type + \": \" + t.message : t.type;\n }\n }]);\n\n return QueryDetail;\n}(_react2.default.Component);\n\n//# sourceURL=webpack:///./components/QueryDetail.jsx?"); /***/ }), @@ -20820,7 +20820,7 @@ eval("\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/i /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.getTotalFromLongCountMetrics = getTotalFromLongCountMetrics;\nexports.getCountWithPercentage = getCountWithPercentage;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n break;\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n break;\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n break;\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\nfunction getTotalFromLongCountMetrics(longCount) {\n if (longCount == null || longCount.total == null) {\n return 0;\n }\n return longCount.total;\n}\n\nfunction getCountWithPercentage(numerator, denominator) {\n if (denominator !== 0) {\n var percentage = numerator / denominator * 100;\n return numerator.toString() + ' (' + precisionRound(percentage) + '%)';\n }\n return numerator.toString();\n}\n\n//# sourceURL=webpack:///./utils.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.getTotalFromLongCountMetrics = getTotalFromLongCountMetrics;\nexports.formatPercentage = formatPercentage;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n break;\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n break;\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n break;\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\nfunction getTotalFromLongCountMetrics(longCount) {\n if (longCount == null || longCount.total == null) {\n return 0;\n }\n return longCount.total;\n}\n\nfunction formatPercentage(numerator, denominator) {\n var percentage = denominator === 0 ? 0 : numerator / denominator * 100;\n return precisionRound(percentage) + \"%\";\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/core/trino-main/src/main/resources/webapp/dist/stage.js b/core/trino-main/src/main/resources/webapp/dist/stage.js index 3a219c2e3ba34..ea5b2b34feaaa 100644 --- a/core/trino-main/src/main/resources/webapp/dist/stage.js +++ b/core/trino-main/src/main/resources/webapp/dist/stage.js @@ -20664,7 +20664,7 @@ eval("\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/i /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.getTotalFromLongCountMetrics = getTotalFromLongCountMetrics;\nexports.getCountWithPercentage = getCountWithPercentage;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n break;\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n break;\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n break;\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\nfunction getTotalFromLongCountMetrics(longCount) {\n if (longCount == null || longCount.total == null) {\n return 0;\n }\n return longCount.total;\n}\n\nfunction getCountWithPercentage(numerator, denominator) {\n if (denominator !== 0) {\n var percentage = numerator / denominator * 100;\n return numerator.toString() + ' (' + precisionRound(percentage) + '%)';\n }\n return numerator.toString();\n}\n\n//# sourceURL=webpack:///./utils.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.getTotalFromLongCountMetrics = getTotalFromLongCountMetrics;\nexports.formatPercentage = formatPercentage;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n break;\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n break;\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n break;\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\nfunction getTotalFromLongCountMetrics(longCount) {\n if (longCount == null || longCount.total == null) {\n return 0;\n }\n return longCount.total;\n}\n\nfunction formatPercentage(numerator, denominator) {\n var percentage = denominator === 0 ? 0 : numerator / denominator * 100;\n return precisionRound(percentage) + \"%\";\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/core/trino-main/src/main/resources/webapp/dist/worker.js b/core/trino-main/src/main/resources/webapp/dist/worker.js index fa90e2e8970ac..bbe628576628f 100644 --- a/core/trino-main/src/main/resources/webapp/dist/worker.js +++ b/core/trino-main/src/main/resources/webapp/dist/worker.js @@ -20616,7 +20616,7 @@ eval("module.exports = function(module) {\n\tif (!module.webpackPolyfill) {\n\t\ /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.getTotalFromLongCountMetrics = getTotalFromLongCountMetrics;\nexports.getCountWithPercentage = getCountWithPercentage;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n break;\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n break;\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n break;\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\nfunction getTotalFromLongCountMetrics(longCount) {\n if (longCount == null || longCount.total == null) {\n return 0;\n }\n return longCount.total;\n}\n\nfunction getCountWithPercentage(numerator, denominator) {\n if (denominator !== 0) {\n var percentage = numerator / denominator * 100;\n return numerator.toString() + ' (' + precisionRound(percentage) + '%)';\n }\n return numerator.toString();\n}\n\n//# sourceURL=webpack:///./utils.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.getTotalFromLongCountMetrics = getTotalFromLongCountMetrics;\nexports.formatPercentage = formatPercentage;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n break;\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n break;\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n break;\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\nfunction getTotalFromLongCountMetrics(longCount) {\n if (longCount == null || longCount.total == null) {\n return 0;\n }\n return longCount.total;\n}\n\nfunction formatPercentage(numerator, denominator) {\n var percentage = denominator === 0 ? 0 : numerator / denominator * 100;\n return precisionRound(percentage) + \"%\";\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }), diff --git a/core/trino-main/src/main/resources/webapp/src/components/QueryDetail.jsx b/core/trino-main/src/main/resources/webapp/src/components/QueryDetail.jsx index 566d5a16abaf1..31a4937c2487c 100644 --- a/core/trino-main/src/main/resources/webapp/src/components/QueryDetail.jsx +++ b/core/trino-main/src/main/resources/webapp/src/components/QueryDetail.jsx @@ -23,8 +23,8 @@ import { formatDataSize, formatDataSizeBytes, formatDuration, + formatPercentage, formatShortDateTime, - getCountWithPercentage, getFirstParameter, getHostAndPort, getHostname, @@ -33,7 +33,6 @@ import { getStageStateColor, getTaskIdSuffix, getTaskNumber, - getTotalFromLongCountMetrics, GLYPHICON_HIGHLIGHT, parseAndFormatDataSize, parseDataSize, @@ -230,6 +229,108 @@ class TaskList extends React.Component { } } +class DataSkippingMetricsList extends React.Component { + formatMetricHeader(metrics) { + const header = []; + if (metrics.isSum) { + header.push(SUM); + } + else { + header.push( + + {metrics.stageId + "-" + metrics.planNodeId} + + ); + header.push( + + {metrics.schemaName}.
+ {metrics.tableName} + + ); + } + return header; + } + + formatMetricWithOption(metric, totalMetric) { + switch (this.props.displayOption) { + case "DataSize": + return ( + + {formatDataSize(metric.dataSize)}
+ ({formatPercentage(metric.dataSize, totalMetric.dataSize)}) + + ) + case "SplitCount": + return ( + + {metric.splitCount}
+ ({formatPercentage(metric.splitCount, totalMetric.splitCount)}) + + ) + default: + return ( + N/A + ) + } + } + + render() { + const renderedMetricsList = this.props.metricsList.map(metrics => ( + + {this.formatMetricHeader(metrics)} + {this.formatMetricWithOption(metrics.total, metrics.total)} + {this.formatMetricWithOption(metrics.read, metrics.total)} + {this.formatMetricWithOption(metrics.skippedByMinmaxStats, metrics.total)} + {this.formatMetricWithOption(metrics.skippedByDfInCoordinator, metrics.total)} + {this.formatMetricWithOption(metrics.skippedByPartFilter, metrics.total)} + {this.formatMetricWithOption(metrics.skippedByIndexInCoordinator, metrics.total)} + {this.formatMetricWithOption(metrics.skippedByDfInWorker, metrics.total)} + {this.formatMetricWithOption(metrics.skippedByIndexInWorker, metrics.total)} + + )); + + return ( + + + + + + + + + + + + + + + + + + + + + {renderedMetricsList} + +
+ ID + Table + Total + ReadSkipped In CoordinatorSkipped In Worker
Minmax + Dyn. Filter + + Part. Filter + + Index + + Dyn. Filter + + Index +
+ ); + } +} + const BAR_CHART_WIDTH = 800; const BAR_CHART_PROPERTIES = { @@ -675,6 +776,9 @@ export class QueryDetail extends React.Component { stageRefresh: true, taskRefresh: true, + dataSkippingMetricsList: null, + metricsDisplayOption: "DataSize", + taskFilter: TASK_FILTER.ALL, }; @@ -1072,76 +1176,117 @@ export class QueryDetail extends React.Component { return renderedEstimates; } - renderSplitFilterMetrics() { - const metrics = this.state.query.queryStats.connectorMetrics; - if (metrics) { - const totalSplitsInPartitions = getTotalFromLongCountMetrics(metrics.iceberg_total_splits_in_partitions) - const totalSplitsRead = getTotalFromLongCountMetrics(metrics.iceberg_total_splits_read) - const skippedSplitsByIndex = getTotalFromLongCountMetrics(metrics.iceberg_skipped_splits_by_index) - const skippedSplitsByMinMax = getTotalFromLongCountMetrics(metrics.iceberg_skipped_splits_by_minmax) - const skippedSplitsByDFInCoordinator = getTotalFromLongCountMetrics(metrics.iceberg_skipped_splits_by_df_in_coordinator) - const skippedSplitsByDFInWorker = getTotalFromLongCountMetrics(metrics.iceberg_skipped_splits_by_df_in_worker) - return ( -
-

Source Split Filter Metrics (Iceberg Only)

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Total Splits in Target Partitions - - {totalSplitsInPartitions} -
- Total Splits Read - - {getCountWithPercentage(totalSplitsRead, totalSplitsInPartitions)} -
- Skipped Splits by Index - - {getCountWithPercentage(skippedSplitsByIndex, totalSplitsInPartitions)} -
- Skipped Splits by MinMax Statistics - - {getCountWithPercentage(skippedSplitsByMinMax, totalSplitsInPartitions)} -
- Skipped Splits by Dynamic Filter in Coordinator - - {getCountWithPercentage(skippedSplitsByDFInCoordinator, totalSplitsInPartitions)} -
- Skipped Splits by Dynamic Filter in Worker - - {getCountWithPercentage(skippedSplitsByDFInWorker, totalSplitsInPartitions)} -
+ renderDataSkippingMetrics() { + if (this.state.dataSkippingMetricsList === null) { + const connectorMetrics = this.state.query.queryStats.connectorMetrics; + const defaultMetric = {splitCount: 0, dataSize: 0}; + this.state.dataSkippingMetricsList = []; + if (connectorMetrics && connectorMetrics.hasOwnProperty('iceberg_data_skipping_metrics_table_level')) { + const metricsMap = connectorMetrics.iceberg_data_skipping_metrics_table_level; + if (metricsMap) { + for (let tableKey in metricsMap) { + if (tableKey !== "@class" && metricsMap.hasOwnProperty(tableKey)) { + const tableKeyArray = tableKey.split(":"); + const tableNameArray = tableKeyArray[2].split("."); + this.state.dataSkippingMetricsList.push({ + isSum: false, + stageId: parseInt(tableKeyArray[0]), + planNodeId: parseInt(tableKeyArray[1]), + schemaName: tableNameArray[0], + tableName: tableNameArray[1], + total: metricsMap[tableKey]['TOTAL'] || defaultMetric, + read: metricsMap[tableKey]['READ'] || defaultMetric, + skippedByMinmaxStats: metricsMap[tableKey]['SKIPPED_BY_MINMAX_STATS'] || defaultMetric, + skippedByIndexInCoordinator: metricsMap[tableKey]['SKIPPED_BY_INDEX_IN_COORDINATOR'] || defaultMetric, + skippedByIndexInWorker: metricsMap[tableKey]['SKIPPED_BY_INDEX_IN_WORKER'] || defaultMetric, + skippedByDfInCoordinator: metricsMap[tableKey]['SKIPPED_BY_DF_IN_COORDINATOR'] || defaultMetric, + skippedByDfInWorker: metricsMap[tableKey]['SKIPPED_BY_DF_IN_WORKER'] || defaultMetric, + skippedByPartFilter: metricsMap[tableKey]['SKIPPED_BY_PART_FILTER'] || defaultMetric + }) + } + } + } + this.state.dataSkippingMetricsList.sort((metricsA, metricsB) => { + if (metricsA.stageId !== metricsB.stageId) { + return metricsA.stageId > metricsB.stageId ? 1 : -1; + } + if (metricsA.planNodeId !== metricsB.planNodeId) { + return metricsA.planNodeId > metricsB.planNodeId ? 1 : -1; + } + return 0; + }) + } + + if (connectorMetrics && connectorMetrics.hasOwnProperty('iceberg_data_skipping_metrics')) { + const sumMetrics = connectorMetrics.iceberg_data_skipping_metrics; + if (sumMetrics) { + this.state.dataSkippingMetricsList.push({ + isSum: true, + total: sumMetrics['TOTAL'] || defaultMetric, + read: sumMetrics['READ'] || defaultMetric, + skippedByMinmaxStats: sumMetrics['SKIPPED_BY_MINMAX_STATS'] || defaultMetric, + skippedByIndexInCoordinator: sumMetrics['SKIPPED_BY_INDEX_IN_COORDINATOR'] || defaultMetric, + skippedByIndexInWorker: sumMetrics['SKIPPED_BY_INDEX_IN_WORKER'] || defaultMetric, + skippedByDfInCoordinator: sumMetrics['SKIPPED_BY_DF_IN_COORDINATOR'] || defaultMetric, + skippedByDfInWorker: sumMetrics['SKIPPED_BY_DF_IN_WORKER'] || defaultMetric, + skippedByPartFilter: sumMetrics['SKIPPED_BY_PART_FILTER'] || defaultMetric + }) + } + } + } + + if (this.state.dataSkippingMetricsList.length === 0) { + return; + } + return ( +
+
+
+

Data Skipping Metrics (Iceberg Only)

+
+
+ + + + + + +
+ {this.renderMetricsDisplayOptionButton()} +
+
- ); +
+
+ +
+
+
+ ); + } + + renderMetricsDisplayOptionButton() { + if (this.state.metricsDisplayOption === "DataSize") { + return + } + else if (this.state.metricsDisplayOption === "SplitCount") { + return + } + } + + handleMetricsDisplayOptionClick() { + if (this.state.metricsDisplayOption === "DataSize") { + this.setState({ + metricsDisplayOption: "SplitCount" + }); } else { - return null; + this.setState({ + metricsDisplayOption: "DataSize" + }); } } @@ -1692,7 +1837,7 @@ export class QueryDetail extends React.Component {
- {this.renderSplitFilterMetrics()} + {this.renderDataSkippingMetrics()} {this.renderWarningInfo()} {this.renderFailureInfo()}
diff --git a/core/trino-main/src/main/resources/webapp/src/utils.js b/core/trino-main/src/main/resources/webapp/src/utils.js index 9fe495eadd760..f3fe4b2e910a0 100644 --- a/core/trino-main/src/main/resources/webapp/src/utils.js +++ b/core/trino-main/src/main/resources/webapp/src/utils.js @@ -525,10 +525,7 @@ export function getTotalFromLongCountMetrics(longCount: any): number { return longCount.total; } -export function getCountWithPercentage(numerator: number, denominator: number): string { - if (denominator !== 0) { - const percentage = numerator / denominator * 100; - return numerator.toString() + ' (' + precisionRound(percentage) + '%)'; - } - return numerator.toString(); +export function formatPercentage(numerator: number, denominator: number): string { + const percentage = denominator === 0 ? 0 : numerator / denominator * 100; + return precisionRound(percentage) + "%"; } diff --git a/core/trino-main/src/test/java/io/trino/operator/TestTaskStats.java b/core/trino-main/src/test/java/io/trino/operator/TestTaskStats.java index 25a13730971a5..7c0b8763fad60 100644 --- a/core/trino-main/src/test/java/io/trino/operator/TestTaskStats.java +++ b/core/trino-main/src/test/java/io/trino/operator/TestTaskStats.java @@ -18,7 +18,6 @@ import io.airlift.json.JsonCodec; import io.airlift.units.DataSize; import io.airlift.units.Duration; -import io.trino.spi.metrics.Metrics; import org.joda.time.DateTime; import org.testng.annotations.Test; @@ -81,8 +80,6 @@ public class TestTaskStats DataSize.ofBytes(25), - Metrics.EMPTY, - 26, new Duration(27, NANOSECONDS), diff --git a/core/trino-spi/src/main/java/io/trino/spi/metrics/DataSkippingMetrics.java b/core/trino-spi/src/main/java/io/trino/spi/metrics/DataSkippingMetrics.java new file mode 100644 index 0000000000000..ecce99f097604 --- /dev/null +++ b/core/trino-spi/src/main/java/io/trino/spi/metrics/DataSkippingMetrics.java @@ -0,0 +1,143 @@ +/* + * Licensed 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 io.trino.spi.metrics; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +public class DataSkippingMetrics + implements Metric +{ + public static final DataSkippingMetrics EMPTY = new DataSkippingMetrics(Map.of()); + + private final Map metricMap; + + @JsonCreator + public DataSkippingMetrics(Map metricMap) + { + this.metricMap = Map.copyOf(requireNonNull(metricMap, "metricMap is null")); + } + + @JsonValue + public Map getMetricMap() + { + return metricMap; + } + + @Override + public DataSkippingMetrics mergeWith(DataSkippingMetrics other) + { + Map mergedMap = new HashMap<>(metricMap); + other.metricMap.forEach((metricType, metricEntry) -> + mergedMap.merge(metricType, metricEntry, MetricEntry::mergeWith)); + return new DataSkippingMetrics(mergedMap); + } + + public static Builder builder() + { + return new Builder(); + } + + public enum MetricType + { + TOTAL, + READ, + SKIPPED_BY_MINMAX_STATS, + SKIPPED_BY_INDEX_IN_COORDINATOR, + SKIPPED_BY_INDEX_IN_WORKER, + SKIPPED_BY_DF_IN_COORDINATOR, + SKIPPED_BY_DF_IN_WORKER, + SKIPPED_BY_PART_FILTER + } + + public static class MetricEntry + { + private final int splitCount; + private final long dataSize; + + @JsonCreator + public MetricEntry( + @JsonProperty("splitCount") int splitCount, + @JsonProperty("dataSize") long dataSize) + { + this.splitCount = splitCount; + this.dataSize = dataSize; + } + + @JsonProperty + public int getSplitCount() + { + return this.splitCount; + } + + @JsonProperty + public long getDataSize() + { + return this.dataSize; + } + + public MetricEntry mergeWith(MetricEntry other) + { + return new MetricEntry(splitCount + other.splitCount, dataSize + other.dataSize); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MetricEntry other = (MetricEntry) o; + return splitCount == other.splitCount && dataSize == other.dataSize; + } + + @Override + public int hashCode() + { + return Objects.hash(splitCount, dataSize); + } + } + + public static class Builder + { + private final Map metricMap = new HashMap<>(); + + public Builder withMetric(MetricType metricType, MetricEntry metricEntry) + { + metricMap.put(metricType, metricEntry); + return this; + } + + public Builder withMetric(MetricType metricType, int splitCount, long dataSize) + { + metricMap.put(metricType, new MetricEntry(splitCount, dataSize)); + return this; + } + + public DataSkippingMetrics build() + { + return metricMap.isEmpty() ? EMPTY : new DataSkippingMetrics(metricMap); + } + } +} diff --git a/core/trino-spi/src/main/java/io/trino/spi/metrics/TableLevelDataSkippingMetrics.java b/core/trino-spi/src/main/java/io/trino/spi/metrics/TableLevelDataSkippingMetrics.java new file mode 100644 index 0000000000000..6172c6e90aaac --- /dev/null +++ b/core/trino-spi/src/main/java/io/trino/spi/metrics/TableLevelDataSkippingMetrics.java @@ -0,0 +1,49 @@ +/* + * Licensed 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 io.trino.spi.metrics; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.HashMap; +import java.util.Map; + +import static java.util.Objects.requireNonNull; + +public class TableLevelDataSkippingMetrics + implements Metric +{ + private final Map dataSkippingMetricsMap; + + @JsonCreator + public TableLevelDataSkippingMetrics(Map dataSkippingMetricsMap) + { + this.dataSkippingMetricsMap = Map.copyOf(requireNonNull(dataSkippingMetricsMap, "dataSkippingMetricsMap is null")); + } + + @JsonValue + public Map getDataSkippingMetricsMap() + { + return dataSkippingMetricsMap; + } + + @Override + public TableLevelDataSkippingMetrics mergeWith(TableLevelDataSkippingMetrics other) + { + Map mergedMap = new HashMap<>(dataSkippingMetricsMap); + other.dataSkippingMetricsMap.forEach((table, dataSkippingMetrics) -> + mergedMap.merge(table, dataSkippingMetrics, DataSkippingMetrics::mergeWith)); + return new TableLevelDataSkippingMetrics(mergedMap); + } +} diff --git a/core/trino-spi/src/test/java/io/trino/spi/metrics/TestDataSkippingMetrics.java b/core/trino-spi/src/test/java/io/trino/spi/metrics/TestDataSkippingMetrics.java new file mode 100644 index 0000000000000..4567e907ee23c --- /dev/null +++ b/core/trino-spi/src/test/java/io/trino/spi/metrics/TestDataSkippingMetrics.java @@ -0,0 +1,73 @@ +/* + * Licensed 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 io.trino.spi.metrics; + +import com.google.common.collect.ImmutableMap; +import org.testng.annotations.Test; + +import static io.trino.spi.metrics.DataSkippingMetrics.MetricType.READ; +import static io.trino.spi.metrics.DataSkippingMetrics.MetricType.SKIPPED_BY_MINMAX_STATS; +import static io.trino.spi.metrics.DataSkippingMetrics.MetricType.TOTAL; +import static org.assertj.core.api.Assertions.assertThat; + +public class TestDataSkippingMetrics +{ + @Test + public void testMergeDataSkippingMetrics() + { + DataSkippingMetrics metrics1 = DataSkippingMetrics.builder() + .withMetric(TOTAL, 1, 100) + .withMetric(SKIPPED_BY_MINMAX_STATS, 2, 1000) + .build(); + DataSkippingMetrics metrics2 = DataSkippingMetrics.builder() + .withMetric(SKIPPED_BY_MINMAX_STATS, 3, 10000) + .withMetric(READ, 4, 100000) + .build(); + DataSkippingMetrics merged = metrics1.mergeWith(metrics2); + + DataSkippingMetrics expected = DataSkippingMetrics.builder() + .withMetric(TOTAL, 1, 100) + .withMetric(SKIPPED_BY_MINMAX_STATS, 5, 11000) + .withMetric(READ, 4, 100000) + .build(); + + assertThat(merged.getMetricMap()).isEqualTo(expected.getMetricMap()); + } + + @Test + public void testMergeTableLevelDataSkippingMetrics() + { + TableLevelDataSkippingMetrics metrics1 = new TableLevelDataSkippingMetrics(ImmutableMap.of( + "table1", DataSkippingMetrics.builder().withMetric(TOTAL, 1, 100).build())); + TableLevelDataSkippingMetrics metrics2 = new TableLevelDataSkippingMetrics(ImmutableMap.of( + "table1", DataSkippingMetrics.builder().withMetric(TOTAL, 2, 1000).build())); + TableLevelDataSkippingMetrics metrics3 = new TableLevelDataSkippingMetrics(ImmutableMap.of( + "table2", DataSkippingMetrics.builder().withMetric(TOTAL, 4, 10000).build())); + TableLevelDataSkippingMetrics merged = metrics1.mergeWith(metrics2).mergeWith(metrics3); + + TableLevelDataSkippingMetrics expected = new TableLevelDataSkippingMetrics(ImmutableMap.of( + "table1", DataSkippingMetrics.builder().withMetric(TOTAL, 3, 1100).build(), + "table2", DataSkippingMetrics.builder().withMetric(TOTAL, 4, 10000).build())); + + assertThat(getDataSkippingMetrics(merged, "table1").getMetricMap()) + .isEqualTo(getDataSkippingMetrics(expected, "table1").getMetricMap()); + assertThat(getDataSkippingMetrics(merged, "table2").getMetricMap()) + .isEqualTo(getDataSkippingMetrics(expected, "table2").getMetricMap()); + } + + private DataSkippingMetrics getDataSkippingMetrics(TableLevelDataSkippingMetrics metrics, String table) + { + return metrics.getDataSkippingMetricsMap().get(table); + } +} diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergPageSource.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergPageSource.java index 097aed6663f2c..bbaaebe30e572 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergPageSource.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergPageSource.java @@ -33,6 +33,8 @@ import static com.google.common.base.Throwables.throwIfInstanceOf; import static io.trino.plugin.iceberg.IcebergErrorCode.ICEBERG_BAD_DATA; import static io.trino.plugin.iceberg.IcebergUtil.deserializePartitionValue; +import static io.trino.plugin.iceberg.util.MetricsUtils.makeMetrics; +import static io.trino.spi.metrics.DataSkippingMetrics.MetricType.READ; import static java.util.Objects.requireNonNull; public class IcebergPageSource @@ -42,23 +44,12 @@ public class IcebergPageSource private final int[] delegateIndexes; private final ConnectorPageSource delegate; private final Optional projectionsAdapter; - private final Metrics metrics; public IcebergPageSource( List columns, Map> partitionKeys, ConnectorPageSource delegate, Optional projectionsAdapter) - { - this(columns, partitionKeys, delegate, projectionsAdapter, Metrics.EMPTY); - } - - public IcebergPageSource( - List columns, - Map> partitionKeys, - ConnectorPageSource delegate, - Optional projectionsAdapter, - Metrics metrics) { int size = requireNonNull(columns, "columns is null").size(); requireNonNull(partitionKeys, "partitionKeys is null"); @@ -67,7 +58,6 @@ public IcebergPageSource( this.prefilledBlocks = new Block[size]; this.delegateIndexes = new int[size]; this.projectionsAdapter = requireNonNull(projectionsAdapter, "projectionsAdapter is null"); - this.metrics = metrics; int outputIndex = 0; int delegateIndex = 0; @@ -167,7 +157,8 @@ public long getSystemMemoryUsage() @Override public Metrics getMetrics() { - return (delegate.getMetrics() != Metrics.EMPTY) ? metrics.mergeWith(delegate.getMetrics()) : metrics; + Metrics metrics = makeMetrics(READ, 1, getCompletedBytes()); + return delegate.getMetrics() == Metrics.EMPTY ? metrics : metrics.mergeWith(delegate.getMetrics()); } protected void closeWithSuppression(Throwable throwable) diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergPageSourceProvider.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergPageSourceProvider.java index dbed9fd52d436..c4c188da7003e 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergPageSourceProvider.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergPageSourceProvider.java @@ -51,7 +51,6 @@ import io.trino.plugin.hive.parquet.ParquetPageSource; import io.trino.plugin.hive.parquet.ParquetReaderConfig; import io.trino.plugin.iceberg.IcebergParquetColumnIOConverter.FieldContext; -import io.trino.plugin.iceberg.util.MetricsUtils; import io.trino.spi.TrinoException; import io.trino.spi.connector.ColumnHandle; import io.trino.spi.connector.ConnectorPageSource; @@ -62,7 +61,6 @@ import io.trino.spi.connector.ConnectorTransactionHandle; import io.trino.spi.connector.DynamicFilter; import io.trino.spi.connector.EmptyPageSource; -import io.trino.spi.metrics.Metrics; import io.trino.spi.predicate.Domain; import io.trino.spi.predicate.TupleDomain; import io.trino.spi.type.ArrayType; @@ -137,7 +135,9 @@ import static io.trino.plugin.iceberg.IcebergSplitManager.ICEBERG_DOMAIN_COMPACTION_THRESHOLD; import static io.trino.plugin.iceberg.TypeConverter.ICEBERG_BINARY_TYPE; import static io.trino.plugin.iceberg.TypeConverter.ORC_ICEBERG_ID_KEY; +import static io.trino.plugin.iceberg.util.MetricsUtils.makeMetrics; import static io.trino.spi.StandardErrorCode.NOT_SUPPORTED; +import static io.trino.spi.metrics.DataSkippingMetrics.MetricType.SKIPPED_BY_INDEX_IN_WORKER; import static io.trino.spi.type.UuidType.UUID; import static io.trino.spi.type.VarbinaryType.VARBINARY; import static java.lang.String.format; @@ -186,7 +186,6 @@ public ConnectorPageSource createPageSource( { IcebergSplit split = (IcebergSplit) connectorSplit; IcebergTableHandle table = (IcebergTableHandle) connectorTable; - Metrics.Accumulator metricsAccumulator = Metrics.accumulator(); HdfsContext hdfsContext = new HdfsContext(session); FileScanTask fileScanTask = split.decodeFileScanTask(); @@ -195,11 +194,9 @@ public ConnectorPageSource createPageSource( HdfsFileIo hdfsFileIo = new HdfsFileIo(hdfsEnvironment, hdfsContext); split.setIsSkippedByIndex(!fileScanTask.isRequired(hdfsFileIo, false)); split.setIndexReadTime(System.currentTimeMillis() - start); - metricsAccumulator.add(MetricsUtils.makeLongCountMetrics( - MetricsUtils.SKIPPED_SPLITS_BY_INDEX, split.isSkippedByIndex() ? 1 : 0)); if (split.isSkippedByIndex()) { log.info("Indices hit for file : %s, split skipped, time spent : %s ms", fileScanTask.file().path(), split.getIndexReadTime()); - return new EmptyPageSource(metricsAccumulator.get()); + return new EmptyPageSource(makeMetrics(SKIPPED_BY_INDEX_IN_WORKER, 1, split.getLength())); } log.info("Indices missed for file : %s, time spent : %s ms", fileScanTask.file().path(), split.getIndexReadTime()); } @@ -239,9 +236,7 @@ public ConnectorPageSource createPageSource( readerColumns, column -> ((IcebergColumnHandle) column).getType(), IcebergPageSourceProvider::applyProjection)); - metricsAccumulator.add(MetricsUtils.makeLongCountMetrics(MetricsUtils.TOTAL_SPLITS_READ, 1)); - return new IcebergPageSource(icebergColumns, partitionKeys, dataPageSource.get(), - projectionsAdapter, metricsAccumulator.get()); + return new IcebergPageSource(icebergColumns, partitionKeys, dataPageSource.get(), projectionsAdapter); } private ReaderPageSource createDataPageSource( diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergSplitSource.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergSplitSource.java index 0a6303d8e0463..d0596ad6e1bea 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergSplitSource.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergSplitSource.java @@ -121,9 +121,13 @@ public class IcebergSplitSource private Iterator fileScanIterator; private TupleDomain pushedDownDynamicFilterPredicate; - private int skippedSplitsByDynamicFilter; private boolean includeIndex; + private int skippedSplitsByDynamicFilter; + private long skippedDataSizeByDynamicFilter; + private int skippedSplitsByPartitionFilter; + private long skippedDataSizeByPartitionFilter; + public IcebergSplitSource( ConnectorSession session, IcebergTableHandle tableHandle, @@ -267,7 +271,7 @@ private ConnectorSplitBatch doGetNextBatch(int maxSize, TupleDomain getMetrics() { - Metrics.Accumulator metricsAccumulator = Metrics.accumulator(); - + Metrics metrics; scanLock.readLock().lock(); try { - metricsAccumulator.add(MetricsUtils.makeMetricsFromFilterMetrics(filterMetrics)); + metrics = MetricsUtils.makeMetrics( + filterMetrics, + skippedSplitsByDynamicFilter, + skippedDataSizeByDynamicFilter, + skippedSplitsByPartitionFilter, + skippedDataSizeByPartitionFilter); } finally { scanLock.readLock().unlock(); } - - metricsAccumulator.add(MetricsUtils.makeLongCountMetrics( - MetricsUtils.SKIPPED_SPLITS_BY_DF_IN_COORDINATOR, skippedSplitsByDynamicFilter)); - return Optional.of(metricsAccumulator.get()); + return Optional.of(metrics); } @Override diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/util/MetricsUtils.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/util/MetricsUtils.java index cb04f4f7ccbe2..9f19271ab61cd 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/util/MetricsUtils.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/util/MetricsUtils.java @@ -14,42 +14,47 @@ package io.trino.plugin.iceberg.util; import com.google.common.collect.ImmutableMap; -import io.trino.plugin.base.metrics.LongCount; -import io.trino.spi.metrics.Metric; +import io.trino.spi.metrics.DataSkippingMetrics; import io.trino.spi.metrics.Metrics; import org.apache.iceberg.FilterMetrics; +import static io.trino.spi.metrics.DataSkippingMetrics.MetricType.SKIPPED_BY_DF_IN_COORDINATOR; +import static io.trino.spi.metrics.DataSkippingMetrics.MetricType.SKIPPED_BY_MINMAX_STATS; +import static io.trino.spi.metrics.DataSkippingMetrics.MetricType.SKIPPED_BY_PART_FILTER; +import static io.trino.spi.metrics.DataSkippingMetrics.MetricType.TOTAL; + public class MetricsUtils { - public static final String TOTAL_SPLITS_IN_PARTITIONS = "iceberg_total_splits_in_partitions"; - public static final String TOTAL_SPLITS_READ = "iceberg_total_splits_read"; - public static final String SKIPPED_SPLITS_BY_INDEX = "iceberg_skipped_splits_by_index"; - public static final String SKIPPED_SPLITS_BY_MINMAX = "iceberg_skipped_splits_by_minmax"; - public static final String SKIPPED_SPLITS_BY_DF_IN_COORDINATOR = "iceberg_skipped_splits_by_df_in_coordinator"; - public static final String SKIPPED_SPLITS_BY_DF_IN_WORKER = "iceberg_skipped_splits_by_df_in_worker"; + public static final String DATA_SKIPPING_METRICS = "iceberg_data_skipping_metrics"; private MetricsUtils() {} - public static Metrics makeMetricsFromFilterMetrics(FilterMetrics filterMetrics) + public static Metrics makeMetrics( + FilterMetrics filterMetrics, + int skippedSplitsByDfInCoordinator, + long skippedDataSizeByDfInCoordinator, + int skippedSplitsByPartitionFilter, + long skippedDataSizeByPartitionFilter) { - if (filterMetrics == null) { - return Metrics.EMPTY; + DataSkippingMetrics.Builder builder = DataSkippingMetrics.builder() + .withMetric(SKIPPED_BY_DF_IN_COORDINATOR, skippedSplitsByDfInCoordinator, skippedDataSizeByDfInCoordinator) + .withMetric(SKIPPED_BY_PART_FILTER, skippedSplitsByPartitionFilter, skippedDataSizeByPartitionFilter); + + if (filterMetrics != null) { + filterMetrics.getMetricEntry(FilterMetrics.MetricType.TOTAL).ifPresent(entry -> + builder.withMetric(TOTAL, entry.getRawSplitCount(), entry.getTotalFileSize())); + filterMetrics.getMetricEntry(FilterMetrics.MetricType.SKIPPED_BY_MINMAX).ifPresent(entry -> + builder.withMetric(SKIPPED_BY_MINMAX_STATS, entry.getRawSplitCount(), entry.getTotalFileSize())); } - ImmutableMap.Builder> metricsBuilder = ImmutableMap.builder(); - metricsBuilder.put(TOTAL_SPLITS_IN_PARTITIONS, new LongCount( - filterMetrics.getMetricEntry(FilterMetrics.MetricType.TOTAL) - .map(FilterMetrics.MetricEntry::getRawSplitCount) - .orElse(0))); - metricsBuilder.put(SKIPPED_SPLITS_BY_MINMAX, new LongCount( - filterMetrics.getMetricEntry(FilterMetrics.MetricType.SKIPPED_BY_MINMAX) - .map(FilterMetrics.MetricEntry::getRawSplitCount) - .orElse(0))); - return new Metrics(metricsBuilder.build()); + return new Metrics(ImmutableMap.of(DATA_SKIPPING_METRICS, builder.build())); } - public static Metrics makeLongCountMetrics(String name, long count) + public static Metrics makeMetrics(DataSkippingMetrics.MetricType metricType, int splitCount, long dataSize) { - return new Metrics(ImmutableMap.of(name, new LongCount(count))); + DataSkippingMetrics dataSkippingMetrics = DataSkippingMetrics.builder() + .withMetric(metricType, splitCount, dataSize) + .build(); + return new Metrics(ImmutableMap.of(DATA_SKIPPING_METRICS, dataSkippingMetrics)); } }