From 36b291c28bc0dfd69e46feac07da6812a85831e4 Mon Sep 17 00:00:00 2001 From: roberthovsepyan <33484604+roberthovsepyan@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:09:45 +0100 Subject: [PATCH] feat: add CommonSuggestion and JoinsSuggestion types, migrate postgresql tests to typescript (#105) * refactor: add CommonSuggestion and JoinsSuggestion types * refactor: migrate limit_clause tests to typescript * refactor: migrate offset_clause tests to typescript * refactor: migrate order_by_clause tests to typescript * refactor: migrate select_conditions tests to typescript * refactor: migrate postgresql specific select tests to typescript * refactor: rename CommonSuggestion --------- Co-authored-by: robhovsepyan --- src/autocomplete/index.ts | 13 +- .../grammar/select/limit_clause.test.json | 41 --- .../grammar/select/limit_clause.test.ts | 53 +++ .../grammar/select/offset_clause.test.json | 70 ---- .../grammar/select/offset_clause.test.ts | 65 ++++ .../grammar/select/order_by_clause.test.json | 251 -------------- .../grammar/select/order_by_clause.test.ts | 317 +++++++++++++++++ .../grammar/select/postgresql_select.test.ts | 327 ++++++++++++++++++ .../select/select_conditions.test.jison | 20 -- .../grammar/select/select_conditions.test.ts | 21 ++ 10 files changed, 795 insertions(+), 383 deletions(-) delete mode 100644 src/autocomplete/parsers/postgresql/grammar/select/limit_clause.test.json create mode 100644 src/autocomplete/parsers/postgresql/grammar/select/limit_clause.test.ts delete mode 100644 src/autocomplete/parsers/postgresql/grammar/select/offset_clause.test.json create mode 100644 src/autocomplete/parsers/postgresql/grammar/select/offset_clause.test.ts delete mode 100644 src/autocomplete/parsers/postgresql/grammar/select/order_by_clause.test.json create mode 100644 src/autocomplete/parsers/postgresql/grammar/select/order_by_clause.test.ts create mode 100644 src/autocomplete/parsers/postgresql/grammar/select/postgresql_select.test.ts delete mode 100644 src/autocomplete/parsers/postgresql/grammar/select/select_conditions.test.jison create mode 100644 src/autocomplete/parsers/postgresql/grammar/select/select_conditions.test.ts diff --git a/src/autocomplete/index.ts b/src/autocomplete/index.ts index f6e293a9..12125ca4 100644 --- a/src/autocomplete/index.ts +++ b/src/autocomplete/index.ts @@ -25,7 +25,9 @@ export interface ParseResult { suggestFilters?: FiltersSuggestion; suggestFunctions?: FunctionsSuggestion; suggestValues?: ValuesSuggestion; - suggestGroupBys?: unknown; + suggestGroupBys?: GroupBysSuggestion; + suggestOrderBys?: OrderBysSuggestion; + suggestJoins?: JoinsSuggestion; suggestIdentifiers?: IdentifierSuggestion[]; suggestTemplates?: boolean; suggestEngines?: EnginesSuggestion; @@ -121,6 +123,15 @@ export interface FiltersSuggestion { tables: Table[]; } +export type GroupBysSuggestion = FiltersSuggestion; + +export type OrderBysSuggestion = FiltersSuggestion; + +export interface JoinsSuggestion { + prependJoin?: boolean; + tables: Table[]; +} + export interface ValuesSuggestion { missingEndQuote?: boolean; partialQuote?: boolean; diff --git a/src/autocomplete/parsers/postgresql/grammar/select/limit_clause.test.json b/src/autocomplete/parsers/postgresql/grammar/select/limit_clause.test.json deleted file mode 100644 index 9dbffc9a..00000000 --- a/src/autocomplete/parsers/postgresql/grammar/select/limit_clause.test.json +++ /dev/null @@ -1,41 +0,0 @@ -[ - { - "namePrefix": "should suggest values", - "beforeCursor": "SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT ", - "afterCursor": "", - "noErrors": true, - "expectedResult": { - "lowerCase": false, - "suggestKeywords": ["10", "100", "1000", "10000", "5000"] - } - }, - { - "namePrefix": "should contain LIMIT in suggestions", - "beforeCursor": "SELECT COUNT(*) AS boo FROM testTable GROUP BY baa OFFSET 10 ", - "afterCursor": "", - "noErrors": true, - "containsKeywords": ["LIMIT"], - "expectedResult": { - "lowerCase": false - } - }, - { - "namePrefix": "should not allow to write offset with comma", - "beforeCursor": "SELECT COUNT(*) AS boo FROM testTable GROUP BY baa LIMIT 100, 100 ", - "afterCursor": "", - "noErrors": false, - "expectedErrors": [ - { - "text": ",", - "token": ",", - "line": 0, - "loc": { - "first_line": 1, - "last_line": 1, - "first_column": 60, - "last_column": 61 - } - } - ] - } -] diff --git a/src/autocomplete/parsers/postgresql/grammar/select/limit_clause.test.ts b/src/autocomplete/parsers/postgresql/grammar/select/limit_clause.test.ts new file mode 100644 index 00000000..06df5f57 --- /dev/null +++ b/src/autocomplete/parsers/postgresql/grammar/select/limit_clause.test.ts @@ -0,0 +1,53 @@ +import {expect, test} from '@jest/globals'; + +import {KeywordSuggestion, ParserSyntaxError, parsePostgreSql} from '../../../../index'; + +test('should suggest values', () => { + const parseResult = parsePostgreSql( + 'SELECT COUNT(*) AS test_count FROM test_table GROUP BY test_count LIMIT ', + '', + ); + + expect(parseResult.errors).toBeUndefined(); + + const suggestions: KeywordSuggestion[] = [ + {value: '10', weight: 10000}, + {value: '100', weight: 10000}, + {value: '1000', weight: 10000}, + {value: '10000', weight: 10000}, + {value: '5000', weight: 10000}, + ]; + expect(parseResult.suggestKeywords).toEqual(suggestions); +}); + +test('should contain LIMIT in suggestions', () => { + const parseResult = parsePostgreSql( + 'SELECT COUNT(*) AS test_count FROM test_table GROUP BY test_count OFFSET 10 ', + '', + ); + + expect(parseResult.errors).toBeUndefined(); + + const suggestion: KeywordSuggestion = {value: 'LIMIT', weight: 2.2}; + expect(parseResult.suggestKeywords).toContainEqual(suggestion); +}); + +test('should not allow to include offset after comma', () => { + const parseResult = parsePostgreSql( + 'SELECT COUNT(*) AS test_count FROM test_table GROUP BY test_count LIMIT 100, 100 ', + '', + ); + + const error: Partial = { + text: ',', + token: ',', + line: 0, + loc: { + first_line: 1, + last_line: 1, + first_column: 75, + last_column: 76, + }, + }; + expect(parseResult.errors).toContainEqual(expect.objectContaining(error)); +}); diff --git a/src/autocomplete/parsers/postgresql/grammar/select/offset_clause.test.json b/src/autocomplete/parsers/postgresql/grammar/select/offset_clause.test.json deleted file mode 100644 index d9dea38f..00000000 --- a/src/autocomplete/parsers/postgresql/grammar/select/offset_clause.test.json +++ /dev/null @@ -1,70 +0,0 @@ -[ - { - "namePrefix": "should suggest OFFSET", - "beforeCursor": "SELECT * FROM testTable LIMIT 100 ", - "afterCursor": "", - "noErrors": true, - "containsKeywords": ["OFFSET"], - "expectedResult": { - "lowerCase": false - } - }, - { - "namePrefix": "should not throw errors", - "beforeCursor": "SELECT * FROM testTable OFFSET 100;", - "afterCursor": "", - "noErrors": true, - "containsKeywords": ["SELECT"], - "expectedResult": { - "lowerCase": false - } - }, - { - "namePrefix": "should not throw errors", - "beforeCursor": "SELECT * FROM testTable LIMIT 1 OFFSET 12; ", - "afterCursor": "", - "noErrors": true, - "containsKeywords": ["SELECT"], - "expectedResult": { - "lowerCase": false - } - }, - { - "namePrefix": "should not throw errors", - "beforeCursor": "SELECT * FROM testTable OFFSET 12 LIMIT 1; ", - "afterCursor": "", - "noErrors": true, - "containsKeywords": ["SELECT"], - "expectedResult": { - "lowerCase": false - } - }, - { - "namePrefix": "should not throw errors", - "beforeCursor": "SELECT * FROM testTable LIMIT 1; ", - "afterCursor": "", - "noErrors": true, - "containsKeywords": ["SELECT"], - "expectedResult": { - "lowerCase": false - } - }, - { - "namePrefix": "should suggest OFFSET", - "beforeCursor": "SELECT * FROM testTable WHERE column = 1 ", - "afterCursor": "", - "noErrors": true, - "containsKeywords": ["OFFSET", "LIMIT"], - "expectedResult": { - "lowerCase": false, - "suggestGroupBys": { - "prefix": "GROUP BY", - "tables": [{"identifierChain": [{"name": "testTable"}]}] - }, - "suggestOrderBys": { - "prefix": "ORDER BY", - "tables": [{"identifierChain": [{"name": "testTable"}]}] - } - } - } -] diff --git a/src/autocomplete/parsers/postgresql/grammar/select/offset_clause.test.ts b/src/autocomplete/parsers/postgresql/grammar/select/offset_clause.test.ts new file mode 100644 index 00000000..e257b6d5 --- /dev/null +++ b/src/autocomplete/parsers/postgresql/grammar/select/offset_clause.test.ts @@ -0,0 +1,65 @@ +import {expect, test} from '@jest/globals'; + +import { + GroupBysSuggestion, + KeywordSuggestion, + OrderBysSuggestion, + parsePostgreSql, +} from '../../../../index'; + +test('should suggest OFFSET', () => { + const parseResult = parsePostgreSql('SELECT * FROM test_table LIMIT 100 ', ''); + + expect(parseResult.errors).toBeUndefined(); + + const suggestion: KeywordSuggestion = {value: 'OFFSET', weight: 2.2}; + expect(parseResult.suggestKeywords).toContainEqual(suggestion); +}); + +test('should not throw errors with OFFSET statement', () => { + const parseResult = parsePostgreSql('SELECT * FROM test_table OFFSET 100;', ''); + + expect(parseResult.errors).toBeUndefined(); +}); + +test('should not throw errors with LIMIT OFFSET statement', () => { + const parseResult = parsePostgreSql('SELECT * FROM test_table LIMIT 1 OFFSET 12;', ''); + + expect(parseResult.errors).toBeUndefined(); +}); + +test('should not throw errors with OFFSET LIMIT statement', () => { + const parseResult = parsePostgreSql('SELECT * FROM test_table OFFSET 12 LIMIT 1;', ''); + + expect(parseResult.errors).toBeUndefined(); +}); + +test('should not throw errors with LIMIT statement', () => { + const parseResult = parsePostgreSql('SELECT * FROM test_table LIMIT 1;', ''); + + expect(parseResult.errors).toBeUndefined(); +}); + +test('should suggest OFFSET, LIMIT, GROUP BY, ORDER BY', () => { + const parseResult = parsePostgreSql('SELECT * FROM test_table WHERE test_column = 1 ', ''); + + expect(parseResult.errors).toBeUndefined(); + + const offsetSuggestion: KeywordSuggestion = {value: 'OFFSET', weight: 2.2}; + expect(parseResult.suggestKeywords).toContainEqual(offsetSuggestion); + + const limitSuggestion: KeywordSuggestion = {value: 'LIMIT', weight: 2.3}; + expect(parseResult.suggestKeywords).toContainEqual(limitSuggestion); + + const groupBysSuggestion: GroupBysSuggestion = { + prefix: 'GROUP BY', + tables: [{identifierChain: [{name: 'test_table'}]}], + }; + expect(parseResult.suggestGroupBys).toEqual(groupBysSuggestion); + + const orderBysSuggestion: OrderBysSuggestion = { + prefix: 'ORDER BY', + tables: [{identifierChain: [{name: 'test_table'}]}], + }; + expect(parseResult.suggestOrderBys).toEqual(orderBysSuggestion); +}); diff --git a/src/autocomplete/parsers/postgresql/grammar/select/order_by_clause.test.json b/src/autocomplete/parsers/postgresql/grammar/select/order_by_clause.test.json deleted file mode 100644 index c5addadf..00000000 --- a/src/autocomplete/parsers/postgresql/grammar/select/order_by_clause.test.json +++ /dev/null @@ -1,251 +0,0 @@ -[ - { - "namePrefix": "should suggest keywords", - "beforeCursor": "SELECT * FROM testTable ORDER ", - "afterCursor": "", - "expectedResult": { - "lowerCase": false, - "suggestKeywords": ["BY"], - "suggestOrderBys": { - "prefix": "BY", - "tables": [ - { - "identifierChain": [ - { - "name": "testTable" - } - ] - } - ] - } - } - }, - { - "namePrefix": "should suggest columns", - "beforeCursor": "SELECT * FROM testTable WHERE baa baaa boo ORDER ", - "afterCursor": "", - "expectedResult": { - "lowerCase": false, - "suggestKeywords": ["BY"], - "suggestOrderBys": { - "prefix": "BY", - "tables": [ - { - "identifierChain": [ - { - "name": "testTable" - } - ] - } - ] - } - } - }, - { - "namePrefix": "should suggest columns", - "beforeCursor": "SELECT * FROM testTable ORDER BY ", - "afterCursor": "", - "containsKeywords": ["CASE"], - "expectedResult": { - "lowerCase": false, - "suggestFunctions": {}, - "suggestAnalyticFunctions": true, - "suggestColumns": { - "source": "order by", - "tables": [ - { - "identifierChain": [ - { - "name": "testTable" - } - ] - } - ] - }, - "suggestOrderBys": { - "tables": [ - { - "identifierChain": [ - { - "name": "testTable" - } - ] - } - ] - } - } - }, - { - "namePrefix": "should suggest columns", - "beforeCursor": "SELECT * FROM database_two.testTable ORDER BY ", - "afterCursor": "", - "containsKeywords": ["CASE"], - "expectedResult": { - "suggestColumns": { - "source": "order by", - "tables": [ - { - "identifierChain": [ - { - "name": "database_two" - }, - { - "name": "testTable" - } - ] - } - ] - }, - "suggestFunctions": {}, - "suggestAnalyticFunctions": true, - "suggestOrderBys": { - "tables": [ - { - "identifierChain": [ - { - "name": "database_two" - }, - { - "name": "testTable" - } - ] - } - ] - }, - "lowerCase": false - } - }, - { - "namePrefix": "should suggest keywords", - "beforeCursor": "SELECT * FROM database_two.testTable ORDER BY foo ", - "afterCursor": "", - "expectedResult": { - "lowerCase": false, - "suggestKeywords": ["ASC", "DESC", "LIMIT", "OFFSET", "UNION"] - } - }, - { - "namePrefix": "should suggest keywords", - "beforeCursor": "SELECT * FROM database_two.testTable ORDER BY foo + ", - "afterCursor": "", - "containsKeywords": ["CASE"], - "expectedResult": { - "lowerCase": false, - "suggestFunctions": { - "types": ["NUMBER"] - }, - "suggestColumns": { - "source": "order by", - "types": ["NUMBER"], - "tables": [ - { - "identifierChain": [ - { - "name": "database_two" - }, - { - "name": "testTable" - } - ] - } - ] - } - } - }, - { - "namePrefix": "should suggest columns", - "beforeCursor": "SELECT * FROM database_two.testTable ORDER BY foo, ", - "afterCursor": "", - "containsKeywords": ["CASE"], - "expectedResult": { - "lowerCase": false, - "suggestFunctions": {}, - "suggestAnalyticFunctions": true, - "suggestColumns": { - "source": "order by", - "tables": [ - { - "identifierChain": [ - { - "name": "database_two" - }, - { - "name": "testTable" - } - ] - } - ] - } - } - }, - { - "namePrefix": "should suggest columns", - "beforeCursor": "SELECT * FROM database_two.testTable ORDER BY foo + baa ASC, ", - "afterCursor": "", - "containsKeywords": ["CASE"], - "expectedResult": { - "lowerCase": false, - "suggestFunctions": {}, - "suggestAnalyticFunctions": true, - "suggestColumns": { - "source": "order by", - "tables": [ - { - "identifierChain": [ - { - "name": "database_two" - }, - { - "name": "testTable" - } - ] - } - ] - } - } - }, - { - "namePrefix": "should suggest columns", - "beforeCursor": "SELECT * FROM database_two.testTable ORDER BY foo ASC, ", - "afterCursor": "", - "containsKeywords": ["CASE"], - "expectedResult": { - "lowerCase": false, - "suggestFunctions": {}, - "suggestAnalyticFunctions": true, - "suggestColumns": { - "source": "order by", - "tables": [ - { - "identifierChain": [ - { - "name": "database_two" - }, - { - "name": "testTable" - } - ] - } - ] - } - } - }, - { - "namePrefix": "should suggest keywords", - "beforeCursor": "SELECT * FROM database_two.testTable ORDER BY foo DESC, bar ", - "afterCursor": "", - "expectedResult": { - "lowerCase": false, - "suggestKeywords": ["ASC", "DESC", "LIMIT", "OFFSET", "UNION"] - } - }, - { - "namePrefix": "should suggest keywords", - "beforeCursor": "SELECT * FROM database_two.testTable ORDER BY foo DESC, bar ", - "afterCursor": ", bla", - "containsKeywords": ["ASC", "DESC"], - "expectedResult": { - "lowerCase": false - } - } -] diff --git a/src/autocomplete/parsers/postgresql/grammar/select/order_by_clause.test.ts b/src/autocomplete/parsers/postgresql/grammar/select/order_by_clause.test.ts new file mode 100644 index 00000000..3fa2e31a --- /dev/null +++ b/src/autocomplete/parsers/postgresql/grammar/select/order_by_clause.test.ts @@ -0,0 +1,317 @@ +import {expect, test} from '@jest/globals'; + +import { + ColumnSuggestion, + FunctionsSuggestion, + KeywordSuggestion, + OrderBysSuggestion, + parsePostgreSql, +} from '../../../../index'; + +test('should suggest ORDER BY', () => { + const parseResult = parsePostgreSql('SELECT * FROM test_table ORDER ', ''); + + expect(parseResult.errors).toBeUndefined(); + + const suggestion: KeywordSuggestion = {value: 'BY', weight: -1}; + expect(parseResult.suggestKeywords).toContainEqual(suggestion); + + const orderBysSuggestion: OrderBysSuggestion = { + prefix: 'BY', + tables: [ + { + identifierChain: [ + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestOrderBys).toEqual(orderBysSuggestion); +}); + +test('should suggest ORDER BY after WHERE', () => { + const parseResult = parsePostgreSql( + 'SELECT * FROM test_table WHERE test_column = "test" ORDER ', + '', + ); + + expect(parseResult.errors).toBeUndefined(); + + const suggestion: KeywordSuggestion = {value: 'BY', weight: -1}; + expect(parseResult.suggestKeywords).toContainEqual(suggestion); + + const orderBysSuggestion: OrderBysSuggestion = { + prefix: 'BY', + tables: [ + { + identifierChain: [ + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestOrderBys).toEqual(orderBysSuggestion); +}); + +test('should suggest orderBys, columns, functions', () => { + const parseResult = parsePostgreSql('SELECT * FROM test_table ORDER BY ', ''); + + expect(parseResult.errors).toBeUndefined(); + + expect(parseResult.suggestAnalyticFunctions).toEqual(true); + + const orderBysSuggestion: OrderBysSuggestion = { + tables: [ + { + identifierChain: [ + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestOrderBys).toEqual(orderBysSuggestion); + + const functionsSuggestion: FunctionsSuggestion = {}; + expect(parseResult.suggestFunctions).toEqual(functionsSuggestion); + + const columnsSuggestion: ColumnSuggestion = { + source: 'order by', + tables: [ + { + identifierChain: [ + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestColumns).toEqual(columnsSuggestion); +}); + +test('should suggest orderBys, columns, functions with database name', () => { + const parseResult = parsePostgreSql('SELECT * FROM test_database.test_table ORDER BY ', ''); + + expect(parseResult.errors).toBeUndefined(); + + expect(parseResult.suggestAnalyticFunctions).toEqual(true); + + const orderBysSuggestion: OrderBysSuggestion = { + tables: [ + { + identifierChain: [ + { + name: 'test_database', + }, + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestOrderBys).toEqual(orderBysSuggestion); + + const functionsSuggestion: FunctionsSuggestion = {}; + expect(parseResult.suggestFunctions).toEqual(functionsSuggestion); + + const columnsSuggestion: ColumnSuggestion = { + source: 'order by', + tables: [ + { + identifierChain: [ + { + name: 'test_database', + }, + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestColumns).toEqual(columnsSuggestion); +}); + +test('should suggest keywords', () => { + const parseResult = parsePostgreSql( + 'SELECT * FROM test_database.test_table ORDER BY test_column ', + '', + ); + + expect(parseResult.errors).toBeUndefined(); + + const suggestions: KeywordSuggestion[] = [ + {value: 'ASC', weight: 2.4}, + {value: 'DESC', weight: 2.4}, + {value: 'LIMIT', weight: 2.3}, + {value: 'OFFSET', weight: 2.2}, + {value: 'UNION', weight: 2.11}, + ]; + expect(parseResult.suggestKeywords).toEqual(suggestions); +}); + +test('should suggest CASE, columns, functions after plus sign', () => { + const parseResult = parsePostgreSql( + 'SELECT * FROM test_database.test_table ORDER BY test_column + ', + '', + ); + + expect(parseResult.errors).toBeUndefined(); + + const functionsSuggestion: FunctionsSuggestion = { + types: ['NUMBER'], + }; + expect(parseResult.suggestFunctions).toEqual(functionsSuggestion); + + const columnsSuggestion: ColumnSuggestion = { + source: 'order by', + types: ['NUMBER'], + tables: [ + { + identifierChain: [ + { + name: 'test_database', + }, + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestColumns).toEqual(columnsSuggestion); +}); + +test('should suggest CASE, columns, functions after comma', () => { + const parseResult = parsePostgreSql( + 'SELECT * FROM test_database.test_table ORDER BY test_column, ', + '', + ); + + expect(parseResult.errors).toBeUndefined(); + + expect(parseResult.suggestAnalyticFunctions).toEqual(true); + + const functionsSuggestion: FunctionsSuggestion = {}; + expect(parseResult.suggestFunctions).toEqual(functionsSuggestion); + + const columnsSuggestion: ColumnSuggestion = { + source: 'order by', + tables: [ + { + identifierChain: [ + { + name: 'test_database', + }, + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestColumns).toEqual(columnsSuggestion); +}); + +test('should suggest CASE, columns, functions after plus sign and ASC', () => { + const parseResult = parsePostgreSql( + 'SELECT * FROM test_database.test_table ORDER BY test_column_1 + test_column_2 ASC, ', + '', + ); + + expect(parseResult.errors).toBeUndefined(); + + expect(parseResult.suggestAnalyticFunctions).toEqual(true); + + const functionsSuggestion: FunctionsSuggestion = {}; + expect(parseResult.suggestFunctions).toEqual(functionsSuggestion); + + const columnsSuggestion: ColumnSuggestion = { + source: 'order by', + tables: [ + { + identifierChain: [ + { + name: 'test_database', + }, + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestColumns).toEqual(columnsSuggestion); +}); + +test('should suggest CASE, columns, functions after ASC', () => { + const parseResult = parsePostgreSql( + 'SELECT * FROM test_database.test_table ORDER BY test_column ASC, ', + '', + ); + + expect(parseResult.errors).toBeUndefined(); + + expect(parseResult.suggestAnalyticFunctions).toEqual(true); + + const functionsSuggestion: FunctionsSuggestion = {}; + expect(parseResult.suggestFunctions).toEqual(functionsSuggestion); + + const columnsSuggestion: ColumnSuggestion = { + source: 'order by', + tables: [ + { + identifierChain: [ + { + name: 'test_database', + }, + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestColumns).toEqual(columnsSuggestion); +}); + +test('should suggest keywords after DESC', () => { + const parseResult = parsePostgreSql( + 'SELECT * FROM test_database.test_table ORDER BY test_column_1 DESC, test_column_2 ', + '', + ); + + expect(parseResult.errors).toBeUndefined(); + + const suggestions: KeywordSuggestion[] = [ + {value: 'ASC', weight: 2.4}, + {value: 'DESC', weight: 2.4}, + {value: 'LIMIT', weight: 2.3}, + {value: 'OFFSET', weight: 2.2}, + {value: 'UNION', weight: 2.11}, + ]; + expect(parseResult.suggestKeywords).toEqual(suggestions); +}); + +test('should suggest keywords after DESC midway', () => { + const parseResult = parsePostgreSql( + 'SELECT * FROM test_database.test_table ORDER BY test_column_1 DESC, test_column_2 ', + ', test_column_3', + ); + + const suggestions: KeywordSuggestion[] = [ + {value: 'ASC', weight: 2.4}, + {value: 'DESC', weight: 2.4}, + {value: 'LIMIT', weight: 2.3}, + {value: 'OFFSET', weight: 2.2}, + {value: 'UNION', weight: 2.11}, + ]; + expect(parseResult.suggestKeywords).toEqual(suggestions); +}); diff --git a/src/autocomplete/parsers/postgresql/grammar/select/postgresql_select.test.ts b/src/autocomplete/parsers/postgresql/grammar/select/postgresql_select.test.ts new file mode 100644 index 00000000..7a98b5e0 --- /dev/null +++ b/src/autocomplete/parsers/postgresql/grammar/select/postgresql_select.test.ts @@ -0,0 +1,327 @@ +import {expect, test} from '@jest/globals'; + +import { + FiltersSuggestion, + GroupBysSuggestion, + JoinsSuggestion, + KeywordSuggestion, + OrderBysSuggestion, + ParserSyntaxError, + parsePostgreSql, +} from '../../../../index'; + +test('should suggest keywords, joins, filters, groupBys, orderBys', () => { + const parseResult = parsePostgreSql('SELECT * FROM test_table_1 tt1, test_table_2 ', ''); + + expect(parseResult.errors).toBeUndefined(); + + const keywordsSuggestion: KeywordSuggestion[] = [ + {value: 'AS', weight: 3}, + {value: 'WHERE', weight: 2.7}, + {value: 'GROUP BY', weight: 2.6}, + {value: 'HAVING', weight: 2.5}, + {value: 'ORDER BY', weight: 2.4}, + {value: 'LIMIT', weight: 2.3}, + {value: 'OFFSET', weight: 2.2}, + {value: 'UNION', weight: 2.11}, + {value: 'FULL JOIN', weight: 1}, + {value: 'FULL OUTER JOIN', weight: 1}, + {value: 'INNER JOIN', weight: 1}, + {value: 'JOIN', weight: 1}, + {value: 'LEFT JOIN', weight: 1}, + {value: 'LEFT OUTER JOIN', weight: 1}, + {value: 'RIGHT JOIN', weight: 1}, + {value: 'RIGHT OUTER JOIN', weight: 1}, + ]; + expect(parseResult.suggestKeywords).toEqual(keywordsSuggestion); + + const orderBysSuggestion: OrderBysSuggestion = { + prefix: 'ORDER BY', + tables: [ + { + identifierChain: [ + { + name: 'test_table_1', + }, + ], + alias: 'tt1', + }, + { + identifierChain: [ + { + name: 'test_table_2', + }, + ], + }, + ], + }; + expect(parseResult.suggestOrderBys).toEqual(orderBysSuggestion); + + const groupBysSuggestion: GroupBysSuggestion = { + prefix: 'GROUP BY', + tables: [ + { + identifierChain: [ + { + name: 'test_table_1', + }, + ], + alias: 'tt1', + }, + { + identifierChain: [ + { + name: 'test_table_2', + }, + ], + }, + ], + }; + expect(parseResult.suggestGroupBys).toEqual(groupBysSuggestion); + + const filtersSuggestion: FiltersSuggestion = { + prefix: 'WHERE', + tables: [ + { + identifierChain: [ + { + name: 'test_table_1', + }, + ], + alias: 'tt1', + }, + { + identifierChain: [ + { + name: 'test_table_2', + }, + ], + }, + ], + }; + expect(parseResult.suggestFilters).toEqual(filtersSuggestion); + + const joinsSuggestion: JoinsSuggestion = { + prependJoin: true, + tables: [ + { + identifierChain: [ + { + name: 'test_table_2', + }, + ], + }, + ], + }; + expect(parseResult.suggestJoins).toEqual(joinsSuggestion); +}); + +test('should report VARIABLE_REFERENCE error', () => { + const parseResult = parsePostgreSql('SELECT * FROM test_table limit ${limit=20}; ', ''); + + const error: Partial = { + text: '${limit=20}', + token: 'VARIABLE_REFERENCE', + line: 0, + loc: { + first_line: 1, + last_line: 1, + first_column: 31, + last_column: 42, + }, + }; + + expect(parseResult.errors).toContainEqual(expect.objectContaining(error)); +}); + +test('should suggest keywords, groupBys, orderBys after WHERE', () => { + const parseResult = parsePostgreSql( + 'SELECT test_column FROM test_table WHERE test_column = 1 ', + '', + ); + + expect(parseResult.errors).toBeUndefined(); + + const keywordsSuggestion: KeywordSuggestion[] = [ + {value: 'GROUP BY', weight: 2.8}, + {value: 'HAVING', weight: 2.7}, + {value: 'ORDER BY', weight: 2.5}, + {value: 'LIMIT', weight: 2.3}, + {value: 'OFFSET', weight: 2.2}, + {value: 'UNION', weight: 2.11}, + {value: '<', weight: 2}, + {value: '<=', weight: 2}, + {value: '<=>', weight: 2}, + {value: '<>', weight: 2}, + {value: '=', weight: 2}, + {value: '>', weight: 2}, + {value: '>=', weight: 2}, + {value: 'AND', weight: 2}, + {value: 'BETWEEN', weight: 2}, + {value: 'IN', weight: 2}, + {value: 'IS FALSE', weight: 2}, + {value: 'IS NOT FALSE', weight: 2}, + {value: 'IS NOT NULL', weight: 2}, + {value: 'IS NOT TRUE', weight: 2}, + {value: 'IS NULL', weight: 2}, + {value: 'IS TRUE', weight: 2}, + {value: 'NOT BETWEEN', weight: 2}, + {value: 'NOT IN', weight: 2}, + {value: 'OR', weight: 2}, + ]; + expect(parseResult.suggestKeywords).toEqual(keywordsSuggestion); + + const orderBysSuggestion: OrderBysSuggestion = { + prefix: 'ORDER BY', + tables: [ + { + identifierChain: [ + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestOrderBys).toEqual(orderBysSuggestion); + + const groupBysSuggestion: GroupBysSuggestion = { + prefix: 'GROUP BY', + tables: [ + { + identifierChain: [ + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestGroupBys).toEqual(groupBysSuggestion); +}); + +test('should suggest keywords, groupBys, orderBys after null safe WHERE', () => { + const parseResult = parsePostgreSql( + 'SELECT test_column FROM test_table WHERE test_column <=> 1 ', + '', + ); + + expect(parseResult.errors).toBeUndefined(); + + const keywordsSuggestion: KeywordSuggestion[] = [ + {value: 'GROUP BY', weight: 2.8}, + {value: 'HAVING', weight: 2.7}, + {value: 'ORDER BY', weight: 2.5}, + {value: 'LIMIT', weight: 2.3}, + {value: 'OFFSET', weight: 2.2}, + {value: 'UNION', weight: 2.11}, + {value: '<', weight: 2}, + {value: '<=', weight: 2}, + {value: '<=>', weight: 2}, + {value: '<>', weight: 2}, + {value: '=', weight: 2}, + {value: '>', weight: 2}, + {value: '>=', weight: 2}, + {value: 'AND', weight: 2}, + {value: 'BETWEEN', weight: 2}, + {value: 'IN', weight: 2}, + {value: 'IS FALSE', weight: 2}, + {value: 'IS NOT FALSE', weight: 2}, + {value: 'IS NOT NULL', weight: 2}, + {value: 'IS NOT TRUE', weight: 2}, + {value: 'IS NULL', weight: 2}, + {value: 'IS TRUE', weight: 2}, + {value: 'NOT BETWEEN', weight: 2}, + {value: 'NOT IN', weight: 2}, + {value: 'OR', weight: 2}, + ]; + expect(parseResult.suggestKeywords).toEqual(keywordsSuggestion); + + const orderBysSuggestion: OrderBysSuggestion = { + prefix: 'ORDER BY', + tables: [ + { + identifierChain: [ + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestOrderBys).toEqual(orderBysSuggestion); + + const groupBysSuggestion: GroupBysSuggestion = { + prefix: 'GROUP BY', + tables: [ + { + identifierChain: [ + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestGroupBys).toEqual(groupBysSuggestion); +}); + +test('should suggest LIMIT, OFFSET, joins, filters, groupBys, orderBys', () => { + const parseResult = parsePostgreSql('SELECT * FROM test_table ', ''); + + expect(parseResult.errors).toBeUndefined(); + + const limitSuggestion: KeywordSuggestion = {value: 'LIMIT', weight: 2.3}; + expect(parseResult.suggestKeywords).toContainEqual(limitSuggestion); + + const offsetSuggestion: KeywordSuggestion = {value: 'OFFSET', weight: 2.2}; + expect(parseResult.suggestKeywords).toContainEqual(offsetSuggestion); + + const orderBysSuggestion: OrderBysSuggestion = { + prefix: 'ORDER BY', + tables: [ + { + identifierChain: [ + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestOrderBys).toEqual(orderBysSuggestion); + + const groupBysSuggestion: GroupBysSuggestion = { + prefix: 'GROUP BY', + tables: [ + { + identifierChain: [ + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestGroupBys).toEqual(groupBysSuggestion); + + const filtersSuggestion: FiltersSuggestion = { + prefix: 'WHERE', + tables: [ + { + identifierChain: [ + { + name: 'test_table', + }, + ], + }, + ], + }; + expect(parseResult.suggestFilters).toEqual(filtersSuggestion); + + const joinsSuggestion: JoinsSuggestion = { + prependJoin: true, + tables: [{identifierChain: [{name: 'test_table'}]}], + }; + expect(parseResult.suggestJoins).toEqual(joinsSuggestion); +}); diff --git a/src/autocomplete/parsers/postgresql/grammar/select/select_conditions.test.jison b/src/autocomplete/parsers/postgresql/grammar/select/select_conditions.test.jison deleted file mode 100644 index e7d5f6d4..00000000 --- a/src/autocomplete/parsers/postgresql/grammar/select/select_conditions.test.jison +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "namePrefix": "should suggest columns", - "beforeCursor": "SELECT * FROM testTable ORDER BY bla bla bla boo ", - "afterCursor": "", - "containsKeywords": ["LIMIT", "UNION"], - "expectedResult": { - "lowerCase": false - } - }, - { - "namePrefix": "should suggest columns", - "beforeCursor": "SELECT * FROM testTable ORDER BY bla bla bla boo ", - "afterCursor": "", - "containsKeywords": ["LIMIT", "UNION"], - "expectedResult": { - "lowerCase": false - } - } -] diff --git a/src/autocomplete/parsers/postgresql/grammar/select/select_conditions.test.ts b/src/autocomplete/parsers/postgresql/grammar/select/select_conditions.test.ts new file mode 100644 index 00000000..c4f21b04 --- /dev/null +++ b/src/autocomplete/parsers/postgresql/grammar/select/select_conditions.test.ts @@ -0,0 +1,21 @@ +import {expect, test} from '@jest/globals'; + +import {KeywordSuggestion, parsePostgreSql} from '../../../../index'; + +test('should suggest keywords', () => { + const parseResult = parsePostgreSql( + 'SELECT * FROM test_table ORDER BY test_column_1, test_column_2, test_column_3, test_column_4 ', + '', + ); + + expect(parseResult.errors).toBeUndefined(); + + const suggestions: KeywordSuggestion[] = [ + {value: 'ASC', weight: 2.4}, + {value: 'DESC', weight: 2.4}, + {value: 'LIMIT', weight: 2.3}, + {value: 'OFFSET', weight: 2.2}, + {value: 'UNION', weight: 2.11}, + ]; + expect(parseResult.suggestKeywords).toEqual(suggestions); +});