Fix web console query view crashing on simple query (#9897)

* only parse full queries

* upgraded sql parser
This commit is contained in:
Vadim Ogievetsky 2020-05-21 12:57:07 -07:00 committed by GitHub
parent b91d50044e
commit 63baa29ad1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 120 additions and 44 deletions

View File

@ -4707,7 +4707,7 @@ license_category: binary
module: web-console
license_name: Apache License version 2.0
copyright: Imply Data
version: 0.5.1
version: 0.6.1
---

View File

@ -4243,9 +4243,9 @@
}
},
"druid-query-toolkit": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.5.1.tgz",
"integrity": "sha512-oI1YddnzIbkcelI93qaRtonu3PLGw65fDqLLK6e35gD4Ef/Yf8bOZvFK9wDYNAXcA6SDy7UDarfWsgD2dDWsjg==",
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.6.1.tgz",
"integrity": "sha512-ykrWD9AbDQEvE55x8ST1kyiiGHSN8zhp/Lqe7z43/l7XG9QD7AQwfBzOn+HATXFynrOQN/3z3Cis70EzdDjc1g==",
"requires": {
"tslib": "^1.10.0"
}

View File

@ -68,7 +68,7 @@
"d3-axis": "^1.0.12",
"d3-scale": "^3.2.0",
"d3-selection": "^1.4.0",
"druid-query-toolkit": "^0.5.1",
"druid-query-toolkit": "^0.6.1",
"file-saver": "^2.0.2",
"has-own-prop": "^2.0.0",
"hjson": "^3.2.1",

View File

@ -81,3 +81,85 @@ exports[`sql view matches snapshot 1`] = `
</t>
</div>
`;
exports[`sql view matches snapshot with query 1`] = `
<div
className="query-view app-view"
>
<ColumnTree
columnMetadataLoading={true}
defaultSchema="druid"
getParsedQuery={[Function]}
onQueryStringChange={[Function]}
/>
<t
customClassName=""
onDragEnd={null}
onDragStart={null}
onSecondaryPaneSizeChange={[Function]}
percentage={true}
primaryIndex={0}
primaryMinSize={30}
secondaryInitialSize={60}
secondaryMinSize={30}
vertical={true}
>
<div
className="control-pane"
>
<QueryInput
currentSchema="druid"
onQueryStringChange={[Function]}
queryString="SELECT +3"
runeMode={false}
/>
<div
className="control-bar"
>
<HotkeysTarget(RunButton)
loading={false}
onEditContext={[Function]}
onExplain={[Function]}
onHistory={[Function]}
onPrettier={[Function]}
onQueryContextChange={[Function]}
onRun={[Function]}
queryContext={Object {}}
runeMode={false}
/>
<Blueprint3.Tooltip
content="Automatically run queries when modified via helper action menus."
hoverCloseDelay={0}
hoverOpenDelay={800}
transitionDuration={100}
>
<Blueprint3.Switch
checked={true}
className="auto-run"
label="Auto run"
onChange={[Function]}
/>
</Blueprint3.Tooltip>
<Blueprint3.Tooltip
content="Automatically wrap the query with a limit to protect against queries with very large result sets."
hoverCloseDelay={0}
hoverOpenDelay={800}
transitionDuration={100}
>
<Blueprint3.Switch
checked={true}
className="smart-query-limit"
label="Smart query limit"
onChange={[Function]}
/>
</Blueprint3.Tooltip>
</div>
</div>
<Memo(QueryOutput)
loading={false}
onQueryChange={[Function]}
runeMode={false}
/>
</t>
</div>
`;

View File

@ -17,21 +17,19 @@
*/
import { render } from '@testing-library/react';
import { sqlParserFactory } from 'druid-query-toolkit';
import { parseSqlQuery } from 'druid-query-toolkit';
import React from 'react';
import { NumberMenuItems } from './number-menu-items';
describe('number menu', () => {
const parser = sqlParserFactory(['COUNT']);
it('matches snapshot when menu is opened for column not inside group by', () => {
const numberMenu = (
<NumberMenuItems
schema="schema"
table="table"
columnName={'added'}
parsedQuery={parser(`SELECT channel, count(*) as cnt FROM wikipedia GROUP BY 1`)}
parsedQuery={parseSqlQuery(`SELECT channel, count(*) as cnt FROM wikipedia GROUP BY 1`)}
onQueryChange={() => {}}
/>
);
@ -46,7 +44,7 @@ describe('number menu', () => {
schema="schema"
table="table"
columnName={'added'}
parsedQuery={parser(`SELECT added, count(*) as cnt FROM wikipedia GROUP BY 1`)}
parsedQuery={parseSqlQuery(`SELECT added, count(*) as cnt FROM wikipedia GROUP BY 1`)}
onQueryChange={() => {}}
/>
);

View File

@ -17,21 +17,19 @@
*/
import { render } from '@testing-library/react';
import { sqlParserFactory } from 'druid-query-toolkit';
import { parseSqlQuery } from 'druid-query-toolkit';
import React from 'react';
import { StringMenuItems } from './string-menu-items';
describe('string menu', () => {
const parser = sqlParserFactory(['COUNT']);
it('matches snapshot when menu is opened for column not inside group by', () => {
const stringMenu = (
<StringMenuItems
table={'table'}
schema={'schema'}
columnName={'cityName'}
parsedQuery={parser(`SELECT channel, count(*) as cnt FROM wikipedia GROUP BY 1`)}
parsedQuery={parseSqlQuery(`SELECT channel, count(*) as cnt FROM wikipedia GROUP BY 1`)}
onQueryChange={() => {}}
/>
);
@ -46,7 +44,7 @@ describe('string menu', () => {
table={'table'}
schema={'schema'}
columnName={'channel'}
parsedQuery={parser(`SELECT channel, count(*) as cnt FROM wikipedia GROUP BY 1`)}
parsedQuery={parseSqlQuery(`SELECT channel, count(*) as cnt FROM wikipedia GROUP BY 1`)}
onQueryChange={() => {}}
/>
);

View File

@ -17,21 +17,19 @@
*/
import { render } from '@testing-library/react';
import { sqlParserFactory } from 'druid-query-toolkit';
import { parseSqlQuery } from 'druid-query-toolkit';
import React from 'react';
import { TimeMenuItems } from './time-menu-items';
describe('time menu', () => {
const parser = sqlParserFactory(['COUNT']);
it('matches snapshot when menu is opened for column not inside group by', () => {
const timeMenu = (
<TimeMenuItems
table={'table'}
schema={'schema'}
columnName={'__time'}
parsedQuery={parser(`SELECT channel, count(*) as cnt FROM wikipedia GROUP BY 1`)}
parsedQuery={parseSqlQuery(`SELECT channel, count(*) as cnt FROM wikipedia GROUP BY 1`)}
onQueryChange={() => {}}
/>
);
@ -46,7 +44,7 @@ describe('time menu', () => {
table={'table'}
schema={'schema'}
columnName={'__time'}
parsedQuery={parser(`SELECT __time, count(*) as cnt FROM wikipedia GROUP BY 1`)}
parsedQuery={parseSqlQuery(`SELECT __time, count(*) as cnt FROM wikipedia GROUP BY 1`)}
onQueryChange={() => {}}
/>
);

View File

@ -17,7 +17,7 @@
*/
import { render } from '@testing-library/react';
import { sqlParserFactory } from 'druid-query-toolkit';
import { parseSqlQuery } from 'druid-query-toolkit';
import React from 'react';
import { ColumnMetadata } from '../../../utils/column-metadata';
@ -25,13 +25,11 @@ import { ColumnMetadata } from '../../../utils/column-metadata';
import { ColumnTree } from './column-tree';
describe('column tree', () => {
const parser = sqlParserFactory(['COUNT']);
it('matches snapshot', () => {
const columnTree = (
<ColumnTree
getParsedQuery={() => {
return parser(`SELECT channel, count(*) as cnt FROM wikipedia GROUP BY 1`);
return parseSqlQuery(`SELECT channel, count(*) as cnt FROM wikipedia GROUP BY 1`);
}}
defaultSchema="druid"
defaultTable="wikipedia"

View File

@ -17,18 +17,14 @@
*/
import { render } from '@testing-library/react';
import { sqlParserFactory } from 'druid-query-toolkit';
import { parseSqlQuery } from 'druid-query-toolkit';
import React from 'react';
import { SQL_FUNCTIONS } from '../../../../lib/sql-docs';
import { QueryOutput } from './query-output';
describe('query output', () => {
it('matches snapshot', () => {
const parser = sqlParserFactory(SQL_FUNCTIONS.map(sqlFunction => sqlFunction.name));
const parsedQuery = parser(`SELECT
const parsedQuery = parseSqlQuery(`SELECT
"language",
COUNT(*) AS "Count", COUNT(DISTINCT "language") AS "dist_language", COUNT(*) FILTER (WHERE "language"= 'xxx') AS "language_filtered_count"
FROM "github"

View File

@ -27,6 +27,11 @@ describe('sql view', () => {
expect(sqlView).toMatchSnapshot();
});
it('matches snapshot with query', () => {
const sqlView = shallow(<QueryView initQuery={'SELECT +3'} />);
expect(sqlView).toMatchSnapshot();
});
it('trimSemicolon', () => {
expect(QueryView.trimSemicolon('SELECT * FROM tbl;')).toEqual('SELECT * FROM tbl');
expect(QueryView.trimSemicolon('SELECT * FROM tbl; ')).toEqual('SELECT * FROM tbl ');

View File

@ -23,8 +23,8 @@ import {
HeaderRows,
isFirstRowHeader,
normalizeQueryResult,
parseSqlQuery,
shouldIncludeTimestamp,
sqlParserFactory,
SqlQuery,
} from 'druid-query-toolkit';
import Hjson from 'hjson';
@ -32,7 +32,6 @@ import memoizeOne from 'memoize-one';
import React from 'react';
import SplitterLayout from 'react-splitter-layout';
import { SQL_FUNCTIONS } from '../../../lib/sql-docs';
import { QueryPlanDialog } from '../../dialogs';
import { EditContextDialog } from '../../dialogs/edit-context-dialog/edit-context-dialog';
import { QueryHistoryDialog } from '../../dialogs/query-history-dialog/query-history-dialog';
@ -63,11 +62,9 @@ import { RunButton } from './run-button/run-button';
import './query-view.scss';
const parserRaw = sqlParserFactory(SQL_FUNCTIONS.map(sqlFunction => sqlFunction.name));
const parser = memoizeOne((sql: string) => {
const parser = memoizeOne((sql: string): SqlQuery | undefined => {
try {
return parserRaw(sql);
return parseSqlQuery(sql);
} catch {
return;
}
@ -87,7 +84,7 @@ export interface QueryViewProps {
export interface QueryViewState {
queryString: string;
parsedQuery: SqlQuery;
parsedQuery?: SqlQuery;
queryContext: QueryContext;
wrapQueryLimit: number | undefined;
autoRun: boolean;
@ -479,18 +476,22 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
const { queryString, queryContext, loading, result, error, columnMetadata } = this.state;
const emptyQuery = QueryView.isEmptyQuery(queryString);
let currentSchema;
let currentTable;
let currentSchema: string | undefined;
let currentTable: string | undefined;
if (result && result.parsedQuery instanceof SqlQuery) {
if (result && result.parsedQuery) {
currentSchema = result.parsedQuery.getSchema();
currentTable = result.parsedQuery.getTableName();
} else if (localStorageGet(LocalStorageKeys.QUERY_KEY)) {
const defaultQueryString = localStorageGet(LocalStorageKeys.QUERY_KEY);
const tempAst = defaultQueryString ? parser(defaultQueryString) : undefined;
if (tempAst) {
currentSchema = tempAst.getSchema();
currentTable = tempAst.getTableName();
const defaultQueryAst: SqlQuery | undefined = defaultQueryString
? parser(defaultQueryString)
: undefined;
if (defaultQueryAst) {
currentSchema = defaultQueryAst.getSchema();
currentTable = defaultQueryAst.getTableName();
}
}