diff --git a/web-console/src/views/workbench-view/flexible-query-input/flexible-query-input.tsx b/web-console/src/views/workbench-view/flexible-query-input/flexible-query-input.tsx index 8691a622da9..ad5b7b38141 100644 --- a/web-console/src/views/workbench-view/flexible-query-input/flexible-query-input.tsx +++ b/web-console/src/views/workbench-view/flexible-query-input/flexible-query-input.tsx @@ -72,9 +72,10 @@ export interface FlexibleQueryInputProps { export interface FlexibleQueryInputState { // For reasons (https://github.com/securingsincity/react-ace/issues/415) react ace editor needs an explicit height - // Since this component will grown and shrink dynamically we will measure its height and then set it. + // Since this component will grow and shrink dynamically we will measure its height and then set it. editorHeight: number; - completions: any[]; + quotedCompletions: Ace.Completion[]; + unquotedCompletions: Ace.Completion[]; prevColumnMetadata?: readonly ColumnMetadata[]; prevCurrentTable?: string; prevCurrentSchema?: string; @@ -89,7 +90,7 @@ export class FlexibleQueryInput extends React.PureComponent< static replaceDefaultAutoCompleter(): void { if (!langTools) return; - const keywordList = ([] as any[]).concat( + const keywordList = ([] as Ace.Completion[]).concat( SQL_KEYWORDS.map(v => ({ name: v, value: v, score: 0, meta: 'keyword' })), SQL_EXPRESSION_PARTS.map(v => ({ name: v, value: v, score: 0, meta: 'keyword' })), SQL_CONSTANTS.map(v => ({ name: v, value: v, score: 0, meta: 'constant' })), @@ -108,7 +109,13 @@ export class FlexibleQueryInput extends React.PureComponent< langTools.snippetCompleter, langTools.textCompleter, { - getCompletions: (_editor: any, _session: any, _pos: any, _prefix: any, callback: any) => { + getCompletions: ( + _state: string, + _session: Ace.EditSession, + _pos: Ace.Point, + _prefix: string, + callback: any, + ) => { return callback(null, keywordList); }, getDocTooltip: (item: any) => { @@ -123,17 +130,19 @@ export class FlexibleQueryInput extends React.PureComponent< static addFunctionAutoCompleter(): void { if (!langTools) return; - const functionList: any[] = Object.entries(SQL_FUNCTIONS).flatMap(([name, versions]) => { - return versions.map(([args, description]) => ({ - name: name, - value: versions.length > 1 ? `${name}(${args})` : name, - score: 1100, // Use a high score to appear over the 'local' suggestions that have a score of 1000 - meta: 'function', - syntax: `${name}(${args})`, - description, - completer: COMPLETER, - })); - }); + const functionList: Ace.Completion[] = Object.entries(SQL_FUNCTIONS).flatMap( + ([name, versions]) => { + return versions.map(([args, description]) => ({ + name: name, + value: versions.length > 1 ? `${name}(${args})` : name, + score: 1100, // Use a high score to appear over the 'local' suggestions that have a score of 1000 + meta: 'function', + syntax: `${name}(${args})`, + description, + completer: COMPLETER, + })); + }, + ); langTools.addCompleter({ getCompletions: (_editor: any, _session: any, _pos: any, _prefix: any, callback: any) => { @@ -154,6 +163,43 @@ export class FlexibleQueryInput extends React.PureComponent<
${item.description}
`; } + static getCompletions( + columnMetadata: readonly ColumnMetadata[], + currentSchema: string | undefined, + currentTable: string | undefined, + quote: boolean, + ): Ace.Completion[] { + return ([] as Ace.Completion[]).concat( + uniq(columnMetadata.map(d => d.TABLE_SCHEMA)).map(v => ({ + value: quote ? String(T(v)) : v, + score: 10, + meta: 'schema', + })), + uniq( + columnMetadata + .filter(d => (currentSchema ? d.TABLE_SCHEMA === currentSchema : true)) + .map(d => d.TABLE_NAME), + ).map(v => ({ + value: quote ? String(T(v)) : v, + score: 49, + meta: 'datasource', + })), + uniq( + columnMetadata + .filter(d => + currentTable && currentSchema + ? d.TABLE_NAME === currentTable && d.TABLE_SCHEMA === currentSchema + : true, + ) + .map(d => d.COLUMN_NAME), + ).map(v => ({ + value: quote ? String(C(v)) : v, + score: 50, + meta: 'column', + })), + ); + } + static getDerivedStateFromProps(props: FlexibleQueryInputProps, state: FlexibleQueryInputState) { const { columnMetadata, currentSchema, currentTable } = props; @@ -163,38 +209,19 @@ export class FlexibleQueryInput extends React.PureComponent< currentSchema !== state.prevCurrentSchema || currentTable !== state.prevCurrentTable) ) { - const completions = ([] as any[]).concat( - uniq(columnMetadata.map(d => d.TABLE_SCHEMA)).map(v => ({ - value: String(T(v)), - score: 10, - meta: 'schema', - })), - uniq( - columnMetadata - .filter(d => (currentSchema ? d.TABLE_SCHEMA === currentSchema : true)) - .map(d => d.TABLE_NAME), - ).map(v => ({ - value: String(T(v)), - score: 49, - meta: 'datasource', - })), - uniq( - columnMetadata - .filter(d => - currentTable && currentSchema - ? d.TABLE_NAME === currentTable && d.TABLE_SCHEMA === currentSchema - : true, - ) - .map(d => d.COLUMN_NAME), - ).map(v => ({ - value: String(C(v)), - score: 50, - meta: 'column', - })), - ); - return { - completions, + quotedCompletions: FlexibleQueryInput.getCompletions( + columnMetadata, + currentSchema, + currentTable, + true, + ), + unquotedCompletions: FlexibleQueryInput.getCompletions( + columnMetadata, + currentSchema, + currentTable, + false, + ), prevColumnMetadata: columnMetadata, prevCurrentSchema: currentSchema, prevCurrentTable: currentTable, @@ -207,7 +234,8 @@ export class FlexibleQueryInput extends React.PureComponent< super(props, context); this.state = { editorHeight: 200, - completions: [], + quotedCompletions: [], + unquotedCompletions: [], }; } @@ -216,8 +244,20 @@ export class FlexibleQueryInput extends React.PureComponent< FlexibleQueryInput.addFunctionAutoCompleter(); if (langTools) { langTools.addCompleter({ - getCompletions: (_editor: any, _session: any, _pos: any, _prefix: any, callback: any) => { - callback(null, this.state.completions); + getCompletions: ( + _state: string, + session: Ace.EditSession, + pos: Ace.Point, + prefix: string, + callback: any, + ) => { + const charBeforePrefix = session.getLine(pos.row)[pos.column - prefix.length - 1]; + callback( + null, + charBeforePrefix === '"' + ? this.state.unquotedCompletions + : this.state.quotedCompletions, + ); }, }); }