diff --git a/src/parser/flink/index.ts b/src/parser/flink/index.ts index 53b2bdcc..2b5d83c8 100644 --- a/src/parser/flink/index.ts +++ b/src/parser/flink/index.ts @@ -156,7 +156,12 @@ export class FlinkSQL extends BasicSQL syn.syntaxContextType === syntaxContextType + ) + ) { originalSyntaxSuggestions.push({ syntaxContextType, wordRanges: tokenRanges, diff --git a/src/parser/hive/index.ts b/src/parser/hive/index.ts index 67e65084..cd8581a1 100644 --- a/src/parser/hive/index.ts +++ b/src/parser/hive/index.ts @@ -144,7 +144,12 @@ export class HiveSQL extends BasicSQL syn.syntaxContextType === syntaxContextType + ) + ) { originalSyntaxSuggestions.push({ syntaxContextType, wordRanges: tokenRanges, diff --git a/src/parser/impala/index.ts b/src/parser/impala/index.ts index 2bff4bbe..05301229 100644 --- a/src/parser/impala/index.ts +++ b/src/parser/impala/index.ts @@ -132,7 +132,12 @@ export class ImpalaSQL extends BasicSQL syn.syntaxContextType === syntaxContextType + ) + ) { originalSyntaxSuggestions.push({ syntaxContextType, wordRanges: tokenRanges, diff --git a/src/parser/mysql/index.ts b/src/parser/mysql/index.ts index b2868b0c..5feb8e4c 100644 --- a/src/parser/mysql/index.ts +++ b/src/parser/mysql/index.ts @@ -121,7 +121,12 @@ export class MySQL extends BasicSQL { break; } - if (syntaxContextType) { + if ( + syntaxContextType && + !originalSyntaxSuggestions.some( + (syn) => syn.syntaxContextType === syntaxContextType + ) + ) { originalSyntaxSuggestions.push({ syntaxContextType, wordRanges: tokenRanges, diff --git a/src/parser/postgresql/index.ts b/src/parser/postgresql/index.ts index a62276cf..6bd20747 100644 --- a/src/parser/postgresql/index.ts +++ b/src/parser/postgresql/index.ts @@ -156,7 +156,12 @@ export class PostgreSQL extends BasicSQL syn.syntaxContextType === syntaxContextType + ) + ) { originalSyntaxSuggestions.push({ syntaxContextType, wordRanges: tokenRanges, diff --git a/src/parser/spark/index.ts b/src/parser/spark/index.ts index f0125fc3..59e95ca3 100644 --- a/src/parser/spark/index.ts +++ b/src/parser/spark/index.ts @@ -139,7 +139,12 @@ export class SparkSQL extends BasicSQL syn.syntaxContextType === syntaxContextType + ) + ) { originalSyntaxSuggestions.push({ syntaxContextType, wordRanges: tokenRanges, diff --git a/src/parser/trino/index.ts b/src/parser/trino/index.ts index 8b70c1e0..906e01c2 100644 --- a/src/parser/trino/index.ts +++ b/src/parser/trino/index.ts @@ -145,7 +145,12 @@ export class TrinoSQL extends BasicSQL syn.syntaxContextType === syntaxContextType + ) + ) { originalSyntaxSuggestions.push({ syntaxContextType, wordRanges: tokenRanges, diff --git a/test/parser/flink/suggestion/fixtures/syntaxSuggestion.sql b/test/parser/flink/suggestion/fixtures/syntaxSuggestion.sql index 15fb1890..f7cb677d 100644 --- a/test/parser/flink/suggestion/fixtures/syntaxSuggestion.sql +++ b/test/parser/flink/suggestion/fixtures/syntaxSuggestion.sql @@ -47,3 +47,7 @@ SELECT * FROM Orders ORDER BY orderTime LIMIT length(order_id); SELECT age CASE WHEN age < 18 THEN 1 ELSE 0 END AS is_minor FROM dt_catalog.dt_db.subscriptions; CREATE TABLE tmp_table (col INT) WITH ('connector'='kafka'); + +SELECT FROM tb1; + +SELECT age FROM tb1; \ No newline at end of file diff --git a/test/parser/flink/suggestion/syntaxSuggestion.test.ts b/test/parser/flink/suggestion/syntaxSuggestion.test.ts index bd3d9b79..41827cfb 100644 --- a/test/parser/flink/suggestion/syntaxSuggestion.test.ts +++ b/test/parser/flink/suggestion/syntaxSuggestion.test.ts @@ -498,6 +498,39 @@ describe('Flink SQL Syntax Suggestion', () => { }); }); + test('Sync suggestion no duplicate syntaxContextType', () => { + const pos: CaretPosition = { + lineNumber: 51, + column: 8, + }; + const syntaxes = flink.getSuggestionAtCaretPosition( + commentOtherLine(syntaxSql, pos.lineNumber), + pos + )?.syntax; + const syntaxContextTypes = syntaxes?.map((syn) => syn.syntaxContextType); + + expect(syntaxContextTypes).not.toBeUndefined(); + expect(syntaxContextTypes).toEqual([EntityContextType.FUNCTION, EntityContextType.COLUMN]); + }); + + test('Select function or column', () => { + const pos: CaretPosition = { + lineNumber: 53, + column: 11, + }; + const syntaxes = flink.getSuggestionAtCaretPosition( + commentOtherLine(syntaxSql, pos.lineNumber), + pos + )?.syntax; + const wordRangesArr = syntaxes?.map((syn) => syn.wordRanges); + + expect(wordRangesArr).not.toBeUndefined(); + expect(wordRangesArr.length).toBe(2); + expect( + wordRangesArr.map((wordRanges) => wordRanges.map((wordRange) => wordRange.text)) + ).toEqual([['age'], ['age']]); + }); + test('Syntax suggestion after a comment', () => { const sql = `-- the comment\nSELECT * FROM db.`; const pos: CaretPosition = { diff --git a/test/parser/hive/suggestion/fixtures/syntaxSuggestion.sql b/test/parser/hive/suggestion/fixtures/syntaxSuggestion.sql index 840d210a..68512ff3 100644 --- a/test/parser/hive/suggestion/fixtures/syntaxSuggestion.sql +++ b/test/parser/hive/suggestion/fixtures/syntaxSuggestion.sql @@ -58,4 +58,8 @@ SELECT a, COUNT(b) OVER (PARTITION BY c, d) FROM T; SELECT a.* FROM a JOIN b ON (a.id = b.id AND a.department = b.department); -SELECT col1, col2, CASE WHEN month = 'January' THEN 2023 ELSE 2024 END AS year, CAST(day AS int) AS day FROM source_table; \ No newline at end of file +SELECT col1, col2, CASE WHEN month = 'January' THEN 2023 ELSE 2024 END AS year, CAST(day AS int) AS day FROM source_table; + +SELECT FROM tb1; + +SELECT age FROM tb1; \ No newline at end of file diff --git a/test/parser/hive/suggestion/syntaxSuggestion.test.ts b/test/parser/hive/suggestion/syntaxSuggestion.test.ts index 764c90eb..19db9684 100644 --- a/test/parser/hive/suggestion/syntaxSuggestion.test.ts +++ b/test/parser/hive/suggestion/syntaxSuggestion.test.ts @@ -592,6 +592,39 @@ describe('Hive SQL Syntax Suggestion', () => { expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['month']); }); + test('Sync suggestion no duplicate syntaxContextType', () => { + const pos: CaretPosition = { + lineNumber: 63, + column: 8, + }; + const syntaxes = hive.getSuggestionAtCaretPosition( + commentOtherLine(syntaxSql, pos.lineNumber), + pos + )?.syntax; + const syntaxContextTypes = syntaxes?.map((syn) => syn.syntaxContextType); + + expect(syntaxContextTypes).not.toBeUndefined(); + expect(syntaxContextTypes).toEqual([EntityContextType.COLUMN, EntityContextType.FUNCTION]); + }); + + test('Select function or column', () => { + const pos: CaretPosition = { + lineNumber: 65, + column: 11, + }; + const syntaxes = hive.getSuggestionAtCaretPosition( + commentOtherLine(syntaxSql, pos.lineNumber), + pos + )?.syntax; + const wordRangesArr = syntaxes?.map((syn) => syn.wordRanges); + + expect(wordRangesArr).not.toBeUndefined(); + expect(wordRangesArr.length).toBe(2); + expect( + wordRangesArr.map((wordRanges) => wordRanges.map((wordRange) => wordRange.text)) + ).toEqual([['age'], ['age']]); + }); + test('Syntax suggestion after a comment', () => { const sql = `-- the comment\nSELECT * FROM db.`; const pos: CaretPosition = { diff --git a/test/parser/impala/suggestion/fixtures/syntaxSuggestion.sql b/test/parser/impala/suggestion/fixtures/syntaxSuggestion.sql index b92003be..995db9c2 100644 --- a/test/parser/impala/suggestion/fixtures/syntaxSuggestion.sql +++ b/test/parser/impala/suggestion/fixtures/syntaxSuggestion.sql @@ -44,4 +44,8 @@ ALTER TABLE my_table ADD COLUMN age INT COMMENT 'Updated Age'; CREATE TABLE kudu_no_partition_by_clause (id bigint PRIMARY KEY, s STRING, b BOOLEAN ) STORED AS KUDU; -CREATE TABLE PARTITIONS_YES PARTITIONED BY (YEAR, MONTH); \ No newline at end of file +CREATE TABLE PARTITIONS_YES PARTITIONED BY (YEAR, MONTH); + +SELECT FROM tb1; + +SELECT age FROM tb1; \ No newline at end of file diff --git a/test/parser/impala/suggestion/syntaxSuggestion.test.ts b/test/parser/impala/suggestion/syntaxSuggestion.test.ts index d2cb2a62..aa0ef448 100644 --- a/test/parser/impala/suggestion/syntaxSuggestion.test.ts +++ b/test/parser/impala/suggestion/syntaxSuggestion.test.ts @@ -463,6 +463,39 @@ describe('Impala SQL Syntax Suggestion', () => { expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['YEAR']); }); + test('Sync suggestion no duplicate syntaxContextType', () => { + const pos: CaretPosition = { + lineNumber: 49, + column: 8, + }; + const syntaxes = impala.getSuggestionAtCaretPosition( + commentOtherLine(syntaxSql, pos.lineNumber), + pos + )?.syntax; + const syntaxContextTypes = syntaxes?.map((syn) => syn.syntaxContextType); + + expect(syntaxContextTypes).not.toBeUndefined(); + expect(syntaxContextTypes).toEqual([EntityContextType.COLUMN, EntityContextType.FUNCTION]); + }); + + test('Select function or column', () => { + const pos: CaretPosition = { + lineNumber: 51, + column: 11, + }; + const syntaxes = impala.getSuggestionAtCaretPosition( + commentOtherLine(syntaxSql, pos.lineNumber), + pos + )?.syntax; + const wordRangesArr = syntaxes?.map((syn) => syn.wordRanges); + + expect(wordRangesArr).not.toBeUndefined(); + expect(wordRangesArr.length).toBe(2); + expect( + wordRangesArr.map((wordRanges) => wordRanges.map((wordRange) => wordRange.text)) + ).toEqual([['age'], ['age']]); + }); + test('Syntax suggestion after a comment', () => { const sql = `-- the comment\nSELECT * FROM db.`; const pos: CaretPosition = { diff --git a/test/parser/mysql/suggestion/fixtures/syntaxSuggestion.sql b/test/parser/mysql/suggestion/fixtures/syntaxSuggestion.sql index 878c11db..c51bd8d0 100644 --- a/test/parser/mysql/suggestion/fixtures/syntaxSuggestion.sql +++ b/test/parser/mysql/suggestion/fixtures/syntaxSuggestion.sql @@ -60,4 +60,8 @@ SELECT user, MAX(salary) FROM users where age = 10 GROUP BY length(user) HAVING SELECT c.category_id FROM category c JOIN product p ON c.category_id = p.category_id; -SELECT score, CASE WHEN score >= 90 THEN 'A' ELSE 'F' END AS grade FROM students; \ No newline at end of file +SELECT score, CASE WHEN score >= 90 THEN 'A' ELSE 'F' END AS grade FROM students; + +SELECT FROM tb1; + +SELECT age FROM tb1; diff --git a/test/parser/mysql/suggestion/syntaxSuggestion.test.ts b/test/parser/mysql/suggestion/syntaxSuggestion.test.ts index ea0f6d66..56dbd245 100644 --- a/test/parser/mysql/suggestion/syntaxSuggestion.test.ts +++ b/test/parser/mysql/suggestion/syntaxSuggestion.test.ts @@ -641,6 +641,39 @@ describe('MySQL Syntax Suggestion', () => { expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['score']); }); + test('Sync suggestion no duplicate syntaxContextType', () => { + const pos: CaretPosition = { + lineNumber: 65, + column: 8, + }; + const syntaxes = mysql.getSuggestionAtCaretPosition( + commentOtherLine(syntaxSql, pos.lineNumber), + pos + )?.syntax; + const syntaxContextTypes = syntaxes?.map((syn) => syn.syntaxContextType); + + expect(syntaxContextTypes).not.toBeUndefined(); + expect(syntaxContextTypes).toEqual([EntityContextType.FUNCTION, EntityContextType.COLUMN]); + }); + + test('Select function or column', () => { + const pos: CaretPosition = { + lineNumber: 67, + column: 11, + }; + const syntaxes = mysql.getSuggestionAtCaretPosition( + commentOtherLine(syntaxSql, pos.lineNumber), + pos + )?.syntax; + const wordRangesArr = syntaxes?.map((syn) => syn.wordRanges); + + expect(wordRangesArr).not.toBeUndefined(); + expect(wordRangesArr.length).toBe(2); + expect( + wordRangesArr.map((wordRanges) => wordRanges.map((wordRange) => wordRange.text)) + ).toEqual([['age'], ['age']]); + }); + test('Syntax suggestion after a comment', () => { const sql = `-- the comment\nSELECT * FROM db.`; const pos: CaretPosition = { diff --git a/test/parser/postgresql/suggestion/fixtures/syntaxSuggestion.sql b/test/parser/postgresql/suggestion/fixtures/syntaxSuggestion.sql index 7870c462..1383abfb 100644 --- a/test/parser/postgresql/suggestion/fixtures/syntaxSuggestion.sql +++ b/test/parser/postgresql/suggestion/fixtures/syntaxSuggestion.sql @@ -87,3 +87,7 @@ VALUES (1, '3'), (3, 'sdsd') ORDER BY sort_expression ASC LIMIT id = 1; CREATE OR REPLACE RULE name AS ON SELECT TO table_name WHERE length(y+x) = 3 DO INSTEAD NOTHING; WITH query_name (id) AS (SELECT id FROM table_expression) SELECT DISTINCT ON (col1) random() AS name1 FROM table_expression WHERE name1=name1 GROUP BY id HAVING sum(len+y) < interval '5 hours' WINDOW w AS (PARTITION BY depname ORDER BY salary DESC) EXCEPT (SELECT * FROM others) ORDER BY salary USING > NULLS FIRST OFFSET start FETCH NEXT ROW ONLY FOR KEY SHARE OF table_name NOWAIT; + +SELECT FROM tb1; + +SELECT age FROM tb1; \ No newline at end of file diff --git a/test/parser/postgresql/suggestion/syntaxSuggestion.test.ts b/test/parser/postgresql/suggestion/syntaxSuggestion.test.ts index 4e80d599..a0cb5f25 100644 --- a/test/parser/postgresql/suggestion/syntaxSuggestion.test.ts +++ b/test/parser/postgresql/suggestion/syntaxSuggestion.test.ts @@ -1143,6 +1143,39 @@ describe('Postgre SQL Syntax Suggestion', () => { expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['depname']); }); + test('Sync suggestion no duplicate syntaxContextType', () => { + const pos: CaretPosition = { + lineNumber: 91, + column: 8, + }; + const syntaxes = postgresql.getSuggestionAtCaretPosition( + commentOtherLine(syntaxSql, pos.lineNumber), + pos + )?.syntax; + const syntaxContextTypes = syntaxes?.map((syn) => syn.syntaxContextType); + + expect(syntaxContextTypes).not.toBeUndefined(); + expect(syntaxContextTypes).toEqual([EntityContextType.FUNCTION, EntityContextType.COLUMN]); + }); + + test('Select function or column', () => { + const pos: CaretPosition = { + lineNumber: 93, + column: 11, + }; + const syntaxes = postgresql.getSuggestionAtCaretPosition( + commentOtherLine(syntaxSql, pos.lineNumber), + pos + )?.syntax; + const wordRangesArr = syntaxes?.map((syn) => syn.wordRanges); + + expect(wordRangesArr).not.toBeUndefined(); + expect(wordRangesArr.length).toBe(2); + expect( + wordRangesArr.map((wordRanges) => wordRanges.map((wordRange) => wordRange.text)) + ).toEqual([['age'], ['age']]); + }); + test('Syntax suggestion after a comment', () => { const sql = `-- the comment\nSELECT * FROM db.`; const pos: CaretPosition = { diff --git a/test/parser/spark/suggestion/fixtures/syntaxSuggestion.sql b/test/parser/spark/suggestion/fixtures/syntaxSuggestion.sql index 982aed11..a5fbc40a 100644 --- a/test/parser/spark/suggestion/fixtures/syntaxSuggestion.sql +++ b/test/parser/spark/suggestion/fixtures/syntaxSuggestion.sql @@ -75,3 +75,7 @@ INSERT OVERWRITE students PARTITION (student_id = 222222) SELECT name, address F SELECT id, name, employee.deptno, deptname FROM employee FULL JOIN department ON employee.deptno = department.deptno; SELECT city, sum(quantity) AS sum FROM dealer GROUP BY sum(city) HAVING max(quantity) > 15; + +SELECT FROM tb1; + +SELECT age FROM tb1; \ No newline at end of file diff --git a/test/parser/spark/suggestion/syntaxSuggestion.test.ts b/test/parser/spark/suggestion/syntaxSuggestion.test.ts index 207cb550..2c5335cc 100644 --- a/test/parser/spark/suggestion/syntaxSuggestion.test.ts +++ b/test/parser/spark/suggestion/syntaxSuggestion.test.ts @@ -762,6 +762,39 @@ describe('Spark SQL Syntax Suggestion', () => { expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['quantity']); }); + test('Sync suggestion no duplicate syntaxContextType', () => { + const pos: CaretPosition = { + lineNumber: 79, + column: 8, + }; + const syntaxes = spark.getSuggestionAtCaretPosition( + commentOtherLine(syntaxSql, pos.lineNumber), + pos + )?.syntax; + const syntaxContextTypes = syntaxes?.map((syn) => syn.syntaxContextType); + + expect(syntaxContextTypes).not.toBeUndefined(); + expect(syntaxContextTypes).toEqual([EntityContextType.COLUMN, EntityContextType.FUNCTION]); + }); + + test('Select function or column', () => { + const pos: CaretPosition = { + lineNumber: 81, + column: 11, + }; + const syntaxes = spark.getSuggestionAtCaretPosition( + commentOtherLine(syntaxSql, pos.lineNumber), + pos + )?.syntax; + const wordRangesArr = syntaxes?.map((syn) => syn.wordRanges); + + expect(wordRangesArr).not.toBeUndefined(); + expect(wordRangesArr.length).toBe(2); + expect( + wordRangesArr.map((wordRanges) => wordRanges.map((wordRange) => wordRange.text)) + ).toEqual([['age'], ['age']]); + }); + test('Syntax suggestion after a comment', () => { const sql = `-- the comment\nSELECT * FROM db.`; const pos: CaretPosition = { diff --git a/test/parser/trino/suggestion/fixtures/syntaxSuggestion.sql b/test/parser/trino/suggestion/fixtures/syntaxSuggestion.sql index a7adc828..2811a759 100644 --- a/test/parser/trino/suggestion/fixtures/syntaxSuggestion.sql +++ b/test/parser/trino/suggestion/fixtures/syntaxSuggestion.sql @@ -57,3 +57,7 @@ SELECT orderId FROM orders WINDOW w AS (PARTITION BY clerk ORDER BY totalprice D SELECT id, amount, CASE WHEN amount > 1000 THEN 'High' WHEN amount BETWEEN 500 AND 1000 THEN 'Medium' ELSE 'Low' END AS sales_category FROM sales; SELECT * FROM users CROSS JOIN UNNEST(friends) WITH ordinality; + +SELECT FROM tb1; + +SELECT age FROM tb1; diff --git a/test/parser/trino/suggestion/syntaxSuggestion.test.ts b/test/parser/trino/suggestion/syntaxSuggestion.test.ts index 947e27da..b23b0aee 100644 --- a/test/parser/trino/suggestion/syntaxSuggestion.test.ts +++ b/test/parser/trino/suggestion/syntaxSuggestion.test.ts @@ -565,6 +565,39 @@ describe('Trino SQL Syntax Suggestion', () => { expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['friends']); }); + test('Sync suggestion no duplicate syntaxContextType', () => { + const pos: CaretPosition = { + lineNumber: 61, + column: 8, + }; + const syntaxes = trino.getSuggestionAtCaretPosition( + commentOtherLine(syntaxSql, pos.lineNumber), + pos + )?.syntax; + const syntaxContextTypes = syntaxes?.map((syn) => syn.syntaxContextType); + + expect(syntaxContextTypes).not.toBeUndefined(); + expect(syntaxContextTypes).toEqual([EntityContextType.COLUMN, EntityContextType.FUNCTION]); + }); + + test('Select function or column', () => { + const pos: CaretPosition = { + lineNumber: 63, + column: 11, + }; + const syntaxes = trino.getSuggestionAtCaretPosition( + commentOtherLine(syntaxSql, pos.lineNumber), + pos + )?.syntax; + const wordRangesArr = syntaxes?.map((syn) => syn.wordRanges); + + expect(wordRangesArr).not.toBeUndefined(); + expect(wordRangesArr.length).toBe(2); + expect( + wordRangesArr.map((wordRanges) => wordRanges.map((wordRange) => wordRange.text)) + ).toEqual([['age'], ['age']]); + }); + test('Syntax suggestion after a comment', () => { const sql = `-- the comment\nSELECT * FROM db.`; const pos: CaretPosition = {