diff --git a/README.md b/README.md index 7e8aeeaa..8afc7767 100644 --- a/README.md +++ b/README.md @@ -79,4 +79,9 @@ Please restart sql-language-server process after create .sqlrc.json. ### TODO -Now only support select statement, we are planning to support other statements in the future. +- [x] SELECT +- [x] INSERT +- [ ] UPDATE +- [ ] DELETE +- [ ] Beautify +- [ ] Lint diff --git a/packages/server/complete.ts b/packages/server/complete.ts index 9457a19f..1500d3bc 100644 --- a/packages/server/complete.ts +++ b/packages/server/complete.ts @@ -66,12 +66,12 @@ function toCompletionItemFromColumn(column: Column): CompletionItem { } } -function getCandidatesFromColumnRefNode(columnRefNode: ColumnRefNode, schema: Schema): CompletionItem[] { - const tableCandidates = schema.filter(v => v.tableName.startsWith(columnRefNode.table)).map(v => toCompletionItemFromTable(v)) +function getTableAndColumnCondidates(tablePrefix: string, schema: Schema, option?: { withoutTable?: boolean }): CompletionItem[] { + const tableCandidates = schema.filter(v => v.tableName.startsWith(tablePrefix)).map(v => toCompletionItemFromTable(v)) const columnCandidates = Array.prototype.concat.apply([], schema.filter(v => tableCandidates.map(v => v.label).includes(v.tableName)).map(v => v.columns) ).map((v: Column) => toCompletionItemFromColumn(v)) - return tableCandidates.concat(columnCandidates) + return option?.withoutTable ? columnCandidates: tableCandidates.concat(columnCandidates) } function isCursorOnFromClause(sql: string, pos: Pos) { @@ -125,7 +125,12 @@ function createTablesFromFromNodes(fromNodes: FromTableNode[]): Schema { } function getCandidatesFromError(target: string, schema: Schema, pos: Pos, e: any, fromNodes: FromTableNode[]): CompletionItem[] { - let candidates = extractExpectedLiterals(e.expected) + switch(e.message) { + case 'EXPECTED COLUMN NAME': { + return getTableAndColumnCondidates('', schema, { withoutTable: true }) + } + } + let candidates = extractExpectedLiterals(e.expected || []) const candidatesLiterals = candidates.map(v => v.label) if (candidatesLiterals.includes("'") || candidatesLiterals.includes('"')) { return [] @@ -167,7 +172,6 @@ function getRidOfAfterCursorString(sql: string, pos: Pos) { return sql.split('\n').filter((_v, idx) => pos.line >= idx).map((v, idx) => idx === pos.line ? v.slice(0, pos.column) : v).join('\n') } - export default function complete(sql: string, pos: Pos, schema: Schema = []) { logger.debug(`complete: ${sql}, ${JSON.stringify(pos)}`) let candidates: CompletionItem[] = [] @@ -190,7 +194,7 @@ export default function complete(sql: string, pos: Pos, schema: Schema = []) { const columnRef = getColumnRefByPos(selectColumnRefs.concat(whereColumnRefs), pos) logger.debug(JSON.stringify(columnRef)) if (columnRef) { - candidates = candidates.concat(getCandidatesFromColumnRefNode(columnRef, schema)) + candidates = candidates.concat(getTableAndColumnCondidates(columnRef.table, schema)) } } diff --git a/packages/server/database_libs/PostgresClient.ts b/packages/server/database_libs/PostgresClient.ts index 32ab205a..2ae468a9 100644 --- a/packages/server/database_libs/PostgresClient.ts +++ b/packages/server/database_libs/PostgresClient.ts @@ -7,7 +7,6 @@ export default class PosgresClient extends AbstractClient { constructor(settings: Settings) { super(settings) - } connect() { diff --git a/packages/server/package.json b/packages/server/package.json index 87e23c47..6a91e96c 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -17,7 +17,7 @@ "dist" ], "dependencies": { - "@joe-re/node-sql-parser": "^0.1.1", + "@joe-re/node-sql-parser": "^0.2.0", "@types/node": "12.12.6", "@types/pg": "^7.4.10", "jest": "^26.0.1", diff --git a/packages/server/rollup.config.js b/packages/server/rollup.config.js index b840cfd2..fd51c3b7 100644 --- a/packages/server/rollup.config.js +++ b/packages/server/rollup.config.js @@ -27,7 +27,6 @@ export default { } }), commonjs({ - include: ["node_modules/**/*", "../../node_modules/**/*"], ignore: ['pg-native' , './native'] }) ] diff --git a/packages/server/test/complete.test.ts b/packages/server/test/complete.test.ts index 84118cb9..72dc22b4 100644 --- a/packages/server/test/complete.test.ts +++ b/packages/server/test/complete.test.ts @@ -25,6 +25,31 @@ describe('keyword completion', () => { expect(result.candidates.length).toEqual(1) expect(result.candidates[0].label).toEqual('DISTINCT') }) + + test("complete 'INESRT' keyword", () => { + const result = complete('I', { line: 0, column: 1 }) + expect(result.candidates.length).toEqual(1) + expect(result.candidates[0].label).toEqual('INSERT') + }) + + test("complete 'INTO' keyword", () => { + const result = complete('INSERT I', { line: 0, column: 8 }) + expect(result.candidates.length).toEqual(1) + expect(result.candidates[0].label).toEqual('INTO') + }) + + test("complete 'INTO' keyword", () => { + const result = complete('INSERT I', { line: 0, column: 8 }) + expect(result.candidates.length).toEqual(1) + expect(result.candidates[0].label).toEqual('INTO') + }) + + test("complete 'VALUES' keyword", () => { + const sql = 'INSERT INTO FOO ( BAR ) V' + const result = complete('INSERT INTO FOO (BAR) V', { line: 0, column: sql.length }) + expect(result.candidates.length).toEqual(1) + expect(result.candidates[0].label).toEqual('VALUES') + }) }) const SIMPLE_SCHEMA = [ @@ -292,3 +317,20 @@ describe('From clause subquery', () => { expect(result.candidates[0].label).toEqual('sub_id') }) }) + +describe('INSERT statement', () => { + test('complete table name', () => { + const sql = 'INSERT INTO T' + const result = complete(sql, { line: 0, column: sql.length }, SIMPLE_SCHEMA) + expect(result.candidates.length).toEqual(1) + expect(result.candidates[0].label).toEqual('TABLE1') + }) + + test('complete column name', () => { + const sql = 'INSERT INTO TABLE1 (C' + const result = complete(sql, { line: 0, column: sql.length }, SIMPLE_SCHEMA) + expect(result.candidates.length).toEqual(2) + expect(result.candidates[0].label).toEqual('COLUMN1') + expect(result.candidates[1].label).toEqual('COLUMN2') + }) +}) \ No newline at end of file