This is an automated email from the ASF dual-hosted git repository.

vogievetsky pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/master by this push:
     new 6a2c29d8091 Web console: load Dart reports (#18897)
6a2c29d8091 is described below

commit 6a2c29d80917262ed95fa414d0e4af1727467d9d
Author: Vadim Ogievetsky <[email protected]>
AuthorDate: Fri Jan 9 18:19:57 2026 +0000

    Web console: load Dart reports (#18897)
    
    * Add retention to build
    
    * Add inflight state checking
    
    * set if not set in effective context
    
    * add getEffectiveEngine checks
    
    * getEffectiveContext
    
    * update dart panel
    
    * Show Dart details
    
    * update API
    
    * better error message for ID reuse
    
    * fix race condition
    
    * reformat
---
 web-console/script/druid                           |   2 +
 .../query-context-completions.ts                   |   1 +
 .../execution/execution-ingest-complete.mock.ts    |   1 +
 .../execution/execution-ingest-error.mock.ts       |   1 +
 .../src/druid-models/execution/execution.ts        |  18 +-
 web-console/src/druid-models/task/task.ts          |   1 +
 .../workbench-query/workbench-query.spec.ts        | 818 +++++++++++++++++++++
 .../workbench-query/workbench-query.ts             |  75 +-
 .../src/utils/query-manager/query-manager.ts       |  63 +-
 .../views/datasources-view/datasources-view.tsx    |   2 +-
 .../src/views/segments-view/segments-view.tsx      |   2 +-
 .../views/supervisors-view/supervisors-view.tsx    |   2 +-
 web-console/src/views/tasks-view/tasks-view.tsx    |   1 +
 .../current-dart-panel/current-dart-panel.tsx      |  25 +-
 .../dart-details-dialog/dart-details-dialog.scss   |  35 -
 .../dart-details-dialog/dart-details-dialog.tsx    |  48 --
 .../execution-details-dialog.tsx                   |   4 +-
 .../execution-details-pane-loader.tsx              |  23 +-
 .../execution-details-pane.spec.tsx.snap           |   2 +-
 .../execution-details-pane.tsx                     |   5 +-
 .../execution-progress-bar-pane.tsx                |   2 +-
 .../views/workbench-view/query-tab/query-tab.tsx   |  11 +-
 .../src/views/workbench-view/workbench-view.tsx    |  33 +-
 23 files changed, 1044 insertions(+), 131 deletions(-)

diff --git a/web-console/script/druid b/web-console/script/druid
index 2edb40439be..c1f4f4bbcab 100755
--- a/web-console/script/druid
+++ b/web-console/script/druid
@@ -70,6 +70,8 @@ function _build_distribution() {
     && echo -e "\n\ndruid.server.http.allowedHttpMethods=[\"HEAD\"]" >> 
conf/druid/auto/_common/common.runtime.properties \
     && echo -e "\n\ndruid.export.storage.baseDir=/" >> 
conf/druid/auto/_common/common.runtime.properties \
     && echo -e "\n\ndruid.msq.dart.enabled=true" >> 
conf/druid/auto/_common/common.runtime.properties \
+    && echo -e "\n\ndruid.msq.dart.controller.maxRetainedReportCount=100" >> 
conf/druid/auto/_common/common.runtime.properties \
+    && echo -e 
"\n\ndruid.msq.dart.controller.maxRetainedReportDuration=PT3600S" >> 
conf/druid/auto/_common/common.runtime.properties \
   )
 }
 
diff --git 
a/web-console/src/dialogs/edit-context-dialog/query-context-completions.ts 
b/web-console/src/dialogs/edit-context-dialog/query-context-completions.ts
index e74513c7e28..9e3cb8dca8d 100644
--- a/web-console/src/dialogs/edit-context-dialog/query-context-completions.ts
+++ b/web-console/src/dialogs/edit-context-dialog/query-context-completions.ts
@@ -110,6 +110,7 @@ export const QUERY_CONTEXT_COMPLETIONS: 
JsonCompletionRule[] = [
       { value: 'forceSegmentSortByTime', documentation: 'Force segments to be 
sorted by time' },
       { value: 'includeAllCounters', documentation: 'Include all counters in 
task reports' },
       // SQL specific
+      { value: 'sqlQueryId', documentation: 'Query ID for SQL queries' },
       { value: 'sqlTimeZone', documentation: 'Time zone for SQL queries' },
       { value: 'useApproximateCountDistinct', documentation: 'Use approximate 
COUNT DISTINCT' },
       { value: 'useApproximateTopN', documentation: 'Use approximate TOP N 
queries' },
diff --git 
a/web-console/src/druid-models/execution/execution-ingest-complete.mock.ts 
b/web-console/src/druid-models/execution/execution-ingest-complete.mock.ts
index 24ba4687ac3..cf52f2d3c4d 100644
--- a/web-console/src/druid-models/execution/execution-ingest-complete.mock.ts
+++ b/web-console/src/druid-models/execution/execution-ingest-complete.mock.ts
@@ -55,6 +55,7 @@ export const EXECUTION_INGEST_COMPLETE = 
Execution.fromTaskReport({
               workerId: 'query-346b9ac6-4912-46e4-9b98-75f11071af87-worker0_0',
               state: 'SUCCESS',
               durationMs: 8789,
+              pendingMs: 123,
             },
           ],
         },
diff --git 
a/web-console/src/druid-models/execution/execution-ingest-error.mock.ts 
b/web-console/src/druid-models/execution/execution-ingest-error.mock.ts
index 0c2c5a93ed0..b4800211341 100644
--- a/web-console/src/druid-models/execution/execution-ingest-error.mock.ts
+++ b/web-console/src/druid-models/execution/execution-ingest-error.mock.ts
@@ -92,6 +92,7 @@ export const EXECUTION_INGEST_ERROR = 
Execution.fromTaskReport({
               workerId: 'query-26d490c6-c06d-4cd2-938f-bc5f7f982754-worker0_0',
               state: 'FAILED',
               durationMs: -1,
+              pendingMs: -1,
             },
           ],
         },
diff --git a/web-console/src/druid-models/execution/execution.ts 
b/web-console/src/druid-models/execution/execution.ts
index 1237b49e62d..0c327b1d064 100644
--- a/web-console/src/druid-models/execution/execution.ts
+++ b/web-console/src/druid-models/execution/execution.ts
@@ -366,8 +366,18 @@ export class Execution {
 
   static getProgressDescription(execution: Execution | undefined): string {
     if (!execution?.stages) return 'Loading...';
-    if (!execution.isWaitingForQuery())
-      return 'Query complete, waiting for segments to be loaded...';
+    if (!execution.isWaitingForQuery()) {
+      switch (execution.engine) {
+        case 'sql-msq-task':
+          return 'Query complete, waiting for segments to be loaded...';
+
+        case 'sql-msq-dart':
+          return 'Got a non-running report. Did you reuse a sqlQueryID?';
+
+        default:
+          return 'Query not running.';
+      }
+    }
 
     let ret = execution.stages.getStage(0)?.phase ? 'Running query...' : 
'Starting query...';
     if (execution.usageInfo) {
@@ -556,6 +566,10 @@ export class Execution {
     return status !== 'SUCCESS' && status !== 'FAILED';
   }
 
+  public isWaitingForSegments(): boolean {
+    return Boolean(this.stages && !this.isWaitingForQuery() && this.engine === 
'sql-msq-task');
+  }
+
   public getSegmentStatusDescription() {
     const { segmentStatus } = this;
 
diff --git a/web-console/src/druid-models/task/task.ts 
b/web-console/src/druid-models/task/task.ts
index e1743c11689..0d6cd07266f 100644
--- a/web-console/src/druid-models/task/task.ts
+++ b/web-console/src/druid-models/task/task.ts
@@ -81,6 +81,7 @@ export interface WorkerState {
   workerId: string;
   state: string;
   durationMs: number;
+  pendingMs: number;
 }
 
 export interface SegmentLoadWaiterStatus {
diff --git 
a/web-console/src/druid-models/workbench-query/workbench-query.spec.ts 
b/web-console/src/druid-models/workbench-query/workbench-query.spec.ts
index 82bbb28f69a..13908bbf2cf 100644
--- a/web-console/src/druid-models/workbench-query/workbench-query.spec.ts
+++ b/web-console/src/druid-models/workbench-query/workbench-query.spec.ts
@@ -843,4 +843,822 @@ describe('WorkbenchQuery', () => {
       });
     });
   });
+
+  describe('#getEffectiveEngine', () => {
+    beforeEach(() => {
+      // Reset to default engines before each test
+      WorkbenchQuery.setQueryEngines(['native', 'sql-native', 'sql-msq-task']);
+    });
+
+    describe('when engine is explicitly set', () => {
+      it('returns the explicitly set engine', () => {
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString('SELECT * FROM wikipedia')
+          .changeEngine('sql-msq-task');
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-msq-task');
+      });
+
+      it('returns explicit engine even if query suggests different engine', () 
=> {
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString('INSERT INTO wiki SELECT * FROM wikipedia')
+          .changeEngine('sql-native');
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+
+      it('returns explicit engine for JSON queries', () => {
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString('{"queryType": "topN", "dataSource": "test"}')
+          .changeEngine('sql-native');
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+
+      it('returns explicit engine even when context has engine set', () => {
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString('SELECT * FROM wikipedia')
+          .changeQueryContext({ engine: 'native' })
+          .changeEngine('sql-msq-task');
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-msq-task');
+      });
+    });
+
+    describe('when context engine is set', () => {
+      it('returns sql-native when context engine is native', () => {
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString('SELECT * FROM wikipedia')
+          .changeQueryContext({ engine: 'native' });
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+
+      it('returns sql-msq-dart when context engine is msq-dart', () => {
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString('SELECT * FROM wikipedia')
+          .changeQueryContext({ engine: 'msq-dart' });
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-msq-dart');
+      });
+
+      it('returns sql-native when context engine is native via SET statement', 
() => {
+        const queryWithSet = sane`
+          SET engine = 'native';
+          SELECT * FROM wikipedia
+        `;
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(queryWithSet);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+
+      it('returns sql-msq-dart when context engine is msq-dart via SET 
statement', () => {
+        const queryWithSet = sane`
+          SET engine = 'msq-dart';
+          SELECT * FROM wikipedia
+        `;
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(queryWithSet);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-msq-dart');
+      });
+
+      it('returns sql-native when context engine is native via JSON context', 
() => {
+        const sqlInJson = sane`
+          {
+            "query": "SELECT * FROM wikipedia",
+            "context": {
+              "engine": "native"
+            }
+          }
+        `;
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(sqlInJson);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+
+      it('returns sql-msq-dart when context engine is msq-dart via JSON 
context', () => {
+        const sqlInJson = sane`
+          {
+            "query": "SELECT * FROM wikipedia",
+            "context": {
+              "engine": "msq-dart"
+            }
+          }
+        `;
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(sqlInJson);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-msq-dart');
+      });
+
+      it('prioritizes SET statement engine over JSON context engine', () => {
+        const sqlInJson = sane`
+          {
+            "query": "SET engine = 'msq-dart'; SELECT * FROM wikipedia",
+            "context": {
+              "engine": "native"
+            }
+          }
+        `;
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(sqlInJson);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-msq-dart');
+      });
+
+      it('falls through to other logic when context engine is not native or 
msq-dart', () => {
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString('SELECT * FROM wikipedia')
+          .changeQueryContext({ engine: 'msq-task' });
+
+        // Should fall through to normal logic and return sql-native
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+
+      it('falls through to other logic when context engine is not set', () => {
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString('SELECT * FROM wikipedia')
+          .changeQueryContext({ maxNumTasks: 3 });
+
+        // Should fall through to normal logic and return sql-native
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+
+      it('handles INSERT query with context engine native', () => {
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString('INSERT INTO wiki SELECT * FROM wikipedia')
+          .changeQueryContext({ engine: 'native' });
+
+        // Context engine takes priority over task engine detection
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+
+      it('handles JSON query with context engine msq-dart', () => {
+        const nativeJson = sane`
+          {
+            "queryType": "topN",
+            "dataSource": "wikipedia",
+            "context": {
+              "engine": "msq-dart"
+            }
+          }
+        `;
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(nativeJson);
+
+        // Context engine takes priority over JSON-like detection
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-msq-dart');
+      });
+    });
+
+    describe('when query is JSON-like', () => {
+      it('returns sql-native for SQL-in-JSON when sql-native is enabled', () 
=> {
+        const sqlInJson = sane`
+          {
+            "query": "SELECT * FROM wikipedia",
+            "context": {}
+          }
+        `;
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(sqlInJson);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+
+      it('returns native for native JSON query when native is enabled', () => {
+        const nativeJson = sane`
+          {
+            "queryType": "topN",
+            "dataSource": "wikipedia",
+            "dimension": "page",
+            "threshold": 10,
+            "intervals": ["2015-09-12/2015-09-13"],
+            "granularity": "all",
+            "aggregations": [
+              {"type": "count", "name": "count"}
+            ]
+          }
+        `;
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(nativeJson);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('native');
+      });
+
+      it('falls through for SQL-in-JSON when sql-native is not enabled', () => 
{
+        WorkbenchQuery.setQueryEngines(['native', 'sql-msq-task']);
+
+        const sqlInJson = sane`
+          {
+            "query": "SELECT * FROM wikipedia",
+            "context": {}
+          }
+        `;
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(sqlInJson);
+
+        // Falls through JSON-like check, task engine check (no 
INSERT/EXTERN), sql-native check (not enabled),
+        // and returns first enabled engine which is 'native'
+        expect(workbenchQuery.getEffectiveEngine()).toBe('native');
+      });
+
+      it('falls through for native JSON when native is not enabled', () => {
+        WorkbenchQuery.setQueryEngines(['sql-native', 'sql-msq-task']);
+
+        const nativeJson = sane`
+          {
+            "queryType": "topN",
+            "dataSource": "wikipedia"
+          }
+        `;
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(nativeJson);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+    });
+
+    describe('when query needs task engine', () => {
+      it('returns sql-msq-task for INSERT query when sql-msq-task is enabled', 
() => {
+        const insertQuery = 'INSERT INTO wiki SELECT * FROM wikipedia';
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(insertQuery);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-msq-task');
+      });
+
+      it('returns sql-msq-task for REPLACE query when sql-msq-task is 
enabled', () => {
+        const replaceQuery = 'REPLACE INTO wiki OVERWRITE ALL SELECT * FROM 
wikipedia';
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(replaceQuery);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-msq-task');
+      });
+
+      it('returns sql-msq-task for EXTERN query when sql-msq-task is enabled', 
() => {
+        const externQuery = sane`
+          SELECT *
+          FROM TABLE(
+            EXTERN(
+              '{"type":"http","uris":["https://example.com/data.json"]}',
+              '{"type":"json"}'
+            )
+          )
+        `;
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(externQuery);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-msq-task');
+      });
+
+      it('falls through when sql-msq-task is not enabled for task engine 
query', () => {
+        WorkbenchQuery.setQueryEngines(['native', 'sql-native']);
+
+        const insertQuery = 'INSERT INTO wiki SELECT * FROM wikipedia';
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(insertQuery);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+    });
+
+    describe('fallback behavior', () => {
+      it('falls back to sql-native for regular SQL query', () => {
+        const regularQuery = 'SELECT * FROM wikipedia';
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(regularQuery);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+
+      it('falls back to sql-native when it is in enabled engines', () => {
+        WorkbenchQuery.setQueryEngines(['native', 'sql-msq-task', 
'sql-native']);
+
+        const regularQuery = "SELECT * FROM wikipedia WHERE channel = 'en'";
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(regularQuery);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+
+      it('falls back to first enabled engine when sql-native is not 
available', () => {
+        WorkbenchQuery.setQueryEngines(['native', 'sql-msq-task']);
+
+        const regularQuery = 'SELECT * FROM wikipedia';
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(regularQuery);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('native');
+      });
+
+      it('falls back to sql-native when no engines are enabled', () => {
+        WorkbenchQuery.setQueryEngines([]);
+
+        const regularQuery = 'SELECT * FROM wikipedia';
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(regularQuery);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+    });
+
+    describe('complex scenarios', () => {
+      it('prioritizes explicit engine over task engine detection', () => {
+        const insertQuery = 'INSERT INTO wiki SELECT * FROM wikipedia';
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(insertQuery)
+          .changeEngine('sql-native');
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+
+      it('handles SQL query with different enabled engines order', () => {
+        WorkbenchQuery.setQueryEngines(['sql-msq-task', 'native', 
'sql-native']);
+
+        const regularQuery = 'SELECT COUNT(*) FROM wikipedia';
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(regularQuery);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+
+      it('returns sql-msq-task for task query even when sql-native is 
enabled', () => {
+        WorkbenchQuery.setQueryEngines(['sql-native', 'sql-msq-task', 
'native']);
+
+        const insertQuery = sane`
+          INSERT INTO wiki
+          SELECT * FROM wikipedia
+          PARTITIONED BY DAY
+        `;
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(insertQuery);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-msq-task');
+      });
+
+      it('handles empty query string', () => {
+        const workbenchQuery = WorkbenchQuery.blank();
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+
+      it('handles query with only whitespace', () => {
+        const workbenchQuery = WorkbenchQuery.blank().changeQueryString('   
\n\t  ');
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-native');
+      });
+
+      it('handles malformed JSON query', () => {
+        const malformedJson = '{ "queryType": "topN"';
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(malformedJson);
+
+        // Malformed JSON will be treated as JSON-like (starts with {) but 
will fail isSqlInJson check,
+        // falling into the native JSON branch which returns 'native' since 
it's enabled
+        expect(workbenchQuery.getEffectiveEngine()).toBe('native');
+      });
+
+      it('correctly identifies case-insensitive INSERT keyword', () => {
+        const insertQuery = 'insert into wiki select * from wikipedia';
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(insertQuery);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-msq-task');
+      });
+
+      it('correctly identifies case-insensitive EXTERN keyword', () => {
+        const externQuery = sane`
+          SELECT * FROM TABLE(
+            extern(
+              '{"type":"http","uris":["https://example.com/data.json"]}',
+              '{"type":"json"}'
+            )
+          )
+        `;
+
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString(externQuery);
+
+        expect(workbenchQuery.getEffectiveEngine()).toBe('sql-msq-task');
+      });
+    });
+  });
+
+  describe('#getEffectiveContext', () => {
+    describe('for regular SQL queries', () => {
+      it('returns queryContext when no SET statements exist', () => {
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString('SELECT * FROM wikipedia')
+          .changeQueryContext({ maxNumTasks: 3, useCache: false });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext).toEqual({
+          maxNumTasks: 3,
+          useCache: false,
+        });
+      });
+
+      it('merges queryContext with SET statement context', () => {
+        const queryWithSets = sane`
+          SET maxNumTasks = 5;
+          SET timeout = 30000;
+          SELECT * FROM wikipedia
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(queryWithSets)
+          .changeQueryContext({ useCache: false, finalizeAggregations: true });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext).toEqual({
+          useCache: false,
+          finalizeAggregations: true,
+          maxNumTasks: 5,
+          timeout: 30000,
+        });
+      });
+
+      it('prioritizes SET statement context over queryContext', () => {
+        const queryWithSets = sane`
+          SET maxNumTasks = 10;
+          SELECT * FROM wikipedia
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(queryWithSets)
+          .changeQueryContext({ maxNumTasks: 3, useCache: false });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext.maxNumTasks).toBe(10);
+        expect(effectiveContext.useCache).toBe(false);
+      });
+
+      it('handles empty query string', () => {
+        const workbenchQuery = WorkbenchQuery.blank().changeQueryContext({
+          maxNumTasks: 3,
+        });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext).toEqual({ maxNumTasks: 3 });
+      });
+
+      it('handles query with only SET statements', () => {
+        const queryWithOnlySets = sane`
+          SET maxNumTasks = 5;
+          SET useCache = TRUE;
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(queryWithOnlySets)
+          .changeQueryContext({ timeout: 60000 });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext).toEqual({
+          timeout: 60000,
+          maxNumTasks: 5,
+          useCache: true,
+        });
+      });
+    });
+
+    describe('for native JSON queries', () => {
+      it('returns queryContext when JSON has no context property', () => {
+        const nativeJson = sane`
+          {
+            "queryType": "topN",
+            "dataSource": "wikipedia"
+          }
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(nativeJson)
+          .changeQueryContext({ maxNumTasks: 3 });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext).toEqual({ maxNumTasks: 3 });
+      });
+
+      it('merges JSON context with queryContext', () => {
+        const nativeJson = sane`
+          {
+            "queryType": "topN",
+            "dataSource": "wikipedia",
+            "context": {
+              "timeout": 30000,
+              "useCache": false
+            }
+          }
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(nativeJson)
+          .changeQueryContext({ maxNumTasks: 3 });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext).toEqual({
+          maxNumTasks: 3,
+          timeout: 30000,
+          useCache: false,
+        });
+      });
+
+      it('prioritizes JSON context over queryContext', () => {
+        const nativeJson = sane`
+          {
+            "queryType": "topN",
+            "dataSource": "wikipedia",
+            "context": {
+              "maxNumTasks": 10
+            }
+          }
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(nativeJson)
+          .changeQueryContext({ maxNumTasks: 3, useCache: false });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext.maxNumTasks).toBe(10);
+        expect(effectiveContext.useCache).toBe(false);
+      });
+    });
+
+    describe('for SQL-in-JSON queries', () => {
+      it('returns queryContext when JSON has no context and SQL has no SET 
statements', () => {
+        const sqlInJson = sane`
+          {
+            "query": "SELECT * FROM wikipedia"
+          }
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(sqlInJson)
+          .changeQueryContext({ maxNumTasks: 3 });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext).toEqual({ maxNumTasks: 3 });
+      });
+
+      it('merges JSON context with queryContext', () => {
+        const sqlInJson = sane`
+          {
+            "query": "SELECT * FROM wikipedia",
+            "context": {
+              "timeout": 30000
+            }
+          }
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(sqlInJson)
+          .changeQueryContext({ maxNumTasks: 3 });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext).toEqual({
+          maxNumTasks: 3,
+          timeout: 30000,
+        });
+      });
+
+      it('merges SQL SET statements context with queryContext', () => {
+        const sqlInJson = sane`
+          {
+            "query": "SET useCache = FALSE; SELECT * FROM wikipedia"
+          }
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(sqlInJson)
+          .changeQueryContext({ maxNumTasks: 3 });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext).toEqual({
+          maxNumTasks: 3,
+          useCache: false,
+        });
+      });
+
+      it('merges all three contexts: queryContext, JSON context, and SET 
statements', () => {
+        const sqlInJson = sane`
+          {
+            "query": "SET timeout = 60000; SET finalizeAggregations = TRUE; 
SELECT * FROM wikipedia",
+            "context": {
+              "maxNumTasks": 5,
+              "useCache": false
+            }
+          }
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(sqlInJson)
+          .changeQueryContext({ maxNumTasks: 3, priority: 10 });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext).toEqual({
+          priority: 10,
+          maxNumTasks: 5,
+          useCache: false,
+          timeout: 60000,
+          finalizeAggregations: true,
+        });
+      });
+
+      it('prioritizes SET statements over JSON context over queryContext', () 
=> {
+        const sqlInJson = sane`
+          {
+            "query": "SET maxNumTasks = 20; SELECT * FROM wikipedia",
+            "context": {
+              "maxNumTasks": 10
+            }
+          }
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(sqlInJson)
+          .changeQueryContext({ maxNumTasks: 3 });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext.maxNumTasks).toBe(20);
+      });
+
+      it('handles SQL-in-JSON with multiple SET statements', () => {
+        const sqlInJson = sane`
+          {
+            "query": "SET maxNumTasks = 8; SET useCache = TRUE; SET timeout = 
45000; SELECT * FROM wikipedia",
+            "context": {
+              "finalizeAggregations": false
+            }
+          }
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(sqlInJson)
+          .changeQueryContext({ priority: 5 });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext).toEqual({
+          priority: 5,
+          finalizeAggregations: false,
+          maxNumTasks: 8,
+          useCache: true,
+          timeout: 45000,
+        });
+      });
+    });
+
+    describe('error handling', () => {
+      it('handles malformed JSON gracefully', () => {
+        const malformedJson = '{ "queryType": "topN"';
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(malformedJson)
+          .changeQueryContext({ maxNumTasks: 3 });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        // Should fall back to queryContext only since JSON parsing fails
+        expect(effectiveContext).toEqual({ maxNumTasks: 3 });
+      });
+
+      it('handles JSON with invalid context property', () => {
+        const jsonWithInvalidContext = sane`
+          {
+            "queryType": "topN",
+            "context": "not an object"
+          }
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(jsonWithInvalidContext)
+          .changeQueryContext({ maxNumTasks: 3 });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        // Should merge the context even if it's not an object
+        expect(effectiveContext).toBeDefined();
+      });
+
+      it('handles SQL-in-JSON with malformed SET statements', () => {
+        const sqlInJson = sane`
+          {
+            "query": "SET maxNumTasks INVALID; SELECT * FROM wikipedia"
+          }
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(sqlInJson)
+          .changeQueryContext({ maxNumTasks: 3 });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        // Should still return queryContext even if SET statement is invalid
+        expect(effectiveContext).toBeDefined();
+        expect(effectiveContext.maxNumTasks).toBe(3);
+      });
+    });
+
+    describe('edge cases', () => {
+      it('handles empty queryContext', () => {
+        const workbenchQuery = 
WorkbenchQuery.blank().changeQueryString('SELECT * FROM wikipedia');
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext).toEqual({});
+      });
+
+      it('handles whitespace-only query', () => {
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString('   \n\t  ')
+          .changeQueryContext({ maxNumTasks: 3 });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext).toEqual({ maxNumTasks: 3 });
+      });
+
+      it('handles JSON with null context', () => {
+        const jsonWithNullContext = sane`
+          {
+            "queryType": "topN",
+            "context": null
+          }
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(jsonWithNullContext)
+          .changeQueryContext({ maxNumTasks: 3 });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext).toBeDefined();
+      });
+
+      it('handles complex nested context values', () => {
+        const sqlInJson = sane`
+          {
+            "query": "SELECT * FROM wikipedia",
+            "context": {
+              "nestedObject": {
+                "key1": "value1",
+                "key2": 42
+              },
+              "arrayValue": [1, 2, 3]
+            }
+          }
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(sqlInJson)
+          .changeQueryContext({ maxNumTasks: 3 });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext).toEqual({
+          maxNumTasks: 3,
+          nestedObject: {
+            key1: 'value1',
+            key2: 42,
+          },
+          arrayValue: [1, 2, 3],
+        });
+      });
+
+      it('preserves boolean false values in context', () => {
+        const queryWithSets = sane`
+          SET useCache = FALSE;
+          SET finalizeAggregations = FALSE;
+          SELECT * FROM wikipedia
+        `;
+
+        const workbenchQuery = WorkbenchQuery.blank()
+          .changeQueryString(queryWithSets)
+          .changeQueryContext({ maxNumTasks: 3 });
+
+        const effectiveContext = workbenchQuery.getEffectiveContext();
+
+        expect(effectiveContext).toEqual({
+          maxNumTasks: 3,
+          useCache: false,
+          finalizeAggregations: false,
+        });
+      });
+    });
+  });
 });
diff --git a/web-console/src/druid-models/workbench-query/workbench-query.ts 
b/web-console/src/druid-models/workbench-query/workbench-query.ts
index 825b6af5b15..7072d41f729 100644
--- a/web-console/src/druid-models/workbench-query/workbench-query.ts
+++ b/web-console/src/druid-models/workbench-query/workbench-query.ts
@@ -309,6 +309,29 @@ export class WorkbenchQuery {
     return SqlSetStatement.getContextFromText(this.queryString);
   }
 
+  public getEffectiveContext(): QueryContext {
+    let effectiveContext = this.queryContext;
+    if (this.isJsonLike()) {
+      try {
+        const query = Hjson.parse(this.queryString);
+        effectiveContext = { ...effectiveContext, ...query.context };
+        if (typeof query.query === 'string') {
+          effectiveContext = {
+            ...effectiveContext,
+            ...SqlSetStatement.getContextFromText(query.query),
+          };
+        }
+      } catch {}
+    } else {
+      effectiveContext = {
+        ...effectiveContext,
+        ...SqlSetStatement.getContextFromText(this.queryString),
+      };
+    }
+
+    return effectiveContext;
+  }
+
   public changeQueryContext(queryContext: QueryContext): WorkbenchQuery {
     return new WorkbenchQuery({ ...this.valueOf(), queryContext });
   }
@@ -340,6 +363,12 @@ export class WorkbenchQuery {
   public getEffectiveEngine(): DruidEngine {
     const { engine } = this;
     if (engine) return engine;
+
+    // If an engine is set explicitly in the config then respect it
+    const contextEngine = this.getEffectiveContext().engine;
+    if (contextEngine === 'native') return 'sql-native';
+    if (contextEngine === 'msq-dart') return 'sql-msq-dart';
+
     const enabledEngines = WorkbenchQuery.getQueryEngines();
     if (this.isJsonLike()) {
       if (this.isSqlInJson()) {
@@ -463,7 +492,7 @@ export class WorkbenchQuery {
   }
 
   public getMaxNumTasks(): number | undefined {
-    return this.getQueryStringContext().maxNumTasks ?? 
this.queryContext.maxNumTasks;
+    return this.getEffectiveContext().maxNumTasks;
   }
 
   public setMaxNumTasksIfUnset(maxNumTasks: number | undefined): 
WorkbenchQuery {
@@ -552,13 +581,21 @@ export class WorkbenchQuery {
       ...queryContext,
     };
 
+    // Effective context lets us see the context which can also include the 
set statements from the SetStatements
+    const effectiveContext = {
+      ...apiQuery.context,
+      ...SqlSetStatement.getContextFromText(apiQuery.query || ''),
+    };
+
     if (engine === 'sql-native') {
-      apiQuery.context.engine ??= 'native';
+      if (typeof effectiveContext.engine === 'undefined') {
+        apiQuery.context.engine = 'native';
+      }
     }
 
     let cancelQueryId: string | undefined;
     if (engine === 'sql-native' || engine === 'sql-msq-dart') {
-      cancelQueryId = apiQuery.context.sqlQueryId;
+      cancelQueryId = effectiveContext.sqlQueryId;
       if (!cancelQueryId) {
         // If the sqlQueryId is not explicitly set on the context generate 
one, so it is possible to cancel the query.
         apiQuery.context.sqlQueryId = cancelQueryId = makeQueryId();
@@ -566,22 +603,40 @@ export class WorkbenchQuery {
     }
 
     if (engine === 'sql-msq-task') {
-      apiQuery.context.executionMode ??= 'async';
+      if (typeof effectiveContext.executionMode === 'undefined') {
+        apiQuery.context.executionMode = 'async';
+      }
+
       if (ingestQuery) {
         // Alter these defaults for ingest queries if unset
-        apiQuery.context.finalizeAggregations ??= false;
-        apiQuery.context.groupByEnableMultiValueUnnesting ??= false;
-        apiQuery.context.waitUntilSegmentsLoad ??= true;
+        if (typeof effectiveContext.finalizeAggregations === 'undefined') {
+          apiQuery.context.finalizeAggregations = false;
+        }
+        if (typeof effectiveContext.groupByEnableMultiValueUnnesting === 
'undefined') {
+          apiQuery.context.groupByEnableMultiValueUnnesting = false;
+        }
+        if (typeof effectiveContext.waitUntilSegmentsLoad === 'undefined') {
+          apiQuery.context.waitUntilSegmentsLoad = true;
+        }
       }
     }
 
     if (engine === 'sql-native' || engine === 'sql-msq-task') {
-      apiQuery.context.sqlStringifyArrays ??= false;
+      if (typeof effectiveContext.sqlStringifyArrays === 'undefined') {
+        apiQuery.context.sqlStringifyArrays = false;
+      }
     }
 
     if (engine === 'sql-msq-dart') {
-      apiQuery.context.engine = 'msq-dart';
-      apiQuery.context.fullReport ??= true;
+      if (typeof effectiveContext.engine === 'undefined') {
+        apiQuery.context.engine = 'msq-dart';
+      }
+      if (typeof effectiveContext.fullReport === 'undefined') {
+        apiQuery.context.fullReport = true;
+      }
+      if (typeof effectiveContext.liveReportCounters === 'undefined') {
+        apiQuery.context.liveReportCounters = true;
+      }
     }
 
     if (Array.isArray(queryParameters) && queryParameters.length) {
diff --git a/web-console/src/utils/query-manager/query-manager.ts 
b/web-console/src/utils/query-manager/query-manager.ts
index 5c1a859758c..c802b8014ce 100644
--- a/web-console/src/utils/query-manager/query-manager.ts
+++ b/web-console/src/utils/query-manager/query-manager.ts
@@ -25,12 +25,19 @@ import { IntermediateQueryState } from 
'./intermediate-query-state';
 import { QueryState } from './query-state';
 import { ResultWithAuxiliaryWork } from './result-with-auxiliary-work';
 
+export interface ProcessQueryExtra<I = never> {
+  setIntermediateQuery: (intermediateQuery: any) => void;
+  setIntermediateStateCallback: (
+    intermediateStateCallback: (signal: AbortSignal) => Promise<I>,
+  ) => void;
+}
+
 export interface QueryManagerOptions<Q, R, I = never, E extends Error = Error> 
{
   initState?: QueryState<R, E, I>;
   processQuery: (
     query: Q,
     signal: AbortSignal,
-    setIntermediateQuery: (intermediateQuery: any) => void,
+    extra: ProcessQueryExtra<I>,
   ) => Promise<R | IntermediateQueryState<I> | ResultWithAuxiliaryWork<R>>;
   backgroundStatusCheck?: (
     state: I,
@@ -56,7 +63,7 @@ export class QueryManager<Q, R, I = never, E extends Error = 
Error> {
   private readonly processQuery: (
     query: Q,
     signal: AbortSignal,
-    setIntermediateQuery: (intermediateQuery: any) => void,
+    extra: ProcessQueryExtra<I>,
   ) => Promise<R | IntermediateQueryState<I> | ResultWithAuxiliaryWork<R>>;
 
   private readonly backgroundStatusCheck?: (
@@ -130,8 +137,56 @@ export class QueryManager<Q, R, I = never, E extends Error 
= Error> {
     const query = this.lastQuery;
     let data: R | IntermediateQueryState<I> | ResultWithAuxiliaryWork<R>;
     try {
-      data = await this.processQuery(query, signal, (intermediateQuery: any) 
=> {
-        this.lastIntermediateQuery = intermediateQuery;
+      data = await this.processQuery(query, signal, {
+        setIntermediateQuery: (intermediateQuery: any) => {
+          this.lastIntermediateQuery = intermediateQuery;
+        },
+        setIntermediateStateCallback: intermediateStateCallback => {
+          let backgroundChecks = 0;
+          let intermediateError: Error | undefined;
+
+          void (async () => {
+            while (!signal.aborted && this.currentQueryId === myQueryId) {
+              try {
+                const delay =
+                  backgroundChecks > 0
+                    ? this.backgroundStatusCheckDelay
+                    : this.backgroundStatusCheckInitDelay;
+
+                if (delay) {
+                  await wait(delay);
+                  if (signal.aborted || this.currentQueryId !== myQueryId) 
return;
+                }
+
+                const intermediate = await intermediateStateCallback(signal);
+
+                if (signal.aborted || this.currentQueryId !== myQueryId || 
!this.state.loading) {
+                  return;
+                }
+
+                this.setState(
+                  new QueryState<R, E, I>({
+                    loading: true,
+                    intermediate,
+                    intermediateError,
+                    lastData: this.state.getSomeData(),
+                  }),
+                );
+
+                intermediateError = undefined; // Clear the intermediate error 
if there was one
+              } catch (e) {
+                if (signal.aborted || this.currentQueryId !== myQueryId) 
return;
+                if (this.swallowBackgroundError?.(e)) {
+                  intermediateError = e;
+                } else {
+                  return; // Stop the loop on unrecoverable error
+                }
+              }
+
+              backgroundChecks++;
+            }
+          })();
+        },
       });
     } catch (e) {
       if (this.currentQueryId !== myQueryId) return;
diff --git a/web-console/src/views/datasources-view/datasources-view.tsx 
b/web-console/src/views/datasources-view/datasources-view.tsx
index 463991a806a..49e0f0e2996 100644
--- a/web-console/src/views/datasources-view/datasources-view.tsx
+++ b/web-console/src/views/datasources-view/datasources-view.tsx
@@ -435,7 +435,7 @@ GROUP BY 1, 2`;
       processQuery: async (
         { capabilities, visibleColumns, showUnused },
         signal,
-        setIntermediateQuery,
+        { setIntermediateQuery },
       ) => {
         let datasources: DatasourceQueryResultRow[];
         if (capabilities.hasSql()) {
diff --git a/web-console/src/views/segments-view/segments-view.tsx 
b/web-console/src/views/segments-view/segments-view.tsx
index 67e6bbba569..11e89818d34 100644
--- a/web-console/src/views/segments-view/segments-view.tsx
+++ b/web-console/src/views/segments-view/segments-view.tsx
@@ -308,7 +308,7 @@ export class SegmentsView extends 
React.PureComponent<SegmentsViewProps, Segment
 
     this.segmentsQueryManager = new QueryManager({
       debounceIdle: 500,
-      processQuery: async (query: SegmentsQuery, signal, setIntermediateQuery) 
=> {
+      processQuery: async (query: SegmentsQuery, signal, { 
setIntermediateQuery }) => {
         const { page, pageSize, filtered, sorted, visibleColumns, 
capabilities, groupByInterval } =
           query;
 
diff --git a/web-console/src/views/supervisors-view/supervisors-view.tsx 
b/web-console/src/views/supervisors-view/supervisors-view.tsx
index 49836246968..294e86d81e9 100644
--- a/web-console/src/views/supervisors-view/supervisors-view.tsx
+++ b/web-console/src/views/supervisors-view/supervisors-view.tsx
@@ -282,7 +282,7 @@ export class SupervisorsView extends React.PureComponent<
       processQuery: async (
         { capabilities, visibleColumns, filtered, sorted, page, pageSize },
         signal,
-        setIntermediateQuery,
+        { setIntermediateQuery },
       ) => {
         let supervisors: SupervisorQueryResultRow[];
         let count = -1;
diff --git a/web-console/src/views/tasks-view/tasks-view.tsx 
b/web-console/src/views/tasks-view/tasks-view.tsx
index bb064c7dab4..0a866194faa 100644
--- a/web-console/src/views/tasks-view/tasks-view.tsx
+++ b/web-console/src/views/tasks-view/tasks-view.tsx
@@ -715,6 +715,7 @@ ORDER BY
         )}
         {executionDialogOpen && (
           <ExecutionDetailsDialog
+            type="task"
             id={executionDialogOpen}
             goToTask={taskId => {
               onFiltersChange(TableFilters.eq({ task_id: taskId }));
diff --git 
a/web-console/src/views/workbench-view/current-dart-panel/current-dart-panel.tsx
 
b/web-console/src/views/workbench-view/current-dart-panel/current-dart-panel.tsx
index 2160bbbf654..007e3eca103 100644
--- 
a/web-console/src/views/workbench-view/current-dart-panel/current-dart-panel.tsx
+++ 
b/web-console/src/views/workbench-view/current-dart-panel/current-dart-panel.tsx
@@ -29,7 +29,6 @@ import { useClock, useInterval, useQueryManager } from 
'../../../hooks';
 import { Api, AppToaster } from '../../../singletons';
 import { formatDuration, prettyFormatIsoDate } from '../../../utils';
 import { CancelQueryDialog } from '../cancel-query-dialog/cancel-query-dialog';
-import { DartDetailsDialog } from '../dart-details-dialog/dart-details-dialog';
 import { getMsqDartVersion, WORK_STATE_STORE } from '../work-state-store';
 
 import './current-dart-panel.scss';
@@ -48,24 +47,22 @@ function stateToIconAndColor(status: 
DartQueryEntry['state']): [IconName, string
 }
 
 export interface CurrentViberPanelProps {
+  onExecutionDetails(id: string): void;
   onClose(): void;
 }
 
 export const CurrentDartPanel = React.memo(function CurrentViberPanel(
   props: CurrentViberPanelProps,
 ) {
-  const { onClose } = props;
+  const { onExecutionDetails, onClose } = props;
 
-  const [showSql, setShowSql] = useState<string | undefined>();
   const [confirmCancelId, setConfirmCancelId] = useState<string | undefined>();
 
   const [dartQueryEntriesState, queryManager] = useQueryManager<number, 
DartQueryEntry[]>({
     query: useStore(WORK_STATE_STORE, getMsqDartVersion),
     processQuery: async (_, signal) => {
-      return (
-        (await Api.instance.get('/druid/v2/sql/queries', { signal })).data
-          .queries as DartQueryEntry[]
-      ).filter(q => q.engine === 'msq-dart');
+      return (await Api.instance.get('/druid/v2/sql/queries', { signal })).data
+        .queries as DartQueryEntry[];
     },
   });
 
@@ -89,9 +86,9 @@ export const CurrentDartPanel = React.memo(function 
CurrentViberPanel(
               <Menu>
                 <MenuItem
                   icon={IconNames.EYE_OPEN}
-                  text="Show SQL"
+                  text="Show details"
                   onClick={() => {
-                    setShowSql(w.sql);
+                    onExecutionDetails(w.sqlQueryId);
                   }}
                 />
                 <MenuItem
@@ -132,10 +129,13 @@ export const CurrentDartPanel = React.memo(function 
CurrentViberPanel(
             const anonymous = w.identity === 'allowAll' && w.authenticator === 
'allowAll';
             return (
               <Popover className="work-entry" key={w.sqlQueryId} 
position="left" content={menu}>
-                <div onDoubleClick={() => setShowSql(w.sql)}>
-                  <div className="line1">
+                <div onDoubleClick={() => onExecutionDetails(w.sqlQueryId)}>
+                  <div
+                    className="line1"
+                    data-tooltip={`Engine: ${w.engine}\nSQL ID: 
${w.sqlQueryId}`}
+                  >
                     <Icon
-                      className={'status-icon ' + w.state.toLowerCase()}
+                      className={`status-icon ${w.state.toLowerCase()}`}
                       icon={icon}
                       style={{ color }}
                       data-tooltip={`State: ${w.state}`}
@@ -187,7 +187,6 @@ export const CurrentDartPanel = React.memo(function 
CurrentViberPanel(
           onDismiss={() => setConfirmCancelId(undefined)}
         />
       )}
-      {showSql && <DartDetailsDialog sql={showSql} onClose={() => 
setShowSql(undefined)} />}
     </div>
   );
 });
diff --git 
a/web-console/src/views/workbench-view/dart-details-dialog/dart-details-dialog.scss
 
b/web-console/src/views/workbench-view/dart-details-dialog/dart-details-dialog.scss
deleted file mode 100644
index f1f380dc4ec..00000000000
--- 
a/web-console/src/views/workbench-view/dart-details-dialog/dart-details-dialog.scss
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-@import '../../../variables';
-
-.dart-details-dialog {
-  &.#{$bp-ns}-dialog {
-    width: 95vw;
-  }
-
-  .#{$bp-ns}-dialog-body {
-    height: 70vh;
-    position: relative;
-    margin: 0;
-
-    .flexible-query-input {
-      height: 100%;
-    }
-  }
-}
diff --git 
a/web-console/src/views/workbench-view/dart-details-dialog/dart-details-dialog.tsx
 
b/web-console/src/views/workbench-view/dart-details-dialog/dart-details-dialog.tsx
deleted file mode 100644
index 0637d6b9644..00000000000
--- 
a/web-console/src/views/workbench-view/dart-details-dialog/dart-details-dialog.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-import { Button, Classes, Dialog } from '@blueprintjs/core';
-import React from 'react';
-
-import { FlexibleQueryInput } from 
'../flexible-query-input/flexible-query-input';
-
-import './dart-details-dialog.scss';
-
-export interface DartDetailsDialogProps {
-  sql: string;
-  onClose(): void;
-}
-
-export const DartDetailsDialog = React.memo(function DartDetailsDialog(
-  props: DartDetailsDialogProps,
-) {
-  const { sql, onClose } = props;
-
-  return (
-    <Dialog className="dart-details-dialog" isOpen onClose={onClose} 
title="Dart SQL">
-      <div className={Classes.DIALOG_BODY}>
-        <FlexibleQueryInput queryString={sql} leaveBackground />
-      </div>
-      <div className={Classes.DIALOG_FOOTER}>
-        <div className={Classes.DIALOG_FOOTER_ACTIONS}>
-          <Button text="Close" onClick={onClose} />
-        </div>
-      </div>
-    </Dialog>
-  );
-});
diff --git 
a/web-console/src/views/workbench-view/execution-details-dialog/execution-details-dialog.tsx
 
b/web-console/src/views/workbench-view/execution-details-dialog/execution-details-dialog.tsx
index 8c9de297c6a..9f390fac2ca 100644
--- 
a/web-console/src/views/workbench-view/execution-details-dialog/execution-details-dialog.tsx
+++ 
b/web-console/src/views/workbench-view/execution-details-dialog/execution-details-dialog.tsx
@@ -26,6 +26,7 @@ import { ExecutionDetailsPaneLoader } from 
'../execution-details-pane-loader/exe
 import './execution-details-dialog.scss';
 
 export interface ExecutionDetailsDialogProps {
+  type: 'task' | 'dart';
   id: string;
   initTab?: ExecutionDetailsTab;
   initExecution?: Execution;
@@ -36,12 +37,13 @@ export interface ExecutionDetailsDialogProps {
 export const ExecutionDetailsDialog = React.memo(function 
ExecutionDetailsDialog(
   props: ExecutionDetailsDialogProps,
 ) {
-  const { id, initTab, initExecution, goToTask, onClose } = props;
+  const { type, id, initTab, initExecution, goToTask, onClose } = props;
 
   return (
     <Dialog className="execution-details-dialog" isOpen onClose={onClose} 
title="Execution details">
       <div className={Classes.DIALOG_BODY}>
         <ExecutionDetailsPaneLoader
+          type={type}
           id={id}
           initTab={initTab}
           initExecution={initExecution}
diff --git 
a/web-console/src/views/workbench-view/execution-details-pane-loader/execution-details-pane-loader.tsx
 
b/web-console/src/views/workbench-view/execution-details-pane-loader/execution-details-pane-loader.tsx
index 8a06877f6bf..28d201388d5 100644
--- 
a/web-console/src/views/workbench-view/execution-details-pane-loader/execution-details-pane-loader.tsx
+++ 
b/web-console/src/views/workbench-view/execution-details-pane-loader/execution-details-pane-loader.tsx
@@ -19,14 +19,25 @@
 import React from 'react';
 
 import { Loader } from '../../../components';
-import type { Execution } from '../../../druid-models';
+import { Execution } from '../../../druid-models';
 import { getTaskExecution } from '../../../helpers';
 import { useInterval, useQueryManager } from '../../../hooks';
+import { Api } from '../../../singletons';
 import { QueryState } from '../../../utils';
 import type { ExecutionDetailsTab } from 
'../execution-details-pane/execution-details-pane';
 import { ExecutionDetailsPane } from 
'../execution-details-pane/execution-details-pane';
 
+async function getDartExecution(sqlQueryId: string, signal: AbortSignal): 
Promise<Execution> {
+  const { data } = await Api.instance.get(
+    `/druid/v2/sql/queries/${Api.encodePath(sqlQueryId)}/reports`,
+    { signal },
+  );
+
+  return Execution.fromDartReport(data.report).changeSqlQuery(data.query.sql);
+}
+
 export interface ExecutionDetailsPaneLoaderProps {
+  type: 'task' | 'dart';
   id: string;
   initTab?: ExecutionDetailsTab;
   initExecution?: Execution;
@@ -36,13 +47,17 @@ export interface ExecutionDetailsPaneLoaderProps {
 export const ExecutionDetailsPaneLoader = React.memo(function 
ExecutionDetailsPaneLoader(
   props: ExecutionDetailsPaneLoaderProps,
 ) {
-  const { id, initTab, initExecution, goToTask } = props;
+  const { type, id, initTab, initExecution, goToTask } = props;
 
   const [executionState, queryManager] = useQueryManager<string, Execution>({
     initQuery: initExecution ? undefined : id,
     initState: initExecution ? new QueryState({ data: initExecution }) : 
undefined,
     processQuery: (id, signal) => {
-      return getTaskExecution(id, undefined, signal);
+      if (type === 'task') {
+        return getTaskExecution(id, undefined, signal);
+      } else {
+        return getDartExecution(id, signal);
+      }
     },
   });
 
@@ -50,7 +65,7 @@ export const ExecutionDetailsPaneLoader = React.memo(function 
ExecutionDetailsPa
     const execution = executionState.data;
     if (!execution) return;
     if (execution.isWaitingForQuery()) {
-      queryManager.runQuery(execution.id);
+      queryManager.rerunLastQuery();
     }
   }, 1000);
 
diff --git 
a/web-console/src/views/workbench-view/execution-details-pane/__snapshots__/execution-details-pane.spec.tsx.snap
 
b/web-console/src/views/workbench-view/execution-details-pane/__snapshots__/execution-details-pane.spec.tsx.snap
index 308b030d19f..4a27ae98ca2 100644
--- 
a/web-console/src/views/workbench-view/execution-details-pane/__snapshots__/execution-details-pane.spec.tsx.snap
+++ 
b/web-console/src/views/workbench-view/execution-details-pane/__snapshots__/execution-details-pane.spec.tsx.snap
@@ -77,8 +77,8 @@ exports[`ExecutionDetailsPane matches snapshot no init tab 
1`] = `
       >
         0:00:04
       </Blueprint5.Tag>
+       (starting at
        
-      (starting at 
       <Blueprint5.Tag
         active={false}
         fill={false}
diff --git 
a/web-console/src/views/workbench-view/execution-details-pane/execution-details-pane.tsx
 
b/web-console/src/views/workbench-view/execution-details-pane/execution-details-pane.tsx
index e59cdb87939..20e5f342eb3 100644
--- 
a/web-console/src/views/workbench-view/execution-details-pane/execution-details-pane.tsx
+++ 
b/web-console/src/views/workbench-view/execution-details-pane/execution-details-pane.tsx
@@ -80,8 +80,9 @@ export const ExecutionDetailsPane = React.memo(function 
ExecutionDetailsPane(
             </p>
             {execution.startTime && !!execution.duration && (
               <p>
-                Query took <Tag 
minimal>{formatDurationWithMsIfNeeded(execution.duration)}</Tag>{' '}
-                (starting at <Tag 
minimal>{prettyFormatIsoDate(execution.startTime)}</Tag>)
+                {execution.status === 'RUNNING' ? 'Query is running for ' : 
'Query took '}
+                <Tag 
minimal>{formatDurationWithMsIfNeeded(execution.duration)}</Tag> (starting at{' 
'}
+                <Tag minimal>{prettyFormatIsoDate(execution.startTime)}</Tag>)
               </p>
             )}
             {execution.destination && (
diff --git 
a/web-console/src/views/workbench-view/execution-progress-bar-pane/execution-progress-bar-pane.tsx
 
b/web-console/src/views/workbench-view/execution-progress-bar-pane/execution-progress-bar-pane.tsx
index 4520c20a39b..b8edf41ff69 100644
--- 
a/web-console/src/views/workbench-view/execution-progress-bar-pane/execution-progress-bar-pane.tsx
+++ 
b/web-console/src/views/workbench-view/execution-progress-bar-pane/execution-progress-bar-pane.tsx
@@ -49,7 +49,7 @@ export const ExecutionProgressBarPane = React.memo(function 
ExecutionProgressBar
   }
 
   const idx = stages ? stages.currentStageIndex() : -1;
-  const waitingForSegments = stages && !execution.isWaitingForQuery();
+  const waitingForSegments = execution?.isWaitingForSegments();
 
   const segmentStatusDescription = execution?.getSegmentStatusDescription();
 
diff --git a/web-console/src/views/workbench-view/query-tab/query-tab.tsx 
b/web-console/src/views/workbench-view/query-tab/query-tab.tsx
index 0bbc26024d9..8ec233b6a81 100644
--- a/web-console/src/views/workbench-view/query-tab/query-tab.tsx
+++ b/web-console/src/views/workbench-view/query-tab/query-tab.tsx
@@ -200,7 +200,7 @@ export const QueryTab = React.memo(function QueryTab(props: 
QueryTabProps) {
   >({
     initQuery: cachedExecutionState ? undefined : currentRunningPromise || 
query.getLastExecution(),
     initState: cachedExecutionState,
-    processQuery: async (q, signal) => {
+    processQuery: async (q, signal, { setIntermediateStateCallback }) => {
       if (q instanceof WorkbenchQuery) {
         ExecutionStateCache.deleteState(id);
         const { engine, query, prefixLines, cancelQueryId } = q.getApiQuery();
@@ -280,6 +280,15 @@ export const QueryTab = React.memo(function 
QueryTab(props: QueryTabProps) {
                   .delete(`/druid/v2/sql/${Api.encodePath(cancelQueryId)}`)
                   .catch(() => {});
               });
+
+              setIntermediateStateCallback(async signal => {
+                const { data } = await Api.instance.get(
+                  
`/druid/v2/sql/queries/${Api.encodePath(cancelQueryId)}/reports`,
+                  { signal },
+                );
+
+                return Execution.fromDartReport(data.report);
+              });
             }
 
             onQueryChange(props.query.changeLastExecution(undefined));
diff --git a/web-console/src/views/workbench-view/workbench-view.tsx 
b/web-console/src/views/workbench-view/workbench-view.tsx
index 16a9f77caae..9fdd778b9c1 100644
--- a/web-console/src/views/workbench-view/workbench-view.tsx
+++ b/web-console/src/views/workbench-view/workbench-view.tsx
@@ -150,7 +150,12 @@ export interface WorkbenchViewState {
 
   columnMetadataState: QueryState<readonly ColumnMetadata[]>;
 
-  details?: { id: string; initTab?: ExecutionDetailsTab; initExecution?: 
Execution };
+  details?: {
+    type: 'task' | 'dart';
+    id: string;
+    initTab?: ExecutionDetailsTab;
+    initExecution?: Execution;
+  };
 
   connectExternalDataDialogOpen: boolean;
   explainDialogOpen: boolean;
@@ -293,9 +298,15 @@ export class WorkbenchView extends 
React.PureComponent<WorkbenchViewProps, Workb
     localStorageSetJson(LocalStorageKeys.WORKBENCH_DART_PANEL, false);
   };
 
-  private readonly handleDetailsWithId = (id: string, initTab?: 
ExecutionDetailsTab) => {
+  private readonly handleDetailsWithTaskId = (id: string, initTab?: 
ExecutionDetailsTab) => {
     this.setState({
-      details: { id, initTab },
+      details: { type: 'task', id, initTab },
+    });
+  };
+
+  private readonly handleDetailsWithSqlId = (id: string, initTab?: 
ExecutionDetailsTab) => {
+    this.setState({
+      details: { type: 'dart', id, initTab },
     });
   };
 
@@ -308,7 +319,12 @@ export class WorkbenchView extends 
React.PureComponent<WorkbenchViewProps, Workb
     initTab?: ExecutionDetailsTab,
   ) => {
     this.setState({
-      details: { id: execution.id, initExecution: execution, initTab },
+      details: {
+        type: execution.engine === 'sql-msq-dart' ? 'dart' : 'task',
+        id: execution.id,
+        initExecution: execution,
+        initTab,
+      },
     });
   };
 
@@ -344,6 +360,7 @@ export class WorkbenchView extends 
React.PureComponent<WorkbenchViewProps, Workb
 
     return (
       <ExecutionDetailsDialog
+        type={details.type}
         id={details.id}
         initTab={details.initTab}
         initExecution={details.initExecution}
@@ -488,6 +505,7 @@ export class WorkbenchView extends 
React.PureComponent<WorkbenchViewProps, Workb
         onSubmit={execution => {
           this.setState({
             details: {
+              type: 'task',
               id: execution.id,
               initExecution: execution,
             },
@@ -968,13 +986,16 @@ export class WorkbenchView extends 
React.PureComponent<WorkbenchViewProps, Workb
               {showRecentQueryTaskPanel && (
                 <RecentQueryTaskPanel
                   onClose={this.handleRecentQueryTaskPanelClose}
-                  onExecutionDetails={this.handleDetailsWithId}
+                  onExecutionDetails={this.handleDetailsWithTaskId}
                   onChangeQuery={this.handleQueryStringChange}
                   onNewTab={this.handleNewTab}
                 />
               )}
               {showCurrentDartPanel && (
-                <CurrentDartPanel onClose={this.handleCurrentDartPanelClose} />
+                <CurrentDartPanel
+                  onClose={this.handleCurrentDartPanelClose}
+                  onExecutionDetails={this.handleDetailsWithSqlId}
+                />
               )}
             </div>
           )}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to