Skip to content

Commit 5756f21

Browse files
authored
Partial loading of query results for modified Redash 2025.08 (#14)
* Reapply changes from PR #2 * Fix issue with 'encoding' parameter * Clean up after cherry-pick * yarn prettier * Allow the admin reporting user
1 parent 16e89bd commit 5756f21

File tree

11 files changed

+210
-145
lines changed

11 files changed

+210
-145
lines changed

client/app/lib/useQueryResultData.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,9 @@ function getQueryResultData(queryResult, queryResultStatus = null) {
2020
export default function useQueryResultData(queryResult) {
2121
// make sure it re-executes when queryResult status changes
2222
const queryResultStatus = invoke(queryResult, "getStatus");
23-
return useMemo(() => getQueryResultData(queryResult, queryResultStatus), [queryResult, queryResultStatus]);
23+
const queryResultRowCount = get(queryResult, "query_result.data.rows.length");
24+
return useMemo(
25+
() => getQueryResultData(queryResult, queryResultStatus),
26+
[queryResult, queryResultStatus, queryResultRowCount]
27+
);
2428
}

client/app/pages/alert/Alert.jsx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,20 @@ class Alert extends React.Component {
124124
});
125125
};
126126

127+
handleResult = (queryResult) => {
128+
if (this._isMounted) {
129+
this.setState({ queryResult });
130+
let { column } = this.state.alert.options;
131+
const columns = queryResult.getColumnNames();
132+
133+
// default to first column name if none chosen, or irrelevant in current query
134+
if (!column || !includes(columns, column)) {
135+
column = head(queryResult.getColumnNames());
136+
}
137+
this.setAlertOptions({ column });
138+
}
139+
};
140+
127141
onQuerySelected = (query) => {
128142
this.setState(({ alert }) => ({
129143
alert: Object.assign(alert, { query }),
@@ -132,19 +146,9 @@ class Alert extends React.Component {
132146

133147
if (query) {
134148
// get cached result for column names and values
135-
new QueryService(query).getQueryResultPromise().then((queryResult) => {
136-
if (this._isMounted) {
137-
this.setState({ queryResult });
138-
let { column } = this.state.alert.options;
139-
const columns = queryResult.getColumnNames();
140-
141-
// default to first column name if none chosen, or irrelevant in current query
142-
if (!column || !includes(columns, column)) {
143-
column = head(queryResult.getColumnNames());
144-
}
145-
this.setAlertOptions({ column });
146-
}
147-
});
149+
const promises = new QueryService(query).getQueryResultPromises();
150+
promises[0].then((queryResult) => this.handleResult(queryResult));
151+
promises[1].then((queryResult) => this.handleResult(queryResult));
148152
}
149153
};
150154

client/app/pages/queries/QueryView.jsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ function QueryView(props) {
5454
const {
5555
queryResult,
5656
loadedInitialResults,
57+
loadedFullResults,
5758
isExecuting,
5859
executionStatus,
5960
executeQuery,
@@ -71,7 +72,7 @@ function QueryView(props) {
7172
setQuery(newQuery);
7273
setSelectedVisualization(visualization.id);
7374
});
74-
const editVisualization = useEditVisualizationDialog(query, queryResult, newQuery => setQuery(newQuery));
75+
const editVisualization = useEditVisualizationDialog(query, queryResult, (newQuery) => setQuery(newQuery));
7576
const deleteVisualization = useDeleteVisualization(query, setQuery);
7677

7778
const doExecuteQuery = useCallback(
@@ -97,7 +98,8 @@ function QueryView(props) {
9798
className={cx("query-page-wrapper", {
9899
"query-view-fullscreen": fullscreen,
99100
"query-fixed-layout": isFixedLayout,
100-
})}>
101+
})}
102+
>
101103
<div className="container w-100">
102104
<QueryPageHeader
103105
query={query}
@@ -112,7 +114,8 @@ function QueryView(props) {
112114
type="primary"
113115
shortcut="mod+enter, alt+enter, ctrl+enter"
114116
disabled={!queryFlags.canExecute || isExecuting || areParametersDirty}
115-
onClick={doExecuteQuery}>
117+
onClick={doExecuteQuery}
118+
>
116119
Refresh
117120
</QueryViewButton>
118121
)}
@@ -177,7 +180,8 @@ function QueryView(props) {
177180
type="primary"
178181
disabled={!queryFlags.canExecute || areParametersDirty}
179182
loading={isExecuting}
180-
onClick={doExecuteQuery}>
183+
onClick={doExecuteQuery}
184+
>
181185
{!isExecuting && <i className="zmdi zmdi-refresh m-r-5" aria-hidden="true" />}
182186
Refresh Now
183187
</Button>
@@ -186,6 +190,9 @@ function QueryView(props) {
186190
canRefresh={policy.canRun(query)}
187191
/>
188192
)}
193+
{loadedInitialResults && !loadedFullResults && (
194+
<div style={{ color: "orange", textAlign: "center" }}>Still loading more results...</div>
195+
)}
189196
<div className="query-results-footer">
190197
{queryResult && !queryResult.getError() && (
191198
<QueryExecutionMetadata
@@ -201,7 +208,8 @@ function QueryView(props) {
201208
title="Toggle Fullscreen"
202209
type="default"
203210
shortcut="alt+f"
204-
onClick={toggleFullscreen}>
211+
onClick={toggleFullscreen}
212+
>
205213
{fullscreen ? <FullscreenExitOutlinedIcon /> : <FullscreenOutlinedIcon />}
206214
</QueryViewButton>
207215
}
@@ -236,6 +244,6 @@ routes.register(
236244
"Queries.View",
237245
routeWithUserSession({
238246
path: "/queries/:queryId",
239-
render: pageProps => <QueryViewPage {...pageProps} />,
247+
render: (pageProps) => <QueryViewPage {...pageProps} />,
240248
})
241249
);

client/app/pages/queries/hooks/useQueryExecute.js

Lines changed: 47 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export default function useQueryExecute(query) {
2323
queryResult: null,
2424
isExecuting: false,
2525
loadedInitialResults: false,
26+
loadedFullResults: false,
2627
executionStatus: null,
2728
isCancelling: false,
2829
cancelCallback: null,
@@ -61,52 +62,59 @@ export default function useQueryExecute(query) {
6162
},
6263
});
6364

64-
const onStatusChange = status => {
65+
const onStatusChange = (status) => {
6566
if (queryResultInExecution.current === newQueryResult) {
6667
setExecutionState({ updatedAt: newQueryResult.getUpdatedAt(), executionStatus: status });
6768
}
6869
};
6970

70-
newQueryResult
71-
.toPromise(onStatusChange)
72-
.then(queryResult => {
73-
if (queryResultInExecution.current === newQueryResult) {
74-
// TODO: this should probably belong in the QueryEditor page.
75-
if (queryResult && queryResult.query_result.query === query.query) {
76-
query.latest_query_data_id = queryResult.getId();
77-
query.queryResult = queryResult;
78-
}
79-
80-
if (executionState.loadedInitialResults) {
81-
notifications.showNotification("Redash", `${query.name} updated.`);
82-
}
83-
84-
setExecutionState({
85-
queryResult,
86-
loadedInitialResults: true,
87-
error: null,
88-
isExecuting: false,
89-
isCancelling: false,
90-
executionStatus: null,
91-
});
71+
const successResult = (queryResult, instance) => {
72+
if (queryResultInExecution.current === newQueryResult) {
73+
// TODO: this should probably belong in the QueryEditor page.
74+
if (queryResult && queryResult.query_result.query === query.query) {
75+
query.latest_query_data_id = queryResult.getId();
76+
query.queryResult = queryResult;
9277
}
93-
})
94-
.catch(queryResult => {
95-
if (queryResultInExecution.current === newQueryResult) {
96-
if (executionState.loadedInitialResults) {
97-
notifications.showNotification("Redash", `${query.name} failed to run: ${queryResult.getError()}`);
98-
}
99-
100-
setExecutionState({
101-
queryResult,
102-
loadedInitialResults: true,
103-
error: queryResult.getError(),
104-
isExecuting: false,
105-
isCancelling: false,
106-
executionStatus: ExecutionStatus.FAILED,
107-
});
78+
79+
if (executionState.loadedInitialResults) {
80+
notifications.showNotification("Redash", `${query.name} updated.`);
10881
}
109-
});
82+
83+
setExecutionState({
84+
queryResult,
85+
loadedInitialResults: true,
86+
loadedFullResults: instance > 0,
87+
error: null,
88+
isExecuting: false,
89+
isCancelling: false,
90+
executionStatus: null,
91+
});
92+
}
93+
};
94+
95+
const errorResult = (queryResult) => {
96+
if (queryResultInExecution.current === newQueryResult) {
97+
if (executionState.loadedInitialResults) {
98+
notifications.showNotification("Redash", `${query.name} failed to run: ${queryResult.getError()}`);
99+
}
100+
101+
setExecutionState({
102+
queryResult,
103+
loadedInitialResults: true,
104+
loadedFullResults: true,
105+
error: queryResult.getError(),
106+
isExecuting: false,
107+
isCancelling: false,
108+
executionStatus: ExecutionStatus.FAILED,
109+
});
110+
}
111+
};
112+
113+
const promises = newQueryResult.toPromise(onStatusChange);
114+
promises[0].then((queryResult) => successResult(queryResult, 0)).catch((queryResult) => errorResult(queryResult));
115+
if (promises[1]) {
116+
promises[1].then((queryResult) => successResult(queryResult, 1)).catch((queryResult) => errorResult(queryResult));
117+
}
110118
});
111119

112120
const queryRef = useRef(query);

0 commit comments

Comments
 (0)