From 8a5158e1a68d6fcd9592bd1764fa7cdb59b3cb31 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Mon, 20 Feb 2023 14:04:46 +0530 Subject: [PATCH 01/36] Support for reset of values in BigArray objects --- .../io/prestosql/array/BlockBigArray.java | 24 +++++++++++++++++++ .../io/prestosql/array/BooleanBigArray.java | 10 ++++++++ .../java/io/prestosql/array/ByteBigArray.java | 10 ++++++++ .../io/prestosql/array/DoubleBigArray.java | 10 ++++++++ .../java/io/prestosql/array/IntBigArray.java | 10 ++++++++ .../java/io/prestosql/array/LongBigArray.java | 10 ++++++++ .../io/prestosql/array/ObjectBigArray.java | 11 +++++++++ .../io/prestosql/array/ShortBigArray.java | 10 ++++++++ .../io/prestosql/array/SliceBigArray.java | 23 ++++++++++++++++++ 9 files changed, 118 insertions(+) diff --git a/presto-array/src/main/java/io/prestosql/array/BlockBigArray.java b/presto-array/src/main/java/io/prestosql/array/BlockBigArray.java index e05880add..d864682da 100644 --- a/presto-array/src/main/java/io/prestosql/array/BlockBigArray.java +++ b/presto-array/src/main/java/io/prestosql/array/BlockBigArray.java @@ -96,6 +96,30 @@ public final class BlockBigArray array.set(index, value); } + /** + * Resets the element of this big array at specified index. + * + * @param index a position in this big array. + */ + public void reset(long index, Block value) + { + Block currentValue = array.get(index); + if (currentValue != null) { + currentValue.retainedBytesForEachPart((object, size) -> { + if (currentValue == object) { + // track instance size separately as the reference count for an instance is always 1 + sizeOfBlocks -= size; + return; + } + if (trackedObjects.decrementAndGet(object) == 0) { + // decrement the size only when it is the last reference + sizeOfBlocks -= size; + } + }); + } + array.reset(index); + } + /** * Ensures this big array is at least the specified length. If the array is smaller, segments * are added until the array is larger then the specified length. diff --git a/presto-array/src/main/java/io/prestosql/array/BooleanBigArray.java b/presto-array/src/main/java/io/prestosql/array/BooleanBigArray.java index 56f7d4fe8..835bad5d4 100644 --- a/presto-array/src/main/java/io/prestosql/array/BooleanBigArray.java +++ b/presto-array/src/main/java/io/prestosql/array/BooleanBigArray.java @@ -81,6 +81,16 @@ public final class BooleanBigArray array[BigArrays.segment(index)][BigArrays.offset(index)] = value; } + /** + * Resets the element of this big array at specified index. + * + * @param index a position in this big array. + */ + public void reset(long index) + { + array[BigArrays.segment(index)][BigArrays.offset(index)] = initialValue; + } + /** * Ensures this big array is at least the specified length. If the array is smaller, segments * are added until the array is larger then the specified length. diff --git a/presto-array/src/main/java/io/prestosql/array/ByteBigArray.java b/presto-array/src/main/java/io/prestosql/array/ByteBigArray.java index 2ea227e95..b3b8c3b9a 100644 --- a/presto-array/src/main/java/io/prestosql/array/ByteBigArray.java +++ b/presto-array/src/main/java/io/prestosql/array/ByteBigArray.java @@ -81,6 +81,16 @@ public final class ByteBigArray array[BigArrays.segment(index)][BigArrays.offset(index)] = value; } + /** + * Resets the element of this big array at specified index. + * + * @param index a position in this big array. + */ + public void reset(long index) + { + array[BigArrays.segment(index)][BigArrays.offset(index)] = initialValue; + } + /** * Ensures this big array is at least the specified length. If the array is smaller, segments * are added until the array is larger then the specified length. diff --git a/presto-array/src/main/java/io/prestosql/array/DoubleBigArray.java b/presto-array/src/main/java/io/prestosql/array/DoubleBigArray.java index 9929cf4a6..410be0875 100644 --- a/presto-array/src/main/java/io/prestosql/array/DoubleBigArray.java +++ b/presto-array/src/main/java/io/prestosql/array/DoubleBigArray.java @@ -84,6 +84,16 @@ public final class DoubleBigArray array[BigArrays.segment(index)][BigArrays.offset(index)] = value; } + /** + * Resets the element of this big array at specified index. + * + * @param index a position in this big array. + */ + public void reset(long index) + { + array[BigArrays.segment(index)][BigArrays.offset(index)] = initialValue; + } + /** * Adds the specified value to the specified element of this big array. * diff --git a/presto-array/src/main/java/io/prestosql/array/IntBigArray.java b/presto-array/src/main/java/io/prestosql/array/IntBigArray.java index 5d5c52f88..c18d9d3d7 100644 --- a/presto-array/src/main/java/io/prestosql/array/IntBigArray.java +++ b/presto-array/src/main/java/io/prestosql/array/IntBigArray.java @@ -89,6 +89,16 @@ public final class IntBigArray array[BigArrays.segment(index)][BigArrays.offset(index)] = value; } + /** + * Resets the element of this big array at specified index. + * + * @param index a position in this big array. + */ + public void reset(long index) + { + array[BigArrays.segment(index)][BigArrays.offset(index)] = initialValue; + } + /** * Increments the element of this big array at specified index. * diff --git a/presto-array/src/main/java/io/prestosql/array/LongBigArray.java b/presto-array/src/main/java/io/prestosql/array/LongBigArray.java index 3dc64e38a..dee5c57f9 100644 --- a/presto-array/src/main/java/io/prestosql/array/LongBigArray.java +++ b/presto-array/src/main/java/io/prestosql/array/LongBigArray.java @@ -96,6 +96,16 @@ public final class LongBigArray array[segment(index)][offset(index)] = value; } + /** + * Resets the element of this big array at specified index. + * + * @param index a position in this big array. + */ + public void reset(long index) + { + array[segment(index)][offset(index)] = initialValue; + } + /** * Increments the element of this big array at specified index. * diff --git a/presto-array/src/main/java/io/prestosql/array/ObjectBigArray.java b/presto-array/src/main/java/io/prestosql/array/ObjectBigArray.java index e4128dbe7..75ab52b11 100644 --- a/presto-array/src/main/java/io/prestosql/array/ObjectBigArray.java +++ b/presto-array/src/main/java/io/prestosql/array/ObjectBigArray.java @@ -105,6 +105,17 @@ public final class ObjectBigArray array[BigArrays.segment(index)][offset(index)] = value; } + /** + * Resets the element of this big array at specified index. + * + * @param index a position in this big array. + * @return true if the previous value was null + */ + public void reset(long index) + { + array[BigArrays.segment(index)][offset(index)] = initialValue; + } + /** * Replaces the element of this big array at specified index. * diff --git a/presto-array/src/main/java/io/prestosql/array/ShortBigArray.java b/presto-array/src/main/java/io/prestosql/array/ShortBigArray.java index bcb682e80..398cd7376 100644 --- a/presto-array/src/main/java/io/prestosql/array/ShortBigArray.java +++ b/presto-array/src/main/java/io/prestosql/array/ShortBigArray.java @@ -84,6 +84,16 @@ public final class ShortBigArray array[BigArrays.segment(index)][BigArrays.offset(index)] = value; } + /** + * Resets the element of this big array at specified index. + * + * @param index a position in this big array. + */ + public void reset(long index) + { + array[BigArrays.segment(index)][BigArrays.offset(index)] = initialValue; + } + /** * Increments the element of this big array at specified index. * diff --git a/presto-array/src/main/java/io/prestosql/array/SliceBigArray.java b/presto-array/src/main/java/io/prestosql/array/SliceBigArray.java index 3a6c97a33..bcd534b83 100644 --- a/presto-array/src/main/java/io/prestosql/array/SliceBigArray.java +++ b/presto-array/src/main/java/io/prestosql/array/SliceBigArray.java @@ -73,6 +73,29 @@ public final class SliceBigArray array.set(index, value); } + /** + * Resets the element of this big array at specified index. + * + * @param index a position in this big array. + */ + public void reset(long index) + { + Slice currentValue = array.get(index); + if (currentValue != null) { + int baseReferenceCount = trackedSlices.decrementAndGet(currentValue.getBase()); + int sliceReferenceCount = trackedSlices.decrementAndGet(currentValue); + if (baseReferenceCount == 0) { + // it is the last referenced base + sizeOfSlices -= currentValue.getRetainedSize(); + } + else if (sliceReferenceCount == 0) { + // it is the last referenced slice + sizeOfSlices -= SLICE_INSTANCE_SIZE; + } + } + array.reset(index); + } + /** * Ensures this big array is at least the specified length. If the array is smaller, segments * are added until the array is larger then the specified length. -- Gitee From 1821e335f835a17b1039866be50956b4b3eec360 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Mon, 20 Feb 2023 14:44:34 +0530 Subject: [PATCH 02/36] Support for reset of values in Aggregator and Accumulator --- .../aggregation/AccumulatorCompiler.java | 25 +++++++++++++++++++ .../GenericAccumulatorFactory.java | 12 +++++++++ .../aggregation/GroupedAccumulator.java | 2 ++ .../arrayagg/GroupArrayAggregationState.java | 6 +++++ .../LegacyArrayAggregationGroupState.java | 6 +++++ .../builder/InMemoryAggregationBuilder.java | 9 +++++++ .../InMemoryHashAggregationBuilder.java | 7 +++++- .../GroupedMultimapAggregationState.java | 6 +++++ ...LegacyGroupedMultimapAggregationState.java | 6 +++++ .../AbstractGroupedAccumulatorState.java | 4 +++ ...ecimalWithOverflowAndLongStateFactory.java | 7 ++++++ .../LongDecimalWithOverflowStateFactory.java | 13 ++++++++++ .../aggregation/state/StateCompiler.java | 10 ++++++-- 13 files changed, 110 insertions(+), 3 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/AccumulatorCompiler.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/AccumulatorCompiler.java index 90e140424..1d8ee01ae 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/AccumulatorCompiler.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/AccumulatorCompiler.java @@ -199,6 +199,7 @@ public class AccumulatorCompiler if (grouped) { generateGroupedEvaluateFinal(definition, stateFields, metadata.getOutputFunction(), callSiteBinder); + generateReset(definition, stateFields); } else { generateEvaluateFinal(definition, stateFields, metadata.getOutputFunction(), callSiteBinder); @@ -917,6 +918,30 @@ public class AccumulatorCompiler body.ret(); } + private static void generateReset(ClassDefinition definition, List stateFields) + { + Parameter groupId = arg("groupId", int.class); + MethodDefinition method = definition.declareMethod(a(PUBLIC), "reset", type(void.class), groupId); + + BytecodeBlock body = method.getBody(); + Variable thisVariable = method.getThis(); + + List states = new ArrayList<>(); + + for (FieldDefinition stateField : stateFields) { + BytecodeExpression state = thisVariable.getField(stateField); + body.append(state.invoke("setGroupId", void.class, groupId.cast(long.class))); + states.add(state); + } + for (FieldDefinition stateField : stateFields) { + BytecodeExpression state = thisVariable.getField(stateField); + body.append(state.invoke("reset", void.class)); + states.add(state); + } + states.forEach(body::append); + body.ret(); + } + private static void generateEvaluateFinal( ClassDefinition definition, List stateFields, diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/GenericAccumulatorFactory.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/GenericAccumulatorFactory.java index b6c8236c4..2bc084577 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/GenericAccumulatorFactory.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/GenericAccumulatorFactory.java @@ -446,6 +446,12 @@ public class GenericAccumulatorFactory accumulator.evaluateFinal(groupId, output); } + @Override + public void reset(int groupId) + { + accumulator.reset(groupId); + } + @Override public void prepareFinal() { @@ -651,6 +657,12 @@ public class GenericAccumulatorFactory accumulator.evaluateFinal(groupId, output); } + @Override + public void reset(int groupId) + { + accumulator.reset(groupId); + } + @Override public void prepareFinal() { diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/GroupedAccumulator.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/GroupedAccumulator.java index eda316ee8..c832097c1 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/GroupedAccumulator.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/GroupedAccumulator.java @@ -38,4 +38,6 @@ public interface GroupedAccumulator void evaluateFinal(int groupId, BlockBuilder output); void prepareFinal(); + + void reset(int groupId); } diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/arrayagg/GroupArrayAggregationState.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/arrayagg/GroupArrayAggregationState.java index ae0eb6b91..d94f24770 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/arrayagg/GroupArrayAggregationState.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/arrayagg/GroupArrayAggregationState.java @@ -38,6 +38,12 @@ public final class GroupArrayAggregationState appendAtChannel(VALUE_CHANNEL, block, position); } + @Override + public void reset() + { + ArrayAggregationState.super.reset(); + } + @Override protected final void accept(ArrayAggregationStateConsumer consumer, PageBuilder pageBuilder, int currentPosition) { diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/arrayagg/LegacyArrayAggregationGroupState.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/arrayagg/LegacyArrayAggregationGroupState.java index 2e7923773..586f9f353 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/arrayagg/LegacyArrayAggregationGroupState.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/arrayagg/LegacyArrayAggregationGroupState.java @@ -90,4 +90,10 @@ public class LegacyArrayAggregationGroupState verify(blockBuilder.getPositionCount() != 0); return false; } + + @Override + public void reset() + { + ArrayAggregationState.super.reset(); + } } diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryAggregationBuilder.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryAggregationBuilder.java index 3678b7f65..e4e39bb99 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryAggregationBuilder.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryAggregationBuilder.java @@ -324,6 +324,11 @@ public abstract class InMemoryAggregationBuilder } } + public void reset(int groupId) + { + aggregation.reset(groupId); + } + public void setOutputPartial() { step = AggregationNode.Step.partialOutput(step); @@ -390,6 +395,10 @@ public abstract class InMemoryAggregationBuilder throw new UnsupportedOperationException(); } + protected void resetGroupBy() + { + } + private static class InMemoryAggregationBuilderState implements Serializable { diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryHashAggregationBuilder.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryHashAggregationBuilder.java index ba0a6fbc2..a3611f7d5 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryHashAggregationBuilder.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryHashAggregationBuilder.java @@ -179,7 +179,12 @@ public class InMemoryHashAggregationBuilder } } - return WorkProcessor.ProcessState.ofResult(pageBuilder.build()); + try { + return WorkProcessor.ProcessState.ofResult(pageBuilder.build()); + } + finally { + resetGroupBy(); + } }); } diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/multimapagg/GroupedMultimapAggregationState.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/multimapagg/GroupedMultimapAggregationState.java index a70639840..4dd51feaf 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/multimapagg/GroupedMultimapAggregationState.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/multimapagg/GroupedMultimapAggregationState.java @@ -40,6 +40,12 @@ public final class GroupedMultimapAggregationState appendAtChannel(KEY_CHANNEL, keyBlock, position); } + @Override + public void reset() + { + MultimapAggregationState.super.reset(); + } + @Override protected final void accept(MultimapAggregationStateConsumer consumer, PageBuilder pageBuilder, int currentPosition) { diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/multimapagg/LegacyGroupedMultimapAggregationState.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/multimapagg/LegacyGroupedMultimapAggregationState.java index bd966d980..ce127c1a5 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/multimapagg/LegacyGroupedMultimapAggregationState.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/multimapagg/LegacyGroupedMultimapAggregationState.java @@ -91,6 +91,12 @@ public class LegacyGroupedMultimapAggregationState return keyBlockBuilders.get(getGroupId()) == null; } + @Override + public void reset() + { + MultimapAggregationState.super.reset(); + } + @Override public long getEstimatedSize() { diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/state/AbstractGroupedAccumulatorState.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/state/AbstractGroupedAccumulatorState.java index cb5aeefaa..f4c9fcf1e 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/state/AbstractGroupedAccumulatorState.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/state/AbstractGroupedAccumulatorState.java @@ -33,6 +33,10 @@ public abstract class AbstractGroupedAccumulatorState return groupId; } + protected void reset() + { + } + @Override public Object capture(BlockEncodingSerdeProvider serdeProvider) { diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/state/LongDecimalWithOverflowAndLongStateFactory.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/state/LongDecimalWithOverflowAndLongStateFactory.java index fad38fc25..705f4832b 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/state/LongDecimalWithOverflowAndLongStateFactory.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/state/LongDecimalWithOverflowAndLongStateFactory.java @@ -79,6 +79,13 @@ public class LongDecimalWithOverflowAndLongStateFactory longs.add(getGroupId(), value); } + @Override + public void reset() + { + super.reset(); + longs.reset(getGroupId()); + } + @Override public long getEstimatedSize() { diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/state/LongDecimalWithOverflowStateFactory.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/state/LongDecimalWithOverflowStateFactory.java index c3a4d8b9d..98cf816db 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/state/LongDecimalWithOverflowStateFactory.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/state/LongDecimalWithOverflowStateFactory.java @@ -90,6 +90,19 @@ public class LongDecimalWithOverflowStateFactory isNotNull.set(getGroupId(), true); } + @Override + public void reset() + { + isNotNull.reset(getGroupId()); + long[] decimalArray = getDecimalArray(); + int decimalArrayOffset = getDecimalArrayOffset(); + decimalArray[decimalArrayOffset] = 0; + decimalArray[decimalArrayOffset + 1] = 0; + if (overflows != null) { + overflows.reset(getGroupId()); + } + } + @Override public long[] getDecimalArray() { diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/state/StateCompiler.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/state/StateCompiler.java index 06e50cdd3..2eb6c89b5 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/state/StateCompiler.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/state/StateCompiler.java @@ -517,14 +517,17 @@ public class StateCompiler // Create ensureCapacity MethodDefinition ensureCapacity = definition.declareMethod(a(PUBLIC), "ensureCapacity", type(void.class), arg("size", long.class)); + MethodDefinition reset = definition.declareMethod(a(PUBLIC), "reset", type(void.class)); + // Generate fields, constructor, and ensureCapacity List fieldDefinitions = new ArrayList<>(); for (StateField field : fields) { - fieldDefinitions.add(generateGroupedField(definition, constructor, ensureCapacity, field)); + fieldDefinitions.add(generateGroupedField(definition, constructor, ensureCapacity, field, reset)); } constructor.getBody().ret(); ensureCapacity.getBody().ret(); + reset.getBody().ret(); // Generate getEstimatedSize MethodDefinition getEstimatedSize = definition.declareMethod(a(PUBLIC), "getEstimatedSize", type(long.class)); @@ -592,7 +595,7 @@ public class StateCompiler .append(constructor.getThis().setField(field, stateField.initialValueExpression())); } - private static FieldDefinition generateGroupedField(ClassDefinition definition, MethodDefinition constructor, MethodDefinition ensureCapacity, StateField stateField) + private static FieldDefinition generateGroupedField(ClassDefinition definition, MethodDefinition constructor, MethodDefinition ensureCapacity, StateField stateField, MethodDefinition reset) { Class bigArrayType = getBigArrayType(stateField.getType()); FieldDefinition field = definition.declareField(a(PRIVATE), UPPER_CAMEL.to(LOWER_CAMEL, stateField.getName()) + "Values", bigArrayType); @@ -621,6 +624,9 @@ public class StateCompiler ensureCapacity.getBody() .append(ensureCapacity.getThis().getField(field).invoke("ensureCapacity", void.class, ensureCapacityScope.getVariable("size"))); + reset.getBody() + .append(reset.getThis().getField(field).invoke("reset", void.class, reset.getThis().invoke("getGroupId", long.class))); + // Initialize field in constructor constructor.getBody() .append(constructor.getThis().setField(field, newInstance(field.getType(), stateField.initialValueExpression()))); -- Gitee From d1ba21bc1c593673d6f9181e502c05ca1b33df76 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Mon, 20 Feb 2023 17:28:02 +0530 Subject: [PATCH 03/36] Support for GroupJoin detection Rule and print of plan. --- .../io/prestosql/SystemSessionProperties.java | 11 + .../prestosql/sql/planner/PlanOptimizers.java | 6 +- .../sql/planner/PlanSymbolAllocator.java | 5 + .../rule/MergePartialAggregationWithJoin.java | 470 ++++++++++++++++++ ...PartialAggregationWithJoinPushProject.java | 147 ++++++ .../HashGenerationOptimizer.java | 249 +++++++++- .../sql/planner/planprinter/PlanPrinter.java | 69 +++ .../sanity/ValidateDependenciesChecker.java | 38 ++ .../io/prestosql/util/GraphvizPrinter.java | 20 + .../spi/plan/JoinOnAggregationNode.java | 441 ++++++++++++++++ .../io/prestosql/spi/plan/PlanVisitor.java | 5 + 11 files changed, 1449 insertions(+), 12 deletions(-) create mode 100644 presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/MergePartialAggregationWithJoin.java create mode 100644 presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/MergePartialAggregationWithJoinPushProject.java create mode 100644 presto-spi/src/main/java/io/prestosql/spi/plan/JoinOnAggregationNode.java diff --git a/presto-main/src/main/java/io/prestosql/SystemSessionProperties.java b/presto-main/src/main/java/io/prestosql/SystemSessionProperties.java index 41d990d2f..994de8ecb 100644 --- a/presto-main/src/main/java/io/prestosql/SystemSessionProperties.java +++ b/presto-main/src/main/java/io/prestosql/SystemSessionProperties.java @@ -121,6 +121,7 @@ public final class SystemSessionProperties public static final String ENABLE_INTERMEDIATE_AGGREGATIONS = "enable_intermediate_aggregations"; public static final String PUSH_AGGREGATION_THROUGH_JOIN = "push_aggregation_through_join"; public static final String PUSH_PARTIAL_AGGREGATION_THROUGH_JOIN = "push_partial_aggregation_through_join"; + public static final String MERGE_PARTIAL_AGGREGATION_WITH_JOIN = "merge_partial_aggregation_with_join"; public static final String PARSE_DECIMAL_LITERALS_AS_DOUBLE = "parse_decimal_literals_as_double"; public static final String FORCE_SINGLE_NODE_OUTPUT = "force_single_node_output"; public static final String FILTER_AND_PROJECT_MIN_OUTPUT_PAGE_SIZE = "filter_and_project_min_output_page_size"; @@ -627,6 +628,11 @@ public final class SystemSessionProperties "Push partial aggregations below joins", false, false), + booleanProperty( + MERGE_PARTIAL_AGGREGATION_WITH_JOIN, + "Merge partial aggregations with joins", + false, + false), booleanProperty( PARSE_DECIMAL_LITERALS_AS_DOUBLE, "Parse decimal literals as DOUBLE instead of DECIMAL", @@ -1452,6 +1458,11 @@ public final class SystemSessionProperties return session.getSystemProperty(PUSH_PARTIAL_AGGREGATION_THROUGH_JOIN, Boolean.class); } + public static boolean isMergePartialAggregationWithJoin(Session session) + { + return session.getSystemProperty(MERGE_PARTIAL_AGGREGATION_WITH_JOIN, Boolean.class); + } + public static boolean isParseDecimalLiteralsAsDouble(Session session) { return session.getSystemProperty(PARSE_DECIMAL_LITERALS_AS_DOUBLE, Boolean.class); diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/PlanOptimizers.java b/presto-main/src/main/java/io/prestosql/sql/planner/PlanOptimizers.java index 9df53f30c..cc206a925 100755 --- a/presto-main/src/main/java/io/prestosql/sql/planner/PlanOptimizers.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/PlanOptimizers.java @@ -63,6 +63,8 @@ import io.prestosql.sql.planner.iterative.rule.MergeLimitWithDistinct; import io.prestosql.sql.planner.iterative.rule.MergeLimitWithSort; import io.prestosql.sql.planner.iterative.rule.MergeLimitWithTopN; import io.prestosql.sql.planner.iterative.rule.MergeLimits; +import io.prestosql.sql.planner.iterative.rule.MergePartialAggregationWithJoin; +import io.prestosql.sql.planner.iterative.rule.MergePartialAggregationWithJoinPushProject; import io.prestosql.sql.planner.iterative.rule.MultipleDistinctAggregationToMarkDistinct; import io.prestosql.sql.planner.iterative.rule.PruneAggregationColumns; import io.prestosql.sql.planner.iterative.rule.PruneAggregationSourceColumns; @@ -762,7 +764,9 @@ public class PlanOptimizers ImmutableSet.of( new PushPartialAggregationThroughJoin(), new PushPartialAggregationThroughExchange(metadata), - new PruneJoinColumns()))); + new PruneJoinColumns(), + new MergePartialAggregationWithJoin(metadata), + new MergePartialAggregationWithJoinPushProject(metadata)))); builder.add(new IterativeOptimizer( ruleStats, diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/PlanSymbolAllocator.java b/presto-main/src/main/java/io/prestosql/sql/planner/PlanSymbolAllocator.java index af0346601..d212ea5ec 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/PlanSymbolAllocator.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/PlanSymbolAllocator.java @@ -166,4 +166,9 @@ public class PlanSymbolAllocator { return nextId++; } + + public void updateSymbolType(Symbol symbol, Type type) + { + symbols.put(symbol, type); + } } diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/MergePartialAggregationWithJoin.java b/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/MergePartialAggregationWithJoin.java new file mode 100644 index 000000000..76b121f0b --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/MergePartialAggregationWithJoin.java @@ -0,0 +1,470 @@ +/* + * 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.prestosql.sql.planner.iterative.rule; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Streams; +import io.prestosql.Session; +import io.prestosql.matching.Capture; +import io.prestosql.matching.Captures; +import io.prestosql.matching.Pattern; +import io.prestosql.metadata.Metadata; +import io.prestosql.spi.function.BuiltInFunctionHandle; +import io.prestosql.spi.function.FunctionHandle; +import io.prestosql.spi.plan.AggregationNode; +import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinNode.EquiJoinClause; +import io.prestosql.spi.plan.JoinOnAggregationNode; +import io.prestosql.spi.plan.JoinOnAggregationNode.JoinInternalAggregation; +import io.prestosql.spi.plan.PlanNode; +import io.prestosql.spi.plan.PlanNodeId; +import io.prestosql.spi.plan.Symbol; +import io.prestosql.spi.relation.CallExpression; +import io.prestosql.spi.relation.VariableReferenceExpression; +import io.prestosql.spi.type.Type; +import io.prestosql.spi.type.TypeSignature; +import io.prestosql.sql.analyzer.TypeSignatureProvider; +import io.prestosql.sql.planner.SymbolsExtractor; +import io.prestosql.sql.planner.iterative.Rule; + +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.google.common.collect.ImmutableSet.toImmutableSet; +import static com.google.common.collect.Sets.intersection; +import static io.prestosql.SystemSessionProperties.isMergePartialAggregationWithJoin; +import static io.prestosql.SystemSessionProperties.isSnapshotEnabled; +import static io.prestosql.SystemSessionProperties.isSpillEnabled; +import static io.prestosql.spi.plan.AggregationNode.Aggregation; +import static io.prestosql.spi.plan.AggregationNode.Step.PARTIAL; +import static io.prestosql.spi.plan.AggregationNode.singleGroupingSet; +import static io.prestosql.spi.type.BigintType.BIGINT; +import static io.prestosql.sql.planner.iterative.rule.MergePartialAggregationWithJoin.GroupJoinAggregationFunction.SUPPORTED_FUNCTIONS; +import static io.prestosql.sql.planner.iterative.rule.Util.restrictOutputs; +import static io.prestosql.sql.planner.plan.Patterns.aggregation; +import static io.prestosql.sql.planner.plan.Patterns.join; +import static io.prestosql.sql.planner.plan.Patterns.source; + +public class MergePartialAggregationWithJoin + implements Rule +{ + static final Capture JOIN_NODE = Capture.newCapture(); + + private static final Pattern PATTERN = aggregation() + .matching(MergePartialAggregationWithJoin::isSupportedAggregationNode) + .with(source().matching(join().capturedAs(JOIN_NODE))); + + private final Metadata metadata; + + public MergePartialAggregationWithJoin(Metadata metadata) + { + this.metadata = metadata; + } + + static boolean isSupportedAggregationNode(AggregationNode aggregationNode) + { + // Don't split streaming aggregations + if (aggregationNode.isStreamable()) { + return false; + } + + if (aggregationNode.getHashSymbol().isPresent()) { + // TODO: add support for hash symbol in aggregation node + return false; + } + return aggregationNode.getStep() == PARTIAL && aggregationNode.getGroupingSetCount() == 1; + } + + @Override + public Pattern getPattern() + { + return PATTERN; + } + + @Override + public boolean isEnabled(Session session) + { + return isMergePartialAggregationWithJoin(session) + && !isSpillEnabled(session) + && !isSnapshotEnabled(session); + } + + @Override + public Result apply(AggregationNode aggregationNode, Captures captures, Context context) + { + JoinNode joinNode = captures.get(JOIN_NODE); + + if (joinNode.getType() != JoinNode.Type.INNER) { + return Result.empty(); + } + + // Merge AggregationNode within JoinNode + return checkAndApplyRule(aggregationNode, context, joinNode); + } + + protected Result checkAndApplyRule(AggregationNode aggregationNode, Context context, JoinNode joinNode) + { + // Verify Aggregations against Whitelist functions + if (!checkAggregationsInWhitelist(aggregationNode.getAggregations())) { + return Result.empty(); + } + if (aggregationKeysSameAsJoinKeysOrSuperWithPresentInStart(aggregationNode.getGroupingKeys(), joinNode.getCriteria(), joinNode)) { + return Result.ofPlanNode(mergePartialAggregationWithJoin(aggregationNode, joinNode, context)); + } + + if (isJoinKeysSubsetOfAggrGroupKeys(aggregationNode.getGroupingKeys(), joinNode.getCriteria())) { + return Result.ofPlanNode(mergePartialAggregationWithJoin(aggregationNode, joinNode, context)); + } + + return Result.empty(); + } + + private boolean checkAggregationsInWhitelist(Map aggregations) + { + for (Map.Entry entry : aggregations.entrySet()) { + if (!SUPPORTED_FUNCTIONS.contains(entry.getValue().getFunctionCall().getDisplayName().toLowerCase(Locale.ROOT))) { + return false; + } + } + return true; + } + + private JoinInternalAggregation replaceAggregationSource( + PlanNodeId id, + AggregationNode aggregation, + Map aggregations, + PlanNode source, + List groupingKeys) + { + return new JoinInternalAggregation( + id, + source, + aggregations, + singleGroupingSet(groupingKeys), + ImmutableList.of(), + aggregation.getStep(), + aggregation.getHashSymbol(), + aggregation.getGroupIdSymbol(), + aggregation.getAggregationType(), + aggregation.getFinalizeSymbol()); + } + + private PlanNode mergePartialAggregationWithJoin(AggregationNode aggregationNode, JoinNode child, Context context) + { + // Divide AggregationNode into left and right AggregationNode + Set joinLeftChildSymbols = ImmutableSet.copyOf(child.getLeft().getOutputSymbols()); + List leftGroupingKeys = getPushedDownGroupingSet(aggregationNode, joinLeftChildSymbols, + intersection(getJoinRequiredSymbols(child), joinLeftChildSymbols)); + Map leftAggregations = getAggregationsMatchingSymbols( + aggregationNode.getAggregations(), joinLeftChildSymbols); + + Set joinRightChildSymbols = ImmutableSet.copyOf(child.getRight().getOutputSymbols()); + List rightGroupingKeys = getPushedDownGroupingSet(aggregationNode, joinRightChildSymbols, + intersection(getJoinRequiredSymbols(child), joinRightChildSymbols)); + Map rightAggregations = getAggregationsMatchingSymbols( + aggregationNode.getAggregations(), joinRightChildSymbols); + Map commonAggregations = getCommonAggregations(aggregationNode.getAggregations()); + if (leftAggregations.size() > 0) { + leftAggregations.putAll(commonAggregations); + } + else if (rightAggregations.size() > 0) { + rightAggregations.putAll(commonAggregations); + } + else { + leftAggregations.putAll(commonAggregations); + } + String aggFunction = "count"; + FunctionHandle functionHandle = metadata.getFunctionAndTypeManager().lookupFunction(aggFunction, + TypeSignatureProvider.fromTypeSignatures()); + CallExpression countExpr = new CallExpression( + aggFunction, + functionHandle, + BIGINT, + ImmutableList.of()); + Aggregation count = new Aggregation( + countExpr, + ImmutableList.of(), + false, + Optional.empty(), + Optional.empty(), + Optional.empty()); + Symbol countSymbolLeft = context.getSymbolAllocator().newSymbol(countExpr.getDisplayName(), BIGINT, null); + Symbol countSymbolRight = context.getSymbolAllocator().newSymbol(countExpr.getDisplayName(), BIGINT, null); + + ImmutableMap.Builder newLeftAggrs = ImmutableMap.builder(); + ImmutableMap.Builder newRightAggrs = ImmutableMap.builder(); + Map aggrOnLeftAggregations = getNewAggrOnAggr(leftAggregations, context, newLeftAggrs); + Map aggrOnRightAggregations = getNewAggrOnAggr(rightAggregations, context, newRightAggrs); + JoinInternalAggregation left = replaceAggregationSource(child.getId(), + aggregationNode, + newLeftAggrs.put(countSymbolLeft, count).build(), + child.getLeft(), + leftGroupingKeys); + JoinInternalAggregation right = replaceAggregationSource(child.getId(), + aggregationNode, + newRightAggrs.put(countSymbolRight, count).build(), + child.getRight(), + rightGroupingKeys); + + JoinInternalAggregation aggrOnLeft = replaceAggregationSource(child.getId(), + aggregationNode, + aggrOnLeftAggregations, + left, + leftGroupingKeys); + JoinInternalAggregation aggrOnRight = replaceAggregationSource(child.getId(), + aggregationNode, + aggrOnRightAggregations, + right, + rightGroupingKeys); + + ImmutableList.Builder outputSymbols = ImmutableList.builder(); + outputSymbols.addAll(aggrOnLeft.getOutputSymbols()).addAll(aggrOnRight.getOutputSymbols()); + JoinOnAggregationNode newJoinNode = new JoinOnAggregationNode(child.getId(), + child.getType(), + child.getCriteria(), + child.getFilter(), + child.getLeftHashSymbol(), + child.getRightHashSymbol(), + child.getDistributionType(), + child.isSpillable(), + child.getDynamicFilters(), + left, + right, + aggrOnLeft, + aggrOnRight, + outputSymbols.build()); + return restrictOutputs(context.getIdAllocator(), + newJoinNode, + ImmutableSet.copyOf(aggregationNode.getOutputSymbols()), + true, + context.getSymbolAllocator().getTypes()).orElse(newJoinNode); + } + + private Map getNewAggrOnAggr(Map aggregations, + Context context, + Builder origAggrsNewRef) + { + String aggFunctionSum = "sum"; + + ImmutableMap.Builder newAggregations = ImmutableMap.builder(); + aggregations.forEach((symbol, aggregation) -> { + CallExpression expression = aggregation.getFunctionCall(); + CallExpression newExpr = null; + + // Define a new symbol for original expression + TypeSignature origAggrReturnTypeSig = ((BuiltInFunctionHandle) expression.getFunctionHandle()).getSignature().getReturnType(); + Type origAggrReturnType = metadata.getFunctionAndTypeManager().getType(origAggrReturnTypeSig); + Symbol newSymbol = null; + + if (expression.getDisplayName().equals("count")) { + FunctionHandle newFunctionHandle = metadata.getFunctionAndTypeManager().lookupFunction(aggFunctionSum, + TypeSignatureProvider.fromTypeSignatures(origAggrReturnTypeSig)); + + // newSymbol is going to point to origExpression, so using its returnType during allocation + newSymbol = context.getSymbolAllocator().newSymbol(expression.getDisplayName(), origAggrReturnType, null); + newExpr = new CallExpression(aggFunctionSum, + newFunctionHandle, + expression.getType(), + ImmutableList.of(new VariableReferenceExpression(newSymbol.getName(), + origAggrReturnType))); + } + else { + FunctionHandle newFunctionHandle = metadata.getFunctionAndTypeManager().lookupFunction(expression.getDisplayName(), + TypeSignatureProvider.fromTypeSignatures(origAggrReturnTypeSig)); + + // newSymbol is going to point to origExpression, so using its returnType during allocation + newSymbol = context.getSymbolAllocator().newSymbol(expression.getDisplayName(), expression.getType(), null); + newExpr = new CallExpression(expression.getDisplayName(), + newFunctionHandle, + expression.getType(), + ImmutableList.of(new VariableReferenceExpression(newSymbol.getName(), + expression.getType()))); + } + context.getSymbolAllocator().updateSymbolType(symbol, newExpr.getType()); + + Aggregation newAggr = new Aggregation( + newExpr, + newExpr.getArguments(), + aggregation.isDistinct(), + aggregation.getFilter(), + aggregation.getOrderingScheme(), + aggregation.getMask()); + newAggregations.put(symbol, newAggr); + origAggrsNewRef.put(newSymbol, aggregation); + }); + return newAggregations.build(); + } + + private Map getAggregationsMatchingSymbols( + Map aggregations, Set symbols) + { + return aggregations.entrySet().stream() + .filter(entry -> { + List aggrSymbolList = SymbolsExtractor.extractAll(entry.getValue()); + return aggrSymbolList.size() == 1 && symbols.contains(aggrSymbolList.get(0)); + }) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + private Map getCommonAggregations(Map aggregations) + { + return aggregations.entrySet().stream() + .filter(entry -> { + List aggrSymbolList = SymbolsExtractor.extractAll(entry.getValue()); + return aggrSymbolList.size() == 0; + }) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + private Set getJoinRequiredSymbols(JoinNode node) + { + return Streams.concat( + node.getCriteria().stream().map(JoinNode.EquiJoinClause::getLeft), + node.getCriteria().stream().map(JoinNode.EquiJoinClause::getRight), + node.getFilter().map(SymbolsExtractor::extractUnique).orElse(ImmutableSet.of()).stream(), + node.getLeftHashSymbol().map(ImmutableSet::of).orElse(ImmutableSet.of()).stream(), + node.getRightHashSymbol().map(ImmutableSet::of).orElse(ImmutableSet.of()).stream()) + .collect(toImmutableSet()); + } + + private List getPushedDownGroupingSet(AggregationNode aggregation, Set availableSymbols, + Set requiredJoinSymbols) + { + List groupingSet = aggregation.getGroupingKeys(); + + // keep symbols that are directly from the join's child (availableSymbols) + ImmutableList.Builder newGrpKeys = ImmutableList.builder(); + List pushedDownGroupingSet = groupingSet.stream() + .filter(availableSymbols::contains) + .collect(Collectors.toList()); + + // add missing required join symbols to grouping set + Set existingSymbols = new HashSet<>(pushedDownGroupingSet); + requiredJoinSymbols.stream() + .filter(existingSymbols::add) + .forEach(newGrpKeys::add); + // New Grp Keys - First have Join Keys and then Non-Join Grp Keys + newGrpKeys.addAll(pushedDownGroupingSet); + return newGrpKeys.build(); + } + + private boolean aggregationKeysSameAsJoinKeysOrSuperWithPresentInStart(List groupingKeys, List criteria, JoinNode joinNode) + { + // Check all join keys are part of grouping keys + // and Grouping keys which are not in Join keys, should belong to only one side + if (criteria.size() < 1) { + return false; + } + if (criteria.size() > groupingKeys.size()) { + return false; + } + Set grpKeySet = new HashSet<>(groupingKeys.subList(0, criteria.size())); + for (JoinNode.EquiJoinClause joinClause : criteria) { + if (grpKeySet.contains(joinClause.getLeft())) { + grpKeySet.remove(joinClause.getLeft()); + } + else if (grpKeySet.contains(joinClause.getRight())) { + grpKeySet.remove(joinClause.getRight()); + } + else { + return false; + } + } + if (criteria.size() == groupingKeys.size()) { + return true; + } + Set remainingGrpKeys = new HashSet<>(groupingKeys.subList(criteria.size(), groupingKeys.size())); + if (allSymbolsOn(remainingGrpKeys, joinNode.getLeft().getOutputSymbols())) { + return true; + } + else if (allSymbolsOn(remainingGrpKeys, joinNode.getRight().getOutputSymbols())) { + return true; + } + return false; + } + + private static boolean allSymbolsOn(Set grpKeySet, List symbols) + { + return new HashSet<>(symbols).containsAll(grpKeySet); + } + + private boolean isJoinKeysSubsetOfAggrGroupKeys(List groupingKeys, List criteria) + { + // Check all join keys are part of grouping keys(Any Place) + // and Grouping keys which are not in Join keys can belong to any side + if (criteria.size() < 1) { + return false; + } + if (criteria.size() > groupingKeys.size()) { + return false; + } + Set grpKeySet = new HashSet<>(groupingKeys); + for (JoinNode.EquiJoinClause joinClause : criteria) { + if (grpKeySet.contains(joinClause.getLeft())) { + grpKeySet.remove(joinClause.getLeft()); + } + else if (grpKeySet.contains(joinClause.getRight())) { + grpKeySet.remove(joinClause.getRight()); + } + else { + return false; + } + } + // Exact match between group by and Join + if (criteria.size() == groupingKeys.size()) { + return true; + } + return true; + } + + public static enum GroupJoinAggregationFunction + { + SUM("sum"), + COUNT("count"), + AVG("avg"), + MIN("min"), + MAX("max"), + STDDEV_SAMP("stddev_samp"), + STDDEV("stddev"); + + private final String name; + + public static final Set SUPPORTED_FUNCTIONS = Stream.of(GroupJoinAggregationFunction.values()).map(GroupJoinAggregationFunction::getName).collect(Collectors.toSet()); + + GroupJoinAggregationFunction(String name) + { + this.name = name; + } + + public String getName() + { + return this.name; + } + + public String toString() + { + return this.name; + } + } +} diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/MergePartialAggregationWithJoinPushProject.java b/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/MergePartialAggregationWithJoinPushProject.java new file mode 100644 index 000000000..abe02605b --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/MergePartialAggregationWithJoinPushProject.java @@ -0,0 +1,147 @@ +/* + * 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.prestosql.sql.planner.iterative.rule; + +import io.prestosql.matching.Capture; +import io.prestosql.matching.Captures; +import io.prestosql.matching.Pattern; +import io.prestosql.metadata.Metadata; +import io.prestosql.spi.plan.AggregationNode; +import io.prestosql.spi.plan.Assignments; +import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.PlanNode; +import io.prestosql.spi.plan.ProjectNode; +import io.prestosql.spi.plan.Symbol; +import io.prestosql.spi.relation.RowExpression; +import io.prestosql.spi.relation.VariableReferenceExpression; +import io.prestosql.sql.planner.SymbolsExtractor; +import io.prestosql.sql.planner.TypeProvider; + +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; + +import static io.prestosql.sql.planner.plan.Patterns.aggregation; +import static io.prestosql.sql.planner.plan.Patterns.join; +import static io.prestosql.sql.planner.plan.Patterns.project; +import static io.prestosql.sql.planner.plan.Patterns.source; + +public class MergePartialAggregationWithJoinPushProject + extends MergePartialAggregationWithJoin +{ + private static final Capture PROJECT_NODE = Capture.newCapture(); + + private static final Pattern PATTERN = aggregation() + .matching(MergePartialAggregationWithJoin::isSupportedAggregationNode) + .with(source().matching(project().capturedAs(PROJECT_NODE) + .with(source().matching(join().capturedAs(JOIN_NODE))))); + + public MergePartialAggregationWithJoinPushProject(Metadata metadata) + { + super(metadata); + } + + @Override + public Pattern getPattern() + { + return PATTERN; + } + + @Override + public Result apply(AggregationNode node, Captures captures, Context context) + { + ProjectNode projectNode = captures.get(PROJECT_NODE); + JoinNode joinNode = captures.get(JOIN_NODE); + if (!nonTrivialProjection(projectNode)) { + return Result.empty(); + } + if (joinNode.getType() != JoinNode.Type.INNER) { + return Result.empty(); + } + + // Check if Project can be pushed down through join + // Check if aggregations can be pushed down through join + // First push Project through Join + // Then call super.apply() or equivalent function to apply the rule + Assignments assignments = projectNode.getAssignments(); + Assignments.Builder leftAssignments = Assignments.builder(); + Assignments.Builder rightAssignments = Assignments.builder(); + HashSet leftSymbolSet = new HashSet<>(joinNode.getLeft().getOutputSymbols()); + HashSet rightSymbolSet = new HashSet<>(joinNode.getRight().getOutputSymbols()); + for (Entry assignment : assignments.entrySet()) { + List symbols = SymbolsExtractor.extractAll(assignment.getValue()); + if (leftSymbolSet.containsAll(symbols)) { + leftAssignments.put(assignment.getKey(), assignment.getValue()); + } + else if (rightSymbolSet.containsAll(symbols)) { + rightAssignments.put(assignment.getKey(), assignment.getValue()); + } + else { + return Result.empty(); + } + } + TypeProvider typeProvider = context.getSymbolAllocator().getTypes(); + for (Entry df : joinNode.getDynamicFilters().entrySet()) { + if (leftSymbolSet.contains(df.getValue())) { + leftAssignments.put(df.getValue(), new VariableReferenceExpression(df.getValue().getName(), typeProvider.get(df.getValue()))); + } + else if (rightSymbolSet.contains(df.getValue())) { + rightAssignments.put(df.getValue(), new VariableReferenceExpression(df.getValue().getName(), typeProvider.get(df.getValue()))); + } + } + + PlanNode leftNode = joinNode.getLeft(); + Assignments build = leftAssignments.build(); + if (build.size() > 0) { + leftNode = new ProjectNode(context.getIdAllocator().getNextId(), joinNode.getLeft(), build); + } + + PlanNode rightNode = joinNode.getRight(); + build = rightAssignments.build(); + if (build.size() > 0) { + rightNode = new ProjectNode(context.getIdAllocator().getNextId(), joinNode.getRight(), build); + } + joinNode = new JoinNode(joinNode.getId(), + joinNode.getType(), + leftNode, + rightNode, + joinNode.getCriteria(), + projectNode.getOutputSymbols(), + joinNode.getFilter(), + joinNode.getLeftHashSymbol(), + joinNode.getRightHashSymbol(), + joinNode.getDistributionType(), + joinNode.isSpillable(), + joinNode.getDynamicFilters()); + node = new AggregationNode(node.getId(), + joinNode, + node.getAggregations(), + node.getGroupingSets(), + node.getPreGroupedSymbols(), + node.getStep(), + node.getHashSymbol(), + node.getGroupIdSymbol(), + node.getAggregationType(), + node.getFinalizeSymbol()); + return checkAndApplyRule(node, context, joinNode); + } + + private static boolean nonTrivialProjection(ProjectNode project) + { + return !project.getAssignments() + .getExpressions().stream() + .allMatch(expression -> expression instanceof VariableReferenceExpression); + } +} diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/HashGenerationOptimizer.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/HashGenerationOptimizer.java index 5cd7a336e..666b52126 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/HashGenerationOptimizer.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/HashGenerationOptimizer.java @@ -14,6 +14,7 @@ package io.prestosql.sql.planner.optimizations; import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; @@ -36,6 +37,8 @@ import io.prestosql.spi.plan.CTEScanNode; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.GroupIdNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; +import io.prestosql.spi.plan.JoinOnAggregationNode.JoinInternalAggregation; import io.prestosql.spi.plan.MarkDistinctNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.PlanNodeIdAllocator; @@ -53,6 +56,7 @@ import io.prestosql.sql.planner.FunctionCallBuilder; import io.prestosql.sql.planner.Partitioning.ArgumentBinding; import io.prestosql.sql.planner.PartitioningScheme; import io.prestosql.sql.planner.PlanSymbolAllocator; +import io.prestosql.sql.planner.SymbolsExtractor; import io.prestosql.sql.planner.TypeProvider; import io.prestosql.sql.planner.plan.ApplyNode; import io.prestosql.sql.planner.plan.DistinctLimitNode; @@ -97,6 +101,7 @@ import static io.prestosql.spi.connector.CatalogSchemaName.DEFAULT_NAMESPACE; import static io.prestosql.spi.function.FunctionKind.SCALAR; import static io.prestosql.spi.function.Signature.mangleOperatorName; import static io.prestosql.spi.operator.ReuseExchangeOperator.STRATEGY.REUSE_STRATEGY_DEFAULT; +import static io.prestosql.spi.plan.AggregationNode.singleGroupingSet; import static io.prestosql.spi.plan.JoinNode.Type.INNER; import static io.prestosql.spi.plan.JoinNode.Type.LEFT; import static io.prestosql.spi.plan.JoinNode.Type.RIGHT; @@ -411,6 +416,197 @@ public class HashGenerationOptimizer hashSymbolsWithParentPreferences); } + @Override + public PlanWithProperties visitJoinOnAggregation(JoinOnAggregationNode node, HashComputationSet parentPreference) + { + List clauses = node.getCriteria(); + + // join does not pass through preferred hash symbols since they take more memory and since + // the join node filters, may take more compute + Optional leftHashComputation = computeHash(metadata, planSymbolAllocator, Lists.transform(clauses, JoinNode.EquiJoinClause::getLeft)); + Optional rightHashComputation = computeHash(metadata, planSymbolAllocator, Lists.transform(clauses, JoinNode.EquiJoinClause::getRight)); + + // build map of all hash symbols + // NOTE: Full outer join doesn't use hash symbols + BiMap allHashSymbols = HashBiMap.create(); + PlanWithProperties left; + PlanWithProperties right; + Optional leftHashSymbol; + Optional rightHashSymbol; + + Optional leftHashSymbolAggr; + Optional rightHashSymbolAggr; + + List leftGrpByKeys = node.getLeftAggr().getGroupingKeys(); + List rightGrpByKeys = node.getRightAggr().getGroupingKeys(); + List aggrOnLeftGrpByKeys = node.getAggrOnLeft().getGroupingKeys(); + List aggrOnRightGrpByKeys = node.getAggrOnRight().getGroupingKeys(); + + // Update Hash Symbol in Left and Right Aggregation of Join + // If Join Keys and Grouping Keys are same, then only join hash symbol can be re-used, otherwise need to calculate for grouping keys + if (!canJoinHashSymbolBeUsedForJoinInternalAggregation(Lists.transform(clauses, JoinNode.EquiJoinClause::getLeft), leftGrpByKeys)) { + left = planAndEnforce(node.getLeft(), new HashComputationSet(leftHashComputation), true, new HashComputationSet(leftHashComputation)); + leftHashSymbol = Optional.of(left.getRequiredHashSymbol(leftHashComputation.get())); + + List groupingKeys = node.getLeftAggr().getGroupingKeys(); + leftGrpByKeys = ImmutableList.builder() + .addAll(groupingKeys.subList(0, node.getCriteria().size())) + .add(leftHashSymbol.get()) + .addAll(groupingKeys.subList(node.getCriteria().size(), groupingKeys.size())) + .build(); + + groupingKeys = node.getAggrOnLeft().getGroupingKeys(); + aggrOnLeftGrpByKeys = ImmutableList.builder() + .addAll(groupingKeys.subList(0, node.getCriteria().size())) + .add(leftHashSymbol.get()) + .addAll(groupingKeys.subList(node.getCriteria().size(), groupingKeys.size())) + .build(); + + Optional leftAggrHashComputation = computeHash(metadata, planSymbolAllocator, leftGrpByKeys); + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + builder.put(leftAggrHashComputation.get(), leftAggrHashComputation.get()); + left = planAndEnforce(left.getNode(), new HashComputationSet(builder.build()), true, new HashComputationSet(builder.build())); + leftHashSymbolAggr = Optional.ofNullable(left.getRequiredHashSymbol(leftAggrHashComputation.get())); + } + else { + left = planAndEnforce(node.getLeft(), new HashComputationSet(leftHashComputation), true, new HashComputationSet(leftHashComputation)); + leftHashSymbol = Optional.of(left.getRequiredHashSymbol(leftHashComputation.get())); + leftHashSymbolAggr = leftHashSymbol; + } + + if (!canJoinHashSymbolBeUsedForJoinInternalAggregation(Lists.transform(clauses, JoinNode.EquiJoinClause::getRight), rightGrpByKeys)) { + right = planAndEnforce(node.getRight(), new HashComputationSet(rightHashComputation), true, new HashComputationSet(rightHashComputation)); + rightHashSymbol = Optional.of(right.getRequiredHashSymbol(rightHashComputation.get())); + + List groupingKeys = node.getRightAggr().getGroupingKeys(); + rightGrpByKeys = ImmutableList.builder() + .addAll(groupingKeys.subList(0, node.getCriteria().size())) + .add(rightHashSymbol.get()) + .addAll(groupingKeys.subList(node.getCriteria().size(), groupingKeys.size())) + .build(); + + groupingKeys = node.getAggrOnRight().getGroupingKeys(); + aggrOnRightGrpByKeys = ImmutableList.builder() + .addAll(groupingKeys.subList(0, node.getCriteria().size())) + .add(rightHashSymbol.get()) + .addAll(groupingKeys.subList(node.getCriteria().size(), groupingKeys.size())) + .build(); + + Optional rightAggrHashComputation = computeHash(metadata, planSymbolAllocator, rightGrpByKeys); + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + builder.put(rightAggrHashComputation.get(), rightAggrHashComputation.get()); + right = planAndEnforce(left.getNode(), new HashComputationSet(builder.build()), true, new HashComputationSet(builder.build())); + rightHashSymbolAggr = Optional.ofNullable(left.getRequiredHashSymbol(rightAggrHashComputation.get())); + } + else { + right = planAndEnforce(node.getRight(), new HashComputationSet(rightHashComputation), true, new HashComputationSet(rightHashComputation)); + rightHashSymbol = Optional.of(right.getRequiredHashSymbol(rightHashComputation.get())); + rightHashSymbolAggr = rightHashSymbol; + } + + if (node.getType() == INNER || node.getType() == LEFT) { + allHashSymbols.putAll(left.getHashSymbols()); + } + if (node.getType() == INNER || node.getType() == RIGHT) { + allHashSymbols.putAll(right.getHashSymbols()); + } + + // retain only hash symbols preferred by parent nodes + Map hashSymbolsWithParentPreferences = + allHashSymbols.entrySet() + .stream() + .filter(entry -> parentPreference.getHashes().contains(entry.getKey())) + .collect(toImmutableMap(Entry::getKey, Entry::getValue)); + + JoinInternalAggregation leftAggr = new JoinInternalAggregation(node.getLeftAggr().getId(), + left.getNode(), + node.getLeftAggr().getAggregations(), + singleGroupingSet(leftGrpByKeys), + ImmutableList.of(), + node.getLeftAggr().getStep(), + leftHashSymbolAggr, + node.getLeftAggr().getGroupIdSymbol(), + node.getLeftAggr().getAggregationType(), + node.getLeftAggr().getFinalizeSymbol()); + + JoinInternalAggregation rightAggr = new JoinInternalAggregation(node.getRightAggr().getId(), + right.getNode(), + node.getRightAggr().getAggregations(), + singleGroupingSet(rightGrpByKeys), + ImmutableList.of(), + node.getRightAggr().getStep(), + rightHashSymbolAggr, + node.getRightAggr().getGroupIdSymbol(), + node.getRightAggr().getAggregationType(), + node.getRightAggr().getFinalizeSymbol()); + + JoinInternalAggregation aggrOnLeftAggr = new JoinInternalAggregation(node.getAggrOnLeft().getId(), + leftAggr, + node.getAggrOnLeft().getAggregations(), + singleGroupingSet(aggrOnLeftGrpByKeys), + ImmutableList.of(), + node.getAggrOnLeft().getStep(), + leftHashSymbolAggr, + node.getAggrOnLeft().getGroupIdSymbol(), + node.getAggrOnLeft().getAggregationType(), + node.getAggrOnLeft().getFinalizeSymbol()); + + JoinInternalAggregation aggrOnRightAggr = new JoinInternalAggregation(node.getAggrOnRight().getId(), + rightAggr, + node.getAggrOnRight().getAggregations(), + singleGroupingSet(aggrOnRightGrpByKeys), + ImmutableList.of(), + node.getAggrOnRight().getStep(), + rightHashSymbolAggr, + node.getAggrOnRight().getGroupIdSymbol(), + node.getAggrOnRight().getAggregationType(), + node.getAggrOnRight().getFinalizeSymbol()); + + ImmutableList.Builder newOutputSymbolBuilder = ImmutableList.builder(); + newOutputSymbolBuilder + .addAll(aggrOnLeftAggr.getOutputSymbols()) + .addAll(aggrOnRightAggr.getOutputSymbols()); + return new PlanWithProperties( + new JoinOnAggregationNode( + node.getId(), + node.getType(), + node.getCriteria(), + node.getFilter(), + leftHashSymbol, + rightHashSymbol, + node.getDistributionType(), + node.isSpillable(), + node.getDynamicFilters(), + leftAggr, + rightAggr, + aggrOnLeftAggr, + aggrOnRightAggr, + newOutputSymbolBuilder.build()), + hashSymbolsWithParentPreferences); + } + + private boolean canJoinHashSymbolBeUsedForJoinInternalAggregation(List joinSymbols, + List groupingKeys) + { + if (joinSymbols.size() == groupingKeys.size() && joinSymbols.containsAll(groupingKeys)) { + return true; + } + return false; + } + + private boolean canHashSymbolUseForJoinInternalAggregation(Optional hashSymbol, + List groupingKeys, + BiMap allHashSymbols) + { + if (hashSymbol.isPresent()) { + HashComputation hashComputation = allHashSymbols.inverse().get(hashSymbol.get()); + if (hashComputation.getFields().size() == groupingKeys.size() && hashComputation.getFields().containsAll(groupingKeys)) { + return true; + } + } + return false; + } + @Override public PlanWithProperties visitSemiJoin(SemiJoinNode node, HashComputationSet parentPreference) { @@ -685,29 +881,60 @@ public class HashGenerationOptimizer // create a new project node with all assignments from the original node Assignments.Builder newAssignments = Assignments.builder(); - newAssignments.putAll(node.getAssignments()); + Assignments assignments = node.getAssignments(); + Map hashSymbolMap = new HashMap<>(); + for (Symbol symbol : assignments.getOutputs()) { + if (symbol.getName().startsWith("$hashvalue")) { + HashComputationSet hashComputationSet = new HashComputationSet(Optional.of(new HashComputation(metadata, planSymbolAllocator, SymbolsExtractor.extractAll(assignments.get(symbol))))); + hashSymbolMap.put(hashComputationSet, symbol); + newAssignments.put(symbol, assignments.get(symbol)); + } + else { + newAssignments.put(symbol, assignments.get(symbol)); + } + } // and all hash symbols that could be translated to the source symbols Map allHashSymbols = new HashMap<>(); for (HashComputation hashComputation : sourceContext.getHashes()) { - Symbol hashSymbol = child.getHashSymbols().get(hashComputation); - RowExpression hashExpression; - if (hashSymbol == null) { - hashSymbol = planSymbolAllocator.newHashSymbol(); - hashExpression = hashComputation.getHashExpression(); + List fields = hashComputation.getFields(); + Symbol symbol = canComputeWith(fields, hashSymbolMap); + if (symbol == null) { + Symbol hashSymbol = child.getHashSymbols().get(hashComputation); + RowExpression hashExpression; + if (hashSymbol == null) { + hashSymbol = planSymbolAllocator.newHashSymbol(); + hashExpression = hashComputation.getHashExpression(); + } + else { + hashExpression = toVariableReference(hashSymbol, planSymbolAllocator.getTypes().get(hashSymbol)); + } + newAssignments.put(hashSymbol, hashExpression); + for (HashComputation sourceHashComputation : sourceContext.lookup(hashComputation)) { + allHashSymbols.put(sourceHashComputation, hashSymbol); + } } else { - hashExpression = toVariableReference(hashSymbol, planSymbolAllocator.getTypes().get(hashSymbol)); - } - newAssignments.put(hashSymbol, hashExpression); - for (HashComputation sourceHashComputation : sourceContext.lookup(hashComputation)) { - allHashSymbols.put(sourceHashComputation, hashSymbol); + for (HashComputation sourceHashComputation : sourceContext.lookup(hashComputation)) { + allHashSymbols.put(sourceHashComputation, symbol); + } } } return new PlanWithProperties(new ProjectNode(node.getId(), child.getNode(), newAssignments.build()), allHashSymbols); } + private Symbol canComputeWith(List fields, Map hashSymbolMap) + { + for (Map.Entry entry : hashSymbolMap.entrySet()) { + boolean anyMatch = entry.getKey().getHashes().stream().anyMatch(hashComputation -> fields.size() == hashComputation.getFields().size() && fields.containsAll(hashComputation.getFields())); + if (anyMatch) { + return entry.getValue(); + } + } + return null; + } + @Override public PlanWithProperties visitUnnest(UnnestNode node, HashComputationSet parentPreference) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/planprinter/PlanPrinter.java b/presto-main/src/main/java/io/prestosql/sql/planner/planprinter/PlanPrinter.java index f8edb93f8..50fc04dd3 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/planprinter/PlanPrinter.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/planprinter/PlanPrinter.java @@ -44,6 +44,8 @@ import io.prestosql.spi.plan.GroupIdNode; import io.prestosql.spi.plan.GroupReference; import io.prestosql.spi.plan.IntersectNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; +import io.prestosql.spi.plan.JoinOnAggregationNode.JoinInternalAggregation; import io.prestosql.spi.plan.LimitNode; import io.prestosql.spi.plan.MarkDistinctNode; import io.prestosql.spi.plan.OrderingScheme; @@ -468,6 +470,43 @@ public class PlanPrinter return null; } + @Override + public Void visitJoinOnAggregation(JoinOnAggregationNode node, Void context) + { + List joinExpressions = new ArrayList<>(); + for (JoinNode.EquiJoinClause clause : node.getCriteria()) { + joinExpressions.add(JoinNodeUtils.toExpression(clause).toString()); + } + node.getFilter().map(formatter::apply).ifPresent(joinExpressions::add); + + NodeRepresentation nodeOutput; + nodeOutput = addNode(node, + "Group" + node.getType().getJoinLabel(), + format("[%s]%s", Joiner.on(" AND ").join(joinExpressions), formatHash(node.getLeftHashSymbol(), node.getRightHashSymbol()))); + + node.getDistributionType().ifPresent(distributionType -> nodeOutput.appendDetailsLine("Distribution: %s", distributionType)); + if (!node.getDynamicFilters().isEmpty()) { + nodeOutput.appendDetailsLine("dynamicFilterAssignments = %s", printDynamicFilterAssignments(node.getDynamicFilters())); + } + + Optional sortExpressionContext = node.getFilter().flatMap(filter -> SortExpressionExtractor.extractSortExpression(metadata, node.getRightOutputSymbols(), filter)); + sortExpressionContext.ifPresent(sortContext -> nodeOutput.appendDetailsLine("SortExpression[%s]", formatter.apply(sortContext.getSortExpression()))); + + JoinInternalAggregation aggrOnLeft = node.getAggrOnLeft(); + JoinInternalAggregation aggrOnRight = node.getAggrOnRight(); + nodeOutput.appendDetailsLine("AggrOnAggrLeft = {%s}", getJoinInternalAggregationDetails(aggrOnLeft)); + nodeOutput.appendDetailsLine("AggrOnAggrRight = {%s}", getJoinInternalAggregationDetails(aggrOnRight)); + + JoinInternalAggregation leftAggr = node.getLeftAggr(); + JoinInternalAggregation rightAggr = node.getRightAggr(); + nodeOutput.appendDetailsLine("AggrLeft = {%s}", getJoinInternalAggregationDetails(leftAggr)); + nodeOutput.appendDetailsLine("AggrRight = {%s}", getJoinInternalAggregationDetails(rightAggr)); + + node.getLeft().accept(this, context); + node.getRight().accept(this, context); + return null; + } + @Override public Void visitSpatialJoin(SpatialJoinNode node, Void context) { @@ -1477,6 +1516,36 @@ public class PlanPrinter representation.addNode(nodeOutput); return nodeOutput; } + + private String getJoinInternalAggregationDetails(JoinInternalAggregation node) + { + String type = ""; + if (node.getStep() != AggregationNode.Step.SINGLE) { + type = format("(%s)", node.getStep().toString()); + } + String key = ""; + if (!node.getGroupingKeys().isEmpty()) { + key = node.getGroupingKeys().toString(); + } + String value = ""; + if (node.getAggregationType().equals(AggregationNode.AggregationType.HASH)) { + value = format("hashAggregate%s%s%s", type, key, formatHash(node.getHashSymbol())); + } + StringBuilder builder = new StringBuilder(value); + if (node.getAggregations().size() > 0) { + node.getAggregations().forEach((symbol, aggregation) -> builder.append(format("%s := %s", symbol, formatAggregation(aggregation)))); + // to remove the last: ", " + builder.setLength(builder.length() - 2); + } + else { + builder.setLength(builder.length() > 0 ? builder.length() - 1 : 0); + } + String columns = node.getOutputSymbols().stream() + .map(symbol -> symbol + ":" + types.get(symbol).getDisplayName()) + .collect(joining(", ")); + builder.append(" Layout: [").append(columns).append("]"); + return builder.toString(); + } } private static String formatFrame(WindowNode.Frame frame) diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/sanity/ValidateDependenciesChecker.java b/presto-main/src/main/java/io/prestosql/sql/planner/sanity/ValidateDependenciesChecker.java index e7833ab0e..b9d761e4d 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/sanity/ValidateDependenciesChecker.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/sanity/ValidateDependenciesChecker.java @@ -26,6 +26,7 @@ import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.GroupIdNode; import io.prestosql.spi.plan.IntersectNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.LimitNode; import io.prestosql.spi.plan.MarkDistinctNode; import io.prestosql.spi.plan.PlanNode; @@ -431,6 +432,43 @@ public final class ValidateDependenciesChecker return null; } + @Override + public Void visitJoinOnAggregation(JoinOnAggregationNode node, Set boundSymbols) + { + node.getLeft().accept(this, boundSymbols); + node.getRight().accept(this, boundSymbols); + + Set leftInputs = createInputs(node.getLeft(), boundSymbols); + Set rightInputs = createInputs(node.getRight(), boundSymbols); + Set allInputs = ImmutableSet.builder() + .addAll(leftInputs) + .addAll(rightInputs) + .build(); + + for (JoinNode.EquiJoinClause clause : node.getCriteria()) { + checkArgument(leftInputs.contains(clause.getLeft()), "Symbol from join clause (%s) not in left source (%s)", clause.getLeft(), node.getLeft().getOutputSymbols()); + checkArgument(rightInputs.contains(clause.getRight()), "Symbol from join clause (%s) not in right source (%s)", clause.getRight(), node.getRight().getOutputSymbols()); + } + + node.getFilter().ifPresent(predicate -> { + Set predicateSymbols; + if (isExpression(predicate)) { + predicateSymbols = SymbolsExtractor.extractUnique(castToExpression(predicate)); + } + else { + predicateSymbols = SymbolsExtractor.extractUnique(predicate); + } + checkArgument( + allInputs.containsAll(predicateSymbols), + "Symbol from filter (%s) not in sources (%s)", + predicateSymbols, + allInputs); + }); + + checkLeftOutputSymbolsBeforeRight(node.getLeft().getOutputSymbols(), node.getOutputSymbols()); + return null; + } + @Override public Void visitSemiJoin(SemiJoinNode node, Set boundSymbols) { diff --git a/presto-main/src/main/java/io/prestosql/util/GraphvizPrinter.java b/presto-main/src/main/java/io/prestosql/util/GraphvizPrinter.java index b41081d72..56e0c8a1b 100644 --- a/presto-main/src/main/java/io/prestosql/util/GraphvizPrinter.java +++ b/presto-main/src/main/java/io/prestosql/util/GraphvizPrinter.java @@ -23,6 +23,7 @@ import io.prestosql.spi.plan.AggregationNode.Aggregation; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.GroupIdNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.LimitNode; import io.prestosql.spi.plan.MarkDistinctNode; import io.prestosql.spi.plan.PlanNode; @@ -106,6 +107,7 @@ public final class GraphvizPrinter INDEX_SOURCE, UNNEST, ANALYZE_FINISH, + GROUPJOIN, } private static final Map NODE_COLORS = immutableEnumMap(ImmutableMap.builder() @@ -130,6 +132,7 @@ public final class GraphvizPrinter .put(NodeType.UNNEST, "crimson") .put(NodeType.SAMPLE, "goldenrod4") .put(NodeType.ANALYZE_FINISH, "plum") + .put(NodeType.GROUPJOIN, "green") .build()); static { @@ -477,6 +480,23 @@ public final class GraphvizPrinter return null; } + @Override + public Void visitJoinOnAggregation(JoinOnAggregationNode node, Void context) + { + List joinExpressions = new ArrayList<>(); + for (JoinNode.EquiJoinClause clause : node.getCriteria()) { + joinExpressions.add(JoinNodeUtils.toExpression(clause)); + } + + String criteria = Joiner.on(" AND ").join(joinExpressions); + printNode(node, "GROUP" + node.getType().getJoinLabel(), criteria, NODE_COLORS.get(NodeType.GROUPJOIN)); + + node.getLeft().accept(this, context); + node.getRight().accept(this, context); + + return null; + } + @Override public Void visitSemiJoin(SemiJoinNode node, Void context) { diff --git a/presto-spi/src/main/java/io/prestosql/spi/plan/JoinOnAggregationNode.java b/presto-spi/src/main/java/io/prestosql/spi/plan/JoinOnAggregationNode.java new file mode 100644 index 000000000..7550c71d4 --- /dev/null +++ b/presto-spi/src/main/java/io/prestosql/spi/plan/JoinOnAggregationNode.java @@ -0,0 +1,441 @@ +/* + * 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.prestosql.spi.plan; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Streams; +import io.prestosql.spi.relation.RowExpression; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkArgument; +import static io.prestosql.spi.plan.AggregationNode.Step.SINGLE; +import static io.prestosql.spi.plan.JoinNode.DistributionType.PARTITIONED; +import static io.prestosql.spi.plan.JoinNode.DistributionType.REPLICATED; +import static io.prestosql.spi.plan.JoinNode.Type.RIGHT; +import static java.util.Objects.requireNonNull; + +public class JoinOnAggregationNode + extends PlanNode +{ + private final JoinNode.Type type; + private final List criteria; + private final Optional filter; + private final Optional leftHashSymbol; + private final Optional rightHashSymbol; + private final Optional distributionType; + private final Optional spillable; + private final Map dynamicFilters; + + private final JoinInternalAggregation leftAggr; + private final JoinInternalAggregation rightAggr; + + private final JoinInternalAggregation aggrOnAggrLeft; + private final JoinInternalAggregation aggrOnAggrRight; + + private final List outputSymbols; + + @JsonCreator + public JoinOnAggregationNode( + @JsonProperty("id") PlanNodeId id, + @JsonProperty("type") JoinNode.Type type, + @JsonProperty("criteria") List criteria, + @JsonProperty("filter") Optional filter, + @JsonProperty("leftHashSymbol") Optional leftHashSymbol, + @JsonProperty("rightHashSymbol") Optional rightHashSymbol, + @JsonProperty("distributionType") Optional distributionType, + @JsonProperty("spillable") Optional spillable, + @JsonProperty("dynamicFilters") Map dynamicFilters, + + @JsonProperty("leftAggr") JoinInternalAggregation leftAggr, + @JsonProperty("rightAggr") JoinInternalAggregation rightAggr, + @JsonProperty("aggrOnAggrLeft") JoinInternalAggregation aggrOnAggrLeft, + @JsonProperty("aggrOnAggrRight") JoinInternalAggregation aggrOnAggrRight, + @JsonProperty("outputSymbols") List outputSymbols) + { + super(id); + requireNonNull(type, "type is null"); + requireNonNull(criteria, "criteria is null"); + requireNonNull(outputSymbols, "outputSymbols is null"); + requireNonNull(filter, "filter is null"); + requireNonNull(leftHashSymbol, "leftHashSymbol is null"); + requireNonNull(rightHashSymbol, "rightHashSymbol is null"); + requireNonNull(distributionType, "distributionType is null"); + requireNonNull(spillable, "spillable is null"); + + this.type = type; + this.criteria = ImmutableList.copyOf(criteria); + this.outputSymbols = ImmutableList.copyOf(outputSymbols); + this.filter = filter; + this.leftHashSymbol = leftHashSymbol; + this.rightHashSymbol = rightHashSymbol; + this.distributionType = distributionType; + this.spillable = spillable; + this.dynamicFilters = ImmutableMap.copyOf(requireNonNull(dynamicFilters, "dynamicFilters is null")); + + Set inputSymbols = ImmutableSet.builder() + .addAll(leftAggr.getSource().getOutputSymbols()) + .addAll(rightAggr.getSource().getOutputSymbols()) + .build(); + checkArgument(new HashSet<>(inputSymbols).containsAll(outputSymbols), "Left and right join inputs do not contain all output symbols"); + + checkArgument(!(criteria.isEmpty() && leftHashSymbol.isPresent()), "Left hash symbol is only valid in an equijoin"); + checkArgument(!(criteria.isEmpty() && rightHashSymbol.isPresent()), "Right hash symbol is only valid in an equijoin"); + + if (distributionType.isPresent()) { + // The implementation of full outer join only works if the data is hash partitioned. + checkArgument( + !(distributionType.get() == REPLICATED && (type == RIGHT || type == JoinNode.Type.FULL)), + "%s join do not work with %s distribution type", + type, + distributionType.get()); + // It does not make sense to PARTITION when there is nothing to partition on + checkArgument( + !(distributionType.get() == PARTITIONED && criteria.isEmpty() && type != RIGHT && type != JoinNode.Type.FULL), + "Equi criteria are empty, so %s join should not have %s distribution type", + type, + distributionType.get()); + } + + for (Symbol symbol : dynamicFilters.values()) { + checkArgument(rightAggr.getSource().getOutputSymbols().contains(symbol), "Right join input doesn't contain symbol for dynamic filter: %s", symbol); + } + + this.leftAggr = leftAggr; + this.rightAggr = rightAggr; + this.aggrOnAggrLeft = aggrOnAggrLeft; + this.aggrOnAggrRight = aggrOnAggrRight; + } + + @JsonProperty("type") + public JoinNode.Type getType() + { + return type; + } + + public PlanNode getLeft() + { + return leftAggr.getSource(); + } + + public PlanNode getRight() + { + return rightAggr.getSource(); + } + + @JsonProperty("criteria") + public List getCriteria() + { + return criteria; + } + + @JsonProperty("filter") + public Optional getFilter() + { + return filter; + } + + public Set getRightOutputSymbols() + { + return ImmutableSet.copyOf(rightAggr.getSource().getOutputSymbols()); + } + + @JsonProperty("leftHashSymbol") + public Optional getLeftHashSymbol() + { + return leftHashSymbol; + } + + @JsonProperty("rightHashSymbol") + public Optional getRightHashSymbol() + { + return rightHashSymbol; + } + + @Override + public List getSources() + { + return ImmutableList.of(leftAggr.getSource(), rightAggr.getSource()); + } + + @Override + @JsonProperty("outputSymbols") + public List getOutputSymbols() + { + return outputSymbols; + } + + @JsonProperty("distributionType") + public Optional getDistributionType() + { + return distributionType; + } + + @JsonProperty("spillable") + public Optional isSpillable() + { + return spillable; + } + + @JsonProperty + public Map getDynamicFilters() + { + return dynamicFilters; + } + + @JsonProperty("leftAggr") + public JoinInternalAggregation getLeftAggr() + { + return leftAggr; + } + + @JsonProperty("rightAggr") + public JoinInternalAggregation getRightAggr() + { + return rightAggr; + } + + @JsonProperty("aggrOnAggrLeft") + public JoinInternalAggregation getAggrOnLeft() + { + return aggrOnAggrLeft; + } + + @JsonProperty("aggrOnAggrRight") + public JoinInternalAggregation getAggrOnRight() + { + return aggrOnAggrRight; + } + + public List getAllSymbols() + { + return Streams.concat(leftAggr.getAllSymbols().stream(), rightAggr.getAllSymbols().stream(), + aggrOnAggrLeft.getAllSymbols().stream(), aggrOnAggrRight.getAllSymbols().stream()) + .distinct() + .collect(Collectors.toList()); + } + + @Override + public PlanNode replaceChildren(List newChildren) + { + throw new UnsupportedOperationException("replaceChildren is not supported"); + } + + @Override + public R accept(PlanVisitor visitor, C context) + { + return visitor.visitJoinOnAggregation(this, context); + } + + public static class JoinInternalAggregation + extends PlanNode + { + private final PlanNode source; + private final Map aggregations; + private final AggregationNode.GroupingSetDescriptor groupingSets; + private final List preGroupedSymbols; + private final AggregationNode.Step step; + private final Optional hashSymbol; + private final Optional groupIdSymbol; + private final List outputs; + private AggregationNode.AggregationType aggregationType; + private Optional finalizeSymbol; + + @JsonCreator + public JoinInternalAggregation( + @JsonProperty("id") PlanNodeId id, + @JsonProperty("source") PlanNode source, + @JsonProperty("aggregations") Map aggregations, + @JsonProperty("groupingSets") AggregationNode.GroupingSetDescriptor groupingSets, + @JsonProperty("preGroupedSymbols") List preGroupedSymbols, + @JsonProperty("step") AggregationNode.Step step, + @JsonProperty("hashSymbol") Optional hashSymbol, + @JsonProperty("groupIdSymbol") Optional groupIdSymbol, + @JsonProperty("aggregationType") AggregationNode.AggregationType aggregationType, + @JsonProperty("finalizeSymbol") Optional finalizeSymbol) + { + super(id); + this.source = source; + this.aggregations = ImmutableMap.copyOf(requireNonNull(aggregations, "aggregations is null")); + + requireNonNull(groupingSets, "groupingSets is null"); + groupIdSymbol.ifPresent(symbol -> checkArgument(groupingSets.getGroupingKeys().contains(symbol), "Grouping columns does not contain groupId column")); + this.groupingSets = groupingSets; + + this.groupIdSymbol = requireNonNull(groupIdSymbol); + + boolean noOrderBy = aggregations.values().stream() + .map(AggregationNode.Aggregation::getOrderingScheme) + .noneMatch(Optional::isPresent); + checkArgument(noOrderBy || step == SINGLE, "ORDER BY does not support distributed aggregation"); + + this.step = step; + this.hashSymbol = hashSymbol; + + requireNonNull(preGroupedSymbols, "preGroupedSymbols is null"); + checkArgument(preGroupedSymbols.isEmpty() || groupingSets.getGroupingKeys().containsAll(preGroupedSymbols), "Pre-grouped symbols must be a subset of the grouping keys"); + this.preGroupedSymbols = ImmutableList.copyOf(preGroupedSymbols); + + ImmutableList.Builder outputs1 = ImmutableList.builder(); + outputs1.addAll(groupingSets.getGroupingKeys()); + hashSymbol.ifPresent(outputs1::add); + outputs1.addAll(aggregations.keySet()); + + AggregationNode.AggregationType tmpAggregationType = aggregationType; + if (tmpAggregationType == AggregationNode.AggregationType.SORT_BASED) { + if (step.equals(AggregationNode.Step.PARTIAL)) { + if (finalizeSymbol.isPresent()) { + List symbolList = new ArrayList<>(Arrays.asList(finalizeSymbol.get())); + outputs1.addAll(symbolList); + } + } + else if (step.equals(AggregationNode.Step.FINAL) && !finalizeSymbol.isPresent()) { + tmpAggregationType = AggregationNode.AggregationType.HASH; + } + } + + this.outputs = outputs1.build(); + this.aggregationType = tmpAggregationType; + this.finalizeSymbol = finalizeSymbol; + } + + @JsonProperty("source") + public PlanNode getSource() + { + return source; + } + + @Override + public List getSources() + { + return ImmutableList.of(source); + } + + public List getOutputSymbols() + { + return outputs; + } + + @Override + public PlanNode replaceChildren(List newChildren) + { + return null; + } + + @JsonProperty("step") + public AggregationNode.Step getStep() + { + return step; + } + + @JsonProperty("hashSymbol") + public Optional getHashSymbol() + { + return hashSymbol; + } + + @JsonProperty("groupIdSymbol") + public Optional getGroupIdSymbol() + { + return groupIdSymbol; + } + + @JsonProperty("aggregations") + public Map getAggregations() + { + return aggregations; + } + + @JsonProperty("preGroupedSymbols") + public List getPreGroupedSymbols() + { + return preGroupedSymbols; + } + + @JsonProperty("groupingSets") + public AggregationNode.GroupingSetDescriptor getGroupingSets() + { + return groupingSets; + } + + public List getGroupingKeys() + { + return groupingSets.getGroupingKeys(); + } + + /** + * @return whether this node should produce default output in case of no input pages. + * For example for query: + *

+ * SELECT count(*) FROM nation WHERE nationkey < 0 + *

+ * A default output of "0" is expected to be produced by FINAL aggregation operator. + */ + public boolean hasDefaultOutput() + { + return hasEmptyGroupingSet() && (step.isOutputPartial() || step.equals(SINGLE)); + } + + public boolean hasEmptyGroupingSet() + { + return !groupingSets.getGlobalGroupingSets().isEmpty(); + } + + public boolean hasNonEmptyGroupingSet() + { + return groupingSets.getGroupingSetCount() > groupingSets.getGlobalGroupingSets().size(); + } + + public int getGroupingSetCount() + { + return groupingSets.getGroupingSetCount(); + } + + public Set getGlobalGroupingSets() + { + return groupingSets.getGlobalGroupingSets(); + } + + public boolean hasOrderings() + { + return aggregations.values().stream() + .map(AggregationNode.Aggregation::getOrderingScheme) + .anyMatch(Optional::isPresent); + } + + @JsonProperty("aggregationType") + public AggregationNode.AggregationType getAggregationType() + { + return aggregationType; + } + + @JsonProperty("finalizeSymbol") + public Optional getFinalizeSymbol() + { + return finalizeSymbol; + } + } +} diff --git a/presto-spi/src/main/java/io/prestosql/spi/plan/PlanVisitor.java b/presto-spi/src/main/java/io/prestosql/spi/plan/PlanVisitor.java index 08965aa62..9e3adea2c 100644 --- a/presto-spi/src/main/java/io/prestosql/spi/plan/PlanVisitor.java +++ b/presto-spi/src/main/java/io/prestosql/spi/plan/PlanVisitor.java @@ -47,6 +47,11 @@ public abstract class PlanVisitor return visitPlan(node, context); } + public R visitJoinOnAggregation(JoinOnAggregationNode node, C context) + { + return visitPlan(node, context); + } + public R visitLimit(LimitNode node, C context) { return visitPlan(node, context); -- Gitee From 683bf45947a7ce5d0dd865bc04f4b626c43e4550 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Mon, 20 Feb 2023 19:13:32 +0530 Subject: [PATCH 04/36] Support for GroupJoin specific interfaces in Hash Classes. --- .../io/prestosql/operator/BigintPagesHash.java | 16 ++++++++++++++++ .../io/prestosql/operator/DefaultPagesHash.java | 16 ++++++++++++++++ .../java/io/prestosql/operator/IPagesHash.java | 15 +++++++++++++++ .../java/io/prestosql/operator/JoinHash.java | 13 +++++++++++++ .../io/prestosql/operator/LookupSource.java | 11 +++++++++++ .../prestosql/operator/PagesHashStrategy.java | 11 +++++++++++ .../operator/PartitionedLookupSource.java | 15 +++++++++++++++ .../operator/SimplePagesHashStrategy.java | 17 +++++++++++++++++ .../aggregation/builder/AggregationBuilder.java | 10 ++++++++++ 9 files changed, 124 insertions(+) diff --git a/presto-main/src/main/java/io/prestosql/operator/BigintPagesHash.java b/presto-main/src/main/java/io/prestosql/operator/BigintPagesHash.java index 70a04983a..77751bee6 100644 --- a/presto-main/src/main/java/io/prestosql/operator/BigintPagesHash.java +++ b/presto-main/src/main/java/io/prestosql/operator/BigintPagesHash.java @@ -15,6 +15,7 @@ package io.prestosql.operator; import com.google.common.collect.ImmutableList; import io.airlift.units.DataSize; +import io.prestosql.operator.aggregation.builder.AggregationBuilder; import io.prestosql.spi.Page; import io.prestosql.spi.PageBuilder; import io.prestosql.spi.block.Block; @@ -258,6 +259,21 @@ public final class BigintPagesHash return result; } + @Override + public long getCountForJoinPosition(long position, int channel) + { + long pageAddress = addresses.getLong(toIntExact(position)); + int blockIndex = decodeSliceIndex(pageAddress); + int blockPosition = decodePosition(pageAddress); + return pagesHashStrategy.getCountForJoinPosition(blockIndex, blockPosition, channel); + } + + @Override + public AggregationBuilder getAggregationBuilder() + { + return pagesHashStrategy.getAggregationBuilder(); + } + @Override public void appendTo(long position, PageBuilder pageBuilder, int outputChannelOffset) { diff --git a/presto-main/src/main/java/io/prestosql/operator/DefaultPagesHash.java b/presto-main/src/main/java/io/prestosql/operator/DefaultPagesHash.java index cc97d06ce..ec34b2df9 100644 --- a/presto-main/src/main/java/io/prestosql/operator/DefaultPagesHash.java +++ b/presto-main/src/main/java/io/prestosql/operator/DefaultPagesHash.java @@ -14,6 +14,7 @@ package io.prestosql.operator; import io.airlift.units.DataSize; +import io.prestosql.operator.aggregation.builder.AggregationBuilder; import io.prestosql.spi.Page; import io.prestosql.spi.PageBuilder; import it.unimi.dsi.fastutil.HashCommon; @@ -170,6 +171,21 @@ public final class DefaultPagesHash return -1; } + @Override + public long getCountForJoinPosition(long position, int channel) + { + long pageAddress = addresses.getLong(toIntExact(position)); + int blockIndex = decodeSliceIndex(pageAddress); + int blockPosition = decodePosition(pageAddress); + return pagesHashStrategy.getCountForJoinPosition(blockIndex, blockPosition, channel); + } + + @Override + public AggregationBuilder getAggregationBuilder() + { + return pagesHashStrategy.getAggregationBuilder(); + } + public void appendTo(long position, PageBuilder pageBuilder, int outputChannelOffset) { long pageAddress = addresses.getLong(toIntExact(position)); diff --git a/presto-main/src/main/java/io/prestosql/operator/IPagesHash.java b/presto-main/src/main/java/io/prestosql/operator/IPagesHash.java index 5b5306829..525a3b5df 100644 --- a/presto-main/src/main/java/io/prestosql/operator/IPagesHash.java +++ b/presto-main/src/main/java/io/prestosql/operator/IPagesHash.java @@ -13,9 +13,14 @@ */ package io.prestosql.operator; +import io.prestosql.operator.aggregation.builder.AggregationBuilder; import io.prestosql.spi.Page; import io.prestosql.spi.PageBuilder; +import static io.prestosql.operator.SyntheticAddress.decodePosition; +import static io.prestosql.operator.SyntheticAddress.decodeSliceIndex; +import static java.lang.Math.toIntExact; + public interface IPagesHash { int getChannelCount(); @@ -50,6 +55,16 @@ public interface IPagesHash return result; } + default long getCountForJoinPosition(long position, int channel) + { + throw new UnsupportedOperationException("Only supported for Group Join usage"); + } + + default AggregationBuilder getAggregationBuilder() + { + throw new UnsupportedOperationException("Only supported for Group Join usage"); + } + void appendTo(long position, PageBuilder pageBuilder, int outputChannelOffset); default int getHashPosition(long raw, long mask) diff --git a/presto-main/src/main/java/io/prestosql/operator/JoinHash.java b/presto-main/src/main/java/io/prestosql/operator/JoinHash.java index 8c96cb672..ada206b20 100644 --- a/presto-main/src/main/java/io/prestosql/operator/JoinHash.java +++ b/presto-main/src/main/java/io/prestosql/operator/JoinHash.java @@ -13,6 +13,7 @@ */ package io.prestosql.operator; +import io.prestosql.operator.aggregation.builder.AggregationBuilder; import io.prestosql.spi.Page; import io.prestosql.spi.PageBuilder; import org.openjdk.jol.info.ClassLayout; @@ -119,6 +120,18 @@ public final class JoinHash return filterFunction == null || filterFunction.filter(toIntExact(currentJoinPosition), probePosition, allProbeChannelsPage); } + @Override + public long getCountForJoinPosition(long position, int channel) + { + return pagesHash.getCountForJoinPosition(position, channel); + } + + @Override + public AggregationBuilder getAggregationBuilder() + { + return pagesHash.getAggregationBuilder(); + } + @Override public void appendTo(long position, PageBuilder pageBuilder, int outputChannelOffset) { diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupSource.java b/presto-main/src/main/java/io/prestosql/operator/LookupSource.java index 24399e242..f974639e5 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupSource.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupSource.java @@ -13,6 +13,7 @@ */ package io.prestosql.operator; +import io.prestosql.operator.aggregation.builder.AggregationBuilder; import io.prestosql.spi.Page; import io.prestosql.spi.PageBuilder; @@ -38,6 +39,16 @@ public interface LookupSource long getNextJoinPosition(long currentJoinPosition, int probePosition, Page allProbeChannelsPage); + default long getCountForJoinPosition(long position, int channel) + { + throw new UnsupportedOperationException("Only supported for Group Join usage"); + } + + default AggregationBuilder getAggregationBuilder() + { + throw new UnsupportedOperationException("Only supported for Group Join usage"); + } + void appendTo(long position, PageBuilder pageBuilder, int outputChannelOffset); boolean isJoinPositionEligible(long currentJoinPosition, int probePosition, Page allProbeChannelsPage); diff --git a/presto-main/src/main/java/io/prestosql/operator/PagesHashStrategy.java b/presto-main/src/main/java/io/prestosql/operator/PagesHashStrategy.java index 0092e5630..f9dfa4f5f 100644 --- a/presto-main/src/main/java/io/prestosql/operator/PagesHashStrategy.java +++ b/presto-main/src/main/java/io/prestosql/operator/PagesHashStrategy.java @@ -13,6 +13,7 @@ */ package io.prestosql.operator; +import io.prestosql.operator.aggregation.builder.AggregationBuilder; import io.prestosql.spi.Page; import io.prestosql.spi.PageBuilder; @@ -110,4 +111,14 @@ public interface PagesHashStrategy * Checks if sort channel is null at the specified position */ boolean isSortChannelPositionNull(int blockIndex, int blockPosition); + + /** + * Gets the aggregation builder for build side. Used in GroupJoin + */ + AggregationBuilder getAggregationBuilder(); + + /** + * Gets the count (occurrence) of record from build side of the record. + */ + long getCountForJoinPosition(int blockIndex, int blockPosition, int channel); } diff --git a/presto-main/src/main/java/io/prestosql/operator/PartitionedLookupSource.java b/presto-main/src/main/java/io/prestosql/operator/PartitionedLookupSource.java index 1ce4e1eed..cac185fd8 100644 --- a/presto-main/src/main/java/io/prestosql/operator/PartitionedLookupSource.java +++ b/presto-main/src/main/java/io/prestosql/operator/PartitionedLookupSource.java @@ -17,6 +17,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.io.Closer; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; +import io.prestosql.operator.aggregation.builder.AggregationBuilder; import io.prestosql.operator.exchange.LocalPartitionGenerator; import io.prestosql.spi.Page; import io.prestosql.spi.PageBuilder; @@ -264,6 +265,20 @@ public class PartitionedLookupSource return lookupSource.isJoinPositionEligible(joinPosition, probePosition, allProbeChannelsPage); } + @Override + public long getCountForJoinPosition(long partitionedJoinPosition, int channel) + { + int partition = decodePartition(partitionedJoinPosition); + long joinPosition = decodeJoinPosition(partitionedJoinPosition); + return lookupSources[partition].getCountForJoinPosition(joinPosition, channel); + } + + @Override + public AggregationBuilder getAggregationBuilder() + { + return lookupSources[0].getAggregationBuilder(); + } + @Override public void appendTo(long partitionedJoinPosition, PageBuilder pageBuilder, int outputChannelOffset) { diff --git a/presto-main/src/main/java/io/prestosql/operator/SimplePagesHashStrategy.java b/presto-main/src/main/java/io/prestosql/operator/SimplePagesHashStrategy.java index a88e0f27f..62bcc3fb2 100644 --- a/presto-main/src/main/java/io/prestosql/operator/SimplePagesHashStrategy.java +++ b/presto-main/src/main/java/io/prestosql/operator/SimplePagesHashStrategy.java @@ -16,6 +16,7 @@ package io.prestosql.operator; import com.google.common.collect.ImmutableList; import io.prestosql.metadata.FunctionAndTypeManager; import io.prestosql.metadata.Metadata; +import io.prestosql.operator.aggregation.builder.AggregationBuilder; import io.prestosql.spi.Page; import io.prestosql.spi.PageBuilder; import io.prestosql.spi.block.Block; @@ -46,6 +47,7 @@ public class SimplePagesHashStrategy private final List precomputedHashChannel; private final Optional sortChannel; private final List distinctFromMethodHandles; + private final AggregationBuilder aggregationBuilder; public SimplePagesHashStrategy( List types, @@ -54,6 +56,7 @@ public class SimplePagesHashStrategy List hashChannels, OptionalInt precomputedHashChannel, Optional sortChannel, + Optional aggregationBuilder, Metadata metadata) { this.types = ImmutableList.copyOf(requireNonNull(types, "types is null")); @@ -69,6 +72,8 @@ public class SimplePagesHashStrategy this.precomputedHashChannel = null; } this.sortChannel = requireNonNull(sortChannel, "sortChannel is null"); + requireNonNull(aggregationBuilder, "aggregationBuilder is null"); + this.aggregationBuilder = aggregationBuilder.orElse(null); requireNonNull(metadata, "metadata is null"); FunctionAndTypeManager functionAndTypeManager = metadata.getFunctionAndTypeManager(); requireNonNull(metadata, "functionManager is null"); @@ -95,6 +100,18 @@ public class SimplePagesHashStrategy .sum(); } + @Override + public long getCountForJoinPosition(int blockIndex, int blockPosition, int channel) + { + return BIGINT.getLong(channels.get(channel).get(blockIndex), blockPosition); + } + + @Override + public AggregationBuilder getAggregationBuilder() + { + return aggregationBuilder; + } + @Override public void appendTo(int blockIndex, int position, PageBuilder pageBuilder, int outputChannelOffset) { diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/AggregationBuilder.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/AggregationBuilder.java index ccce66905..3777dfe2d 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/AggregationBuilder.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/AggregationBuilder.java @@ -45,4 +45,14 @@ public interface AggregationBuilder ListenableFuture startMemoryRevoke(); void finishMemoryRevoke(); + + default AggregationBuilder duplicate() + { + throw new UnsupportedOperationException("Only supported for Group Join flow"); + } + + default int getAggregationCount() + { + throw new UnsupportedOperationException("Only supported for Group Join flow"); + } } -- Gitee From 08f6f0c1b8c686ceba1962502ab871d04eec7ff4 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Tue, 21 Feb 2023 11:12:57 +0530 Subject: [PATCH 05/36] Group Join issue fixes. --- .../main/java/io/prestosql/array/BlockBigArray.java | 2 +- .../java/io/prestosql/operator/PagesHashStrategy.java | 10 ++++++++-- .../builder/InMemoryAggregationBuilder.java | 3 ++- .../io/prestosql/sql/planner/PlanSymbolAllocator.java | 4 ++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/presto-array/src/main/java/io/prestosql/array/BlockBigArray.java b/presto-array/src/main/java/io/prestosql/array/BlockBigArray.java index d864682da..3858068a6 100644 --- a/presto-array/src/main/java/io/prestosql/array/BlockBigArray.java +++ b/presto-array/src/main/java/io/prestosql/array/BlockBigArray.java @@ -101,7 +101,7 @@ public final class BlockBigArray * * @param index a position in this big array. */ - public void reset(long index, Block value) + public void reset(long index) { Block currentValue = array.get(index); if (currentValue != null) { diff --git a/presto-main/src/main/java/io/prestosql/operator/PagesHashStrategy.java b/presto-main/src/main/java/io/prestosql/operator/PagesHashStrategy.java index f9dfa4f5f..43c802787 100644 --- a/presto-main/src/main/java/io/prestosql/operator/PagesHashStrategy.java +++ b/presto-main/src/main/java/io/prestosql/operator/PagesHashStrategy.java @@ -115,10 +115,16 @@ public interface PagesHashStrategy /** * Gets the aggregation builder for build side. Used in GroupJoin */ - AggregationBuilder getAggregationBuilder(); + default AggregationBuilder getAggregationBuilder() + { + throw new UnsupportedOperationException("Only supported for Group Join"); + } /** * Gets the count (occurrence) of record from build side of the record. */ - long getCountForJoinPosition(int blockIndex, int blockPosition, int channel); + default long getCountForJoinPosition(int blockIndex, int blockPosition, int channel) + { + throw new UnsupportedOperationException("Only supported for Group Join"); + } } diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryAggregationBuilder.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryAggregationBuilder.java index e4e39bb99..eb7da4b5c 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryAggregationBuilder.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryAggregationBuilder.java @@ -55,11 +55,12 @@ import static java.util.Objects.requireNonNull; public abstract class InMemoryAggregationBuilder implements AggregationBuilder, Restorable { - protected final GroupBy groupBy; protected final List aggregators; protected final boolean partial; protected final OptionalLong maxPartialMemory; protected final UpdateMemory updateMemory; + + protected GroupBy groupBy; protected boolean full; public InMemoryAggregationBuilder( diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/PlanSymbolAllocator.java b/presto-main/src/main/java/io/prestosql/sql/planner/PlanSymbolAllocator.java index d212ea5ec..426e8a45d 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/PlanSymbolAllocator.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/PlanSymbolAllocator.java @@ -167,8 +167,8 @@ public class PlanSymbolAllocator return nextId++; } - public void updateSymbolType(Symbol symbol, Type type) + public void updateSymbolType(Symbol symbol, Type newReturnType) { - symbols.put(symbol, type); + symbols.put(symbol, newReturnType); } } -- Gitee From 2bea9d348ed119128581e48b99548412d085242a Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Tue, 21 Feb 2023 12:23:11 +0530 Subject: [PATCH 06/36] Group Join compiler changes. --- .../io/prestosql/operator/PagesIndex.java | 19 ++- ...MemoryHashAggregationBuilderWithReset.java | 109 +++++++++++++++++ .../io/prestosql/sql/gen/JoinCompiler.java | 114 +++++++++++++++--- 3 files changed, 227 insertions(+), 15 deletions(-) create mode 100644 presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryHashAggregationBuilderWithReset.java diff --git a/presto-main/src/main/java/io/prestosql/operator/PagesIndex.java b/presto-main/src/main/java/io/prestosql/operator/PagesIndex.java index a7cedd971..17822fa69 100644 --- a/presto-main/src/main/java/io/prestosql/operator/PagesIndex.java +++ b/presto-main/src/main/java/io/prestosql/operator/PagesIndex.java @@ -25,6 +25,7 @@ import io.prestosql.Session; import io.prestosql.geospatial.Rectangle; import io.prestosql.metadata.Metadata; import io.prestosql.operator.SpatialIndexBuilderOperator.SpatialPredicate; +import io.prestosql.operator.aggregation.builder.AggregationBuilder; import io.prestosql.spi.Page; import io.prestosql.spi.PageBuilder; import io.prestosql.spi.block.Block; @@ -445,6 +446,7 @@ public class PagesIndex joinChannels, hashChannel, Optional.empty(), + Optional.empty(), metadata); } @@ -482,6 +484,20 @@ public class PagesIndex Optional sortChannel, List searchFunctionFactories, Optional> outputChannels) + { + return createLookupSourceSupplier(session, joinChannels, hashChannel, filterFunctionFactory, sortChannel, searchFunctionFactories, outputChannels, Optional.empty(), Optional.empty()); + } + + public LookupSourceSupplier createLookupSourceSupplier( + Session session, + List joinChannels, + OptionalInt hashChannel, + Optional filterFunctionFactory, + Optional sortChannel, + List searchFunctionFactories, + Optional> outputChannels, + Optional countChannel, + Optional aggregationBuilder) { List> channelLists = ImmutableList.copyOf(this.channels); if (!joinChannels.isEmpty()) { @@ -490,7 +506,7 @@ public class PagesIndex // OUTER joins into NestedLoopsJoin and remove "type == INNER" condition in LocalExecutionPlanner.visitJoin() try { - LookupSourceSupplierFactory lookupSourceFactory = joinCompiler.compileLookupSourceFactory(types, joinChannels, sortChannel, outputChannels); + LookupSourceSupplierFactory lookupSourceFactory = joinCompiler.compileLookupSourceFactory(types, joinChannels, sortChannel, outputChannels, countChannel, aggregationBuilder); return lookupSourceFactory.createLookupSourceSupplier( session, valueAddresses, @@ -513,6 +529,7 @@ public class PagesIndex joinChannels, hashChannel, sortChannel, + aggregationBuilder, metadata); return new JoinHashSupplier( diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryHashAggregationBuilderWithReset.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryHashAggregationBuilderWithReset.java new file mode 100644 index 000000000..9fa0a735b --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryHashAggregationBuilderWithReset.java @@ -0,0 +1,109 @@ +package io.prestosql.operator.aggregation.builder; + +import com.google.common.primitives.Ints; +import io.airlift.units.DataSize; +import io.prestosql.operator.OperatorContext; +import io.prestosql.operator.UpdateMemory; +import io.prestosql.operator.aggregation.AccumulatorFactory; +import io.prestosql.spi.plan.AggregationNode; +import io.prestosql.spi.type.Type; +import io.prestosql.sql.gen.JoinCompiler; +import it.unimi.dsi.fastutil.ints.IntIterator; + +import java.util.List; +import java.util.Optional; + +import static io.prestosql.SystemSessionProperties.isDictionaryAggregationEnabled; +import static io.prestosql.operator.GroupByHash.createGroupByHash; + +public class InMemoryHashAggregationBuilderWithReset + extends InMemoryHashAggregationBuilder +{ + private final List accumulatorFactoriesm; + private final AggregationNode.Step step; + private final int expectedGroups; + private final List groupByTypes; + private final List groupByChannels; + private final Optional hashChannel; + private final OperatorContext operatorContext; + private final Optional maxPartialMemory; + private final JoinCompiler joinCompiler; + + public InMemoryHashAggregationBuilderWithReset( + List accumulatorFactories, + AggregationNode.Step step, + int expectedGroups, + List groupByTypes, + List groupByChannels, + Optional hashChannel, + OperatorContext operatorContext, + Optional maxPartialMemory, + JoinCompiler joinCompiler, + UpdateMemory updateMemory) + { + super(accumulatorFactories, + AggregationNode.Step.partialInput(step), + expectedGroups, + groupByTypes, + groupByChannels, + hashChannel, + isDictionaryAggregationEnabled(operatorContext.getSession()), + maxPartialMemory, + Optional.empty(), + joinCompiler, + updateMemory, + AggregationNode.AggregationType.HASH); + this.accumulatorFactoriesm = accumulatorFactories; + this.step = AggregationNode.Step.partialInput(step); + this.expectedGroups = expectedGroups; + this.groupByTypes = groupByTypes; + this.groupByChannels = groupByChannels; + this.hashChannel = hashChannel; + this.operatorContext = operatorContext; + this.maxPartialMemory = maxPartialMemory; + this.joinCompiler = joinCompiler; + } + + @Override + protected void resetGroupBy() + { + IntIterator intIterator = consecutiveGroupIds(); + while (intIterator.hasNext()) { + int groupId = intIterator.nextInt(); + for (Aggregator aggregator : aggregators) { + aggregator.reset(groupId); + } + } + + this.groupBy = createGroupByHash( + groupByTypes, + Ints.toArray(groupByChannels), + hashChannel, + expectedGroups, + isDictionaryAggregationEnabled(operatorContext.getSession()), + joinCompiler, + updateMemory); + } + + @Override + public int getAggregationCount() + { + return accumulatorFactoriesm.size(); + } + + @Override + public AggregationBuilder duplicate() + { + return new InMemoryHashAggregationBuilderWithReset( + accumulatorFactoriesm, + step, + expectedGroups, + groupByTypes, + groupByChannels, + hashChannel, + operatorContext, + maxPartialMemory, + joinCompiler, + updateMemory); + } +} diff --git a/presto-main/src/main/java/io/prestosql/sql/gen/JoinCompiler.java b/presto-main/src/main/java/io/prestosql/sql/gen/JoinCompiler.java index 5b952caf9..1ff09abb5 100644 --- a/presto-main/src/main/java/io/prestosql/sql/gen/JoinCompiler.java +++ b/presto-main/src/main/java/io/prestosql/sql/gen/JoinCompiler.java @@ -42,6 +42,7 @@ import io.prestosql.operator.JoinHash; import io.prestosql.operator.JoinHashSupplier; import io.prestosql.operator.LookupSourceSupplier; import io.prestosql.operator.PagesHashStrategy; +import io.prestosql.operator.aggregation.builder.AggregationBuilder; import io.prestosql.spi.Page; import io.prestosql.spi.PageBuilder; import io.prestosql.spi.block.Block; @@ -100,18 +101,18 @@ public class JoinCompiler .recordStats() .maximumSize(1000) .build(CacheLoader.from(key -> - internalCompileLookupSourceFactory(key.getTypes(), key.getOutputChannels(), key.getJoinChannels(), key.getSortChannel()))); + internalCompileLookupSourceFactory(key.getTypes(), key.getOutputChannels(), key.getJoinChannels(), key.getSortChannel(), key.getCountChannel(), key.getAggregationBuilder()))); private final LoadingCache> hashStrategies = CacheBuilder.newBuilder() .recordStats() .maximumSize(1000) .build(CacheLoader.from(key -> - internalCompileHashStrategy(key.getTypes(), key.getOutputChannels(), key.getJoinChannels(), key.getSortChannel()))); + internalCompileHashStrategy(key.getTypes(), key.getOutputChannels(), key.getJoinChannels(), key.getSortChannel(), key.getCountChannel(), key.getAggregationBuilder()))); private final boolean enableSingleChannelBigintLookupSource; public LookupSourceSupplierFactory compileLookupSourceFactory(List types, List joinChannels, Optional sortChannel) { - return compileLookupSourceFactory(types, joinChannels, sortChannel, Optional.empty()); + return compileLookupSourceFactory(types, joinChannels, sortChannel, Optional.empty(), Optional.empty(), Optional.empty()); } @Inject @@ -141,13 +142,16 @@ public class JoinCompiler return new CacheStatsMBean(hashStrategies); } - public LookupSourceSupplierFactory compileLookupSourceFactory(List types, List joinChannels, Optional sortChannel, Optional> outputChannels) + public LookupSourceSupplierFactory compileLookupSourceFactory(List types, List joinChannels, Optional sortChannel, Optional> outputChannels, + Optional countChannel, Optional aggregationBuilder) { return lookupSourceFactories.getUnchecked(new CacheKey( types, outputChannels.orElse(rangeList(types.size())), joinChannels, - sortChannel)); + sortChannel, + countChannel, + aggregationBuilder)); } public PagesHashStrategyFactory compilePagesHashStrategyFactory(List types, List joinChannels) @@ -165,6 +169,8 @@ public class JoinCompiler types, outputChannels.orElse(rangeList(types.size())), joinChannels, + Optional.empty(), + Optional.empty(), Optional.empty()))); } @@ -175,9 +181,10 @@ public class JoinCompiler .collect(toImmutableList()); } - private LookupSourceSupplierFactory internalCompileLookupSourceFactory(List types, List outputChannels, List joinChannels, Optional sortChannel) + private LookupSourceSupplierFactory internalCompileLookupSourceFactory(List types, List outputChannels, List joinChannels, Optional sortChannel, + Optional countChannel, Optional aggregationBuilder) { - Class pagesHashStrategyClass = internalCompileHashStrategy(types, outputChannels, joinChannels, sortChannel); + Class pagesHashStrategyClass = internalCompileHashStrategy(types, outputChannels, joinChannels, sortChannel, countChannel, aggregationBuilder); OptionalInt singleBigintJoinChannel = OptionalInt.empty(); if (enableSingleChannelBigintLookupSource @@ -213,7 +220,8 @@ public class JoinCompiler return instanceSize; } - private Class internalCompileHashStrategy(List types, List outputChannels, List joinChannels, Optional sortChannel) + private Class internalCompileHashStrategy(List types, List outputChannels, List joinChannels, Optional sortChannel, + Optional countChannel, Optional aggregationBuilder) { CallSiteBinder callSiteBinder = new CallSiteBinder(); @@ -238,10 +246,13 @@ public class JoinCompiler joinChannelFields.add(channelField); } FieldDefinition hashChannelField = classDefinition.declareField(a(PRIVATE, FINAL), "hashChannel", type(List.class, Block.class)); + FieldDefinition aggregationBuilderField = classDefinition.declareField(a(PRIVATE, FINAL), "aggregationBuilder", type(AggregationBuilder.class)); - generateConstructor(classDefinition, joinChannels, sizeField, instanceSizeField, channelFields, joinChannelFields, hashChannelField); + generateConstructor(classDefinition, joinChannels, sizeField, instanceSizeField, channelFields, joinChannelFields, hashChannelField, aggregationBuilderField); generateGetChannelCountMethod(classDefinition, outputChannels.size()); + generateGetterAggregationBuilder(classDefinition, aggregationBuilderField); generateGetSizeInBytesMethod(classDefinition, sizeField); + generateGetCountForJoinPosition(classDefinition, callSiteBinder, channelFields, countChannel); generateAppendToMethod(classDefinition, callSiteBinder, types, outputChannels, channelFields); generateHashPositionMethod(classDefinition, callSiteBinder, joinChannelTypes, joinChannelFields, hashChannelField); generateHashRowMethod(classDefinition, callSiteBinder, joinChannelTypes); @@ -259,17 +270,53 @@ public class JoinCompiler return defineClass(classDefinition, PagesHashStrategy.class, callSiteBinder.getBindings(), getClass().getClassLoader()); } + private void generateGetCountForJoinPosition(ClassDefinition classDefinition, CallSiteBinder callSiteBinder, List channelFields, Optional countChannel) + { + Parameter blockIndex = arg("blockIndex", int.class); + Parameter blockPosition = arg("blockPosition", int.class); + Parameter channel = arg("channel", int.class); + MethodDefinition getCountForJoinPosition = classDefinition.declareMethod( + a(PUBLIC), + "getCountForJoinPosition", + type(long.class), + blockIndex, + blockPosition, + channel); + + if (!countChannel.isPresent()) { + getCountForJoinPosition.getBody() + .append(newInstance(UnsupportedOperationException.class)) + .throwObject(); + return; + } + Variable thisVariable = getCountForJoinPosition.getThis(); + BytecodeBlock body = getCountForJoinPosition.getBody(); + BytecodeExpression block = thisVariable + .getField(channelFields.get(countChannel.get())) + .invoke("get", Object.class, blockIndex) + .cast(Block.class); + BytecodeExpression bigintType = constantType(callSiteBinder, BIGINT); + BytecodeExpression getLong = bigintType.invoke( + "getLong", + long.class, + block, + blockPosition) + .ret(); + body.append(getLong); + } + private static void generateConstructor(ClassDefinition classDefinition, List joinChannels, FieldDefinition sizeField, FieldDefinition instanceSizeField, List channelFields, List joinChannelFields, - FieldDefinition hashChannelField) + FieldDefinition hashChannelField, FieldDefinition aggregationBuilderField) { Parameter channels = arg("channels", type(List.class, type(List.class, Block.class))); Parameter hashChannel = arg("hashChannel", type(OptionalInt.class)); - MethodDefinition constructorDefinition = classDefinition.declareConstructor(a(PUBLIC), channels, hashChannel); + Parameter aggregationBuilder = arg("aggregationBuilder", type(Optional.class, AggregationBuilder.class)); + MethodDefinition constructorDefinition = classDefinition.declareConstructor(a(PUBLIC), channels, hashChannel, aggregationBuilder); Variable thisVariable = constructorDefinition.getThis(); Variable blockIndex = constructorDefinition.getScope().declareVariable(int.class, "blockIndex"); @@ -331,6 +378,16 @@ public class JoinCompiler .ifFalse(thisVariable.setField( hashChannelField, constantNull(hashChannelField.getType())))); + + constructor.comment("Set aggregationBuilder"); + constructor.append(new IfStatement() + .condition(aggregationBuilder.invoke("isPresent", boolean.class)) + .ifTrue(thisVariable.setField( + aggregationBuilderField, + aggregationBuilder.invoke("get", Object.class).cast(AggregationBuilder.class))) + .ifFalse(thisVariable.setField( + aggregationBuilderField, + constantNull(aggregationBuilderField.getType())))); constructor.ret(); } @@ -345,6 +402,19 @@ public class JoinCompiler .retInt(); } + private static void generateGetterAggregationBuilder(ClassDefinition classDefinition, FieldDefinition aggregationBuilder) + { + MethodDefinition getAggregationBuilder = classDefinition.declareMethod( + a(PUBLIC), + "getAggregationBuilder", + type(AggregationBuilder.class)); + Variable thisVariable = getAggregationBuilder.getThis(); + getAggregationBuilder + .getBody() + .append(thisVariable.getField(aggregationBuilder)) + .ret(AggregationBuilder.class); + } + private static void generateGetSizeInBytesMethod(ClassDefinition classDefinition, FieldDefinition sizeField) { MethodDefinition getSizeInBytesMethod = classDefinition.declareMethod(a(PUBLIC), "getSizeInBytes", type(long.class)); @@ -971,13 +1041,17 @@ public class JoinCompiler private final List outputChannels; private final List joinChannels; private final Optional sortChannel; + private final Optional countChannel; + private final Optional aggregationBuilder; - private CacheKey(List types, List outputChannels, List joinChannels, Optional sortChannel) + private CacheKey(List types, List outputChannels, List joinChannels, Optional sortChannel, Optional countChannel, Optional aggregationBuilder) { this.types = ImmutableList.copyOf(requireNonNull(types, "types is null")); this.outputChannels = ImmutableList.copyOf(requireNonNull(outputChannels, "outputChannels is null")); this.joinChannels = ImmutableList.copyOf(requireNonNull(joinChannels, "joinChannels is null")); this.sortChannel = requireNonNull(sortChannel, "sortChannel is null"); + this.countChannel = requireNonNull(countChannel, "countChannel is null"); + this.aggregationBuilder = requireNonNull(aggregationBuilder, "aggregationBuilder is null"); } private List getTypes() @@ -1000,10 +1074,20 @@ public class JoinCompiler return sortChannel; } + private Optional getCountChannel() + { + return countChannel; + } + + private Optional getAggregationBuilder() + { + return aggregationBuilder; + } + @Override public int hashCode() { - return Objects.hash(types, outputChannels, joinChannels, sortChannel); + return Objects.hash(types, outputChannels, joinChannels, sortChannel, countChannel, aggregationBuilder); } @Override @@ -1019,7 +1103,9 @@ public class JoinCompiler return Objects.equals(this.types, other.types) && Objects.equals(this.outputChannels, other.outputChannels) && Objects.equals(this.joinChannels, other.joinChannels) && - Objects.equals(this.sortChannel, other.sortChannel); + Objects.equals(this.sortChannel, other.sortChannel) && + Objects.equals(this.countChannel, other.countChannel) && + Objects.equals(this.aggregationBuilder, other.aggregationBuilder); } } } -- Gitee From 18c7e14cf8be302d8fbbd3cbb066d997b54ab655 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Tue, 21 Feb 2023 12:34:02 +0530 Subject: [PATCH 07/36] Group Join compiler changes. --- .../io/prestosql/operator/PagesIndex.java | 3 ++- .../io/prestosql/sql/gen/JoinCompiler.java | 22 ++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/PagesIndex.java b/presto-main/src/main/java/io/prestosql/operator/PagesIndex.java index 17822fa69..e23f42ef5 100644 --- a/presto-main/src/main/java/io/prestosql/operator/PagesIndex.java +++ b/presto-main/src/main/java/io/prestosql/operator/PagesIndex.java @@ -514,7 +514,8 @@ public class PagesIndex hashChannel, filterFunctionFactory, sortChannel, - searchFunctionFactories); + searchFunctionFactories, + aggregationBuilder); } catch (Exception e) { log.error(e, "Lookup source compile failed for types=%s error=%s", types, e); diff --git a/presto-main/src/main/java/io/prestosql/sql/gen/JoinCompiler.java b/presto-main/src/main/java/io/prestosql/sql/gen/JoinCompiler.java index 1ff09abb5..692b3e5ed 100644 --- a/presto-main/src/main/java/io/prestosql/sql/gen/JoinCompiler.java +++ b/presto-main/src/main/java/io/prestosql/sql/gen/JoinCompiler.java @@ -311,7 +311,8 @@ public class JoinCompiler FieldDefinition instanceSizeField, List channelFields, List joinChannelFields, - FieldDefinition hashChannelField, FieldDefinition aggregationBuilderField) + FieldDefinition hashChannelField, + FieldDefinition aggregationBuilderField) { Parameter channels = arg("channels", type(List.class, type(List.class, Block.class))); Parameter hashChannel = arg("hashChannel", type(OptionalInt.class)); @@ -998,9 +999,10 @@ public class JoinCompiler OptionalInt hashChannel, Optional filterFunctionFactory, Optional sortChannel, - List searchFunctionFactories) + List searchFunctionFactories, + Optional aggregationBuilder) { - PagesHashStrategy pagesHashStrategy = pagesHashStrategyFactory.createPagesHashStrategy(channels, hashChannel); + PagesHashStrategy pagesHashStrategy = pagesHashStrategyFactory.createPagesHashStrategy(channels, hashChannel, aggregationBuilder); try { return constructor.newInstance(session, pagesHashStrategy, addresses, channels, filterFunctionFactory, sortChannel, searchFunctionFactories, singleBigintJoinChannel); } @@ -1017,7 +1019,7 @@ public class JoinCompiler public PagesHashStrategyFactory(Class pagesHashStrategyClass) { try { - constructor = pagesHashStrategyClass.getConstructor(List.class, OptionalInt.class); + constructor = pagesHashStrategyClass.getConstructor(List.class, OptionalInt.class, Optional.class); } catch (NoSuchMethodException e) { throw new RuntimeException(e); @@ -1027,7 +1029,17 @@ public class JoinCompiler public PagesHashStrategy createPagesHashStrategy(List> channels, OptionalInt hashChannel) { try { - return constructor.newInstance(channels, hashChannel); + return constructor.newInstance(channels, hashChannel, Optional.empty()); + } + catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + public PagesHashStrategy createPagesHashStrategy(List> channels, OptionalInt hashChannel, Optional aggregationBuilder) + { + try { + return constructor.newInstance(channels, hashChannel, aggregationBuilder); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); -- Gitee From f1b6aab3b6454e9c74f09d9680807d974992c898 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Tue, 21 Feb 2023 12:47:46 +0530 Subject: [PATCH 08/36] Group Join Probe Class. --- .../io/prestosql/operator/GroupJoinProbe.java | 146 ++++++++++++++++ .../operator/UnSpilledGroupJoinProbe.java | 165 ++++++++++++++++++ 2 files changed, 311 insertions(+) create mode 100644 presto-main/src/main/java/io/prestosql/operator/GroupJoinProbe.java create mode 100644 presto-main/src/main/java/io/prestosql/operator/UnSpilledGroupJoinProbe.java diff --git a/presto-main/src/main/java/io/prestosql/operator/GroupJoinProbe.java b/presto-main/src/main/java/io/prestosql/operator/GroupJoinProbe.java new file mode 100644 index 000000000..b0971da59 --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/operator/GroupJoinProbe.java @@ -0,0 +1,146 @@ +/* + * 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.prestosql.operator; + +import io.prestosql.operator.aggregation.builder.AggregationBuilder; +import io.prestosql.spi.Page; +import io.prestosql.spi.block.Block; + +import java.util.List; +import java.util.Optional; +import java.util.OptionalInt; + +import static io.prestosql.spi.type.BigintType.BIGINT; +import static java.util.Objects.requireNonNull; + +public class GroupJoinProbe +{ + public static class GroupJoinProbeFactory + { + private final int[] probeOutputChannels; + private final List probeJoinChannels; + private final OptionalInt probeHashChannel; + private final OptionalInt probeCountChannel; + + public GroupJoinProbeFactory(int[] probeOutputChannels, List probeJoinChannels, OptionalInt probeHashChannel, OptionalInt probeCountChannel) + { + this.probeOutputChannels = probeOutputChannels; + this.probeJoinChannels = probeJoinChannels; + this.probeHashChannel = probeHashChannel; + this.probeCountChannel = probeCountChannel; + } + + public GroupJoinProbe createGroupJoinProbe(Page page, boolean isSpilled, LookupSourceProvider lookupSourceProvider, + AggregationBuilder probeAggregationBuilder, + AggregationBuilder buildAggregationBuilder ) + { + LookupSource lookupSource = lookupSourceProvider.withLease((lookupSourceLease -> lookupSourceLease.getLookupSource())); + if (isSpilled || !(lookupSource instanceof JoinHash || lookupSource instanceof OuterLookupSource)) { + return new GroupJoinProbe(probeOutputChannels, page, probeJoinChannels, probeHashChannel, probeCountChannel, probeAggregationBuilder, buildAggregationBuilder); + } + else { + Page loadedProbePage = page.getLoadedPage(probeJoinChannels.stream().mapToInt(i -> i).toArray()); + return new UnSpilledGroupJoinProbe(probeOutputChannels, page, probeJoinChannels, probeHashChannel, loadedProbePage, lookupSource, (probeHashChannel.isPresent() ? (probeHashChannel.getAsInt() >= 0 ? page.getBlock(probeHashChannel.getAsInt()).getLoadedBlock() : null) : null), probeAggregationBuilder, buildAggregationBuilder); + } + } + } + + private final int[] probeOutputChannels; + private final int positionCount; + private final Block[] probeBlocks; + private final AggregationBuilder probeAggregationBuilder; + private final AggregationBuilder buildAggregationBuilder; + private final Page page; + private final Page probePage; + private final Optional probeHashBlock; + private final Optional probeCountBlock; + + private int position = -1; + + protected GroupJoinProbe(int[] probeOutputChannels, Page page, List probeJoinChannels, OptionalInt probeHashChannel, + OptionalInt probeCountChannel, AggregationBuilder probeAggregationBuilder, AggregationBuilder buildAggregationBuilder) + { + this.probeOutputChannels = probeOutputChannels; + this.positionCount = page.getPositionCount(); + this.probeBlocks = new Block[probeJoinChannels.size()]; + this.probeAggregationBuilder = requireNonNull(probeAggregationBuilder, "probeAggregationBuilder is null"); + this.buildAggregationBuilder = requireNonNull(buildAggregationBuilder, "buildAggregationBuilder is null"); + + for (int i = 0; i < probeJoinChannels.size(); i++) { + probeBlocks[i] = page.getBlock(probeJoinChannels.get(i)); + } + this.page = page; + this.probePage = new Page(page.getPositionCount(), probeBlocks); + this.probeHashBlock = probeHashChannel.isPresent() ? Optional.of(page.getBlock(probeHashChannel.getAsInt())) : Optional.empty(); + this.probeCountBlock = probeCountChannel.isPresent() ? Optional.of(page.getBlock(probeCountChannel.getAsInt())) : Optional.empty(); + } + + public AggregationBuilder getProbeAggregationBuilder() + { + return probeAggregationBuilder; + } + + public AggregationBuilder getBuildAggregationBuilder() + { + return buildAggregationBuilder; + } + + public int[] getOutputChannels() + { + return probeOutputChannels; + } + + public boolean advanceNextPosition() + { + position++; + return position < positionCount; + } + + public long getCurrentJoinPosition(LookupSource lookupSource) + { + if (currentRowContainsNull()) { + return -1; + } + if (probeHashBlock.isPresent()) { + long rawHash = BIGINT.getLong(probeHashBlock.get(), position); + return lookupSource.getJoinPosition(position, probePage, page, rawHash); + } + return lookupSource.getJoinPosition(position, probePage, page); + } + + public int getPosition() + { + return position; + } + + public Page getPage() + { + return page; + } + + private boolean currentRowContainsNull() + { + for (Block probeBlock : probeBlocks) { + if (probeBlock.isNull(position)) { + return true; + } + } + return false; + } + + public long getCountProbeRecord() + { + return BIGINT.getLong(probeCountBlock.get(), position); + } +} diff --git a/presto-main/src/main/java/io/prestosql/operator/UnSpilledGroupJoinProbe.java b/presto-main/src/main/java/io/prestosql/operator/UnSpilledGroupJoinProbe.java new file mode 100644 index 000000000..c8a6d955f --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/operator/UnSpilledGroupJoinProbe.java @@ -0,0 +1,165 @@ +/* + * 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.prestosql.operator; + +import io.prestosql.operator.aggregation.builder.AggregationBuilder; +import io.prestosql.spi.Page; +import io.prestosql.spi.block.Block; + +import javax.annotation.Nullable; + +import java.util.Arrays; +import java.util.List; +import java.util.OptionalInt; +import java.util.stream.IntStream; + +import static com.google.common.base.Verify.verify; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static io.prestosql.spi.type.BigintType.BIGINT; +import static java.util.Objects.requireNonNull; + +/** + * UnSpilledGroupJoinProbe + * + * @since 21-Feb-2023 + */ +public class UnSpilledGroupJoinProbe + extends GroupJoinProbe +{ + private final int[] probeOutputChannels; + private final Page page; + private final long[] joinPositionCache; + private int position = -1; + + public UnSpilledGroupJoinProbe(int[] probeOutputChannels, Page page, List probeJoinChannels, OptionalInt probeHashChannel, Page probePage, LookupSource lookupSource, @Nullable Block probeHashBlock, AggregationBuilder probeAggregationBuilder, AggregationBuilder buildAggregationBuilder) + { + super(probeOutputChannels, page, probeJoinChannels, probeHashChannel, probeAggregationBuilder, buildAggregationBuilder); + + this.probeOutputChannels = requireNonNull(probeOutputChannels, "probeOutputChannels is null"); + this.page = requireNonNull(page, "page is null"); + + joinPositionCache = fillCache(lookupSource, page, probeHashBlock, probePage); + } + + public int[] getOutputChannels() + { + return probeOutputChannels; + } + + public boolean advanceNextPosition() + { + verify(++position <= page.getPositionCount(), "already finished"); + return !isFinished(); + } + + public boolean isFinished() + { + return position == page.getPositionCount(); + } + + @Override + public long getCurrentJoinPosition(LookupSource lookupSource) + { + return joinPositionCache[position]; + } + + public int getPosition() + { + return position; + } + + public Page getPage() + { + return page; + } + + private static long[] fillCache( + LookupSource lookupSource, + Page page, + Block probeHashBlock, + Page probePage) + { + int positionCount = page.getPositionCount(); + List nullableBlocks = IntStream.range(0, probePage.getChannelCount()) + .mapToObj(i -> probePage.getBlock(i)) + .filter(Block::mayHaveNull) + .collect(toImmutableList()); + + long[] joinPositionsCache = new long[positionCount]; + if (!nullableBlocks.isEmpty()) { + Arrays.fill(joinPositionsCache, -1); + boolean[] isNull = new boolean[positionCount]; + int nonNullCount = getIsNull(nullableBlocks, positionCount, isNull); + if (nonNullCount < positionCount) { + // We only store positions that are not null + int[] positions = new int[nonNullCount]; + nonNullCount = 0; + for (int i = 0; i < positionCount; i++) { + if (!isNull[i]) { + positions[nonNullCount] = i; + } + // This way less code is in the if branch and CPU should be able to optimize branch prediction better + nonNullCount += isNull[i] ? 0 : 1; + } + if (probeHashBlock != null) { + long[] hashes = new long[positionCount]; + for (int i = 0; i < positionCount; i++) { + hashes[i] = BIGINT.getLong(probeHashBlock, i); + } + lookupSource.getJoinPosition(positions, probePage, page, hashes, joinPositionsCache); + } + else { + lookupSource.getJoinPosition(positions, probePage, page, joinPositionsCache); + } + return joinPositionsCache; + } // else fall back to non-null path + } + int[] positions = new int[positionCount]; + for (int i = 0; i < positionCount; i++) { + positions[i] = i; + } + if (probeHashBlock != null) { + long[] hashes = new long[positionCount]; + for (int i = 0; i < positionCount; i++) { + hashes[i] = BIGINT.getLong(probeHashBlock, i); + } + lookupSource.getJoinPosition(positions, probePage, page, hashes, joinPositionsCache); + } + else { + lookupSource.getJoinPosition(positions, probePage, page, joinPositionsCache); + } + + return joinPositionsCache; + } + + private static int getIsNull(List nullableBlocks, int positionCount, boolean[] isNull) + { + for (int i = 0; i < nullableBlocks.size() - 1; i++) { + Block block = nullableBlocks.get(i); + for (int jPosition = 0; jPosition < positionCount; jPosition++) { + isNull[jPosition] |= block.isNull(jPosition); + } + } + // Last block will also calculate `nonNullCount` + int nonNullCount = 0; + Block lastBlock = nullableBlocks.get(nullableBlocks.size() - 1); + for (int jPosition = 0; jPosition < positionCount; jPosition++) { + isNull[jPosition] |= lastBlock.isNull(jPosition); + nonNullCount += isNull[jPosition] ? 0 : 1; + } + + return nonNullCount; + } +} -- Gitee From a21784e3483d8fac82fba2eb32717a2fad8ca10d Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Tue, 21 Feb 2023 17:21:28 +0530 Subject: [PATCH 09/36] Group Join Lookup Page Builder class --- .../operator/LookupGroupJoinPageBuilder.java | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java new file mode 100644 index 000000000..4ddd3c6b0 --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java @@ -0,0 +1,165 @@ +/* + * 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.prestosql.operator; + +import com.google.common.collect.ImmutableList; +import io.prestosql.operator.aggregation.builder.AggregationBuilder; +import io.prestosql.spi.Page; +import io.prestosql.spi.PageBuilder; +import io.prestosql.spi.block.Block; +import io.prestosql.spi.snapshot.BlockEncodingSerdeProvider; +import io.prestosql.spi.snapshot.Restorable; +import io.prestosql.spi.type.Type; +import it.unimi.dsi.fastutil.ints.IntArrayList; + +import java.io.Serializable; +import java.util.List; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Verify.verify; +import static io.prestosql.spi.block.PageBuilderStatus.DEFAULT_MAX_PAGE_SIZE_IN_BYTES; +import static java.util.Objects.requireNonNull; + +/** + * This page builder creates pages for group join after applying aggregator on probe and build side based on count. + */ +public class LookupGroupJoinPageBuilder + implements Restorable +{ + private final PageBuilder finalPageBuilder; + private final PageBuilder buildPageBuilderTmp; + private final List outputBuildChannels; + private final List outputProbeChannels; + private int estimatedProbeBlockBytes; + + public LookupGroupJoinPageBuilder(List outputTypes, List buildTypes, + List outputBuildChannels, List outputProbeChannels) + { + this.outputBuildChannels = requireNonNull(outputBuildChannels, "outputBuildChannels is null"); + this.outputProbeChannels = requireNonNull(outputProbeChannels, "outputProbeChannels is null"); + this.finalPageBuilder = new PageBuilder(ImmutableList.copyOf(requireNonNull(outputTypes, "outputTypes is null"))); + this.buildPageBuilderTmp = new PageBuilder(requireNonNull(buildTypes, "buildTypes is null")); + } + + public boolean isFull() + { + return estimatedProbeBlockBytes + finalPageBuilder.getSizeInBytes() >= DEFAULT_MAX_PAGE_SIZE_IN_BYTES || finalPageBuilder.isFull(); + } + + public boolean isEmpty() + { + return finalPageBuilder.isEmpty(); + } + + public void reset() + { + finalPageBuilder.reset(); + buildPageBuilderTmp.reset(); + estimatedProbeBlockBytes = 0; + } + + /** + * append the index for the probe and copy the row for the build + */ + public void appendRow(GroupJoinProbe probe, LookupSource lookupSource, long joinPosition) + { + // count is stored in last channel. + long buildCount = lookupSource.getCountForJoinPosition(joinPosition, lookupSource.getChannelCount() - 1); + long probeCount = probe.getCountProbeRecord(); + Page probePage = probe.getPage().getRegion(probe.getPosition(), 1); + buildPageBuilderTmp.declarePosition(); + lookupSource.appendTo(joinPosition, buildPageBuilderTmp, 0); + Page buildPage = buildPageBuilderTmp.build(); + + // probe side + Page probeFinalPage = null; + if (outputProbeChannels.size() != 0) { + AggregationBuilder probeAggregationBuilder = probe.getProbeAggregationBuilder(); + if (probeAggregationBuilder.getAggregationCount() == 0) { + buildCount = 1; + } + for (int i = 0; i < buildCount; i++) { + Work work = probeAggregationBuilder.processPage(probePage); + // Knowingly kept empty while loop + while (!work.process()); + } + WorkProcessor probePageWorkProcessor = probeAggregationBuilder.buildResult(); + while (!probePageWorkProcessor.process()); + probeAggregationBuilder.updateMemory(); + probeFinalPage = probePageWorkProcessor.getResult(); + } + + // build side + Page buildFinalPage = null; + if (outputBuildChannels.size() != 0) { + AggregationBuilder buildAggregationBuilder = probe.getBuildAggregationBuilder(); + if (buildAggregationBuilder.getAggregationCount() == 0) { + probeCount = 1; + } + for (int i = 0; i < probeCount; i++) { + Work work = buildAggregationBuilder.processPage(buildPage); + // Knowingly kept empty while loop + while (!work.process()); + } + WorkProcessor buildPageWorkProcessor = buildAggregationBuilder.buildResult(); + while (!buildPageWorkProcessor.process()); + buildAggregationBuilder.updateMemory(); + buildFinalPage = buildPageWorkProcessor.getResult(); + } + + int probeChannelLength = outputProbeChannels.size(); + for (int i = 0; i < probeChannelLength; i++) { + if (probeFinalPage.getBlock(outputProbeChannels.get(i)).isNull(0)) { + finalPageBuilder.getBlockBuilder(i).appendNull(); + continue; + } + probeFinalPage.getBlock(outputProbeChannels.get(i)).writePositionTo(0, finalPageBuilder.getBlockBuilder(i)); + } + + for (int i = 0; i < outputBuildChannels.size(); i++) { + if (buildFinalPage.getBlock(outputBuildChannels.get(i)).isNull(0)) { + finalPageBuilder.getBlockBuilder(i + probeChannelLength).appendNull(); + continue; + } + buildFinalPage.getBlock(outputBuildChannels.get(i)).writePositionTo(0, finalPageBuilder.getBlockBuilder(i + probeChannelLength)); + } + buildPageBuilderTmp.reset(); + } + + public Page build(GroupJoinProbe probe) + { + return finalPageBuilder.build(); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("estimatedSize", estimatedProbeBlockBytes + finalPageBuilder.getSizeInBytes()) + .add("positionCount", finalPageBuilder.getPositionCount()) + .toString(); + } + + @Override + public Object capture(BlockEncodingSerdeProvider serdeProvider) + { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public void restore(Object state, BlockEncodingSerdeProvider serdeProvider) + { + throw new UnsupportedOperationException("Not supported"); + } +} -- Gitee From 1771bd840167a00bd46e9e25b39a7ddfd1c8fc37 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Tue, 21 Feb 2023 19:19:38 +0530 Subject: [PATCH 10/36] Group Join static issue fixes. --- .../io/prestosql/operator/GroupJoinProbe.java | 5 +- .../io/prestosql/operator/IPagesHash.java | 4 -- .../operator/LookupGroupJoinPageBuilder.java | 56 +++++++++---------- .../operator/UnSpilledGroupJoinProbe.java | 4 +- ...MemoryHashAggregationBuilderWithReset.java | 13 +++++ .../prestosql/operator/TestPositionLinks.java | 1 + .../prestosql/sql/gen/TestJoinCompiler.java | 2 +- 7 files changed, 45 insertions(+), 40 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/GroupJoinProbe.java b/presto-main/src/main/java/io/prestosql/operator/GroupJoinProbe.java index b0971da59..e0b834348 100644 --- a/presto-main/src/main/java/io/prestosql/operator/GroupJoinProbe.java +++ b/presto-main/src/main/java/io/prestosql/operator/GroupJoinProbe.java @@ -42,8 +42,7 @@ public class GroupJoinProbe } public GroupJoinProbe createGroupJoinProbe(Page page, boolean isSpilled, LookupSourceProvider lookupSourceProvider, - AggregationBuilder probeAggregationBuilder, - AggregationBuilder buildAggregationBuilder ) + AggregationBuilder probeAggregationBuilder, AggregationBuilder buildAggregationBuilder) { LookupSource lookupSource = lookupSourceProvider.withLease((lookupSourceLease -> lookupSourceLease.getLookupSource())); if (isSpilled || !(lookupSource instanceof JoinHash || lookupSource instanceof OuterLookupSource)) { @@ -51,7 +50,7 @@ public class GroupJoinProbe } else { Page loadedProbePage = page.getLoadedPage(probeJoinChannels.stream().mapToInt(i -> i).toArray()); - return new UnSpilledGroupJoinProbe(probeOutputChannels, page, probeJoinChannels, probeHashChannel, loadedProbePage, lookupSource, (probeHashChannel.isPresent() ? (probeHashChannel.getAsInt() >= 0 ? page.getBlock(probeHashChannel.getAsInt()).getLoadedBlock() : null) : null), probeAggregationBuilder, buildAggregationBuilder); + return new UnSpilledGroupJoinProbe(probeOutputChannels, page, probeJoinChannels, probeHashChannel, loadedProbePage, lookupSource, (probeHashChannel.isPresent() ? (probeHashChannel.getAsInt() >= 0 ? page.getBlock(probeHashChannel.getAsInt()).getLoadedBlock() : null) : null), probeCountChannel, probeAggregationBuilder, buildAggregationBuilder); } } } diff --git a/presto-main/src/main/java/io/prestosql/operator/IPagesHash.java b/presto-main/src/main/java/io/prestosql/operator/IPagesHash.java index 525a3b5df..7734c6667 100644 --- a/presto-main/src/main/java/io/prestosql/operator/IPagesHash.java +++ b/presto-main/src/main/java/io/prestosql/operator/IPagesHash.java @@ -17,10 +17,6 @@ import io.prestosql.operator.aggregation.builder.AggregationBuilder; import io.prestosql.spi.Page; import io.prestosql.spi.PageBuilder; -import static io.prestosql.operator.SyntheticAddress.decodePosition; -import static io.prestosql.operator.SyntheticAddress.decodeSliceIndex; -import static java.lang.Math.toIntExact; - public interface IPagesHash { int getChannelCount(); diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java index 4ddd3c6b0..3917dc1ea 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java @@ -17,17 +17,13 @@ import com.google.common.collect.ImmutableList; import io.prestosql.operator.aggregation.builder.AggregationBuilder; import io.prestosql.spi.Page; import io.prestosql.spi.PageBuilder; -import io.prestosql.spi.block.Block; import io.prestosql.spi.snapshot.BlockEncodingSerdeProvider; import io.prestosql.spi.snapshot.Restorable; import io.prestosql.spi.type.Type; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import java.io.Serializable; import java.util.List; import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Verify.verify; import static io.prestosql.spi.block.PageBuilderStatus.DEFAULT_MAX_PAGE_SIZE_IN_BYTES; import static java.util.Objects.requireNonNull; @@ -85,37 +81,17 @@ public class LookupGroupJoinPageBuilder // probe side Page probeFinalPage = null; if (outputProbeChannels.size() != 0) { - AggregationBuilder probeAggregationBuilder = probe.getProbeAggregationBuilder(); - if (probeAggregationBuilder.getAggregationCount() == 0) { - buildCount = 1; - } - for (int i = 0; i < buildCount; i++) { - Work work = probeAggregationBuilder.processPage(probePage); - // Knowingly kept empty while loop - while (!work.process()); - } - WorkProcessor probePageWorkProcessor = probeAggregationBuilder.buildResult(); - while (!probePageWorkProcessor.process()); - probeAggregationBuilder.updateMemory(); - probeFinalPage = probePageWorkProcessor.getResult(); + probeFinalPage = processAggregationOnPage(probe.getProbeAggregationBuilder().getAggregationCount() == 0 ? 1 : buildCount, + probePage, + probe.getProbeAggregationBuilder()); } // build side Page buildFinalPage = null; if (outputBuildChannels.size() != 0) { - AggregationBuilder buildAggregationBuilder = probe.getBuildAggregationBuilder(); - if (buildAggregationBuilder.getAggregationCount() == 0) { - probeCount = 1; - } - for (int i = 0; i < probeCount; i++) { - Work work = buildAggregationBuilder.processPage(buildPage); - // Knowingly kept empty while loop - while (!work.process()); - } - WorkProcessor buildPageWorkProcessor = buildAggregationBuilder.buildResult(); - while (!buildPageWorkProcessor.process()); - buildAggregationBuilder.updateMemory(); - buildFinalPage = buildPageWorkProcessor.getResult(); + buildFinalPage = processAggregationOnPage(probe.getBuildAggregationBuilder().getAggregationCount() == 0 ? 1 : probeCount, + buildPage, + probe.getBuildAggregationBuilder()); } int probeChannelLength = outputProbeChannels.size(); @@ -137,6 +113,26 @@ public class LookupGroupJoinPageBuilder buildPageBuilderTmp.reset(); } + private static Page processAggregationOnPage(long count, Page sourcePage, AggregationBuilder aggregationBuilder) + { + // TODO Vineet check on how to convert into future object and relate in normal code flow. + Page finalPage; + for (int i = 0; i < count; i++) { + Work work = aggregationBuilder.processPage(sourcePage); + // Knowingly kept empty while loop + while (!work.process()) { + i = i; + } + } + WorkProcessor pageWorkProcessor = aggregationBuilder.buildResult(); + while (!pageWorkProcessor.process()) { + finalPage = null; + } + aggregationBuilder.updateMemory(); + finalPage = pageWorkProcessor.getResult(); + return finalPage; + } + public Page build(GroupJoinProbe probe) { return finalPageBuilder.build(); diff --git a/presto-main/src/main/java/io/prestosql/operator/UnSpilledGroupJoinProbe.java b/presto-main/src/main/java/io/prestosql/operator/UnSpilledGroupJoinProbe.java index c8a6d955f..e96d15815 100644 --- a/presto-main/src/main/java/io/prestosql/operator/UnSpilledGroupJoinProbe.java +++ b/presto-main/src/main/java/io/prestosql/operator/UnSpilledGroupJoinProbe.java @@ -43,9 +43,9 @@ public class UnSpilledGroupJoinProbe private final long[] joinPositionCache; private int position = -1; - public UnSpilledGroupJoinProbe(int[] probeOutputChannels, Page page, List probeJoinChannels, OptionalInt probeHashChannel, Page probePage, LookupSource lookupSource, @Nullable Block probeHashBlock, AggregationBuilder probeAggregationBuilder, AggregationBuilder buildAggregationBuilder) + public UnSpilledGroupJoinProbe(int[] probeOutputChannels, Page page, List probeJoinChannels, OptionalInt probeHashChannel, Page probePage, LookupSource lookupSource, @Nullable Block probeHashBlock, OptionalInt probeCountChannel, AggregationBuilder probeAggregationBuilder, AggregationBuilder buildAggregationBuilder) { - super(probeOutputChannels, page, probeJoinChannels, probeHashChannel, probeAggregationBuilder, buildAggregationBuilder); + super(probeOutputChannels, page, probeJoinChannels, probeHashChannel, probeCountChannel, probeAggregationBuilder, buildAggregationBuilder); this.probeOutputChannels = requireNonNull(probeOutputChannels, "probeOutputChannels is null"); this.page = requireNonNull(page, "page is null"); diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryHashAggregationBuilderWithReset.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryHashAggregationBuilderWithReset.java index 9fa0a735b..7a8688500 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryHashAggregationBuilderWithReset.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryHashAggregationBuilderWithReset.java @@ -1,3 +1,16 @@ +/* + * 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.prestosql.operator.aggregation.builder; import com.google.common.primitives.Ints; diff --git a/presto-main/src/test/java/io/prestosql/operator/TestPositionLinks.java b/presto-main/src/test/java/io/prestosql/operator/TestPositionLinks.java index d6b377190..9b4c30e1b 100644 --- a/presto-main/src/test/java/io/prestosql/operator/TestPositionLinks.java +++ b/presto-main/src/test/java/io/prestosql/operator/TestPositionLinks.java @@ -314,6 +314,7 @@ public class TestPositionLinks ImmutableList.of(), OptionalInt.empty(), Optional.of(0), + Optional.empty(), createTestMetadataManager()); } diff --git a/presto-main/src/test/java/io/prestosql/sql/gen/TestJoinCompiler.java b/presto-main/src/test/java/io/prestosql/sql/gen/TestJoinCompiler.java index a6ad8ff80..0ceafe280 100644 --- a/presto-main/src/test/java/io/prestosql/sql/gen/TestJoinCompiler.java +++ b/presto-main/src/test/java/io/prestosql/sql/gen/TestJoinCompiler.java @@ -191,7 +191,7 @@ public class TestJoinCompiler PagesHashStrategyFactory pagesHashStrategyFactory = tmpJoinCompiler.compilePagesHashStrategyFactory(types, joinChannels, Optional.of(outputChannels)); PagesHashStrategy hashStrategy = pagesHashStrategyFactory.createPagesHashStrategy(channels, hashChannel); // todo add tests for filter function - PagesHashStrategy expectedHashStrategy = new SimplePagesHashStrategy(types, outputChannels, channels, joinChannels, hashChannel, Optional.empty(), metadata); + PagesHashStrategy expectedHashStrategy = new SimplePagesHashStrategy(types, outputChannels, channels, joinChannels, hashChannel, Optional.empty(), Optional.empty(), metadata); // verify channel count assertEquals(hashStrategy.getChannelCount(), outputChannels.size()); -- Gitee From fd9ad63ff70dd5f86a6ccf7906a600b5a1a538b2 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Wed, 22 Feb 2023 15:38:09 +0530 Subject: [PATCH 11/36] Group Join Hash Builder Code. --- .../operator/GroupJoinAggregator.java | 228 ++++++++ .../HashBuilderGroupJoinOperator.java | 519 ++++++++++++++++++ .../HashBuilderGroupJoinOperatorFactory.java | 339 ++++++++++++ 3 files changed, 1086 insertions(+) create mode 100644 presto-main/src/main/java/io/prestosql/operator/GroupJoinAggregator.java create mode 100644 presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java create mode 100644 presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java diff --git a/presto-main/src/main/java/io/prestosql/operator/GroupJoinAggregator.java b/presto-main/src/main/java/io/prestosql/operator/GroupJoinAggregator.java new file mode 100644 index 000000000..dba15cedf --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/operator/GroupJoinAggregator.java @@ -0,0 +1,228 @@ +/* + * 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.prestosql.operator; + +import com.google.common.collect.ImmutableList; +import io.airlift.units.DataSize; +import io.prestosql.operator.aggregation.Accumulator; +import io.prestosql.operator.aggregation.AccumulatorFactory; +import io.prestosql.operator.aggregation.partial.PartialAggregationController; +import io.prestosql.operator.scalar.CombineHashFunction; +import io.prestosql.spi.Page; +import io.prestosql.spi.PageBuilder; +import io.prestosql.spi.plan.AggregationNode; +import io.prestosql.spi.type.BigintType; +import io.prestosql.spi.type.Type; +import io.prestosql.sql.gen.JoinCompiler; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkArgument; +import static io.prestosql.operator.aggregation.builder.InMemoryHashAggregationBuilder.toTypes; +import static io.prestosql.sql.planner.optimizations.HashGenerationOptimizer.INITIAL_HASH_VALUE; +import static io.prestosql.type.TypeUtils.NULL_HASH_CODE; +import static java.util.Objects.requireNonNull; + +public class GroupJoinAggregator +{ + private final Optional hashChannel; + private final Optional groupIdChannel; + private final List accumulatorFactories; + private final List groupByTypes; + private final List groupByChannels; + private final List globalAggregationGroupIds; + private final AggregationNode.Step step; + private final int expectedGroups; + private final Optional maxPartialMemory; + private final JoinCompiler joinCompiler; + private final boolean useSystemMemory; + private final Optional partialAggregationController; + private final boolean produceDefaultOutput; + + protected final List types; + + public GroupJoinAggregator(Optional hashChannel, + Optional groupIdChannel, + List accumulatorFactories, + List groupByTypes, + List groupByChannels, + List globalAggregationGroupIds, + AggregationNode.Step step, + int expectedGroups, + Optional maxPartialMemory, + JoinCompiler joinCompiler, + boolean useSystemMemory, + Optional partialAggregationController, + boolean produceDefaultOutput) + { + requireNonNull(accumulatorFactories, "accumulatorFactories is null"); + checkArgument(!partialAggregationController.isPresent() || step.isOutputPartial(), + "partialAggregationController should be present only for partial aggregation"); + this.hashChannel = requireNonNull(hashChannel, "hashChannel is null"); + this.groupIdChannel = requireNonNull(groupIdChannel, "groupIdChannel is null"); + this.accumulatorFactories = ImmutableList.copyOf(accumulatorFactories); + this.groupByTypes = ImmutableList.copyOf(groupByTypes); + this.groupByChannels = ImmutableList.copyOf(groupByChannels); + this.globalAggregationGroupIds = ImmutableList.copyOf(globalAggregationGroupIds); + this.step = requireNonNull(step, "step is null"); + this.expectedGroups = expectedGroups; + this.maxPartialMemory = requireNonNull(maxPartialMemory, "maxPartialMemory is null"); + this.joinCompiler = requireNonNull(joinCompiler, "joinCompiler is null"); + this.useSystemMemory = useSystemMemory; + this.partialAggregationController = requireNonNull(partialAggregationController, "partialAggregationController is null"); + this.produceDefaultOutput = produceDefaultOutput; + this.types = toTypes(groupByTypes, step, accumulatorFactories, hashChannel); + } + + public Optional getHashChannel() + { + return hashChannel; + } + + public Optional getGroupIdChannel() + { + return groupIdChannel; + } + + public List getAccumulatorFactories() + { + return accumulatorFactories; + } + + public List getGroupByTypes() + { + return groupByTypes; + } + + public List getGroupByChannels() + { + return groupByChannels; + } + + public List getGlobalAggregationGroupIds() + { + return globalAggregationGroupIds; + } + + public AggregationNode.Step getStep() + { + return step; + } + + public int getExpectedGroups() + { + return expectedGroups; + } + + public Optional getMaxPartialMemory() + { + return maxPartialMemory; + } + + public JoinCompiler getJoinCompiler() + { + return joinCompiler; + } + + public boolean isUseSystemMemory() + { + return useSystemMemory; + } + + public Optional getPartialAggregationController() + { + return partialAggregationController; + } + + public boolean isProduceDefaultOutput() + { + return produceDefaultOutput; + } + + public List getTypes() + { + return types; + } + + protected Page getGlobalAggregationOutput() + { + List accumulators = accumulatorFactories.stream() + .map(AccumulatorFactory::createAccumulator) + .collect(Collectors.toList()); + + // global aggregation output page will only be constructed once, + // so a new PageBuilder is constructed (instead of using PageBuilder.reset) + PageBuilder output = new PageBuilder(globalAggregationGroupIds.size(), types); + + for (int groupId : globalAggregationGroupIds) { + output.declarePosition(); + int channel = 0; + + for (; channel < groupByTypes.size(); channel++) { + if (channel == groupIdChannel.get()) { + output.getBlockBuilder(channel).writeLong(groupId); + } + else { + output.getBlockBuilder(channel).appendNull(); + } + } + + if (hashChannel.isPresent()) { + long hashValue = calculateDefaultOutputHash(groupByTypes, groupIdChannel.get(), groupId); + output.getBlockBuilder(channel++).writeLong(hashValue); + } + + for (int j = 0; j < accumulators.size(); channel++, j++) { + if (step.isOutputPartial()) { + accumulators.get(j).evaluateIntermediate(output.getBlockBuilder(channel)); + } + else { + accumulators.get(j).evaluateFinal(output.getBlockBuilder(channel)); + } + } + } + + if (output.isEmpty()) { + return null; + } + return output.build(); + } + + private static long calculateDefaultOutputHash(List groupByChannels, int groupIdChannel, int groupId) + { + // Default output has NULLs on all columns except of groupIdChannel + long result = INITIAL_HASH_VALUE; + for (int channel = 0; channel < groupByChannels.size(); channel++) { + if (channel != groupIdChannel) { + result = CombineHashFunction.getHash(result, NULL_HASH_CODE); + } + else { + result = CombineHashFunction.getHash(result, BigintType.hash(groupId)); + } + } + return result; + } + + protected boolean hasOrderBy() + { + return accumulatorFactories.stream().anyMatch(AccumulatorFactory::hasOrderBy); + } + + protected boolean hasDistinct() + { + return accumulatorFactories.stream().anyMatch(AccumulatorFactory::hasDistinct); + } +} diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java new file mode 100644 index 000000000..84500714a --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java @@ -0,0 +1,519 @@ +/* + * 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.prestosql.operator; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.io.Closer; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import io.prestosql.memory.context.LocalMemoryContext; +import io.prestosql.operator.aggregation.builder.AggregationBuilder; +import io.prestosql.operator.aggregation.builder.InMemoryHashAggregationBuilder; +import io.prestosql.operator.aggregation.builder.InMemoryHashAggregationBuilderWithReset; +import io.prestosql.spi.Page; +import io.prestosql.spi.plan.Symbol; +import io.prestosql.sql.gen.JoinFilterFunctionCompiler; + +import javax.annotation.Nullable; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.OptionalLong; + +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Verify.verify; +import static java.util.Objects.requireNonNull; + +public class HashBuilderGroupJoinOperator + implements SinkOperator +{ + @VisibleForTesting + public enum State + { + /** + * Operator accepts input + */ + CONSUMING_INPUT, + + /** + * All inputs accepted, finishing aggregations + */ + AGGR_FINISHING, + + /** + * Aggregation on input finished + */ + AGGR_FINISHED, + + /** + * LookupSource has been built and passed on without any spill occurring + */ + LOOKUP_SOURCE_BUILT, + + /** + * No longer needed + */ + CLOSED + } + + private final OperatorContext operatorContext; + private final LocalMemoryContext localUserMemoryContext; + private final LocalMemoryContext localRevocableMemoryContext; + //TODO-cp-I2DSGR: Shared field + private final PartitionedLookupSourceFactory lookupSourceFactory; + private final ListenableFuture lookupSourceFactoryDestroyed; + private final int partitionIndex; + private final List outputChannels; + private final List hashChannels; + private final OptionalInt preComputedHashChannel; + private final Optional filterFunctionFactory; + private final Optional sortChannel; + private final Optional countChannel; + private final List searchFunctionFactories; + private final PagesIndex index; + private final boolean spillEnabled; + private final HashCollisionsCounter hashCollisionsCounter; + private State state = State.CONSUMING_INPUT; + private Optional> lookupSourceNotNeeded = Optional.empty(); + @Nullable + private LookupSourceSupplier lookupSourceSupplier; + private OptionalLong lookupSourceChecksum = OptionalLong.empty(); + + private Optional finishMemoryRevoke = Optional.empty(); + private final long expectedValues; + private boolean spillToHdfsEnabled; + + protected AggregationBuilder aggregationBuilder; + protected AggregationBuilder aggrOnAggregationBuilder; + protected LocalMemoryContext memoryContext; + protected WorkProcessor outputPages; + + /*protected SettableFuture aggrFinishInProgress;*/ + //protected boolean aggregationFinishing; + //private boolean aggregationFinished; + //protected boolean aggregationInputProcessed; + + // for yield when memory is not available + protected Work unfinishedAggrWork; + protected long numberOfInputRowsProcessed; + protected long numberOfUniqueRowsProduced; + private final GroupJoinAggregator aggregator; + private final GroupJoinAggregator aggrOnAggregator; + + private final List buildFinalOutputSymbols; + private final List buildFinalOutputChannels; + + public HashBuilderGroupJoinOperator( + OperatorContext operatorContext, + PartitionedLookupSourceFactory lookupSourceFactory, + int partitionIndex, + List outputChannels, + List hashChannels, + OptionalInt preComputedHashChannel, + Optional filterFunctionFactory, + Optional sortChannel, + Optional countChannel, + List searchFunctionFactories, + int expectedPositions, + PagesIndex.Factory pagesIndexFactory, + boolean spillEnabled, + boolean spillToHdfsEnabled, + GroupJoinAggregator aggregator, + GroupJoinAggregator aggrOnAggregator, + List buildFinalOutputSymbols, + List buildFinalOutputChannels, + ListeningExecutorService executor) + { + requireNonNull(pagesIndexFactory, "pagesIndexFactory is null"); + this.operatorContext = operatorContext; + this.partitionIndex = partitionIndex; + this.filterFunctionFactory = filterFunctionFactory; + this.sortChannel = sortChannel; + this.countChannel = countChannel; + this.searchFunctionFactories = searchFunctionFactories; + this.localUserMemoryContext = operatorContext.localUserMemoryContext(); + this.localRevocableMemoryContext = operatorContext.localRevocableMemoryContext(); + + this.index = pagesIndexFactory.newPagesIndex(lookupSourceFactory.getTypes(), expectedPositions); + this.lookupSourceFactory = lookupSourceFactory; + this.lookupSourceFactoryDestroyed = lookupSourceFactory.isDestroyed(); + + this.outputChannels = outputChannels; + this.hashChannels = hashChannels; + this.preComputedHashChannel = preComputedHashChannel; + + this.hashCollisionsCounter = new HashCollisionsCounter(operatorContext); + operatorContext.setInfoSupplier(hashCollisionsCounter); + this.spillEnabled = spillEnabled; + this.spillToHdfsEnabled = spillToHdfsEnabled; + this.expectedValues = expectedPositions * 10L; + + /*this.aggrFinishInProgress = SettableFuture.create(); + this.aggrFinishInProgress.set(null);*/ + + requireNonNull(operatorContext, "operatorContext is null"); + this.memoryContext = operatorContext.localUserMemoryContext(); + if (aggregator.isUseSystemMemory()) { + this.memoryContext = operatorContext.localSystemMemoryContext(); + } + + this.aggregator = aggregator; + this.aggrOnAggregator = aggrOnAggregator; + this.buildFinalOutputSymbols = buildFinalOutputSymbols; + this.buildFinalOutputChannels = buildFinalOutputChannels; + } + + @Override + public OperatorContext getOperatorContext() + { + return operatorContext; + } + + @Override + public boolean needsInput() + { + if (state == State.CONSUMING_INPUT) { + if (/*aggregationFinishing || */outputPages != null) { + return false; + } + else if (aggregationBuilder != null && aggregationBuilder.isFull()) { + return false; + } + else if (lookupSourceFactoryDestroyed.isDone()/*aggrFinishInProgress.isDone()*/) { + return false; + } + else { + // TODO Vineet Need to move this out of needsInput and need to make it light weight. + if (unfinishedAggrWork != null) { + boolean workDone = unfinishedAggrWork.process(); + aggregationBuilder.updateMemory(); + if (!workDone) { + return false; + } + unfinishedAggrWork = null; + } + return true; + } + } + return false; + } + + @Override + public void addInput(Page page) + { + requireNonNull(page, "page is null"); + + checkState(unfinishedAggrWork == null, "Operator has unfinished work"); + //checkState(!aggregationFinishing, "Operator is already finishing"); + checkState(state == State.CONSUMING_INPUT, "Operator is not in Consuming Input state"); + /*aggregationInputProcessed = true;*/ + + if (aggregationBuilder == null) { + createAggregationBuilder(); + } + else { + checkState(!aggregationBuilder.isFull(), "Aggregation buffer is full"); + } + + // process the current page; save the unfinished work if we are waiting for memory + unfinishedAggrWork = aggregationBuilder.processPage(page); + if (unfinishedAggrWork.process()) { + unfinishedAggrWork = null; + } + aggregationBuilder.updateMemory(); + numberOfInputRowsProcessed += page.getPositionCount(); + } + + protected boolean hasOrderBy() + { + return aggregator.hasOrderBy(); + } + + protected boolean hasDistinct() + { + return aggregator.hasDistinct(); + } + + public void createAggregationBuilder() + { + if (aggregator.getStep().isOutputPartial() || !spillEnabled || hasOrderBy() || hasDistinct()) { + // TODO: We ignore spillEnabled here if any aggregate has ORDER BY clause or DISTINCT because they are not yet implemented for spilling. + aggregationBuilder = new InMemoryHashAggregationBuilder( + aggregator.getAccumulatorFactories(), + aggregator.getStep(), + aggregator.getExpectedGroups(), + aggregator.getGroupByTypes(), + aggregator.getGroupByChannels(), + aggregator.getHashChannel(), + operatorContext, + aggregator.getMaxPartialMemory(), + aggregator.getJoinCompiler(), + () -> { + memoryContext.setBytes(((InMemoryHashAggregationBuilder) aggregationBuilder).getSizeInMemory()); + if (aggregator.getStep().isOutputPartial() && aggregator.getMaxPartialMemory().isPresent()) { + // do not yield on memory for partial aggregations + return true; + } + return operatorContext.isWaitingForMemory().isDone(); + }); + aggrOnAggregationBuilder = new InMemoryHashAggregationBuilderWithReset( + aggrOnAggregator.getAccumulatorFactories(), + aggrOnAggregator.getStep(), + aggrOnAggregator.getExpectedGroups(), + aggrOnAggregator.getGroupByTypes(), + aggrOnAggregator.getGroupByChannels(), + aggrOnAggregator.getHashChannel(), + operatorContext, + aggrOnAggregator.getMaxPartialMemory(), + aggrOnAggregator.getJoinCompiler(), + () -> { + memoryContext.setBytes(((InMemoryHashAggregationBuilder) aggrOnAggregationBuilder).getSizeInMemory()); + if (aggrOnAggregator.getStep().isOutputPartial() && aggrOnAggregator.getMaxPartialMemory().isPresent()) { + // do not yield on memory for partial aggregations + return true; + } + return operatorContext.isWaitingForMemory().isDone(); + }); + } + else { + throw new UnsupportedOperationException("Not Supported"); + } + } + + private void updateIndex(Page page) + { + index.addPage(page); + if (spillEnabled) { + localRevocableMemoryContext.setBytes(index.getEstimatedSize().toBytes()); + } + else { + if (!localUserMemoryContext.trySetBytes(index.getEstimatedSize().toBytes())) { + index.compact(); + localUserMemoryContext.setBytes(index.getEstimatedSize().toBytes()); + } + } + operatorContext.recordOutput(page.getSizeInBytes(), page.getPositionCount()); + } + + public Page processAggregation() + { + if (state == State.AGGR_FINISHED) { + return null; + } + + // process unfinished work if one exists + if (unfinishedAggrWork != null) { + boolean workDone = unfinishedAggrWork.process(); + aggregationBuilder.updateMemory(); + if (!workDone) { + return null; + } + unfinishedAggrWork = null; + } + + if (outputPages == null) { + //if (state == State.AGGR_FINISHING/*aggregationFinishing*/) { + if (/*!aggregationInputProcessed && */aggregator.isProduceDefaultOutput()) { + // global aggregations always generate an output row with the default aggregation output (e.g. 0 for COUNT, NULL for SUM) + state = State.AGGR_FINISHED; //aggregationFinished = true; + return aggregator.getGlobalAggregationOutput(); + } + + if (aggregationBuilder == null) { + state = State.AGGR_FINISHED; //aggregationFinished = true; + return null; + } + //} + + // only flush if we are finishing or the aggregation builder is full + if (/*!aggregationFinishing && */(/*aggregationBuilder == null || */!aggregationBuilder.isFull())) { + return null; + } + + outputPages = aggregationBuilder.buildResult(); + } + + if (!outputPages.process()) { + return null; + } + + if (outputPages.isFinished()) { + closeAggregationBuilder(); + return null; + } + + Page result = outputPages.getResult(); + numberOfUniqueRowsProduced += result.getPositionCount(); + return result; + } + + protected void closeAggregationBuilder() + { + outputPages = null; + if (aggregationBuilder != null) { + aggregationBuilder.recordHashCollisions(hashCollisionsCounter); + aggregationBuilder.close(); + // aggregationBuilder.close() will release all memory reserved in memory accounting. + // The reference must be set to null afterwards to avoid unaccounted memory. + aggregationBuilder = null; + } + memoryContext.setBytes(0); + aggregator.getPartialAggregationController().ifPresent( + controller -> controller.onFlush(numberOfInputRowsProcessed, numberOfUniqueRowsProduced)); + numberOfInputRowsProcessed = 0; + numberOfUniqueRowsProduced = 0; + } + + @Override + public void finish() + { + /*aggregationFinishing = true;*/ + if (state == State.CONSUMING_INPUT) { + state = State.AGGR_FINISHING; + } + doFinish(); + } + + private void doFinish() + { + if (lookupSourceFactoryDestroyed.isDone()) { + close(); + return; + } + + if (finishMemoryRevoke.isPresent()) { + return; + } + + switch (state) { + case CONSUMING_INPUT: + return; + case AGGR_FINISHING: + // finish all aggregation operation first + finishAggregation(); + return; + case AGGR_FINISHED: + finishInput(); + return; + case LOOKUP_SOURCE_BUILT: + disposeLookupSourceIfRequested(); + return; + + case CLOSED: + // no-op + return; + } + + throw new IllegalStateException("Unhandled state: " + state); + } + + private void finishAggregation() + { + Page page = processAggregation(); + while (page != null) { + updateIndex(page); + page = processAggregation(); + } + } + + private void finishInput() + { + checkState(state == State.AGGR_FINISHED); + if (lookupSourceFactoryDestroyed.isDone()) { + close(); + return; + } + + LookupSourceSupplier partition = buildLookupSource(); + if (spillEnabled) { + localRevocableMemoryContext.setBytes(partition.get().getInMemorySizeInBytes()); + } + else { + localUserMemoryContext.setBytes(partition.get().getInMemorySizeInBytes()); + } + lookupSourceNotNeeded = Optional.of(lookupSourceFactory.lendPartitionLookupSource(partitionIndex, partition)); + state = State.LOOKUP_SOURCE_BUILT; + } + + private void disposeLookupSourceIfRequested() + { + checkState(state == State.LOOKUP_SOURCE_BUILT); + verify(lookupSourceNotNeeded.isPresent()); + if (!lookupSourceNotNeeded.get().isDone()) { + return; + } + + index.clear(); + localRevocableMemoryContext.setBytes(0); + localUserMemoryContext.setBytes(index.getEstimatedSize().toBytes()); + lookupSourceSupplier = null; + close(); + } + + private LookupSourceSupplier buildLookupSource() + { + LookupSourceSupplier partition = index.createLookupSourceSupplier(operatorContext.getSession(), hashChannels, preComputedHashChannel, + filterFunctionFactory, sortChannel, searchFunctionFactories, Optional.of(outputChannels), + countChannel, Optional.ofNullable(aggrOnAggregationBuilder)); + hashCollisionsCounter.recordHashCollision(partition.getHashCollisions(), partition.getExpectedHashCollisions()); + checkState(lookupSourceSupplier == null, "lookupSourceSupplier is already set"); + this.lookupSourceSupplier = partition; + return partition; + } + + @Override + public boolean isFinished() + { + if (lookupSourceFactoryDestroyed.isDone()) { + // Finish early when the probe side is empty + close(); + return true; + } + + return state == State.CLOSED; + } + + @Override + public void close() + { + if (state == State.CONSUMING_INPUT) { + closeAggregationBuilder(); + } + if (state == State.CLOSED) { + return; + } + // close() can be called in any state, due for example to query failure, and must clean resource up unconditionally + + lookupSourceSupplier = null; + state = State.CLOSED; + finishMemoryRevoke = finishMemoryRevoke.map(ifPresent -> () -> {}); + + try (Closer closer = Closer.create()) { + closer.register(index::clear); + closer.register(() -> localUserMemoryContext.setBytes(0)); + closer.register(() -> localRevocableMemoryContext.setBytes(0)); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + + @VisibleForTesting + public State getState() + { + return state; + } +} diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java new file mode 100644 index 000000000..5e55770ff --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java @@ -0,0 +1,339 @@ +/* + * 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.prestosql.operator; + +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.ListeningExecutorService; +import io.airlift.units.DataSize; +import io.prestosql.execution.Lifespan; +import io.prestosql.operator.aggregation.AccumulatorFactory; +import io.prestosql.operator.aggregation.partial.PartialAggregationController; +import io.prestosql.spi.plan.AggregationNode; +import io.prestosql.spi.plan.PlanNodeId; +import io.prestosql.spi.plan.Symbol; +import io.prestosql.spi.type.Type; +import io.prestosql.sql.gen.JoinCompiler; +import io.prestosql.sql.gen.JoinFilterFunctionCompiler.JoinFilterFunctionFactory; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalInt; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Verify.verify; +import static java.util.Objects.requireNonNull; + +public class HashBuilderGroupJoinOperatorFactory + implements OperatorFactory +{ + private final int operatorId; + private final PlanNodeId planNodeId; + private final JoinBridgeManager lookupSourceFactoryManager; + private final List outputChannels; + private final List hashChannels; + private final OptionalInt preComputedHashChannel; + private final Optional filterFunctionFactory; + private final Optional sortChannel; + private final Optional countChannel; + private final List searchFunctionFactories; + private final PagesIndex.Factory pagesIndexFactory; + private final int expectedPositions; + private final boolean spillEnabled; + private final Map partitionIndexManager = new HashMap<>(); + private boolean closed; + private boolean spillToHdfsEnabled; + private final GroupJoinAggregator aggrOnAggrfactory; + private final GroupJoinAggregator aggrfactory; + private final List buildFinalOutputSymbols; + private final List buildFinalOutputChannels; + + private final ListeningExecutorService executor; + + public static Builder builder() + { + return new Builder(); + } + + public HashBuilderGroupJoinOperatorFactory( + int operatorId, + PlanNodeId planNodeId, + JoinBridgeManager lookupSourceFactoryManager, + List outputChannels, + List hashChannels, + OptionalInt preComputedHashChannel, + Optional filterFunctionFactory, + Optional sortChannel, + Optional countChannel, + List searchFunctionFactories, + int expectedPositions, + PagesIndex.Factory pagesIndexFactory, + boolean spillEnabled, + boolean spillToHdfsEnabled, + GroupJoinAggregator aggrfactory, + GroupJoinAggregator aggrOnAggrfactory, + List buildFinalOutputSymbols, + List buildFinalOutputChannels, + ListeningExecutorService executor) + { + this.operatorId = operatorId; + this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); + requireNonNull(sortChannel, "sortChannel can not be null"); + requireNonNull(searchFunctionFactories, "searchFunctionFactories is null"); + checkArgument(sortChannel.isPresent() != searchFunctionFactories.isEmpty(), "both or none sortChannel and searchFunctionFactories must be set"); + this.lookupSourceFactoryManager = requireNonNull(lookupSourceFactoryManager, "lookupSourceFactoryManager is null"); + + this.outputChannels = ImmutableList.copyOf(requireNonNull(outputChannels, "outputChannels is null")); + this.hashChannels = ImmutableList.copyOf(requireNonNull(hashChannels, "hashChannels is null")); + this.preComputedHashChannel = requireNonNull(preComputedHashChannel, "preComputedHashChannel is null"); + this.filterFunctionFactory = requireNonNull(filterFunctionFactory, "filterFunctionFactory is null"); + this.sortChannel = sortChannel; + this.countChannel = countChannel; + this.searchFunctionFactories = ImmutableList.copyOf(searchFunctionFactories); + this.pagesIndexFactory = requireNonNull(pagesIndexFactory, "pagesIndexFactory is null"); + this.spillEnabled = spillEnabled; + this.spillToHdfsEnabled = spillToHdfsEnabled; + this.expectedPositions = expectedPositions; + + this.aggrfactory = aggrfactory; + this.aggrOnAggrfactory = aggrOnAggrfactory; + + this.buildFinalOutputSymbols = buildFinalOutputSymbols; + this.buildFinalOutputChannels = buildFinalOutputChannels; + this.executor = executor; + } + + @Override + public Operator createOperator(DriverContext driverContext) + { + checkState(!closed, "Factory is already closed"); + OperatorContext addOperatorContext = driverContext.addOperatorContext(operatorId, planNodeId, HashBuilderGroupJoinOperator.class.getSimpleName()); + + PartitionedLookupSourceFactory partitionedLookupSourceFactory = this.lookupSourceFactoryManager.getJoinBridge(driverContext.getLifespan()); + int incrementPartitionIndex = getAndIncrementPartitionIndex(driverContext.getLifespan()); + // Snapshot: make driver ID and source/partition index the same, to ensure consistency before and after resuming. + // LocalExchangeSourceOperator also uses the same mechanism to ensure consistency. + if (addOperatorContext.isSnapshotEnabled()) { + incrementPartitionIndex = driverContext.getDriverId(); + } + verify(incrementPartitionIndex < partitionedLookupSourceFactory.partitions()); + return new HashBuilderGroupJoinOperator( + addOperatorContext, + partitionedLookupSourceFactory, + incrementPartitionIndex, + outputChannels, + hashChannels, + preComputedHashChannel, + filterFunctionFactory, + sortChannel, + countChannel, + searchFunctionFactories, + expectedPositions, + pagesIndexFactory, + spillEnabled, + spillToHdfsEnabled, + aggrfactory, + aggrOnAggrfactory, + buildFinalOutputSymbols, + buildFinalOutputChannels, + executor); + } + + @Override + public void noMoreOperators() + { + closed = true; + } + + @Override + public OperatorFactory duplicate() + { + throw new UnsupportedOperationException("Parallel hash build can not be duplicated"); + } + + private int getAndIncrementPartitionIndex(Lifespan lifespan) + { + return partitionIndexManager.compute(lifespan, (k, v) -> v == null ? 1 : v + 1) - 1; + } + + public static class Builder + { + private GroupJoinAggregator aggrOnAggrfactory; + private GroupJoinAggregator aggrfactory; + private List buildFinalOutputSymbols; + private List buildFinalOutputChannels; + + private int operatorId; + private PlanNodeId planNodeId; + private JoinBridgeManager lookupSourceFactoryManager; + private List outputChannels; + private List hashChannels; + private OptionalInt preComputedHashChannel; + private Optional filterFunctionFactory; + private Optional sortChannel; + private Optional countChannel; + private List searchFunctionFactories; + private int expectedPositions; + private PagesIndex.Factory pagesIndexFactory; + private boolean spillEnabled; + private boolean spillToHdfsEnabled; + + private ListeningExecutorService executor; + + public Builder() + { + } + + public Builder withExecutor(ListeningExecutorService executor) + { + this.executor = executor; + return this; + } + + public Builder withJoinInfo(int operatorId, + PlanNodeId planNodeId, + JoinBridgeManager lookupSourceFactoryManager, + List outputChannels, + List hashChannels, + OptionalInt preComputedHashChannel, + Optional filterFunctionFactory, + Optional sortChannel, + Optional countChannel, + List searchFunctionFactories, + int expectedPositions, + PagesIndex.Factory pagesIndexFactory, + boolean spillEnabled, + boolean spillToHdfsEnabled) + { + this.operatorId = operatorId; + this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); + requireNonNull(sortChannel, "sortChannel can not be null"); + requireNonNull(searchFunctionFactories, "searchFunctionFactories is null"); + checkArgument(sortChannel.isPresent() != searchFunctionFactories.isEmpty(), "both or none sortChannel and searchFunctionFactories must be set"); + this.lookupSourceFactoryManager = requireNonNull(lookupSourceFactoryManager, "lookupSourceFactoryManager is null"); + + this.outputChannels = ImmutableList.copyOf(requireNonNull(outputChannels, "outputChannels is null")); + this.hashChannels = ImmutableList.copyOf(requireNonNull(hashChannels, "hashChannels is null")); + this.preComputedHashChannel = requireNonNull(preComputedHashChannel, "preComputedHashChannel is null"); + this.filterFunctionFactory = requireNonNull(filterFunctionFactory, "filterFunctionFactory is null"); + this.sortChannel = sortChannel; + this.countChannel = countChannel; + this.searchFunctionFactories = ImmutableList.copyOf(searchFunctionFactories); + this.pagesIndexFactory = requireNonNull(pagesIndexFactory, "pagesIndexFactory is null"); + this.spillEnabled = spillEnabled; + this.spillToHdfsEnabled = spillToHdfsEnabled; + this.expectedPositions = expectedPositions; + return this; + } + + public Builder withBuildOutputInfo(List buildFinalOutputSymbols, + List buildFinalOutputChannels) + { + this.buildFinalOutputChannels = ImmutableList.copyOf(buildFinalOutputChannels); + this.buildFinalOutputSymbols = ImmutableList.copyOf(buildFinalOutputSymbols); + return this; + } + + public Builder withAggrOnAggrFactory(List groupByTypes, + List groupByChannels, + List globalAggregationGroupIds, + AggregationNode.Step step, + List accumulatorFactories, + Optional hashChannel, + Optional groupIdChannel, + int expectedGroups, + Optional maxPartialMemory, + JoinCompiler joinCompiler, + boolean useSystemMemory, + Optional partialAggregationController, + boolean produceDefaultOutput) + { + aggrOnAggrfactory = new GroupJoinAggregator(hashChannel, + groupIdChannel, + accumulatorFactories, + groupByTypes, + groupByChannels, + globalAggregationGroupIds, + step, + expectedGroups, + maxPartialMemory, + joinCompiler, + useSystemMemory, + partialAggregationController, + produceDefaultOutput); + return this; + } + + public Builder withAggrFactory(List groupByTypes, + List groupByChannels, + List globalAggregationGroupIds, + AggregationNode.Step step, + List accumulatorFactories, + Optional hashChannel, + Optional groupIdChannel, + int expectedGroups, + Optional maxPartialMemory, + JoinCompiler joinCompiler, + boolean useSystemMemory, + Optional partialAggregationController, + boolean produceDefaultOutput) + { + aggrfactory = new GroupJoinAggregator(hashChannel, + groupIdChannel, + accumulatorFactories, + groupByTypes, + groupByChannels, + globalAggregationGroupIds, + step, + expectedGroups, + maxPartialMemory, + joinCompiler, + useSystemMemory, + partialAggregationController, + produceDefaultOutput); + return this; + } + + public HashBuilderGroupJoinOperatorFactory build() + { + requireNonNull(aggrfactory, "aggrfactory is null"); + requireNonNull(aggrOnAggrfactory, "aggrOnAggrfactory is null"); + requireNonNull(executor, "executor is null"); + requireNonNull(buildFinalOutputChannels, "buildFinalOutputChannels is null"); + requireNonNull(hashChannels, "hashChannels is null"); + return new HashBuilderGroupJoinOperatorFactory( + operatorId, + planNodeId, + lookupSourceFactoryManager, + outputChannels, + hashChannels, + preComputedHashChannel, + filterFunctionFactory, + sortChannel, + countChannel, + searchFunctionFactories, + expectedPositions, + pagesIndexFactory, + spillEnabled, + spillToHdfsEnabled, + aggrfactory, + aggrOnAggrfactory, + buildFinalOutputSymbols, + buildFinalOutputChannels, + executor); + } + } +} -- Gitee From 19ab978db05eec287d4c4e94f645ce8d5c8d0e53 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Thu, 23 Feb 2023 11:34:20 +0530 Subject: [PATCH 12/36] Group Join Hash Builder Code fixes. --- .../HashBuilderGroupJoinOperator.java | 24 ++++++------------- .../HashBuilderGroupJoinOperatorFactory.java | 23 ++++-------------- ...MemoryHashAggregationBuilderWithReset.java | 8 +++---- 3 files changed, 16 insertions(+), 39 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java index 84500714a..6388a178e 100644 --- a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java @@ -134,8 +134,7 @@ public class HashBuilderGroupJoinOperator GroupJoinAggregator aggregator, GroupJoinAggregator aggrOnAggregator, List buildFinalOutputSymbols, - List buildFinalOutputChannels, - ListeningExecutorService executor) + List buildFinalOutputChannels) { requireNonNull(pagesIndexFactory, "pagesIndexFactory is null"); this.operatorContext = operatorContext; @@ -161,9 +160,6 @@ public class HashBuilderGroupJoinOperator this.spillToHdfsEnabled = spillToHdfsEnabled; this.expectedValues = expectedPositions * 10L; - /*this.aggrFinishInProgress = SettableFuture.create(); - this.aggrFinishInProgress.set(null);*/ - requireNonNull(operatorContext, "operatorContext is null"); this.memoryContext = operatorContext.localUserMemoryContext(); if (aggregator.isUseSystemMemory()) { @@ -186,13 +182,13 @@ public class HashBuilderGroupJoinOperator public boolean needsInput() { if (state == State.CONSUMING_INPUT) { - if (/*aggregationFinishing || */outputPages != null) { + if (outputPages != null) { return false; } else if (aggregationBuilder != null && aggregationBuilder.isFull()) { return false; } - else if (lookupSourceFactoryDestroyed.isDone()/*aggrFinishInProgress.isDone()*/) { + else if (lookupSourceFactoryDestroyed.isDone()) { return false; } else { @@ -215,11 +211,8 @@ public class HashBuilderGroupJoinOperator public void addInput(Page page) { requireNonNull(page, "page is null"); - checkState(unfinishedAggrWork == null, "Operator has unfinished work"); - //checkState(!aggregationFinishing, "Operator is already finishing"); checkState(state == State.CONSUMING_INPUT, "Operator is not in Consuming Input state"); - /*aggregationInputProcessed = true;*/ if (aggregationBuilder == null) { createAggregationBuilder(); @@ -325,21 +318,19 @@ public class HashBuilderGroupJoinOperator } if (outputPages == null) { - //if (state == State.AGGR_FINISHING/*aggregationFinishing*/) { - if (/*!aggregationInputProcessed && */aggregator.isProduceDefaultOutput()) { + if (aggregator.isProduceDefaultOutput()) { // global aggregations always generate an output row with the default aggregation output (e.g. 0 for COUNT, NULL for SUM) - state = State.AGGR_FINISHED; //aggregationFinished = true; + state = State.AGGR_FINISHED; return aggregator.getGlobalAggregationOutput(); } if (aggregationBuilder == null) { - state = State.AGGR_FINISHED; //aggregationFinished = true; + state = State.AGGR_FINISHED; return null; } - //} // only flush if we are finishing or the aggregation builder is full - if (/*!aggregationFinishing && */(/*aggregationBuilder == null || */!aggregationBuilder.isFull())) { + if (!aggregationBuilder.isFull()) { return null; } @@ -380,7 +371,6 @@ public class HashBuilderGroupJoinOperator @Override public void finish() { - /*aggregationFinishing = true;*/ if (state == State.CONSUMING_INPUT) { state = State.AGGR_FINISHING; } diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java index 5e55770ff..5dadb5c8a 100644 --- a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java @@ -61,8 +61,6 @@ public class HashBuilderGroupJoinOperatorFactory private final List buildFinalOutputSymbols; private final List buildFinalOutputChannels; - private final ListeningExecutorService executor; - public static Builder builder() { return new Builder(); @@ -86,8 +84,7 @@ public class HashBuilderGroupJoinOperatorFactory GroupJoinAggregator aggrfactory, GroupJoinAggregator aggrOnAggrfactory, List buildFinalOutputSymbols, - List buildFinalOutputChannels, - ListeningExecutorService executor) + List buildFinalOutputChannels) { this.operatorId = operatorId; this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); @@ -113,7 +110,6 @@ public class HashBuilderGroupJoinOperatorFactory this.buildFinalOutputSymbols = buildFinalOutputSymbols; this.buildFinalOutputChannels = buildFinalOutputChannels; - this.executor = executor; } @Override @@ -148,8 +144,8 @@ public class HashBuilderGroupJoinOperatorFactory aggrfactory, aggrOnAggrfactory, buildFinalOutputSymbols, - buildFinalOutputChannels, - executor); + buildFinalOutputChannels + ); } @Override @@ -191,18 +187,10 @@ public class HashBuilderGroupJoinOperatorFactory private boolean spillEnabled; private boolean spillToHdfsEnabled; - private ListeningExecutorService executor; - public Builder() { } - public Builder withExecutor(ListeningExecutorService executor) - { - this.executor = executor; - return this; - } - public Builder withJoinInfo(int operatorId, PlanNodeId planNodeId, JoinBridgeManager lookupSourceFactoryManager, @@ -311,7 +299,6 @@ public class HashBuilderGroupJoinOperatorFactory { requireNonNull(aggrfactory, "aggrfactory is null"); requireNonNull(aggrOnAggrfactory, "aggrOnAggrfactory is null"); - requireNonNull(executor, "executor is null"); requireNonNull(buildFinalOutputChannels, "buildFinalOutputChannels is null"); requireNonNull(hashChannels, "hashChannels is null"); return new HashBuilderGroupJoinOperatorFactory( @@ -332,8 +319,8 @@ public class HashBuilderGroupJoinOperatorFactory aggrfactory, aggrOnAggrfactory, buildFinalOutputSymbols, - buildFinalOutputChannels, - executor); + buildFinalOutputChannels + ); } } } diff --git a/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryHashAggregationBuilderWithReset.java b/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryHashAggregationBuilderWithReset.java index 7a8688500..cccad19b6 100644 --- a/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryHashAggregationBuilderWithReset.java +++ b/presto-main/src/main/java/io/prestosql/operator/aggregation/builder/InMemoryHashAggregationBuilderWithReset.java @@ -32,7 +32,7 @@ import static io.prestosql.operator.GroupByHash.createGroupByHash; public class InMemoryHashAggregationBuilderWithReset extends InMemoryHashAggregationBuilder { - private final List accumulatorFactoriesm; + private final List accumulatorFactories; private final AggregationNode.Step step; private final int expectedGroups; private final List groupByTypes; @@ -66,7 +66,7 @@ public class InMemoryHashAggregationBuilderWithReset joinCompiler, updateMemory, AggregationNode.AggregationType.HASH); - this.accumulatorFactoriesm = accumulatorFactories; + this.accumulatorFactories = accumulatorFactories; this.step = AggregationNode.Step.partialInput(step); this.expectedGroups = expectedGroups; this.groupByTypes = groupByTypes; @@ -101,14 +101,14 @@ public class InMemoryHashAggregationBuilderWithReset @Override public int getAggregationCount() { - return accumulatorFactoriesm.size(); + return accumulatorFactories.size(); } @Override public AggregationBuilder duplicate() { return new InMemoryHashAggregationBuilderWithReset( - accumulatorFactoriesm, + accumulatorFactories, step, expectedGroups, groupByTypes, -- Gitee From 3ab941c6e28eb79207bb27ba301423878486c600 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Thu, 23 Feb 2023 15:09:48 +0530 Subject: [PATCH 13/36] Group Join Lookup Code operator factory and related handling in LocalExecutionPlanner for builder and lookup. --- .../operator/GroupJoinAggregator.java | 29 ++ .../HashBuilderGroupJoinOperator.java | 1 - .../HashBuilderGroupJoinOperatorFactory.java | 23 +- .../LookupGroupJoinOperatorFactory.java | 383 +++++++++++++++ .../operator/LookupJoinOperators.java | 41 ++ .../sql/planner/LocalDynamicFilter.java | 47 ++ .../sql/planner/LocalExecutionPlanner.java | 455 +++++++++++++++++- .../prestosql/utils/DynamicFilterUtils.java | 10 + 8 files changed, 966 insertions(+), 23 deletions(-) create mode 100644 presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperatorFactory.java diff --git a/presto-main/src/main/java/io/prestosql/operator/GroupJoinAggregator.java b/presto-main/src/main/java/io/prestosql/operator/GroupJoinAggregator.java index dba15cedf..0ec4ba177 100644 --- a/presto-main/src/main/java/io/prestosql/operator/GroupJoinAggregator.java +++ b/presto-main/src/main/java/io/prestosql/operator/GroupJoinAggregator.java @@ -225,4 +225,33 @@ public class GroupJoinAggregator { return accumulatorFactories.stream().anyMatch(AccumulatorFactory::hasDistinct); } + + public static GroupJoinAggregator buildGroupJoinAggregator(List groupByTypes, + List groupByChannels, + List globalAggregationGroupIds, + AggregationNode.Step step, + List accumulatorFactories, + Optional hashChannel, + Optional groupIdChannel, + int expectedGroups, + Optional maxPartialMemory, + JoinCompiler joinCompiler, + boolean useSystemMemory, + Optional partialAggregationController, + boolean produceDefaultOutput) + { + return new GroupJoinAggregator(hashChannel, + groupIdChannel, + accumulatorFactories, + groupByTypes, + groupByChannels, + globalAggregationGroupIds, + step, + expectedGroups, + maxPartialMemory, + joinCompiler, + useSystemMemory, + partialAggregationController, + produceDefaultOutput); + } } diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java index 6388a178e..d8810f8dd 100644 --- a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java @@ -16,7 +16,6 @@ package io.prestosql.operator; import com.google.common.annotations.VisibleForTesting; import com.google.common.io.Closer; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; import io.prestosql.memory.context.LocalMemoryContext; import io.prestosql.operator.aggregation.builder.AggregationBuilder; import io.prestosql.operator.aggregation.builder.InMemoryHashAggregationBuilder; diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java index 5dadb5c8a..cc38cf10f 100644 --- a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java @@ -14,7 +14,6 @@ package io.prestosql.operator; import com.google.common.collect.ImmutableList; -import com.google.common.util.concurrent.ListeningExecutorService; import io.airlift.units.DataSize; import io.prestosql.execution.Lifespan; import io.prestosql.operator.aggregation.AccumulatorFactory; @@ -144,8 +143,7 @@ public class HashBuilderGroupJoinOperatorFactory aggrfactory, aggrOnAggrfactory, buildFinalOutputSymbols, - buildFinalOutputChannels - ); + buildFinalOutputChannels); } @Override @@ -249,13 +247,13 @@ public class HashBuilderGroupJoinOperatorFactory Optional partialAggregationController, boolean produceDefaultOutput) { - aggrOnAggrfactory = new GroupJoinAggregator(hashChannel, - groupIdChannel, - accumulatorFactories, - groupByTypes, + aggrOnAggrfactory = GroupJoinAggregator.buildGroupJoinAggregator(groupByTypes, groupByChannels, globalAggregationGroupIds, step, + accumulatorFactories, + hashChannel, + groupIdChannel, expectedGroups, maxPartialMemory, joinCompiler, @@ -279,13 +277,13 @@ public class HashBuilderGroupJoinOperatorFactory Optional partialAggregationController, boolean produceDefaultOutput) { - aggrfactory = new GroupJoinAggregator(hashChannel, - groupIdChannel, - accumulatorFactories, - groupByTypes, + aggrfactory = GroupJoinAggregator.buildGroupJoinAggregator(groupByTypes, groupByChannels, globalAggregationGroupIds, step, + accumulatorFactories, + hashChannel, + groupIdChannel, expectedGroups, maxPartialMemory, joinCompiler, @@ -319,8 +317,7 @@ public class HashBuilderGroupJoinOperatorFactory aggrfactory, aggrOnAggrfactory, buildFinalOutputSymbols, - buildFinalOutputChannels - ); + buildFinalOutputChannels); } } } diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperatorFactory.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperatorFactory.java new file mode 100644 index 000000000..8fd073ce5 --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperatorFactory.java @@ -0,0 +1,383 @@ +/* + * 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.prestosql.operator; + +import com.google.common.collect.ImmutableList; +import io.airlift.units.DataSize; +import io.prestosql.execution.Lifespan; +import io.prestosql.operator.GroupJoinProbe.GroupJoinProbeFactory; +import io.prestosql.operator.LookupJoinOperators.JoinType; +import io.prestosql.operator.aggregation.AccumulatorFactory; +import io.prestosql.operator.aggregation.partial.PartialAggregationController; +import io.prestosql.spi.plan.AggregationNode; +import io.prestosql.spi.plan.PlanNodeId; +import io.prestosql.spi.plan.Symbol; +import io.prestosql.spi.type.Type; +import io.prestosql.spiller.PartitioningSpillerFactory; +import io.prestosql.sql.gen.JoinCompiler; + +import java.util.List; +import java.util.Optional; +import java.util.OptionalInt; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static io.prestosql.SystemSessionProperties.isSpillToHdfsEnabled; +import static io.prestosql.operator.LookupJoinOperators.JoinType.INNER; +import static java.util.Objects.requireNonNull; + +public class LookupGroupJoinOperatorFactory + implements JoinOperatorFactory +{ + private final int operatorId; + private final PlanNodeId planNodeId; + private final List probeTypes; + private final List outputTypes; + private final List buildTypes; + private final LookupJoinOperators.JoinType joinType; + private final GroupJoinProbeFactory joinProbeFactory; + private final Optional outerOperatorFactoryResult; + private final JoinBridgeManager joinBridgeManager; + private final OptionalInt totalOperatorsCount; + private final HashGenerator probeHashGenerator; + private final boolean forked; + private final PartitioningSpillerFactory partitioningSpillerFactory; + private final List probeFinalOutputSymbols; + private final List probeFinalOutputChannels; + private final List buildFinalOutputChannels; + private boolean closed; + private final GroupJoinAggregator aggrOnAggrfactory; + private final GroupJoinAggregator aggrfactory; + + public static Builder builder() + { + return new Builder(); + } + + public LookupGroupJoinOperatorFactory( + int operatorId, + PlanNodeId planNodeId, + JoinBridgeManager lookupSourceFactoryManager, + List probeTypes, + List outputTypes, + List buildTypes, + JoinType joinType, + GroupJoinProbeFactory joinProbeFactory, + OptionalInt totalOperatorsCount, + List probeJoinChannels, + OptionalInt probeHashChannel, + PartitioningSpillerFactory partitioningSpillerFactory, + boolean forked, + GroupJoinAggregator aggrfactory, + GroupJoinAggregator aggrOnAggrfactory, + List probeFinalOutputSymbols, + List probeFinalOutputChannels, + List buildFinalOutputChannels) + { + this.forked = forked; + this.operatorId = operatorId; + this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); + this.probeTypes = ImmutableList.copyOf(requireNonNull(probeTypes, "probeTypes is null")); + this.outputTypes = ImmutableList.copyOf(requireNonNull(outputTypes, "outputTypes is null")); + this.buildTypes = ImmutableList.copyOf(requireNonNull(buildTypes, "buildTypes is null")); + this.joinType = requireNonNull(joinType, "joinType is null"); + this.joinProbeFactory = requireNonNull(joinProbeFactory, "joinProbeFactory is null"); + + this.joinBridgeManager = lookupSourceFactoryManager; + joinBridgeManager.incrementProbeFactoryCount(); + checkArgument(joinType == INNER, "joinType is not INNER"); + this.outerOperatorFactoryResult = Optional.empty(); + this.totalOperatorsCount = requireNonNull(totalOperatorsCount, "totalOperatorsCount is null"); + + requireNonNull(probeHashChannel, "probeHashChannel is null"); + if (probeHashChannel.isPresent()) { + this.probeHashGenerator = new PrecomputedHashGenerator(probeHashChannel.getAsInt()); + } + else { + requireNonNull(probeJoinChannels, "probeJoinChannels is null"); + List hashTypes = probeJoinChannels.stream() + .map(probeTypes::get) + .collect(toImmutableList()); + this.probeHashGenerator = new InterpretedHashGenerator(hashTypes, probeJoinChannels); + } + this.partitioningSpillerFactory = partitioningSpillerFactory; + + this.aggrfactory = aggrfactory; + this.aggrOnAggrfactory = aggrOnAggrfactory; + + this.probeFinalOutputSymbols = probeFinalOutputSymbols; + this.probeFinalOutputChannels = probeFinalOutputChannels; + this.buildFinalOutputChannels = buildFinalOutputChannels; + } + + private LookupGroupJoinOperatorFactory(LookupGroupJoinOperatorFactory other) + { + requireNonNull(other, "other is null"); + checkArgument(!other.closed, "cannot duplicated closed OperatorFactory"); + + this.forked = true; + this.operatorId = other.operatorId; + this.planNodeId = other.planNodeId; + this.probeTypes = other.probeTypes; + this.outputTypes = other.outputTypes; + this.buildTypes = other.buildTypes; + this.joinType = other.joinType; + this.joinProbeFactory = other.joinProbeFactory; + this.joinBridgeManager = other.joinBridgeManager; + this.outerOperatorFactoryResult = other.outerOperatorFactoryResult; + this.totalOperatorsCount = other.totalOperatorsCount; + this.probeHashGenerator = other.probeHashGenerator; + this.partitioningSpillerFactory = other.partitioningSpillerFactory; + + this.aggrfactory = other.aggrfactory; + this.aggrOnAggrfactory = other.aggrOnAggrfactory; + + this.probeFinalOutputChannels = other.probeFinalOutputChannels; + this.probeFinalOutputSymbols = other.probeFinalOutputSymbols; + this.buildFinalOutputChannels = other.buildFinalOutputChannels; + + this.closed = false; + this.joinBridgeManager.incrementProbeFactoryCount(); + } + + public int getOperatorId() + { + return operatorId; + } + + @Override + public Operator createOperator(DriverContext driverContext) + { + checkState(!closed, "Factory is already closed"); + LookupSourceFactory lookupSourceFactory = joinBridgeManager.getJoinBridge(driverContext.getLifespan()); + + OperatorContext operatorContext = driverContext.addOperatorContext(operatorId, planNodeId, LookupGroupJoinOperator.class.getSimpleName()); + lookupSourceFactory.setTaskContext(driverContext.getPipelineContext().getTaskContext()); + + joinBridgeManager.probeOperatorCreated(driverContext.getLifespan()); + return new LookupGroupJoinOperator( + operatorContext, + forked, + probeTypes, + outputTypes, + buildTypes, + joinType, + lookupSourceFactory, + joinProbeFactory, + () -> joinBridgeManager.probeOperatorClosed(driverContext.getLifespan()), + totalOperatorsCount, + probeHashGenerator, + partitioningSpillerFactory, + () -> joinBridgeManager.probeOperatorFinished(driverContext.getLifespan()), + isSpillToHdfsEnabled(driverContext.getPipelineContext().getTaskContext().getSession()), + aggrfactory, + aggrOnAggrfactory, + probeFinalOutputSymbols, + probeFinalOutputChannels, + buildFinalOutputChannels); + } + + @Override + public void noMoreOperators() + { + checkState(!closed); + closed = true; + joinBridgeManager.probeOperatorFactoryClosedForAllLifespans(); + } + + @Override + public void noMoreOperators(Lifespan lifespan) + { + joinBridgeManager.probeOperatorFactoryClosed(lifespan); + } + + @Override + public OperatorFactory duplicate() + { + return new LookupGroupJoinOperatorFactory(this); + } + + @Override + public Optional createOuterOperatorFactory() + { + return outerOperatorFactoryResult; + } + + public LookupSourceFactory getLookupSourceFactory(Lifespan lifespan) + { + return joinBridgeManager.getJoinBridge(lifespan); + } + + public static class Builder + { + private GroupJoinAggregator aggrOnAggrfactory; + private GroupJoinAggregator aggrfactory; + private List probeFinalOutputSymbols; + private List probeFinalOutputChannels; + private List buildFinalOutputChannels; + + private int operatorId; + private PlanNodeId planNodeId; + private List probeTypes; + private List outputTypes; + private List buildTypes; + private LookupJoinOperators.JoinType joinType; + private GroupJoinProbeFactory joinProbeFactory; + private JoinBridgeManager joinBridgeManager; + private OptionalInt totalOperatorsCount; + private boolean forked; + private PartitioningSpillerFactory partitioningSpillerFactory; + private OptionalInt probeHashChannel; + private List probeJoinChannels; + + public Builder() + { + } + + public Builder withJoinInfo(int operatorId, + PlanNodeId planNodeId, + JoinBridgeManager lookupSourceFactoryManager, + List probeTypes, + List outputTypes, + List buildTypes, + JoinType joinType, + GroupJoinProbeFactory joinProbeFactory, + OptionalInt totalOperatorsCount, + List probeJoinChannels, + OptionalInt probeHashChannel, + PartitioningSpillerFactory partitioningSpillerFactory, + boolean forked) + { + this.operatorId = operatorId; + this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); + this.forked = forked; + this.probeTypes = ImmutableList.copyOf(requireNonNull(probeTypes, "probeTypes is null")); + this.outputTypes = ImmutableList.copyOf(requireNonNull(outputTypes, "outputTypes is null")); + this.buildTypes = ImmutableList.copyOf(requireNonNull(buildTypes, "buildTypes is null")); + this.joinType = requireNonNull(joinType, "joinType is null"); + this.joinProbeFactory = requireNonNull(joinProbeFactory, "joinProbeFactory is null"); + this.joinBridgeManager = lookupSourceFactoryManager; + checkArgument(joinType == INNER, "joinType is not INNER"); + this.totalOperatorsCount = requireNonNull(totalOperatorsCount, "totalOperatorsCount is null"); + requireNonNull(probeHashChannel, "probeHashChannel is null"); + this.probeHashChannel = probeHashChannel; + this.probeJoinChannels = probeJoinChannels; + this.partitioningSpillerFactory = partitioningSpillerFactory; + return this; + } + + public Builder withProbeOutputInfo(List probeFinalOutputSymbols, + List probeFinalOutputChannels) + { + this.probeFinalOutputChannels = ImmutableList.copyOf(probeFinalOutputChannels); + this.probeFinalOutputSymbols = ImmutableList.copyOf(probeFinalOutputSymbols); + return this; + } + + public Builder withBuildOutputInfo(List buildFinalOutputChannels) + { + this.buildFinalOutputChannels = ImmutableList.copyOf(buildFinalOutputChannels); + return this; + } + + public Builder withAggrOnAggrFactory(List groupByTypes, + List groupByChannels, + List globalAggregationGroupIds, + AggregationNode.Step step, + List accumulatorFactories, + Optional hashChannel, + Optional groupIdChannel, + int expectedGroups, + Optional maxPartialMemory, + JoinCompiler joinCompiler, + boolean useSystemMemory, + Optional partialAggregationController, + boolean produceDefaultOutput) + { + aggrOnAggrfactory = new GroupJoinAggregator(hashChannel, + groupIdChannel, + accumulatorFactories, + groupByTypes, + groupByChannels, + globalAggregationGroupIds, + step, + expectedGroups, + maxPartialMemory, + joinCompiler, + useSystemMemory, + partialAggregationController, + produceDefaultOutput); + return this; + } + + public Builder withAggrFactory(List groupByTypes, + List groupByChannels, + List globalAggregationGroupIds, + AggregationNode.Step step, + List accumulatorFactories, + Optional hashChannel, + Optional groupIdChannel, + int expectedGroups, + Optional maxPartialMemory, + JoinCompiler joinCompiler, + boolean useSystemMemory, + Optional partialAggregationController, + boolean produceDefaultOutput) + { + aggrfactory = new GroupJoinAggregator(hashChannel, + groupIdChannel, + accumulatorFactories, + groupByTypes, + groupByChannels, + globalAggregationGroupIds, + step, + expectedGroups, + maxPartialMemory, + joinCompiler, + useSystemMemory, + partialAggregationController, + produceDefaultOutput); + return this; + } + + public LookupGroupJoinOperatorFactory build() + { + requireNonNull(aggrfactory, "aggrfactory is null"); + requireNonNull(aggrOnAggrfactory, "aggrOnAggrfactory is null"); + requireNonNull(buildFinalOutputChannels, "buildFinalOutputChannels is null"); + requireNonNull(probeFinalOutputChannels, "buildFinalOutputChannels is null"); + requireNonNull(joinProbeFactory, "joinProbeFactory is null"); + return new LookupGroupJoinOperatorFactory( + operatorId, + planNodeId, + joinBridgeManager, + probeTypes, + outputTypes, + buildTypes, + joinType, + joinProbeFactory, + totalOperatorsCount, + probeJoinChannels, + probeHashChannel, + partitioningSpillerFactory, + forked, + aggrfactory, + aggrOnAggrfactory, + probeFinalOutputSymbols, + probeFinalOutputChannels, + buildFinalOutputChannels); + } + } +} diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupJoinOperators.java b/presto-main/src/main/java/io/prestosql/operator/LookupJoinOperators.java index 09779bf15..0320cec77 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupJoinOperators.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupJoinOperators.java @@ -15,6 +15,7 @@ package io.prestosql.operator; import io.prestosql.operator.JoinProbe.JoinProbeFactory; import io.prestosql.spi.plan.PlanNodeId; +import io.prestosql.spi.plan.Symbol; import io.prestosql.spi.type.Type; import io.prestosql.spiller.PartitioningSpillerFactory; @@ -99,4 +100,44 @@ public class LookupJoinOperators probeHashChannel, partitioningSpillerFactory); } + + public OperatorFactory groupInnerJoin( + int operatorId, + PlanNodeId planNodeId, + JoinBridgeManager lookupSourceFactoryManager, + List probeTypes, + List probeJoinChannels, + OptionalInt probeHashChannel, + OptionalInt probeCountChannel, + List probeOutputChannels, + OptionalInt totalOperatorsCount, + PartitioningSpillerFactory partitioningSpillerFactory, + boolean forked, + GroupJoinAggregator aggrfactory, + GroupJoinAggregator aggrOnAggrfactory, + List probeFinalOutputSymbols, + List probeFinalOutputChannels, + List buildFinalOutputChannels, + List outputTypes) + { + return new LookupGroupJoinOperatorFactory( + operatorId, + planNodeId, + lookupSourceFactoryManager, + probeTypes, + outputTypes, + lookupSourceFactoryManager.getBuildOutputTypes(), + JoinType.INNER, + new GroupJoinProbe.GroupJoinProbeFactory(probeOutputChannels.stream().mapToInt(i -> i).toArray(), probeJoinChannels, probeHashChannel, probeCountChannel), + totalOperatorsCount, + probeJoinChannels, + probeHashChannel, + partitioningSpillerFactory, + forked, + aggrfactory, + aggrOnAggrfactory, + probeFinalOutputSymbols, + probeFinalOutputChannels, + buildFinalOutputChannels); + } } diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/LocalDynamicFilter.java b/presto-main/src/main/java/io/prestosql/sql/planner/LocalDynamicFilter.java index bb4dab8dd..0e9f02615 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/LocalDynamicFilter.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/LocalDynamicFilter.java @@ -28,6 +28,7 @@ import io.prestosql.spi.dynamicfilter.BloomFilterDynamicFilter; import io.prestosql.spi.dynamicfilter.DynamicFilter; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.Symbol; import io.prestosql.spi.predicate.TupleDomain; @@ -163,6 +164,52 @@ public class LocalDynamicFilter return Optional.of(new LocalDynamicFilter(localProbeSymbols, localBuildChannels, partitionCount, localType, session, taskId, stateStoreProvider)); } + public static Optional create(JoinOnAggregationNode planNode, int partitionCount, Session session, TaskId taskId, StateStoreProvider stateStoreProvider) + { + Set joinDynamicFilters = planNode.getDynamicFilters().keySet(); + // Mapping from probe-side dynamic filters' IDs to their matching probe symbols. + Multimap localProbeSymbols = MultimapBuilder.treeKeys().arrayListValues().build(); + PlanNode buildNode; + DynamicFilter.Type localType = DynamicFilter.Type.LOCAL; + + List filterNodes = findFilterNodeInStage(planNode); + if (filterNodes.isEmpty()) { + buildNode = planNode.getRight(); + mapProbeSymbolsFromCriteria(planNode.getDynamicFilters(), localProbeSymbols, planNode.getCriteria()); + localType = DynamicFilter.Type.GLOBAL; + } + else { + buildNode = planNode.getRight(); + for (FilterNode filterNode : filterNodes) { + mapProbeSymbols(filterNode.getPredicate(), joinDynamicFilters, localProbeSymbols); + } + } + + final List buildSideSymbols = buildNode.getOutputSymbols(); + + Map localBuildChannels = planNode + .getDynamicFilters() + .entrySet() + .stream() + // Skip build channels that don't match local probe dynamic filters. + .filter(entry -> localProbeSymbols.containsKey(entry.getKey())) + .collect(toMap( + // Dynamic filter ID + entry -> entry.getKey(), + // Build-side channel index + entry -> { + Symbol buildSymbol = entry.getValue(); + int buildChannelIndex = buildSideSymbols.indexOf(buildSymbol); + verify(buildChannelIndex >= 0); + return buildChannelIndex; + })); + + if (localBuildChannels.isEmpty()) { + return Optional.empty(); + } + return Optional.of(new LocalDynamicFilter(localProbeSymbols, localBuildChannels, partitionCount, localType, session, taskId, stateStoreProvider)); + } + public static Optional create(SemiJoinNode semiJoinNode, Session session, TaskId taskId, StateStoreProvider stateStoreProvider) { if (!semiJoinNode.getDynamicFilterId().isPresent()) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java b/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java index 598a1b9ab..6d6da242b 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java @@ -64,7 +64,9 @@ import io.prestosql.operator.ExchangeOperator.ExchangeOperatorFactory; import io.prestosql.operator.ExplainAnalyzeOperator.ExplainAnalyzeOperatorFactory; import io.prestosql.operator.FilterAndProjectOperator; import io.prestosql.operator.GroupIdOperator; +import io.prestosql.operator.GroupJoinAggregator; import io.prestosql.operator.HashAggregationOperator.HashAggregationOperatorFactory; +import io.prestosql.operator.HashBuilderGroupJoinOperatorFactory; import io.prestosql.operator.HashBuilderOperator.HashBuilderOperatorFactory; import io.prestosql.operator.HashSemiJoinOperator.HashSemiJoinOperatorFactory; import io.prestosql.operator.JoinBridgeManager; @@ -160,6 +162,8 @@ import io.prestosql.spi.plan.CTEScanNode; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.GroupIdNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; +import io.prestosql.spi.plan.JoinOnAggregationNode.JoinInternalAggregation; import io.prestosql.spi.plan.LimitNode; import io.prestosql.spi.plan.MarkDistinctNode; import io.prestosql.spi.plan.OrderingScheme; @@ -331,6 +335,7 @@ import static io.prestosql.spi.util.Reflection.constructorMethodHandle; import static io.prestosql.spiller.PartitioningSpillerFactory.unsupportedPartitioningSpillerFactory; import static io.prestosql.sql.DynamicFilters.extractStaticFilters; import static io.prestosql.sql.gen.LambdaBytecodeGenerator.compileLambdaProvider; +import static io.prestosql.sql.planner.LocalExecutionPlanner.PhysicalOperation.toTypes; import static io.prestosql.sql.planner.RowExpressionInterpreter.Level.OPTIMIZED; import static io.prestosql.sql.planner.SystemPartitioningHandle.COORDINATOR_DISTRIBUTION; import static io.prestosql.sql.planner.SystemPartitioningHandle.FIXED_ARBITRARY_DISTRIBUTION; @@ -2301,6 +2306,25 @@ public class LocalExecutionPlanner } } + @Override + public PhysicalOperation visitJoinOnAggregation(JoinOnAggregationNode node, LocalExecutionPlanContext context) + { + List clauses = node.getCriteria(); + if (!node.getDynamicFilters().isEmpty()) { + log.debug("[GroupJoin] Dynamic filters: %s", node.getDynamicFilters()); + } + + List leftSymbols = Lists.transform(clauses, JoinNode.EquiJoinClause::getLeft); + List rightSymbols = Lists.transform(clauses, JoinNode.EquiJoinClause::getRight); + + switch (node.getType()) { + case INNER: + return createLookupGroupJoin(node, node.getLeft(), leftSymbols, node.getLeftHashSymbol(), node.getRight(), rightSymbols, node.getRightHashSymbol(), context); + default: + throw new UnsupportedOperationException("Unsupported join type: " + node.getType()); + } + } + @Override public PhysicalOperation visitSpatialJoin(SpatialJoinNode node, LocalExecutionPlanContext context) { @@ -2661,6 +2685,36 @@ public class LocalExecutionPlanner return new PhysicalOperation(operator, outputMappings.build(), context, probeSource); } + private PhysicalOperation createLookupGroupJoin(JoinOnAggregationNode node, + PlanNode probeNode, + List probeSymbols, + Optional probeHashSymbol, + PlanNode buildNode, + List buildSymbols, + Optional buildHashSymbol, + LocalExecutionPlanContext context) + { + // Plan probe + PhysicalOperation probeSource = probeNode.accept(this, context); + + // Plan build + ImmutableList.Builder finalOutputBuildTypes = ImmutableList.builder(); + ImmutableList.Builder buildFinalOutputChannels = ImmutableList.builder(); + JoinBridgeManager lookupSourceFactory = + createGroupLookupSourceFactory(node, buildNode, buildSymbols, buildHashSymbol, probeSource, context, finalOutputBuildTypes, buildFinalOutputChannels); + + OperatorFactory operator = createGroupLookupJoin(node, probeSource, probeSymbols, probeHashSymbol, lookupSourceFactory, context, finalOutputBuildTypes.build(), buildFinalOutputChannels.build()); + + ImmutableMap.Builder outputMappings = ImmutableMap.builder(); + List outputSymbols = node.getOutputSymbols(); + for (int i = 0; i < outputSymbols.size(); i++) { + Symbol symbol = outputSymbols.get(i); + outputMappings.put(symbol, i); + } + + return new PhysicalOperation(operator, outputMappings.build(), context, probeSource); + } + protected Optional createDynamicFilter(JoinNode node, LocalExecutionPlanContext context, int partitionCount) { if (!isEnableDynamicFiltering(context.getSession())) { @@ -2680,6 +2734,25 @@ public class LocalExecutionPlanner }); } + protected Optional createDynamicFilter(JoinOnAggregationNode node, LocalExecutionPlanContext context, int partitionCount) + { + if (!isEnableDynamicFiltering(context.getSession())) { + return Optional.empty(); + } + if (node.getDynamicFilters().isEmpty()) { + return Optional.empty(); + } + LocalDynamicFiltersCollector collector = context.getDynamicFiltersCollector(); + return LocalDynamicFilter + .create(node, partitionCount, context.getSession(), context.taskContext.getTaskId(), stateStoreProvider) + .map(filter -> { + // Intersect dynamic filters' predicates when they become ready, + // in order to support multiple join nodes in the same plan fragment. + addSuccessCallback(filter.getDynamicFilterResultFuture(), collector::intersectDynamicFilter); + return filter; + }); + } + private Optional createDynamicFilter(SemiJoinNode node, LocalExecutionPlanContext context) { if (!node.getDynamicFilterId().isPresent()) { @@ -2829,6 +2902,172 @@ public class LocalExecutionPlanner return lookupSourceFactoryManager; } + private JoinBridgeManager createGroupLookupSourceFactory( + JoinOnAggregationNode node, + PlanNode buildNode, + List buildSymbols, + Optional buildHashSymbol, + PhysicalOperation probeSource, + LocalExecutionPlanContext context, + ImmutableList.Builder finalOutputBuildTypes, + ImmutableList.Builder buildFinalOutputChannelsBuilder) + { + JoinInternalAggregation aggregationBuild = node.getAggrOnRight(); + LocalExecutionPlanContext buildContext = context.createSubContext(); + PhysicalOperation buildSource = buildNode.accept(this, buildContext); + if (buildSource.getPipelineExecutionStrategy() == GROUPED_EXECUTION) { + checkState( + probeSource.getPipelineExecutionStrategy() == GROUPED_EXECUTION, + "Build execution is GROUPED_EXECUTION. Probe execution is expected be GROUPED_EXECUTION, but is UNGROUPED_EXECUTION."); + } + + ImmutableMap.Builder buildAggrOutputMappings = ImmutableMap.builder(); + List aggrNodeOutputSymbols = aggregationBuild.getOutputSymbols(); + for (int i = 0; i < aggrNodeOutputSymbols.size(); i++) { + buildAggrOutputMappings.put(aggrNodeOutputSymbols.get(i), i); + } + ImmutableMap buildAggrLayout = buildAggrOutputMappings.build(); + List buildAggrTypes = toTypes(buildAggrLayout, buildContext); + + List buildTypes = toTypes(buildSource.getLayout(), buildContext); + + JoinInternalAggregation aggregationProbe = node.getLeftAggr(); + ImmutableMap.Builder probeAggrOutputMappings = ImmutableMap.builder(); + List probeAggrNodeOutputSymbols = aggregationProbe.getOutputSymbols(); + for (int i = 0; i < probeAggrNodeOutputSymbols.size(); i++) { + probeAggrOutputMappings.put(probeAggrNodeOutputSymbols.get(i), i); + } + ImmutableMap probeAggrLayout = probeAggrOutputMappings.build(); + + List buildAggrOutputSymbols = ImmutableList.copyOf(aggregationBuild.getOutputSymbols()); + List buildAggrOutputChannels = ImmutableList.copyOf(getChannelsForSymbols(buildAggrOutputSymbols, buildAggrLayout)); + List buildJoinChannels = ImmutableList.copyOf(getChannelsForSymbols(buildSymbols, buildAggrLayout)); + OptionalInt buildJoinHashChannel = buildHashSymbol.map(channelGetter(buildAggrLayout)) + .map(OptionalInt::of).orElse(OptionalInt.empty()); + int taskCount = buildContext.getDriverInstanceCount().orElse(1); + + Optional filterFunctionFactory = node.getFilter() + .map(filterExpression -> compileJoinFilterFunction( + filterExpression, + probeAggrLayout, + buildAggrLayout, + context.getTypes(), + context.getSession())); + + Optional sortExpressionContext = node.getFilter().flatMap(filter -> SortExpressionExtractor.extractSortExpression(metadata, node.getRightOutputSymbols(), filter)); + + Optional sortChannel = sortExpressionContext + .map(SortExpressionContext::getSortExpression) + .map(sortExpression -> sortExpressionAsSortChannel(sortExpression, probeAggrLayout, buildAggrLayout, context)); + + List searchFunctionFactories = sortExpressionContext + .map(SortExpressionContext::getSearchExpressions) + .map(searchExpressions -> searchExpressions.stream() + .map(searchExpression -> compileJoinFilterFunction( + searchExpression, + probeAggrLayout, + buildAggrLayout, + context.getTypes(), + context.getSession())) + .collect(toImmutableList())) + .orElse(ImmutableList.of()); + + ImmutableList buildAggrOutputTypes = buildAggrOutputChannels.stream() + .map(buildAggrTypes::get) + .collect(toImmutableList()); + JoinBridgeManager lookupSourceFactoryManager = new JoinBridgeManager<>( + false, + probeSource.getPipelineExecutionStrategy(), + buildSource.getPipelineExecutionStrategy(), + lifespan -> new PartitionedLookupSourceFactory( + buildAggrTypes, + buildAggrOutputTypes, + buildJoinChannels.stream() + .map(buildAggrTypes::get) + .collect(toImmutableList()), + taskCount, + buildAggrLayout, + false, + false), + buildAggrOutputTypes); + + ImmutableList.Builder factoriesBuilder = new ImmutableList.Builder(); + factoriesBuilder.addAll(buildSource.getOperatorFactories()); + + createDynamicFilter(node, context, taskCount).ifPresent( + filter -> { + List filterBuildChannels = filter + .getBuildChannels() + .entrySet() + .stream() + .map(entry -> { + String filterId = entry.getKey(); + int index = entry.getValue(); + Type type = buildAggrTypes.get(index); + return new DynamicFilterSourceOperator.Channel(filterId, type, index, context.getSession().getQueryId().toString()); + }) + .collect(Collectors.toList()); + factoriesBuilder.add( + new DynamicFilterSourceOperator.DynamicFilterSourceOperatorFactory( + buildContext.getNextOperatorId(), + node.getId(), + filter.getValueConsumer(), /** the consumer to process all values collected to build the dynamic filter */ + filterBuildChannels, + getDynamicFilteringMaxPerDriverValueCount(buildContext.getSession()), + getDynamicFilteringMaxPerDriverSize(buildContext.getSession()))); + }); + + int startOutputChannel = 0; + ImmutableMap.Builder outputMappings = ImmutableMap.builder(); + ImmutableMap.Builder finalOutputMappings = ImmutableMap.builder(); + GroupJoinAggregator aggrfactory = prepareGroupJoinAggregator(aggregationBuild, + buildSource.getLayout(), + buildSource.getTypes(), + startOutputChannel, + outputMappings); + GroupJoinAggregator aggrOnAggrfactory = prepareGroupJoinAggregator(node.getAggrOnRight(), + buildAggrLayout, + buildAggrTypes, + startOutputChannel, + finalOutputMappings); + List buildFinalOutputSymbols = finalOutputMappings.build().keySet().stream() + .filter(symbol -> node.getOutputSymbols().contains(symbol)) + .collect(toImmutableList()); + List buildFinalOutputChannels = ImmutableList.copyOf(getChannelsForSymbols(buildFinalOutputSymbols, finalOutputMappings.build())); + buildFinalOutputChannelsBuilder.addAll(buildFinalOutputChannels); + + HashBuilderGroupJoinOperatorFactory hashBuilderOperatorFactory = new HashBuilderGroupJoinOperatorFactory( + buildContext.getNextOperatorId(), + node.getId(), + lookupSourceFactoryManager, + buildAggrOutputChannels, + buildJoinChannels, + buildJoinHashChannel, + filterFunctionFactory, + sortChannel, + Optional.of(buildAggrLayout.size() - 1), + searchFunctionFactories, + 10_000, + pagesIndexFactory, + taskCount > 1, + isSpillToHdfsEnabled(context.getSession()), + aggrfactory, + aggrOnAggrfactory, + buildFinalOutputSymbols, + buildFinalOutputChannels); + + factoriesBuilder.add(hashBuilderOperatorFactory); + + context.addDriverFactory( + buildContext.isInputDriver(), + false, + factoriesBuilder.build(), + buildContext.getDriverInstanceCount(), + buildSource.getPipelineExecutionStrategy()); + + return lookupSourceFactoryManager; + } + protected JoinFilterFunctionFactory compileJoinFilterFunction( RowExpression filterExpression, Map probeLayout, @@ -2852,6 +3091,155 @@ public class LocalExecutionPlanner return ((InputReferenceExpression) rewrittenSortExpression).getField(); } + private OperatorFactory createGroupLookupJoin( + JoinOnAggregationNode node, + PhysicalOperation probeSource, + List probeSymbols, + Optional probeHashSymbol, + JoinBridgeManager lookupSourceFactoryManager, + LocalExecutionPlanContext context, + List finalOutputBuildTypes, + List buildFinalOutputChannels) + { + // Build output depends on Left Aggregation + JoinInternalAggregation aggregationProbe = node.getLeftAggr(); + ImmutableMap.Builder probeAggrOutputMappings = ImmutableMap.builder(); + List aggrnodeOutputSymbols = aggregationProbe.getOutputSymbols(); + for (int i = 0; i < aggrnodeOutputSymbols.size(); i++) { + probeAggrOutputMappings.put(aggrnodeOutputSymbols.get(i), i); + } + ImmutableMap probeAggrLayout = probeAggrOutputMappings.build(); + LocalExecutionPlanContext probeContext = context.createSubContext(); + + List probeAggrTypes = toTypes(probeAggrLayout, probeContext); + List probeAggrOutputSymbols = ImmutableList.copyOf(aggregationProbe.getOutputSymbols()); + List probeAggrOutputChannels = ImmutableList.copyOf(getChannelsForSymbols(probeAggrOutputSymbols, probeAggrLayout)); + List probeJoinChannels = ImmutableList.copyOf(getChannelsForSymbols(probeSymbols, probeAggrLayout)); + OptionalInt probeJoinHashChannel = probeHashSymbol.map(channelGetter(probeAggrLayout)) + .map(OptionalInt::of).orElse(OptionalInt.empty()); + OptionalInt totalOperatorsCount = context.getDriverInstanceCount(); + OptionalInt probeAggrCountChannel = OptionalInt.of(probeAggrOutputChannels.size() - 1); + + int startOutputChannel = 0; + ImmutableMap.Builder outputMappings = ImmutableMap.builder(); + /*List accumulatorFactories = new ArrayList<>();*/ + + /*Optional groupIdChannel = getOutputMappingAndGroupIdChannel(aggregationProbe.getAggregations(), + aggregationProbe.getGroupingKeys(), + aggregationProbe.getHashSymbol(), + aggregationProbe.getGroupIdSymbol(), + probeSource, + startOutputChannel, + outputMappings, + accumulatorFactories, + Optional.empty(), + Optional.empty());*/ + /*List probeGroupByChannels = getChannelsForSymbols(aggregationProbe.getGroupingKeys(), probeSource.getLayout());*/ + /*List probeGroupbyTypes = probeGroupByChannels.stream() + .map(entry -> probeSource.getTypes().get(entry)) + .collect(toImmutableList()); + Optional probeAggrHashChannel = aggregationProbe.getHashSymbol().map(channelGetter(probeSource));*/ + /*JoinInternalAggregation aggregationOnLeftProbe = node.getAggrOnLeft();*/ + ImmutableMap.Builder finalOutputMappings = ImmutableMap.builder(); + /*List finalAccumulatorFactories = new ArrayList<>(); + List finalGroupByChannels = getChannelsForSymbols(aggregationOnLeftProbe.getGroupingKeys(), probeAggrLayout);*/ + /*List finalGroupByTypes = finalGroupByChannels.stream() + .map(probeAggrTypes::get) + .collect(toImmutableList()); + Optional finalHashChannel = aggregationOnLeftProbe.getHashSymbol().map(channelGetter(probeAggrLayout)); + Optional finalGroupIdChannel = getOutputMappingAndGroupIdChannel(aggregationOnLeftProbe.getAggregations(), + aggregationOnLeftProbe.getGroupingKeys(), + aggregationOnLeftProbe.getHashSymbol(), + aggregationOnLeftProbe.getGroupIdSymbol(), + probeAggrLayout, + probeAggrTypes, + startOutputChannel, + outputMappings, + finalAccumulatorFactories, + Optional.empty(), + Optional.empty());*/ + + GroupJoinAggregator aggrfactory = prepareGroupJoinAggregator(aggregationProbe, + probeSource.getLayout(), + probeSource.getTypes(), + startOutputChannel, + outputMappings); + GroupJoinAggregator aggrOnAggrfactory = prepareGroupJoinAggregator(node.getAggrOnLeft(), + probeAggrLayout, + probeAggrTypes, + startOutputChannel, + finalOutputMappings); + + List probeFinalOutputSymbols = finalOutputMappings.build().keySet().stream() + .filter(symbol -> node.getOutputSymbols().contains(symbol)) + .collect(toImmutableList()); + List probeFinalOutputChannels = ImmutableList.copyOf(getChannelsForSymbols(probeFinalOutputSymbols, finalOutputMappings.build())); + + ImmutableList.Builder outputTypes = ImmutableList.builder(); + outputTypes.addAll(toTypes(probeFinalOutputSymbols, probeContext)).addAll(finalOutputBuildTypes); + + switch (node.getType()) { + case INNER: + return lookupJoinOperators.groupInnerJoin(context.getNextOperatorId(), + node.getId(), + lookupSourceFactoryManager, + probeAggrTypes, + probeJoinChannels, + probeJoinHashChannel, + probeAggrCountChannel, + probeAggrOutputChannels, + totalOperatorsCount, + partitioningSpillerFactory, + false, + aggrfactory, + aggrOnAggrfactory, + probeFinalOutputSymbols, + probeFinalOutputChannels, + buildFinalOutputChannels, + outputTypes.build()); + default: + throw new UnsupportedOperationException("Unsupported join type: " + node.getType()); + } + } + + private GroupJoinAggregator prepareGroupJoinAggregator(JoinInternalAggregation aggregation, + Map layout, + List types, + int startOutputChannel, + ImmutableMap.Builder outputMappings) + { + List accumulatorFactories = new ArrayList<>(); + List groupByChannels = getChannelsForSymbols(aggregation.getGroupingKeys(), layout); + List groupByTypes = groupByChannels.stream() + .map(types::get) + .collect(toImmutableList()); + Optional hashChannel = aggregation.getHashSymbol().map(channelGetter(layout)); + Optional groupIdChannel = getOutputMappingAndGroupIdChannel(aggregation.getAggregations(), + aggregation.getGroupingKeys(), + aggregation.getHashSymbol(), + aggregation.getGroupIdSymbol(), + layout, + types, + startOutputChannel, + outputMappings, + accumulatorFactories, + Optional.empty(), + Optional.empty()); + return GroupJoinAggregator.buildGroupJoinAggregator(groupByTypes, + groupByChannels, + ImmutableList.copyOf(aggregation.getGlobalGroupingSets()), + aggregation.getStep(), + accumulatorFactories, + hashChannel, + groupIdChannel, + 10_000, + Optional.ofNullable(maxPartialAggregationMemorySize), + joinCompiler, + aggregation.getStep().isOutputPartial(), + createPartialAggregationController(aggregation.getStep(), session), + aggregation.hasDefaultOutput()); + } + private OperatorFactory createLookupJoin( JoinNode node, PhysicalOperation probeSource, @@ -3545,6 +3933,14 @@ public class LocalExecutionPlanner protected AccumulatorFactory buildAccumulatorFactory( PhysicalOperation source, Aggregation aggregation) + { + return buildAccumulatorFactory(source.getLayout(), source.getTypes(), aggregation); + } + + protected AccumulatorFactory buildAccumulatorFactory( + Map layout, + List types, + Aggregation aggregation) { InternalAggregationFunction internalAggregationFunction = metadata.getFunctionAndTypeManager().getAggregateFunctionImplementation(aggregation.getFunctionHandle()); @@ -3552,7 +3948,7 @@ public class LocalExecutionPlanner for (RowExpression argument : aggregation.getArguments()) { if (!(argument instanceof LambdaDefinitionExpression)) { checkArgument(argument instanceof VariableReferenceExpression, "argument must be variable reference"); - valueChannels.add(source.getLayout().get(new Symbol(((VariableReferenceExpression) argument).getName()))); + valueChannels.add(layout.get(new Symbol(((VariableReferenceExpression) argument).getName()))); } } @@ -3572,7 +3968,7 @@ public class LocalExecutionPlanner } } - Optional maskChannel = aggregation.getMask().map(value -> source.getLayout().get(value)); + Optional maskChannel = aggregation.getMask().map(value -> layout.get(value)); List sortOrders = ImmutableList.of(); List sortKeys = ImmutableList.of(); if (aggregation.getOrderingScheme().isPresent()) { @@ -3586,8 +3982,8 @@ public class LocalExecutionPlanner return internalAggregationFunction.bind( valueChannels, maskChannel, - source.getTypes(), - getChannelsForSymbols(sortKeys, source.getLayout()), + types, + getChannelsForSymbols(sortKeys, layout), sortOrders, pagesIndexFactory, aggregation.isDistinct(), @@ -3804,11 +4200,36 @@ public class LocalExecutionPlanner createPartialAggregationController(step, session)); } + private Optional getOutputMappingAndGroupIdChannel(Map aggregations, + List groupBySymbols, + Optional hashSymbol, + Optional groupIdSymbol, + PhysicalOperation source, + int startOutputChannel, + ImmutableMap.Builder outputMappings, + List accumulatorFactories, + Optional step, + Optional finalizeSymbol) + { + return getOutputMappingAndGroupIdChannel(aggregations, + groupBySymbols, + hashSymbol, + groupIdSymbol, + source.getLayout(), + source.getTypes(), + startOutputChannel, + outputMappings, + accumulatorFactories, + step, + finalizeSymbol); + } + private Optional getOutputMappingAndGroupIdChannel(Map aggregations, List groupBySymbols, Optional hashSymbol, Optional groupIdSymbol, - PhysicalOperation source, + Map layout, + List types, int startOutputChannel, ImmutableMap.Builder outputMappings, List accumulatorFactories, @@ -3819,7 +4240,7 @@ public class LocalExecutionPlanner for (Map.Entry entry : aggregations.entrySet()) { Symbol symbol = entry.getKey(); Aggregation aggregation = entry.getValue(); - accumulatorFactories.add(buildAccumulatorFactory(source, aggregation)); + accumulatorFactories.add(buildAccumulatorFactory(layout, types, aggregation)); aggregationOutputSymbols.add(symbol); } @@ -3937,10 +4358,15 @@ public class LocalExecutionPlanner } protected static Function channelGetter(PhysicalOperation source) + { + return channelGetter(source.getLayout()); + } + + protected static Function channelGetter(Map layout) { return input -> { - checkArgument(source.getLayout().containsKey(input)); - return source.getLayout().get(input); + checkArgument(layout.containsKey(input)); + return layout.get(input); }; } @@ -3987,7 +4413,18 @@ public class LocalExecutionPlanner this.pipelineExecutionStrategy = pipelineExecutionStrategy; } - private static List toTypes(Map layout, LocalExecutionPlanContext context) + public static List toTypes(List symbols, LocalExecutionPlanContext context) + { + ImmutableList.Builder types = ImmutableList.builder(); + symbols.forEach(symbol -> { + Type type = context.getTypes().get(symbol); + checkArgument(type != null, "Layout does not have a symbol for every output channel: %s", symbol); + types.add(type); + }); + return types.build(); + } + + public static List toTypes(Map layout, LocalExecutionPlanContext context) { // verify layout covers all values int channelCount = layout.values().stream().mapToInt(Integer::intValue).max().orElse(-1) + 1; diff --git a/presto-main/src/main/java/io/prestosql/utils/DynamicFilterUtils.java b/presto-main/src/main/java/io/prestosql/utils/DynamicFilterUtils.java index d76d10ba2..a22877c6d 100644 --- a/presto-main/src/main/java/io/prestosql/utils/DynamicFilterUtils.java +++ b/presto-main/src/main/java/io/prestosql/utils/DynamicFilterUtils.java @@ -18,6 +18,7 @@ import io.prestosql.spi.dynamicfilter.DynamicFilter.DataType; import io.prestosql.spi.dynamicfilter.DynamicFilter.Type; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.TableScanNode; import io.prestosql.sql.analyzer.FeaturesConfig.DynamicFilterDataType; @@ -72,6 +73,15 @@ public class DynamicFilterUtils return filterNodes; } + public static List findFilterNodeInStage(JoinOnAggregationNode node) + { + List filterNodes = PlanNodeSearcher + .searchFrom(node.getLeft()) + .where(DynamicFilterUtils::isFilterAboveTableScan) + .findAll(); + return filterNodes; + } + public static List findFilterNodeInStage(SemiJoinNode node) { List filterNodes = PlanNodeSearcher -- Gitee From df72b8b85d8097efa9e530744ab8e36ab50c25c2 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Thu, 23 Feb 2023 15:44:58 +0530 Subject: [PATCH 14/36] Group join - Lookup operator and Stats Rule for GroupJoinNode --- .../cost/JoinOnAggregationStatsRule.java | 254 ++++++ .../prestosql/cost/StatsCalculatorModule.java | 1 + .../operator/LookupGroupJoinOperator.java | 757 ++++++++++++++++++ .../prestosql/sql/planner/plan/Patterns.java | 6 + 4 files changed, 1018 insertions(+) create mode 100644 presto-main/src/main/java/io/prestosql/cost/JoinOnAggregationStatsRule.java create mode 100644 presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java diff --git a/presto-main/src/main/java/io/prestosql/cost/JoinOnAggregationStatsRule.java b/presto-main/src/main/java/io/prestosql/cost/JoinOnAggregationStatsRule.java new file mode 100644 index 000000000..d4c886955 --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/cost/JoinOnAggregationStatsRule.java @@ -0,0 +1,254 @@ +/* + * 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.prestosql.cost; + +import com.google.common.annotations.VisibleForTesting; +import io.prestosql.Session; +import io.prestosql.matching.Pattern; +import io.prestosql.spi.plan.JoinNode.EquiJoinClause; +import io.prestosql.spi.plan.JoinOnAggregationNode; +import io.prestosql.spi.plan.Symbol; +import io.prestosql.sql.planner.TypeProvider; +import io.prestosql.sql.planner.iterative.Lookup; +import io.prestosql.sql.tree.ComparisonExpression; +import io.prestosql.util.MoreMath; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static io.prestosql.SystemSessionProperties.getJoinMultiClauseIndependenceFactor; +import static io.prestosql.cost.FilterStatsCalculator.UNKNOWN_FILTER_COEFFICIENT; +import static io.prestosql.cost.PlanNodeStatsEstimateMath.estimateCorrelatedConjunctionRowCount; +import static io.prestosql.cost.SymbolStatsEstimate.buildFrom; +import static io.prestosql.sql.planner.SymbolUtils.toSymbolReference; +import static io.prestosql.sql.planner.plan.Patterns.joinOnAggregation; +import static io.prestosql.sql.relational.OriginalExpressionUtils.castToExpression; +import static io.prestosql.sql.relational.OriginalExpressionUtils.isExpression; +import static io.prestosql.sql.tree.ComparisonExpression.Operator.EQUAL; +import static java.lang.Double.isNaN; +import static java.util.Objects.requireNonNull; + +public class JoinOnAggregationStatsRule + extends SimpleStatsRule +{ + private static final Pattern PATTERN = joinOnAggregation(); + private static final double DEFAULT_UNMATCHED_JOIN_COMPLEMENT_NDVS_COEFFICIENT = 0.5; + + private final FilterStatsCalculator filterStatsCalculator; + private final StatsNormalizer normalizer; + private final double unmatchedJoinComplementNdvsCoefficient; + + public JoinOnAggregationStatsRule(FilterStatsCalculator filterStatsCalculator, StatsNormalizer normalizer) + { + this(filterStatsCalculator, normalizer, DEFAULT_UNMATCHED_JOIN_COMPLEMENT_NDVS_COEFFICIENT); + } + + @VisibleForTesting + JoinOnAggregationStatsRule(FilterStatsCalculator filterStatsCalculator, StatsNormalizer normalizer, double unmatchedJoinComplementNdvsCoefficient) + { + super(normalizer); + this.filterStatsCalculator = requireNonNull(filterStatsCalculator, "filterStatsCalculator is null"); + this.normalizer = normalizer; + this.unmatchedJoinComplementNdvsCoefficient = unmatchedJoinComplementNdvsCoefficient; + } + + @Override + public Pattern getPattern() + { + return PATTERN; + } + + @Override + protected Optional doCalculate(JoinOnAggregationNode node, StatsProvider sourceStats, Lookup lookup, Session session, TypeProvider types) + { + PlanNodeStatsEstimate leftStats = sourceStats.getStats(node.getLeft()); + PlanNodeStatsEstimate rightStats = sourceStats.getStats(node.getRight()); + PlanNodeStatsEstimate leftAggrStats = AggregationStatsRule.groupBy(leftStats, node.getLeftAggr().getGroupingKeys(), node.getLeftAggr().getAggregations()); + PlanNodeStatsEstimate rightAggrStats = AggregationStatsRule.groupBy(rightStats, node.getRightAggr().getGroupingKeys(), node.getRightAggr().getAggregations()); + // TODO Vineet Check if following consideration is correct?? + PlanNodeStatsEstimate leftAggrOnAggrStats = AggregationStatsRule.groupBy(leftAggrStats, node.getAggrOnLeft().getGroupingKeys(), node.getAggrOnLeft().getAggregations()); + PlanNodeStatsEstimate rightAggrOnAggrStats = AggregationStatsRule.groupBy(rightAggrStats, node.getAggrOnRight().getGroupingKeys(), node.getAggrOnRight().getAggregations()); + PlanNodeStatsEstimate crossJoinStats = crossJoinStats(node, leftAggrOnAggrStats, rightAggrOnAggrStats, types); + + switch (node.getType()) { + case INNER: + return Optional.of(computeInnerJoinStats(node, crossJoinStats, session, types)); + default: + throw new IllegalStateException("Unknown group join type: " + node.getType()); + } + } + + private PlanNodeStatsEstimate computeInnerJoinStats(JoinOnAggregationNode node, PlanNodeStatsEstimate crossJoinStats, Session session, TypeProvider types) + { + List equiJoinCriteria = node.getCriteria(); + + Map layout = new HashMap<>(); + int channel = 0; + for (Symbol symbol : node.getOutputSymbols()) { + layout.put(channel++, symbol); + } + + if (equiJoinCriteria.isEmpty()) { + if (!node.getFilter().isPresent()) { + return crossJoinStats; + } + // TODO: this might explode stats + if (isExpression(node.getFilter().get())) { + return filterStatsCalculator.filterStats(crossJoinStats, castToExpression(node.getFilter().get()), session, types); + } + else { + return filterStatsCalculator.filterStats(crossJoinStats, node.getFilter().get(), session, types, layout); + } + } + + PlanNodeStatsEstimate equiJoinEstimate = filterByEquiJoinClauses(crossJoinStats, node.getCriteria(), session, types); + + if (equiJoinEstimate.isOutputRowCountUnknown()) { + return PlanNodeStatsEstimate.unknown(); + } + + if (!node.getFilter().isPresent()) { + return equiJoinEstimate; + } + + PlanNodeStatsEstimate filteredEquiJoinEstimate; + if (isExpression(node.getFilter().get())) { + filteredEquiJoinEstimate = filterStatsCalculator.filterStats(equiJoinEstimate, castToExpression(node.getFilter().get()), session, types); + } + else { + filteredEquiJoinEstimate = filterStatsCalculator.filterStats(equiJoinEstimate, node.getFilter().get(), session, types, layout); + } + + if (filteredEquiJoinEstimate.isOutputRowCountUnknown()) { + return normalizer.normalize(equiJoinEstimate.mapOutputRowCount(rowCount -> rowCount * UNKNOWN_FILTER_COEFFICIENT), types); + } + + return filteredEquiJoinEstimate; + } + + private PlanNodeStatsEstimate filterByEquiJoinClauses( + PlanNodeStatsEstimate stats, + Collection clauses, + Session session, + TypeProvider types) + { + checkArgument(!clauses.isEmpty(), "clauses is empty"); + // Join equality clauses are usually correlated. Therefore, we shouldn't treat each join equality + // clause separately because stats estimates would be way off. + List knownEstimates = clauses.stream() + .map(clause -> { + ComparisonExpression predicate = new ComparisonExpression(EQUAL, toSymbolReference(clause.getLeft()), toSymbolReference(clause.getRight())); + return new PlanNodeStatsEstimateWithClause(filterStatsCalculator.filterStats(stats, predicate, session, types), clause); + }) + .collect(toImmutableList()); + + double outputRowCount = estimateCorrelatedConjunctionRowCount( + stats, + knownEstimates.stream().map(PlanNodeStatsEstimateWithClause::getEstimate).collect(toImmutableList()), + getJoinMultiClauseIndependenceFactor(session)); + if (isNaN(outputRowCount)) { + return PlanNodeStatsEstimate.unknown(); + } + return normalizer.normalize(new PlanNodeStatsEstimate(outputRowCount, intersectCorrelatedJoinClause(stats, knownEstimates)), types); + } + + private static double firstNonNaN(double... values) + { + for (double value : values) { + if (!isNaN(value)) { + return value; + } + } + throw new IllegalArgumentException("All values are NaN"); + } + + private PlanNodeStatsEstimate crossJoinStats(JoinOnAggregationNode node, PlanNodeStatsEstimate leftStats, PlanNodeStatsEstimate rightStats, TypeProvider types) + { + PlanNodeStatsEstimate.Builder builder = PlanNodeStatsEstimate.builder() + .setOutputRowCount(leftStats.getOutputRowCount() * rightStats.getOutputRowCount()); + + node.getLeft().getOutputSymbols().forEach(symbol -> builder.addSymbolStatistics(symbol, leftStats.getSymbolStatistics(symbol))); + node.getRight().getOutputSymbols().forEach(symbol -> builder.addSymbolStatistics(symbol, rightStats.getSymbolStatistics(symbol))); + + return normalizer.normalize(builder.build(), types); + } + + private static Map intersectCorrelatedJoinClause( + PlanNodeStatsEstimate stats, + List equiJoinClauseEstimates) + { + // Add initial statistics (including stats for columns which are not part of equi-join clauses) + PlanNodeStatsEstimate.Builder result = PlanNodeStatsEstimate.builder() + .addSymbolStatistics(stats.getSymbolStatistics()); + + for (PlanNodeStatsEstimateWithClause estimateWithClause : equiJoinClauseEstimates) { + EquiJoinClause clause = estimateWithClause.getClause(); + // we just clear null fraction and adjust ranges here, selectivity is handled outside this function + SymbolStatsEstimate leftStats = stats.getSymbolStatistics(clause.getLeft()); + SymbolStatsEstimate rightStats = stats.getSymbolStatistics(clause.getRight()); + StatisticRange leftRange = StatisticRange.from(leftStats); + StatisticRange rightRange = StatisticRange.from(rightStats); + + StatisticRange intersect = leftRange.intersect(rightRange); + double leftFilterValue = firstNonNaN(leftRange.overlapPercentWith(intersect), 1); + double rightFilterValue = firstNonNaN(rightRange.overlapPercentWith(intersect), 1); + double leftNdvInRange = leftFilterValue * leftRange.getDistinctValuesCount(); + double rightNdvInRange = rightFilterValue * rightRange.getDistinctValuesCount(); + double retainedNdv = MoreMath.min(leftNdvInRange, rightNdvInRange); + + SymbolStatsEstimate newLeftStats = buildFrom(leftStats) + .setNullsFraction(0) + .setStatisticsRange(intersect) + .setDistinctValuesCount(retainedNdv) + .build(); + + SymbolStatsEstimate newRightStats = buildFrom(rightStats) + .setNullsFraction(0) + .setStatisticsRange(intersect) + .setDistinctValuesCount(retainedNdv) + .build(); + + result.addSymbolStatistics(clause.getLeft(), newLeftStats) + .addSymbolStatistics(clause.getRight(), newRightStats); + } + return result.build().getSymbolStatistics(); + } + + private static class PlanNodeStatsEstimateWithClause + { + private final PlanNodeStatsEstimate estimate; + private final EquiJoinClause clause; + + private PlanNodeStatsEstimateWithClause(PlanNodeStatsEstimate estimate, EquiJoinClause clause) + { + this.estimate = requireNonNull(estimate, "estimate is null"); + this.clause = requireNonNull(clause, "clause is null"); + } + + private PlanNodeStatsEstimate getEstimate() + { + return estimate; + } + + private EquiJoinClause getClause() + { + return clause; + } + } +} diff --git a/presto-main/src/main/java/io/prestosql/cost/StatsCalculatorModule.java b/presto-main/src/main/java/io/prestosql/cost/StatsCalculatorModule.java index 7463839fc..fe8f08028 100644 --- a/presto-main/src/main/java/io/prestosql/cost/StatsCalculatorModule.java +++ b/presto-main/src/main/java/io/prestosql/cost/StatsCalculatorModule.java @@ -51,6 +51,7 @@ public class StatsCalculatorModule rules.add(new ProjectStatsRule(scalarStatsCalculator, normalizer)); rules.add(new ExchangeStatsRule(normalizer)); rules.add(new JoinStatsRule(filterStatsCalculator, normalizer)); + rules.add(new JoinOnAggregationStatsRule(filterStatsCalculator, normalizer)); rules.add(new SpatialJoinStatsRule(filterStatsCalculator, normalizer)); rules.add(new AggregationStatsRule(normalizer)); rules.add(new UnionStatsRule(normalizer)); diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java new file mode 100644 index 000000000..a393e1a48 --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java @@ -0,0 +1,757 @@ +/* + * 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.prestosql.operator; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; +import com.google.common.io.Closer; +import com.google.common.util.concurrent.ListenableFuture; +import io.prestosql.memory.context.LocalMemoryContext; +import io.prestosql.operator.GroupJoinProbe.GroupJoinProbeFactory; +import io.prestosql.operator.LookupJoinOperator.SpillInfoSnapshot; +import io.prestosql.operator.LookupJoinOperators.JoinType; +import io.prestosql.operator.PartitionedConsumption.Partition; +import io.prestosql.operator.aggregation.builder.AggregationBuilder; +import io.prestosql.operator.aggregation.builder.InMemoryHashAggregationBuilder; +import io.prestosql.operator.aggregation.builder.InMemoryHashAggregationBuilderWithReset; +import io.prestosql.operator.exchange.LocalPartitionGenerator; +import io.prestosql.snapshot.SingleInputSnapshotState; +import io.prestosql.spi.Page; +import io.prestosql.spi.plan.Symbol; +import io.prestosql.spi.type.Type; +import io.prestosql.spiller.PartitioningSpiller; +import io.prestosql.spiller.PartitioningSpillerFactory; + +import javax.annotation.Nullable; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.function.Supplier; + +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Verify.verify; +import static io.airlift.concurrent.MoreFutures.addSuccessCallback; +import static io.airlift.concurrent.MoreFutures.getDone; +import static io.prestosql.SystemSessionProperties.isInnerJoinSpillFilteringEnabled; +import static io.prestosql.operator.LookupJoinOperators.JoinType.FULL_OUTER; +import static io.prestosql.operator.LookupJoinOperators.JoinType.PROBE_OUTER; +import static java.util.Collections.emptyIterator; +import static java.util.Objects.requireNonNull; + +public class LookupGroupJoinOperator + implements Operator +{ + @VisibleForTesting + public enum State + { + /** + * Operator accepts input + */ + CONSUMING_INPUT, + + /** + * All inputs accepted, finishing aggregations + */ + AGGR_FINISHING, + + /** + * Aggregation on input finished + */ + AGGR_FINISHED, + + /** + * LookupSource has been built and passed on without any spill occurring + */ + SOURCE_BUILT, + + /** + * No longer needed + */ + CLOSED + } + + private final OperatorContext operatorContext; + + private final List probeTypes; + private final List probeFinalOutputSymbols; + private final List probeFinalOutputChannels; + private final List buildFinalOutputChannels; + private final GroupJoinProbeFactory joinProbeFactory; + private final Runnable afterClose; + + private final PartitioningSpillerFactory partitioningSpillerFactory; + + private Runnable afterMemOpFinish; + private final OptionalInt lookupJoinsCount; + private final HashGenerator hashGenerator; + private final LookupSourceFactory lookupSourceFactory; + + private final JoinStatisticsCounter statisticsCounter; + + private final LookupGroupJoinPageBuilder pageBuilder; + + private final boolean probeOnOuterSide; + private final boolean spillBypassEnabled; + + private final ListenableFuture lookupSourceProviderFuture; + private LookupSourceProvider lookupSourceProvider; + private GroupJoinProbe probe; + + private Page outputPage; + + private Optional spiller = Optional.empty(); + private Optional partitionGenerator = Optional.empty(); + private ListenableFuture spillInProgress = NOT_BLOCKED; + private long inputPageSpillEpoch; + private boolean closed; + private boolean finishing; + private boolean unspilling; + private boolean isSpillerRestored; + private boolean finished; + private long joinPosition = -1; + private int joinSourcePositions; + + private boolean currentProbePositionProducedRow; + + @Nullable + private ListenableFuture>> partitionedConsumption; + @Nullable + private Iterator>> lookupPartitions; + private Optional>> currentPartition = Optional.empty(); + private Optional>> unspilledLookupSource = Optional.empty(); + private Iterator unspilledInputPages = emptyIterator(); + private Iterator unspilledMemoryPartitions = emptyIterator(); + private Map> backUpUnspilledMemoryPartitions = new HashMap<>(); + private Map backUpRestoredPages = new HashMap<>(); + private Integer restoredPartition; + private final GroupJoinAggregator aggregator; + private final GroupJoinAggregator aggrOnAggregator; + + private final SingleInputSnapshotState snapshotState; + private boolean isSingleSessionSpiller; + private List spilledPartitionsList = new ArrayList<>(); + + protected AggregationBuilder aggregationBuilder; + protected AggregationBuilder probeAggregationBuilder; + protected AggregationBuilder buildAggregationBuilder; + protected LocalMemoryContext memoryContext; + + private final HashCollisionsCounter hashCollisionsCounter; + + protected boolean aggregationFinishing; + protected long numberOfInputRowsProcessed; + protected long numberOfUniqueRowsProduced; + protected Work unfinishedWork; + protected boolean aggregationInputProcessed; + private final boolean spillEnabled = false; + private boolean aggregationFinished; + protected WorkProcessor outputPages; + protected State state = State.CONSUMING_INPUT; + + public LookupGroupJoinOperator( + OperatorContext operatorContext, + boolean forked, + List probeTypes, + List outputTypes, + List buildTypes, + JoinType joinType, + LookupSourceFactory lookupSourceFactory, + GroupJoinProbeFactory joinProbeFactory, + Runnable afterClose, + OptionalInt lookupJoinsCount, + HashGenerator hashGenerator, + PartitioningSpillerFactory partitioningSpillerFactory, + Runnable afterMemOpFinish, + boolean isSingleSessionSpiller, + GroupJoinAggregator aggregator, + GroupJoinAggregator aggrOnAggregator, + List probeFinalOutputSymbols, + List probeFinalOutputChannels, + List buildFinalOutputChannels) + { + this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); + this.probeTypes = ImmutableList.copyOf(requireNonNull(probeTypes, "probeTypes is null")); + + requireNonNull(joinType, "joinType is null"); + // Cannot use switch case here, because javac will synthesize an inner class and cause IllegalAccessError + probeOnOuterSide = joinType == PROBE_OUTER || joinType == FULL_OUTER; + spillBypassEnabled = probeOnOuterSide || !isInnerJoinSpillFilteringEnabled(operatorContext.getDriverContext().getSession()); + + this.joinProbeFactory = requireNonNull(joinProbeFactory, "joinProbeFactory is null"); + this.afterClose = requireNonNull(afterClose, "afterClose is null"); + this.lookupJoinsCount = requireNonNull(lookupJoinsCount, "lookupJoinsCount is null"); + this.hashGenerator = requireNonNull(hashGenerator, "hashGenerator is null"); + this.lookupSourceFactory = requireNonNull(lookupSourceFactory, "lookupSourceFactory is null"); + this.partitioningSpillerFactory = requireNonNull(partitioningSpillerFactory, "partitioningSpillerFactory is null"); + this.lookupSourceProviderFuture = lookupSourceFactory.createLookupSourceProvider(); + + this.statisticsCounter = new JoinStatisticsCounter(joinType); + operatorContext.setInfoSupplier(this.statisticsCounter); + + this.pageBuilder = new LookupGroupJoinPageBuilder(outputTypes, buildTypes, buildFinalOutputChannels, probeFinalOutputChannels); + this.snapshotState = operatorContext.isSnapshotEnabled() ? SingleInputSnapshotState.forOperator(this, operatorContext) : null; + + this.afterMemOpFinish = afterMemOpFinish; + this.isSingleSessionSpiller = isSingleSessionSpiller; + + this.hashCollisionsCounter = new HashCollisionsCounter(operatorContext); + operatorContext.setInfoSupplier(hashCollisionsCounter); + + this.memoryContext = operatorContext.localUserMemoryContext(); + if (aggregator.isUseSystemMemory()) { + this.memoryContext = operatorContext.localSystemMemoryContext(); + } + + this.aggregator = aggregator; + this.aggrOnAggregator = aggrOnAggregator; + this.probeFinalOutputSymbols = probeFinalOutputSymbols; + this.probeFinalOutputChannels = probeFinalOutputChannels; + this.buildFinalOutputChannels = buildFinalOutputChannels; + } + + @Override + public OperatorContext getOperatorContext() + { + return operatorContext; + } + + @Override + public void finish() + { + if (finishing) { + return; + } + finishing = true; + aggregationFinishing = true; + } + + @Override + public boolean isFinished() + { + boolean finishedNow = this.finishing && this.finished && probe == null && pageBuilder.isEmpty() && outputPage == null; + // TODO Vineet Check the aggregation related pending work + // if finishedNow drop references so memory is freed early + if (finishedNow) { + close(); + } + return finishedNow; + } + + @Override + public ListenableFuture isBlocked() + { + if (finishing) { + return NOT_BLOCKED; + } + + return lookupSourceProviderFuture; + } + + @Override + public boolean needsInput() + { + // TODO Vineet check aggregation related conditions + if (!finishing && state == State.CONSUMING_INPUT) { + if (aggregationFinishing || outputPages != null) { + return false; + } + else if (aggregationBuilder != null && aggregationBuilder.isFull()) { + return false; + } + else { + // TODO Vineet Need to move this out of needsInput and need to make it light weight. + if (unfinishedWork != null) { + boolean workDone = unfinishedWork.process(); + aggregationBuilder.updateMemory(); + if (!workDone) { + return false; + } + unfinishedWork = null; + } + return true; + } + } + else { + return allowMarker2() + && lookupSourceProviderFuture.isDone(); + } + } + + public boolean allowMarker2() + { + return !finishing + && probe == null + && outputPage == null; + } + + @Override + public void addInput(Page page) + { + addInput(page, false); + } + + private void addInput(Page page, boolean isRestoredPage) + { + requireNonNull(page, "page is null"); + checkState(probe == null, "Current page has not been completely processed yet"); + + checkState(tryFetchLookupSourceProvider(), "Not ready to handle input yet"); + addInput2(page); + } + + private void addInput2(Page page) + { + // create Aggregators and pass page to them for process + checkState(!aggregationFinishing, "Operator is already finishing"); + aggregationInputProcessed = true; + + if (aggregationBuilder == null) { + createAggregationBuilder(); + } + else { + checkState(!aggregationBuilder.isFull(), "Aggregation buffer is full"); + } + + // process the current page; save the unfinished work if we are waiting for memory + unfinishedWork = aggregationBuilder.processPage(page); + if (unfinishedWork.process()) { + unfinishedWork = null; + // TODO Vineet check if can index this pages here. + } + aggregationBuilder.updateMemory(); + numberOfInputRowsProcessed += page.getPositionCount(); + + //createProbe(page); + } + + private void createProbe(Page page) + { + // create probe + if (buildAggregationBuilder == null) { + LookupSource lookupSource = lookupSourceProvider.withLease((lookupSourceLease -> lookupSourceLease.getLookupSource())); + buildAggregationBuilder = lookupSource.getAggregationBuilder().duplicate(); + } + probe = joinProbeFactory.createGroupJoinProbe(page/*newPage*/, false/*isSpilled*/, lookupSourceProvider, probeAggregationBuilder, buildAggregationBuilder); + + // initialize to invalid join position to force output code to advance the cursors + joinPosition = -1; + } + + private boolean tryFetchLookupSourceProvider() + { + if (lookupSourceProvider == null) { + if (!lookupSourceProviderFuture.isDone()) { + return false; + } + lookupSourceProvider = requireNonNull(getDone(lookupSourceProviderFuture)); + statisticsCounter.updateLookupSourcePositions(lookupSourceProvider.withLease(lookupSourceLease -> lookupSourceLease.getLookupSource().getJoinPositionCount())); + } + return true; + } + + private void finishAggregation() + { + Page page = processAggregation(); + if (page != null) { + // TODO Vineet may need to cache these pages and also check the condition for state change. + createProbe(page); + } + } + + @Override + public Page getOutput() + { + switch (state) { + case CONSUMING_INPUT: + if (probe == null) { + finishAggregation(); + } + break; + case SOURCE_BUILT: + break; + case CLOSED: + // no-op + return null; + } + + if (probe == null && pageBuilder.isEmpty() && !finishing) { + return null; + } + + if (!tryFetchLookupSourceProvider()) { + if (!finishing) { + return null; + } + + verify(finishing); + // We are no longer interested in the build side (the lookupSourceProviderFuture's value). + addSuccessCallback(lookupSourceProviderFuture, LookupSourceProvider::close); + lookupSourceProvider = new StaticLookupSourceProvider(new EmptyLookupSource()); + } + + if (probe == null && finishing && unfinishedWork == null && !unspilling) { + /* + * We do not have input probe and we won't have any, as we're finishing. + * Let LookupSourceFactory know LookupSources can be disposed as far as we're concerned. + */ + verify(partitionedConsumption == null, "partitioned consumption already started"); + lookupSourceProvider.close(); + partitionedConsumption = lookupSourceFactory.finishProbeOperator(lookupJoinsCount); + afterMemOpFinish.run(); + afterMemOpFinish = () -> {}; + unspilling = true; + finished = true; + } + + if (probe != null) { + processProbe(); + } + + if (outputPage != null) { + verify(pageBuilder.isEmpty()); + Page output = outputPage; + outputPage = null; + return output; + } + + // It is impossible to have probe == null && !pageBuilder.isEmpty(), + // because we will flush a page whenever we reach the probe end + verify(probe != null || pageBuilder.isEmpty()); + return null; + } + + protected boolean hasOrderBy() + { + return aggregator.hasOrderBy(); + } + + protected boolean hasDistinct() + { + return aggregator.hasDistinct(); + } + + public void createAggregationBuilder() + { + if (aggregator.getStep().isOutputPartial() || !spillEnabled || hasOrderBy() || hasDistinct()) { + aggregationBuilder = new InMemoryHashAggregationBuilder( + aggregator.getAccumulatorFactories(), + aggregator.getStep(), + aggregator.getExpectedGroups(), + aggregator.getGroupByTypes(), + aggregator.getGroupByChannels(), + aggregator.getHashChannel(), + operatorContext, + aggregator.getMaxPartialMemory(), + aggregator.getJoinCompiler(), + () -> { + memoryContext.setBytes(((InMemoryHashAggregationBuilder) aggregationBuilder).getSizeInMemory()); + if (aggregator.getStep().isOutputPartial() && aggregator.getMaxPartialMemory().isPresent()) { + // do not yield on memory for partial aggregations + return true; + } + return operatorContext.isWaitingForMemory().isDone(); + }); + probeAggregationBuilder = new InMemoryHashAggregationBuilderWithReset( + aggrOnAggregator.getAccumulatorFactories(), + aggrOnAggregator.getStep(), + aggrOnAggregator.getExpectedGroups(), + aggrOnAggregator.getGroupByTypes(), + aggrOnAggregator.getGroupByChannels(), + aggrOnAggregator.getHashChannel(), + operatorContext, + aggrOnAggregator.getMaxPartialMemory(), + aggrOnAggregator.getJoinCompiler(), + () -> { + memoryContext.setBytes(((InMemoryHashAggregationBuilder) probeAggregationBuilder).getSizeInMemory()); + if (aggrOnAggregator.getStep().isOutputPartial() && aggrOnAggregator.getMaxPartialMemory().isPresent()) { + // do not yield on memory for partial aggregations + return true; + } + return operatorContext.isWaitingForMemory().isDone(); + }); + } + else { + throw new UnsupportedOperationException("Not Supported"); + } + } + + public Page processAggregation() + { + if (aggregationFinished) { + return null; + } + + // process unfinished work if one exists + if (unfinishedWork != null) { + boolean workDone = unfinishedWork.process(); + aggregationBuilder.updateMemory(); + if (!workDone) { + return null; + } + unfinishedWork = null; + } + + if (outputPages == null) { + if (aggregationFinishing) { + if (!aggregationInputProcessed && aggregator.isProduceDefaultOutput()) { + // global aggregations always generate an output row with the default aggregation output (e.g. 0 for COUNT, NULL for SUM) + aggregationFinished = true; + state = State.SOURCE_BUILT; + return aggregator.getGlobalAggregationOutput(); + } + + if (aggregationBuilder == null) { + aggregationFinished = true; + state = State.SOURCE_BUILT; + return null; + } + } + + // only flush if we are finishing or the aggregation builder is full + if (!aggregationFinishing && (aggregationBuilder == null || !aggregationBuilder.isFull())) { + return null; + } + + outputPages = aggregationBuilder.buildResult(); + } + + if (!outputPages.process()) { + return null; + } + + if (outputPages.isFinished()) { + closeAggregationBuilder(); + return null; + } + + Page result = outputPages.getResult(); + numberOfUniqueRowsProduced += result.getPositionCount(); + return result; + } + + protected void closeAggregationBuilder() + { + outputPages = null; + if (aggregationBuilder != null) { + aggregationBuilder.recordHashCollisions(hashCollisionsCounter); + aggregationBuilder.close(); + // aggregationBuilder.close() will release all memory reserved in memory accounting. + // The reference must be set to null afterwards to avoid unaccounted memory. + aggregationBuilder = null; + } + memoryContext.setBytes(0); + aggregator.getPartialAggregationController().ifPresent( + controller -> controller.onFlush(numberOfInputRowsProcessed, numberOfUniqueRowsProduced)); + numberOfInputRowsProcessed = 0; + numberOfUniqueRowsProduced = 0; + } + + private void processProbe() + { + verify(probe != null); + Optional value = lookupSourceProvider.withLease(lookupSourceLease -> { + if (lookupSourceLease.spillEpoch() == inputPageSpillEpoch) { + // Spill state didn't change, so process as usual. + processProbe(lookupSourceLease.getLookupSource()); + return Optional.empty(); + } + + return Optional.of(SpillInfoSnapshot.from(lookupSourceLease)); + }); + if (!value.isPresent()) { + return; + } + long joinPositionWithinPartition; + if (joinPosition >= 0) { + joinPositionWithinPartition = lookupSourceProvider.withLease(lookupSourceLease -> lookupSourceLease.getLookupSource().joinPositionWithinPartition(joinPosition)); + } + else { + joinPositionWithinPartition = -1; + } + if (probe == null) { + return; + } + + Page currentPage = probe.getPage(); + int currentPosition = probe.getPosition(); + long currentJoinPosition = this.joinPosition; + boolean probePositionProducedRow = this.currentProbePositionProducedRow; + + clearProbe(); + + if (currentPosition < 0) { + // Processing of the page hasn't been started yet. + createProbe(currentPage); + } + else { + Page remaining = pageTail(currentPage, currentPosition); + restoreProbe(remaining, currentJoinPosition, probePositionProducedRow, joinSourcePositions); + } + } + + private void processProbe(LookupSource lookupSource) + { + verify(probe != null); + + DriverYieldSignal yieldSignal = operatorContext.getDriverContext().getYieldSignal(); + while (!yieldSignal.isSet()) { + if (probe.getPosition() >= 0) { + if (!joinCurrentPosition(lookupSource, yieldSignal)) { + break; + } + if (!currentProbePositionProducedRow) { + currentProbePositionProducedRow = true; + } + } + currentProbePositionProducedRow = false; + if (!advanceProbePosition(lookupSource)) { + break; + } + statisticsCounter.recordProbe(joinSourcePositions); + joinSourcePositions = 0; + } + } + + private void restoreProbe(Page probePage, long joinPosition, boolean currentProbePositionProducedRow, int joinSourcePositions) + { + verify(probe == null); + createProbe(probePage); + if (probe != null) { + verify(probe.advanceNextPosition()); + } + this.joinPosition = joinPosition; + this.currentProbePositionProducedRow = currentProbePositionProducedRow; + this.joinSourcePositions = joinSourcePositions; + } + + private Page pageTail(Page currentPage, int startAtPosition) + { + verify(currentPage.getPositionCount() - startAtPosition >= 0); + return currentPage.getRegion(startAtPosition, currentPage.getPositionCount() - startAtPosition); + } + + @Override + public void close() + { + if (closed) { + return; + } + closed = true; + probe = null; + + try (Closer closer = Closer.create()) { + // `afterClose` must be run last. + // Closer is documented to mimic try-with-resource, which implies close will happen in reverse order. + closer.register(afterMemOpFinish::run); + closer.register(afterClose::run); + + closer.register(pageBuilder::reset); + closer.register(() -> Optional.ofNullable(lookupSourceProvider).ifPresent(LookupSourceProvider::close)); + closer.register(() -> { + if (snapshotState != null) { + snapshotState.close(); + } + }); + spiller.ifPresent(closer::register); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Produce rows matching join condition for the current probe position. If this method was called previously + * for the current probe position, calling this again will produce rows that wasn't been produced in previous + * invocations. + * + * @return true if all eligible rows have been produced; false otherwise + */ + private boolean joinCurrentPosition(LookupSource lookupSource, DriverYieldSignal yieldSignal) + { + // while we have a position on lookup side to join against... + while (joinPosition >= 0) { + if (lookupSource.isJoinPositionEligible(joinPosition, probe.getPosition(), probe.getPage())) { + currentProbePositionProducedRow = true; + + // Build count * Probe Rec, probe count * Build Rec, then add to Output Page Builder + pageBuilder.appendRow(probe, lookupSource, joinPosition); + joinSourcePositions++; + } + + // get next position on lookup side for this probe row + joinPosition = lookupSource.getNextJoinPosition(joinPosition, probe.getPosition(), probe.getPage()); + + if (yieldSignal.isSet() || tryBuildPage()) { + return false; + } + } + return true; + } + + /** + * @return whether there are more positions on probe side + */ + private boolean advanceProbePosition(LookupSource lookupSource) + { + if (!probe.advanceNextPosition()) { + clearProbe(); + return false; + } + + // update join position + joinPosition = probe.getCurrentJoinPosition(lookupSource); + return true; + } + + private boolean tryBuildPage() + { + if (pageBuilder.isFull()) { + buildPage(); + return true; + } + return false; + } + + private void buildPage() + { + verify(outputPage == null); + verify(probe != null); + + if (pageBuilder.isEmpty()) { + return; + } + + outputPage = pageBuilder.build(probe); + pageBuilder.reset(); + } + + private void clearProbe() + { + // Before updating the probe flush the current page + buildPage(); + probe = null; + } + + @Override + public boolean supportsConsolidatedWrites() + { + return false; + } +} diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/plan/Patterns.java b/presto-main/src/main/java/io/prestosql/sql/planner/plan/Patterns.java index 3a796f2af..3363af61b 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/plan/Patterns.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/plan/Patterns.java @@ -22,6 +22,7 @@ import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.GroupIdNode; import io.prestosql.spi.plan.IntersectNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.LimitNode; import io.prestosql.spi.plan.MarkDistinctNode; import io.prestosql.spi.plan.PlanNode; @@ -114,6 +115,11 @@ public class Patterns return typeOf(JoinNode.class); } + public static Pattern joinOnAggregation() + { + return typeOf(JoinOnAggregationNode.class); + } + public static Pattern spatialJoin() { return typeOf(SpatialJoinNode.class); -- Gitee From 48a9c98d8e6bb1afadc93b890c4652b3fec94117 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Thu, 23 Feb 2023 17:07:14 +0530 Subject: [PATCH 15/36] Group Join issue fixes and visit implementation in rules, scheduler. --- .../cost/CostCalculatorUsingExchanges.java | 12 ++++ .../CostCalculatorWithEstimatedExchanges.java | 13 ++++ .../policy/AllAtOnceExecutionSchedule.java | 9 +++ .../policy/PhasedExecutionSchedule.java | 7 ++ ...rioritizeUtilizationExecutionSchedule.java | 11 ++++ .../planner/DistributedExecutionPlanner.java | 21 ++++++ .../sql/planner/ExpressionExtractor.java | 8 +++ .../prestosql/sql/planner/PlanFragmenter.java | 66 +++++++++++++++++++ .../sql/planner/SchedulingOrderVisitor.java | 9 +++ .../sql/planner/SplitSourceFactory.java | 12 ++++ .../iterative/rule/HintedReorderJoins.java | 14 ++++ ...atedSubquerySelfJoinToWindowFunctions.java | 8 +++ ...erySelfJoinAggregatesToWindowFunction.java | 8 +++ .../optimizations/joins/JoinGraph.java | 20 ++++++ .../planner/sanity/DynamicFiltersChecker.java | 22 ++++++- .../ExternalFunctionPushDownChecker.java | 11 ++++ .../io/prestosql/utils/OptimizerUtils.java | 12 ++++ .../spi/plan/JoinOnAggregationNode.java | 41 ++++++++++-- 18 files changed, 296 insertions(+), 8 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/cost/CostCalculatorUsingExchanges.java b/presto-main/src/main/java/io/prestosql/cost/CostCalculatorUsingExchanges.java index 2af5f25c8..db32c0f45 100644 --- a/presto-main/src/main/java/io/prestosql/cost/CostCalculatorUsingExchanges.java +++ b/presto-main/src/main/java/io/prestosql/cost/CostCalculatorUsingExchanges.java @@ -22,6 +22,7 @@ import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.GroupIdNode; import io.prestosql.spi.plan.GroupReference; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.LimitNode; import io.prestosql.spi.plan.MarkDistinctNode; import io.prestosql.spi.plan.PlanNode; @@ -247,6 +248,17 @@ public class CostCalculatorUsingExchanges return costForLookupJoin(node, localCost); } + @Override + public PlanCostEstimate visitJoinOnAggregation(JoinOnAggregationNode node, Void context) + { + LocalCostEstimate localCost = calculateJoinCost( + node, + node.getLeft(), + node.getRight(), + Objects.equals(node.getDistributionType(), Optional.of(JoinNode.DistributionType.REPLICATED))); + return costForLookupJoin(node, localCost); + } + private LocalCostEstimate calculateJoinCost(PlanNode join, PlanNode probe, PlanNode build, boolean replicated) { int estimatedSourceDistributedTaskCount = taskCountEstimator.estimateSourceDistributedTaskCount(); diff --git a/presto-main/src/main/java/io/prestosql/cost/CostCalculatorWithEstimatedExchanges.java b/presto-main/src/main/java/io/prestosql/cost/CostCalculatorWithEstimatedExchanges.java index 9785c74c0..df4696b44 100644 --- a/presto-main/src/main/java/io/prestosql/cost/CostCalculatorWithEstimatedExchanges.java +++ b/presto-main/src/main/java/io/prestosql/cost/CostCalculatorWithEstimatedExchanges.java @@ -18,6 +18,7 @@ import io.prestosql.Session; import io.prestosql.spi.plan.AggregationNode; import io.prestosql.spi.plan.GroupReference; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.UnionNode; import io.prestosql.sql.planner.TypeProvider; @@ -135,6 +136,18 @@ public class CostCalculatorWithEstimatedExchanges taskCountEstimator.estimateSourceDistributedTaskCount()); } + @Override + public LocalCostEstimate visitJoinOnAggregation(JoinOnAggregationNode node, Void context) + { + return calculateJoinExchangeCost( + node.getLeft(), + node.getRight(), + stats, + types, + Objects.equals(node.getDistributionType(), Optional.of(JoinNode.DistributionType.REPLICATED)), + taskCountEstimator.estimateSourceDistributedTaskCount()); + } + @Override public LocalCostEstimate visitSemiJoin(SemiJoinNode node, Void context) { diff --git a/presto-main/src/main/java/io/prestosql/execution/scheduler/policy/AllAtOnceExecutionSchedule.java b/presto-main/src/main/java/io/prestosql/execution/scheduler/policy/AllAtOnceExecutionSchedule.java index c9e504ad5..1aea5fa7c 100644 --- a/presto-main/src/main/java/io/prestosql/execution/scheduler/policy/AllAtOnceExecutionSchedule.java +++ b/presto-main/src/main/java/io/prestosql/execution/scheduler/policy/AllAtOnceExecutionSchedule.java @@ -20,6 +20,7 @@ import com.google.common.collect.Ordering; import io.prestosql.execution.SqlStageExecution; import io.prestosql.execution.StageState; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.UnionNode; import io.prestosql.sql.planner.PlanFragment; @@ -140,6 +141,14 @@ public class AllAtOnceExecutionSchedule return null; } + @Override + public Void visitJoinOnAggregation(JoinOnAggregationNode node, Void context) + { + node.getRight().accept(this, context); + node.getLeft().accept(this, context); + return null; + } + @Override public Void visitSemiJoin(SemiJoinNode node, Void context) { diff --git a/presto-main/src/main/java/io/prestosql/execution/scheduler/policy/PhasedExecutionSchedule.java b/presto-main/src/main/java/io/prestosql/execution/scheduler/policy/PhasedExecutionSchedule.java index 914d44402..a9f16faff 100644 --- a/presto-main/src/main/java/io/prestosql/execution/scheduler/policy/PhasedExecutionSchedule.java +++ b/presto-main/src/main/java/io/prestosql/execution/scheduler/policy/PhasedExecutionSchedule.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableSet; import io.prestosql.execution.SqlStageExecution; import io.prestosql.execution.StageState; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.UnionNode; import io.prestosql.sql.planner.PlanFragment; @@ -205,6 +206,12 @@ public class PhasedExecutionSchedule return processJoin(node.getRight(), node.getLeft(), currentFragmentId); } + @Override + public Set visitJoinOnAggregation(JoinOnAggregationNode node, PlanFragmentId currentFragmentId) + { + return processJoin(node.getRight(), node.getLeft(), currentFragmentId); + } + @Override public Set visitSpatialJoin(SpatialJoinNode node, PlanFragmentId currentFragmentId) { diff --git a/presto-main/src/main/java/io/prestosql/execution/scheduler/policy/PrioritizeUtilizationExecutionSchedule.java b/presto-main/src/main/java/io/prestosql/execution/scheduler/policy/PrioritizeUtilizationExecutionSchedule.java index 2b3cf8783..fce5c90eb 100644 --- a/presto-main/src/main/java/io/prestosql/execution/scheduler/policy/PrioritizeUtilizationExecutionSchedule.java +++ b/presto-main/src/main/java/io/prestosql/execution/scheduler/policy/PrioritizeUtilizationExecutionSchedule.java @@ -27,6 +27,7 @@ import io.prestosql.spi.QueryId; import io.prestosql.spi.plan.AggregationNode; import io.prestosql.spi.plan.CTEScanNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.sql.planner.PlanFragment; import io.prestosql.sql.planner.plan.CacheTableWriterNode; @@ -367,6 +368,16 @@ public class PrioritizeUtilizationExecutionSchedule currentFragmentId); } + @Override + public FragmentSubGraph visitJoinOnAggregation(JoinOnAggregationNode node, PlanFragmentId currentFragmentId) + { + return processJoin( + node.getDistributionType().orElseThrow(() -> new NoSuchElementException("No Value Present")) == JoinNode.DistributionType.REPLICATED, + node.getLeft(), + node.getRight(), + currentFragmentId); + } + @Override public FragmentSubGraph visitSpatialJoin(SpatialJoinNode node, PlanFragmentId currentFragmentId) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/DistributedExecutionPlanner.java b/presto-main/src/main/java/io/prestosql/sql/planner/DistributedExecutionPlanner.java index 798a80aca..db688474f 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/DistributedExecutionPlanner.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/DistributedExecutionPlanner.java @@ -40,6 +40,7 @@ import io.prestosql.spi.plan.CTEScanNode; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.GroupIdNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.LimitNode; import io.prestosql.spi.plan.MarkDistinctNode; import io.prestosql.spi.plan.PlanNode; @@ -391,6 +392,17 @@ public class DistributedExecutionPlanner .build(); } + @Override + public Map visitJoinOnAggregation(JoinOnAggregationNode node, Void context) + { + Map leftSplits = node.getLeft().accept(this, context); + Map rightSplits = node.getRight().accept(this, context); + return ImmutableMap.builder() + .putAll(leftSplits) + .putAll(rightSplits) + .build(); + } + @Override public Map visitSemiJoin(SemiJoinNode node, Void context) { @@ -770,6 +782,15 @@ public class DistributedExecutionPlanner return ret; } + @Override + public Map visitJoinOnAggregation(JoinOnAggregationNode node, Void context) + { + pendingJoins++; + Map ret = super.visitJoinOnAggregation(node, context); + sourceStack.pop(); + return ret; + } + @Override public Map visitSemiJoin(SemiJoinNode node, Void context) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/ExpressionExtractor.java b/presto-main/src/main/java/io/prestosql/sql/planner/ExpressionExtractor.java index fd1be43d6..d9664f709 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/ExpressionExtractor.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/ExpressionExtractor.java @@ -19,6 +19,7 @@ import io.prestosql.spi.plan.AggregationNode.Aggregation; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.GroupReference; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.ProjectNode; import io.prestosql.spi.plan.ValuesNode; @@ -121,6 +122,13 @@ public final class ExpressionExtractor return super.visitJoin(node, context); } + @Override + public Void visitJoinOnAggregation(JoinOnAggregationNode node, Consumer context) + { + node.getFilter().ifPresent(context); + return super.visitJoinOnAggregation(node, context); + } + @Override public Void visitValues(ValuesNode node, Consumer context) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/PlanFragmenter.java b/presto-main/src/main/java/io/prestosql/sql/planner/PlanFragmenter.java index 437c989fd..259663c9e 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/PlanFragmenter.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/PlanFragmenter.java @@ -33,6 +33,7 @@ import io.prestosql.spi.metadata.TableHandle; import io.prestosql.spi.plan.AggregationNode; import io.prestosql.spi.plan.CTEScanNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.PlanNodeId; import io.prestosql.spi.plan.ProjectNode; @@ -761,6 +762,71 @@ public class PlanFragmenter } } + @Override + public GroupedExecutionProperties visitJoinOnAggregation(JoinOnAggregationNode node, Void context) + { + GroupedExecutionProperties left = node.getLeft().accept(this, null); + GroupedExecutionProperties right = node.getRight().accept(this, null); + + if (!node.getDistributionType().isPresent()) { + // This is possible when the optimizers is invoked with `forceSingleNode` set to true. + return GroupedExecutionProperties.notCapable(); + } + + if ((node.getType() == JoinNode.Type.RIGHT || node.getType() == JoinNode.Type.FULL) && !right.currentNodeCapable) { + // For a plan like this, if the fragment participates in grouped execution, + // the LookupOuterOperator corresponding to the RJoin will not work execute properly. + // + // * The operator has to execute as not-grouped because it can only look at the "used" flags in + // join build after all probe has finished. + // * The operator has to execute as grouped the subsequent LJoin expects that incoming + // operators are grouped. Otherwise, the LJoin won't be able to throw out the build side + // for each group as soon as the group completes. + // + // LJoin + // / \ + // RJoin Scan + // / \ + // Scan Remote + // + // TODO: + // The RJoin can still execute as grouped if there is no subsequent operator that depends + // on the RJoin being executed in a grouped manner. However, this is not currently implemented. + // Support for this scenario is already implemented in the execution side. + return GroupedExecutionProperties.notCapable(); + } + + switch (node.getDistributionType().get()) { + case REPLICATED: + // Broadcast join maintains partitioning for the left side. + // Right side of a broadcast is not capable of grouped execution because it always comes from a remote exchange. + checkState(!right.currentNodeCapable); + return left; + case PARTITIONED: + if (left.currentNodeCapable && right.currentNodeCapable) { + return new GroupedExecutionProperties( + true, + true, + ImmutableList.builder() + .addAll(left.capableTableScanNodes) + .addAll(right.capableTableScanNodes) + .build()); + } + // right.subTreeUseful && !left.currentNodeCapable: + // It's not particularly helpful to do grouped execution on the right side + // because the benefit is likely cancelled out due to required buffering for hash build. + // In theory, it could still be helpful (e.g. when the underlying aggregation's intermediate group state maybe larger than aggregation output). + // However, this is not currently implemented. JoinBridgeManager need to support such a lifecycle. + // !right.currentNodeCapable: + // The build/right side needs to buffer fully for this JOIN, but the probe/left side will still stream through. + // As a result, there is no reason to change currentNodeCapable or subTreeUseful to false. + // + return left; + default: + throw new UnsupportedOperationException("Unknown distribution type: " + node.getDistributionType()); + } + } + @Override public GroupedExecutionProperties visitAggregation(AggregationNode node, Void context) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/SchedulingOrderVisitor.java b/presto-main/src/main/java/io/prestosql/sql/planner/SchedulingOrderVisitor.java index 722b3302c..f8a055ad5 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/SchedulingOrderVisitor.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/SchedulingOrderVisitor.java @@ -16,6 +16,7 @@ package io.prestosql.sql.planner; import com.google.common.collect.ImmutableList; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.PlanNodeId; import io.prestosql.spi.plan.TableScanNode; @@ -59,6 +60,14 @@ public class SchedulingOrderVisitor return null; } + @Override + public Void visitJoinOnAggregation(JoinOnAggregationNode node, Consumer schedulingOrder) + { + node.getRight().accept(this, schedulingOrder); + node.getLeft().accept(this, schedulingOrder); + return null; + } + @Override public Void visitSemiJoin(SemiJoinNode node, Consumer schedulingOrder) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/SplitSourceFactory.java b/presto-main/src/main/java/io/prestosql/sql/planner/SplitSourceFactory.java index c12a17892..fb32fe9af 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/SplitSourceFactory.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/SplitSourceFactory.java @@ -23,6 +23,7 @@ import io.prestosql.spi.plan.AggregationNode; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.GroupIdNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.LimitNode; import io.prestosql.spi.plan.MarkDistinctNode; import io.prestosql.spi.plan.PlanNode; @@ -177,6 +178,17 @@ public class SplitSourceFactory .build(); } + @Override + public Map visitJoinOnAggregation(JoinOnAggregationNode node, Void context) + { + Map leftSplits = node.getLeft().accept(this, context); + Map rightSplits = node.getRight().accept(this, context); + return ImmutableMap.builder() + .putAll(leftSplits) + .putAll(rightSplits) + .build(); + } + @Override public Map visitSemiJoin(SemiJoinNode node, Void context) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/HintedReorderJoins.java b/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/HintedReorderJoins.java index 065b15316..92c637332 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/HintedReorderJoins.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/HintedReorderJoins.java @@ -40,6 +40,7 @@ import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.JoinNode; import io.prestosql.spi.plan.JoinNode.DistributionType; import io.prestosql.spi.plan.JoinNode.EquiJoinClause; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.PlanNodeIdAllocator; import io.prestosql.spi.plan.Symbol; @@ -916,6 +917,19 @@ public class HintedReorderJoins return null; } + @Override + public Void visitJoinOnAggregation(JoinOnAggregationNode node, StringBuilder context) + { + PlanNode left = lookup.resolve(node.getLeft()); + PlanNode right = lookup.resolve(node.getRight()); + context.append('('); + left.accept(this, context); + context.append(','); + right.accept(this, context); + context.append(')'); + return null; + } + public static boolean startsWith(String actualPattern, String expectedPattern) { return expectedPattern.contains(actualPattern); diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/TransformUnCorrelatedSubquerySelfJoinToWindowFunctions.java b/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/TransformUnCorrelatedSubquerySelfJoinToWindowFunctions.java index 6653d0d6c..f4ac8af79 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/TransformUnCorrelatedSubquerySelfJoinToWindowFunctions.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/TransformUnCorrelatedSubquerySelfJoinToWindowFunctions.java @@ -30,6 +30,7 @@ import io.prestosql.spi.plan.Assignments; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.GroupReference; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.OrderingScheme; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.ProjectNode; @@ -616,6 +617,13 @@ public class TransformUnCorrelatedSubquerySelfJoinToWindowFunctions return super.visitJoin(node, context); } + @Override + public Void visitJoinOnAggregation(JoinOnAggregationNode node, Details context) + { + context.joinClauses.addAll(node.getCriteria()); + return super.visitJoinOnAggregation(node, context); + } + @Override public Void visitProject(ProjectNode node, Details context) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/TransformUncorrelatedSubquerySelfJoinAggregatesToWindowFunction.java b/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/TransformUncorrelatedSubquerySelfJoinAggregatesToWindowFunction.java index e81c26210..d6d6fd446 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/TransformUncorrelatedSubquerySelfJoinAggregatesToWindowFunction.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/TransformUncorrelatedSubquerySelfJoinAggregatesToWindowFunction.java @@ -30,6 +30,7 @@ import io.prestosql.spi.plan.Assignments; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.GroupReference; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.OrderingScheme; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.ProjectNode; @@ -590,6 +591,13 @@ public class TransformUncorrelatedSubquerySelfJoinAggregatesToWindowFunction return super.visitJoin(node, context); } + @Override + public Void visitJoinOnAggregation(JoinOnAggregationNode node, Details context) + { + context.joinClauses.addAll(node.getCriteria()); + return super.visitJoinOnAggregation(node, context); + } + @Override public Void visitProject(ProjectNode node, Details context) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/joins/JoinGraph.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/joins/JoinGraph.java index cecb10ce7..f95676255 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/joins/JoinGraph.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/joins/JoinGraph.java @@ -19,6 +19,7 @@ import com.google.common.collect.Multimap; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.GroupReference; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.PlanNodeId; import io.prestosql.spi.plan.ProjectNode; @@ -274,6 +275,25 @@ public class JoinGraph return graph; } + @Override + public JoinGraph visitJoinOnAggregation(JoinOnAggregationNode node, Context context) + { + //TODO: add support for non inner joins + if (node.getType() != INNER) { + return visitPlan(node, context); + } + + JoinGraph left = node.getLeft().accept(this, context); + JoinGraph right = node.getRight().accept(this, context); + + JoinGraph graph = left.joinWith(right, node.getCriteria(), context, node.getId()); + + if (node.getFilter().isPresent()) { + return graph.withFilter(node.getFilter().get()); + } + return graph; + } + @Override public JoinGraph visitProject(ProjectNode node, Context context) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/sanity/DynamicFiltersChecker.java b/presto-main/src/main/java/io/prestosql/sql/planner/sanity/DynamicFiltersChecker.java index 4148a22af..582720e89 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/sanity/DynamicFiltersChecker.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/sanity/DynamicFiltersChecker.java @@ -20,6 +20,7 @@ import io.prestosql.expressions.LogicalRowExpressions; import io.prestosql.metadata.Metadata; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.relation.RowExpression; import io.prestosql.sql.DynamicFilters; @@ -67,7 +68,7 @@ public class DynamicFiltersChecker public Set visitOutput(OutputNode node, List context) { Set unmatched = visitPlan(node, context); - verify(unmatched.isEmpty(), "All consumed dynamic filters could not be matched with a join/semi-join."); + verify(unmatched.isEmpty(), "All consumed dynamic filters could not be matched with a join/semi-join/group-join."); return unmatched; } @@ -90,6 +91,25 @@ public class DynamicFiltersChecker return ImmutableSet.copyOf(unmatched); } + @Override + public Set visitJoinOnAggregation(JoinOnAggregationNode node, List context) + { + Set currentJoinDynamicFilters = node.getDynamicFilters().keySet(); + context.addAll(currentJoinDynamicFilters); + Set consumedProbeSide = node.getLeft().accept(this, context); + verify(difference(currentJoinDynamicFilters, consumedProbeSide).isEmpty(), + "Dynamic filters present in group-join were not fully consumed by it's probe side."); + + context.removeAll(currentJoinDynamicFilters); + Set consumedBuildSide = node.getRight().accept(this, context); + verify(intersection(currentJoinDynamicFilters, consumedBuildSide).isEmpty()); + + Set unmatched = new HashSet<>(consumedBuildSide); + unmatched.addAll(consumedProbeSide); + unmatched.removeAll(currentJoinDynamicFilters); + return ImmutableSet.copyOf(unmatched); + } + @Override public Set visitSemiJoin(SemiJoinNode node, List context) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/sanity/ExternalFunctionPushDownChecker.java b/presto-main/src/main/java/io/prestosql/sql/planner/sanity/ExternalFunctionPushDownChecker.java index f493e1031..f1314da9d 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/sanity/ExternalFunctionPushDownChecker.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/sanity/ExternalFunctionPushDownChecker.java @@ -25,6 +25,7 @@ import io.prestosql.spi.connector.CatalogSchemaName; import io.prestosql.spi.plan.AggregationNode; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.ProjectNode; import io.prestosql.spi.plan.ValuesNode; @@ -165,6 +166,16 @@ public class ExternalFunctionPushDownChecker return null; } + @Override + public Void visitJoinOnAggregation(JoinOnAggregationNode node, Set context) + { + for (PlanNode planNode : node.getSources()) { + planNode.accept(this, context); + } + node.getFilter().ifPresent(rowExpression -> visitRowExpressions(context, rowExpression)); + return null; + } + @Override public Void visitSpatialJoin(SpatialJoinNode node, Set context) { diff --git a/presto-main/src/main/java/io/prestosql/utils/OptimizerUtils.java b/presto-main/src/main/java/io/prestosql/utils/OptimizerUtils.java index 536f90d59..75af063eb 100644 --- a/presto-main/src/main/java/io/prestosql/utils/OptimizerUtils.java +++ b/presto-main/src/main/java/io/prestosql/utils/OptimizerUtils.java @@ -19,6 +19,7 @@ import io.prestosql.Session; import io.prestosql.SystemSessionProperties; import io.prestosql.spi.HetuConstant; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.TableScanNode; import io.prestosql.sql.analyzer.FeaturesConfig; @@ -216,6 +217,17 @@ public class OptimizerUtils return super.visitJoin(node, context); } + @Override + public Void visitJoinOnAggregation(JoinOnAggregationNode node, Void context) + { + count++; + if (count >= maxLimit) { + // Break once reached the maximum count + return null; + } + return super.visitJoinOnAggregation(node, context); + } + public boolean isMaxCountReached() { return count >= maxLimit; diff --git a/presto-spi/src/main/java/io/prestosql/spi/plan/JoinOnAggregationNode.java b/presto-spi/src/main/java/io/prestosql/spi/plan/JoinOnAggregationNode.java index 7550c71d4..052b3374e 100644 --- a/presto-spi/src/main/java/io/prestosql/spi/plan/JoinOnAggregationNode.java +++ b/presto-spi/src/main/java/io/prestosql/spi/plan/JoinOnAggregationNode.java @@ -24,7 +24,7 @@ import io.prestosql.spi.relation.RowExpression; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; @@ -69,7 +69,6 @@ public class JoinOnAggregationNode @JsonProperty("distributionType") Optional distributionType, @JsonProperty("spillable") Optional spillable, @JsonProperty("dynamicFilters") Map dynamicFilters, - @JsonProperty("leftAggr") JoinInternalAggregation leftAggr, @JsonProperty("rightAggr") JoinInternalAggregation rightAggr, @JsonProperty("aggrOnAggrLeft") JoinInternalAggregation aggrOnAggrLeft, @@ -96,11 +95,11 @@ public class JoinOnAggregationNode this.spillable = spillable; this.dynamicFilters = ImmutableMap.copyOf(requireNonNull(dynamicFilters, "dynamicFilters is null")); - Set inputSymbols = ImmutableSet.builder() + /*Set inputSymbols = ImmutableSet.builder() .addAll(leftAggr.getSource().getOutputSymbols()) .addAll(rightAggr.getSource().getOutputSymbols()) - .build(); - checkArgument(new HashSet<>(inputSymbols).containsAll(outputSymbols), "Left and right join inputs do not contain all output symbols"); + .build();*/ + /*checkArgument(new HashSet<>(inputSymbols).containsAll(outputSymbols), "Left and right join inputs do not contain all output symbols");*/ checkArgument(!(criteria.isEmpty() && leftHashSymbol.isPresent()), "Left hash symbol is only valid in an equijoin"); checkArgument(!(criteria.isEmpty() && rightHashSymbol.isPresent()), "Right hash symbol is only valid in an equijoin"); @@ -241,7 +240,26 @@ public class JoinOnAggregationNode @Override public PlanNode replaceChildren(List newChildren) { - throw new UnsupportedOperationException("replaceChildren is not supported"); + checkArgument(newChildren.size() == 2, "expected newChildren to contain 2 nodes"); + JoinInternalAggregation leftAggr = (JoinInternalAggregation) this.leftAggr.replaceChildren(newChildren.subList(0, 1)); + JoinInternalAggregation rightAggr = (JoinInternalAggregation) this.rightAggr.replaceChildren(newChildren.subList(1, 2)); + JoinInternalAggregation aggrOnAggrLeft = (JoinInternalAggregation) this.aggrOnAggrLeft.replaceChildren(Collections.singletonList(leftAggr)); + JoinInternalAggregation aggrOnAggrRight = (JoinInternalAggregation) this.aggrOnAggrRight.replaceChildren(Collections.singletonList(rightAggr)); + + return new JoinOnAggregationNode(getId(), + type, + criteria, + filter, + leftHashSymbol, + rightHashSymbol, + distributionType, + spillable, + dynamicFilters, + leftAggr, + rightAggr, + aggrOnAggrLeft, + aggrOnAggrRight, + outputSymbols); } @Override @@ -342,7 +360,16 @@ public class JoinOnAggregationNode @Override public PlanNode replaceChildren(List newChildren) { - return null; + return new JoinInternalAggregation(getId(), + newChildren.get(0), + aggregations, + groupingSets, + preGroupedSymbols, + step, + hashSymbol, + groupIdSymbol, + aggregationType, + finalizeSymbol); } @JsonProperty("step") -- Gitee From 1f94e8afecc69d197fb46460e9e3d608a5dc93e2 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Thu, 23 Feb 2023 17:44:06 +0530 Subject: [PATCH 16/36] group Join issue fixes. --- .../io/prestosql/operator/HashBuilderGroupJoinOperator.java | 4 ++-- .../java/io/prestosql/operator/LookupGroupJoinOperator.java | 5 ----- .../io/prestosql/operator/LookupGroupJoinPageBuilder.java | 1 + .../java/io/prestosql/sql/planner/LocalExecutionPlanner.java | 4 ++-- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java index d8810f8dd..37f670b9c 100644 --- a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java @@ -329,9 +329,9 @@ public class HashBuilderGroupJoinOperator } // only flush if we are finishing or the aggregation builder is full - if (!aggregationBuilder.isFull()) { + /*if (!aggregationBuilder.isFull()) { return null; - } + }*/ outputPages = aggregationBuilder.buildResult(); } diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java index a393e1a48..59794fb0f 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java @@ -313,11 +313,6 @@ public class LookupGroupJoinOperator checkState(probe == null, "Current page has not been completely processed yet"); checkState(tryFetchLookupSourceProvider(), "Not ready to handle input yet"); - addInput2(page); - } - - private void addInput2(Page page) - { // create Aggregators and pass page to them for process checkState(!aggregationFinishing, "Operator is already finishing"); aggregationInputProcessed = true; diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java index 3917dc1ea..e2c3cb994 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java @@ -95,6 +95,7 @@ public class LookupGroupJoinPageBuilder } int probeChannelLength = outputProbeChannels.size(); + finalPageBuilder.declarePosition(); for (int i = 0; i < probeChannelLength; i++) { if (probeFinalPage.getBlock(outputProbeChannels.get(i)).isNull(0)) { finalPageBuilder.getBlockBuilder(i).appendNull(); diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java b/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java index 6d6da242b..8eece6be4 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java @@ -2912,7 +2912,7 @@ public class LocalExecutionPlanner ImmutableList.Builder finalOutputBuildTypes, ImmutableList.Builder buildFinalOutputChannelsBuilder) { - JoinInternalAggregation aggregationBuild = node.getAggrOnRight(); + JoinInternalAggregation aggregationBuild = node.getRightAggr(); LocalExecutionPlanContext buildContext = context.createSubContext(); PhysicalOperation buildSource = buildNode.accept(this, buildContext); if (buildSource.getPipelineExecutionStrategy() == GROUPED_EXECUTION) { @@ -3035,7 +3035,7 @@ public class LocalExecutionPlanner .collect(toImmutableList()); List buildFinalOutputChannels = ImmutableList.copyOf(getChannelsForSymbols(buildFinalOutputSymbols, finalOutputMappings.build())); buildFinalOutputChannelsBuilder.addAll(buildFinalOutputChannels); - + finalOutputBuildTypes.addAll(toTypes(buildFinalOutputSymbols, buildContext)); HashBuilderGroupJoinOperatorFactory hashBuilderOperatorFactory = new HashBuilderGroupJoinOperatorFactory( buildContext.getNextOperatorId(), node.getId(), -- Gitee From bcdc89c526295c2b068ea5d8bdba486dd37ae9e7 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Thu, 23 Feb 2023 18:58:35 +0530 Subject: [PATCH 17/36] group Join issue fixes. --- .../HashBuilderGroupJoinOperator.java | 4 ++ .../operator/LookupGroupJoinOperator.java | 40 ++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java index 37f670b9c..c2e1a8894 100644 --- a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java @@ -411,9 +411,13 @@ public class HashBuilderGroupJoinOperator private void finishAggregation() { + DriverYieldSignal yieldSignal = operatorContext.getDriverContext().getYieldSignal(); Page page = processAggregation(); while (page != null) { updateIndex(page); + if (yieldSignal.isSet()) { + break; + } page = processAggregation(); } } diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java index 59794fb0f..392932f17 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java @@ -551,13 +551,45 @@ public class LookupGroupJoinOperator // The reference must be set to null afterwards to avoid unaccounted memory. aggregationBuilder = null; } - memoryContext.setBytes(0); + //memoryContext.setBytes(0); aggregator.getPartialAggregationController().ifPresent( controller -> controller.onFlush(numberOfInputRowsProcessed, numberOfUniqueRowsProduced)); numberOfInputRowsProcessed = 0; numberOfUniqueRowsProduced = 0; } + protected void closeProbeAggrOnAggregationBuilder() + { + if (probeAggregationBuilder != null) { + probeAggregationBuilder.recordHashCollisions(hashCollisionsCounter); + probeAggregationBuilder.close(); + // aggregationBuilder.close() will release all memory reserved in memory accounting. + // The reference must be set to null afterwards to avoid unaccounted memory. + probeAggregationBuilder = null; + } + //memoryContext.setBytes(0); + /*aggrOnAggregator.getPartialAggregationController().ifPresent( + controller -> controller.onFlush(numberOfInputRowsProcessed, numberOfUniqueRowsProduced)); + numberOfInputRowsProcessed = 0; + numberOfUniqueRowsProduced = 0;*/ + } + + protected void closeBuildAggrOnAggregationBuilder() + { + if (buildAggregationBuilder != null) { + buildAggregationBuilder.recordHashCollisions(hashCollisionsCounter); + buildAggregationBuilder.close(); + // aggregationBuilder.close() will release all memory reserved in memory accounting. + // The reference must be set to null afterwards to avoid unaccounted memory. + buildAggregationBuilder = null; + } + //memoryContext.setBytes(0); + /*aggrOnAggregator.getPartialAggregationController().ifPresent( + controller -> controller.onFlush(numberOfInputRowsProcessed, numberOfUniqueRowsProduced)); + numberOfInputRowsProcessed = 0; + numberOfUniqueRowsProduced = 0;*/ + } + private void processProbe() { verify(probe != null); @@ -650,6 +682,9 @@ public class LookupGroupJoinOperator } closed = true; probe = null; + if (state == State.CONSUMING_INPUT) { + closeAggregationBuilder(); + } try (Closer closer = Closer.create()) { // `afterClose` must be run last. @@ -669,6 +704,9 @@ public class LookupGroupJoinOperator catch (IOException e) { throw new RuntimeException(e); } + closeProbeAggrOnAggregationBuilder(); + closeBuildAggrOnAggregationBuilder(); + memoryContext.setBytes(0); } /** -- Gitee From 86fa84731815845f6866aa8c033c8621ec635d65 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Thu, 23 Feb 2023 20:43:50 +0530 Subject: [PATCH 18/36] group Join issue fixes. --- .../HashBuilderGroupJoinOperator.java | 19 +------- .../HashBuilderGroupJoinOperatorFactory.java | 30 ++---------- .../operator/LookupGroupJoinOperator.java | 2 - .../sql/planner/LocalExecutionPlanner.java | 46 ++----------------- .../HashGenerationOptimizer.java | 4 +- .../spi/plan/JoinOnAggregationNode.java | 22 ++++----- 6 files changed, 20 insertions(+), 103 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java index c2e1a8894..723fee75f 100644 --- a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java @@ -100,11 +100,6 @@ public class HashBuilderGroupJoinOperator protected LocalMemoryContext memoryContext; protected WorkProcessor outputPages; - /*protected SettableFuture aggrFinishInProgress;*/ - //protected boolean aggregationFinishing; - //private boolean aggregationFinished; - //protected boolean aggregationInputProcessed; - // for yield when memory is not available protected Work unfinishedAggrWork; protected long numberOfInputRowsProcessed; @@ -112,9 +107,6 @@ public class HashBuilderGroupJoinOperator private final GroupJoinAggregator aggregator; private final GroupJoinAggregator aggrOnAggregator; - private final List buildFinalOutputSymbols; - private final List buildFinalOutputChannels; - public HashBuilderGroupJoinOperator( OperatorContext operatorContext, PartitionedLookupSourceFactory lookupSourceFactory, @@ -131,9 +123,7 @@ public class HashBuilderGroupJoinOperator boolean spillEnabled, boolean spillToHdfsEnabled, GroupJoinAggregator aggregator, - GroupJoinAggregator aggrOnAggregator, - List buildFinalOutputSymbols, - List buildFinalOutputChannels) + GroupJoinAggregator aggrOnAggregator) { requireNonNull(pagesIndexFactory, "pagesIndexFactory is null"); this.operatorContext = operatorContext; @@ -167,8 +157,6 @@ public class HashBuilderGroupJoinOperator this.aggregator = aggregator; this.aggrOnAggregator = aggrOnAggregator; - this.buildFinalOutputSymbols = buildFinalOutputSymbols; - this.buildFinalOutputChannels = buildFinalOutputChannels; } @Override @@ -328,11 +316,6 @@ public class HashBuilderGroupJoinOperator return null; } - // only flush if we are finishing or the aggregation builder is full - /*if (!aggregationBuilder.isFull()) { - return null; - }*/ - outputPages = aggregationBuilder.buildResult(); } diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java index cc38cf10f..ae220368d 100644 --- a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java @@ -57,8 +57,6 @@ public class HashBuilderGroupJoinOperatorFactory private boolean spillToHdfsEnabled; private final GroupJoinAggregator aggrOnAggrfactory; private final GroupJoinAggregator aggrfactory; - private final List buildFinalOutputSymbols; - private final List buildFinalOutputChannels; public static Builder builder() { @@ -81,9 +79,7 @@ public class HashBuilderGroupJoinOperatorFactory boolean spillEnabled, boolean spillToHdfsEnabled, GroupJoinAggregator aggrfactory, - GroupJoinAggregator aggrOnAggrfactory, - List buildFinalOutputSymbols, - List buildFinalOutputChannels) + GroupJoinAggregator aggrOnAggrfactory) { this.operatorId = operatorId; this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); @@ -106,9 +102,6 @@ public class HashBuilderGroupJoinOperatorFactory this.aggrfactory = aggrfactory; this.aggrOnAggrfactory = aggrOnAggrfactory; - - this.buildFinalOutputSymbols = buildFinalOutputSymbols; - this.buildFinalOutputChannels = buildFinalOutputChannels; } @Override @@ -141,9 +134,7 @@ public class HashBuilderGroupJoinOperatorFactory spillEnabled, spillToHdfsEnabled, aggrfactory, - aggrOnAggrfactory, - buildFinalOutputSymbols, - buildFinalOutputChannels); + aggrOnAggrfactory); } @Override @@ -167,9 +158,6 @@ public class HashBuilderGroupJoinOperatorFactory { private GroupJoinAggregator aggrOnAggrfactory; private GroupJoinAggregator aggrfactory; - private List buildFinalOutputSymbols; - private List buildFinalOutputChannels; - private int operatorId; private PlanNodeId planNodeId; private JoinBridgeManager lookupSourceFactoryManager; @@ -225,14 +213,6 @@ public class HashBuilderGroupJoinOperatorFactory return this; } - public Builder withBuildOutputInfo(List buildFinalOutputSymbols, - List buildFinalOutputChannels) - { - this.buildFinalOutputChannels = ImmutableList.copyOf(buildFinalOutputChannels); - this.buildFinalOutputSymbols = ImmutableList.copyOf(buildFinalOutputSymbols); - return this; - } - public Builder withAggrOnAggrFactory(List groupByTypes, List groupByChannels, List globalAggregationGroupIds, @@ -297,7 +277,6 @@ public class HashBuilderGroupJoinOperatorFactory { requireNonNull(aggrfactory, "aggrfactory is null"); requireNonNull(aggrOnAggrfactory, "aggrOnAggrfactory is null"); - requireNonNull(buildFinalOutputChannels, "buildFinalOutputChannels is null"); requireNonNull(hashChannels, "hashChannels is null"); return new HashBuilderGroupJoinOperatorFactory( operatorId, @@ -315,9 +294,8 @@ public class HashBuilderGroupJoinOperatorFactory spillEnabled, spillToHdfsEnabled, aggrfactory, - aggrOnAggrfactory, - buildFinalOutputSymbols, - buildFinalOutputChannels); + aggrOnAggrfactory + ); } } } diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java index 392932f17..2ea487d5e 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java @@ -332,8 +332,6 @@ public class LookupGroupJoinOperator } aggregationBuilder.updateMemory(); numberOfInputRowsProcessed += page.getPositionCount(); - - //createProbe(page); } private void createProbe(Page page) diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java b/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java index 8eece6be4..6719808b2 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java @@ -3052,9 +3052,8 @@ public class LocalExecutionPlanner taskCount > 1, isSpillToHdfsEnabled(context.getSession()), aggrfactory, - aggrOnAggrfactory, - buildFinalOutputSymbols, - buildFinalOutputChannels); + aggrOnAggrfactory + ); factoriesBuilder.add(hashBuilderOperatorFactory); @@ -3122,42 +3121,7 @@ public class LocalExecutionPlanner int startOutputChannel = 0; ImmutableMap.Builder outputMappings = ImmutableMap.builder(); - /*List accumulatorFactories = new ArrayList<>();*/ - - /*Optional groupIdChannel = getOutputMappingAndGroupIdChannel(aggregationProbe.getAggregations(), - aggregationProbe.getGroupingKeys(), - aggregationProbe.getHashSymbol(), - aggregationProbe.getGroupIdSymbol(), - probeSource, - startOutputChannel, - outputMappings, - accumulatorFactories, - Optional.empty(), - Optional.empty());*/ - /*List probeGroupByChannels = getChannelsForSymbols(aggregationProbe.getGroupingKeys(), probeSource.getLayout());*/ - /*List probeGroupbyTypes = probeGroupByChannels.stream() - .map(entry -> probeSource.getTypes().get(entry)) - .collect(toImmutableList()); - Optional probeAggrHashChannel = aggregationProbe.getHashSymbol().map(channelGetter(probeSource));*/ - /*JoinInternalAggregation aggregationOnLeftProbe = node.getAggrOnLeft();*/ ImmutableMap.Builder finalOutputMappings = ImmutableMap.builder(); - /*List finalAccumulatorFactories = new ArrayList<>(); - List finalGroupByChannels = getChannelsForSymbols(aggregationOnLeftProbe.getGroupingKeys(), probeAggrLayout);*/ - /*List finalGroupByTypes = finalGroupByChannels.stream() - .map(probeAggrTypes::get) - .collect(toImmutableList()); - Optional finalHashChannel = aggregationOnLeftProbe.getHashSymbol().map(channelGetter(probeAggrLayout)); - Optional finalGroupIdChannel = getOutputMappingAndGroupIdChannel(aggregationOnLeftProbe.getAggregations(), - aggregationOnLeftProbe.getGroupingKeys(), - aggregationOnLeftProbe.getHashSymbol(), - aggregationOnLeftProbe.getGroupIdSymbol(), - probeAggrLayout, - probeAggrTypes, - startOutputChannel, - outputMappings, - finalAccumulatorFactories, - Optional.empty(), - Optional.empty());*/ GroupJoinAggregator aggrfactory = prepareGroupJoinAggregator(aggregationProbe, probeSource.getLayout(), @@ -4415,13 +4379,13 @@ public class LocalExecutionPlanner public static List toTypes(List symbols, LocalExecutionPlanContext context) { - ImmutableList.Builder types = ImmutableList.builder(); + ImmutableList.Builder builder = ImmutableList.builder(); symbols.forEach(symbol -> { Type type = context.getTypes().get(symbol); checkArgument(type != null, "Layout does not have a symbol for every output channel: %s", symbol); - types.add(type); + builder.add(type); }); - return types.build(); + return builder.build(); } public static List toTypes(Map layout, LocalExecutionPlanContext context) diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/HashGenerationOptimizer.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/HashGenerationOptimizer.java index 666b52126..3db239263 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/HashGenerationOptimizer.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/HashGenerationOptimizer.java @@ -495,8 +495,8 @@ public class HashGenerationOptimizer Optional rightAggrHashComputation = computeHash(metadata, planSymbolAllocator, rightGrpByKeys); ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); builder.put(rightAggrHashComputation.get(), rightAggrHashComputation.get()); - right = planAndEnforce(left.getNode(), new HashComputationSet(builder.build()), true, new HashComputationSet(builder.build())); - rightHashSymbolAggr = Optional.ofNullable(left.getRequiredHashSymbol(rightAggrHashComputation.get())); + right = planAndEnforce(right.getNode(), new HashComputationSet(builder.build()), true, new HashComputationSet(builder.build())); + rightHashSymbolAggr = Optional.ofNullable(right.getRequiredHashSymbol(rightAggrHashComputation.get())); } else { right = planAndEnforce(node.getRight(), new HashComputationSet(rightHashComputation), true, new HashComputationSet(rightHashComputation)); diff --git a/presto-spi/src/main/java/io/prestosql/spi/plan/JoinOnAggregationNode.java b/presto-spi/src/main/java/io/prestosql/spi/plan/JoinOnAggregationNode.java index 052b3374e..8c2df7914 100644 --- a/presto-spi/src/main/java/io/prestosql/spi/plan/JoinOnAggregationNode.java +++ b/presto-spi/src/main/java/io/prestosql/spi/plan/JoinOnAggregationNode.java @@ -95,12 +95,6 @@ public class JoinOnAggregationNode this.spillable = spillable; this.dynamicFilters = ImmutableMap.copyOf(requireNonNull(dynamicFilters, "dynamicFilters is null")); - /*Set inputSymbols = ImmutableSet.builder() - .addAll(leftAggr.getSource().getOutputSymbols()) - .addAll(rightAggr.getSource().getOutputSymbols()) - .build();*/ - /*checkArgument(new HashSet<>(inputSymbols).containsAll(outputSymbols), "Left and right join inputs do not contain all output symbols");*/ - checkArgument(!(criteria.isEmpty() && leftHashSymbol.isPresent()), "Left hash symbol is only valid in an equijoin"); checkArgument(!(criteria.isEmpty() && rightHashSymbol.isPresent()), "Right hash symbol is only valid in an equijoin"); @@ -241,10 +235,10 @@ public class JoinOnAggregationNode public PlanNode replaceChildren(List newChildren) { checkArgument(newChildren.size() == 2, "expected newChildren to contain 2 nodes"); - JoinInternalAggregation leftAggr = (JoinInternalAggregation) this.leftAggr.replaceChildren(newChildren.subList(0, 1)); - JoinInternalAggregation rightAggr = (JoinInternalAggregation) this.rightAggr.replaceChildren(newChildren.subList(1, 2)); - JoinInternalAggregation aggrOnAggrLeft = (JoinInternalAggregation) this.aggrOnAggrLeft.replaceChildren(Collections.singletonList(leftAggr)); - JoinInternalAggregation aggrOnAggrRight = (JoinInternalAggregation) this.aggrOnAggrRight.replaceChildren(Collections.singletonList(rightAggr)); + JoinInternalAggregation leftAggrLocal = (JoinInternalAggregation) this.leftAggr.replaceChildren(newChildren.subList(0, 1)); + JoinInternalAggregation rightAggrLocal = (JoinInternalAggregation) this.rightAggr.replaceChildren(newChildren.subList(1, 2)); + JoinInternalAggregation aggrOnAggrLeftLocal = (JoinInternalAggregation) this.aggrOnAggrLeft.replaceChildren(Collections.singletonList(leftAggrLocal)); + JoinInternalAggregation aggrOnAggrRightLocal = (JoinInternalAggregation) this.aggrOnAggrRight.replaceChildren(Collections.singletonList(rightAggrLocal)); return new JoinOnAggregationNode(getId(), type, @@ -255,10 +249,10 @@ public class JoinOnAggregationNode distributionType, spillable, dynamicFilters, - leftAggr, - rightAggr, - aggrOnAggrLeft, - aggrOnAggrRight, + leftAggrLocal, + rightAggrLocal, + aggrOnAggrLeftLocal, + aggrOnAggrRightLocal, outputSymbols); } -- Gitee From 55bf16af1d8a770417bcac1db61d08894b8e13a8 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Fri, 24 Feb 2023 11:07:34 +0530 Subject: [PATCH 19/36] group Join issue fixes. --- .../io/prestosql/operator/HashBuilderGroupJoinOperator.java | 1 - .../operator/HashBuilderGroupJoinOperatorFactory.java | 4 +--- .../java/io/prestosql/sql/planner/LocalExecutionPlanner.java | 3 +-- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java index 723fee75f..9cd0816c6 100644 --- a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java @@ -21,7 +21,6 @@ import io.prestosql.operator.aggregation.builder.AggregationBuilder; import io.prestosql.operator.aggregation.builder.InMemoryHashAggregationBuilder; import io.prestosql.operator.aggregation.builder.InMemoryHashAggregationBuilderWithReset; import io.prestosql.spi.Page; -import io.prestosql.spi.plan.Symbol; import io.prestosql.sql.gen.JoinFilterFunctionCompiler; import javax.annotation.Nullable; diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java index ae220368d..7b0f35e73 100644 --- a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java @@ -20,7 +20,6 @@ import io.prestosql.operator.aggregation.AccumulatorFactory; import io.prestosql.operator.aggregation.partial.PartialAggregationController; import io.prestosql.spi.plan.AggregationNode; import io.prestosql.spi.plan.PlanNodeId; -import io.prestosql.spi.plan.Symbol; import io.prestosql.spi.type.Type; import io.prestosql.sql.gen.JoinCompiler; import io.prestosql.sql.gen.JoinFilterFunctionCompiler.JoinFilterFunctionFactory; @@ -294,8 +293,7 @@ public class HashBuilderGroupJoinOperatorFactory spillEnabled, spillToHdfsEnabled, aggrfactory, - aggrOnAggrfactory - ); + aggrOnAggrfactory); } } } diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java b/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java index 6719808b2..73aef66bd 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java @@ -3052,8 +3052,7 @@ public class LocalExecutionPlanner taskCount > 1, isSpillToHdfsEnabled(context.getSession()), aggrfactory, - aggrOnAggrfactory - ); + aggrOnAggrfactory); factoriesBuilder.add(hashBuilderOperatorFactory); -- Gitee From 92f3c4dcc1b7b5a303c1e186cb44f2a5428297f1 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Fri, 24 Feb 2023 11:15:58 +0530 Subject: [PATCH 20/36] group Join issue fixes for Q23 and stat rule update. --- .../cost/JoinOnAggregationStatsRule.java | 7 +++-- .../optimizations/PropertyDerivations.java | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/cost/JoinOnAggregationStatsRule.java b/presto-main/src/main/java/io/prestosql/cost/JoinOnAggregationStatsRule.java index d4c886955..4adc9b692 100644 --- a/presto-main/src/main/java/io/prestosql/cost/JoinOnAggregationStatsRule.java +++ b/presto-main/src/main/java/io/prestosql/cost/JoinOnAggregationStatsRule.java @@ -81,13 +81,12 @@ public class JoinOnAggregationStatsRule PlanNodeStatsEstimate rightStats = sourceStats.getStats(node.getRight()); PlanNodeStatsEstimate leftAggrStats = AggregationStatsRule.groupBy(leftStats, node.getLeftAggr().getGroupingKeys(), node.getLeftAggr().getAggregations()); PlanNodeStatsEstimate rightAggrStats = AggregationStatsRule.groupBy(rightStats, node.getRightAggr().getGroupingKeys(), node.getRightAggr().getAggregations()); - // TODO Vineet Check if following consideration is correct?? - PlanNodeStatsEstimate leftAggrOnAggrStats = AggregationStatsRule.groupBy(leftAggrStats, node.getAggrOnLeft().getGroupingKeys(), node.getAggrOnLeft().getAggregations()); - PlanNodeStatsEstimate rightAggrOnAggrStats = AggregationStatsRule.groupBy(rightAggrStats, node.getAggrOnRight().getGroupingKeys(), node.getAggrOnRight().getAggregations()); - PlanNodeStatsEstimate crossJoinStats = crossJoinStats(node, leftAggrOnAggrStats, rightAggrOnAggrStats, types); + + PlanNodeStatsEstimate crossJoinStats = crossJoinStats(node, leftAggrStats, rightAggrStats, types); switch (node.getType()) { case INNER: + // TODO Vineet Check - how to consider cost of AggrOnAggr after join return Optional.of(computeInnerJoinStats(node, crossJoinStats, session, types)); default: throw new IllegalStateException("Unknown group join type: " + node.getType()); diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PropertyDerivations.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PropertyDerivations.java index 76254d322..3f5ca4bb9 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PropertyDerivations.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PropertyDerivations.java @@ -34,6 +34,7 @@ import io.prestosql.spi.plan.CTEScanNode; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.GroupIdNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.LimitNode; import io.prestosql.spi.plan.MarkDistinctNode; import io.prestosql.spi.plan.OrderingScheme; @@ -514,6 +515,32 @@ public class PropertyDerivations } } + @Override + public ActualProperties visitJoinOnAggregation(JoinOnAggregationNode node, List inputProperties) + { + ActualProperties probeProperties = inputProperties.get(0); + ActualProperties buildProperties = inputProperties.get(1); + + boolean unordered = spillPossible(session, node.getType()); + + switch (node.getType()) { + case INNER: + probeProperties = probeProperties.translate(column -> filterOrRewrite(node.getOutputSymbols(), node.getCriteria(), column)); + buildProperties = buildProperties.translate(column -> filterOrRewrite(node.getOutputSymbols(), node.getCriteria(), column)); + + Map constants = new HashMap<>(); + constants.putAll(probeProperties.getConstants()); + constants.putAll(buildProperties.getConstants()); + + return ActualProperties.builderFrom(probeProperties) + .constants(constants) + .unordered(unordered) + .build(); + default: + throw new UnsupportedOperationException("Unsupported group join type: " + node.getType()); + } + } + @Override public ActualProperties visitSemiJoin(SemiJoinNode node, List inputProperties) { -- Gitee From 12bdc24adc19ad1e905372dec49515110b1533d1 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Fri, 24 Feb 2023 11:30:04 +0530 Subject: [PATCH 21/36] group Join visitor call updates in rules. --- .../optimizations/AddLocalExchanges.java | 36 +++++++++++++++++++ .../optimizations/AddReuseExchange.java | 20 +++++++++++ .../planner/optimizations/PruneCTENodes.java | 30 ++++++++++++++++ .../StreamPropertyDerivations.java | 22 ++++++++++++ .../spi/plan/JoinOnAggregationNode.java | 18 ++++++++++ 5 files changed, 126 insertions(+) diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/AddLocalExchanges.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/AddLocalExchanges.java index 033135b12..b01faba71 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/AddLocalExchanges.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/AddLocalExchanges.java @@ -26,6 +26,7 @@ import io.prestosql.spi.connector.SortingProperty; import io.prestosql.spi.plan.AggregationNode; import io.prestosql.spi.plan.CTEScanNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.LimitNode; import io.prestosql.spi.plan.MarkDistinctNode; import io.prestosql.spi.plan.PlanNode; @@ -706,6 +707,41 @@ public class AddLocalExchanges return rebaseAndDeriveProperties(node, ImmutableList.of(probe, build)); } + @Override + public PlanWithProperties visitJoinOnAggregation(JoinOnAggregationNode inputNode, StreamPreferredProperties parentPreferences) + { + JoinOnAggregationNode node = inputNode; + PlanWithProperties probe = planAndEnforce( + node.getLeft(), + defaultParallelism(session), + parentPreferences.constrainTo(node.getLeft().getOutputSymbols()).withDefaultParallelism(session)); + + if (isSpillEnabled(session)) { + if (probe.getProperties().getDistribution() != FIXED) { + // Disable spill for joins over non-fixed streams as otherwise we would need to insert local exchange. + // Such local exchanges can hurt performance when spill is not triggered. + // When spill is not triggered it should not induce performance penalty. + node = node.withSpillable(false); + } + else { + node = node.withSpillable(true); + } + } + + // this build consumes the input completely, so we do not pass through parent preferences + List buildHashSymbols = Lists.transform(node.getCriteria(), JoinNode.EquiJoinClause::getRight); + StreamPreferredProperties buildPreference; + if (getTaskConcurrency(session) > 1) { + buildPreference = exactlyPartitionedOn(buildHashSymbols); + } + else { + buildPreference = singleStream(); + } + PlanWithProperties build = planAndEnforce(node.getRight(), buildPreference, buildPreference); + + return rebaseAndDeriveProperties(node, ImmutableList.of(probe, build)); + } + @Override public PlanWithProperties visitSemiJoin(SemiJoinNode node, StreamPreferredProperties parentPreferences) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/AddReuseExchange.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/AddReuseExchange.java index 97caae6c0..418e6516d 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/AddReuseExchange.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/AddReuseExchange.java @@ -24,6 +24,7 @@ import io.prestosql.spi.plan.Assignments; import io.prestosql.spi.plan.CTEScanNode; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.PlanNodeIdAllocator; import io.prestosql.spi.plan.ProjectNode; @@ -202,6 +203,25 @@ public class AddReuseExchange return node; } + @Override + public PlanNode visitJoinOnAggregation(JoinOnAggregationNode inputNode, RewriteContext context) + { + JoinOnAggregationNode node = inputNode; + node = (JoinOnAggregationNode) visitPlan(node, context); + // verify right side + TableScanNode left = (TableScanNode) getTableScanNode(node.getLeft()); + TableScanNode right = (TableScanNode) getTableScanNode(node.getRight()); + if (left != null && right != null && WrapperScanNode.of(left).equals(WrapperScanNode.of(right))) { + WrapperScanNode leftNode = WrapperScanNode.of(left); + if (planNodeListHashMap.get(leftNode) != null) { + // These nodes are part of reuse exchange, adjust them. + planNodeListHashMap.remove(leftNode); + } + } + + return node; + } + private double getMaxTableSizeToEnableReuseExchange(TableStatistics stats, Map assignments) { return assignments.values().stream().map(x -> stats.getColumnStatistics().get(x)).filter(x -> x != null) diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PruneCTENodes.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PruneCTENodes.java index 3cce8552a..546137d7e 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PruneCTENodes.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PruneCTENodes.java @@ -22,6 +22,7 @@ import io.prestosql.spi.plan.AggregationNode; import io.prestosql.spi.plan.CTEScanNode; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.PlanNodeId; import io.prestosql.spi.plan.PlanNodeIdAllocator; @@ -148,6 +149,35 @@ public class PruneCTENodes return true; } + private boolean checkCTELevel(JoinOnAggregationNode node) + { + int leftLevel = getChildCTELevel(node.getLeft(), 0); + int rightLevel = getChildCTELevel(node.getRight(), 0); + if (leftLevel == 0 || rightLevel == 0) { + return true; + } + else if (leftLevel == rightLevel) { + return false; + } + return true; + } + + @Override + public PlanNode visitJoinOnAggregation(JoinOnAggregationNode node, RewriteContext context) + { + Integer left = getChildCTERefNum(node.getLeft()); + Integer right = getChildCTERefNum(node.getRight()); + if (left != null && right != null && left.equals(right) && checkCTELevel(node)) { + if (!isNodeAlreadyVisited) { + PlanNodeId probeCteNodeId = getProbeCTENodeId(node.getLeft()); + if (probeCteNodeId != null) { + probeCTEToPrune.add(probeCteNodeId); + } + } + } + return context.defaultRewrite(node, context.get()); + } + private PlanNodeId getProbeCTENodeId(PlanNode node) { if (node instanceof CTEScanNode) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/StreamPropertyDerivations.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/StreamPropertyDerivations.java index 5ee06e922..2338b2491 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/StreamPropertyDerivations.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/StreamPropertyDerivations.java @@ -28,6 +28,7 @@ import io.prestosql.spi.plan.CTEScanNode; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.GroupIdNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.LimitNode; import io.prestosql.spi.plan.MarkDistinctNode; import io.prestosql.spi.plan.PlanNode; @@ -226,6 +227,27 @@ public final class StreamPropertyDerivations } } + @Override + public StreamProperties visitJoinOnAggregation(JoinOnAggregationNode node, List inputProperties) + { + StreamProperties leftProperties = inputProperties.get(0); + boolean unordered = spillPossible(session, node); + + switch (node.getType()) { + case INNER: + return leftProperties + .translate(column -> PropertyDerivations.filterOrRewrite(node.getOutputSymbols(), node.getCriteria(), column)) + .unordered(unordered); + default: + throw new UnsupportedOperationException("Unsupported group join type: " + node.getType()); + } + } + + private static boolean spillPossible(Session session, JoinOnAggregationNode node) + { + return isSpillEnabled(session) && node.isSpillable().orElseThrow(() -> new IllegalArgumentException("spillable not yet set")); + } + private static boolean spillPossible(Session session, JoinNode node) { return isSpillEnabled(session) && node.isSpillable().orElseThrow(() -> new IllegalArgumentException("spillable not yet set")); diff --git a/presto-spi/src/main/java/io/prestosql/spi/plan/JoinOnAggregationNode.java b/presto-spi/src/main/java/io/prestosql/spi/plan/JoinOnAggregationNode.java index 8c2df7914..bae2293e8 100644 --- a/presto-spi/src/main/java/io/prestosql/spi/plan/JoinOnAggregationNode.java +++ b/presto-spi/src/main/java/io/prestosql/spi/plan/JoinOnAggregationNode.java @@ -262,6 +262,24 @@ public class JoinOnAggregationNode return visitor.visitJoinOnAggregation(this, context); } + public JoinOnAggregationNode withSpillable(boolean spillable) + { + return new JoinOnAggregationNode(getId(), + type, + criteria, + filter, + leftHashSymbol, + rightHashSymbol, + distributionType, + Optional.of(spillable), + dynamicFilters, + leftAggr, + rightAggr, + aggrOnAggrLeft, + aggrOnAggrRight, + outputSymbols); + } + public static class JoinInternalAggregation extends PlanNode { -- Gitee From cf437c965c02721f06da61be425456ed73bffcfd Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Fri, 24 Feb 2023 13:20:50 +0530 Subject: [PATCH 22/36] group Join memory context fixes. --- .../HashBuilderGroupJoinOperator.java | 40 ++++++--- .../operator/LookupGroupJoinOperator.java | 84 +++++++++---------- 2 files changed, 65 insertions(+), 59 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java index 9cd0816c6..b5ea7320d 100644 --- a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java @@ -29,7 +29,6 @@ import java.io.IOException; import java.util.List; import java.util.Optional; import java.util.OptionalInt; -import java.util.OptionalLong; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Verify.verify; @@ -88,15 +87,13 @@ public class HashBuilderGroupJoinOperator private Optional> lookupSourceNotNeeded = Optional.empty(); @Nullable private LookupSourceSupplier lookupSourceSupplier; - private OptionalLong lookupSourceChecksum = OptionalLong.empty(); - private Optional finishMemoryRevoke = Optional.empty(); - private final long expectedValues; private boolean spillToHdfsEnabled; protected AggregationBuilder aggregationBuilder; protected AggregationBuilder aggrOnAggregationBuilder; - protected LocalMemoryContext memoryContext; + protected LocalMemoryContext aggrMemoryContext; + protected LocalMemoryContext aggrOnAggrMemoryContext; protected WorkProcessor outputPages; // for yield when memory is not available @@ -144,14 +141,18 @@ public class HashBuilderGroupJoinOperator this.hashCollisionsCounter = new HashCollisionsCounter(operatorContext); operatorContext.setInfoSupplier(hashCollisionsCounter); - this.spillEnabled = spillEnabled; - this.spillToHdfsEnabled = spillToHdfsEnabled; - this.expectedValues = expectedPositions * 10L; + // TODO Vineet Use the actual spillEnabled flag when spill is supported + this.spillEnabled = false; + this.spillToHdfsEnabled = false; requireNonNull(operatorContext, "operatorContext is null"); - this.memoryContext = operatorContext.localUserMemoryContext(); + this.aggrMemoryContext = operatorContext.localUserMemoryContext(); + this.aggrOnAggrMemoryContext = operatorContext.localUserMemoryContext(); if (aggregator.isUseSystemMemory()) { - this.memoryContext = operatorContext.localSystemMemoryContext(); + this.aggrMemoryContext = operatorContext.localSystemMemoryContext(); + } + if (aggrOnAggregator.isUseSystemMemory()) { + this.aggrOnAggrMemoryContext = operatorContext.localSystemMemoryContext(); } this.aggregator = aggregator; @@ -241,7 +242,7 @@ public class HashBuilderGroupJoinOperator aggregator.getMaxPartialMemory(), aggregator.getJoinCompiler(), () -> { - memoryContext.setBytes(((InMemoryHashAggregationBuilder) aggregationBuilder).getSizeInMemory()); + aggrMemoryContext.setBytes(((InMemoryHashAggregationBuilder) aggregationBuilder).getSizeInMemory()); if (aggregator.getStep().isOutputPartial() && aggregator.getMaxPartialMemory().isPresent()) { // do not yield on memory for partial aggregations return true; @@ -259,7 +260,7 @@ public class HashBuilderGroupJoinOperator aggrOnAggregator.getMaxPartialMemory(), aggrOnAggregator.getJoinCompiler(), () -> { - memoryContext.setBytes(((InMemoryHashAggregationBuilder) aggrOnAggregationBuilder).getSizeInMemory()); + aggrOnAggrMemoryContext.setBytes(((InMemoryHashAggregationBuilder) aggrOnAggregationBuilder).getSizeInMemory()); if (aggrOnAggregator.getStep().isOutputPartial() && aggrOnAggregator.getMaxPartialMemory().isPresent()) { // do not yield on memory for partial aggregations return true; @@ -342,13 +343,25 @@ public class HashBuilderGroupJoinOperator // The reference must be set to null afterwards to avoid unaccounted memory. aggregationBuilder = null; } - memoryContext.setBytes(0); + aggrMemoryContext.setBytes(0); aggregator.getPartialAggregationController().ifPresent( controller -> controller.onFlush(numberOfInputRowsProcessed, numberOfUniqueRowsProduced)); numberOfInputRowsProcessed = 0; numberOfUniqueRowsProduced = 0; } + protected void closeAggrOnAggregationBuilder() + { + if (aggrOnAggregationBuilder != null) { + aggrOnAggregationBuilder.recordHashCollisions(hashCollisionsCounter); + aggrOnAggregationBuilder.close(); + // aggrOnAggregationBuilder.close() will release all memory reserved in memory accounting. + // The reference must be set to null afterwards to avoid unaccounted memory. + aggrOnAggregationBuilder = null; + } + aggrOnAggrMemoryContext.setBytes(0); + } + @Override public void finish() { @@ -480,6 +493,7 @@ public class HashBuilderGroupJoinOperator closer.register(index::clear); closer.register(() -> localUserMemoryContext.setBytes(0)); closer.register(() -> localRevocableMemoryContext.setBytes(0)); + closer.register(this::closeAggrOnAggregationBuilder); } catch (IOException e) { throw new RuntimeException(e); diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java index 2ea487d5e..37f6156dc 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java @@ -89,15 +89,10 @@ public class LookupGroupJoinOperator private final OperatorContext operatorContext; - private final List probeTypes; - private final List probeFinalOutputSymbols; - private final List probeFinalOutputChannels; - private final List buildFinalOutputChannels; private final GroupJoinProbeFactory joinProbeFactory; private final Runnable afterClose; private final PartitioningSpillerFactory partitioningSpillerFactory; - private Runnable afterMemOpFinish; private final OptionalInt lookupJoinsCount; private final HashGenerator hashGenerator; @@ -123,7 +118,6 @@ public class LookupGroupJoinOperator private boolean closed; private boolean finishing; private boolean unspilling; - private boolean isSpillerRestored; private boolean finished; private long joinPosition = -1; private int joinSourcePositions; @@ -151,18 +145,19 @@ public class LookupGroupJoinOperator protected AggregationBuilder aggregationBuilder; protected AggregationBuilder probeAggregationBuilder; protected AggregationBuilder buildAggregationBuilder; - protected LocalMemoryContext memoryContext; + protected LocalMemoryContext aggrMemoryContext; + protected LocalMemoryContext aggrOnAggrMemoryContext; private final HashCollisionsCounter hashCollisionsCounter; protected boolean aggregationFinishing; protected long numberOfInputRowsProcessed; protected long numberOfUniqueRowsProduced; - protected Work unfinishedWork; + protected Work unfinishedAggrWork; protected boolean aggregationInputProcessed; private final boolean spillEnabled = false; private boolean aggregationFinished; - protected WorkProcessor outputPages; + protected WorkProcessor aggrOutputPages; protected State state = State.CONSUMING_INPUT; public LookupGroupJoinOperator( @@ -187,7 +182,7 @@ public class LookupGroupJoinOperator List buildFinalOutputChannels) { this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); - this.probeTypes = ImmutableList.copyOf(requireNonNull(probeTypes, "probeTypes is null")); + List probeTypes1 = ImmutableList.copyOf(requireNonNull(probeTypes, "probeTypes is null")); requireNonNull(joinType, "joinType is null"); // Cannot use switch case here, because javac will synthesize an inner class and cause IllegalAccessError @@ -214,16 +209,17 @@ public class LookupGroupJoinOperator this.hashCollisionsCounter = new HashCollisionsCounter(operatorContext); operatorContext.setInfoSupplier(hashCollisionsCounter); - this.memoryContext = operatorContext.localUserMemoryContext(); + this.aggrMemoryContext = operatorContext.localUserMemoryContext(); + this.aggrOnAggrMemoryContext = operatorContext.localUserMemoryContext(); if (aggregator.isUseSystemMemory()) { - this.memoryContext = operatorContext.localSystemMemoryContext(); + this.aggrMemoryContext = operatorContext.localSystemMemoryContext(); + } + if (aggrOnAggregator.isUseSystemMemory()) { + this.aggrOnAggrMemoryContext = operatorContext.localSystemMemoryContext(); } this.aggregator = aggregator; this.aggrOnAggregator = aggrOnAggregator; - this.probeFinalOutputSymbols = probeFinalOutputSymbols; - this.probeFinalOutputChannels = probeFinalOutputChannels; - this.buildFinalOutputChannels = buildFinalOutputChannels; } @Override @@ -269,7 +265,7 @@ public class LookupGroupJoinOperator { // TODO Vineet check aggregation related conditions if (!finishing && state == State.CONSUMING_INPUT) { - if (aggregationFinishing || outputPages != null) { + if (aggregationFinishing || aggrOutputPages != null) { return false; } else if (aggregationBuilder != null && aggregationBuilder.isFull()) { @@ -277,13 +273,13 @@ public class LookupGroupJoinOperator } else { // TODO Vineet Need to move this out of needsInput and need to make it light weight. - if (unfinishedWork != null) { - boolean workDone = unfinishedWork.process(); + if (unfinishedAggrWork != null) { + boolean workDone = unfinishedAggrWork.process(); aggregationBuilder.updateMemory(); if (!workDone) { return false; } - unfinishedWork = null; + unfinishedAggrWork = null; } return true; } @@ -325,9 +321,9 @@ public class LookupGroupJoinOperator } // process the current page; save the unfinished work if we are waiting for memory - unfinishedWork = aggregationBuilder.processPage(page); - if (unfinishedWork.process()) { - unfinishedWork = null; + unfinishedAggrWork = aggregationBuilder.processPage(page); + if (unfinishedAggrWork.process()) { + unfinishedAggrWork = null; // TODO Vineet check if can index this pages here. } aggregationBuilder.updateMemory(); @@ -341,7 +337,7 @@ public class LookupGroupJoinOperator LookupSource lookupSource = lookupSourceProvider.withLease((lookupSourceLease -> lookupSourceLease.getLookupSource())); buildAggregationBuilder = lookupSource.getAggregationBuilder().duplicate(); } - probe = joinProbeFactory.createGroupJoinProbe(page/*newPage*/, false/*isSpilled*/, lookupSourceProvider, probeAggregationBuilder, buildAggregationBuilder); + probe = joinProbeFactory.createGroupJoinProbe(page, false, lookupSourceProvider, probeAggregationBuilder, buildAggregationBuilder); // initialize to invalid join position to force output code to advance the cursors joinPosition = -1; @@ -399,7 +395,7 @@ public class LookupGroupJoinOperator lookupSourceProvider = new StaticLookupSourceProvider(new EmptyLookupSource()); } - if (probe == null && finishing && unfinishedWork == null && !unspilling) { + if (probe == null && finishing && unfinishedAggrWork == null && !unspilling) { /* * We do not have input probe and we won't have any, as we're finishing. * Let LookupSourceFactory know LookupSources can be disposed as far as we're concerned. @@ -454,7 +450,7 @@ public class LookupGroupJoinOperator aggregator.getMaxPartialMemory(), aggregator.getJoinCompiler(), () -> { - memoryContext.setBytes(((InMemoryHashAggregationBuilder) aggregationBuilder).getSizeInMemory()); + aggrMemoryContext.setBytes(((InMemoryHashAggregationBuilder) aggregationBuilder).getSizeInMemory()); if (aggregator.getStep().isOutputPartial() && aggregator.getMaxPartialMemory().isPresent()) { // do not yield on memory for partial aggregations return true; @@ -472,7 +468,7 @@ public class LookupGroupJoinOperator aggrOnAggregator.getMaxPartialMemory(), aggrOnAggregator.getJoinCompiler(), () -> { - memoryContext.setBytes(((InMemoryHashAggregationBuilder) probeAggregationBuilder).getSizeInMemory()); + aggrOnAggrMemoryContext.setBytes(((InMemoryHashAggregationBuilder) probeAggregationBuilder).getSizeInMemory()); if (aggrOnAggregator.getStep().isOutputPartial() && aggrOnAggregator.getMaxPartialMemory().isPresent()) { // do not yield on memory for partial aggregations return true; @@ -492,27 +488,27 @@ public class LookupGroupJoinOperator } // process unfinished work if one exists - if (unfinishedWork != null) { - boolean workDone = unfinishedWork.process(); + if (unfinishedAggrWork != null) { + boolean workDone = unfinishedAggrWork.process(); aggregationBuilder.updateMemory(); if (!workDone) { return null; } - unfinishedWork = null; + unfinishedAggrWork = null; } - if (outputPages == null) { + if (aggrOutputPages == null) { if (aggregationFinishing) { if (!aggregationInputProcessed && aggregator.isProduceDefaultOutput()) { // global aggregations always generate an output row with the default aggregation output (e.g. 0 for COUNT, NULL for SUM) aggregationFinished = true; - state = State.SOURCE_BUILT; + /*state = State.SOURCE_BUILT;*/ return aggregator.getGlobalAggregationOutput(); } if (aggregationBuilder == null) { aggregationFinished = true; - state = State.SOURCE_BUILT; + /*state = State.SOURCE_BUILT;*/ return null; } } @@ -522,26 +518,26 @@ public class LookupGroupJoinOperator return null; } - outputPages = aggregationBuilder.buildResult(); + aggrOutputPages = aggregationBuilder.buildResult(); } - if (!outputPages.process()) { + if (!aggrOutputPages.process()) { return null; } - if (outputPages.isFinished()) { + if (aggrOutputPages.isFinished()) { closeAggregationBuilder(); return null; } - Page result = outputPages.getResult(); + Page result = aggrOutputPages.getResult(); numberOfUniqueRowsProduced += result.getPositionCount(); return result; } protected void closeAggregationBuilder() { - outputPages = null; + aggrOutputPages = null; if (aggregationBuilder != null) { aggregationBuilder.recordHashCollisions(hashCollisionsCounter); aggregationBuilder.close(); @@ -549,7 +545,7 @@ public class LookupGroupJoinOperator // The reference must be set to null afterwards to avoid unaccounted memory. aggregationBuilder = null; } - //memoryContext.setBytes(0); + aggrMemoryContext.setBytes(0); aggregator.getPartialAggregationController().ifPresent( controller -> controller.onFlush(numberOfInputRowsProcessed, numberOfUniqueRowsProduced)); numberOfInputRowsProcessed = 0; @@ -561,11 +557,11 @@ public class LookupGroupJoinOperator if (probeAggregationBuilder != null) { probeAggregationBuilder.recordHashCollisions(hashCollisionsCounter); probeAggregationBuilder.close(); - // aggregationBuilder.close() will release all memory reserved in memory accounting. + // probeAggregationBuilder.close() will release all memory reserved in memory accounting. // The reference must be set to null afterwards to avoid unaccounted memory. probeAggregationBuilder = null; } - //memoryContext.setBytes(0); + aggrOnAggrMemoryContext.setBytes(0); /*aggrOnAggregator.getPartialAggregationController().ifPresent( controller -> controller.onFlush(numberOfInputRowsProcessed, numberOfUniqueRowsProduced)); numberOfInputRowsProcessed = 0; @@ -618,9 +614,7 @@ public class LookupGroupJoinOperator int currentPosition = probe.getPosition(); long currentJoinPosition = this.joinPosition; boolean probePositionProducedRow = this.currentProbePositionProducedRow; - clearProbe(); - if (currentPosition < 0) { // Processing of the page hasn't been started yet. createProbe(currentPage); @@ -634,7 +628,6 @@ public class LookupGroupJoinOperator private void processProbe(LookupSource lookupSource) { verify(probe != null); - DriverYieldSignal yieldSignal = operatorContext.getDriverContext().getYieldSignal(); while (!yieldSignal.isSet()) { if (probe.getPosition() >= 0) { @@ -698,13 +691,12 @@ public class LookupGroupJoinOperator } }); spiller.ifPresent(closer::register); + closer.register(this::closeProbeAggrOnAggregationBuilder); } catch (IOException e) { throw new RuntimeException(e); } - closeProbeAggrOnAggregationBuilder(); - closeBuildAggrOnAggregationBuilder(); - memoryContext.setBytes(0); + //closeBuildAggrOnAggregationBuilder(); } /** -- Gitee From c9abb06e23f3a18e8360de2f0f3d90c4b8d09025 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Fri, 24 Feb 2023 15:03:25 +0530 Subject: [PATCH 23/36] group Join visitor rule update and code issue fixes. --- .../planner/AbstractCostBasedPlanTest.java | 10 +++ .../HashBuilderGroupJoinOperator.java | 20 +++-- .../operator/LookupGroupJoinOperator.java | 90 +++++-------------- .../LookupGroupJoinOperatorFactory.java | 7 -- .../operator/LookupJoinOperators.java | 3 - .../sql/planner/LocalExecutionPlanner.java | 1 - ...PartialAggregationWithJoinPushProject.java | 4 +- 7 files changed, 47 insertions(+), 88 deletions(-) diff --git a/presto-benchto-benchmarks/src/test/java/io/prestosql/sql/planner/AbstractCostBasedPlanTest.java b/presto-benchto-benchmarks/src/test/java/io/prestosql/sql/planner/AbstractCostBasedPlanTest.java index 796ff4cea..31d082b0a 100644 --- a/presto-benchto-benchmarks/src/test/java/io/prestosql/sql/planner/AbstractCostBasedPlanTest.java +++ b/presto-benchto-benchmarks/src/test/java/io/prestosql/sql/planner/AbstractCostBasedPlanTest.java @@ -31,6 +31,7 @@ import io.prestosql.spi.plan.AggregationNode; import io.prestosql.spi.plan.CTEScanNode; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.TableScanNode; import io.prestosql.spi.plan.ValuesNode; import io.prestosql.sql.planner.assertions.BasePlanTest; @@ -282,6 +283,15 @@ public abstract class AbstractCostBasedPlanTest return visitPlan(node, indent + 1); } + @Override + public Void visitJoinOnAggregation(JoinOnAggregationNode node, Integer indent) + { + JoinNode.DistributionType distributionType = node.getDistributionType() + .orElseThrow(() -> new VerifyException("Expected distribution type to be set")); + output(indent, "join (%s, %s):", node.getType(), distributionType); + return visitPlan(node, indent + 1); + } + @Override public Void visitExchange(ExchangeNode node, Integer indent) { diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java index b5ea7320d..a4a3a1091 100644 --- a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java @@ -94,7 +94,7 @@ public class HashBuilderGroupJoinOperator protected AggregationBuilder aggrOnAggregationBuilder; protected LocalMemoryContext aggrMemoryContext; protected LocalMemoryContext aggrOnAggrMemoryContext; - protected WorkProcessor outputPages; + protected WorkProcessor aggrOutputPages; // for yield when memory is not available protected Work unfinishedAggrWork; @@ -102,6 +102,7 @@ public class HashBuilderGroupJoinOperator protected long numberOfUniqueRowsProduced; private final GroupJoinAggregator aggregator; private final GroupJoinAggregator aggrOnAggregator; + private boolean aggregationInputProcessed; public HashBuilderGroupJoinOperator( OperatorContext operatorContext, @@ -169,7 +170,7 @@ public class HashBuilderGroupJoinOperator public boolean needsInput() { if (state == State.CONSUMING_INPUT) { - if (outputPages != null) { + if (aggrOutputPages != null) { return false; } else if (aggregationBuilder != null && aggregationBuilder.isFull()) { @@ -207,6 +208,7 @@ public class HashBuilderGroupJoinOperator else { checkState(!aggregationBuilder.isFull(), "Aggregation buffer is full"); } + aggregationInputProcessed = true; // process the current page; save the unfinished work if we are waiting for memory unfinishedAggrWork = aggregationBuilder.processPage(page); @@ -304,8 +306,8 @@ public class HashBuilderGroupJoinOperator unfinishedAggrWork = null; } - if (outputPages == null) { - if (aggregator.isProduceDefaultOutput()) { + if (aggrOutputPages == null) { + if (!aggregationInputProcessed && aggregator.isProduceDefaultOutput()) { // global aggregations always generate an output row with the default aggregation output (e.g. 0 for COUNT, NULL for SUM) state = State.AGGR_FINISHED; return aggregator.getGlobalAggregationOutput(); @@ -316,26 +318,26 @@ public class HashBuilderGroupJoinOperator return null; } - outputPages = aggregationBuilder.buildResult(); + aggrOutputPages = aggregationBuilder.buildResult(); } - if (!outputPages.process()) { + if (!aggrOutputPages.process()) { return null; } - if (outputPages.isFinished()) { + if (aggrOutputPages.isFinished()) { closeAggregationBuilder(); return null; } - Page result = outputPages.getResult(); + Page result = aggrOutputPages.getResult(); numberOfUniqueRowsProduced += result.getPositionCount(); return result; } protected void closeAggregationBuilder() { - outputPages = null; + aggrOutputPages = null; if (aggregationBuilder != null) { aggregationBuilder.recordHashCollisions(hashCollisionsCounter); aggregationBuilder.close(); diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java index 37f6156dc..88b5b7864 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java @@ -28,7 +28,6 @@ import io.prestosql.operator.aggregation.builder.InMemoryHashAggregationBuilderW import io.prestosql.operator.exchange.LocalPartitionGenerator; import io.prestosql.snapshot.SingleInputSnapshotState; import io.prestosql.spi.Page; -import io.prestosql.spi.plan.Symbol; import io.prestosql.spi.type.Type; import io.prestosql.spiller.PartitioningSpiller; import io.prestosql.spiller.PartitioningSpillerFactory; @@ -150,13 +149,13 @@ public class LookupGroupJoinOperator private final HashCollisionsCounter hashCollisionsCounter; - protected boolean aggregationFinishing; + /*protected boolean aggregationFinishing;*/ protected long numberOfInputRowsProcessed; protected long numberOfUniqueRowsProduced; protected Work unfinishedAggrWork; protected boolean aggregationInputProcessed; private final boolean spillEnabled = false; - private boolean aggregationFinished; + /*private boolean aggregationFinished;*/ protected WorkProcessor aggrOutputPages; protected State state = State.CONSUMING_INPUT; @@ -177,7 +176,6 @@ public class LookupGroupJoinOperator boolean isSingleSessionSpiller, GroupJoinAggregator aggregator, GroupJoinAggregator aggrOnAggregator, - List probeFinalOutputSymbols, List probeFinalOutputChannels, List buildFinalOutputChannels) { @@ -234,15 +232,16 @@ public class LookupGroupJoinOperator if (finishing) { return; } + if (State.CONSUMING_INPUT == state) { + state = State.AGGR_FINISHING; + } finishing = true; - aggregationFinishing = true; } @Override public boolean isFinished() { boolean finishedNow = this.finishing && this.finished && probe == null && pageBuilder.isEmpty() && outputPage == null; - // TODO Vineet Check the aggregation related pending work // if finishedNow drop references so memory is freed early if (finishedNow) { close(); @@ -263,9 +262,8 @@ public class LookupGroupJoinOperator @Override public boolean needsInput() { - // TODO Vineet check aggregation related conditions - if (!finishing && state == State.CONSUMING_INPUT) { - if (aggregationFinishing || aggrOutputPages != null) { + if (state == State.CONSUMING_INPUT) { + if (aggrOutputPages != null) { return false; } else if (aggregationBuilder != null && aggregationBuilder.isFull()) { @@ -284,35 +282,18 @@ public class LookupGroupJoinOperator return true; } } - else { - return allowMarker2() - && lookupSourceProviderFuture.isDone(); - } - } - - public boolean allowMarker2() - { - return !finishing - && probe == null - && outputPage == null; + return false; } @Override public void addInput(Page page) - { - addInput(page, false); - } - - private void addInput(Page page, boolean isRestoredPage) { requireNonNull(page, "page is null"); checkState(probe == null, "Current page has not been completely processed yet"); - checkState(tryFetchLookupSourceProvider(), "Not ready to handle input yet"); // create Aggregators and pass page to them for process - checkState(!aggregationFinishing, "Operator is already finishing"); + checkState(state == State.CONSUMING_INPUT, "Operator is already finishing"); aggregationInputProcessed = true; - if (aggregationBuilder == null) { createAggregationBuilder(); } @@ -324,7 +305,6 @@ public class LookupGroupJoinOperator unfinishedAggrWork = aggregationBuilder.processPage(page); if (unfinishedAggrWork.process()) { unfinishedAggrWork = null; - // TODO Vineet check if can index this pages here. } aggregationBuilder.updateMemory(); numberOfInputRowsProcessed += page.getPositionCount(); @@ -359,7 +339,6 @@ public class LookupGroupJoinOperator { Page page = processAggregation(); if (page != null) { - // TODO Vineet may need to cache these pages and also check the condition for state change. createProbe(page); } } @@ -369,6 +348,9 @@ public class LookupGroupJoinOperator { switch (state) { case CONSUMING_INPUT: + return null; + case AGGR_FINISHING: + case AGGR_FINISHED: if (probe == null) { finishAggregation(); } @@ -483,7 +465,8 @@ public class LookupGroupJoinOperator public Page processAggregation() { - if (aggregationFinished) { + if (state == State.AGGR_FINISHED) { + state = State.SOURCE_BUILT; return null; } @@ -498,25 +481,21 @@ public class LookupGroupJoinOperator } if (aggrOutputPages == null) { - if (aggregationFinishing) { - if (!aggregationInputProcessed && aggregator.isProduceDefaultOutput()) { - // global aggregations always generate an output row with the default aggregation output (e.g. 0 for COUNT, NULL for SUM) - aggregationFinished = true; - /*state = State.SOURCE_BUILT;*/ - return aggregator.getGlobalAggregationOutput(); - } + if (!aggregationInputProcessed && aggregator.isProduceDefaultOutput()) { + // global aggregations always generate an output row with the default aggregation output (e.g. 0 for COUNT, NULL for SUM) + state = State.AGGR_FINISHED; + return aggregator.getGlobalAggregationOutput(); + } - if (aggregationBuilder == null) { - aggregationFinished = true; - /*state = State.SOURCE_BUILT;*/ - return null; - } + if (aggregationBuilder == null) { + state = State.AGGR_FINISHED; + return null; } // only flush if we are finishing or the aggregation builder is full - if (!aggregationFinishing && (aggregationBuilder == null || !aggregationBuilder.isFull())) { + /*if (!aggregationBuilder.isFull()) { return null; - } + }*/ aggrOutputPages = aggregationBuilder.buildResult(); } @@ -562,26 +541,6 @@ public class LookupGroupJoinOperator probeAggregationBuilder = null; } aggrOnAggrMemoryContext.setBytes(0); - /*aggrOnAggregator.getPartialAggregationController().ifPresent( - controller -> controller.onFlush(numberOfInputRowsProcessed, numberOfUniqueRowsProduced)); - numberOfInputRowsProcessed = 0; - numberOfUniqueRowsProduced = 0;*/ - } - - protected void closeBuildAggrOnAggregationBuilder() - { - if (buildAggregationBuilder != null) { - buildAggregationBuilder.recordHashCollisions(hashCollisionsCounter); - buildAggregationBuilder.close(); - // aggregationBuilder.close() will release all memory reserved in memory accounting. - // The reference must be set to null afterwards to avoid unaccounted memory. - buildAggregationBuilder = null; - } - //memoryContext.setBytes(0); - /*aggrOnAggregator.getPartialAggregationController().ifPresent( - controller -> controller.onFlush(numberOfInputRowsProcessed, numberOfUniqueRowsProduced)); - numberOfInputRowsProcessed = 0; - numberOfUniqueRowsProduced = 0;*/ } private void processProbe() @@ -696,7 +655,6 @@ public class LookupGroupJoinOperator catch (IOException e) { throw new RuntimeException(e); } - //closeBuildAggrOnAggregationBuilder(); } /** diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperatorFactory.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperatorFactory.java index 8fd073ce5..83dc1e579 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperatorFactory.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperatorFactory.java @@ -54,7 +54,6 @@ public class LookupGroupJoinOperatorFactory private final HashGenerator probeHashGenerator; private final boolean forked; private final PartitioningSpillerFactory partitioningSpillerFactory; - private final List probeFinalOutputSymbols; private final List probeFinalOutputChannels; private final List buildFinalOutputChannels; private boolean closed; @@ -82,7 +81,6 @@ public class LookupGroupJoinOperatorFactory boolean forked, GroupJoinAggregator aggrfactory, GroupJoinAggregator aggrOnAggrfactory, - List probeFinalOutputSymbols, List probeFinalOutputChannels, List buildFinalOutputChannels) { @@ -116,8 +114,6 @@ public class LookupGroupJoinOperatorFactory this.aggrfactory = aggrfactory; this.aggrOnAggrfactory = aggrOnAggrfactory; - - this.probeFinalOutputSymbols = probeFinalOutputSymbols; this.probeFinalOutputChannels = probeFinalOutputChannels; this.buildFinalOutputChannels = buildFinalOutputChannels; } @@ -145,7 +141,6 @@ public class LookupGroupJoinOperatorFactory this.aggrOnAggrfactory = other.aggrOnAggrfactory; this.probeFinalOutputChannels = other.probeFinalOutputChannels; - this.probeFinalOutputSymbols = other.probeFinalOutputSymbols; this.buildFinalOutputChannels = other.buildFinalOutputChannels; this.closed = false; @@ -184,7 +179,6 @@ public class LookupGroupJoinOperatorFactory isSpillToHdfsEnabled(driverContext.getPipelineContext().getTaskContext().getSession()), aggrfactory, aggrOnAggrfactory, - probeFinalOutputSymbols, probeFinalOutputChannels, buildFinalOutputChannels); } @@ -375,7 +369,6 @@ public class LookupGroupJoinOperatorFactory forked, aggrfactory, aggrOnAggrfactory, - probeFinalOutputSymbols, probeFinalOutputChannels, buildFinalOutputChannels); } diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupJoinOperators.java b/presto-main/src/main/java/io/prestosql/operator/LookupJoinOperators.java index 0320cec77..e5fe98682 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupJoinOperators.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupJoinOperators.java @@ -15,7 +15,6 @@ package io.prestosql.operator; import io.prestosql.operator.JoinProbe.JoinProbeFactory; import io.prestosql.spi.plan.PlanNodeId; -import io.prestosql.spi.plan.Symbol; import io.prestosql.spi.type.Type; import io.prestosql.spiller.PartitioningSpillerFactory; @@ -115,7 +114,6 @@ public class LookupJoinOperators boolean forked, GroupJoinAggregator aggrfactory, GroupJoinAggregator aggrOnAggrfactory, - List probeFinalOutputSymbols, List probeFinalOutputChannels, List buildFinalOutputChannels, List outputTypes) @@ -136,7 +134,6 @@ public class LookupJoinOperators forked, aggrfactory, aggrOnAggrfactory, - probeFinalOutputSymbols, probeFinalOutputChannels, buildFinalOutputChannels); } diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java b/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java index 73aef66bd..c0f1924a7 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java @@ -3156,7 +3156,6 @@ public class LocalExecutionPlanner false, aggrfactory, aggrOnAggrfactory, - probeFinalOutputSymbols, probeFinalOutputChannels, buildFinalOutputChannels, outputTypes.build()); diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/MergePartialAggregationWithJoinPushProject.java b/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/MergePartialAggregationWithJoinPushProject.java index abe02605b..3efe09790 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/MergePartialAggregationWithJoinPushProject.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/MergePartialAggregationWithJoinPushProject.java @@ -125,7 +125,7 @@ public class MergePartialAggregationWithJoinPushProject joinNode.getDistributionType(), joinNode.isSpillable(), joinNode.getDynamicFilters()); - node = new AggregationNode(node.getId(), + AggregationNode nodeNew = new AggregationNode(node.getId(), joinNode, node.getAggregations(), node.getGroupingSets(), @@ -135,7 +135,7 @@ public class MergePartialAggregationWithJoinPushProject node.getGroupIdSymbol(), node.getAggregationType(), node.getFinalizeSymbol()); - return checkAndApplyRule(node, context, joinNode); + return checkAndApplyRule(nodeNew, context, joinNode); } private static boolean nonTrivialProjection(ProjectNode project) -- Gitee From 17ef8fc6b7d66d9f324428b60fdb472780f03ecc Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Fri, 24 Feb 2023 17:56:06 +0530 Subject: [PATCH 24/36] plan print fix. --- .../io/prestosql/operator/LookupGroupJoinPageBuilder.java | 4 +--- .../io/prestosql/sql/planner/planprinter/PlanPrinter.java | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java index e2c3cb994..4bd7fd97f 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java @@ -114,7 +114,7 @@ public class LookupGroupJoinPageBuilder buildPageBuilderTmp.reset(); } - private static Page processAggregationOnPage(long count, Page sourcePage, AggregationBuilder aggregationBuilder) + private Page processAggregationOnPage(long count, Page sourcePage, AggregationBuilder aggregationBuilder) { // TODO Vineet check on how to convert into future object and relate in normal code flow. Page finalPage; @@ -122,12 +122,10 @@ public class LookupGroupJoinPageBuilder Work work = aggregationBuilder.processPage(sourcePage); // Knowingly kept empty while loop while (!work.process()) { - i = i; } } WorkProcessor pageWorkProcessor = aggregationBuilder.buildResult(); while (!pageWorkProcessor.process()) { - finalPage = null; } aggregationBuilder.updateMemory(); finalPage = pageWorkProcessor.getResult(); diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/planprinter/PlanPrinter.java b/presto-main/src/main/java/io/prestosql/sql/planner/planprinter/PlanPrinter.java index 50fc04dd3..d4024b85b 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/planprinter/PlanPrinter.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/planprinter/PlanPrinter.java @@ -1529,11 +1529,11 @@ public class PlanPrinter } String value = ""; if (node.getAggregationType().equals(AggregationNode.AggregationType.HASH)) { - value = format("hashAggregate%s%s%s", type, key, formatHash(node.getHashSymbol())); + value = format("HashAggregate%s%s%s", type, key, formatHash(node.getHashSymbol())); } StringBuilder builder = new StringBuilder(value); if (node.getAggregations().size() > 0) { - node.getAggregations().forEach((symbol, aggregation) -> builder.append(format("%s := %s", symbol, formatAggregation(aggregation)))); + node.getAggregations().forEach((symbol, aggregation) -> builder.append(format("%s := %s, ", symbol, formatAggregation(aggregation)))); // to remove the last: ", " builder.setLength(builder.length() - 2); } -- Gitee From 400ee5182ba12be6a23a89cabd5d3c396b7b7a42 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Mon, 27 Feb 2023 17:32:52 +0530 Subject: [PATCH 25/36] Group Join - making needsInput light weight. --- .../HashBuilderGroupJoinOperator.java | 64 +++++++++++++++++-- .../HashBuilderGroupJoinOperatorFactory.java | 19 +++++- .../operator/LookupGroupJoinOperator.java | 52 +++++++++++---- .../LookupGroupJoinOperatorFactory.java | 19 +++++- .../operator/LookupGroupJoinPageBuilder.java | 8 ++- .../operator/LookupJoinOperators.java | 7 +- .../operator/groupjoin/ExecutionHelper.java | 21 ++++++ .../groupjoin/ExecutionHelperFactory.java | 19 ++++++ .../groupjoin/GeneralExecutionHelper.java | 34 ++++++++++ .../GeneralExecutionHelperFactory.java | 51 +++++++++++++++ .../io/prestosql/server/ServerMainModule.java | 3 + .../sql/analyzer/FeaturesConfig.java | 14 ++++ .../sql/planner/LocalExecutionPlanner.java | 12 +++- .../prestosql/testing/LocalQueryRunner.java | 6 +- 14 files changed, 298 insertions(+), 31 deletions(-) create mode 100644 presto-main/src/main/java/io/prestosql/operator/groupjoin/ExecutionHelper.java create mode 100644 presto-main/src/main/java/io/prestosql/operator/groupjoin/ExecutionHelperFactory.java create mode 100644 presto-main/src/main/java/io/prestosql/operator/groupjoin/GeneralExecutionHelper.java create mode 100644 presto-main/src/main/java/io/prestosql/operator/groupjoin/GeneralExecutionHelperFactory.java diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java index a4a3a1091..406f0f705 100644 --- a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java @@ -20,6 +20,7 @@ import io.prestosql.memory.context.LocalMemoryContext; import io.prestosql.operator.aggregation.builder.AggregationBuilder; import io.prestosql.operator.aggregation.builder.InMemoryHashAggregationBuilder; import io.prestosql.operator.aggregation.builder.InMemoryHashAggregationBuilderWithReset; +import io.prestosql.operator.groupjoin.ExecutionHelperFactory; import io.prestosql.spi.Page; import io.prestosql.sql.gen.JoinFilterFunctionCompiler; @@ -37,6 +38,8 @@ import static java.util.Objects.requireNonNull; public class HashBuilderGroupJoinOperator implements SinkOperator { + private ExecutionHelperFactory executionHelperFactory; + @VisibleForTesting public enum State { @@ -103,6 +106,7 @@ public class HashBuilderGroupJoinOperator private final GroupJoinAggregator aggregator; private final GroupJoinAggregator aggrOnAggregator; private boolean aggregationInputProcessed; + private ListenableFuture executionHelper = NOT_BLOCKED; public HashBuilderGroupJoinOperator( OperatorContext operatorContext, @@ -120,8 +124,10 @@ public class HashBuilderGroupJoinOperator boolean spillEnabled, boolean spillToHdfsEnabled, GroupJoinAggregator aggregator, - GroupJoinAggregator aggrOnAggregator) + GroupJoinAggregator aggrOnAggregator, + ExecutionHelperFactory executionHelperFactory) { + this.executionHelperFactory = executionHelperFactory; requireNonNull(pagesIndexFactory, "pagesIndexFactory is null"); this.operatorContext = operatorContext; this.partitionIndex = partitionIndex; @@ -171,9 +177,23 @@ public class HashBuilderGroupJoinOperator { if (state == State.CONSUMING_INPUT) { if (aggrOutputPages != null) { + if (executionHelper != null) { + return false; + } + executionHelper = executionHelperFactory.create().submitWork(() -> { + processAggrOutputOrFullCase(); + executionHelper = null; + }); return false; } else if (aggregationBuilder != null && aggregationBuilder.isFull()) { + if (executionHelper != null) { + return false; + } + executionHelper = executionHelperFactory.create().submitWork(() -> { + processAggrOutputOrFullCase(); + executionHelper = null; + }); return false; } else if (lookupSourceFactoryDestroyed.isDone()) { @@ -182,12 +202,21 @@ public class HashBuilderGroupJoinOperator else { // TODO Vineet Need to move this out of needsInput and need to make it light weight. if (unfinishedAggrWork != null) { - boolean workDone = unfinishedAggrWork.process(); - aggregationBuilder.updateMemory(); - if (!workDone) { + if (executionHelper != null) { return false; } - unfinishedAggrWork = null; + executionHelper = executionHelperFactory.create().submitWork(() -> { + boolean workDone = unfinishedAggrWork.process(); + aggregationBuilder.updateMemory(); + while (!workDone) { + workDone = unfinishedAggrWork.process(); + aggregationBuilder.updateMemory(); + } + + unfinishedAggrWork = null; + executionHelper = null; + }); + return false; } return true; } @@ -290,6 +319,14 @@ public class HashBuilderGroupJoinOperator operatorContext.recordOutput(page.getSizeInBytes(), page.getPositionCount()); } + private void processAggrOutputOrFullCase() + { + Page page = processAggregation(); + if (page != null) { + updateIndex(page); + } + } + public Page processAggregation() { if (state == State.AGGR_FINISHED) { @@ -318,6 +355,11 @@ public class HashBuilderGroupJoinOperator return null; } + // Only flush if we are finishing(consuming input will change to AggrFinishing) or the aggregation builder is full + if (state == State.CONSUMING_INPUT && !aggregationBuilder.isFull()) { + return null; + } + aggrOutputPages = aggregationBuilder.buildResult(); } @@ -502,6 +544,18 @@ public class HashBuilderGroupJoinOperator } } + @Override + public ListenableFuture isBlocked() + { + if (state == State.CONSUMING_INPUT) { + if (executionHelper != null) { + return executionHelper.isDone() ? NOT_BLOCKED : executionHelper; + } + } + + return NOT_BLOCKED; + } + @VisibleForTesting public State getState() { diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java index 7b0f35e73..46613f989 100644 --- a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperatorFactory.java @@ -18,6 +18,7 @@ import io.airlift.units.DataSize; import io.prestosql.execution.Lifespan; import io.prestosql.operator.aggregation.AccumulatorFactory; import io.prestosql.operator.aggregation.partial.PartialAggregationController; +import io.prestosql.operator.groupjoin.ExecutionHelperFactory; import io.prestosql.spi.plan.AggregationNode; import io.prestosql.spi.plan.PlanNodeId; import io.prestosql.spi.type.Type; @@ -40,6 +41,7 @@ public class HashBuilderGroupJoinOperatorFactory { private final int operatorId; private final PlanNodeId planNodeId; + private ExecutionHelperFactory executionHelperFactory; private final JoinBridgeManager lookupSourceFactoryManager; private final List outputChannels; private final List hashChannels; @@ -78,10 +80,12 @@ public class HashBuilderGroupJoinOperatorFactory boolean spillEnabled, boolean spillToHdfsEnabled, GroupJoinAggregator aggrfactory, - GroupJoinAggregator aggrOnAggrfactory) + GroupJoinAggregator aggrOnAggrfactory, + ExecutionHelperFactory executionHelperFactory) { this.operatorId = operatorId; this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); + this.executionHelperFactory = requireNonNull(executionHelperFactory, "executionHelperFactory is null"); requireNonNull(sortChannel, "sortChannel can not be null"); requireNonNull(searchFunctionFactories, "searchFunctionFactories is null"); checkArgument(sortChannel.isPresent() != searchFunctionFactories.isEmpty(), "both or none sortChannel and searchFunctionFactories must be set"); @@ -133,7 +137,8 @@ public class HashBuilderGroupJoinOperatorFactory spillEnabled, spillToHdfsEnabled, aggrfactory, - aggrOnAggrfactory); + aggrOnAggrfactory, + executionHelperFactory); } @Override @@ -171,11 +176,18 @@ public class HashBuilderGroupJoinOperatorFactory private PagesIndex.Factory pagesIndexFactory; private boolean spillEnabled; private boolean spillToHdfsEnabled; + private ExecutionHelperFactory executionHelperFactory; public Builder() { } + public Builder withExecutorHelperFactory(ExecutionHelperFactory executionHelperFactory) + { + this.executionHelperFactory = requireNonNull(executionHelperFactory, "executionHelperFactory is null"); + return this; + } + public Builder withJoinInfo(int operatorId, PlanNodeId planNodeId, JoinBridgeManager lookupSourceFactoryManager, @@ -293,7 +305,8 @@ public class HashBuilderGroupJoinOperatorFactory spillEnabled, spillToHdfsEnabled, aggrfactory, - aggrOnAggrfactory); + aggrOnAggrfactory, + executionHelperFactory); } } } diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java index 88b5b7864..642428cef 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java @@ -26,6 +26,7 @@ import io.prestosql.operator.aggregation.builder.AggregationBuilder; import io.prestosql.operator.aggregation.builder.InMemoryHashAggregationBuilder; import io.prestosql.operator.aggregation.builder.InMemoryHashAggregationBuilderWithReset; import io.prestosql.operator.exchange.LocalPartitionGenerator; +import io.prestosql.operator.groupjoin.ExecutionHelperFactory; import io.prestosql.snapshot.SingleInputSnapshotState; import io.prestosql.spi.Page; import io.prestosql.spi.type.Type; @@ -87,6 +88,7 @@ public class LookupGroupJoinOperator } private final OperatorContext operatorContext; + private ExecutionHelperFactory executionHelperFactory; private final GroupJoinProbeFactory joinProbeFactory; private final Runnable afterClose; @@ -158,6 +160,7 @@ public class LookupGroupJoinOperator /*private boolean aggregationFinished;*/ protected WorkProcessor aggrOutputPages; protected State state = State.CONSUMING_INPUT; + private ListenableFuture executionHelper = NOT_BLOCKED; public LookupGroupJoinOperator( OperatorContext operatorContext, @@ -177,9 +180,11 @@ public class LookupGroupJoinOperator GroupJoinAggregator aggregator, GroupJoinAggregator aggrOnAggregator, List probeFinalOutputChannels, - List buildFinalOutputChannels) + List buildFinalOutputChannels, + ExecutionHelperFactory executionHelperFactory) { this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); + this.executionHelperFactory = executionHelperFactory; List probeTypes1 = ImmutableList.copyOf(requireNonNull(probeTypes, "probeTypes is null")); requireNonNull(joinType, "joinType is null"); @@ -252,6 +257,11 @@ public class LookupGroupJoinOperator @Override public ListenableFuture isBlocked() { + if (state == State.CONSUMING_INPUT) { + if (executionHelper != null) { + return executionHelper.isDone() ? NOT_BLOCKED : executionHelper; + } + } if (finishing) { return NOT_BLOCKED; } @@ -272,12 +282,18 @@ public class LookupGroupJoinOperator else { // TODO Vineet Need to move this out of needsInput and need to make it light weight. if (unfinishedAggrWork != null) { - boolean workDone = unfinishedAggrWork.process(); - aggregationBuilder.updateMemory(); - if (!workDone) { - return false; - } - unfinishedAggrWork = null; + executionHelper = executionHelperFactory.create().submitWork(() -> { + boolean workDone = unfinishedAggrWork.process(); + aggregationBuilder.updateMemory(); + while (!workDone) { + workDone = unfinishedAggrWork.process(); + aggregationBuilder.updateMemory(); + } + + unfinishedAggrWork = null; + executionHelper = null; + }); + return false; } return true; } @@ -290,7 +306,7 @@ public class LookupGroupJoinOperator { requireNonNull(page, "page is null"); checkState(probe == null, "Current page has not been completely processed yet"); - checkState(tryFetchLookupSourceProvider(), "Not ready to handle input yet"); + //checkState(tryFetchLookupSourceProvider(), "Not ready to handle input yet"); // create Aggregators and pass page to them for process checkState(state == State.CONSUMING_INPUT, "Operator is already finishing"); aggregationInputProcessed = true; @@ -348,10 +364,19 @@ public class LookupGroupJoinOperator { switch (state) { case CONSUMING_INPUT: + if (probe == null) { + if (aggregationBuilder != null && aggregationBuilder.isFull() && tryFetchLookupSourceProvider()) { + finishAggregation(); + break; + } + } + if (probe != null) { + break; + } return null; case AGGR_FINISHING: case AGGR_FINISHED: - if (probe == null) { + if (probe == null && tryFetchLookupSourceProvider()) { finishAggregation(); } break; @@ -365,6 +390,9 @@ public class LookupGroupJoinOperator if (probe == null && pageBuilder.isEmpty() && !finishing) { return null; } + if (!lookupSourceProviderFuture.isDone()) { + return null; + } if (!tryFetchLookupSourceProvider()) { if (!finishing) { @@ -492,10 +520,10 @@ public class LookupGroupJoinOperator return null; } - // only flush if we are finishing or the aggregation builder is full - /*if (!aggregationBuilder.isFull()) { + // Only flush if we are finishing(consuming input will change to AggrFinishing) or the aggregation builder is full + if (state == State.CONSUMING_INPUT && !aggregationBuilder.isFull()) { return null; - }*/ + } aggrOutputPages = aggregationBuilder.buildResult(); } diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperatorFactory.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperatorFactory.java index 83dc1e579..c839884f1 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperatorFactory.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperatorFactory.java @@ -20,6 +20,7 @@ import io.prestosql.operator.GroupJoinProbe.GroupJoinProbeFactory; import io.prestosql.operator.LookupJoinOperators.JoinType; import io.prestosql.operator.aggregation.AccumulatorFactory; import io.prestosql.operator.aggregation.partial.PartialAggregationController; +import io.prestosql.operator.groupjoin.ExecutionHelperFactory; import io.prestosql.spi.plan.AggregationNode; import io.prestosql.spi.plan.PlanNodeId; import io.prestosql.spi.plan.Symbol; @@ -50,6 +51,7 @@ public class LookupGroupJoinOperatorFactory private final GroupJoinProbeFactory joinProbeFactory; private final Optional outerOperatorFactoryResult; private final JoinBridgeManager joinBridgeManager; + private ExecutionHelperFactory executionHelperFactory; private final OptionalInt totalOperatorsCount; private final HashGenerator probeHashGenerator; private final boolean forked; @@ -82,7 +84,8 @@ public class LookupGroupJoinOperatorFactory GroupJoinAggregator aggrfactory, GroupJoinAggregator aggrOnAggrfactory, List probeFinalOutputChannels, - List buildFinalOutputChannels) + List buildFinalOutputChannels, + ExecutionHelperFactory executionHelperFactory) { this.forked = forked; this.operatorId = operatorId; @@ -94,6 +97,7 @@ public class LookupGroupJoinOperatorFactory this.joinProbeFactory = requireNonNull(joinProbeFactory, "joinProbeFactory is null"); this.joinBridgeManager = lookupSourceFactoryManager; + this.executionHelperFactory = requireNonNull(executionHelperFactory, "executionHelperFactory is null"); joinBridgeManager.incrementProbeFactoryCount(); checkArgument(joinType == INNER, "joinType is not INNER"); this.outerOperatorFactoryResult = Optional.empty(); @@ -180,7 +184,8 @@ public class LookupGroupJoinOperatorFactory aggrfactory, aggrOnAggrfactory, probeFinalOutputChannels, - buildFinalOutputChannels); + buildFinalOutputChannels, + executionHelperFactory); } @Override @@ -235,11 +240,18 @@ public class LookupGroupJoinOperatorFactory private PartitioningSpillerFactory partitioningSpillerFactory; private OptionalInt probeHashChannel; private List probeJoinChannels; + private ExecutionHelperFactory executionHelperFactory; public Builder() { } + public Builder withExecutorHelperFactory(ExecutionHelperFactory executionHelperFactory) + { + this.executionHelperFactory = requireNonNull(executionHelperFactory, "executionHelperFactory is null"); + return this; + } + public Builder withJoinInfo(int operatorId, PlanNodeId planNodeId, JoinBridgeManager lookupSourceFactoryManager, @@ -370,7 +382,8 @@ public class LookupGroupJoinOperatorFactory aggrfactory, aggrOnAggrfactory, probeFinalOutputChannels, - buildFinalOutputChannels); + buildFinalOutputChannels, + executionHelperFactory); } } } diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java index 4bd7fd97f..76661fb98 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java @@ -121,11 +121,15 @@ public class LookupGroupJoinPageBuilder for (int i = 0; i < count; i++) { Work work = aggregationBuilder.processPage(sourcePage); // Knowingly kept empty while loop - while (!work.process()) { + boolean process = work.process(); + while (!process) { + process = work.process(); } } WorkProcessor pageWorkProcessor = aggregationBuilder.buildResult(); - while (!pageWorkProcessor.process()) { + boolean process = pageWorkProcessor.process(); + while (!process) { + process = pageWorkProcessor.process(); } aggregationBuilder.updateMemory(); finalPage = pageWorkProcessor.getResult(); diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupJoinOperators.java b/presto-main/src/main/java/io/prestosql/operator/LookupJoinOperators.java index e5fe98682..bb0d7a326 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupJoinOperators.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupJoinOperators.java @@ -14,6 +14,7 @@ package io.prestosql.operator; import io.prestosql.operator.JoinProbe.JoinProbeFactory; +import io.prestosql.operator.groupjoin.ExecutionHelperFactory; import io.prestosql.spi.plan.PlanNodeId; import io.prestosql.spi.type.Type; import io.prestosql.spiller.PartitioningSpillerFactory; @@ -116,7 +117,8 @@ public class LookupJoinOperators GroupJoinAggregator aggrOnAggrfactory, List probeFinalOutputChannels, List buildFinalOutputChannels, - List outputTypes) + List outputTypes, + ExecutionHelperFactory executionHelperFactory) { return new LookupGroupJoinOperatorFactory( operatorId, @@ -135,6 +137,7 @@ public class LookupJoinOperators aggrfactory, aggrOnAggrfactory, probeFinalOutputChannels, - buildFinalOutputChannels); + buildFinalOutputChannels, + executionHelperFactory); } } diff --git a/presto-main/src/main/java/io/prestosql/operator/groupjoin/ExecutionHelper.java b/presto-main/src/main/java/io/prestosql/operator/groupjoin/ExecutionHelper.java new file mode 100644 index 000000000..6e83faa25 --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/operator/groupjoin/ExecutionHelper.java @@ -0,0 +1,21 @@ +/* + * 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.prestosql.operator.groupjoin; + +import com.google.common.util.concurrent.ListenableFuture; + +public interface ExecutionHelper +{ + ListenableFuture submitWork(Runnable work); +} diff --git a/presto-main/src/main/java/io/prestosql/operator/groupjoin/ExecutionHelperFactory.java b/presto-main/src/main/java/io/prestosql/operator/groupjoin/ExecutionHelperFactory.java new file mode 100644 index 000000000..ea295da78 --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/operator/groupjoin/ExecutionHelperFactory.java @@ -0,0 +1,19 @@ +/* + * 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.prestosql.operator.groupjoin; + +public interface ExecutionHelperFactory +{ + ExecutionHelper create(); +} diff --git a/presto-main/src/main/java/io/prestosql/operator/groupjoin/GeneralExecutionHelper.java b/presto-main/src/main/java/io/prestosql/operator/groupjoin/GeneralExecutionHelper.java new file mode 100644 index 000000000..a5994c1e3 --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/operator/groupjoin/GeneralExecutionHelper.java @@ -0,0 +1,34 @@ +/* + * 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.prestosql.operator.groupjoin; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; + +public class GeneralExecutionHelper + implements ExecutionHelper +{ + private final ListeningExecutorService executor; + + public GeneralExecutionHelper(ListeningExecutorService executor) + { + this.executor = executor; + } + + @Override + public ListenableFuture submitWork(Runnable work) + { + return executor.submit(work); + } +} diff --git a/presto-main/src/main/java/io/prestosql/operator/groupjoin/GeneralExecutionHelperFactory.java b/presto-main/src/main/java/io/prestosql/operator/groupjoin/GeneralExecutionHelperFactory.java new file mode 100644 index 000000000..7289358e9 --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/operator/groupjoin/GeneralExecutionHelperFactory.java @@ -0,0 +1,51 @@ +/* + * 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.prestosql.operator.groupjoin; + +import com.google.common.util.concurrent.ListeningExecutorService; +import io.prestosql.sql.analyzer.FeaturesConfig; + +import javax.annotation.PreDestroy; +import javax.inject.Inject; + +import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator; +import static io.airlift.concurrent.Threads.daemonThreadsNamed; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.Executors.newFixedThreadPool; + +public class GeneralExecutionHelperFactory + implements ExecutionHelperFactory +{ + private final ListeningExecutorService executor; + + @Inject + public GeneralExecutionHelperFactory(FeaturesConfig featuresConfig) + { + executor = listeningDecorator(newFixedThreadPool( + requireNonNull(featuresConfig, "featuresConfig is null").getGroupJoinThreads(), + daemonThreadsNamed("group-join-executor-%s"))); + } + + @Override + public ExecutionHelper create() + { + return new GeneralExecutionHelper(executor); + } + + @PreDestroy + public void destroy() + { + executor.shutdownNow(); + } +} diff --git a/presto-main/src/main/java/io/prestosql/server/ServerMainModule.java b/presto-main/src/main/java/io/prestosql/server/ServerMainModule.java index 70365a42b..fdb124de2 100644 --- a/presto-main/src/main/java/io/prestosql/server/ServerMainModule.java +++ b/presto-main/src/main/java/io/prestosql/server/ServerMainModule.java @@ -113,6 +113,8 @@ import io.prestosql.operator.ForExchange; import io.prestosql.operator.LookupJoinOperators; import io.prestosql.operator.OperatorStats; import io.prestosql.operator.PagesIndex; +import io.prestosql.operator.groupjoin.ExecutionHelperFactory; +import io.prestosql.operator.groupjoin.GeneralExecutionHelperFactory; import io.prestosql.operator.index.IndexJoinLookupStats; import io.prestosql.security.PasswordSecurityConfig; import io.prestosql.seedstore.SeedStoreManager; @@ -580,6 +582,7 @@ public class ServerMainModule binder.bind(SpillerFactory.class).to(GenericSpillerFactory.class).in(Scopes.SINGLETON); binder.bind(SingleStreamSpillerFactory.class).to(FileSingleStreamSpillerFactory.class).in(Scopes.SINGLETON); binder.bind(PartitioningSpillerFactory.class).to(GenericPartitioningSpillerFactory.class).in(Scopes.SINGLETON); + binder.bind(ExecutionHelperFactory.class).to(GeneralExecutionHelperFactory.class).in(Scopes.SINGLETON); binder.bind(SpillerStats.class).in(Scopes.SINGLETON); newExporter(binder).export(SpillerFactory.class).withGeneratedName(); binder.bind(LocalSpillManager.class).in(Scopes.SINGLETON); diff --git a/presto-main/src/main/java/io/prestosql/sql/analyzer/FeaturesConfig.java b/presto-main/src/main/java/io/prestosql/sql/analyzer/FeaturesConfig.java index 1b965093f..03c910b40 100644 --- a/presto-main/src/main/java/io/prestosql/sql/analyzer/FeaturesConfig.java +++ b/presto-main/src/main/java/io/prestosql/sql/analyzer/FeaturesConfig.java @@ -198,6 +198,7 @@ public class FeaturesConfig private DataSize cteMaterializationThresholdSize = new DataSize(128, MEGABYTE); private long joinPartitionedBuildMinRowCount = 1_000_000L; + private int groupJoinThreads = 4; @Config("optimizer.transform-self-join-to-window") public FeaturesConfig setTransformSelfJoinToWindow(boolean value) @@ -1673,4 +1674,17 @@ public class FeaturesConfig this.cteMaterializationThresholdSize = cteMaterializationThresholdSize; return this; } + + @Min(1) + public int getGroupJoinThreads() + { + return groupJoinThreads; + } + + @Config("experimental.group-join-threads") + public FeaturesConfig setGroupJoinThreads(int groupJoinThreads) + { + this.groupJoinThreads = groupJoinThreads; + return this; + } } diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java b/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java index c0f1924a7..fe09cc682 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java @@ -126,6 +126,7 @@ import io.prestosql.operator.exchange.LocalExchangeSinkOperator.LocalExchangeSin import io.prestosql.operator.exchange.LocalExchangeSourceOperator.LocalExchangeSourceOperatorFactory; import io.prestosql.operator.exchange.LocalMergeSourceOperator.LocalMergeSourceOperatorFactory; import io.prestosql.operator.exchange.PageChannelSelector; +import io.prestosql.operator.groupjoin.ExecutionHelperFactory; import io.prestosql.operator.index.DynamicTupleFilterFactory; import io.prestosql.operator.index.FieldSetFilteringRecordSet; import io.prestosql.operator.index.IndexBuildDriverFactoryProvider; @@ -387,6 +388,7 @@ public class LocalExecutionPlanner protected final SpillerFactory spillerFactory; protected final SingleStreamSpillerFactory singleStreamSpillerFactory; protected final PartitioningSpillerFactory partitioningSpillerFactory; + protected final ExecutionHelperFactory executionHelperFactory; protected final PagesIndex.Factory pagesIndexFactory; protected final JoinCompiler joinCompiler; protected final LookupJoinOperators lookupJoinOperators; @@ -587,7 +589,8 @@ public class LocalExecutionPlanner ExchangeManagerRegistry exchangeManagerRegistry, TableExecuteContextManager tableExecuteContextManager, CachedDataManager cachedDataManager, - HetuConfig hetuConfig) + HetuConfig hetuConfig, + ExecutionHelperFactory executionHelperFactory) { this.explainAnalyzeContext = requireNonNull(explainAnalyzeContext, "explainAnalyzeContext is null"); this.pageSourceProvider = requireNonNull(pageSourceProvider, "pageSourceProvider is null"); @@ -625,6 +628,7 @@ public class LocalExecutionPlanner this.tableExecuteContextManager = requireNonNull(tableExecuteContextManager, "tableExecuteContextManager is null"); this.cachedDataManager = requireNonNull(cachedDataManager, "cachedDataManager is null"); this.userName = requireNonNull(hetuConfig, "hetuConfig is null").getCachingUserName(); + this.executionHelperFactory = requireNonNull(executionHelperFactory, "executionHelperFactory is null"); } public LocalExecutionPlan plan( @@ -3052,7 +3056,8 @@ public class LocalExecutionPlanner taskCount > 1, isSpillToHdfsEnabled(context.getSession()), aggrfactory, - aggrOnAggrfactory); + aggrOnAggrfactory, + executionHelperFactory); factoriesBuilder.add(hashBuilderOperatorFactory); @@ -3158,7 +3163,8 @@ public class LocalExecutionPlanner aggrOnAggrfactory, probeFinalOutputChannels, buildFinalOutputChannels, - outputTypes.build()); + outputTypes.build(), + executionHelperFactory); default: throw new UnsupportedOperationException("Unsupported join type: " + node.getType()); } diff --git a/presto-main/src/main/java/io/prestosql/testing/LocalQueryRunner.java b/presto-main/src/main/java/io/prestosql/testing/LocalQueryRunner.java index bb0135890..dc77b7511 100644 --- a/presto-main/src/main/java/io/prestosql/testing/LocalQueryRunner.java +++ b/presto-main/src/main/java/io/prestosql/testing/LocalQueryRunner.java @@ -119,6 +119,7 @@ import io.prestosql.operator.OutputFactory; import io.prestosql.operator.PagesIndex; import io.prestosql.operator.StageExecutionDescriptor; import io.prestosql.operator.TaskContext; +import io.prestosql.operator.groupjoin.GeneralExecutionHelperFactory; import io.prestosql.operator.index.IndexJoinLookupStats; import io.prestosql.security.GroupProviderManager; import io.prestosql.seedstore.SeedStoreManager; @@ -302,6 +303,7 @@ public class LocalQueryRunner private final CatalogConnectorStore catalogConnectorStore = new CatalogConnectorStore(); private final HeuristicIndexerManager heuristicIndexerManager; private final CubeManager cubeManager; + private final GeneralExecutionHelperFactory executionHelperFactory; private boolean printPlan; private final CachedDataManager cachedDataManager; private final HetuConfig hetuConfig; @@ -534,6 +536,7 @@ public class LocalQueryRunner SpillerStats spillerStats = new SpillerStats(); this.singleStreamSpillerFactory = new FileSingleStreamSpillerFactory(metadata, spillerStats, featuresConfig, nodeSpillConfig, fileSystemClientManager); this.partitioningSpillerFactory = new GenericPartitioningSpillerFactory(this.singleStreamSpillerFactory); + this.executionHelperFactory = new GeneralExecutionHelperFactory(featuresConfig); this.spillerFactory = new GenericSpillerFactory(singleStreamSpillerFactory); this.recoveryUtils = new RecoveryUtils(fileSystemClientManager, new RecoveryConfig(), nodeManager); @@ -862,7 +865,8 @@ public class LocalQueryRunner exchangeManagerRegistry, tableExecuteContextManager, cachedDataManager, - hetuConfig); + hetuConfig, + executionHelperFactory); // plan query StageExecutionDescriptor stageExecutionDescriptor = subplan.getFragment().getStageExecutionDescriptor(); -- Gitee From 222cbd8e13f54c05804a2a6c605d80e3c26c528e Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Mon, 27 Feb 2023 17:39:39 +0530 Subject: [PATCH 26/36] issue fix. --- .../src/test/java/io/prestosql/execution/TaskTestUtils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/presto-main/src/test/java/io/prestosql/execution/TaskTestUtils.java b/presto-main/src/test/java/io/prestosql/execution/TaskTestUtils.java index 9a30f28ed..0be425e6c 100644 --- a/presto-main/src/test/java/io/prestosql/execution/TaskTestUtils.java +++ b/presto-main/src/test/java/io/prestosql/execution/TaskTestUtils.java @@ -41,6 +41,7 @@ import io.prestosql.metadata.Split; import io.prestosql.metastore.HetuMetaStoreManager; import io.prestosql.operator.LookupJoinOperators; import io.prestosql.operator.PagesIndex; +import io.prestosql.operator.groupjoin.GeneralExecutionHelperFactory; import io.prestosql.operator.index.IndexJoinLookupStats; import io.prestosql.seedstore.SeedStoreManager; import io.prestosql.spi.connector.CatalogName; @@ -189,7 +190,8 @@ public final class TaskTestUtils cubeManager, new ExchangeManagerRegistry(new ExchangeHandleResolver()), tableExecuteContextManager, - new CachedDataManager(new HetuConfig(), new CacheStorageMonitor(new HetuConfig(), metadata), metadata, null, new SessionPropertyManager()), new HetuConfig()); + new CachedDataManager(new HetuConfig(), new CacheStorageMonitor(new HetuConfig(), metadata), metadata, null, new SessionPropertyManager()), new HetuConfig(), + new GeneralExecutionHelperFactory(featuresConfig)); } public static TaskInfo updateTask(SqlTask sqlTask, List taskSources, OutputBuffers outputBuffers) -- Gitee From 2dda297ef17e90b329eb26e874085a8993088ad2 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Tue, 28 Feb 2023 13:24:11 +0530 Subject: [PATCH 27/36] JoinOnAggregationNode Web handling. --- presto-main/src/main/resources/webapp/dist/auditlog.js | 2 +- presto-main/src/main/resources/webapp/dist/embedded_plan.js | 2 +- presto-main/src/main/resources/webapp/dist/hetuqueryeditor.js | 2 +- presto-main/src/main/resources/webapp/dist/index.js | 4 ++-- presto-main/src/main/resources/webapp/dist/nodes.js | 2 +- presto-main/src/main/resources/webapp/dist/overview.js | 2 +- presto-main/src/main/resources/webapp/dist/plan.js | 2 +- presto-main/src/main/resources/webapp/dist/query.js | 2 +- presto-main/src/main/resources/webapp/dist/querymonitor.js | 4 ++-- presto-main/src/main/resources/webapp/dist/stage.js | 2 +- presto-main/src/main/resources/webapp/dist/worker.js | 2 +- presto-main/src/main/resources/webapp/src/newUtils.js | 2 ++ presto-main/src/main/resources/webapp/src/utils.js | 2 ++ 13 files changed, 17 insertions(+), 13 deletions(-) diff --git a/presto-main/src/main/resources/webapp/dist/auditlog.js b/presto-main/src/main/resources/webapp/dist/auditlog.js index 5f2b765b2..c43bc1802 100644 --- a/presto-main/src/main/resources/webapp/dist/auditlog.js +++ b/presto-main/src/main/resources/webapp/dist/auditlog.js @@ -23639,7 +23639,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.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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/embedded_plan.js b/presto-main/src/main/resources/webapp/dist/embedded_plan.js index 998567209..c23ced129 100644 --- a/presto-main/src/main/resources/webapp/dist/embedded_plan.js +++ b/presto-main/src/main/resources/webapp/dist/embedded_plan.js @@ -20542,7 +20542,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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/hetuqueryeditor.js b/presto-main/src/main/resources/webapp/dist/hetuqueryeditor.js index 795253c33..190cb44c8 100644 --- a/presto-main/src/main/resources/webapp/dist/hetuqueryeditor.js +++ b/presto-main/src/main/resources/webapp/dist/hetuqueryeditor.js @@ -33028,7 +33028,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.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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/index.js b/presto-main/src/main/resources/webapp/dist/index.js index 74e54743e..cab92ab62 100644 --- a/presto-main/src/main/resources/webapp/dist/index.js +++ b/presto-main/src/main/resources/webapp/dist/index.js @@ -154,7 +154,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.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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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.failed) {\n case \"USER_ERROR\":\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.failed) {\n case \"USER_ERROR\":\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\n }\n\n return query.state;\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./newUtils.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.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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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.failed) {\n case \"USER_ERROR\":\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.failed) {\n case \"USER_ERROR\":\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\n }\n\n return query.state;\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./newUtils.js?"); /***/ }), @@ -23427,7 +23427,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.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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/nodes.js b/presto-main/src/main/resources/webapp/dist/nodes.js index 1ba91b4e3..8acca55d5 100644 --- a/presto-main/src/main/resources/webapp/dist/nodes.js +++ b/presto-main/src/main/resources/webapp/dist/nodes.js @@ -20816,7 +20816,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.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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/overview.js b/presto-main/src/main/resources/webapp/dist/overview.js index 92d5c592e..43794d9eb 100644 --- a/presto-main/src/main/resources/webapp/dist/overview.js +++ b/presto-main/src/main/resources/webapp/dist/overview.js @@ -26526,7 +26526,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.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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/plan.js b/presto-main/src/main/resources/webapp/dist/plan.js index 06b0df86b..80dda104b 100644 --- a/presto-main/src/main/resources/webapp/dist/plan.js +++ b/presto-main/src/main/resources/webapp/dist/plan.js @@ -20542,7 +20542,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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/query.js b/presto-main/src/main/resources/webapp/dist/query.js index b1eef39bd..07f24e07c 100644 --- a/presto-main/src/main/resources/webapp/dist/query.js +++ b/presto-main/src/main/resources/webapp/dist/query.js @@ -20710,7 +20710,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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/querymonitor.js b/presto-main/src/main/resources/webapp/dist/querymonitor.js index 847cff014..def71da8d 100644 --- a/presto-main/src/main/resources/webapp/dist/querymonitor.js +++ b/presto-main/src/main/resources/webapp/dist/querymonitor.js @@ -118,7 +118,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.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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.failed) {\n case \"USER_ERROR\":\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.failed) {\n case \"USER_ERROR\":\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\n }\n\n return query.state;\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./newUtils.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.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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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.failed) {\n case \"USER_ERROR\":\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.failed) {\n case \"USER_ERROR\":\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\n }\n\n return query.state;\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./newUtils.js?"); /***/ }), @@ -23403,7 +23403,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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/stage.js b/presto-main/src/main/resources/webapp/dist/stage.js index c399b1e4e..31bd67b0e 100644 --- a/presto-main/src/main/resources/webapp/dist/stage.js +++ b/presto-main/src/main/resources/webapp/dist/stage.js @@ -20542,7 +20542,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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/worker.js b/presto-main/src/main/resources/webapp/dist/worker.js index 6388d8af3..053edff7c 100644 --- a/presto-main/src/main/resources/webapp/dist/worker.js +++ b/presto-main/src/main/resources/webapp/dist/worker.js @@ -20506,7 +20506,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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }), diff --git a/presto-main/src/main/resources/webapp/src/newUtils.js b/presto-main/src/main/resources/webapp/src/newUtils.js index 9cbc2c099..e240dc25e 100644 --- a/presto-main/src/main/resources/webapp/src/newUtils.js +++ b/presto-main/src/main/resources/webapp/src/newUtils.js @@ -217,6 +217,8 @@ export function getChildren(nodeInfo: any) return [nodeInfo.source]; case 'JoinNode': return [nodeInfo.left, nodeInfo.right]; + case 'JoinOnAggregationNode': + return [nodeInfo.left, nodeInfo.right]; case 'SemiJoinNode': return [nodeInfo.source, nodeInfo.filteringSource]; case 'SpatialJoinNode': diff --git a/presto-main/src/main/resources/webapp/src/utils.js b/presto-main/src/main/resources/webapp/src/utils.js index 9181bdd9c..7ab18b9e1 100644 --- a/presto-main/src/main/resources/webapp/src/utils.js +++ b/presto-main/src/main/resources/webapp/src/utils.js @@ -245,6 +245,8 @@ export function getChildren(nodeInfo: any) return [nodeInfo.source]; case 'JoinNode': return [nodeInfo.left, nodeInfo.right]; + case 'JoinOnAggregationNode': + return [nodeInfo.left, nodeInfo.right]; case 'SemiJoinNode': return [nodeInfo.source, nodeInfo.filteringSource]; case 'SpatialJoinNode': -- Gitee From 0724e121d6e979ee291e79326f0126ccc1788aa7 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Tue, 28 Feb 2023 13:25:11 +0530 Subject: [PATCH 28/36] Reset of executionHelper added. --- .../HashBuilderGroupJoinOperator.java | 56 +++++++++++++------ .../operator/LookupGroupJoinOperator.java | 42 +++++++++++++- 2 files changed, 77 insertions(+), 21 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java index 406f0f705..20dedad62 100644 --- a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java @@ -164,6 +164,7 @@ public class HashBuilderGroupJoinOperator this.aggregator = aggregator; this.aggrOnAggregator = aggrOnAggregator; + createAggrOnAggregationBuilder(); } @Override @@ -178,6 +179,7 @@ public class HashBuilderGroupJoinOperator if (state == State.CONSUMING_INPUT) { if (aggrOutputPages != null) { if (executionHelper != null) { + checkAndResetExeHelper(); return false; } executionHelper = executionHelperFactory.create().submitWork(() -> { @@ -188,6 +190,7 @@ public class HashBuilderGroupJoinOperator } else if (aggregationBuilder != null && aggregationBuilder.isFull()) { if (executionHelper != null) { + checkAndResetExeHelper(); return false; } executionHelper = executionHelperFactory.create().submitWork(() -> { @@ -203,6 +206,7 @@ public class HashBuilderGroupJoinOperator // TODO Vineet Need to move this out of needsInput and need to make it light weight. if (unfinishedAggrWork != null) { if (executionHelper != null) { + checkAndResetExeHelper(); return false; } executionHelper = executionHelperFactory.create().submitWork(() -> { @@ -224,6 +228,13 @@ public class HashBuilderGroupJoinOperator return false; } + private void checkAndResetExeHelper() + { + if (executionHelper.isDone()) { + executionHelper = null; + } + } + @Override public void addInput(Page page) { @@ -258,6 +269,33 @@ public class HashBuilderGroupJoinOperator return aggregator.hasDistinct(); } + public void createAggrOnAggregationBuilder() + { + if (aggregator.getStep().isOutputPartial() || !spillEnabled || hasOrderBy() || hasDistinct()) { + aggrOnAggregationBuilder = new InMemoryHashAggregationBuilderWithReset( + aggrOnAggregator.getAccumulatorFactories(), + aggrOnAggregator.getStep(), + aggrOnAggregator.getExpectedGroups(), + aggrOnAggregator.getGroupByTypes(), + aggrOnAggregator.getGroupByChannels(), + aggrOnAggregator.getHashChannel(), + operatorContext, + aggrOnAggregator.getMaxPartialMemory(), + aggrOnAggregator.getJoinCompiler(), + () -> { + aggrOnAggrMemoryContext.setBytes(((InMemoryHashAggregationBuilder) aggrOnAggregationBuilder).getSizeInMemory()); + if (aggrOnAggregator.getStep().isOutputPartial() && aggrOnAggregator.getMaxPartialMemory().isPresent()) { + // do not yield on memory for partial aggregations + return true; + } + return operatorContext.isWaitingForMemory().isDone(); + }); + } + else { + throw new UnsupportedOperationException("Not Supported"); + } + } + public void createAggregationBuilder() { if (aggregator.getStep().isOutputPartial() || !spillEnabled || hasOrderBy() || hasDistinct()) { @@ -280,24 +318,6 @@ public class HashBuilderGroupJoinOperator } return operatorContext.isWaitingForMemory().isDone(); }); - aggrOnAggregationBuilder = new InMemoryHashAggregationBuilderWithReset( - aggrOnAggregator.getAccumulatorFactories(), - aggrOnAggregator.getStep(), - aggrOnAggregator.getExpectedGroups(), - aggrOnAggregator.getGroupByTypes(), - aggrOnAggregator.getGroupByChannels(), - aggrOnAggregator.getHashChannel(), - operatorContext, - aggrOnAggregator.getMaxPartialMemory(), - aggrOnAggregator.getJoinCompiler(), - () -> { - aggrOnAggrMemoryContext.setBytes(((InMemoryHashAggregationBuilder) aggrOnAggregationBuilder).getSizeInMemory()); - if (aggrOnAggregator.getStep().isOutputPartial() && aggrOnAggregator.getMaxPartialMemory().isPresent()) { - // do not yield on memory for partial aggregations - return true; - } - return operatorContext.isWaitingForMemory().isDone(); - }); } else { throw new UnsupportedOperationException("Not Supported"); diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java index 642428cef..b3984a0a7 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java @@ -150,14 +150,11 @@ public class LookupGroupJoinOperator protected LocalMemoryContext aggrOnAggrMemoryContext; private final HashCollisionsCounter hashCollisionsCounter; - - /*protected boolean aggregationFinishing;*/ protected long numberOfInputRowsProcessed; protected long numberOfUniqueRowsProduced; protected Work unfinishedAggrWork; protected boolean aggregationInputProcessed; private final boolean spillEnabled = false; - /*private boolean aggregationFinished;*/ protected WorkProcessor aggrOutputPages; protected State state = State.CONSUMING_INPUT; private ListenableFuture executionHelper = NOT_BLOCKED; @@ -223,6 +220,7 @@ public class LookupGroupJoinOperator this.aggregator = aggregator; this.aggrOnAggregator = aggrOnAggregator; + createAggrOnAggregationBuilder(); } @Override @@ -282,6 +280,10 @@ public class LookupGroupJoinOperator else { // TODO Vineet Need to move this out of needsInput and need to make it light weight. if (unfinishedAggrWork != null) { + if (executionHelper != null) { + checkAndResetExeHelper(); + return false; + } executionHelper = executionHelperFactory.create().submitWork(() -> { boolean workDone = unfinishedAggrWork.process(); aggregationBuilder.updateMemory(); @@ -301,6 +303,13 @@ public class LookupGroupJoinOperator return false; } + private void checkAndResetExeHelper() + { + if (executionHelper.isDone()) { + executionHelper = null; + } + } + @Override public void addInput(Page page) { @@ -491,6 +500,33 @@ public class LookupGroupJoinOperator } } + public void createAggrOnAggregationBuilder() + { + if (aggregator.getStep().isOutputPartial() || !spillEnabled || hasOrderBy() || hasDistinct()) { + probeAggregationBuilder = new InMemoryHashAggregationBuilderWithReset( + aggrOnAggregator.getAccumulatorFactories(), + aggrOnAggregator.getStep(), + aggrOnAggregator.getExpectedGroups(), + aggrOnAggregator.getGroupByTypes(), + aggrOnAggregator.getGroupByChannels(), + aggrOnAggregator.getHashChannel(), + operatorContext, + aggrOnAggregator.getMaxPartialMemory(), + aggrOnAggregator.getJoinCompiler(), + () -> { + aggrOnAggrMemoryContext.setBytes(((InMemoryHashAggregationBuilder) probeAggregationBuilder).getSizeInMemory()); + if (aggrOnAggregator.getStep().isOutputPartial() && aggrOnAggregator.getMaxPartialMemory().isPresent()) { + // do not yield on memory for partial aggregations + return true; + } + return operatorContext.isWaitingForMemory().isDone(); + }); + } + else { + throw new UnsupportedOperationException("Not Supported"); + } + } + public Page processAggregation() { if (state == State.AGGR_FINISHED) { -- Gitee From 778aaf31007336027aa3f2f82f14bd496d6a153b Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Tue, 28 Feb 2023 14:40:04 +0530 Subject: [PATCH 29/36] Added Pages Index in Lookup Group Join Side. --- .../operator/LookupGroupJoinOperator.java | 57 +++++++++++++++---- .../LookupGroupJoinOperatorFactory.java | 22 ++++++- .../operator/LookupJoinOperators.java | 8 ++- .../sql/planner/LocalExecutionPlanner.java | 4 +- 4 files changed, 73 insertions(+), 18 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java index b3984a0a7..600b57a00 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java @@ -149,6 +149,9 @@ public class LookupGroupJoinOperator protected LocalMemoryContext aggrMemoryContext; protected LocalMemoryContext aggrOnAggrMemoryContext; + private final LocalMemoryContext localUserMemoryContext; + private final LocalMemoryContext localRevocableMemoryContext; + private final HashCollisionsCounter hashCollisionsCounter; protected long numberOfInputRowsProcessed; protected long numberOfUniqueRowsProduced; @@ -158,6 +161,8 @@ public class LookupGroupJoinOperator protected WorkProcessor aggrOutputPages; protected State state = State.CONSUMING_INPUT; private ListenableFuture executionHelper = NOT_BLOCKED; + private final PagesIndex index; + private Iterator pageIndexItr; public LookupGroupJoinOperator( OperatorContext operatorContext, @@ -178,7 +183,9 @@ public class LookupGroupJoinOperator GroupJoinAggregator aggrOnAggregator, List probeFinalOutputChannels, List buildFinalOutputChannels, - ExecutionHelperFactory executionHelperFactory) + ExecutionHelperFactory executionHelperFactory, + int expectedPositions, + PagesIndex.Factory pagesIndexFactory) { this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); this.executionHelperFactory = executionHelperFactory; @@ -220,6 +227,10 @@ public class LookupGroupJoinOperator this.aggregator = aggregator; this.aggrOnAggregator = aggrOnAggregator; + requireNonNull(pagesIndexFactory, "pagesIndexFactory is null"); + this.index = pagesIndexFactory.newPagesIndex(probeTypes, expectedPositions); + this.localUserMemoryContext = operatorContext.localUserMemoryContext(); + this.localRevocableMemoryContext = operatorContext.localRevocableMemoryContext(); createAggrOnAggregationBuilder(); } @@ -360,11 +371,26 @@ public class LookupGroupJoinOperator return true; } + private void updateIndex(Page page) + { + index.addPage(page); + if (spillEnabled) { + localRevocableMemoryContext.setBytes(index.getEstimatedSize().toBytes()); + } + else { + if (!localUserMemoryContext.trySetBytes(index.getEstimatedSize().toBytes())) { + index.compact(); + localUserMemoryContext.setBytes(index.getEstimatedSize().toBytes()); + } + } + operatorContext.recordOutput(page.getSizeInBytes(), page.getPositionCount()); + } + private void finishAggregation() { Page page = processAggregation(); if (page != null) { - createProbe(page); + updateIndex(page); } } @@ -373,21 +399,29 @@ public class LookupGroupJoinOperator { switch (state) { case CONSUMING_INPUT: - if (probe == null) { - if (aggregationBuilder != null && aggregationBuilder.isFull() && tryFetchLookupSourceProvider()) { - finishAggregation(); - break; - } - } - if (probe != null) { + // Only prepare the Aggr outputs + if (aggregationBuilder != null && aggregationBuilder.isFull()) { + finishAggregation(); break; } return null; case AGGR_FINISHING: - case AGGR_FINISHED: - if (probe == null && tryFetchLookupSourceProvider()) { + if (aggregationBuilder != null) { finishAggregation(); } + case AGGR_FINISHED: + if (pageIndexItr == null) { + pageIndexItr = index.getPages(); + } + if (probe == null && tryFetchLookupSourceProvider() && pageIndexItr != null && pageIndexItr.hasNext()) { + createProbe(pageIndexItr.next()); + } + if (probe == null && tryFetchLookupSourceProvider() && pageIndexItr != null && !pageIndexItr.hasNext() && finishing) { + state = State.SOURCE_BUILT; + } + /*if (probe != null) { + break; + }*/ break; case SOURCE_BUILT: break; @@ -530,7 +564,6 @@ public class LookupGroupJoinOperator public Page processAggregation() { if (state == State.AGGR_FINISHED) { - state = State.SOURCE_BUILT; return null; } diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperatorFactory.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperatorFactory.java index c839884f1..78e8a570c 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperatorFactory.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperatorFactory.java @@ -62,6 +62,9 @@ public class LookupGroupJoinOperatorFactory private final GroupJoinAggregator aggrOnAggrfactory; private final GroupJoinAggregator aggrfactory; + private final PagesIndex.Factory pagesIndexFactory; + private final int expectedPositions; + public static Builder builder() { return new Builder(); @@ -85,7 +88,9 @@ public class LookupGroupJoinOperatorFactory GroupJoinAggregator aggrOnAggrfactory, List probeFinalOutputChannels, List buildFinalOutputChannels, - ExecutionHelperFactory executionHelperFactory) + ExecutionHelperFactory executionHelperFactory, + int expectedPositions, + PagesIndex.Factory pagesIndexFactory) { this.forked = forked; this.operatorId = operatorId; @@ -120,6 +125,8 @@ public class LookupGroupJoinOperatorFactory this.aggrOnAggrfactory = aggrOnAggrfactory; this.probeFinalOutputChannels = probeFinalOutputChannels; this.buildFinalOutputChannels = buildFinalOutputChannels; + this.expectedPositions = expectedPositions; + this.pagesIndexFactory = pagesIndexFactory; } private LookupGroupJoinOperatorFactory(LookupGroupJoinOperatorFactory other) @@ -147,6 +154,8 @@ public class LookupGroupJoinOperatorFactory this.probeFinalOutputChannels = other.probeFinalOutputChannels; this.buildFinalOutputChannels = other.buildFinalOutputChannels; + this.expectedPositions = other.expectedPositions; + this.pagesIndexFactory = other.pagesIndexFactory; this.closed = false; this.joinBridgeManager.incrementProbeFactoryCount(); } @@ -185,7 +194,9 @@ public class LookupGroupJoinOperatorFactory aggrOnAggrfactory, probeFinalOutputChannels, buildFinalOutputChannels, - executionHelperFactory); + executionHelperFactory, + expectedPositions, + pagesIndexFactory); } @Override @@ -242,6 +253,9 @@ public class LookupGroupJoinOperatorFactory private List probeJoinChannels; private ExecutionHelperFactory executionHelperFactory; + private PagesIndex.Factory pagesIndexFactory; + private int expectedPositions; + public Builder() { } @@ -383,7 +397,9 @@ public class LookupGroupJoinOperatorFactory aggrOnAggrfactory, probeFinalOutputChannels, buildFinalOutputChannels, - executionHelperFactory); + executionHelperFactory, + expectedPositions, + pagesIndexFactory); } } } diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupJoinOperators.java b/presto-main/src/main/java/io/prestosql/operator/LookupJoinOperators.java index bb0d7a326..86a735fcd 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupJoinOperators.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupJoinOperators.java @@ -118,7 +118,9 @@ public class LookupJoinOperators List probeFinalOutputChannels, List buildFinalOutputChannels, List outputTypes, - ExecutionHelperFactory executionHelperFactory) + ExecutionHelperFactory executionHelperFactory, + int expectedPositions, + PagesIndex.Factory pagesIndexFactory) { return new LookupGroupJoinOperatorFactory( operatorId, @@ -138,6 +140,8 @@ public class LookupJoinOperators aggrOnAggrfactory, probeFinalOutputChannels, buildFinalOutputChannels, - executionHelperFactory); + executionHelperFactory, + expectedPositions, + pagesIndexFactory); } } diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java b/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java index fe09cc682..56812c177 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java @@ -3164,7 +3164,9 @@ public class LocalExecutionPlanner probeFinalOutputChannels, buildFinalOutputChannels, outputTypes.build(), - executionHelperFactory); + executionHelperFactory, + 10_000, + pagesIndexFactory); default: throw new UnsupportedOperationException("Unsupported join type: " + node.getType()); } -- Gitee From 33fc57bc6c5ee77ab859556d3bd4136f1ccaafd1 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Tue, 28 Feb 2023 15:54:28 +0530 Subject: [PATCH 30/36] javascript fix. --- presto-main/src/main/resources/webapp/dist/auditlog.js | 2 +- presto-main/src/main/resources/webapp/dist/embedded_plan.js | 2 +- presto-main/src/main/resources/webapp/dist/hetuqueryeditor.js | 2 +- presto-main/src/main/resources/webapp/dist/index.js | 4 ++-- presto-main/src/main/resources/webapp/dist/nodes.js | 2 +- presto-main/src/main/resources/webapp/dist/overview.js | 2 +- presto-main/src/main/resources/webapp/dist/plan.js | 2 +- presto-main/src/main/resources/webapp/dist/query.js | 2 +- presto-main/src/main/resources/webapp/dist/querymonitor.js | 4 ++-- presto-main/src/main/resources/webapp/dist/stage.js | 2 +- presto-main/src/main/resources/webapp/dist/worker.js | 2 +- presto-main/src/main/resources/webapp/src/newUtils.js | 2 +- presto-main/src/main/resources/webapp/src/utils.js | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/presto-main/src/main/resources/webapp/dist/auditlog.js b/presto-main/src/main/resources/webapp/dist/auditlog.js index c43bc1802..3d8e0b9ca 100644 --- a/presto-main/src/main/resources/webapp/dist/auditlog.js +++ b/presto-main/src/main/resources/webapp/dist/auditlog.js @@ -23639,7 +23639,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.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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.leftAggr.source, nodeInfo.rightAggr.source];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/embedded_plan.js b/presto-main/src/main/resources/webapp/dist/embedded_plan.js index c23ced129..d5b18ce99 100644 --- a/presto-main/src/main/resources/webapp/dist/embedded_plan.js +++ b/presto-main/src/main/resources/webapp/dist/embedded_plan.js @@ -20542,7 +20542,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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.leftAggr.source, nodeInfo.rightAggr.source];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/hetuqueryeditor.js b/presto-main/src/main/resources/webapp/dist/hetuqueryeditor.js index 190cb44c8..3c554a117 100644 --- a/presto-main/src/main/resources/webapp/dist/hetuqueryeditor.js +++ b/presto-main/src/main/resources/webapp/dist/hetuqueryeditor.js @@ -33028,7 +33028,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.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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.leftAggr.source, nodeInfo.rightAggr.source];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/index.js b/presto-main/src/main/resources/webapp/dist/index.js index cab92ab62..1c2fb3866 100644 --- a/presto-main/src/main/resources/webapp/dist/index.js +++ b/presto-main/src/main/resources/webapp/dist/index.js @@ -154,7 +154,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.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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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.failed) {\n case \"USER_ERROR\":\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.failed) {\n case \"USER_ERROR\":\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\n }\n\n return query.state;\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./newUtils.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.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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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.failed) {\n case \"USER_ERROR\":\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.failed) {\n case \"USER_ERROR\":\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\n }\n\n return query.state;\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.leftAggr.source, nodeInfo.rightAggr.source];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./newUtils.js?"); /***/ }), @@ -23427,7 +23427,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.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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.leftAggr.source, nodeInfo.rightAggr.source];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/nodes.js b/presto-main/src/main/resources/webapp/dist/nodes.js index 8acca55d5..10547767c 100644 --- a/presto-main/src/main/resources/webapp/dist/nodes.js +++ b/presto-main/src/main/resources/webapp/dist/nodes.js @@ -20816,7 +20816,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.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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.leftAggr.source, nodeInfo.rightAggr.source];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/overview.js b/presto-main/src/main/resources/webapp/dist/overview.js index 43794d9eb..ae885a5be 100644 --- a/presto-main/src/main/resources/webapp/dist/overview.js +++ b/presto-main/src/main/resources/webapp/dist/overview.js @@ -26526,7 +26526,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.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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.leftAggr.source, nodeInfo.rightAggr.source];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/plan.js b/presto-main/src/main/resources/webapp/dist/plan.js index 80dda104b..577aed4f0 100644 --- a/presto-main/src/main/resources/webapp/dist/plan.js +++ b/presto-main/src/main/resources/webapp/dist/plan.js @@ -20542,7 +20542,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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.leftAggr.source, nodeInfo.rightAggr.source];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/query.js b/presto-main/src/main/resources/webapp/dist/query.js index 07f24e07c..d2aec23e3 100644 --- a/presto-main/src/main/resources/webapp/dist/query.js +++ b/presto-main/src/main/resources/webapp/dist/query.js @@ -20710,7 +20710,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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.leftAggr.source, nodeInfo.rightAggr.source];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/querymonitor.js b/presto-main/src/main/resources/webapp/dist/querymonitor.js index def71da8d..82c6c07dd 100644 --- a/presto-main/src/main/resources/webapp/dist/querymonitor.js +++ b/presto-main/src/main/resources/webapp/dist/querymonitor.js @@ -118,7 +118,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.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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.failed) {\n case \"USER_ERROR\":\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.failed) {\n case \"USER_ERROR\":\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\n }\n\n return query.state;\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./newUtils.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.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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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.failed) {\n case \"USER_ERROR\":\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.failed) {\n case \"USER_ERROR\":\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\n }\n\n return query.state;\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.leftAggr.source, nodeInfo.rightAggr.source];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./newUtils.js?"); /***/ }), @@ -23403,7 +23403,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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.leftAggr.source, nodeInfo.rightAggr.source];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/stage.js b/presto-main/src/main/resources/webapp/dist/stage.js index 31bd67b0e..0737615fc 100644 --- a/presto-main/src/main/resources/webapp/dist/stage.js +++ b/presto-main/src/main/resources/webapp/dist/stage.js @@ -20542,7 +20542,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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.leftAggr.source, nodeInfo.rightAggr.source];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/presto-main/src/main/resources/webapp/dist/worker.js b/presto-main/src/main/resources/webapp/dist/worker.js index 053edff7c..39070d446 100644 --- a/presto-main/src/main/resources/webapp/dist/worker.js +++ b/presto-main/src/main/resources/webapp/dist/worker.js @@ -20506,7 +20506,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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\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.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\nexports.bubbleSort = bubbleSort;\nexports.removeNodeTypePackage = removeNodeTypePackage;\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: '#0e5201' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#678975',\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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 \"SUSPENDED\":\n return STATE_COLOR_MAP.BLOCKED;\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) {\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 switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === \"USER_CANCELED\") {\n return \"USER CANCELED\";\n }\n return \"USER ERROR\";\n case \"INTERNAL_ERROR\":\n return \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n return \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n return \"EXTERNAL ERROR\";\n }\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) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query);\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 var nodeType = removeNodeTypePackage(nodeInfo[\"@type\"]);\n switch (nodeType) {\n case \"OutputNode\":\n case \"ExplainAnalyzeNode\":\n case \"ProjectNode\":\n case \"FilterNode\":\n case \"AggregationNode\":\n case \"SortNode\":\n case \"MarkDistinctNode\":\n case \"WindowNode\":\n case \"RowNumberNode\":\n case \"TopNRowNumberNode\":\n case \"LimitNode\":\n case \"DistinctLimitNode\":\n case \"TopNNode\":\n case \"SampleNode\":\n case \"TableWriterNode\":\n case \"DeleteNode\":\n case 'TableDeleteNode':\n case 'TableFinishNode':\n case 'GroupIdNode':\n case 'CTEScanNode':\n case 'UnnestNode':\n case 'EnforceSingleRowNode':\n return [nodeInfo.source];\n case 'JoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'JoinOnAggregationNode':\n return [nodeInfo.leftAggr.source, nodeInfo.rightAggr.source];\n case 'SemiJoinNode':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'SpatialJoinNode':\n return [nodeInfo.left, nodeInfo.right];\n case 'IndexJoinNode':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'UnionNode':\n case 'ExchangeNode':\n return nodeInfo.sources;\n case 'RemoteSourceNode':\n case 'TableScanNode':\n case 'ValuesNode':\n case 'IndexSourceNode':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeType);\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 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 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 bubbleSort(arr) {\n var len = arr.length;\n for (var i = 0; i < len - 1; i++) {\n for (var j = 0; j < len - 1 - i; j++) {\n if (arr[j].value < arr[j + 1].value) {\n var temp = arr[j + 1];\n arr[j + 1] = arr[j];\n arr[j] = temp;\n }\n }\n }\n return arr;\n}\n\n// Remove the Java package from each node type to convert the node type to the short name.\n// For example, in the response sent from the server, an output node is represented by\n// \"io.prestosql.sql.planner.plan.OutputNode\". After the invocation of this function,\n// the short name \"OutputNode\" will be returned.\nfunction removeNodeTypePackage(nodeType) {\n var classEndIndex = nodeType.lastIndexOf(\".\");\n return nodeType.substr(classEndIndex + 1);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }), diff --git a/presto-main/src/main/resources/webapp/src/newUtils.js b/presto-main/src/main/resources/webapp/src/newUtils.js index e240dc25e..09bbe4ad6 100644 --- a/presto-main/src/main/resources/webapp/src/newUtils.js +++ b/presto-main/src/main/resources/webapp/src/newUtils.js @@ -218,7 +218,7 @@ export function getChildren(nodeInfo: any) case 'JoinNode': return [nodeInfo.left, nodeInfo.right]; case 'JoinOnAggregationNode': - return [nodeInfo.left, nodeInfo.right]; + return [nodeInfo.leftAggr.source, nodeInfo.rightAggr.source]; case 'SemiJoinNode': return [nodeInfo.source, nodeInfo.filteringSource]; case 'SpatialJoinNode': diff --git a/presto-main/src/main/resources/webapp/src/utils.js b/presto-main/src/main/resources/webapp/src/utils.js index 7ab18b9e1..fe991a591 100644 --- a/presto-main/src/main/resources/webapp/src/utils.js +++ b/presto-main/src/main/resources/webapp/src/utils.js @@ -246,7 +246,7 @@ export function getChildren(nodeInfo: any) case 'JoinNode': return [nodeInfo.left, nodeInfo.right]; case 'JoinOnAggregationNode': - return [nodeInfo.left, nodeInfo.right]; + return [nodeInfo.leftAggr.source, nodeInfo.rightAggr.source]; case 'SemiJoinNode': return [nodeInfo.source, nodeInfo.filteringSource]; case 'SpatialJoinNode': -- Gitee From 54f31827b61de34d20796e743d2b41e1f765a2bf Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Tue, 28 Feb 2023 18:27:18 +0530 Subject: [PATCH 31/36] Group Join Dynamic Filter fix. --- .../dynamicfilter/DynamicFilterService.java | 51 +++++++++++++++++++ .../execution/SqlStageExecution.java | 5 ++ .../java/io/prestosql/operator/JoinUtils.java | 10 ++++ .../planner/iterative/rule/TablePushdown.java | 9 ++-- .../optimizations/BeginTableWrite.java | 14 +++++ .../planner/optimizations/PruneCTENodes.java | 24 +++++++++ 6 files changed, 109 insertions(+), 4 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/dynamicfilter/DynamicFilterService.java b/presto-main/src/main/java/io/prestosql/dynamicfilter/DynamicFilterService.java index ed09b3269..1d8b4d2e8 100644 --- a/presto-main/src/main/java/io/prestosql/dynamicfilter/DynamicFilterService.java +++ b/presto-main/src/main/java/io/prestosql/dynamicfilter/DynamicFilterService.java @@ -34,6 +34,7 @@ import io.prestosql.spi.dynamicfilter.DynamicFilter.DataType; import io.prestosql.spi.dynamicfilter.DynamicFilterFactory; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.Symbol; import io.prestosql.spi.relation.CallExpression; @@ -338,6 +339,13 @@ public class DynamicFilterService registerTasksHelper(node, semiJoinNode.getFilteringSourceJoinSymbol(), Collections.singletonMap(semiJoinNode.getDynamicFilterId().get(), semiJoinNode.getFilteringSourceJoinSymbol()), taskIds, workers, stateMachine); } } + else if (node instanceof JoinOnAggregationNode) { + JoinOnAggregationNode joinNode = (JoinOnAggregationNode) node; + List criterias = joinNode.getCriteria(); + if (!criterias.isEmpty()) { + registerTasksHelper(node, criterias.get(0).getRight(), joinNode.getDynamicFilters(), taskIds, workers, stateMachine); + } + } } private void registerTasksHelper(PlanNode node, Symbol buildSymbol, Map dynamicFiltersMap, Set taskIds, Set workers, StageStateMachine stateMachine) @@ -358,6 +366,9 @@ public class DynamicFilterService else if (node instanceof SemiJoinNode) { filters.put(filterId, extractDynamicFilterRegistryInfo((SemiJoinNode) node, stateMachine.getSession())); } + else if (node instanceof JoinOnAggregationNode) { + filters.put(filterId, extractDynamicFilterRegistryInfo((JoinOnAggregationNode) node, stateMachine.getSession(), filterId)); + } dynamicFiltersToTask.putIfAbsent(filterId + "-" + queryId, new CopyOnWriteArraySet<>()); CopyOnWriteArraySet taskSet = dynamicFiltersToTask.get(filterId + "-" + queryId); taskSet.addAll(taskIds); @@ -511,6 +522,43 @@ public class DynamicFilterService } } + private static DynamicFilterRegistryInfo extractDynamicFilterRegistryInfo(JoinOnAggregationNode node, Session session, String filterId) + { + Symbol symbol = node.getCriteria().isEmpty() ? null : node.getCriteria().get(0).getLeft(); + List filterNodes = findFilterNodeInStage(node); + + if (filterNodes.isEmpty()) { + return new DynamicFilterRegistryInfo(symbol, GLOBAL, session, Optional.empty()); + } + else { + Optional> filterPredicate = Optional.empty(); + if (symbol == null) { + //Symbol is not found in Join Node. It must have been pushed down to filters. + for (FilterNode filter : filterNodes) { + DynamicFilters.ExtractResult extractResult = DynamicFilters.extractDynamicFilters(filter.getPredicate()); + List dynamicConjuncts = extractResult.getDynamicConjuncts(); + for (DynamicFilters.Descriptor desc : dynamicConjuncts) { + if (desc.getId().equals(filterId)) { + checkArgument(desc.getInput() instanceof VariableReferenceExpression, "Expression not symbol reference"); + symbol = new Symbol(((VariableReferenceExpression) desc.getInput()).getName()); + if (desc.getFilter().isPresent()) { + filterPredicate = DynamicFilters.createDynamicFilterPredicate(desc.getFilter()); + } + break; + } + } + if (symbol != null) { + break; + } + } + if (symbol == null) { + throw new IllegalStateException("DynamicFilter symbol not found to register"); + } + } + return new DynamicFilterRegistryInfo(symbol, LOCAL, session, filterPredicate); + } + } + private static DynamicFilterRegistryInfo extractDynamicFilterRegistryInfo(SemiJoinNode node, Session session) { Symbol symbol = node.getFilteringSourceJoinSymbol(); @@ -625,6 +673,9 @@ public class DynamicFilterService if (planNode instanceof SemiJoinNode) { return ((SemiJoinNode) planNode).getDynamicFilterId().map(ImmutableSet::of).orElse(ImmutableSet.of()); } + else if (planNode instanceof JoinOnAggregationNode) { + return ((JoinOnAggregationNode) planNode).getDynamicFilters().keySet(); + } throw new IllegalStateException("getDynamicFiltersProducedInPlanNode called with neither JoinNode nor SemiJoinNode"); } diff --git a/presto-main/src/main/java/io/prestosql/execution/SqlStageExecution.java b/presto-main/src/main/java/io/prestosql/execution/SqlStageExecution.java index ad847703e..e4c3cf90e 100644 --- a/presto-main/src/main/java/io/prestosql/execution/SqlStageExecution.java +++ b/presto-main/src/main/java/io/prestosql/execution/SqlStageExecution.java @@ -43,6 +43,7 @@ import io.prestosql.snapshot.QuerySnapshotManager; import io.prestosql.spi.PrestoException; import io.prestosql.spi.QueryId; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.PlanNodeId; import io.prestosql.spi.plan.TableScanNode; @@ -247,6 +248,10 @@ public final class SqlStageExecution SemiJoinNode semiJoinNode = (SemiJoinNode) node; dynamicFilterService.registerTasks(semiJoinNode, allTasks, getScheduledNodes(), stateMachine); } + else if (node instanceof JoinOnAggregationNode) { + JoinOnAggregationNode joinNode = (JoinOnAggregationNode) node; + dynamicFilterService.registerTasks(joinNode, allTasks, getScheduledNodes(), stateMachine); + } traverseNodesForDynamicFiltering(node.getSources()); } } diff --git a/presto-main/src/main/java/io/prestosql/operator/JoinUtils.java b/presto-main/src/main/java/io/prestosql/operator/JoinUtils.java index 6689b4449..bf6a7de6b 100644 --- a/presto-main/src/main/java/io/prestosql/operator/JoinUtils.java +++ b/presto-main/src/main/java/io/prestosql/operator/JoinUtils.java @@ -17,6 +17,7 @@ import com.google.common.collect.ImmutableList; import io.prestosql.spi.Page; import io.prestosql.spi.block.Block; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.ProjectNode; import io.prestosql.sql.planner.optimizations.PlanNodeSearcher; @@ -72,6 +73,15 @@ public final class JoinUtils .where(joinNode -> isRemoteReplicatedExchange(joinNode) || isRemoteReplicatedSourceNode(joinNode)) .matches(); } + else if (node instanceof JoinOnAggregationNode) { + return PlanNodeSearcher.searchFrom(((JoinOnAggregationNode) node).getRight()) + .recurseOnlyWhen( + MorePredicates.isInstanceOfAny(ProjectNode.class) + .or(JoinUtils::isLocalRepartitionExchange) + .or(JoinUtils::isLocalGatherExchange)) // used in cross join case + .where(joinNode -> isRemoteReplicatedExchange(joinNode) || isRemoteReplicatedSourceNode(joinNode)) + .matches(); + } return PlanNodeSearcher.searchFrom(((SemiJoinNode) node).getFilteringSource()) .recurseOnlyWhen( MorePredicates.isInstanceOfAny(ProjectNode.class) diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/TablePushdown.java b/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/TablePushdown.java index cfd1fb773..f7f7a35b1 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/TablePushdown.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/TablePushdown.java @@ -27,6 +27,7 @@ import io.prestosql.spi.metadata.TableHandle; import io.prestosql.spi.plan.AggregationNode; import io.prestosql.spi.plan.Assignments; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.ProjectNode; import io.prestosql.spi.plan.Symbol; @@ -187,12 +188,12 @@ public class TablePushdown * */ stack.push(new NodeWithTreeDirection(node, DIRECTION.LEFT)); if (!(node instanceof TableScanNode)) { - if (node instanceof JoinNode) { + if (node instanceof JoinNode || node instanceof JoinOnAggregationNode) { if (updateStack(resolveNodeFromGroupReference(node, 0, lookup), lookup, stack)) { return true; } else { - while (!(stack.peek().getNode() instanceof JoinNode)) { + while (!(stack.peek().getNode() instanceof JoinNode || stack.peek().getNode() instanceof JoinOnAggregationNode)) { stack.pop(); } NodeWithTreeDirection tempNode = stack.pop(); @@ -373,7 +374,7 @@ public class TablePushdown for (NodeWithTreeDirection nodeInPath : stack) { PlanNode node = nodeInPath.getNode(); - if (node instanceof JoinNode) { + if (node instanceof JoinNode || node instanceof JoinOnAggregationNode) { hasJoinInPath = true; break; } @@ -444,7 +445,7 @@ public class TablePushdown Stack intermediateOuterTableStack = new Stack<>(); // First pop the stack till we reach a join node and push into another intermediateOuterTableStack. - while (!(stack.peek().getNode() instanceof JoinNode)) { + while (!(stack.peek().getNode() instanceof JoinNode || stack.peek().getNode() instanceof JoinOnAggregationNode)) { intermediateOuterTableStack.push(stack.pop()); } diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/BeginTableWrite.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/BeginTableWrite.java index 8f4445167..dab3562c6 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/BeginTableWrite.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/BeginTableWrite.java @@ -22,6 +22,7 @@ import io.prestosql.spi.metadata.TableHandle; import io.prestosql.spi.operator.ReuseExchangeOperator; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.PlanNodeIdAllocator; import io.prestosql.spi.plan.ProjectNode; @@ -393,6 +394,12 @@ public class BeginTableWrite return locateTableScanHandle(joinNode.getLeft()); } } + if (node instanceof JoinOnAggregationNode) { + JoinOnAggregationNode joinNode = (JoinOnAggregationNode) node; + if (joinNode.getType() == JoinNode.Type.INNER) { + return locateTableScanHandle(joinNode.getLeft()); + } + } throw new IllegalArgumentException("Invalid descendant for DeleteNode or UpdateNode" + node.getClass().getName()); } @@ -458,6 +465,13 @@ public class BeginTableWrite return replaceChildren(node, ImmutableList.of(source, joinNode.getRight())); } } + if (node instanceof JoinOnAggregationNode) { + JoinOnAggregationNode joinNode = (JoinOnAggregationNode) node; + if (joinNode.getType() == JoinNode.Type.INNER) { + PlanNode source = rewriteModifyTableScan(joinNode.getLeft(), handle); + return replaceChildren(node, ImmutableList.of(source, joinNode.getRight())); + } + } throw new IllegalArgumentException("Invalid descendant for DeleteNode or UpdateNode: " + node.getClass().getName()); } } diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PruneCTENodes.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PruneCTENodes.java index 546137d7e..8b3537138 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PruneCTENodes.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PruneCTENodes.java @@ -198,6 +198,9 @@ public class PruneCTENodes else if (node instanceof JoinNode) { return getProbeCTENodeId(((JoinNode) node).getLeft()); } + else if (node instanceof JoinOnAggregationNode) { + return getProbeCTENodeId(((JoinOnAggregationNode) node).getLeft()); + } return null; } @@ -240,6 +243,10 @@ public class PruneCTENodes PlanNode joinNode = ((JoinNode) node).getLeft(); return getChildCTERefNum(joinNode); } + else if (node instanceof JoinOnAggregationNode) { + PlanNode joinNode = ((JoinOnAggregationNode) node).getLeft(); + return getChildCTERefNum(joinNode); + } return null; } @@ -267,6 +274,11 @@ public class PruneCTENodes level++; return getChildCTELevel(joinNode, level); } + else if (node instanceof JoinOnAggregationNode) { + PlanNode joinNode = ((JoinOnAggregationNode) node).getLeft(); + level++; + return getChildCTELevel(joinNode, level); + } return level; } @@ -295,6 +307,18 @@ public class PruneCTENodes return node.getSource(); } } + + // If there is a self join below CTE node, then CTE should be removed. + if (node.getSource() instanceof JoinOnAggregationNode) { + // check if this join is self join + TableHandle left = getTableHandle(((JoinOnAggregationNode) node.getSource()).getLeft()); + TableHandle right = getTableHandle(((JoinOnAggregationNode) node.getSource()).getRight()); + if (left != null && right != null && left.getConnectorHandle().equals(right.getConnectorHandle())) { + // both tables are same, means it is self join. + node = (CTEScanNode) visitPlan(node, context); + return node.getSource(); + } + } } if (!isNodeAlreadyVisited) { cteUsageMap.merge(commonCTERefNum, 1, Integer::sum); -- Gitee From 62df4899eeaaa09d72ca814d9e461ecc00167253 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Wed, 1 Mar 2023 12:00:38 +0530 Subject: [PATCH 32/36] Group Join - optimizations. --- .../operator/LookupGroupJoinOperator.java | 35 ++++--- .../operator/LookupGroupJoinPageBuilder.java | 3 + .../iterative/rule/HintedReorderJoins.java | 92 +++++++++++++------ .../optimizations/PredicatePushDown.java | 4 +- 4 files changed, 94 insertions(+), 40 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java index 600b57a00..6df691cda 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java @@ -17,6 +17,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.io.Closer; import com.google.common.util.concurrent.ListenableFuture; +import io.airlift.log.Logger; import io.prestosql.memory.context.LocalMemoryContext; import io.prestosql.operator.GroupJoinProbe.GroupJoinProbeFactory; import io.prestosql.operator.LookupJoinOperator.SpillInfoSnapshot; @@ -87,6 +88,8 @@ public class LookupGroupJoinOperator CLOSED } + private static final Logger LOG = Logger.get(LookupGroupJoinOperator.class); + private final OperatorContext operatorContext; private ExecutionHelperFactory executionHelperFactory; @@ -388,9 +391,14 @@ public class LookupGroupJoinOperator private void finishAggregation() { + DriverYieldSignal yieldSignal = operatorContext.getDriverContext().getYieldSignal(); Page page = processAggregation(); - if (page != null) { + while (page != null) { updateIndex(page); + if (yieldSignal.isSet()) { + break; + } + page = processAggregation(); } } @@ -402,13 +410,11 @@ public class LookupGroupJoinOperator // Only prepare the Aggr outputs if (aggregationBuilder != null && aggregationBuilder.isFull()) { finishAggregation(); - break; } return null; case AGGR_FINISHING: - if (aggregationBuilder != null) { - finishAggregation(); - } + finishAggregation(); + return null; case AGGR_FINISHED: if (pageIndexItr == null) { pageIndexItr = index.getPages(); @@ -470,6 +476,9 @@ public class LookupGroupJoinOperator verify(pageBuilder.isEmpty()); Page output = outputPage; outputPage = null; + if (output.getPositionCount() > 1) { + LOG.error("Page has more than 1 record. %s", output); + } return output; } @@ -580,12 +589,16 @@ public class LookupGroupJoinOperator if (aggrOutputPages == null) { if (!aggregationInputProcessed && aggregator.isProduceDefaultOutput()) { // global aggregations always generate an output row with the default aggregation output (e.g. 0 for COUNT, NULL for SUM) - state = State.AGGR_FINISHED; + if (state == State.AGGR_FINISHING) { + state = State.AGGR_FINISHED; + } return aggregator.getGlobalAggregationOutput(); } if (aggregationBuilder == null) { - state = State.AGGR_FINISHED; + if (state == State.AGGR_FINISHING) { + state = State.AGGR_FINISHED; + } return null; } @@ -655,13 +668,13 @@ public class LookupGroupJoinOperator if (!value.isPresent()) { return; } - long joinPositionWithinPartition; + /*long joinPositionWithinPartition; if (joinPosition >= 0) { joinPositionWithinPartition = lookupSourceProvider.withLease(lookupSourceLease -> lookupSourceLease.getLookupSource().joinPositionWithinPartition(joinPosition)); } else { joinPositionWithinPartition = -1; - } + }*/ if (probe == null) { return; } @@ -690,9 +703,9 @@ public class LookupGroupJoinOperator if (!joinCurrentPosition(lookupSource, yieldSignal)) { break; } - if (!currentProbePositionProducedRow) { + /*if (!currentProbePositionProducedRow) { currentProbePositionProducedRow = true; - } + }*/ } currentProbePositionProducedRow = false; if (!advanceProbePosition(lookupSource)) { diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java index 76661fb98..809ac19f8 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinPageBuilder.java @@ -117,6 +117,9 @@ public class LookupGroupJoinPageBuilder private Page processAggregationOnPage(long count, Page sourcePage, AggregationBuilder aggregationBuilder) { // TODO Vineet check on how to convert into future object and relate in normal code flow. + if (count == 1) { + return sourcePage; + } Page finalPage; for (int i = 0; i < count; i++) { Work work = aggregationBuilder.processPage(sourcePage); diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/HintedReorderJoins.java b/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/HintedReorderJoins.java index 92c637332..668f25627 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/HintedReorderJoins.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/iterative/rule/HintedReorderJoins.java @@ -218,25 +218,43 @@ public class HintedReorderJoins private void flattenNode(PlanNode node, int limit) { // (limit - 2) because you need to account for adding left and right side - if (!(node instanceof JoinNode) || (sources.size() > (limit - 2))) { + if (!(node instanceof JoinNode || node instanceof JoinOnAggregationNode) || (sources.size() > (limit - 2))) { sources.add(node); return; } - JoinNode joinNode = (JoinNode) node; - if (joinNode.getType() != INNER - || !logicalRowExpressions.isDeterministic(joinNode.getFilter().orElse(TRUE_CONSTANT)) - || joinNode.getDistributionType().isPresent()) { - sources.add(node); - return; - } + if (node instanceof JoinNode) { + JoinNode joinNode = (JoinNode) node; + if (joinNode.getType() != INNER + || !logicalRowExpressions.isDeterministic(joinNode.getFilter().orElse(TRUE_CONSTANT)) + || joinNode.getDistributionType().isPresent()) { + sources.add(node); + return; + } + + // we set the left limit to limit - 1 to account for the node on the right + flattenNode(joinNode.getLeft(), limit - 1); + flattenNode(joinNode.getRight(), limit); + joinNode.getCriteria().stream() + .map(criteria -> toRowExpression(criteria, types)) + .forEach(filters::add); + } + else if (node instanceof JoinOnAggregationNode) { + JoinOnAggregationNode joinNode = (JoinOnAggregationNode) node; + if (joinNode.getType() != INNER + || !logicalRowExpressions.isDeterministic(joinNode.getFilter().orElse(TRUE_CONSTANT)) + || joinNode.getDistributionType().isPresent()) { + sources.add(node); + return; + } - // we set the left limit to limit - 1 to account for the node on the right - flattenNode(joinNode.getLeft(), limit - 1); - flattenNode(joinNode.getRight(), limit); - joinNode.getCriteria().stream() - .map(criteria -> toRowExpression(criteria, types)) - .forEach(filters::add); + // we set the left limit to limit - 1 to account for the node on the right + flattenNode(joinNode.getLeft(), limit - 1); + flattenNode(joinNode.getRight(), limit); + joinNode.getCriteria().stream() + .map(criteria -> toRowExpression(criteria, types)) + .forEach(filters::add); + } } ReorderJoins.MultiJoinNode toMultiJoinNode() @@ -770,26 +788,44 @@ public class HintedReorderJoins PlanNode resolved = lookup.resolve(node); // (limit - 2) because you need to account for adding left and right side - if (!(resolved instanceof JoinNode) || (sources.size() > (limit - 2))) { + if (!(resolved instanceof JoinNode || resolved instanceof JoinOnAggregationNode) || (sources.size() > (limit - 2))) { sources.add(node); return; } + if (resolved instanceof JoinNode) { + JoinNode joinNode = (JoinNode) resolved; + if (joinNode.getType() != INNER + || !determinismEvaluator.isDeterministic(joinNode.getFilter().orElse(TRUE_CONSTANT)) + || joinNode.getDistributionType().isPresent()) { + sources.add(node); + return; + } - JoinNode joinNode = (JoinNode) resolved; - if (joinNode.getType() != INNER - || !determinismEvaluator.isDeterministic(joinNode.getFilter().orElse(TRUE_CONSTANT)) - || joinNode.getDistributionType().isPresent()) { - sources.add(node); - return; + // we set the left limit to limit - 1 to account for the node on the right + flattenNode(joinNode.getLeft(), limit - 1); + flattenNode(joinNode.getRight(), limit); + joinNode.getCriteria().stream() + .map(criteria -> toRowExpression(criteria, types)) + .forEach(filters::add); + joinNode.getFilter().ifPresent(filters::add); } + else if (resolved instanceof JoinOnAggregationNode) { + JoinOnAggregationNode joinNode = (JoinOnAggregationNode) resolved; + if (joinNode.getType() != INNER + || !determinismEvaluator.isDeterministic(joinNode.getFilter().orElse(TRUE_CONSTANT)) + || joinNode.getDistributionType().isPresent()) { + sources.add(node); + return; + } - // we set the left limit to limit - 1 to account for the node on the right - flattenNode(joinNode.getLeft(), limit - 1); - flattenNode(joinNode.getRight(), limit); - joinNode.getCriteria().stream() - .map(criteria -> toRowExpression(criteria, types)) - .forEach(filters::add); - joinNode.getFilter().ifPresent(filters::add); + // we set the left limit to limit - 1 to account for the node on the right + flattenNode(joinNode.getLeft(), limit - 1); + flattenNode(joinNode.getRight(), limit); + joinNode.getCriteria().stream() + .map(criteria -> toRowExpression(criteria, types)) + .forEach(filters::add); + joinNode.getFilter().ifPresent(filters::add); + } } MultiJoinNode toMultiJoinNode() diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PredicatePushDown.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PredicatePushDown.java index 39ef407cc..e6eb95966 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PredicatePushDown.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PredicatePushDown.java @@ -40,6 +40,7 @@ import io.prestosql.spi.plan.CTEScanNode; import io.prestosql.spi.plan.FilterNode; import io.prestosql.spi.plan.GroupIdNode; import io.prestosql.spi.plan.JoinNode; +import io.prestosql.spi.plan.JoinOnAggregationNode; import io.prestosql.spi.plan.MarkDistinctNode; import io.prestosql.spi.plan.PlanNode; import io.prestosql.spi.plan.PlanNodeIdAllocator; @@ -1248,7 +1249,8 @@ public class PredicatePushDown // See if we can push the left effective predicate to the right side for (RowExpression conjunct : new RowExpressionEqualityInference.Builder(metadata, typeManager).nonInferrableConjuncts(leftEffectivePredicate)) { // do not push down dynamic filters here - if (!(node instanceof JoinNode && getDescriptor(conjunct).isPresent() && ((JoinNode) node).getDynamicFilters().keySet().contains(getDescriptor(conjunct).get().getId()))) { + if (!((node instanceof JoinNode && getDescriptor(conjunct).isPresent() && ((JoinNode) node).getDynamicFilters().keySet().contains(getDescriptor(conjunct).get().getId())) + || (node instanceof JoinOnAggregationNode && getDescriptor(conjunct).isPresent() && ((JoinOnAggregationNode) node).getDynamicFilters().keySet().contains(getDescriptor(conjunct).get().getId())))) { RowExpression rewritten = allInference.rewriteExpression(conjunct, not(in(leftVariables))); if (rewritten != null) { rightPushDownConjuncts.add(rewritten); -- Gitee From 8a7c0dbf9ad89b3b961db5f1e98f9fa5e2ee8dd2 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Wed, 1 Mar 2023 18:28:54 +0530 Subject: [PATCH 33/36] Group Join - lookup record count fix. --- .../io/prestosql/operator/LookupGroupJoinOperator.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java index 6df691cda..ba5345687 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java @@ -17,7 +17,6 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.io.Closer; import com.google.common.util.concurrent.ListenableFuture; -import io.airlift.log.Logger; import io.prestosql.memory.context.LocalMemoryContext; import io.prestosql.operator.GroupJoinProbe.GroupJoinProbeFactory; import io.prestosql.operator.LookupJoinOperator.SpillInfoSnapshot; @@ -88,8 +87,6 @@ public class LookupGroupJoinOperator CLOSED } - private static final Logger LOG = Logger.get(LookupGroupJoinOperator.class); - private final OperatorContext operatorContext; private ExecutionHelperFactory executionHelperFactory; @@ -386,7 +383,6 @@ public class LookupGroupJoinOperator localUserMemoryContext.setBytes(index.getEstimatedSize().toBytes()); } } - operatorContext.recordOutput(page.getSizeInBytes(), page.getPositionCount()); } private void finishAggregation() @@ -476,9 +472,6 @@ public class LookupGroupJoinOperator verify(pageBuilder.isEmpty()); Page output = outputPage; outputPage = null; - if (output.getPositionCount() > 1) { - LOG.error("Page has more than 1 record. %s", output); - } return output; } -- Gitee From 5c1ab749523ee302ddf0c036ae8b619ff1347ca4 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Fri, 3 Mar 2023 18:51:12 +0530 Subject: [PATCH 34/36] Group Join - probe early processing logic. --- .../operator/LookupGroupJoinOperator.java | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java index ba5345687..1a7d0f854 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java @@ -17,6 +17,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.io.Closer; import com.google.common.util.concurrent.ListenableFuture; +import io.airlift.log.Logger; import io.prestosql.memory.context.LocalMemoryContext; import io.prestosql.operator.GroupJoinProbe.GroupJoinProbeFactory; import io.prestosql.operator.LookupJoinOperator.SpillInfoSnapshot; @@ -87,6 +88,8 @@ public class LookupGroupJoinOperator CLOSED } + private final Logger log = Logger.get(LookupGroupJoinOperator.class); + private final OperatorContext operatorContext; private ExecutionHelperFactory executionHelperFactory; @@ -247,6 +250,7 @@ public class LookupGroupJoinOperator return; } if (State.CONSUMING_INPUT == state) { + log.info("State changed to %s", State.AGGR_FINISHING); state = State.AGGR_FINISHING; } finishing = true; @@ -407,18 +411,43 @@ public class LookupGroupJoinOperator if (aggregationBuilder != null && aggregationBuilder.isFull()) { finishAggregation(); } + if (lookupSourceProviderFuture.isDone()) { + // eager processing of aggregated pages as build side is done. + if (pageIndexItr == null) { + log.info("Lookup Probe Page Itr created"); + pageIndexItr = index.getPages(); + } + if (probe == null && tryFetchLookupSourceProvider() && pageIndexItr != null && pageIndexItr.hasNext()) { + createProbe(pageIndexItr.next()); + } + break; + } return null; case AGGR_FINISHING: finishAggregation(); + if (lookupSourceProviderFuture.isDone()) { + // eager processing of aggregated pages as build side is done. + if (pageIndexItr == null) { + log.info("Lookup Probe Page Itr created"); + pageIndexItr = index.getPages(); + } + if (probe == null && tryFetchLookupSourceProvider() && pageIndexItr != null && pageIndexItr.hasNext()) { + createProbe(pageIndexItr.next()); + } + break; + } return null; case AGGR_FINISHED: + // All Aggregation done, probe can start if (pageIndexItr == null) { + log.info("Lookup Probe Page Itr created"); pageIndexItr = index.getPages(); } if (probe == null && tryFetchLookupSourceProvider() && pageIndexItr != null && pageIndexItr.hasNext()) { createProbe(pageIndexItr.next()); } if (probe == null && tryFetchLookupSourceProvider() && pageIndexItr != null && !pageIndexItr.hasNext() && finishing) { + log.info("State changed to %s", State.SOURCE_BUILT); state = State.SOURCE_BUILT; } /*if (probe != null) { @@ -432,9 +461,9 @@ public class LookupGroupJoinOperator return null; } - if (probe == null && pageBuilder.isEmpty() && !finishing) { + /*if (probe == null && pageBuilder.isEmpty()*//* && !finishing*//*) { return null; - } + }*/ if (!lookupSourceProviderFuture.isDone()) { return null; } @@ -450,7 +479,7 @@ public class LookupGroupJoinOperator lookupSourceProvider = new StaticLookupSourceProvider(new EmptyLookupSource()); } - if (probe == null && finishing && unfinishedAggrWork == null && !unspilling) { + if (probe == null && finishing && state == State.SOURCE_BUILT) { /* * We do not have input probe and we won't have any, as we're finishing. * Let LookupSourceFactory know LookupSources can be disposed as far as we're concerned. @@ -460,7 +489,7 @@ public class LookupGroupJoinOperator partitionedConsumption = lookupSourceFactory.finishProbeOperator(lookupJoinsCount); afterMemOpFinish.run(); afterMemOpFinish = () -> {}; - unspilling = true; + //unspilling = true; finished = true; } @@ -583,6 +612,7 @@ public class LookupGroupJoinOperator if (!aggregationInputProcessed && aggregator.isProduceDefaultOutput()) { // global aggregations always generate an output row with the default aggregation output (e.g. 0 for COUNT, NULL for SUM) if (state == State.AGGR_FINISHING) { + log.info("State changed to %s", State.AGGR_FINISHED); state = State.AGGR_FINISHED; } return aggregator.getGlobalAggregationOutput(); @@ -590,6 +620,7 @@ public class LookupGroupJoinOperator if (aggregationBuilder == null) { if (state == State.AGGR_FINISHING) { + log.info("State changed to %s", State.AGGR_FINISHED); state = State.AGGR_FINISHED; } return null; @@ -742,6 +773,7 @@ public class LookupGroupJoinOperator try (Closer closer = Closer.create()) { // `afterClose` must be run last. // Closer is documented to mimic try-with-resource, which implies close will happen in reverse order. + closer.register(index::clear); closer.register(afterMemOpFinish::run); closer.register(afterClose::run); -- Gitee From 41bac77177da39c15bd0f0b40a72011dbfb8726a Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Tue, 7 Mar 2023 12:19:45 +0530 Subject: [PATCH 35/36] Group Join - probe early processing logic fixes. --- .../HashBuilderGroupJoinOperator.java | 2 +- .../operator/LookupGroupJoinOperator.java | 102 ++++++++---------- .../io/prestosql/operator/PagesIndex.java | 36 +++++++ 3 files changed, 82 insertions(+), 58 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java index 20dedad62..3472a4b5c 100644 --- a/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/HashBuilderGroupJoinOperator.java @@ -271,7 +271,7 @@ public class HashBuilderGroupJoinOperator public void createAggrOnAggregationBuilder() { - if (aggregator.getStep().isOutputPartial() || !spillEnabled || hasOrderBy() || hasDistinct()) { + if (aggrOnAggregator.getStep().isOutputPartial() || !spillEnabled || hasOrderBy() || hasDistinct()) { aggrOnAggregationBuilder = new InMemoryHashAggregationBuilderWithReset( aggrOnAggregator.getAccumulatorFactories(), aggrOnAggregator.getStep(), diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java index 1a7d0f854..75e9fae3a 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java @@ -147,8 +147,8 @@ public class LookupGroupJoinOperator private List spilledPartitionsList = new ArrayList<>(); protected AggregationBuilder aggregationBuilder; - protected AggregationBuilder probeAggregationBuilder; - protected AggregationBuilder buildAggregationBuilder; + protected AggregationBuilder probeAggrOnAggregationBuilder; + protected AggregationBuilder buildAggrOnAggregationBuilder; protected LocalMemoryContext aggrMemoryContext; protected LocalMemoryContext aggrOnAggrMemoryContext; @@ -270,14 +270,15 @@ public class LookupGroupJoinOperator @Override public ListenableFuture isBlocked() { - if (state == State.CONSUMING_INPUT) { + if (state == State.CONSUMING_INPUT || state == State.AGGR_FINISHING) { if (executionHelper != null) { return executionHelper.isDone() ? NOT_BLOCKED : executionHelper; } - } - if (finishing) { return NOT_BLOCKED; } + /*if (finishing) { + return NOT_BLOCKED; + }*/ return lookupSourceProviderFuture; } @@ -329,7 +330,7 @@ public class LookupGroupJoinOperator public void addInput(Page page) { requireNonNull(page, "page is null"); - checkState(probe == null, "Current page has not been completely processed yet"); + //`checkState(probe == null, "Current page has not been completely processed yet"); //checkState(tryFetchLookupSourceProvider(), "Not ready to handle input yet"); // create Aggregators and pass page to them for process checkState(state == State.CONSUMING_INPUT, "Operator is already finishing"); @@ -353,11 +354,11 @@ public class LookupGroupJoinOperator private void createProbe(Page page) { // create probe - if (buildAggregationBuilder == null) { + if (buildAggrOnAggregationBuilder == null) { LookupSource lookupSource = lookupSourceProvider.withLease((lookupSourceLease -> lookupSourceLease.getLookupSource())); - buildAggregationBuilder = lookupSource.getAggregationBuilder().duplicate(); + buildAggrOnAggregationBuilder = lookupSource.getAggregationBuilder().duplicate(); } - probe = joinProbeFactory.createGroupJoinProbe(page, false, lookupSourceProvider, probeAggregationBuilder, buildAggregationBuilder); + probe = joinProbeFactory.createGroupJoinProbe(page, false, lookupSourceProvider, probeAggrOnAggregationBuilder, buildAggrOnAggregationBuilder); // initialize to invalid join position to force output code to advance the cursors joinPosition = -1; @@ -407,46 +408,32 @@ public class LookupGroupJoinOperator { switch (state) { case CONSUMING_INPUT: - // Only prepare the Aggr outputs + // Only prepare the Aggr outputs. if (aggregationBuilder != null && aggregationBuilder.isFull()) { finishAggregation(); } if (lookupSourceProviderFuture.isDone()) { // eager processing of aggregated pages as build side is done. - if (pageIndexItr == null) { - log.info("Lookup Probe Page Itr created"); - pageIndexItr = index.getPages(); - } - if (probe == null && tryFetchLookupSourceProvider() && pageIndexItr != null && pageIndexItr.hasNext()) { - createProbe(pageIndexItr.next()); - } + createConditionalPageItrAndProbe(); break; } return null; case AGGR_FINISHING: + // Prepare the Aggr outputs for pending unprocessed pages. finishAggregation(); if (lookupSourceProviderFuture.isDone()) { // eager processing of aggregated pages as build side is done. - if (pageIndexItr == null) { - log.info("Lookup Probe Page Itr created"); - pageIndexItr = index.getPages(); - } - if (probe == null && tryFetchLookupSourceProvider() && pageIndexItr != null && pageIndexItr.hasNext()) { - createProbe(pageIndexItr.next()); - } + createConditionalPageItrAndProbe(); break; } return null; case AGGR_FINISHED: // All Aggregation done, probe can start - if (pageIndexItr == null) { - log.info("Lookup Probe Page Itr created"); - pageIndexItr = index.getPages(); - } - if (probe == null && tryFetchLookupSourceProvider() && pageIndexItr != null && pageIndexItr.hasNext()) { - createProbe(pageIndexItr.next()); + if (lookupSourceProviderFuture.isDone()) { + createConditionalPageItrAndProbe(); } - if (probe == null && tryFetchLookupSourceProvider() && pageIndexItr != null && !pageIndexItr.hasNext() && finishing) { + // Terminal Condition. + if (finishing && probe == null && tryFetchLookupSourceProvider() && pageIndexItr != null && !pageIndexItr.hasNext()) { log.info("State changed to %s", State.SOURCE_BUILT); state = State.SOURCE_BUILT; } @@ -479,7 +466,7 @@ public class LookupGroupJoinOperator lookupSourceProvider = new StaticLookupSourceProvider(new EmptyLookupSource()); } - if (probe == null && finishing && state == State.SOURCE_BUILT) { + if (probe == null && outputPage == null && state == State.SOURCE_BUILT) { /* * We do not have input probe and we won't have any, as we're finishing. * Let LookupSourceFactory know LookupSources can be disposed as far as we're concerned. @@ -510,6 +497,25 @@ public class LookupGroupJoinOperator return null; } + private void createConditionalPageItrAndProbe() + { + if (pageIndexItr == null) { + log.info("Lookup Probe Page Itr created"); + pageIndexItr = index.getPagesIterator(); + } + if (pageIndexItr != null && finishing && state == State.AGGR_FINISHED) { + if (pageIndexItr instanceof PagesIndex.PageIterator) { + ((PagesIndex.PageIterator) pageIndexItr).setFinished(true); + } + } + if (probe == null && outputPage == null && tryFetchLookupSourceProvider() && pageIndexItr != null && pageIndexItr.hasNext()) { + Page page = pageIndexItr.next(); + if (page != null) { + createProbe(page); + } + } + } + protected boolean hasOrderBy() { return aggregator.hasOrderBy(); @@ -541,24 +547,6 @@ public class LookupGroupJoinOperator } return operatorContext.isWaitingForMemory().isDone(); }); - probeAggregationBuilder = new InMemoryHashAggregationBuilderWithReset( - aggrOnAggregator.getAccumulatorFactories(), - aggrOnAggregator.getStep(), - aggrOnAggregator.getExpectedGroups(), - aggrOnAggregator.getGroupByTypes(), - aggrOnAggregator.getGroupByChannels(), - aggrOnAggregator.getHashChannel(), - operatorContext, - aggrOnAggregator.getMaxPartialMemory(), - aggrOnAggregator.getJoinCompiler(), - () -> { - aggrOnAggrMemoryContext.setBytes(((InMemoryHashAggregationBuilder) probeAggregationBuilder).getSizeInMemory()); - if (aggrOnAggregator.getStep().isOutputPartial() && aggrOnAggregator.getMaxPartialMemory().isPresent()) { - // do not yield on memory for partial aggregations - return true; - } - return operatorContext.isWaitingForMemory().isDone(); - }); } else { throw new UnsupportedOperationException("Not Supported"); @@ -567,8 +555,8 @@ public class LookupGroupJoinOperator public void createAggrOnAggregationBuilder() { - if (aggregator.getStep().isOutputPartial() || !spillEnabled || hasOrderBy() || hasDistinct()) { - probeAggregationBuilder = new InMemoryHashAggregationBuilderWithReset( + if (aggrOnAggregator.getStep().isOutputPartial() || !spillEnabled || hasOrderBy() || hasDistinct()) { + probeAggrOnAggregationBuilder = new InMemoryHashAggregationBuilderWithReset( aggrOnAggregator.getAccumulatorFactories(), aggrOnAggregator.getStep(), aggrOnAggregator.getExpectedGroups(), @@ -579,7 +567,7 @@ public class LookupGroupJoinOperator aggrOnAggregator.getMaxPartialMemory(), aggrOnAggregator.getJoinCompiler(), () -> { - aggrOnAggrMemoryContext.setBytes(((InMemoryHashAggregationBuilder) probeAggregationBuilder).getSizeInMemory()); + aggrOnAggrMemoryContext.setBytes(((InMemoryHashAggregationBuilder) probeAggrOnAggregationBuilder).getSizeInMemory()); if (aggrOnAggregator.getStep().isOutputPartial() && aggrOnAggregator.getMaxPartialMemory().isPresent()) { // do not yield on memory for partial aggregations return true; @@ -667,12 +655,12 @@ public class LookupGroupJoinOperator protected void closeProbeAggrOnAggregationBuilder() { - if (probeAggregationBuilder != null) { - probeAggregationBuilder.recordHashCollisions(hashCollisionsCounter); - probeAggregationBuilder.close(); + if (probeAggrOnAggregationBuilder != null) { + probeAggrOnAggregationBuilder.recordHashCollisions(hashCollisionsCounter); + probeAggrOnAggregationBuilder.close(); // probeAggregationBuilder.close() will release all memory reserved in memory accounting. // The reference must be set to null afterwards to avoid unaccounted memory. - probeAggregationBuilder = null; + probeAggrOnAggregationBuilder = null; } aggrOnAggrMemoryContext.setBytes(0); } diff --git a/presto-main/src/main/java/io/prestosql/operator/PagesIndex.java b/presto-main/src/main/java/io/prestosql/operator/PagesIndex.java index e23f42ef5..df96f4eee 100644 --- a/presto-main/src/main/java/io/prestosql/operator/PagesIndex.java +++ b/presto-main/src/main/java/io/prestosql/operator/PagesIndex.java @@ -583,6 +583,42 @@ public class PagesIndex }; } + public class PageIterator

+ extends AbstractIterator + { + private int pageCounter; + private boolean isFinished; + + @Override + protected Page computeNext() + { + if (pageCounter == channels[0].size()) { + if (isFinished) { + return endOfData(); + } + else { + return null; + } + } + + Block[] blocks = Stream.of(channels) + .map(channel -> channel.get(pageCounter)) + .toArray(Block[]::new); + pageCounter++; + return new Page(blocks); + } + + public void setFinished(boolean isFinished) + { + this.isFinished = isFinished; + } + } + + public PageIterator getPagesIterator() + { + return new PageIterator<>(); + } + public Iterator getSortedPages() { return new AbstractIterator() -- Gitee From caa34be7277251215cc71fbabb2827ff3abdb6ba Mon Sep 17 00:00:00 2001 From: Vineet Kumar Maheshwari Date: Wed, 8 Mar 2023 13:55:57 +0530 Subject: [PATCH 36/36] Group Join - probe early processing logic fixes. --- .../java/io/prestosql/operator/LookupGroupJoinOperator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java index 75e9fae3a..6740dc1ff 100644 --- a/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java +++ b/presto-main/src/main/java/io/prestosql/operator/LookupGroupJoinOperator.java @@ -330,7 +330,7 @@ public class LookupGroupJoinOperator public void addInput(Page page) { requireNonNull(page, "page is null"); - //`checkState(probe == null, "Current page has not been completely processed yet"); + //checkState(probe == null, "Current page has not been completely processed yet"); //checkState(tryFetchLookupSourceProvider(), "Not ready to handle input yet"); // create Aggregators and pass page to them for process checkState(state == State.CONSUMING_INPUT, "Operator is already finishing"); -- Gitee