diff --git a/web-console/lib/keywords.js b/web-console/lib/keywords.js index 62dec991f86c..bf7b9a039102 100644 --- a/web-console/lib/keywords.js +++ b/web-console/lib/keywords.js @@ -108,6 +108,7 @@ exports.SQL_EXPRESSION_PARTS = [ 'YEAR', 'TIMESTAMP', 'INTERVAL', + 'CSV', ]; exports.SQL_CONSTANTS = ['NULL', 'FALSE', 'TRUE']; diff --git a/web-console/src/druid-models/execution/execution.ts b/web-console/src/druid-models/execution/execution.ts index 3059a401c4f9..0cf8d5d0ed37 100644 --- a/web-console/src/druid-models/execution/execution.ts +++ b/web-console/src/druid-models/execution/execution.ts @@ -567,7 +567,7 @@ export class Execution { return this.destination?.numTotalRows; } - public isSuccessfulInsert(): boolean { + public isSuccessfulIngest(): boolean { return Boolean(this.status === 'SUCCESS' && this.getIngestDatasource()); } diff --git a/web-console/src/druid-models/workbench-query/workbench-query.spec.ts b/web-console/src/druid-models/workbench-query/workbench-query.spec.ts index ec057b3efc7f..02bb3e399b02 100644 --- a/web-console/src/druid-models/workbench-query/workbench-query.spec.ts +++ b/web-console/src/druid-models/workbench-query/workbench-query.spec.ts @@ -455,100 +455,6 @@ describe('WorkbenchQuery', () => { }); }); - describe('#getIngestDatasource', () => { - it('works with INSERT', () => { - const sql = sane` - -- Some comment - INSERT INTO trips2 - SELECT - TIME_PARSE(pickup_datetime) AS __time, - * - FROM TABLE( - EXTERN( - '{"type": "local", ...}', - '{"type":"csv", ...}' - ) - ) EXTEND (cab_type, VARCHAR) - CLUSTERED BY trip_id - `; - - const workbenchQuery = WorkbenchQuery.blank().changeQueryString(sql); - expect(workbenchQuery.getIngestDatasource()).toEqual('trips2'); - expect(workbenchQuery.changeEngine('sql-native').getIngestDatasource()).toBeUndefined(); - }); - - it('works with INSERT (unparsable)', () => { - const sql = sane` - -- Some comment - INSERT into trips2 - SELECT - TIME_PARSE(pickup_datetime) AS __time, - * - FROM TABLE( - `; - - const workbenchQuery = WorkbenchQuery.blank().changeQueryString(sql); - expect(workbenchQuery.getIngestDatasource()).toEqual('trips2'); - expect(workbenchQuery.changeEngine('sql-native').getIngestDatasource()).toBeUndefined(); - }); - - it('works with INSERT (unparsable with paren)', () => { - const sql = sane` - -- Some comment - INSERT into trips2 - (SELECT TIME_PARSE(pickup_datetime) AS __time, - `; - - const workbenchQuery = WorkbenchQuery.blank().changeQueryString(sql); - expect(workbenchQuery.getIngestDatasource()).toEqual('trips2'); - expect(workbenchQuery.changeEngine('sql-native').getIngestDatasource()).toBeUndefined(); - }); - - it('works with REPLACE', () => { - const sql = sane` - REPLACE INTO trips2 OVERWRITE ALL - SELECT - TIME_PARSE(pickup_datetime) AS __time, - * - FROM TABLE( - EXTERN( - '{"type": "local", ...}', - '{"type":"csv", ...}' - ) - ) EXTEND (cab_type, VARCHAR) - CLUSTERED BY trip_id - `; - - const workbenchQuery = WorkbenchQuery.blank().changeQueryString(sql); - expect(workbenchQuery.getIngestDatasource()).toEqual('trips2'); - expect(workbenchQuery.changeEngine('sql-native').getIngestDatasource()).toBeUndefined(); - }); - - it('works with REPLACE (unparsable)', () => { - const sql = sane` - REPLACE INTO trips2 OVERWRITE ALL - WITH kttm_data AS (SELECT * - `; - - const workbenchQuery = WorkbenchQuery.blank().changeQueryString(sql); - expect(workbenchQuery.getIngestDatasource()).toEqual('trips2'); - expect(workbenchQuery.changeEngine('sql-native').getIngestDatasource()).toBeUndefined(); - }); - - it('works with REPLACE (unparsable with comment at start)', () => { - const sql = sane` - -- Hello world SELECT - - REPLACE INTO trips2 OVERWRITE ALL - WITH kttm_data AS (SELECT * - `; - - const workbenchQuery = WorkbenchQuery.blank().changeQueryString(sql); - expect(workbenchQuery.getIngestDatasource()).toEqual('trips2'); - expect(workbenchQuery.changeEngine('sql-native').getIngestDatasource()).toBeUndefined(); - }); - }); - describe('#getIssue', () => { it('works', () => { expect( diff --git a/web-console/src/druid-models/workbench-query/workbench-query.ts b/web-console/src/druid-models/workbench-query/workbench-query.ts index f027efdc0a55..27cf6a0109da 100644 --- a/web-console/src/druid-models/workbench-query/workbench-query.ts +++ b/web-console/src/druid-models/workbench-query/workbench-query.ts @@ -158,7 +158,7 @@ export class WorkbenchQuery { .changeQueryString(queryString) .changeQueryContext(cleanContext); - if (noSqlOuterLimit && !retQuery.getIngestDatasource()) { + if (noSqlOuterLimit && !retQuery.isIngestQuery()) { retQuery = retQuery.changeUnlimited(true); } @@ -221,23 +221,6 @@ export class WorkbenchQuery { return /EXTERN\s*\(|(?:INSERT|REPLACE)\s+INTO/im.test(queryString); } - static getIngestDatasourceFromQueryFragment(queryFragment: string): string | undefined { - // Assuming the queryFragment is no parsable find the prefix that look like: - // REPLACEINTOSELECT - const matchInsertReplaceIndex = queryFragment.match(/(?:INSERT|REPLACE)\s+INTO/i)?.index; - if (typeof matchInsertReplaceIndex !== 'number') return; - - const queryStartingWithInsertOrReplace = queryFragment.substring(matchInsertReplaceIndex); - - const matchEnd = queryStartingWithInsertOrReplace.match(/\(|\b(?:SELECT|WITH)\b|$/i); - const fragmentQuery = SqlQuery.maybeParse( - queryStartingWithInsertOrReplace.substring(0, matchEnd?.index) + ' SELECT * FROM t', - ); - if (!fragmentQuery) return; - - return fragmentQuery.getIngestTable()?.getName(); - } - public readonly queryString: string; public readonly queryContext: QueryContext; public readonly queryParameters?: QueryParameter[]; @@ -409,21 +392,17 @@ export class WorkbenchQuery { } } - public getIngestDatasource(): string | undefined { - if (this.getEffectiveEngine() !== 'sql-msq-task') return; + public isIngestQuery(): boolean { + if (this.getEffectiveEngine() !== 'sql-msq-task') return false; const { queryString, parsedQuery } = this; if (parsedQuery) { - return parsedQuery.getIngestTable()?.getName(); + return Boolean(parsedQuery.getIngestTable()); } - if (this.isJsonLike()) return; - - return WorkbenchQuery.getIngestDatasourceFromQueryFragment(queryString); - } + if (this.isJsonLike()) return false; - public isIngestQuery(): boolean { - return Boolean(this.getIngestDatasource()); + return /(?:INSERT|REPLACE)\s+INTO/i.test(queryString); } public toggleUnlimited(): WorkbenchQuery { diff --git a/web-console/src/views/workbench-view/query-tab/query-tab.scss b/web-console/src/views/workbench-view/query-tab/query-tab.scss index eee0375e0ea7..4c143194a872 100644 --- a/web-console/src/views/workbench-view/query-tab/query-tab.scss +++ b/web-console/src/views/workbench-view/query-tab/query-tab.scss @@ -124,5 +124,25 @@ $vertical-gap: 6px; right: 0; } } + + .generic-status-container { + position: relative; + + .generic-status-container-info { + position: absolute; + top: 5px; + left: 5px; + right: 5px; + height: 30px; + } + + .execution-stages-pane { + position: absolute; + top: 40px; + bottom: 0; + left: 0; + right: 0; + } + } } } diff --git a/web-console/src/views/workbench-view/query-tab/query-tab.tsx b/web-console/src/views/workbench-view/query-tab/query-tab.tsx index 8c7db0427365..bd3a7dc769c1 100644 --- a/web-console/src/views/workbench-view/query-tab/query-tab.tsx +++ b/web-console/src/views/workbench-view/query-tab/query-tab.tsx @@ -286,11 +286,11 @@ export const QueryTab = React.memo(function QueryTab(props: QueryTabProps) { useCallback(state => state.increment, []), ); useEffect(() => { - if (execution?.isSuccessfulInsert()) { + if (execution?.isSuccessfulIngest()) { incrementMetadataVersion(); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [Boolean(execution?.isSuccessfulInsert())]); + }, [Boolean(execution?.isSuccessfulIngest())]); function moveToPosition(position: RowColumn) { const currentQueryInput = queryInputRef.current; @@ -434,12 +434,6 @@ export const QueryTab = React.memo(function QueryTab(props: QueryTabProps) { queryResult={execution.result} onQueryAction={handleQueryAction} /> - ) : execution.isSuccessfulInsert() ? ( - ) : execution.error ? (
@@ -452,8 +446,24 @@ export const QueryTab = React.memo(function QueryTab(props: QueryTabProps) { /> )}
+ ) : execution.isSuccessfulIngest() ? ( + ) : ( -
Unknown query execution state
+
+
+ {`Execution completed with status: ${execution.status}`} +
+ onDetails(statsTaskId!, 'error')} + onWarningClick={() => onDetails(statsTaskId!, 'warnings')} + goToTask={goToTask} + /> +
))} {executionState.error && (