diff --git a/packages/web/src/sqleditor/SqlEditor.js b/packages/web/src/sqleditor/SqlEditor.js index de42bbeed..1dc3b8a5c 100644 --- a/packages/web/src/sqleditor/SqlEditor.js +++ b/packages/web/src/sqleditor/SqlEditor.js @@ -4,6 +4,7 @@ import AceEditor from 'react-ace'; import useDimensions from '../utility/useDimensions'; import { addCompleter, setCompleters } from 'ace-builds/src-noconflict/ext-language_tools'; import { getDatabaseInfo } from '../utility/metadataLoaders'; +import analyseQuerySources from './analyseQuerySources'; const Wrapper = styled.div` position: absolute; @@ -94,6 +95,31 @@ export default function SqlEditor({ } } + const colMatch = line.match(/([a-zA-Z0-9_]+)\.([a-zA-Z0-9_]*)?$/); + if (colMatch && dbinfo) { + const table = colMatch[1]; + const sources = analyseQuerySources(editor.getValue(), [ + ...dbinfo.tables.map((x) => x.pureName), + ...dbinfo.views.map((x) => x.pureName), + ]); + const source = sources.find((x) => (x.alias || x.name) == table); + if (source) { + const table = dbinfo.tables.find((x) => x.pureName == source.name); + if (table) { + list = [ + ...list, + ...table.columns.map((x) => ({ + name: x.columnName, + value: x.columnName, + caption: x.columnName, + meta: 'column', + score: 1000, + })), + ]; + } + } + } + callback(null, list); }, }); @@ -109,7 +135,7 @@ export default function SqlEditor({ if (e.command.name === 'backspace') { // do not hide after backspace } else if (e.command.name === 'insertstring') { - if (!hasCompleter) { + if (!hasCompleter || e.args == '.') { editor.execCommand('startAutocomplete'); } diff --git a/packages/web/src/sqleditor/analyseQuerySources.js b/packages/web/src/sqleditor/analyseQuerySources.js new file mode 100644 index 000000000..f01ca9d70 --- /dev/null +++ b/packages/web/src/sqleditor/analyseQuerySources.js @@ -0,0 +1,34 @@ +export default function analyseQuerySources(sql, sourceNames) { + const upperSourceNames = sourceNames.map((x) => x.toUpperCase()); + const tokens = sql.split(/\s+/); + const res = []; + for (let i = 0; i < tokens.length; i += 1) { + const lastWordMatch = tokens[i].match(/([^.]+)$/); + if (lastWordMatch) { + const word = lastWordMatch[1]; + const wordUpper = word.toUpperCase(); + if (upperSourceNames.includes(wordUpper)) { + const preWord = tokens[i - 1]; + if (preWord && /^(join)|(from)|(update)|(delete)|(insert)$/i.test(preWord)) { + let postWord = tokens[i + 1]; + if (postWord && /^as$/i.test(postWord)) { + postWord = tokens[i + 2]; + } + if (!postWord) { + res.push({ + name: word, + }); + } + if (/^(where)|(inner)|(left)|(right)$/i.test(postWord)) { + continue; + } + res.push({ + name: word, + alias: postWord, + }); + } + } + } + } + return res; +}