Skip to content

Commit 2f1fa0c

Browse files
Improves query execution/cancelation
1 parent 00ea913 commit 2f1fa0c

File tree

2 files changed

+129
-21
lines changed

2 files changed

+129
-21
lines changed

superset-frontend/packages/superset-core/src/api/sqlLab.ts

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -565,31 +565,91 @@ export declare function setActiveTab(tabId: string): Promise<void>;
565565
*/
566566

567567
/**
568-
* Executes the SQL query in the current tab.
568+
* Options for executing a SQL query.
569+
*/
570+
export interface QueryOptions {
571+
/**
572+
* SQL to execute without modifying editor content.
573+
* If not provided, uses the current editor content.
574+
*/
575+
sql?: string;
576+
577+
/**
578+
* Run only the selected text in the editor.
579+
* Ignored if `sql` option is provided.
580+
*/
581+
selectedOnly?: boolean;
582+
583+
/**
584+
* Override the query row limit.
585+
* If not provided, uses the tab's configured limit.
586+
*/
587+
limit?: number;
588+
589+
/**
590+
* Template parameters for Jinja templating.
591+
* Merged with existing template parameters from the editor.
592+
*/
593+
templateParams?: Record<string, unknown>;
594+
595+
/**
596+
* Create Table/View As Select options.
597+
* When provided, query results are stored in a new table instead of returned directly.
598+
*/
599+
ctas?: {
600+
/**
601+
* Whether to create a TABLE or VIEW.
602+
*/
603+
method: 'TABLE' | 'VIEW';
604+
605+
/**
606+
* Name of the table or view to create.
607+
*/
608+
tableName: string;
609+
};
610+
}
611+
612+
/**
613+
* Executes a SQL query in the current tab.
569614
*
570-
* @returns Promise that resolves with the query client ID
615+
* @param options Optional query execution options
616+
* @returns Promise that resolves with the query ID
571617
*
572618
* @example
573619
* ```typescript
574-
* const queryId = await runQuery();
620+
* // Execute the current editor content
621+
* const queryId = await executeQuery();
622+
*
623+
* // Execute custom SQL without modifying the editor
624+
* const queryId = await executeQuery({
625+
* sql: "SELECT * FROM users LIMIT 10"
626+
* });
627+
*
628+
* // Execute only selected text
629+
* const queryId = await executeQuery({ selectedOnly: true });
630+
*
631+
* // Create a table from query results
632+
* const queryId = await executeQuery({
633+
* ctas: { method: 'TABLE', tableName: 'my_results' }
634+
* });
575635
* ```
576636
*/
577-
export declare function runQuery(): Promise<string>;
637+
export declare function executeQuery(options?: QueryOptions): Promise<string>;
578638

579639
/**
580-
* Stops a running query.
640+
* Cancels a running query.
581641
*
582-
* @param queryId The client ID of the query to stop
583-
* @returns Promise that resolves when the stop request is sent
642+
* @param queryId The client ID of the query to cancel
643+
* @returns Promise that resolves when the cancellation request is sent
584644
*
585645
* @example
586646
* ```typescript
587-
* const queryId = await runQuery();
647+
* const queryId = await executeQuery();
588648
* // Later, if needed:
589-
* await stopQuery(queryId);
649+
* await cancelQuery(queryId);
590650
* ```
591651
*/
592-
export declare function stopQuery(queryId: string): Promise<void>;
652+
export declare function cancelQuery(queryId: string): Promise<void>;
593653

594654
/**
595655
* Tab Context APIs

superset-frontend/src/core/sqlLab/index.ts

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* under the License.
1818
*/
1919
import { sqlLab as sqlLabApi } from '@apache-superset/core';
20+
import { nanoid } from 'nanoid';
2021
import {
2122
ADD_QUERY_EDITOR,
2223
QUERY_FAILED,
@@ -38,8 +39,9 @@ import {
3839
queryEditorSetDb,
3940
queryEditorSetCatalog,
4041
queryEditorSetSchema,
41-
runQueryFromSqlEditor,
42+
runQuery as runQueryAction,
4243
postStopQuery,
44+
Query,
4345
} from 'src/SqlLab/actions/sqlLab';
4446
import { RootState, store } from 'src/views/store';
4547
import { AnyListenerPredicate } from '@reduxjs/toolkit';
@@ -156,16 +158,16 @@ const makeTab = (
156158
id: string,
157159
name: string,
158160
dbId: number,
159-
catalog?: string | null,
160-
schema?: string | null,
161+
catalog: string | null = null,
162+
schema: string | null = null,
161163
): Tab => {
162164
const panels: Panel[] = []; // TODO: Populate panels
163165
return new Tab(
164166
id,
165167
name,
166168
dbId,
167-
catalog ?? null,
168-
schema ?? null,
169+
catalog,
170+
schema,
169171
() => getEditorAsync(id),
170172
panels,
171173
);
@@ -591,7 +593,7 @@ const setActiveTab: typeof sqlLabApi.setActiveTab = async (tabId: string) => {
591593
}
592594
};
593595

594-
const runQuery: typeof sqlLabApi.runQuery = async () => {
596+
const executeQuery: typeof sqlLabApi.executeQuery = async options => {
595597
const state = store.getState() as SqlLabRootState;
596598
const editorId = activeEditorId();
597599
const queryEditor = findQueryEditor(editorId);
@@ -609,12 +611,58 @@ const runQuery: typeof sqlLabApi.runQuery = async () => {
609611
const database = qe.dbId ? databases[qe.dbId] : null;
610612
const defaultLimit = state.common?.conf?.DEFAULT_SQLLAB_LIMIT ?? 1000;
611613

612-
store.dispatch(runQueryFromSqlEditor(database, qe, defaultLimit) as any);
614+
// Determine SQL to execute
615+
let sql: string;
616+
let updateTabState = true;
617+
618+
if (options?.sql !== undefined) {
619+
// Custom SQL provided - don't update tab state
620+
({ sql } = options);
621+
updateTabState = false;
622+
} else if (options?.selectedOnly && qe.selectedText) {
623+
// Run selected text only
624+
sql = qe.selectedText;
625+
updateTabState = false;
626+
} else {
627+
// Default: use editor content (selected text takes precedence)
628+
sql = qe.selectedText || qe.sql;
629+
updateTabState = !qe.selectedText;
630+
}
631+
632+
// Merge template params
633+
const templateParams = options?.templateParams
634+
? JSON.stringify({
635+
...JSON.parse(qe.templateParams || '{}'),
636+
...options.templateParams,
637+
})
638+
: qe.templateParams;
639+
640+
const queryId = nanoid(11);
641+
642+
const query: Query = {
643+
id: queryId,
644+
dbId: qe.dbId,
645+
sql,
646+
sqlEditorId: qe.tabViewId ?? qe.id,
647+
sqlEditorImmutableId: qe.immutableId,
648+
tab: qe.name,
649+
catalog: qe.catalog,
650+
schema: qe.schema,
651+
tempTable: options?.ctas?.tableName,
652+
templateParams,
653+
queryLimit: options?.limit ?? qe.queryLimit ?? defaultLimit,
654+
runAsync: database ? database.allow_run_async : false,
655+
ctas: !!options?.ctas,
656+
ctas_method: options?.ctas?.method,
657+
updateTabState,
658+
};
659+
660+
store.dispatch(runQueryAction(query));
613661

614-
return qe.id;
662+
return queryId;
615663
};
616664

617-
const stopQuery: typeof sqlLabApi.stopQuery = async (queryId: string) => {
665+
const cancelQuery: typeof sqlLabApi.cancelQuery = async (queryId: string) => {
618666
const state = store.getState() as SqlLabRootState;
619667
const query = state.sqlLab.queries[queryId];
620668

@@ -664,8 +712,8 @@ export const sqlLab: typeof sqlLabApi = {
664712
createTab,
665713
closeTab,
666714
setActiveTab,
667-
runQuery,
668-
stopQuery,
715+
executeQuery,
716+
cancelQuery,
669717
setDatabase,
670718
setCatalog,
671719
setSchema,

0 commit comments

Comments
 (0)