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 4a31ae26f4 Web console: Page downloader, and fix JSON error resetting 
(#14712)
4a31ae26f4 is described below

commit 4a31ae26f44f02ab04c77c93ec29f052f91c3a95
Author: Vadim Ogievetsky <[email protected]>
AuthorDate: Tue Aug 1 14:25:41 2023 -0700

    Web console: Page downloader, and fix JSON error resetting (#14712)
    
    * fix error reset
    
    * add page dialog logic
    
    * add to detail archive
    
    * update tests
    
    * fix plurals
    
    * use jsonl ext
    
    * fix regex issue
---
 .../src/components/json-input/json-input.tsx       |   9 +-
 .../compaction-config-dialog.tsx                   |  15 +-
 .../coordinator-dynamic-config-dialog.tsx          |  15 +-
 .../index-spec-dialog/index-spec-dialog.tsx        |  17 +-
 .../lookup-edit-dialog/lookup-edit-dialog.tsx      |  15 +-
 .../overlord-dynamic-config-dialog.tsx             |  15 +-
 .../dialogs/retention-dialog/retention-dialog.tsx  |   2 +-
 .../src/druid-models/async-query/async-query.ts    |   8 +-
 .../execution/execution-ingest-complete.mock.ts    | 240 ++++++++++----------
 .../execution/execution-ingest-error.mock.ts       |  48 ++--
 .../src/druid-models/execution/execution.spec.ts   |  62 ++++--
 .../src/druid-models/execution/execution.ts        | 145 ++++--------
 web-console/src/druid-models/task/task.ts          |   1 +
 .../src/helpers/execution/sql-task-execution.ts    |  33 ++-
 .../src/utils/download-query-detail-archive.ts     |  20 +-
 web-console/src/utils/download.ts                  |  17 ++
 web-console/src/utils/general.tsx                  |  15 +-
 .../destination-pages-dialog.scss}                 |  23 +-
 .../destination-pages-dialog.tsx                   |  49 ++++
 .../destination-pages-pane.tsx                     | 135 +++++++++++
 .../execution-details-pane.spec.tsx.snap           | 247 ++++++++++++---------
 .../execution-details-pane.tsx                     |  29 ++-
 .../execution-error-pane.spec.tsx.snap             |   2 +-
 .../execution-stages-pane.spec.tsx.snap            |  17 +-
 .../execution-submit-dialog.tsx                    |  15 +-
 .../execution-summary-panel.tsx                    |  49 ++--
 .../ingest-success-pane/ingest-success-pane.tsx    |   2 +-
 .../recent-query-task-panel.tsx                    |   2 +-
 28 files changed, 771 insertions(+), 476 deletions(-)

diff --git a/web-console/src/components/json-input/json-input.tsx 
b/web-console/src/components/json-input/json-input.tsx
index e3467303e3..b206db77e9 100644
--- a/web-console/src/components/json-input/json-input.tsx
+++ b/web-console/src/components/json-input/json-input.tsx
@@ -67,7 +67,7 @@ interface InternalValue {
 interface JsonInputProps {
   value: any;
   onChange: (value: any) => void;
-  onError?: (error: Error) => void;
+  setError?: (error: Error | undefined) => void;
   placeholder?: string;
   focus?: boolean;
   width?: string;
@@ -76,7 +76,7 @@ interface JsonInputProps {
 }
 
 export const JsonInput = React.memo(function JsonInput(props: JsonInputProps) {
-  const { onChange, onError, placeholder, focus, width, height, value, 
issueWithValue } = props;
+  const { onChange, setError, placeholder, focus, width, height, value, 
issueWithValue } = props;
   const [internalValue, setInternalValue] = useState<InternalValue>(() => ({
     value,
     stringified: stringifyJson(value),
@@ -121,9 +121,8 @@ export const JsonInput = React.memo(function 
JsonInput(props: JsonInputProps) {
             stringified: inputJson,
           });
 
-          if (error) {
-            onError?.(error);
-          } else {
+          setError?.(error);
+          if (!error) {
             onChange(value);
           }
 
diff --git 
a/web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.tsx 
b/web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.tsx
index 7c70d22571..8a3a232072 100644
--- 
a/web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.tsx
+++ 
b/web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.tsx
@@ -87,7 +87,13 @@ export const CompactionConfigDialog = React.memo(function 
CompactionConfigDialog
           </p>
         </Callout>
       )}
-      <FormJsonSelector tab={currentTab} onChange={setCurrentTab} />
+      <FormJsonSelector
+        tab={currentTab}
+        onChange={t => {
+          setJsonError(undefined);
+          setCurrentTab(t);
+        }}
+      />
       <div className="content">
         {currentTab === 'form' ? (
           <AutoForm
@@ -98,11 +104,8 @@ export const CompactionConfigDialog = React.memo(function 
CompactionConfigDialog
         ) : (
           <JsonInput
             value={currentConfig}
-            onChange={v => {
-              setCurrentConfig(v);
-              setJsonError(undefined);
-            }}
-            onError={setJsonError}
+            onChange={setCurrentConfig}
+            setError={setJsonError}
             issueWithValue={value => AutoForm.issueWithModel(value, 
COMPACTION_CONFIG_FIELDS)}
             height="100%"
           />
diff --git 
a/web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.tsx
 
b/web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.tsx
index 118f53aef5..b31f774d88 100644
--- 
a/web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.tsx
+++ 
b/web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.tsx
@@ -114,7 +114,13 @@ export const CoordinatorDynamicConfigDialog = 
React.memo(function CoordinatorDyn
             </ExternalLink>
             .
           </p>
-          <FormJsonSelector tab={currentTab} onChange={setCurrentTab} />
+          <FormJsonSelector
+            tab={currentTab}
+            onChange={t => {
+              setJsonError(undefined);
+              setCurrentTab(t);
+            }}
+          />
           {currentTab === 'form' ? (
             <AutoForm
               fields={COORDINATOR_DYNAMIC_CONFIG_FIELDS}
@@ -125,11 +131,8 @@ export const CoordinatorDynamicConfigDialog = 
React.memo(function CoordinatorDyn
             <JsonInput
               value={dynamicConfig}
               height="50vh"
-              onChange={v => {
-                setDynamicConfig(v);
-                setJsonError(undefined);
-              }}
-              onError={setJsonError}
+              onChange={setDynamicConfig}
+              setError={setJsonError}
             />
           )}
         </>
diff --git a/web-console/src/dialogs/index-spec-dialog/index-spec-dialog.tsx 
b/web-console/src/dialogs/index-spec-dialog/index-spec-dialog.tsx
index daa9e06069..5f579953d1 100644
--- a/web-console/src/dialogs/index-spec-dialog/index-spec-dialog.tsx
+++ b/web-console/src/dialogs/index-spec-dialog/index-spec-dialog.tsx
@@ -50,22 +50,25 @@ export const IndexSpecDialog = React.memo(function 
IndexSpecDialog(props: IndexS
       canOutsideClickClose={false}
       title={title ?? 'Index spec'}
     >
-      <FormJsonSelector tab={currentTab} onChange={setCurrentTab} />
+      <FormJsonSelector
+        tab={currentTab}
+        onChange={t => {
+          setJsonError(undefined);
+          setCurrentTab(t);
+        }}
+      />
       <div className="content">
         {currentTab === 'form' ? (
           <AutoForm
             fields={INDEX_SPEC_FIELDS}
             model={currentIndexSpec}
-            onChange={m => setCurrentIndexSpec(m)}
+            onChange={setCurrentIndexSpec}
           />
         ) : (
           <JsonInput
             value={currentIndexSpec}
-            onChange={v => {
-              setCurrentIndexSpec(v);
-              setJsonError(undefined);
-            }}
-            onError={setJsonError}
+            onChange={setCurrentIndexSpec}
+            setError={setJsonError}
             issueWithValue={value => AutoForm.issueWithModel(value, 
INDEX_SPEC_FIELDS)}
             height="100%"
           />
diff --git a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx 
b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx
index 0903ede6f9..6109dfb921 100644
--- a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx
+++ b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx
@@ -121,7 +121,13 @@ export const LookupEditDialog = React.memo(function 
LookupEditDialog(props: Look
             }
           />
         </FormGroup>
-        <FormJsonSelector tab={currentTab} onChange={setCurrentTab} />
+        <FormJsonSelector
+          tab={currentTab}
+          onChange={t => {
+            setJsonError(undefined);
+            setCurrentTab(t);
+          }}
+        />
         {currentTab === 'form' ? (
           <AutoForm
             fields={LOOKUP_FIELDS}
@@ -134,11 +140,8 @@ export const LookupEditDialog = React.memo(function 
LookupEditDialog(props: Look
           <JsonInput
             value={lookupSpec}
             height="80vh"
-            onChange={m => {
-              onChange('spec', m);
-              setJsonError(undefined);
-            }}
-            onError={setJsonError}
+            onChange={m => onChange('spec', m)}
+            setError={setJsonError}
             issueWithValue={spec => AutoForm.issueWithModel(spec, 
LOOKUP_FIELDS)}
           />
         )}
diff --git 
a/web-console/src/dialogs/overlord-dynamic-config-dialog/overlord-dynamic-config-dialog.tsx
 
b/web-console/src/dialogs/overlord-dynamic-config-dialog/overlord-dynamic-config-dialog.tsx
index fa31d2378b..109901a71b 100644
--- 
a/web-console/src/dialogs/overlord-dynamic-config-dialog/overlord-dynamic-config-dialog.tsx
+++ 
b/web-console/src/dialogs/overlord-dynamic-config-dialog/overlord-dynamic-config-dialog.tsx
@@ -114,7 +114,13 @@ export const OverlordDynamicConfigDialog = 
React.memo(function OverlordDynamicCo
             </ExternalLink>
             .
           </p>
-          <FormJsonSelector tab={currentTab} onChange={setCurrentTab} />
+          <FormJsonSelector
+            tab={currentTab}
+            onChange={t => {
+              setJsonError(undefined);
+              setCurrentTab(t);
+            }}
+          />
           {currentTab === 'form' ? (
             <AutoForm
               fields={OVERLORD_DYNAMIC_CONFIG_FIELDS}
@@ -125,11 +131,8 @@ export const OverlordDynamicConfigDialog = 
React.memo(function OverlordDynamicCo
             <JsonInput
               value={dynamicConfig}
               height="50vh"
-              onChange={v => {
-                setDynamicConfig(v);
-                setJsonError(undefined);
-              }}
-              onError={setJsonError}
+              onChange={setDynamicConfig}
+              setError={setJsonError}
             />
           )}
         </>
diff --git a/web-console/src/dialogs/retention-dialog/retention-dialog.tsx 
b/web-console/src/dialogs/retention-dialog/retention-dialog.tsx
index 547fd99ee3..6420413706 100644
--- a/web-console/src/dialogs/retention-dialog/retention-dialog.tsx
+++ b/web-console/src/dialogs/retention-dialog/retention-dialog.tsx
@@ -187,7 +187,7 @@ ORDER BY 1`,
         <JsonInput
           value={currentRules}
           onChange={setCurrentRules}
-          onError={setJsonError}
+          setError={setJsonError}
           height="100%"
         />
       )}
diff --git a/web-console/src/druid-models/async-query/async-query.ts 
b/web-console/src/druid-models/async-query/async-query.ts
index 401569f558..e823ba6a17 100644
--- a/web-console/src/druid-models/async-query/async-query.ts
+++ b/web-console/src/druid-models/async-query/async-query.ts
@@ -28,10 +28,14 @@ export interface AsyncStatusResponse {
   schema?: { name: string; type: string; nativeType: string }[];
   result?: {
     dataSource: string;
-    sampleRecords: any[][];
+    sampleRecords?: any[][];
     numTotalRows: number;
     totalSizeInBytes: number;
-    pages: any[];
+    pages?: {
+      id: number;
+      numRows: number;
+      sizeInBytes: number;
+    }[];
   };
   errorDetails?: ErrorResponse;
 }
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 797fdd5f25..3ae77b27ef 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
@@ -43,12 +43,12 @@ PARTITIONED BY ALL TIME
 export const EXECUTION_INGEST_COMPLETE = Execution.fromTaskReport({
   multiStageQuery: {
     type: 'multiStageQuery',
-    taskId: 'query-5aa683e2-a6ee-4655-a834-a643e91055b1',
+    taskId: 'query-93a855fa-c35a-48df-b596-6bc98eed1101',
     payload: {
       status: {
         status: 'SUCCESS',
-        startTime: '2023-06-19T05:39:26.377Z',
-        durationMs: 23170,
+        startTime: '2023-08-01T03:12:59.527Z',
+        durationMs: 23699,
         pendingTasks: 0,
         runningTasks: 2,
       },
@@ -56,7 +56,7 @@ export const EXECUTION_INGEST_COMPLETE = 
Execution.fromTaskReport({
         {
           stageNumber: 0,
           definition: {
-            id: '8af42220-2724-4a76-b39f-c2f98df2de69_0',
+            id: 'ad318360-2ccf-4afc-b221-27c8704bf4fe_0',
             input: [
               {
                 type: 'external',
@@ -129,16 +129,17 @@ export const EXECUTION_INGEST_COMPLETE = 
Execution.fromTaskReport({
                 context: {
                   __timeColumn: 'v0',
                   __user: 'allowAll',
+                  executionMode: 'async',
                   finalize: false,
                   finalizeAggregations: false,
                   groupByEnableMultiValueUnnesting: false,
                   maxNumTasks: 2,
                   maxParseExceptions: 0,
-                  queryId: '5aa683e2-a6ee-4655-a834-a643e91055b1',
+                  queryId: '93a855fa-c35a-48df-b596-6bc98eed1101',
                   scanSignature:
                     
'[{"name":"agent_type","type":"STRING"},{"name":"v0","type":"LONG"}]',
                   sqlInsertSegmentGranularity: '{"type":"all"}',
-                  sqlQueryId: '5aa683e2-a6ee-4655-a834-a643e91055b1',
+                  sqlQueryId: '93a855fa-c35a-48df-b596-6bc98eed1101',
                   sqlReplaceTimeChunks: 'all',
                 },
                 granularity: {
@@ -178,14 +179,14 @@ export const EXECUTION_INGEST_COMPLETE = 
Execution.fromTaskReport({
           phase: 'FINISHED',
           workerCount: 1,
           partitionCount: 1,
-          startTime: '2023-06-19T05:39:26.711Z',
-          duration: 20483,
+          startTime: '2023-08-01T03:12:59.865Z',
+          duration: 21324,
           sort: true,
         },
         {
           stageNumber: 1,
           definition: {
-            id: '8af42220-2724-4a76-b39f-c2f98df2de69_1',
+            id: 'ad318360-2ccf-4afc-b221-27c8704bf4fe_1',
             input: [
               {
                 type: 'stage',
@@ -250,8 +251,8 @@ export const EXECUTION_INGEST_COMPLETE = 
Execution.fromTaskReport({
           phase: 'FINISHED',
           workerCount: 1,
           partitionCount: 1,
-          startTime: '2023-06-19T05:39:47.166Z',
-          duration: 2381,
+          startTime: '2023-08-01T03:13:21.156Z',
+          duration: 2070,
         },
       ],
       counters: {
@@ -314,120 +315,135 @@ export const EXECUTION_INGEST_COMPLETE = 
Execution.fromTaskReport({
       },
     },
   },
-}).updateWithTaskPayload({
-  task: 'query-5aa683e2-a6ee-4655-a834-a643e91055b1',
-  payload: {
-    type: 'query_controller',
-    id: 'query-5aa683e2-a6ee-4655-a834-a643e91055b1',
-    spec: {
-      query: {
-        queryType: 'scan',
-        dataSource: {
-          type: 'external',
-          inputSource: {
-            type: 'http',
-            uris: 
['https://static.imply.io/example-data/kttm-v2/kttm-v2-2019-08-25.json.gz'],
+})
+  .updateWithTaskPayload({
+    task: 'query-93a855fa-c35a-48df-b596-6bc98eed1101',
+    payload: {
+      type: 'query_controller',
+      id: 'query-93a855fa-c35a-48df-b596-6bc98eed1101',
+      spec: {
+        query: {
+          queryType: 'scan',
+          dataSource: {
+            type: 'external',
+            inputSource: {
+              type: 'http',
+              uris: 
['https://static.imply.io/example-data/kttm-v2/kttm-v2-2019-08-25.json.gz'],
+            },
+            inputFormat: {
+              type: 'json',
+              keepNullColumns: false,
+              assumeNewlineDelimited: false,
+              useJsonNodeReader: false,
+            },
+            signature: [
+              {
+                name: 'timestamp',
+                type: 'STRING',
+              },
+              {
+                name: 'agent_type',
+                type: 'STRING',
+              },
+            ],
           },
-          inputFormat: {
-            type: 'json',
-            keepNullColumns: false,
-            assumeNewlineDelimited: false,
-            useJsonNodeReader: false,
+          intervals: {
+            type: 'intervals',
+            intervals: 
['-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z'],
           },
-          signature: [
+          virtualColumns: [
             {
-              name: 'timestamp',
-              type: 'STRING',
-            },
-            {
-              name: 'agent_type',
-              type: 'STRING',
+              type: 'expression',
+              name: 'v0',
+              expression: 'timestamp_parse("timestamp",null,\'UTC\')',
+              outputType: 'LONG',
             },
           ],
+          resultFormat: 'compactedList',
+          columns: ['agent_type', 'v0'],
+          legacy: false,
+          context: {
+            __user: 'allowAll',
+            executionMode: 'async',
+            finalize: false,
+            finalizeAggregations: false,
+            groupByEnableMultiValueUnnesting: false,
+            maxNumTasks: 2,
+            maxParseExceptions: 0,
+            queryId: '93a855fa-c35a-48df-b596-6bc98eed1101',
+            scanSignature: 
'[{"name":"agent_type","type":"STRING"},{"name":"v0","type":"LONG"}]',
+            sqlInsertSegmentGranularity: '{"type":"all"}',
+            sqlQueryId: '93a855fa-c35a-48df-b596-6bc98eed1101',
+            sqlReplaceTimeChunks: 'all',
+          },
+          granularity: {
+            type: 'all',
+          },
         },
-        intervals: {
-          type: 'intervals',
-          intervals: 
['-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z'],
-        },
-        virtualColumns: [
+        columnMappings: [
+          {
+            queryColumn: 'v0',
+            outputColumn: '__time',
+          },
           {
-            type: 'expression',
-            name: 'v0',
-            expression: 'timestamp_parse("timestamp",null,\'UTC\')',
-            outputType: 'LONG',
+            queryColumn: 'agent_type',
+            outputColumn: 'agent_type',
           },
         ],
-        resultFormat: 'compactedList',
-        columns: ['agent_type', 'v0'],
-        legacy: false,
-        context: {
-          __user: 'allowAll',
-          finalize: false,
-          finalizeAggregations: false,
-          groupByEnableMultiValueUnnesting: false,
-          maxNumTasks: 2,
-          maxParseExceptions: 0,
-          queryId: '5aa683e2-a6ee-4655-a834-a643e91055b1',
-          scanSignature: 
'[{"name":"agent_type","type":"STRING"},{"name":"v0","type":"LONG"}]',
-          sqlInsertSegmentGranularity: '{"type":"all"}',
-          sqlQueryId: '5aa683e2-a6ee-4655-a834-a643e91055b1',
-          sqlReplaceTimeChunks: 'all',
+        destination: {
+          type: 'dataSource',
+          dataSource: 'kttm_simple',
+          segmentGranularity: {
+            type: 'all',
+          },
+          replaceTimeChunks: 
['-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z'],
         },
-        granularity: {
-          type: 'all',
+        assignmentStrategy: 'max',
+        tuningConfig: {
+          maxNumWorkers: 1,
+          maxRowsInMemory: 100000,
+          rowsPerSegment: 3000000,
         },
       },
-      columnMappings: [
-        {
-          queryColumn: 'v0',
-          outputColumn: '__time',
-        },
-        {
-          queryColumn: 'agent_type',
-          outputColumn: 'agent_type',
-        },
-      ],
-      destination: {
-        type: 'dataSource',
-        dataSource: 'kttm_simple',
-        segmentGranularity: {
-          type: 'all',
-        },
-        replaceTimeChunks: 
['-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z'],
+      sqlQuery:
+        'REPLACE INTO "kttm_simple" OVERWRITE ALL\nSELECT\n  
TIME_PARSE("timestamp") AS "__time",\n  "agent_type"\nFROM TABLE(\n  EXTERN(\n  
  
\'{"type":"http","uris":["https://static.imply.io/example-data/kttm-v2/kttm-v2-2019-08-25.json.gz"]}\',\n
    \'{"type":"json"}\'\n  )\n) EXTEND ("timestamp" VARCHAR, "agent_type" 
VARCHAR)\nPARTITIONED BY ALL TIME',
+      sqlQueryContext: {
+        finalizeAggregations: false,
+        sqlQueryId: '93a855fa-c35a-48df-b596-6bc98eed1101',
+        groupByEnableMultiValueUnnesting: false,
+        sqlInsertSegmentGranularity: '{"type":"all"}',
+        maxNumTasks: 2,
+        sqlReplaceTimeChunks: 'all',
+        executionMode: 'async',
+        queryId: '93a855fa-c35a-48df-b596-6bc98eed1101',
       },
-      assignmentStrategy: 'max',
-      tuningConfig: {
-        maxNumWorkers: 1,
-        maxRowsInMemory: 100000,
-        rowsPerSegment: 3000000,
+      sqlResultsContext: {
+        timeZone: 'UTC',
+        serializeComplexValues: true,
+        stringifyArrays: true,
+      },
+      sqlTypeNames: ['TIMESTAMP', 'VARCHAR'],
+      nativeTypeNames: ['LONG', 'STRING'],
+      context: {
+        forceTimeChunkLock: true,
+        useLineageBasedSegmentAllocation: true,
+      },
+      groupId: 'query-93a855fa-c35a-48df-b596-6bc98eed1101',
+      dataSource: 'kttm_simple',
+      resource: {
+        availabilityGroup: 'query-93a855fa-c35a-48df-b596-6bc98eed1101',
+        requiredCapacity: 1,
       },
     },
-    sqlQuery:
-      'REPLACE INTO "kttm_simple" OVERWRITE ALL\nSELECT\n  
TIME_PARSE("timestamp") AS "__time",\n  "agent_type"\nFROM TABLE(\n  EXTERN(\n  
  
\'{"type":"http","uris":["https://static.imply.io/example-data/kttm-v2/kttm-v2-2019-08-25.json.gz"]}\',\n
    \'{"type":"json"}\'\n  )\n) EXTEND ("timestamp" VARCHAR, "agent_type" 
VARCHAR)\nPARTITIONED BY ALL TIME',
-    sqlQueryContext: {
-      finalizeAggregations: false,
-      sqlQueryId: '5aa683e2-a6ee-4655-a834-a643e91055b1',
-      groupByEnableMultiValueUnnesting: false,
-      sqlInsertSegmentGranularity: '{"type":"all"}',
-      maxNumTasks: 2,
-      sqlReplaceTimeChunks: 'all',
-      queryId: '5aa683e2-a6ee-4655-a834-a643e91055b1',
-    },
-    sqlResultsContext: {
-      timeZone: 'UTC',
-      serializeComplexValues: true,
-      stringifyArrays: true,
-    },
-    sqlTypeNames: ['TIMESTAMP', 'VARCHAR'],
-    context: {
-      forceTimeChunkLock: true,
-      useLineageBasedSegmentAllocation: true,
-    },
-    groupId: 'query-5aa683e2-a6ee-4655-a834-a643e91055b1',
-    dataSource: 'kttm_simple',
-    resource: {
-      availabilityGroup: 'query-5aa683e2-a6ee-4655-a834-a643e91055b1',
-      requiredCapacity: 1,
+  })
+  .updateWithAsyncStatus({
+    queryId: 'query-93a855fa-c35a-48df-b596-6bc98eed1101',
+    state: 'SUCCESS',
+    createdAt: '2023-08-01T03:12:50.121Z',
+    durationMs: 37657,
+    result: {
+      numTotalRows: 465346,
+      totalSizeInBytes: 0,
+      dataSource: 'kttm_simple',
     },
-  },
-});
+  });
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 15715a1a0e..9d58a7e558 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
@@ -44,12 +44,12 @@ PARTITIONED BY DAY
 export const EXECUTION_INGEST_ERROR = Execution.fromTaskReport({
   multiStageQuery: {
     type: 'multiStageQuery',
-    taskId: 'query-8f889312-e989-4b4c-9895-485a1fe796d3',
+    taskId: 'query-af8a263d-213f-418e-ad8d-37d55beff59b',
     payload: {
       status: {
         status: 'FAILED',
         errorReport: {
-          taskId: 'query-8f889312-e989-4b4c-9895-485a1fe796d3-worker0_0',
+          taskId: 'query-af8a263d-213f-418e-ad8d-37d55beff59b-worker0_0',
           host: 'localhost',
           error: {
             errorCode: 'TooManyWarnings',
@@ -60,7 +60,7 @@ export const EXECUTION_INGEST_ERROR = 
Execution.fromTaskReport({
         },
         warnings: [
           {
-            taskId: 'query-8f889312-e989-4b4c-9895-485a1fe796d3-worker0_0',
+            taskId: 'query-af8a263d-213f-418e-ad8d-37d55beff59b-worker0_0',
             host: 'localhost:8101',
             stageNumber: 0,
             error: {
@@ -69,10 +69,10 @@ export const EXECUTION_INGEST_ERROR = 
Execution.fromTaskReport({
                 'Unable to parse row [] (Path: 
https://static.imply.io/example-data/kttm-with-issues/kttm-blank-lines.json, 
Record: 3, Line: 3)',
             },
             exceptionStackTrace:
-              'org.apache.druid.java.util.common.parsers.ParseException: 
Unable to parse row [] (Path: 
https://static.imply.io/example-data/kttm-with-issues/kttm-blank-lines.json, 
Record: 3, Line: 3)\n\tat 
org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:79)\n\tat
 
org.apache.druid.java.util.common.parsers.CloseableIterator$2.findNextIteratorIfNecessary(CloseableIterator.java:74)\n\tat
 org.apache.druid.java.util.common.parsers.CloseableI [...]
+              'org.apache.druid.java.util.common.parsers.ParseException: 
Unable to parse row [] (Path: 
https://static.imply.io/example-data/kttm-with-issues/kttm-blank-lines.json, 
Record: 3, Line: 3)\n\tat 
org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:79)\n\tat
 
org.apache.druid.java.util.common.parsers.CloseableIterator$2.findNextIteratorIfNecessary(CloseableIterator.java:74)\n\tat
 org.apache.druid.java.util.common.parsers.CloseableI [...]
           },
           {
-            taskId: 'query-8f889312-e989-4b4c-9895-485a1fe796d3-worker0_0',
+            taskId: 'query-af8a263d-213f-418e-ad8d-37d55beff59b-worker0_0',
             host: 'localhost:8101',
             stageNumber: 0,
             error: {
@@ -81,11 +81,11 @@ export const EXECUTION_INGEST_ERROR = 
Execution.fromTaskReport({
                 'Unable to parse row [] (Path: 
https://static.imply.io/example-data/kttm-with-issues/kttm-blank-lines.json, 
Record: 6, Line: 7)',
             },
             exceptionStackTrace:
-              'org.apache.druid.java.util.common.parsers.ParseException: 
Unable to parse row [] (Path: 
https://static.imply.io/example-data/kttm-with-issues/kttm-blank-lines.json, 
Record: 6, Line: 7)\n\tat 
org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:79)\n\tat
 
org.apache.druid.java.util.common.parsers.CloseableIterator$2.findNextIteratorIfNecessary(CloseableIterator.java:74)\n\tat
 org.apache.druid.java.util.common.parsers.CloseableI [...]
+              'org.apache.druid.java.util.common.parsers.ParseException: 
Unable to parse row [] (Path: 
https://static.imply.io/example-data/kttm-with-issues/kttm-blank-lines.json, 
Record: 6, Line: 7)\n\tat 
org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:79)\n\tat
 
org.apache.druid.java.util.common.parsers.CloseableIterator$2.findNextIteratorIfNecessary(CloseableIterator.java:74)\n\tat
 org.apache.druid.java.util.common.parsers.CloseableI [...]
           },
         ],
-        startTime: '2023-06-19T05:37:48.605Z',
-        durationMs: 14760,
+        startTime: '2023-08-01T04:20:24.945Z',
+        durationMs: 14545,
         pendingTasks: 0,
         runningTasks: 2,
       },
@@ -93,7 +93,7 @@ export const EXECUTION_INGEST_ERROR = 
Execution.fromTaskReport({
         {
           stageNumber: 0,
           definition: {
-            id: 'd337a3d8-e361-4795-8eaa-97ced72d9a7b_0',
+            id: 'f635e36d-6b90-4b74-ad5e-a179c99f0ddc_0',
             input: [
               {
                 type: 'external',
@@ -168,16 +168,17 @@ export const EXECUTION_INGEST_ERROR = 
Execution.fromTaskReport({
                 context: {
                   __timeColumn: 'v0',
                   __user: 'allowAll',
+                  executionMode: 'async',
                   finalize: false,
                   finalizeAggregations: false,
                   groupByEnableMultiValueUnnesting: false,
                   maxNumTasks: 2,
                   maxParseExceptions: 2,
-                  queryId: '8f889312-e989-4b4c-9895-485a1fe796d3',
+                  queryId: 'af8a263d-213f-418e-ad8d-37d55beff59b',
                   scanSignature:
                     
'[{"name":"agent_type","type":"STRING"},{"name":"v0","type":"LONG"}]',
                   sqlInsertSegmentGranularity: '"DAY"',
-                  sqlQueryId: '8f889312-e989-4b4c-9895-485a1fe796d3',
+                  sqlQueryId: 'af8a263d-213f-418e-ad8d-37d55beff59b',
                   sqlReplaceTimeChunks: 'all',
                 },
                 granularity: {
@@ -225,14 +226,14 @@ export const EXECUTION_INGEST_ERROR = 
Execution.fromTaskReport({
           },
           phase: 'FAILED',
           workerCount: 1,
-          startTime: '2023-06-19T05:37:48.952Z',
-          duration: 14412,
+          startTime: '2023-08-01T04:20:25.296Z',
+          duration: 14193,
           sort: true,
         },
         {
           stageNumber: 1,
           definition: {
-            id: 'd337a3d8-e361-4795-8eaa-97ced72d9a7b_1',
+            id: 'f635e36d-6b90-4b74-ad5e-a179c99f0ddc_1',
             input: [
               {
                 type: 'stage',
@@ -334,10 +335,10 @@ export const EXECUTION_INGEST_ERROR = 
Execution.fromTaskReport({
     },
   },
 }).updateWithTaskPayload({
-  task: 'query-8f889312-e989-4b4c-9895-485a1fe796d3',
+  task: 'query-af8a263d-213f-418e-ad8d-37d55beff59b',
   payload: {
     type: 'query_controller',
-    id: 'query-8f889312-e989-4b4c-9895-485a1fe796d3',
+    id: 'query-af8a263d-213f-418e-ad8d-37d55beff59b',
     spec: {
       query: {
         queryType: 'scan',
@@ -381,15 +382,16 @@ export const EXECUTION_INGEST_ERROR = 
Execution.fromTaskReport({
         legacy: false,
         context: {
           __user: 'allowAll',
+          executionMode: 'async',
           finalize: false,
           finalizeAggregations: false,
           groupByEnableMultiValueUnnesting: false,
           maxNumTasks: 2,
           maxParseExceptions: 2,
-          queryId: '8f889312-e989-4b4c-9895-485a1fe796d3',
+          queryId: 'af8a263d-213f-418e-ad8d-37d55beff59b',
           scanSignature: 
'[{"name":"agent_type","type":"STRING"},{"name":"v0","type":"LONG"}]',
           sqlInsertSegmentGranularity: '"DAY"',
-          sqlQueryId: '8f889312-e989-4b4c-9895-485a1fe796d3',
+          sqlQueryId: 'af8a263d-213f-418e-ad8d-37d55beff59b',
           sqlReplaceTimeChunks: 'all',
         },
         granularity: {
@@ -424,12 +426,13 @@ export const EXECUTION_INGEST_ERROR = 
Execution.fromTaskReport({
     sqlQueryContext: {
       maxParseExceptions: 2,
       finalizeAggregations: false,
-      sqlQueryId: '8f889312-e989-4b4c-9895-485a1fe796d3',
+      sqlQueryId: 'af8a263d-213f-418e-ad8d-37d55beff59b',
       groupByEnableMultiValueUnnesting: false,
       sqlInsertSegmentGranularity: '"DAY"',
       maxNumTasks: 2,
       sqlReplaceTimeChunks: 'all',
-      queryId: '8f889312-e989-4b4c-9895-485a1fe796d3',
+      executionMode: 'async',
+      queryId: 'af8a263d-213f-418e-ad8d-37d55beff59b',
     },
     sqlResultsContext: {
       timeZone: 'UTC',
@@ -437,14 +440,15 @@ export const EXECUTION_INGEST_ERROR = 
Execution.fromTaskReport({
       stringifyArrays: true,
     },
     sqlTypeNames: ['TIMESTAMP', 'VARCHAR'],
+    nativeTypeNames: ['LONG', 'STRING'],
     context: {
       forceTimeChunkLock: true,
       useLineageBasedSegmentAllocation: true,
     },
-    groupId: 'query-8f889312-e989-4b4c-9895-485a1fe796d3',
+    groupId: 'query-af8a263d-213f-418e-ad8d-37d55beff59b',
     dataSource: 'kttm-blank-lines',
     resource: {
-      availabilityGroup: 'query-8f889312-e989-4b4c-9895-485a1fe796d3',
+      availabilityGroup: 'query-af8a263d-213f-418e-ad8d-37d55beff59b',
       requiredCapacity: 1,
     },
   },
diff --git a/web-console/src/druid-models/execution/execution.spec.ts 
b/web-console/src/druid-models/execution/execution.spec.ts
index dd7833c635..a6b55db680 100644
--- a/web-console/src/druid-models/execution/execution.spec.ts
+++ b/web-console/src/druid-models/execution/execution.spec.ts
@@ -42,10 +42,14 @@ describe('Execution', () => {
                 "useLineageBasedSegmentAllocation": true,
               },
               "dataSource": "kttm_simple",
-              "groupId": "query-5aa683e2-a6ee-4655-a834-a643e91055b1",
-              "id": "query-5aa683e2-a6ee-4655-a834-a643e91055b1",
+              "groupId": "query-93a855fa-c35a-48df-b596-6bc98eed1101",
+              "id": "query-93a855fa-c35a-48df-b596-6bc98eed1101",
+              "nativeTypeNames": Array [
+                "LONG",
+                "STRING",
+              ],
               "resource": Object {
-                "availabilityGroup": 
"query-5aa683e2-a6ee-4655-a834-a643e91055b1",
+                "availabilityGroup": 
"query-93a855fa-c35a-48df-b596-6bc98eed1101",
                 "requiredCapacity": 1,
               },
               "spec": Object {
@@ -77,15 +81,16 @@ describe('Execution', () => {
                   ],
                   "context": Object {
                     "__user": "allowAll",
+                    "executionMode": "async",
                     "finalize": false,
                     "finalizeAggregations": false,
                     "groupByEnableMultiValueUnnesting": false,
                     "maxNumTasks": 2,
                     "maxParseExceptions": 0,
-                    "queryId": "5aa683e2-a6ee-4655-a834-a643e91055b1",
+                    "queryId": "93a855fa-c35a-48df-b596-6bc98eed1101",
                     "scanSignature": 
"[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]",
                     "sqlInsertSegmentGranularity": "{\\"type\\":\\"all\\"}",
-                    "sqlQueryId": "5aa683e2-a6ee-4655-a834-a643e91055b1",
+                    "sqlQueryId": "93a855fa-c35a-48df-b596-6bc98eed1101",
                     "sqlReplaceTimeChunks": "all",
                   },
                   "dataSource": Object {
@@ -152,12 +157,13 @@ describe('Execution', () => {
         ) EXTEND (\\"timestamp\\" VARCHAR, \\"agent_type\\" VARCHAR)
         PARTITIONED BY ALL TIME",
               "sqlQueryContext": Object {
+                "executionMode": "async",
                 "finalizeAggregations": false,
                 "groupByEnableMultiValueUnnesting": false,
                 "maxNumTasks": 2,
-                "queryId": "5aa683e2-a6ee-4655-a834-a643e91055b1",
+                "queryId": "93a855fa-c35a-48df-b596-6bc98eed1101",
                 "sqlInsertSegmentGranularity": "{\\"type\\":\\"all\\"}",
-                "sqlQueryId": "5aa683e2-a6ee-4655-a834-a643e91055b1",
+                "sqlQueryId": "93a855fa-c35a-48df-b596-6bc98eed1101",
                 "sqlReplaceTimeChunks": "all",
               },
               "sqlResultsContext": Object {
@@ -171,11 +177,12 @@ describe('Execution', () => {
               ],
               "type": "query_controller",
             },
-            "task": "query-5aa683e2-a6ee-4655-a834-a643e91055b1",
+            "task": "query-93a855fa-c35a-48df-b596-6bc98eed1101",
           },
           "capacityInfo": undefined,
           "destination": Object {
             "dataSource": "kttm_simple",
+            "numTotalRows": 465346,
             "replaceTimeChunks": Array [
               "-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z",
             ],
@@ -184,10 +191,11 @@ describe('Execution', () => {
             },
             "type": "dataSource",
           },
-          "duration": 23170,
+          "destinationPages": undefined,
+          "duration": 23699,
           "engine": "sql-msq-task",
           "error": undefined,
-          "id": "query-5aa683e2-a6ee-4655-a834-a643e91055b1",
+          "id": "query-93a855fa-c35a-48df-b596-6bc98eed1101",
           "nativeQuery": Object {
             "columns": Array [
               "agent_type",
@@ -195,15 +203,16 @@ describe('Execution', () => {
             ],
             "context": Object {
               "__user": "allowAll",
+              "executionMode": "async",
               "finalize": false,
               "finalizeAggregations": false,
               "groupByEnableMultiValueUnnesting": false,
               "maxNumTasks": 2,
               "maxParseExceptions": 0,
-              "queryId": "5aa683e2-a6ee-4655-a834-a643e91055b1",
+              "queryId": "93a855fa-c35a-48df-b596-6bc98eed1101",
               "scanSignature": 
"[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]",
               "sqlInsertSegmentGranularity": "{\\"type\\":\\"all\\"}",
-              "sqlQueryId": "5aa683e2-a6ee-4655-a834-a643e91055b1",
+              "sqlQueryId": "93a855fa-c35a-48df-b596-6bc98eed1101",
               "sqlReplaceTimeChunks": "all",
             },
             "dataSource": Object {
@@ -253,6 +262,7 @@ describe('Execution', () => {
             ],
           },
           "queryContext": Object {
+            "executionMode": "async",
             "finalizeAggregations": false,
             "groupByEnableMultiValueUnnesting": false,
             "maxNumTasks": 2,
@@ -357,7 +367,7 @@ describe('Execution', () => {
             "stages": Array [
               Object {
                 "definition": Object {
-                  "id": "8af42220-2724-4a76-b39f-c2f98df2de69_0",
+                  "id": "ad318360-2ccf-4afc-b221-27c8704bf4fe_0",
                   "input": Array [
                     Object {
                       "inputFormat": Object {
@@ -395,15 +405,16 @@ describe('Execution', () => {
                       "context": Object {
                         "__timeColumn": "v0",
                         "__user": "allowAll",
+                        "executionMode": "async",
                         "finalize": false,
                         "finalizeAggregations": false,
                         "groupByEnableMultiValueUnnesting": false,
                         "maxNumTasks": 2,
                         "maxParseExceptions": 0,
-                        "queryId": "5aa683e2-a6ee-4655-a834-a643e91055b1",
+                        "queryId": "93a855fa-c35a-48df-b596-6bc98eed1101",
                         "scanSignature": 
"[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]",
                         "sqlInsertSegmentGranularity": 
"{\\"type\\":\\"all\\"}",
-                        "sqlQueryId": "5aa683e2-a6ee-4655-a834-a643e91055b1",
+                        "sqlQueryId": "93a855fa-c35a-48df-b596-6bc98eed1101",
                         "sqlReplaceTimeChunks": "all",
                       },
                       "dataSource": Object {
@@ -482,17 +493,17 @@ describe('Execution', () => {
                     },
                   ],
                 },
-                "duration": 20483,
+                "duration": 21324,
                 "partitionCount": 1,
                 "phase": "FINISHED",
                 "sort": true,
                 "stageNumber": 0,
-                "startTime": "2023-06-19T05:39:26.711Z",
+                "startTime": "2023-08-01T03:12:59.865Z",
                 "workerCount": 1,
               },
               Object {
                 "definition": Object {
-                  "id": "8af42220-2724-4a76-b39f-c2f98df2de69_1",
+                  "id": "ad318360-2ccf-4afc-b221-27c8704bf4fe_1",
                   "input": Array [
                     Object {
                       "stage": 0,
@@ -558,16 +569,16 @@ describe('Execution', () => {
                   },
                   "signature": Array [],
                 },
-                "duration": 2381,
+                "duration": 2070,
                 "partitionCount": 1,
                 "phase": "FINISHED",
                 "stageNumber": 1,
-                "startTime": "2023-06-19T05:39:47.166Z",
+                "startTime": "2023-08-01T03:13:21.156Z",
                 "workerCount": 1,
               },
             ],
           },
-          "startTime": 2023-06-19T05:39:26.377Z,
+          "startTime": 2023-08-01T03:12:59.527Z,
           "status": "SUCCESS",
           "usageInfo": Object {
             "pendingTasks": 0,
@@ -586,8 +597,16 @@ describe('Execution', () => {
           "_payload": undefined,
           "capacityInfo": undefined,
           "destination": Object {
+            "numTotalRows": 2,
             "type": "taskReport",
           },
+          "destinationPages": Array [
+            Object {
+              "id": 0,
+              "numRows": 2,
+              "sizeInBytes": 116,
+            },
+          ],
           "duration": 29168,
           "engine": "sql-msq-task",
           "error": undefined,
@@ -640,6 +659,7 @@ describe('Execution', () => {
           "_payload": undefined,
           "capacityInfo": undefined,
           "destination": undefined,
+          "destinationPages": undefined,
           "duration": 11217,
           "engine": "sql-msq-task",
           "error": Object {
diff --git a/web-console/src/druid-models/execution/execution.ts 
b/web-console/src/druid-models/execution/execution.ts
index b64ba19548..8f30711b28 100644
--- a/web-console/src/druid-models/execution/execution.ts
+++ b/web-console/src/druid-models/execution/execution.ts
@@ -31,12 +31,7 @@ import type { DruidEngine } from 
'../druid-engine/druid-engine';
 import { validDruidEngine } from '../druid-engine/druid-engine';
 import type { QueryContext } from '../query-context/query-context';
 import { Stages } from '../stages/stages';
-import type {
-  MsqTaskPayloadResponse,
-  MsqTaskReportResponse,
-  TaskStatus,
-  TaskStatusResponse,
-} from '../task/task';
+import type { MsqTaskPayloadResponse, MsqTaskReportResponse, TaskStatus } from 
'../task/task';
 
 const IGNORE_CONTEXT_KEYS = [
   '__asyncIdentity__',
@@ -70,12 +65,19 @@ export interface ExecutionError {
   exceptionStackTrace?: string;
 }
 
-type ExecutionDestination =
+export type ExecutionDestination =
   | {
       type: 'taskReport';
+      numTotalRows?: number;
     }
-  | { type: 'dataSource'; dataSource: string; numRows?: number; loaded?: 
boolean }
-  | { type: 'download' };
+  | { type: 'durableStorage'; numTotalRows?: number }
+  | { type: 'dataSource'; dataSource: string; numTotalRows?: number; loaded?: 
boolean };
+
+export interface ExecutionDestinationPage {
+  id: number;
+  numRows: number;
+  sizeInBytes: number;
+}
 
 export type ExecutionStatus = 'RUNNING' | 'FAILED' | 'SUCCESS';
 
@@ -174,6 +176,7 @@ export interface ExecutionValue {
   usageInfo?: UsageInfo;
   stages?: Stages;
   destination?: ExecutionDestination;
+  destinationPages?: ExecutionDestinationPage[];
   result?: QueryResult;
   error?: ExecutionError;
   warnings?: ExecutionError[];
@@ -214,38 +217,6 @@ export class Execution {
     }
   }
 
-  static fromTaskSubmit(
-    taskSubmitResult: { state: any; taskId: string; error: any },
-    sqlQuery?: string,
-    queryContext?: QueryContext,
-  ): Execution {
-    const status = Execution.normalizeTaskStatus(taskSubmitResult.state);
-    return new Execution({
-      engine: 'sql-msq-task',
-      id: taskSubmitResult.taskId,
-      status: taskSubmitResult.error ? 'FAILED' : status,
-      sqlQuery,
-      queryContext,
-      error: taskSubmitResult.error
-        ? {
-            error: {
-              errorCode: 'AsyncError',
-              errorMessage: JSON.stringify(taskSubmitResult.error),
-            },
-          }
-        : status === 'FAILED'
-        ? {
-            error: {
-              errorCode: 'UnknownError',
-              errorMessage:
-                'Execution failed, there is no detail information, and there 
is no error in the status response',
-            },
-          }
-        : undefined,
-      destination: undefined,
-    });
-  }
-
   static fromAsyncStatus(
     asyncSubmitResult: AsyncStatusResponse,
     sqlQuery?: string,
@@ -286,49 +257,18 @@ export class Execution {
             ? {
                 type: 'dataSource',
                 dataSource: result.dataSource,
-                numRows: result.numTotalRows,
+                numTotalRows: result.numTotalRows,
               }
             : {
                 type: 'taskReport',
+                numTotalRows: result.numTotalRows,
               }
           : undefined,
+      destinationPages: result?.pages,
       result: queryResult,
     });
   }
 
-  static fromTaskStatus(
-    taskStatus: TaskStatusResponse,
-    sqlQuery?: string,
-    queryContext?: QueryContext,
-  ): Execution {
-    const status = Execution.normalizeTaskStatus(taskStatus.status.status);
-    return new Execution({
-      engine: 'sql-msq-task',
-      id: taskStatus.task,
-      status: taskStatus.status.error ? 'FAILED' : status,
-      usageInfo: getUsageInfoFromStatusPayload(taskStatus.status),
-      sqlQuery,
-      queryContext,
-      error: taskStatus.status.error
-        ? {
-            error: {
-              errorCode: 'AsyncError',
-              errorMessage: JSON.stringify(taskStatus.status.error),
-            },
-          }
-        : status === 'FAILED'
-        ? {
-            error: {
-              errorCode: 'UnknownError',
-              errorMessage:
-                'Execution failed, there is no detail information, and there 
is no error in the status response',
-            },
-          }
-        : undefined,
-      destination: undefined,
-    });
-  }
-
   static fromTaskReport(taskReport: MsqTaskReportResponse): Execution {
     // Must have status set for a valid report
     const id = deepGet(taskReport, 'multiStageQuery.taskId');
@@ -424,6 +364,7 @@ export class Execution {
   public readonly usageInfo?: UsageInfo;
   public readonly stages?: Stages;
   public readonly destination?: ExecutionDestination;
+  public readonly destinationPages?: ExecutionDestinationPage[];
   public readonly result?: QueryResult;
   public readonly error?: ExecutionError;
   public readonly warnings?: ExecutionError[];
@@ -444,6 +385,7 @@ export class Execution {
     this.usageInfo = value.usageInfo;
     this.stages = value.stages;
     this.destination = value.destination;
+    this.destinationPages = value.destinationPages;
     this.result = value.result;
     this.error = value.error;
     this.warnings = nonEmptyArray(value.warnings) ? value.warnings : undefined;
@@ -465,6 +407,7 @@ export class Execution {
       usageInfo: this.usageInfo,
       stages: this.stages,
       destination: this.destination,
+      destinationPages: this.destinationPages,
       result: this.result,
       error: this.error,
       warnings: this.warnings,
@@ -494,6 +437,13 @@ export class Execution {
     });
   }
 
+  public changeDestinationPages(destinationPages: ExecutionDestinationPage[]): 
Execution {
+    return new Execution({
+      ...this.valueOf(),
+      destinationPages,
+    });
+  }
+
   public changeResult(result: QueryResult): Execution {
     return new Execution({
       ...this.valueOf(),
@@ -514,7 +464,7 @@ export class Execution {
     value._payload = taskPayload;
     value.destination = {
       ...value.destination,
-      ...(deepGet(taskPayload, 'payload.spec.destination') || {}),
+      ...deepGet(taskPayload, 'payload.spec.destination'),
     };
     value.nativeQuery = deepGet(taskPayload, 'payload.spec.query');
 
@@ -530,18 +480,23 @@ export class Execution {
     return ret;
   }
 
-  public attachErrorFromStatus(status: any): Execution {
-    const errorMsg = deepGet(status, 'status.errorMsg');
+  public updateWithAsyncStatus(statusPayload: AsyncStatusResponse): Execution {
+    const value = this.valueOf();
+
+    const { pages, numTotalRows } = statusPayload.result || {};
 
-    return new Execution({
-      ...this.valueOf(),
-      error: {
-        error: {
-          errorCode: 'UnknownError',
-          errorMessage: errorMsg,
-        },
-      },
-    });
+    if (!value.destinationPages && pages) {
+      value.destinationPages = pages;
+    }
+
+    if (typeof value.destination?.numTotalRows !== 'number' && typeof 
numTotalRows === 'number') {
+      value.destination = {
+        ...(value.destination || { type: 'taskReport' }),
+        numTotalRows,
+      };
+    }
+
+    return new Execution(value);
   }
 
   public markDestinationDatasourceLoaded(): Execution {
@@ -588,20 +543,8 @@ export class Execution {
     return destination.dataSource;
   }
 
-  public getIngestNumRows(): number | undefined {
-    const { destination, stages } = this;
-
-    if (destination?.type === 'dataSource' && typeof destination.numRows === 
'number') {
-      return destination.numRows;
-    }
-
-    const lastStage = stages?.getLastStage();
-    if (stages && lastStage && lastStage.definition.processor.type === 
'segmentGenerator') {
-      // Assume input0 since we know the segmentGenerator will only ever have 
one stage input
-      return stages.getTotalCounterForStage(lastStage, 'input0', 'rows');
-    }
-
-    return;
+  public getOutputNumTotalRows(): number | undefined {
+    return this.destination?.numTotalRows;
   }
 
   public isSuccessfulInsert(): boolean {
diff --git a/web-console/src/druid-models/task/task.ts 
b/web-console/src/druid-models/task/task.ts
index 65dbb62c06..8f6f456ccb 100644
--- a/web-console/src/druid-models/task/task.ts
+++ b/web-console/src/druid-models/task/task.ts
@@ -66,6 +66,7 @@ export interface MsqTaskPayloadResponse {
     sqlQueryContext: Record<string, any>;
     sqlResultsContext: Record<string, any>;
     sqlTypeNames: string[];
+    nativeTypeNames: string[];
     context: Record<string, any>;
     groupId: string;
     dataSource: string;
diff --git a/web-console/src/helpers/execution/sql-task-execution.ts 
b/web-console/src/helpers/execution/sql-task-execution.ts
index 79e28e486d..1643e8a076 100644
--- a/web-console/src/helpers/execution/sql-task-execution.ts
+++ b/web-console/src/helpers/execution/sql-task-execution.ts
@@ -19,7 +19,7 @@
 import { L, QueryResult } from '@druid-toolkit/query';
 import type { AxiosResponse, CancelToken } from 'axios';
 
-import type { AsyncStatusResponse, QueryContext } from '../../druid-models';
+import type { AsyncStatusResponse, MsqTaskPayloadResponse, QueryContext } from 
'../../druid-models';
 import { Execution } from '../../druid-models';
 import { Api } from '../../singletons';
 import {
@@ -48,7 +48,6 @@ function ensureExecutionModeIsSet(context: QueryContext | 
undefined): QueryConte
 export interface SubmitTaskQueryOptions {
   query: string | Record<string, any>;
   context?: QueryContext;
-  skipResults?: boolean;
   prefixLines?: number;
   cancelToken?: CancelToken;
   preserveOnTermination?: boolean;
@@ -58,15 +57,7 @@ export interface SubmitTaskQueryOptions {
 export async function submitTaskQuery(
   options: SubmitTaskQueryOptions,
 ): Promise<Execution | IntermediateQueryState<Execution>> {
-  const {
-    query,
-    context,
-    skipResults,
-    prefixLines,
-    cancelToken,
-    preserveOnTermination,
-    onSubmitted,
-  } = options;
+  const { query, context, prefixLines, cancelToken, preserveOnTermination, 
onSubmitted } = options;
 
   let sqlQuery: string;
   let jsonQuery: Record<string, any>;
@@ -123,10 +114,6 @@ export async function submitTaskQuery(
     onSubmitted(execution.id);
   }
 
-  if (skipResults) {
-    execution = execution.changeDestination({ type: 'download' });
-  }
-
   execution = await updateExecutionWithDatasourceLoadedIfNeeded(execution, 
cancelToken);
 
   if (execution.isFullyComplete()) return execution;
@@ -178,7 +165,7 @@ export async function updateExecutionWithTaskIfNeeded(
 
 export async function getTaskExecution(
   id: string,
-  taskPayloadOverride?: { payload: any; task: string },
+  taskPayloadOverride?: MsqTaskPayloadResponse,
   cancelToken?: CancelToken,
 ): Promise<Execution> {
   const encodedId = Api.encodePath(id);
@@ -221,7 +208,7 @@ export async function getTaskExecution(
     execution = Execution.fromAsyncStatus(statusResp.data);
   }
 
-  let taskPayload: any = taskPayloadOverride;
+  let taskPayload = taskPayloadOverride;
   if (USE_TASK_PAYLOAD && !taskPayload) {
     try {
       taskPayload = (
@@ -237,6 +224,18 @@ export async function getTaskExecution(
     execution = execution.updateWithTaskPayload(taskPayload);
   }
 
+  // Still have to pull the destination page info from the async status
+  if (execution.status === 'SUCCESS' && !execution.destinationPages) {
+    const statusResp = await Api.instance.get<AsyncStatusResponse>(
+      `/druid/v2/sql/statements/${encodedId}`,
+      {
+        cancelToken,
+      },
+    );
+
+    execution = execution.updateWithAsyncStatus(statusResp.data);
+  }
+
   if (execution.hasPotentiallyStuckStage()) {
     const capacityInfo = await maybeGetClusterCapacity();
     if (capacityInfo) {
diff --git a/web-console/src/utils/download-query-detail-archive.ts 
b/web-console/src/utils/download-query-detail-archive.ts
index cb4dcb818b..124b322c5f 100644
--- a/web-console/src/utils/download-query-detail-archive.ts
+++ b/web-console/src/utils/download-query-detail-archive.ts
@@ -22,35 +22,39 @@ import { Api } from '../singletons';
 
 import { downloadFile } from './download';
 
-interface QueryDetailArchive {
+export interface QueryDetailArchive {
   id: string;
   detailArchiveVersion: number;
   status?: any;
   reports?: any;
   payload?: any;
+  statementsStatus?: any;
   serverStatus?: any;
 }
 
 export async function downloadQueryDetailArchive(id: string) {
+  const encodedId = Api.encodePath(id);
   const profile: QueryDetailArchive = {
     id,
     detailArchiveVersion: 2,
   };
 
   try {
-    profile.status = (
-      await 
Api.instance.get(`/druid/indexer/v1/task/${Api.encodePath(id)}/status`)
-    ).data;
+    profile.status = (await 
Api.instance.get(`/druid/indexer/v1/task/${encodedId}/status`)).data;
   } catch {}
 
   try {
-    profile.reports = (
-      await 
Api.instance.get(`/druid/indexer/v1/task/${Api.encodePath(id)}/reports`)
-    ).data;
+    profile.reports = (await 
Api.instance.get(`/druid/indexer/v1/task/${encodedId}/reports`)).data;
+  } catch {}
+
+  try {
+    profile.payload = (await 
Api.instance.get(`/druid/indexer/v1/task/${encodedId}`)).data;
   } catch {}
 
   try {
-    profile.payload = (await 
Api.instance.get(`/druid/indexer/v1/task/${Api.encodePath(id)}`)).data;
+    profile.statementsStatus = (
+      await Api.instance.get(`/druid/v2/sql/statements/${encodedId}`)
+    ).data;
   } catch {}
 
   try {
diff --git a/web-console/src/utils/download.ts 
b/web-console/src/utils/download.ts
index 03705ee3b8..41c558edc0 100644
--- a/web-console/src/utils/download.ts
+++ b/web-console/src/utils/download.ts
@@ -22,6 +22,23 @@ import * as JSONBig from 'json-bigint-native';
 
 import { stringifyValue } from './general';
 
+export function downloadUrl(url: string, filename: string) {
+  // Create a link and set the URL using `createObjectURL`
+  const link = document.createElement('a');
+  link.style.display = 'none';
+  link.href = url;
+  link.download = filename;
+  document.body.appendChild(link);
+  link.click();
+
+  // To make this work on Firefox we need to wait
+  // a little while before removing it.
+  setTimeout(() => {
+    if (!link.parentNode) return;
+    link.parentNode.removeChild(link);
+  }, 0);
+}
+
 export function formatForFormat(s: null | string | number | Date, format: 
'csv' | 'tsv'): string {
   // stringify and remove line break
   const str = stringifyValue(s).replace(/(?:\r\n|\r|\n)/g, ' ');
diff --git a/web-console/src/utils/general.tsx 
b/web-console/src/utils/general.tsx
index e9b8e8f2c1..4d821ae053 100644
--- a/web-console/src/utils/general.tsx
+++ b/web-console/src/utils/general.tsx
@@ -302,8 +302,21 @@ export function formatDurationHybrid(ms: NumberLike): 
string {
   }
 }
 
+function pluralize(word: string): string {
+  // Ignoring irregular plurals.
+  if (/(s|x|z|ch|sh)$/.test(word)) {
+    return word + 'es';
+  } else if (/([^aeiou])y$/.test(word)) {
+    return word.slice(0, -1) + 'ies';
+  } else if (/(f|fe)$/.test(word)) {
+    return word.replace(/fe?$/, 'ves');
+  } else {
+    return word + 's';
+  }
+}
+
 export function pluralIfNeeded(n: NumberLike, singular: string, plural?: 
string): string {
-  if (!plural) plural = singular + 's';
+  if (!plural) plural = pluralize(singular);
   return `${formatInteger(n)} ${n === 1 ? singular : plural}`;
 }
 
diff --git a/web-console/src/druid-models/async-query/async-query.ts 
b/web-console/src/views/workbench-view/destination-pages-dialog/destination-pages-dialog.scss
similarity index 62%
copy from web-console/src/druid-models/async-query/async-query.ts
copy to 
web-console/src/views/workbench-view/destination-pages-dialog/destination-pages-dialog.scss
index 401569f558..430cdc300d 100644
--- a/web-console/src/druid-models/async-query/async-query.ts
+++ 
b/web-console/src/views/workbench-view/destination-pages-dialog/destination-pages-dialog.scss
@@ -16,22 +16,11 @@
  * limitations under the License.
  */
 
-import type { ErrorResponse } from '../../utils';
+@import '../../../variables';
 
-export type AsyncState = 'ACCEPTED' | 'RUNNING' | 'SUCCESS' | 'FAILED';
-
-export interface AsyncStatusResponse {
-  queryId: string;
-  state: AsyncState;
-  createdAt: string;
-  durationMs: number;
-  schema?: { name: string; type: string; nativeType: string }[];
-  result?: {
-    dataSource: string;
-    sampleRecords: any[][];
-    numTotalRows: number;
-    totalSizeInBytes: number;
-    pages: any[];
-  };
-  errorDetails?: ErrorResponse;
+.destination-pages-dialog {
+  &.#{$bp-ns}-dialog {
+    min-width: 700px;
+    min-height: 480px;
+  }
 }
diff --git 
a/web-console/src/views/workbench-view/destination-pages-dialog/destination-pages-dialog.tsx
 
b/web-console/src/views/workbench-view/destination-pages-dialog/destination-pages-dialog.tsx
new file mode 100644
index 0000000000..1e40aefdd0
--- /dev/null
+++ 
b/web-console/src/views/workbench-view/destination-pages-dialog/destination-pages-dialog.tsx
@@ -0,0 +1,49 @@
+/*
+ * 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 type { Execution } from '../../../druid-models';
+import { DestinationPagesPane } from 
'../destination-pages-pane/destination-pages-pane';
+
+import './destination-pages-dialog.scss';
+
+export interface DestinationPagesDialogProps {
+  execution: Execution;
+  onClose(): void;
+}
+
+export const DestinationPagesDialog = React.memo(function 
DestinationPagesDialog(
+  props: DestinationPagesDialogProps,
+) {
+  const { execution, onClose } = props;
+
+  return (
+    <Dialog className="destination-pages-dialog" isOpen onClose={onClose} 
title="Result pages">
+      <div className={Classes.DIALOG_BODY}>
+        <DestinationPagesPane execution={execution} />
+      </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/destination-pages-pane/destination-pages-pane.tsx
 
b/web-console/src/views/workbench-view/destination-pages-pane/destination-pages-pane.tsx
new file mode 100644
index 0000000000..20f68a8af6
--- /dev/null
+++ 
b/web-console/src/views/workbench-view/destination-pages-pane/destination-pages-pane.tsx
@@ -0,0 +1,135 @@
+/*
+ * 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 { AnchorButton, Button } from '@blueprintjs/core';
+import { IconNames } from '@blueprintjs/icons';
+import React from 'react';
+import ReactTable from 'react-table';
+
+import type { Execution } from '../../../druid-models';
+import { Api, UrlBaser } from '../../../singletons';
+import {
+  clamp,
+  downloadUrl,
+  formatBytes,
+  formatInteger,
+  pluralIfNeeded,
+  wait,
+} from '../../../utils';
+
+const MAX_DETAIL_ROWS = 20;
+
+interface DestinationPagesPaneProps {
+  execution: Execution;
+}
+
+export const DestinationPagesPane = React.memo(function DestinationPagesPane(
+  props: DestinationPagesPaneProps,
+) {
+  const { execution } = props;
+  const destination = execution.destination;
+  const pages = execution.destinationPages;
+  if (!pages) return null;
+  const id = Api.encodePath(execution.id);
+
+  const numTotalRows = destination?.numTotalRows;
+
+  function getPageUrl(pageIndex: number) {
+    return 
UrlBaser.base(`/druid/v2/sql/statements/${id}/results?page=${pageIndex}`);
+  }
+
+  function getPageFilename(pageIndex: number) {
+    return `${id}_page${pageIndex}.jsonl`;
+  }
+
+  async function downloadAllPages() {
+    if (!pages) return;
+    for (let i = 0; i < pages.length; i++) {
+      downloadUrl(getPageUrl(i), getPageFilename(i));
+      await wait(100);
+    }
+  }
+
+  return (
+    <div className="execution-details-pane">
+      <p>
+        {`${
+          typeof numTotalRows === 'number' ? pluralIfNeeded(numTotalRows, 
'row') : 'Results'
+        } have been written to ${pluralIfNeeded(pages.length, 'page')}. `}
+        {pages.length > 1 && (
+          <Button
+            icon={IconNames.DOWNLOAD}
+            text={`Download all data (as ${pluralIfNeeded(pages.length, 
'file')})`}
+            minimal
+            onClick={() => void downloadAllPages()}
+          />
+        )}
+      </p>
+      <ReactTable
+        className="padded-header"
+        data={pages}
+        loading={false}
+        sortable
+        defaultSorted={[{ id: 'id', desc: false }]}
+        defaultPageSize={clamp(pages.length, 1, MAX_DETAIL_ROWS)}
+        showPagination={pages.length > MAX_DETAIL_ROWS}
+        columns={[
+          {
+            Header: 'Page number',
+            id: 'id',
+            accessor: 'id',
+            className: 'padded',
+            width: 100,
+          },
+          {
+            Header: 'Number of rows',
+            id: 'numRows',
+            accessor: 'numRows',
+            className: 'padded',
+            width: 200,
+            Cell: ({ value }) => formatInteger(value),
+          },
+          {
+            Header: 'Size',
+            id: 'sizeInBytes',
+            accessor: 'sizeInBytes',
+            className: 'padded',
+            width: 200,
+            Cell: ({ value }) => formatBytes(value),
+          },
+          {
+            Header: '',
+            id: 'download',
+            accessor: 'id',
+            sortable: false,
+            width: 300,
+            Cell: ({ value }) => (
+              <AnchorButton
+                icon={IconNames.DOWNLOAD}
+                text="download .jsonl"
+                minimal
+                href={getPageUrl(value)}
+                download={getPageFilename(value)}
+              />
+            ),
+          },
+        ]}
+      />
+    </div>
+  );
+});
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 fb8c7a0eff..6f3c23bee0 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
@@ -23,6 +23,7 @@ exports[`ExecutionDetailsPane matches snapshot no init tab 
1`] = `
         "label": "Native query",
       },
       undefined,
+      undefined,
       Object {
         "icon": "error",
         "id": "error",
@@ -38,7 +39,10 @@ exports[`ExecutionDetailsPane matches snapshot no init tab 
1`] = `
 >
   <div>
     <p>
-      General info for query-8f889312-e989-4b4c-9895-485a1fe796d3 ingesting 
into "kttm-blank-lines"
+      General info for query-af8a263d-213f-418e-ad8d-37d55beff59b ingesting 
into "kttm-blank-lines"
+    </p>
+    <p>
+      Results written to dataSource
     </p>
     <Memo(ExecutionErrorPane)
       execution={
@@ -50,10 +54,14 @@ exports[`ExecutionDetailsPane matches snapshot no init tab 
1`] = `
                 "useLineageBasedSegmentAllocation": true,
               },
               "dataSource": "kttm-blank-lines",
-              "groupId": "query-8f889312-e989-4b4c-9895-485a1fe796d3",
-              "id": "query-8f889312-e989-4b4c-9895-485a1fe796d3",
+              "groupId": "query-af8a263d-213f-418e-ad8d-37d55beff59b",
+              "id": "query-af8a263d-213f-418e-ad8d-37d55beff59b",
+              "nativeTypeNames": Array [
+                "LONG",
+                "STRING",
+              ],
               "resource": Object {
-                "availabilityGroup": 
"query-8f889312-e989-4b4c-9895-485a1fe796d3",
+                "availabilityGroup": 
"query-af8a263d-213f-418e-ad8d-37d55beff59b",
                 "requiredCapacity": 1,
               },
               "spec": Object {
@@ -83,15 +91,16 @@ exports[`ExecutionDetailsPane matches snapshot no init tab 
1`] = `
                   ],
                   "context": Object {
                     "__user": "allowAll",
+                    "executionMode": "async",
                     "finalize": false,
                     "finalizeAggregations": false,
                     "groupByEnableMultiValueUnnesting": false,
                     "maxNumTasks": 2,
                     "maxParseExceptions": 2,
-                    "queryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+                    "queryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
                     "scanSignature": 
"[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]",
                     "sqlInsertSegmentGranularity": "\\"DAY\\"",
-                    "sqlQueryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+                    "sqlQueryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
                     "sqlReplaceTimeChunks": "all",
                   },
                   "dataSource": Object {
@@ -158,13 +167,14 @@ FROM TABLE(
 ) EXTEND (\\"timestamp\\" VARCHAR, \\"agent_type\\" VARCHAR)
 PARTITIONED BY DAY",
               "sqlQueryContext": Object {
+                "executionMode": "async",
                 "finalizeAggregations": false,
                 "groupByEnableMultiValueUnnesting": false,
                 "maxNumTasks": 2,
                 "maxParseExceptions": 2,
-                "queryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+                "queryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
                 "sqlInsertSegmentGranularity": "\\"DAY\\"",
-                "sqlQueryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+                "sqlQueryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
                 "sqlReplaceTimeChunks": "all",
               },
               "sqlResultsContext": Object {
@@ -178,7 +188,7 @@ PARTITIONED BY DAY",
               ],
               "type": "query_controller",
             },
-            "task": "query-8f889312-e989-4b4c-9895-485a1fe796d3",
+            "task": "query-af8a263d-213f-418e-ad8d-37d55beff59b",
           },
           "capacityInfo": undefined,
           "destination": Object {
@@ -189,7 +199,8 @@ PARTITIONED BY DAY",
             "segmentGranularity": "DAY",
             "type": "dataSource",
           },
-          "duration": 14760,
+          "destinationPages": undefined,
+          "duration": 14545,
           "engine": "sql-msq-task",
           "error": Object {
             "error": Object {
@@ -199,9 +210,9 @@ PARTITIONED BY DAY",
               "rootErrorCode": "CannotParseExternalData",
             },
             "host": "localhost",
-            "taskId": "query-8f889312-e989-4b4c-9895-485a1fe796d3-worker0_0",
+            "taskId": "query-af8a263d-213f-418e-ad8d-37d55beff59b-worker0_0",
           },
-          "id": "query-8f889312-e989-4b4c-9895-485a1fe796d3",
+          "id": "query-af8a263d-213f-418e-ad8d-37d55beff59b",
           "nativeQuery": Object {
             "columns": Array [
               "agent_type",
@@ -209,15 +220,16 @@ PARTITIONED BY DAY",
             ],
             "context": Object {
               "__user": "allowAll",
+              "executionMode": "async",
               "finalize": false,
               "finalizeAggregations": false,
               "groupByEnableMultiValueUnnesting": false,
               "maxNumTasks": 2,
               "maxParseExceptions": 2,
-              "queryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+              "queryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
               "scanSignature": 
"[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]",
               "sqlInsertSegmentGranularity": "\\"DAY\\"",
-              "sqlQueryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+              "sqlQueryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
               "sqlReplaceTimeChunks": "all",
             },
             "dataSource": Object {
@@ -267,6 +279,7 @@ PARTITIONED BY DAY",
             ],
           },
           "queryContext": Object {
+            "executionMode": "async",
             "finalizeAggregations": false,
             "groupByEnableMultiValueUnnesting": false,
             "maxNumTasks": 2,
@@ -337,7 +350,7 @@ PARTITIONED BY DAY",
             "stages": Array [
               Object {
                 "definition": Object {
-                  "id": "d337a3d8-e361-4795-8eaa-97ced72d9a7b_0",
+                  "id": "f635e36d-6b90-4b74-ad5e-a179c99f0ddc_0",
                   "input": Array [
                     Object {
                       "inputFormat": Object {
@@ -375,15 +388,16 @@ PARTITIONED BY DAY",
                       "context": Object {
                         "__timeColumn": "v0",
                         "__user": "allowAll",
+                        "executionMode": "async",
                         "finalize": false,
                         "finalizeAggregations": false,
                         "groupByEnableMultiValueUnnesting": false,
                         "maxNumTasks": 2,
                         "maxParseExceptions": 2,
-                        "queryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+                        "queryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
                         "scanSignature": 
"[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]",
                         "sqlInsertSegmentGranularity": "\\"DAY\\"",
-                        "sqlQueryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+                        "sqlQueryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
                         "sqlReplaceTimeChunks": "all",
                       },
                       "dataSource": Object {
@@ -471,16 +485,16 @@ PARTITIONED BY DAY",
                     },
                   ],
                 },
-                "duration": 14412,
+                "duration": 14193,
                 "phase": "FAILED",
                 "sort": true,
                 "stageNumber": 0,
-                "startTime": "2023-06-19T05:37:48.952Z",
+                "startTime": "2023-08-01T04:20:25.296Z",
                 "workerCount": 1,
               },
               Object {
                 "definition": Object {
-                  "id": "d337a3d8-e361-4795-8eaa-97ced72d9a7b_1",
+                  "id": "f635e36d-6b90-4b74-ad5e-a179c99f0ddc_1",
                   "input": Array [
                     Object {
                       "stage": 0,
@@ -550,7 +564,7 @@ PARTITIONED BY DAY",
               },
             ],
           },
-          "startTime": 2023-06-19T05:37:48.605Z,
+          "startTime": 2023-08-01T04:20:24.945Z,
           "status": "FAILED",
           "usageInfo": Object {
             "pendingTasks": 0,
@@ -567,19 +581,20 @@ PARTITIONED BY DAY",
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$2.findNextIteratorIfNecessary(CloseableIterator.java:74)
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$2.next(CloseableIterator.java:108)
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$1.next(CloseableIterator.java:52)
-       at 
org.apache.druid.msq.input.external.ExternalInputSliceReader$1$1.hasNext(ExternalInputSliceReader.java:183)
+       at 
org.apache.druid.msq.input.external.ExternalSegment$1$1.hasNext(ExternalSegment.java:95)
        at 
org.apache.druid.java.util.common.guava.BaseSequence$1.next(BaseSequence.java:115)
        at org.apache.druid.segment.RowWalker.advance(RowWalker.java:70)
        at 
org.apache.druid.segment.RowBasedCursor.advanceUninterruptibly(RowBasedCursor.java:110)
        at 
org.apache.druid.segment.RowBasedCursor.advance(RowBasedCursor.java:103)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeeded(ScanQueryFrameProcessor.java:246)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runWithSegment(ScanQueryFrameProcessor.java:173)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeeded(ScanQueryFrameProcessor.java:275)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeededWithExceptionHandling(ScanQueryFrameProcessor.java:242)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runWithSegment(ScanQueryFrameProcessor.java:181)
        at 
org.apache.druid.msq.querykit.BaseLeafFrameProcessor.runIncrementally(BaseLeafFrameProcessor.java:159)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runIncrementally(ScanQueryFrameProcessor.java:138)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runIncrementally(ScanQueryFrameProcessor.java:146)
        at 
org.apache.druid.frame.processor.FrameProcessors$1FrameProcessorWithBaggage.runIncrementally(FrameProcessors.java:75)
        at 
org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.runProcessorNow(FrameProcessorExecutor.java:229)
        at 
org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.run(FrameProcessorExecutor.java:137)
-       at org.apache.druid.msq.exec.WorkerImpl$1$2.run(WorkerImpl.java:820)
+       at org.apache.druid.msq.exec.WorkerImpl$1$2.run(WorkerImpl.java:837)
        at 
java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at 
org.apache.druid.query.PrioritizedListenableFutureTask.run(PrioritizedExecutorService.java:251)
@@ -596,11 +611,11 @@ Caused by: 
com.fasterxml.jackson.databind.exc.MismatchedInputException: No conte
        at 
org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:75)
        at 
org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:48)
        at 
org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:71)
-       ... 22 more
+       ... 23 more
 ",
               "host": "localhost:8101",
               "stageNumber": 0,
-              "taskId": "query-8f889312-e989-4b4c-9895-485a1fe796d3-worker0_0",
+              "taskId": "query-af8a263d-213f-418e-ad8d-37d55beff59b-worker0_0",
             },
             Object {
               "error": Object {
@@ -612,19 +627,20 @@ Caused by: 
com.fasterxml.jackson.databind.exc.MismatchedInputException: No conte
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$2.findNextIteratorIfNecessary(CloseableIterator.java:74)
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$2.next(CloseableIterator.java:108)
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$1.next(CloseableIterator.java:52)
-       at 
org.apache.druid.msq.input.external.ExternalInputSliceReader$1$1.hasNext(ExternalInputSliceReader.java:183)
+       at 
org.apache.druid.msq.input.external.ExternalSegment$1$1.hasNext(ExternalSegment.java:95)
        at 
org.apache.druid.java.util.common.guava.BaseSequence$1.next(BaseSequence.java:115)
        at org.apache.druid.segment.RowWalker.advance(RowWalker.java:70)
        at 
org.apache.druid.segment.RowBasedCursor.advanceUninterruptibly(RowBasedCursor.java:110)
        at 
org.apache.druid.segment.RowBasedCursor.advance(RowBasedCursor.java:103)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeeded(ScanQueryFrameProcessor.java:246)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runWithSegment(ScanQueryFrameProcessor.java:173)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeeded(ScanQueryFrameProcessor.java:275)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeededWithExceptionHandling(ScanQueryFrameProcessor.java:242)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runWithSegment(ScanQueryFrameProcessor.java:181)
        at 
org.apache.druid.msq.querykit.BaseLeafFrameProcessor.runIncrementally(BaseLeafFrameProcessor.java:159)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runIncrementally(ScanQueryFrameProcessor.java:138)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runIncrementally(ScanQueryFrameProcessor.java:146)
        at 
org.apache.druid.frame.processor.FrameProcessors$1FrameProcessorWithBaggage.runIncrementally(FrameProcessors.java:75)
        at 
org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.runProcessorNow(FrameProcessorExecutor.java:229)
        at 
org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.run(FrameProcessorExecutor.java:137)
-       at org.apache.druid.msq.exec.WorkerImpl$1$2.run(WorkerImpl.java:820)
+       at org.apache.druid.msq.exec.WorkerImpl$1$2.run(WorkerImpl.java:837)
        at 
java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at 
org.apache.druid.query.PrioritizedListenableFutureTask.run(PrioritizedExecutorService.java:251)
@@ -641,11 +657,11 @@ Caused by: 
com.fasterxml.jackson.databind.exc.MismatchedInputException: No conte
        at 
org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:75)
        at 
org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:48)
        at 
org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:71)
-       ... 22 more
+       ... 23 more
 ",
               "host": "localhost:8101",
               "stageNumber": 0,
-              "taskId": "query-8f889312-e989-4b4c-9895-485a1fe796d3-worker0_0",
+              "taskId": "query-af8a263d-213f-418e-ad8d-37d55beff59b-worker0_0",
             },
           ],
         }
@@ -661,10 +677,14 @@ Caused by: 
com.fasterxml.jackson.databind.exc.MismatchedInputException: No conte
                 "useLineageBasedSegmentAllocation": true,
               },
               "dataSource": "kttm-blank-lines",
-              "groupId": "query-8f889312-e989-4b4c-9895-485a1fe796d3",
-              "id": "query-8f889312-e989-4b4c-9895-485a1fe796d3",
+              "groupId": "query-af8a263d-213f-418e-ad8d-37d55beff59b",
+              "id": "query-af8a263d-213f-418e-ad8d-37d55beff59b",
+              "nativeTypeNames": Array [
+                "LONG",
+                "STRING",
+              ],
               "resource": Object {
-                "availabilityGroup": 
"query-8f889312-e989-4b4c-9895-485a1fe796d3",
+                "availabilityGroup": 
"query-af8a263d-213f-418e-ad8d-37d55beff59b",
                 "requiredCapacity": 1,
               },
               "spec": Object {
@@ -694,15 +714,16 @@ Caused by: 
com.fasterxml.jackson.databind.exc.MismatchedInputException: No conte
                   ],
                   "context": Object {
                     "__user": "allowAll",
+                    "executionMode": "async",
                     "finalize": false,
                     "finalizeAggregations": false,
                     "groupByEnableMultiValueUnnesting": false,
                     "maxNumTasks": 2,
                     "maxParseExceptions": 2,
-                    "queryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+                    "queryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
                     "scanSignature": 
"[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]",
                     "sqlInsertSegmentGranularity": "\\"DAY\\"",
-                    "sqlQueryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+                    "sqlQueryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
                     "sqlReplaceTimeChunks": "all",
                   },
                   "dataSource": Object {
@@ -769,13 +790,14 @@ FROM TABLE(
 ) EXTEND (\\"timestamp\\" VARCHAR, \\"agent_type\\" VARCHAR)
 PARTITIONED BY DAY",
               "sqlQueryContext": Object {
+                "executionMode": "async",
                 "finalizeAggregations": false,
                 "groupByEnableMultiValueUnnesting": false,
                 "maxNumTasks": 2,
                 "maxParseExceptions": 2,
-                "queryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+                "queryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
                 "sqlInsertSegmentGranularity": "\\"DAY\\"",
-                "sqlQueryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+                "sqlQueryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
                 "sqlReplaceTimeChunks": "all",
               },
               "sqlResultsContext": Object {
@@ -789,7 +811,7 @@ PARTITIONED BY DAY",
               ],
               "type": "query_controller",
             },
-            "task": "query-8f889312-e989-4b4c-9895-485a1fe796d3",
+            "task": "query-af8a263d-213f-418e-ad8d-37d55beff59b",
           },
           "capacityInfo": undefined,
           "destination": Object {
@@ -800,7 +822,8 @@ PARTITIONED BY DAY",
             "segmentGranularity": "DAY",
             "type": "dataSource",
           },
-          "duration": 14760,
+          "destinationPages": undefined,
+          "duration": 14545,
           "engine": "sql-msq-task",
           "error": Object {
             "error": Object {
@@ -810,9 +833,9 @@ PARTITIONED BY DAY",
               "rootErrorCode": "CannotParseExternalData",
             },
             "host": "localhost",
-            "taskId": "query-8f889312-e989-4b4c-9895-485a1fe796d3-worker0_0",
+            "taskId": "query-af8a263d-213f-418e-ad8d-37d55beff59b-worker0_0",
           },
-          "id": "query-8f889312-e989-4b4c-9895-485a1fe796d3",
+          "id": "query-af8a263d-213f-418e-ad8d-37d55beff59b",
           "nativeQuery": Object {
             "columns": Array [
               "agent_type",
@@ -820,15 +843,16 @@ PARTITIONED BY DAY",
             ],
             "context": Object {
               "__user": "allowAll",
+              "executionMode": "async",
               "finalize": false,
               "finalizeAggregations": false,
               "groupByEnableMultiValueUnnesting": false,
               "maxNumTasks": 2,
               "maxParseExceptions": 2,
-              "queryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+              "queryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
               "scanSignature": 
"[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]",
               "sqlInsertSegmentGranularity": "\\"DAY\\"",
-              "sqlQueryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+              "sqlQueryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
               "sqlReplaceTimeChunks": "all",
             },
             "dataSource": Object {
@@ -878,6 +902,7 @@ PARTITIONED BY DAY",
             ],
           },
           "queryContext": Object {
+            "executionMode": "async",
             "finalizeAggregations": false,
             "groupByEnableMultiValueUnnesting": false,
             "maxNumTasks": 2,
@@ -948,7 +973,7 @@ PARTITIONED BY DAY",
             "stages": Array [
               Object {
                 "definition": Object {
-                  "id": "d337a3d8-e361-4795-8eaa-97ced72d9a7b_0",
+                  "id": "f635e36d-6b90-4b74-ad5e-a179c99f0ddc_0",
                   "input": Array [
                     Object {
                       "inputFormat": Object {
@@ -986,15 +1011,16 @@ PARTITIONED BY DAY",
                       "context": Object {
                         "__timeColumn": "v0",
                         "__user": "allowAll",
+                        "executionMode": "async",
                         "finalize": false,
                         "finalizeAggregations": false,
                         "groupByEnableMultiValueUnnesting": false,
                         "maxNumTasks": 2,
                         "maxParseExceptions": 2,
-                        "queryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+                        "queryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
                         "scanSignature": 
"[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]",
                         "sqlInsertSegmentGranularity": "\\"DAY\\"",
-                        "sqlQueryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+                        "sqlQueryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
                         "sqlReplaceTimeChunks": "all",
                       },
                       "dataSource": Object {
@@ -1082,16 +1108,16 @@ PARTITIONED BY DAY",
                     },
                   ],
                 },
-                "duration": 14412,
+                "duration": 14193,
                 "phase": "FAILED",
                 "sort": true,
                 "stageNumber": 0,
-                "startTime": "2023-06-19T05:37:48.952Z",
+                "startTime": "2023-08-01T04:20:25.296Z",
                 "workerCount": 1,
               },
               Object {
                 "definition": Object {
-                  "id": "d337a3d8-e361-4795-8eaa-97ced72d9a7b_1",
+                  "id": "f635e36d-6b90-4b74-ad5e-a179c99f0ddc_1",
                   "input": Array [
                     Object {
                       "stage": 0,
@@ -1161,7 +1187,7 @@ PARTITIONED BY DAY",
               },
             ],
           },
-          "startTime": 2023-06-19T05:37:48.605Z,
+          "startTime": 2023-08-01T04:20:24.945Z,
           "status": "FAILED",
           "usageInfo": Object {
             "pendingTasks": 0,
@@ -1178,19 +1204,20 @@ PARTITIONED BY DAY",
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$2.findNextIteratorIfNecessary(CloseableIterator.java:74)
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$2.next(CloseableIterator.java:108)
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$1.next(CloseableIterator.java:52)
-       at 
org.apache.druid.msq.input.external.ExternalInputSliceReader$1$1.hasNext(ExternalInputSliceReader.java:183)
+       at 
org.apache.druid.msq.input.external.ExternalSegment$1$1.hasNext(ExternalSegment.java:95)
        at 
org.apache.druid.java.util.common.guava.BaseSequence$1.next(BaseSequence.java:115)
        at org.apache.druid.segment.RowWalker.advance(RowWalker.java:70)
        at 
org.apache.druid.segment.RowBasedCursor.advanceUninterruptibly(RowBasedCursor.java:110)
        at 
org.apache.druid.segment.RowBasedCursor.advance(RowBasedCursor.java:103)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeeded(ScanQueryFrameProcessor.java:246)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runWithSegment(ScanQueryFrameProcessor.java:173)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeeded(ScanQueryFrameProcessor.java:275)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeededWithExceptionHandling(ScanQueryFrameProcessor.java:242)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runWithSegment(ScanQueryFrameProcessor.java:181)
        at 
org.apache.druid.msq.querykit.BaseLeafFrameProcessor.runIncrementally(BaseLeafFrameProcessor.java:159)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runIncrementally(ScanQueryFrameProcessor.java:138)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runIncrementally(ScanQueryFrameProcessor.java:146)
        at 
org.apache.druid.frame.processor.FrameProcessors$1FrameProcessorWithBaggage.runIncrementally(FrameProcessors.java:75)
        at 
org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.runProcessorNow(FrameProcessorExecutor.java:229)
        at 
org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.run(FrameProcessorExecutor.java:137)
-       at org.apache.druid.msq.exec.WorkerImpl$1$2.run(WorkerImpl.java:820)
+       at org.apache.druid.msq.exec.WorkerImpl$1$2.run(WorkerImpl.java:837)
        at 
java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at 
org.apache.druid.query.PrioritizedListenableFutureTask.run(PrioritizedExecutorService.java:251)
@@ -1207,11 +1234,11 @@ Caused by: 
com.fasterxml.jackson.databind.exc.MismatchedInputException: No conte
        at 
org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:75)
        at 
org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:48)
        at 
org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:71)
-       ... 22 more
+       ... 23 more
 ",
               "host": "localhost:8101",
               "stageNumber": 0,
-              "taskId": "query-8f889312-e989-4b4c-9895-485a1fe796d3-worker0_0",
+              "taskId": "query-af8a263d-213f-418e-ad8d-37d55beff59b-worker0_0",
             },
             Object {
               "error": Object {
@@ -1223,19 +1250,20 @@ Caused by: 
com.fasterxml.jackson.databind.exc.MismatchedInputException: No conte
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$2.findNextIteratorIfNecessary(CloseableIterator.java:74)
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$2.next(CloseableIterator.java:108)
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$1.next(CloseableIterator.java:52)
-       at 
org.apache.druid.msq.input.external.ExternalInputSliceReader$1$1.hasNext(ExternalInputSliceReader.java:183)
+       at 
org.apache.druid.msq.input.external.ExternalSegment$1$1.hasNext(ExternalSegment.java:95)
        at 
org.apache.druid.java.util.common.guava.BaseSequence$1.next(BaseSequence.java:115)
        at org.apache.druid.segment.RowWalker.advance(RowWalker.java:70)
        at 
org.apache.druid.segment.RowBasedCursor.advanceUninterruptibly(RowBasedCursor.java:110)
        at 
org.apache.druid.segment.RowBasedCursor.advance(RowBasedCursor.java:103)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeeded(ScanQueryFrameProcessor.java:246)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runWithSegment(ScanQueryFrameProcessor.java:173)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeeded(ScanQueryFrameProcessor.java:275)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeededWithExceptionHandling(ScanQueryFrameProcessor.java:242)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runWithSegment(ScanQueryFrameProcessor.java:181)
        at 
org.apache.druid.msq.querykit.BaseLeafFrameProcessor.runIncrementally(BaseLeafFrameProcessor.java:159)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runIncrementally(ScanQueryFrameProcessor.java:138)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runIncrementally(ScanQueryFrameProcessor.java:146)
        at 
org.apache.druid.frame.processor.FrameProcessors$1FrameProcessorWithBaggage.runIncrementally(FrameProcessors.java:75)
        at 
org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.runProcessorNow(FrameProcessorExecutor.java:229)
        at 
org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.run(FrameProcessorExecutor.java:137)
-       at org.apache.druid.msq.exec.WorkerImpl$1$2.run(WorkerImpl.java:820)
+       at org.apache.druid.msq.exec.WorkerImpl$1$2.run(WorkerImpl.java:837)
        at 
java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at 
org.apache.druid.query.PrioritizedListenableFutureTask.run(PrioritizedExecutorService.java:251)
@@ -1252,11 +1280,11 @@ Caused by: 
com.fasterxml.jackson.databind.exc.MismatchedInputException: No conte
        at 
org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:75)
        at 
org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:48)
        at 
org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:71)
-       ... 22 more
+       ... 23 more
 ",
               "host": "localhost:8101",
               "stageNumber": 0,
-              "taskId": "query-8f889312-e989-4b4c-9895-485a1fe796d3-worker0_0",
+              "taskId": "query-af8a263d-213f-418e-ad8d-37d55beff59b-worker0_0",
             },
           ],
         }
@@ -1292,6 +1320,7 @@ exports[`ExecutionDetailsPane matches snapshot with init 
tab 1`] = `
         "label": "Native query",
       },
       undefined,
+      undefined,
       Object {
         "icon": "error",
         "id": "error",
@@ -1315,10 +1344,14 @@ exports[`ExecutionDetailsPane matches snapshot with 
init tab 1`] = `
               "useLineageBasedSegmentAllocation": true,
             },
             "dataSource": "kttm-blank-lines",
-            "groupId": "query-8f889312-e989-4b4c-9895-485a1fe796d3",
-            "id": "query-8f889312-e989-4b4c-9895-485a1fe796d3",
+            "groupId": "query-af8a263d-213f-418e-ad8d-37d55beff59b",
+            "id": "query-af8a263d-213f-418e-ad8d-37d55beff59b",
+            "nativeTypeNames": Array [
+              "LONG",
+              "STRING",
+            ],
             "resource": Object {
-              "availabilityGroup": 
"query-8f889312-e989-4b4c-9895-485a1fe796d3",
+              "availabilityGroup": 
"query-af8a263d-213f-418e-ad8d-37d55beff59b",
               "requiredCapacity": 1,
             },
             "spec": Object {
@@ -1348,15 +1381,16 @@ exports[`ExecutionDetailsPane matches snapshot with 
init tab 1`] = `
                 ],
                 "context": Object {
                   "__user": "allowAll",
+                  "executionMode": "async",
                   "finalize": false,
                   "finalizeAggregations": false,
                   "groupByEnableMultiValueUnnesting": false,
                   "maxNumTasks": 2,
                   "maxParseExceptions": 2,
-                  "queryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+                  "queryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
                   "scanSignature": 
"[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]",
                   "sqlInsertSegmentGranularity": "\\"DAY\\"",
-                  "sqlQueryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+                  "sqlQueryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
                   "sqlReplaceTimeChunks": "all",
                 },
                 "dataSource": Object {
@@ -1423,13 +1457,14 @@ FROM TABLE(
 ) EXTEND (\\"timestamp\\" VARCHAR, \\"agent_type\\" VARCHAR)
 PARTITIONED BY DAY",
             "sqlQueryContext": Object {
+              "executionMode": "async",
               "finalizeAggregations": false,
               "groupByEnableMultiValueUnnesting": false,
               "maxNumTasks": 2,
               "maxParseExceptions": 2,
-              "queryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+              "queryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
               "sqlInsertSegmentGranularity": "\\"DAY\\"",
-              "sqlQueryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+              "sqlQueryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
               "sqlReplaceTimeChunks": "all",
             },
             "sqlResultsContext": Object {
@@ -1443,7 +1478,7 @@ PARTITIONED BY DAY",
             ],
             "type": "query_controller",
           },
-          "task": "query-8f889312-e989-4b4c-9895-485a1fe796d3",
+          "task": "query-af8a263d-213f-418e-ad8d-37d55beff59b",
         },
         "capacityInfo": undefined,
         "destination": Object {
@@ -1454,7 +1489,8 @@ PARTITIONED BY DAY",
           "segmentGranularity": "DAY",
           "type": "dataSource",
         },
-        "duration": 14760,
+        "destinationPages": undefined,
+        "duration": 14545,
         "engine": "sql-msq-task",
         "error": Object {
           "error": Object {
@@ -1464,9 +1500,9 @@ PARTITIONED BY DAY",
             "rootErrorCode": "CannotParseExternalData",
           },
           "host": "localhost",
-          "taskId": "query-8f889312-e989-4b4c-9895-485a1fe796d3-worker0_0",
+          "taskId": "query-af8a263d-213f-418e-ad8d-37d55beff59b-worker0_0",
         },
-        "id": "query-8f889312-e989-4b4c-9895-485a1fe796d3",
+        "id": "query-af8a263d-213f-418e-ad8d-37d55beff59b",
         "nativeQuery": Object {
           "columns": Array [
             "agent_type",
@@ -1474,15 +1510,16 @@ PARTITIONED BY DAY",
           ],
           "context": Object {
             "__user": "allowAll",
+            "executionMode": "async",
             "finalize": false,
             "finalizeAggregations": false,
             "groupByEnableMultiValueUnnesting": false,
             "maxNumTasks": 2,
             "maxParseExceptions": 2,
-            "queryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+            "queryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
             "scanSignature": 
"[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]",
             "sqlInsertSegmentGranularity": "\\"DAY\\"",
-            "sqlQueryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+            "sqlQueryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
             "sqlReplaceTimeChunks": "all",
           },
           "dataSource": Object {
@@ -1532,6 +1569,7 @@ PARTITIONED BY DAY",
           ],
         },
         "queryContext": Object {
+          "executionMode": "async",
           "finalizeAggregations": false,
           "groupByEnableMultiValueUnnesting": false,
           "maxNumTasks": 2,
@@ -1602,7 +1640,7 @@ PARTITIONED BY DAY",
           "stages": Array [
             Object {
               "definition": Object {
-                "id": "d337a3d8-e361-4795-8eaa-97ced72d9a7b_0",
+                "id": "f635e36d-6b90-4b74-ad5e-a179c99f0ddc_0",
                 "input": Array [
                   Object {
                     "inputFormat": Object {
@@ -1640,15 +1678,16 @@ PARTITIONED BY DAY",
                     "context": Object {
                       "__timeColumn": "v0",
                       "__user": "allowAll",
+                      "executionMode": "async",
                       "finalize": false,
                       "finalizeAggregations": false,
                       "groupByEnableMultiValueUnnesting": false,
                       "maxNumTasks": 2,
                       "maxParseExceptions": 2,
-                      "queryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+                      "queryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
                       "scanSignature": 
"[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]",
                       "sqlInsertSegmentGranularity": "\\"DAY\\"",
-                      "sqlQueryId": "8f889312-e989-4b4c-9895-485a1fe796d3",
+                      "sqlQueryId": "af8a263d-213f-418e-ad8d-37d55beff59b",
                       "sqlReplaceTimeChunks": "all",
                     },
                     "dataSource": Object {
@@ -1736,16 +1775,16 @@ PARTITIONED BY DAY",
                   },
                 ],
               },
-              "duration": 14412,
+              "duration": 14193,
               "phase": "FAILED",
               "sort": true,
               "stageNumber": 0,
-              "startTime": "2023-06-19T05:37:48.952Z",
+              "startTime": "2023-08-01T04:20:25.296Z",
               "workerCount": 1,
             },
             Object {
               "definition": Object {
-                "id": "d337a3d8-e361-4795-8eaa-97ced72d9a7b_1",
+                "id": "f635e36d-6b90-4b74-ad5e-a179c99f0ddc_1",
                 "input": Array [
                   Object {
                     "stage": 0,
@@ -1815,7 +1854,7 @@ PARTITIONED BY DAY",
             },
           ],
         },
-        "startTime": 2023-06-19T05:37:48.605Z,
+        "startTime": 2023-08-01T04:20:24.945Z,
         "status": "FAILED",
         "usageInfo": Object {
           "pendingTasks": 0,
@@ -1832,19 +1871,20 @@ PARTITIONED BY DAY",
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$2.findNextIteratorIfNecessary(CloseableIterator.java:74)
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$2.next(CloseableIterator.java:108)
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$1.next(CloseableIterator.java:52)
-       at 
org.apache.druid.msq.input.external.ExternalInputSliceReader$1$1.hasNext(ExternalInputSliceReader.java:183)
+       at 
org.apache.druid.msq.input.external.ExternalSegment$1$1.hasNext(ExternalSegment.java:95)
        at 
org.apache.druid.java.util.common.guava.BaseSequence$1.next(BaseSequence.java:115)
        at org.apache.druid.segment.RowWalker.advance(RowWalker.java:70)
        at 
org.apache.druid.segment.RowBasedCursor.advanceUninterruptibly(RowBasedCursor.java:110)
        at 
org.apache.druid.segment.RowBasedCursor.advance(RowBasedCursor.java:103)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeeded(ScanQueryFrameProcessor.java:246)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runWithSegment(ScanQueryFrameProcessor.java:173)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeeded(ScanQueryFrameProcessor.java:275)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeededWithExceptionHandling(ScanQueryFrameProcessor.java:242)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runWithSegment(ScanQueryFrameProcessor.java:181)
        at 
org.apache.druid.msq.querykit.BaseLeafFrameProcessor.runIncrementally(BaseLeafFrameProcessor.java:159)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runIncrementally(ScanQueryFrameProcessor.java:138)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runIncrementally(ScanQueryFrameProcessor.java:146)
        at 
org.apache.druid.frame.processor.FrameProcessors$1FrameProcessorWithBaggage.runIncrementally(FrameProcessors.java:75)
        at 
org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.runProcessorNow(FrameProcessorExecutor.java:229)
        at 
org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.run(FrameProcessorExecutor.java:137)
-       at org.apache.druid.msq.exec.WorkerImpl$1$2.run(WorkerImpl.java:820)
+       at org.apache.druid.msq.exec.WorkerImpl$1$2.run(WorkerImpl.java:837)
        at 
java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at 
org.apache.druid.query.PrioritizedListenableFutureTask.run(PrioritizedExecutorService.java:251)
@@ -1861,11 +1901,11 @@ Caused by: 
com.fasterxml.jackson.databind.exc.MismatchedInputException: No conte
        at 
org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:75)
        at 
org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:48)
        at 
org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:71)
-       ... 22 more
+       ... 23 more
 ",
             "host": "localhost:8101",
             "stageNumber": 0,
-            "taskId": "query-8f889312-e989-4b4c-9895-485a1fe796d3-worker0_0",
+            "taskId": "query-af8a263d-213f-418e-ad8d-37d55beff59b-worker0_0",
           },
           Object {
             "error": Object {
@@ -1877,19 +1917,20 @@ Caused by: 
com.fasterxml.jackson.databind.exc.MismatchedInputException: No conte
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$2.findNextIteratorIfNecessary(CloseableIterator.java:74)
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$2.next(CloseableIterator.java:108)
        at 
org.apache.druid.java.util.common.parsers.CloseableIterator$1.next(CloseableIterator.java:52)
-       at 
org.apache.druid.msq.input.external.ExternalInputSliceReader$1$1.hasNext(ExternalInputSliceReader.java:183)
+       at 
org.apache.druid.msq.input.external.ExternalSegment$1$1.hasNext(ExternalSegment.java:95)
        at 
org.apache.druid.java.util.common.guava.BaseSequence$1.next(BaseSequence.java:115)
        at org.apache.druid.segment.RowWalker.advance(RowWalker.java:70)
        at 
org.apache.druid.segment.RowBasedCursor.advanceUninterruptibly(RowBasedCursor.java:110)
        at 
org.apache.druid.segment.RowBasedCursor.advance(RowBasedCursor.java:103)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeeded(ScanQueryFrameProcessor.java:246)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runWithSegment(ScanQueryFrameProcessor.java:173)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeeded(ScanQueryFrameProcessor.java:275)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeededWithExceptionHandling(ScanQueryFrameProcessor.java:242)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runWithSegment(ScanQueryFrameProcessor.java:181)
        at 
org.apache.druid.msq.querykit.BaseLeafFrameProcessor.runIncrementally(BaseLeafFrameProcessor.java:159)
-       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runIncrementally(ScanQueryFrameProcessor.java:138)
+       at 
org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runIncrementally(ScanQueryFrameProcessor.java:146)
        at 
org.apache.druid.frame.processor.FrameProcessors$1FrameProcessorWithBaggage.runIncrementally(FrameProcessors.java:75)
        at 
org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.runProcessorNow(FrameProcessorExecutor.java:229)
        at 
org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.run(FrameProcessorExecutor.java:137)
-       at org.apache.druid.msq.exec.WorkerImpl$1$2.run(WorkerImpl.java:820)
+       at org.apache.druid.msq.exec.WorkerImpl$1$2.run(WorkerImpl.java:837)
        at 
java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at 
org.apache.druid.query.PrioritizedListenableFutureTask.run(PrioritizedExecutorService.java:251)
@@ -1906,11 +1947,11 @@ Caused by: 
com.fasterxml.jackson.databind.exc.MismatchedInputException: No conte
        at 
org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:75)
        at 
org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:48)
        at 
org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:71)
-       ... 22 more
+       ... 23 more
 ",
             "host": "localhost:8101",
             "stageNumber": 0,
-            "taskId": "query-8f889312-e989-4b4c-9895-485a1fe796d3-worker0_0",
+            "taskId": "query-af8a263d-213f-418e-ad8d-37d55beff59b-worker0_0",
           },
         ],
       }
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 a2d67233e6..91914e1f3b 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
@@ -15,6 +15,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 import { IconNames } from '@blueprintjs/icons';
 import { T } from '@druid-toolkit/query';
 import * as JSONBig from 'json-bigint-native';
@@ -22,6 +23,8 @@ import React, { useState } from 'react';
 
 import { FancyTabPane } from '../../../components';
 import type { Execution } from '../../../druid-models';
+import { pluralIfNeeded } from '../../../utils';
+import { DestinationPagesPane } from 
'../destination-pages-pane/destination-pages-pane';
 import { ExecutionErrorPane } from 
'../execution-error-pane/execution-error-pane';
 import { ExecutionStagesPane } from 
'../execution-stages-pane/execution-stages-pane';
 import { ExecutionWarningsPane } from 
'../execution-warnings-pane/execution-warnings-pane';
@@ -30,7 +33,14 @@ import { ResultTablePane } from 
'../result-table-pane/result-table-pane';
 
 import './execution-details-pane.scss';
 
-export type ExecutionDetailsTab = 'general' | 'sql' | 'native' | 'result' | 
'error' | 'warnings';
+export type ExecutionDetailsTab =
+  | 'general'
+  | 'sql'
+  | 'native'
+  | 'result'
+  | 'pages'
+  | 'error'
+  | 'warnings';
 
 interface ExecutionDetailsPaneProps {
   execution: Execution;
@@ -53,6 +63,14 @@ export const ExecutionDetailsPane = React.memo(function 
ExecutionDetailsPane(
             <p>{`General info for ${execution.id}${
               ingestDatasource ? ` ingesting into ${T(ingestDatasource)}` : ''
             }`}</p>
+            {execution.destination && (
+              <p>
+                {`Results written to ${execution.destination.type}`}
+                {execution.destinationPages
+                  ? ` (${pluralIfNeeded(execution.destinationPages.length, 
'page')})`
+                  : undefined}
+              </p>
+            )}
             {execution.error && <ExecutionErrorPane execution={execution} />}
             {execution.stages ? (
               <ExecutionStagesPane
@@ -92,6 +110,10 @@ export const ExecutionDetailsPane = React.memo(function 
ExecutionDetailsPane(
           />
         );
 
+      case 'pages':
+        if (!execution.destinationPages) return;
+        return <DestinationPagesPane execution={execution} />;
+
       case 'error':
         return <ExecutionErrorPane execution={execution} />;
 
@@ -129,6 +151,11 @@ export const ExecutionDetailsPane = React.memo(function 
ExecutionDetailsPane(
           label: 'Results',
           icon: IconNames.TH,
         },
+        execution.destinationPages && {
+          id: 'pages',
+          label: 'Result pages',
+          icon: IconNames.APPLICATIONS,
+        },
         execution.error && {
           id: 'error',
           label: 'Error',
diff --git 
a/web-console/src/views/workbench-view/execution-error-pane/__snapshots__/execution-error-pane.spec.tsx.snap
 
b/web-console/src/views/workbench-view/execution-error-pane/__snapshots__/execution-error-pane.spec.tsx.snap
index a57de9ed74..f6000ab38d 100644
--- 
a/web-console/src/views/workbench-view/execution-error-pane/__snapshots__/execution-error-pane.spec.tsx.snap
+++ 
b/web-console/src/views/workbench-view/execution-error-pane/__snapshots__/execution-error-pane.spec.tsx.snap
@@ -21,7 +21,7 @@ exports[`ExecutionErrorPane matches snapshot 1`] = `
   <div>
     Failed task ID: 
     <Memo(ClickToCopy)
-      text="query-8f889312-e989-4b4c-9895-485a1fe796d3-worker0_0"
+      text="query-af8a263d-213f-418e-ad8d-37d55beff59b-worker0_0"
     />
     <React.Fragment>
        (on host: 
diff --git 
a/web-console/src/views/workbench-view/execution-stages-pane/__snapshots__/execution-stages-pane.spec.tsx.snap
 
b/web-console/src/views/workbench-view/execution-stages-pane/__snapshots__/execution-stages-pane.spec.tsx.snap
index f28821b1b3..a5018aec85 100644
--- 
a/web-console/src/views/workbench-view/execution-stages-pane/__snapshots__/execution-stages-pane.spec.tsx.snap
+++ 
b/web-console/src/views/workbench-view/execution-stages-pane/__snapshots__/execution-stages-pane.spec.tsx.snap
@@ -158,7 +158,7 @@ exports[`ExecutionStagesPane matches snapshot 1`] = `
     Array [
       Object {
         "definition": Object {
-          "id": "8af42220-2724-4a76-b39f-c2f98df2de69_0",
+          "id": "ad318360-2ccf-4afc-b221-27c8704bf4fe_0",
           "input": Array [
             Object {
               "inputFormat": Object {
@@ -196,15 +196,16 @@ exports[`ExecutionStagesPane matches snapshot 1`] = `
               "context": Object {
                 "__timeColumn": "v0",
                 "__user": "allowAll",
+                "executionMode": "async",
                 "finalize": false,
                 "finalizeAggregations": false,
                 "groupByEnableMultiValueUnnesting": false,
                 "maxNumTasks": 2,
                 "maxParseExceptions": 0,
-                "queryId": "5aa683e2-a6ee-4655-a834-a643e91055b1",
+                "queryId": "93a855fa-c35a-48df-b596-6bc98eed1101",
                 "scanSignature": 
"[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]",
                 "sqlInsertSegmentGranularity": "{\\"type\\":\\"all\\"}",
-                "sqlQueryId": "5aa683e2-a6ee-4655-a834-a643e91055b1",
+                "sqlQueryId": "93a855fa-c35a-48df-b596-6bc98eed1101",
                 "sqlReplaceTimeChunks": "all",
               },
               "dataSource": Object {
@@ -283,17 +284,17 @@ exports[`ExecutionStagesPane matches snapshot 1`] = `
             },
           ],
         },
-        "duration": 20483,
+        "duration": 21324,
         "partitionCount": 1,
         "phase": "FINISHED",
         "sort": true,
         "stageNumber": 0,
-        "startTime": "2023-06-19T05:39:26.711Z",
+        "startTime": "2023-08-01T03:12:59.865Z",
         "workerCount": 1,
       },
       Object {
         "definition": Object {
-          "id": "8af42220-2724-4a76-b39f-c2f98df2de69_1",
+          "id": "ad318360-2ccf-4afc-b221-27c8704bf4fe_1",
           "input": Array [
             Object {
               "stage": 0,
@@ -359,11 +360,11 @@ exports[`ExecutionStagesPane matches snapshot 1`] = `
           },
           "signature": Array [],
         },
-        "duration": 2381,
+        "duration": 2070,
         "partitionCount": 1,
         "phase": "FINISHED",
         "stageNumber": 1,
-        "startTime": "2023-06-19T05:39:47.166Z",
+        "startTime": "2023-08-01T03:13:21.156Z",
         "workerCount": 1,
       },
     ]
diff --git 
a/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx
 
b/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx
index 182c47ff56..046de0ffb5 100644
--- 
a/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx
+++ 
b/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx
@@ -23,6 +23,7 @@ import AceEditor from 'react-ace';
 
 import { Execution } from '../../../druid-models';
 import { AppToaster } from '../../../singletons';
+import type { QueryDetailArchive } from '../../../utils';
 import { offsetToRowColumn } from '../../../utils';
 
 import './execution-submit-dialog.scss';
@@ -39,7 +40,7 @@ export const ExecutionSubmitDialog = React.memo(function 
ExecutionSubmitDialog(
   const [archive, setArchive] = useState('');
 
   function handleSubmit(): void {
-    let parsed: any;
+    let parsed: QueryDetailArchive;
     try {
       parsed = JSONBig.parse(archive);
     } catch (e) {
@@ -55,13 +56,13 @@ export const ExecutionSubmitDialog = React.memo(function 
ExecutionSubmitDialog(
     }
 
     let execution: Execution | undefined;
-    const detailArchiveVersion = parsed.detailArchiveVersion ?? 
parsed.profileVersion;
+    const detailArchiveVersion = parsed.detailArchiveVersion ?? (parsed as 
any).profileVersion;
     if (typeof detailArchiveVersion === 'number') {
       try {
         if (detailArchiveVersion === 2) {
-          execution = 
Execution.fromTaskReport(parsed.reports).updateWithTaskPayload(
-            parsed.payload,
-          );
+          execution = Execution.fromTaskReport(parsed.reports)
+            .updateWithTaskPayload(parsed.payload)
+            .updateWithAsyncStatus(parsed.statementsStatus);
         } else {
           AppToaster.show({
             intent: Intent.DANGER,
@@ -76,9 +77,9 @@ export const ExecutionSubmitDialog = React.memo(function 
ExecutionSubmitDialog(
         });
         return;
       }
-    } else if (typeof parsed.multiStageQuery === 'object') {
+    } else if (typeof (parsed as any).multiStageQuery === 'object') {
       try {
-        execution = Execution.fromTaskReport(parsed);
+        execution = Execution.fromTaskReport(parsed as any);
       } catch (e) {
         AppToaster.show({
           intent: Intent.DANGER,
diff --git 
a/web-console/src/views/workbench-view/execution-summary-panel/execution-summary-panel.tsx
 
b/web-console/src/views/workbench-view/execution-summary-panel/execution-summary-panel.tsx
index c98c94c465..bdeb738196 100644
--- 
a/web-console/src/views/workbench-view/execution-summary-panel/execution-summary-panel.tsx
+++ 
b/web-console/src/views/workbench-view/execution-summary-panel/execution-summary-panel.tsx
@@ -20,10 +20,11 @@ import { Button, ButtonGroup, Menu, MenuDivider, MenuItem, 
Position } from '@blu
 import { IconNames } from '@blueprintjs/icons';
 import { Popover2 } from '@blueprintjs/popover2';
 import type { JSX } from 'react';
-import React from 'react';
+import React, { useState } from 'react';
 
 import type { Execution } from '../../../druid-models';
 import { downloadQueryResults, formatDurationHybrid, pluralIfNeeded } from 
'../../../utils';
+import { DestinationPagesDialog } from 
'../destination-pages-dialog/destination-pages-dialog';
 
 import './execution-summary-panel.scss';
 
@@ -37,6 +38,7 @@ export const ExecutionSummaryPanel = React.memo(function 
ExecutionSummaryPanel(
   props: ExecutionSummaryPanelProps,
 ) {
   const { execution, onExecutionDetail, onReset } = props;
+  const [showDestinationPages, setShowDestinationPages] = useState(false);
   const queryResult = execution?.result;
 
   const buttons: JSX.Element[] = [];
@@ -71,21 +73,30 @@ export const ExecutionSummaryPanel = React.memo(function 
ExecutionSummaryPanel(
           }
         }}
       />,
-      <Popover2
-        key="download"
-        className="download-button"
-        content={
-          <Menu>
-            <MenuDivider title="Download results as..." />
-            <MenuItem text="CSV" onClick={() => handleDownload('csv')} />
-            <MenuItem text="TSV" onClick={() => handleDownload('tsv')} />
-            <MenuItem text="JSON (new line delimited)" onClick={() => 
handleDownload('json')} />
-          </Menu>
-        }
-        position={Position.BOTTOM_RIGHT}
-      >
-        <Button icon={IconNames.DOWNLOAD} minimal />
-      </Popover2>,
+      execution?.destination?.type === 'durableStorage' && 
execution.destinationPages ? (
+        <Button
+          key="download"
+          icon={IconNames.DOWNLOAD}
+          minimal
+          onClick={() => setShowDestinationPages(true)}
+        />
+      ) : (
+        <Popover2
+          key="download"
+          className="download-button"
+          content={
+            <Menu>
+              <MenuDivider title="Download results as..." />
+              <MenuItem text="CSV" onClick={() => handleDownload('csv')} />
+              <MenuItem text="TSV" onClick={() => handleDownload('tsv')} />
+              <MenuItem text="JSON (new line delimited)" onClick={() => 
handleDownload('json')} />
+            </Menu>
+          }
+          position={Position.BOTTOM_RIGHT}
+        >
+          <Button icon={IconNames.DOWNLOAD} minimal />
+        </Popover2>
+      ),
     );
   }
 
@@ -96,6 +107,12 @@ export const ExecutionSummaryPanel = React.memo(function 
ExecutionSummaryPanel(
   return (
     <ButtonGroup className="execution-summary-panel" minimal>
       {buttons}
+      {showDestinationPages && execution && (
+        <DestinationPagesDialog
+          execution={execution}
+          onClose={() => setShowDestinationPages(false)}
+        />
+      )}
     </ButtonGroup>
   );
 });
diff --git 
a/web-console/src/views/workbench-view/ingest-success-pane/ingest-success-pane.tsx
 
b/web-console/src/views/workbench-view/ingest-success-pane/ingest-success-pane.tsx
index 3017490080..539ad234e7 100644
--- 
a/web-console/src/views/workbench-view/ingest-success-pane/ingest-success-pane.tsx
+++ 
b/web-console/src/views/workbench-view/ingest-success-pane/ingest-success-pane.tsx
@@ -40,7 +40,7 @@ export const IngestSuccessPane = React.memo(function 
IngestSuccessPane(
   const datasource = execution.getIngestDatasource();
   if (!datasource) return null;
   const table = T(datasource);
-  const rows = execution.getIngestNumRows();
+  const rows = execution.getOutputNumTotalRows();
 
   const warnings = execution.stages?.getWarningCount() || 0;
 
diff --git 
a/web-console/src/views/workbench-view/recent-query-task-panel/recent-query-task-panel.tsx
 
b/web-console/src/views/workbench-view/recent-query-task-panel/recent-query-task-panel.tsx
index 718f5315c1..73415efa3c 100644
--- 
a/web-console/src/views/workbench-view/recent-query-task-panel/recent-query-task-panel.tsx
+++ 
b/web-console/src/views/workbench-view/recent-query-task-panel/recent-query-task-panel.tsx
@@ -255,7 +255,7 @@ LIMIT 100`,
                       })}
                     >
                       {w.datasource === Execution.INLINE_DATASOURCE_MARKER
-                        ? 'data in report'
+                        ? 'select query'
                         : w.datasource}
                     </div>
                   </div>


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

Reply via email to