From 7a586c8ca11e23ff077768c8766528013c50a4ce Mon Sep 17 00:00:00 2001 From: Pierre GIRAUD Date: Tue, 30 Jun 2026 08:17:40 +0200 Subject: [PATCH 1/2] feat(parser): parse hash buckets --- src/enums.ts | 5 ++ src/filters.ts | 12 ++++ .../__tests__/12-line-wrapped-plans/01-expect | 2 +- .../__tests__/12-line-wrapped-plans/02-expect | 2 +- .../__tests__/12-line-wrapped-plans/05-expect | 2 +- .../__tests__/12-line-wrapped-plans/06-expect | 4 +- .../__tests__/12-line-wrapped-plans/07-expect | 4 +- .../__tests__/from-text/45-buckets-expect | 23 ++++++++ .../__tests__/from-text/45-buckets-plan | 23 ++++++++ .../__tests__/from-text/46-buckets-expect | 24 ++++++++ .../__tests__/from-text/46-buckets-plan | 56 +++++++++++++++++++ src/services/plan-service.ts | 28 ++++++++++ 12 files changed, 178 insertions(+), 7 deletions(-) create mode 100644 src/services/__tests__/from-text/45-buckets-expect create mode 100644 src/services/__tests__/from-text/45-buckets-plan create mode 100644 src/services/__tests__/from-text/46-buckets-expect create mode 100644 src/services/__tests__/from-text/46-buckets-plan diff --git a/src/enums.ts b/src/enums.ts index a949ee8e..5d776150 100644 --- a/src/enums.ts +++ b/src/enums.ts @@ -106,6 +106,8 @@ export enum Property { GROUP_COUNT = "Group Count", GROUP_KEY = "Group Key", HASH_CONDITION = "Hash Cond", + HASH_BATCHES = "Hash Batches", + HASH_BUCKETS = "Hash Buckets", HEAP_FETCHES = "Heap Fetches", INDEX_NAME = "Index Name", IO_READ_TIME = "I/O Read Time", @@ -123,10 +125,13 @@ export enum Property { NODE_ID = "nodeId", NODE_TYPE = "Node Type", OPERATION = "Operation", + ORIGINAL_HASH_BATCHES = "Original Hash Batches", + ORIGINAL_HASH_BUCKETS = "Original Hash Buckets", OUTPUT = "Output", PARALLEL_AWARE = "Parallel Aware", PARENT_RELATIONSHIP = "Parent Relationship", PARTIAL_MODE = "Partial Mode", + PEAK_MEMORY_USAGE = "Peak Memory Usage", PEAK_SORT_SPACE_USED = "Peak Sort Space Used", PEV_PLAN_TAG = "plan_", PLANNER_ESTIMATE_DIRECTION = "*Planner Row Estimate Direction", diff --git a/src/filters.ts b/src/filters.ts index 32a57365..06b93188 100644 --- a/src/filters.ts +++ b/src/filters.ts @@ -67,6 +67,13 @@ export function formatCost(value: unknown): string { return (value as number).toLocaleString(undefined, { minimumFractionDigits: 2 }) } +export function formatNumber(value: unknown): string { + if (value === undefined) { + return "N/A" + } + return (value as number).toLocaleString() +} + export function formatRows(value: unknown): string { if (value === undefined) { return "N/A" @@ -245,6 +252,8 @@ const nodePropFormatters: Partial> = { [Property.EXCLUSIVE_TEMP_WRITTEN_BLOCKS]: formatBlocksHtml, [Property.FULL_SORT_GROUPS]: formatSortGroups, [Property.HEAP_FETCHES]: formatRows, + [Property.HASH_BATCHES]: formatNumber, + [Property.HASH_BUCKETS]: formatNumber, [Property.IO_READ_TIME]: formatDuration, [Property.IO_WRITE_TIME]: formatDuration, [Property.JIT]: formatJit, @@ -254,8 +263,11 @@ const nodePropFormatters: Partial> = { [Property.LOCAL_IO_WRITE_TIME]: formatDuration, [Property.LOCAL_READ_BLOCKS]: formatBlocksHtml, [Property.LOCAL_WRITTEN_BLOCKS]: formatBlocksHtml, + [Property.ORIGINAL_HASH_BATCHES]: formatNumber, + [Property.ORIGINAL_HASH_BUCKETS]: formatNumber, [Property.OUTPUT]: formatList, [Property.PARALLEL_AWARE]: formatBoolean, + [Property.PEAK_MEMORY_USAGE]: formatKilobytes, [Property.PLANNER_ESTIMATE_DIRECTION]: formatEstimateDirection, [Property.PLANNER_ESTIMATE_FACTOR]: formatFactor, [Property.PLAN_ROWS]: formatRows, diff --git a/src/services/__tests__/12-line-wrapped-plans/01-expect b/src/services/__tests__/12-line-wrapped-plans/01-expect index eb835753..0c098f5b 100644 --- a/src/services/__tests__/12-line-wrapped-plans/01-expect +++ b/src/services/__tests__/12-line-wrapped-plans/01-expect @@ -54,7 +54,7 @@ "Actual Total Time": 0.062, "Actual Rows": 2, "Actual Loops": 1, - "Buckets": 1024, + "Hash Buckets": 1024, "Plans": [ { "Node Type": "Seq Scan", diff --git a/src/services/__tests__/12-line-wrapped-plans/02-expect b/src/services/__tests__/12-line-wrapped-plans/02-expect index eb835753..0c098f5b 100644 --- a/src/services/__tests__/12-line-wrapped-plans/02-expect +++ b/src/services/__tests__/12-line-wrapped-plans/02-expect @@ -54,7 +54,7 @@ "Actual Total Time": 0.062, "Actual Rows": 2, "Actual Loops": 1, - "Buckets": 1024, + "Hash Buckets": 1024, "Plans": [ { "Node Type": "Seq Scan", diff --git a/src/services/__tests__/12-line-wrapped-plans/05-expect b/src/services/__tests__/12-line-wrapped-plans/05-expect index 1fa85d19..d401938a 100644 --- a/src/services/__tests__/12-line-wrapped-plans/05-expect +++ b/src/services/__tests__/12-line-wrapped-plans/05-expect @@ -111,7 +111,7 @@ "Actual Total Time": 4.997, "Actual Rows": 10, "Actual Loops": 3, - "Buckets": 1024, + "Hash Buckets": 1024, "Shared Hit Blocks": 26, "Shared Read Blocks": 9, "Shared Written Blocks": 0, diff --git a/src/services/__tests__/12-line-wrapped-plans/06-expect b/src/services/__tests__/12-line-wrapped-plans/06-expect index 8550e2a3..0ba111de 100644 --- a/src/services/__tests__/12-line-wrapped-plans/06-expect +++ b/src/services/__tests__/12-line-wrapped-plans/06-expect @@ -127,7 +127,7 @@ "Actual Total Time": 0.555, "Actual Rows": 10, "Actual Loops": 3, - "Buckets": 1024, + "Hash Buckets": 1024, "Shared Hit Blocks": 33, "Shared Read Blocks": 0, "Shared Written Blocks": 0, @@ -164,7 +164,7 @@ "Actual Total Time": 1.289, "Actual Rows": 10, "Actual Loops": 3, - "Buckets": 1024, + "Hash Buckets": 1024, "Shared Hit Blocks": 14, "Shared Read Blocks": 0, "Shared Written Blocks": 0, diff --git a/src/services/__tests__/12-line-wrapped-plans/07-expect b/src/services/__tests__/12-line-wrapped-plans/07-expect index 6079f87e..ec6d252b 100644 --- a/src/services/__tests__/12-line-wrapped-plans/07-expect +++ b/src/services/__tests__/12-line-wrapped-plans/07-expect @@ -127,7 +127,7 @@ "Actual Total Time": 0.4, "Actual Rows": 10, "Actual Loops": 3, - "Buckets": 1024, + "Hash Buckets": 1024, "Shared Hit Blocks": 33, "Shared Read Blocks": 0, "Shared Written Blocks": 0, @@ -164,7 +164,7 @@ "Actual Total Time": 0.933, "Actual Rows": 10, "Actual Loops": 3, - "Buckets": 1024, + "Hash Buckets": 1024, "Shared Hit Blocks": 14, "Shared Read Blocks": 0, "Shared Written Blocks": 0, diff --git a/src/services/__tests__/from-text/45-buckets-expect b/src/services/__tests__/from-text/45-buckets-expect new file mode 100644 index 00000000..d32c3abc --- /dev/null +++ b/src/services/__tests__/from-text/45-buckets-expect @@ -0,0 +1,23 @@ +{ + "Plan": { + "Node Type": "Hash Join", + "Plans": [ + { + "Node Type": "Seq Scan" + }, { + "Node Type": "Hash", + "Shared Hit Blocks": 14909, + "Shared Read Blocks": 98728, + "Temp Written Blocks": 88109, + "Hash Buckets": 1048576, + "Hash Batches": 16, + "Peak Memory Usage": 62533, + "Plans": [ + { + "Node Type": "Seq Scan" + } + ] + } + ] + } +} diff --git a/src/services/__tests__/from-text/45-buckets-plan b/src/services/__tests__/from-text/45-buckets-plan new file mode 100644 index 00000000..10bc7214 --- /dev/null +++ b/src/services/__tests__/from-text/45-buckets-plan @@ -0,0 +1,23 @@ + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------- + Hash Join (cost=471037.90..1304640.12 rows=14999862 width=114) (actual time=1480.644..9895.695 rows=15001138 loops=1) + Hash Cond: (details.eid = entetes.eid) + Buffers: shared hit=15940 read=282899, temp read=246065 written=246065 + I/O Timings: shared read=242.957, temp read=317.342 write=830.025 + -> Seq Scan on details (cost=0.00..335215.62 rows=15001362 width=65) (actual time=0.014..1114.539 rows=15001289 loops=1) + Filter: (b IS NOT NULL) + Buffers: shared hit=1031 read=184171 + I/O Timings: shared read=157.615 + -> Hash (cost=238637.70..238637.70 rows=9999056 width=57) (actual time=1474.384..1474.385 rows=9999900 loops=1) + Buckets: 1048576 Batches: 16 Memory Usage: 62533kB + Buffers: shared hit=14909 read=98728, temp written=88109 + I/O Timings: shared read=85.342, temp write=294.483 + -> Seq Scan on entetes (cost=0.00..238637.70 rows=9999056 width=57) (actual time=0.016..567.370 rows=9999900 loops=1) + Filter: (a > 100) + Rows Removed by Filter: 101 + Buffers: shared hit=14909 read=98728 + I/O Timings: shared read=85.342 + Planning: + Buffers: shared hit=8 + Planning Time: 0.119 ms + Execution Time: 10244.597 ms diff --git a/src/services/__tests__/from-text/46-buckets-expect b/src/services/__tests__/from-text/46-buckets-expect new file mode 100644 index 00000000..c1128094 --- /dev/null +++ b/src/services/__tests__/from-text/46-buckets-expect @@ -0,0 +1,24 @@ +{ + "Plan": { + "Node Type": "Aggregate", + "Plans": [ + { + "Node Type": "Hash Join", + "Plans": [ + { + "Node Type": "Seq Scan" + }, + { + "Node Type": "Hash", + "Actual Rows": 1001000, + "Hash Buckets": 262144, + "Original Hash Buckets": 262144, + "Hash Batches": 2048, + "Original Hash Batches": 8, + "Peak Memory Usage": 35157 + } + ] + } + ] + } +} diff --git a/src/services/__tests__/from-text/46-buckets-plan b/src/services/__tests__/from-text/46-buckets-plan new file mode 100644 index 00000000..9a6b0ed4 --- /dev/null +++ b/src/services/__tests__/from-text/46-buckets-plan @@ -0,0 +1,56 @@ +-- source: https://www.postgresql.org/message-id/flat/CA%2BhUKGKWWmf%3DWELLG%3DaUGbcugRaSQbtm0tKYiBut-B2rVKX63g%40mail.gmail.com + +SET max_parallel_workers_per_gather = 0; +SET synchronize_seqscans = off; +SET work_mem = '4MB'; + + + +CREATE TABLE r AS SELECT generate_series(1, 10000000)::int i; +ANALYZE r; + + + +-- 1k uniform keys + 1m duplicates +CREATE TABLE s1k (i int); +INSERT INTO s1k SELECT generate_series(1, 1000)::int i; +ALTER TABLE s1k SET (autovacuum_enabled = off); +ANALYZE s1k; +INSERT INTO s1k SELECT 42 FROM generate_series(1, 1000000); + + + +EXPLAIN ANALYZE SELECT COUNT(*) FROM r JOIN s1k USING (i); +SET +SET +SET +SELECT 10000000 +ANALYZE +CREATE TABLE +INSERT 0 1000 +ALTER TABLE +ANALYZE +INSERT 0 1000000 + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------- + Aggregate (cost=302291.30..302291.31 rows=1 width=8) (actual time=3656.686..3656.688 rows=1.00 loops=1) + Buffers: shared hit=6734 read=41984 dirtied=13621, temp read=49855 written=49855 + -> Hash Join (cost=27826.00..300076.30 rows=886000 width=0) (actual time=146.635..3619.685 rows=1001000.00 loops=1) + Hash Cond: (r.i = s1k.i) + Buffers: shared hit=6734 read=41984 dirtied=13621, temp read=49855 written=49855 + -> Seq Scan on r (cost=0.00..144294.76 rows=10000676 width=4) (actual time=0.201..1200.806 rows=10000000.00 loops=1) + Buffers: shared hit=2304 read=41984 dirtied=13621 + -> Hash (cost=13290.00..13290.00 rows=886000 width=4) (actual time=145.449..145.450 rows=1001000.00 loops=1) + Buckets: 262144 (originally 262144) Batches: 2048 (originally 8) Memory Usage: 35157kB + Buffers: shared hit=4430, temp written=2930 + -> Seq Scan on s1k (cost=0.00..13290.00 rows=886000 width=4) (actual time=0.011..61.783 rows=1001000.00 loops=1) + Buffers: shared hit=4430 + Planning: + Buffers: shared hit=83 read=9 + Planning Time: 0.495 ms + JIT: + Functions: 10 + Options: Inlining false, Optimization false, Expressions true, Deforming true + Timing: Generation 0.492 ms (Deform 0.134 ms), Inlining 0.000 ms, Optimization 0.266 ms, Emission 3.684 ms, Total 4.442 ms + Execution Time: 3674.128 ms +(20 rows) diff --git a/src/services/plan-service.ts b/src/services/plan-service.ts index df9560e1..10f448ef 100644 --- a/src/services/plan-service.ts +++ b/src/services/plan-service.ts @@ -994,6 +994,10 @@ export class PlanService { return } + if (this.parseBuckets(extraMatches[2], element as Node)) { + return + } + if (this.parseWAL(extraMatches[2], element as Node)) { return } @@ -1120,6 +1124,30 @@ export class PlanService { el[_.map([type, method, "blocks"], _.capitalize).join(" ")] = value } + private parseBuckets(text: string, el: Node): boolean { + + enum BucketsMatch { + Buckets = 1, + OriginalBuckets, + Batches, + OriginalBatches, + MemoryUsage, + } + + const bucketsRegex = /Buckets:\s+([0-9]*)(?:\s*\(originally\s+([0-9]*)\))*\s+Batches:\s+([0-9]*)(?:\s*\(originally\s+([0-9]*)\))*\s+Memory Usage:\s+([0-9]*)kB\s*$/g + const bucketsMatches = bucketsRegex.exec(text) + + if (bucketsMatches) { + el[Property.HASH_BUCKETS] = parseInt(bucketsMatches[BucketsMatch.Buckets], 0) + el[Property.HASH_BATCHES] = parseInt(bucketsMatches[BucketsMatch.Batches], 0) + el[Property.ORIGINAL_HASH_BUCKETS] = parseInt(bucketsMatches[BucketsMatch.OriginalBuckets], 0) + el[Property.ORIGINAL_HASH_BATCHES] = parseInt(bucketsMatches[BucketsMatch.OriginalBatches], 0) + el[Property.PEAK_MEMORY_USAGE] = parseInt(bucketsMatches[BucketsMatch.MemoryUsage], 0) + return true + } + return false + } + private getWorker(node: Node, workerNumber: number): Worker | undefined { return _.find(node[Property.WORKERS], (worker) => { return worker[Property.WORKER_NUMBER] === workerNumber From df3615122cf6d7aabbc83682fcc57085b27b84d2 Mon Sep 17 00:00:00 2001 From: Pierre GIRAUD Date: Wed, 1 Jul 2026 15:42:43 +0200 Subject: [PATCH 2/2] feat(parser): parse hash aggregate planned partitions --- src/enums.ts | 5 ++- src/filters.ts | 3 ++ .../__tests__/26-planned-partitions.spec.ts | 42 +++++++++++++++++++ .../from-text/47-planned-partitions-expect | 15 +++++++ .../from-text/47-planned-partitions-plan | 11 +++++ src/services/plan-service.ts | 35 ++++++++++++++++ 6 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 src/services/__tests__/26-planned-partitions.spec.ts create mode 100644 src/services/__tests__/from-text/47-planned-partitions-expect create mode 100644 src/services/__tests__/from-text/47-planned-partitions-plan diff --git a/src/enums.ts b/src/enums.ts index 5d776150..4f9b5937 100644 --- a/src/enums.ts +++ b/src/enums.ts @@ -66,6 +66,7 @@ export enum Property { CTE_NAME = "CTE Name", CTE_SCAN = "CTE Scan", DISABLED = "Disabled", + DISK_USAGE = "Disk Usage", EXCLUSIVE_AVERAGE_IO_READ_SPEED = "*I/O Read Speed (exclusive)", EXCLUSIVE_AVERAGE_IO_WRITE_SPEED = "*I/O Write Speed (exclusive)", EXCLUSIVE_AVERAGE_LOCAL_IO_READ_SPEED = "*Local I/O Read Speed (exclusive)", @@ -105,9 +106,10 @@ export enum Property { FUNCTION_NAME = "Function Name", GROUP_COUNT = "Group Count", GROUP_KEY = "Group Key", - HASH_CONDITION = "Hash Cond", + HASHAGG_BATCHES = "HashAgg Batches", HASH_BATCHES = "Hash Batches", HASH_BUCKETS = "Hash Buckets", + HASH_CONDITION = "Hash Cond", HEAP_FETCHES = "Heap Fetches", INDEX_NAME = "Index Name", IO_READ_TIME = "I/O Read Time", @@ -134,6 +136,7 @@ export enum Property { PEAK_MEMORY_USAGE = "Peak Memory Usage", PEAK_SORT_SPACE_USED = "Peak Sort Space Used", PEV_PLAN_TAG = "plan_", + PLANNED_PARTITIONS = "Planned Partitions", PLANNER_ESTIMATE_DIRECTION = "*Planner Row Estimate Direction", PLANNER_ESTIMATE_FACTOR = "*Planner Row Estimate Factor", PLANS = "Plans", diff --git a/src/filters.ts b/src/filters.ts index 06b93188..040e91ed 100644 --- a/src/filters.ts +++ b/src/filters.ts @@ -222,6 +222,7 @@ const nodePropFormatters: Partial> = { [Property.AVERAGE_SUM_IO_WRITE_SPEED]: formatTransferRate, [Property.AVERAGE_TEMP_IO_READ_SPEED]: formatTransferRate, [Property.AVERAGE_TEMP_IO_WRITE_SPEED]: formatTransferRate, + [Property.DISK_USAGE]: formatKilobytes, [Property.EXCLUSIVE_AVERAGE_IO_READ_SPEED]: formatTransferRate, [Property.EXCLUSIVE_AVERAGE_IO_WRITE_SPEED]: formatTransferRate, [Property.EXCLUSIVE_AVERAGE_LOCAL_IO_READ_SPEED]: formatTransferRate, @@ -252,6 +253,7 @@ const nodePropFormatters: Partial> = { [Property.EXCLUSIVE_TEMP_WRITTEN_BLOCKS]: formatBlocksHtml, [Property.FULL_SORT_GROUPS]: formatSortGroups, [Property.HEAP_FETCHES]: formatRows, + [Property.HASHAGG_BATCHES]: formatNumber, [Property.HASH_BATCHES]: formatNumber, [Property.HASH_BUCKETS]: formatNumber, [Property.IO_READ_TIME]: formatDuration, @@ -268,6 +270,7 @@ const nodePropFormatters: Partial> = { [Property.OUTPUT]: formatList, [Property.PARALLEL_AWARE]: formatBoolean, [Property.PEAK_MEMORY_USAGE]: formatKilobytes, + [Property.PLANNED_PARTITIONS]: formatNumber, [Property.PLANNER_ESTIMATE_DIRECTION]: formatEstimateDirection, [Property.PLANNER_ESTIMATE_FACTOR]: formatFactor, [Property.PLAN_ROWS]: formatRows, diff --git a/src/services/__tests__/26-planned-partitions.spec.ts b/src/services/__tests__/26-planned-partitions.spec.ts new file mode 100644 index 00000000..44e8f16e --- /dev/null +++ b/src/services/__tests__/26-planned-partitions.spec.ts @@ -0,0 +1,42 @@ +import { describe, expect, test } from "vitest" +import { PlanService } from "@/services/plan-service" +import type { IPlanContent } from "@/interfaces" + +describe("PlanService", () => { + test("Planned partitions line is correctly parsed", () => { + const planService = new PlanService() + const source = ` + HashAggregate (cost=13760592.78..14872410.30 rows=1736877 width=61) + Group Key: ml.offer_type, ml.property_main_type, COALESCE(ml.district_id, '00000000-0000-0000-0000-000000000000'::uuid), COALESCE(ml.sale_price, ml.gross_rent_monthly, ml.rent_net_monthly, '0'::real) + Filter: (array_length(array_agg(ml.id), 1) > 1) + Planned Partitions: 128 + -> Seq Scan on post_merged_listings ml (cost=0.00..11025300.36 rows=32660208 width=33) + Filter: (duplicate_id IS NULL) + ` + const plan = planService.fromText(source) as IPlanContent + expect(plan.Plan["Planned Partitions"]).toEqual(128) + expect(plan.Plan["HashAgg Batches"]).toBeUndefined() + expect(plan.Plan["Peak Memory Usage"]).toBeUndefined() + expect(plan.Plan["Disk Usage"]).toBeUndefined() + }) + + test("Planned partitions line is correctly parsed", () => { + const planService = new PlanService() + const source = ` + HashAggregate (cost=79468.50..98636.70 rows=940257 width=11) (actual time=231.946..413.626 rows=970675 loops=1) + Group Key: name + Planned Partitions: 32 Batches: 193 Memory Usage: 4153kB Disk Usage: 31752kB + -> Seq Scan on t1 (cost=0.00..15406.00 rows=1000000 width=11) (actual time=0.118..36.474 rows=1000000 loops=1) + Planning Time: 0.201 ms + Execution Time: 438.186 ms +(6 rows) + +Time: 438.861 ms + ` + const plan = planService.fromText(source) as IPlanContent + expect(plan.Plan["Planned Partitions"]).toEqual(32) + expect(plan.Plan["HashAgg Batches"]).toEqual(193) + expect(plan.Plan["Peak Memory Usage"]).toEqual(4153) + expect(plan.Plan["Disk Usage"]).toEqual(31752) + }) +}) diff --git a/src/services/__tests__/from-text/47-planned-partitions-expect b/src/services/__tests__/from-text/47-planned-partitions-expect new file mode 100644 index 00000000..e3706e7b --- /dev/null +++ b/src/services/__tests__/from-text/47-planned-partitions-expect @@ -0,0 +1,15 @@ +{ + "Plan": { + "Node Type": "HashAggregate", + "Planned Partitions": 32, + "HashAgg Batches": 193, + "Peak Memory Usage": 4153, + "Disk Usage": 31752, + "Plans": [ + { + "Node Type": "Seq Scan" + } + ] + } +} + diff --git a/src/services/__tests__/from-text/47-planned-partitions-plan b/src/services/__tests__/from-text/47-planned-partitions-plan new file mode 100644 index 00000000..0722b906 --- /dev/null +++ b/src/services/__tests__/from-text/47-planned-partitions-plan @@ -0,0 +1,11 @@ +-- taken from https://explain.dalibo.com/plan/bcc3fd5fc7bc771b + + HashAggregate (cost=79468.50..98636.70 rows=940257 width=11) (actual time=231.946..413.626 rows=970675 loops=1) + Group Key: name + Planned Partitions: 32 Batches: 193 Memory Usage: 4153kB Disk Usage: 31752kB + -> Seq Scan on t1 (cost=0.00..15406.00 rows=1000000 width=11) (actual time=0.118..36.474 rows=1000000 loops=1) + Planning Time: 0.201 ms + Execution Time: 438.186 ms +(6 rows) + +Time: 438.861 ms diff --git a/src/services/plan-service.ts b/src/services/plan-service.ts index 10f448ef..9394c041 100644 --- a/src/services/plan-service.ts +++ b/src/services/plan-service.ts @@ -998,6 +998,10 @@ export class PlanService { return } + if (this.parsePlannedPartitions(extraMatches[2], element as Node)) { + return + } + if (this.parseWAL(extraMatches[2], element as Node)) { return } @@ -1148,6 +1152,37 @@ export class PlanService { return false } + private parsePlannedPartitions(text: string, el: Node): boolean { + + enum PlannedPartitionsMatch { + Partitions = 1, + Batches, + MemoryUsage, + DiskUsage, + } + + const plannedPartitionsRegex = /Planned Partitions:\s+([0-9]*)(?:\s+Batches:\s+([0-9]*))*(?:\s+Memory Usage:\s+([0-9]*)kB)*(?:\s*Disk Usage:\s+([0-9]*)kB)*\s*$/g + const plannedPartitionsMatches = plannedPartitionsRegex.exec(text) + + if (plannedPartitionsMatches) { + el[Property.PLANNED_PARTITIONS] = parseInt(plannedPartitionsMatches[PlannedPartitionsMatch.Partitions], 0) + let batches = plannedPartitionsMatches[PlannedPartitionsMatch.Batches] + if (batches) {; + el[Property.HASHAGG_BATCHES] = parseInt(batches, 0) + } + let memoryUsage = plannedPartitionsMatches[PlannedPartitionsMatch.MemoryUsage] + if (memoryUsage) { + el[Property.PEAK_MEMORY_USAGE] = parseInt(memoryUsage) + } + let diskUsage = plannedPartitionsMatches[PlannedPartitionsMatch.DiskUsage] + if (diskUsage) { + el[Property.DISK_USAGE] = parseInt(diskUsage) + } + return true + } + return false + } + private getWorker(node: Node, workerNumber: number): Worker | undefined { return _.find(node[Property.WORKERS], (worker) => { return worker[Property.WORKER_NUMBER] === workerNumber