feat: optimize suggestion (#231)

* feat: optimize the strategy of finding the right range

* test: apply commentOtherLine util to all suggestion tests

* test: decomment suggestion test cases

* test: add suggestion test cases in multiple statements

* chore: improve comments

* test: update log info in test
This commit is contained in:
Hayden 2023-12-13 11:33:47 +08:00 committed by GitHub
parent fd50c09a86
commit 3c7c59fb70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 930 additions and 155 deletions

View File

@ -277,46 +277,67 @@ export default abstract class BasicParser<
/** /**
* Split sql by statement. * Split sql by statement.
* Try to collect candidates from the caret statement only. * Try to collect candidates in as small a range as possible.
*/ */
this.listen(splitListener, this._parseTree); this.listen(splitListener, this._parseTree);
const statementCount = splitListener.statementsContext?.length;
const statementsContext = splitListener.statementsContext;
// If there are multiple statements. // If there are multiple statements.
if (splitListener.statementsContext.length > 1) { if (statementCount > 1) {
// find statement rule context where caretPosition is located.
const caretStatementContext = splitListener?.statementsContext.find((ctx) => {
return (
caretTokenIndex <= ctx.stop?.tokenIndex &&
caretTokenIndex >= ctx.start.tokenIndex
);
});
if (caretStatementContext) {
c3Context = caretStatementContext;
} else {
const lastStatementToken =
splitListener.statementsContext[splitListener?.statementsContext.length - 1]
.start;
/** /**
* If caretStatementContext is not found and it follows all statements. * Find a minimum valid range, reparse the fragment, and provide a new parse tree to C3.
* Reparses part of the input following the penultimate statement. * The boundaries of this range must be statements with no syntax errors.
* And c3 will collect candidates in the new parseTreeContext. * This can ensure the stable performance of the C3.
*/ */
if (caretTokenIndex > lastStatementToken?.tokenIndex) { let startStatement: ParserRuleContext;
let stopStatement: ParserRuleContext;
for (let index = 0; index < statementCount; index++) {
const ctx = statementsContext[index];
const isCurrentCtxValid = !ctx.exception;
if (!isCurrentCtxValid) continue;
/** /**
* Save offset of the tokenIndex in the partInput * Ensure that the statementContext before the left boundary
* and the last statementContext on the right boundary are qualified SQL statements.
*/
const isPrevCtxValid = index === 0 || !statementsContext[index - 1]?.exception;
const isNextCtxValid =
index === statementCount - 1 || !statementsContext[index + 1]?.exception;
if (ctx.stop.tokenIndex < caretTokenIndex && isPrevCtxValid) {
startStatement = ctx;
}
if (!stopStatement && ctx.start.tokenIndex > caretTokenIndex && isNextCtxValid) {
stopStatement = ctx;
break;
}
}
// A boundary consisting of the index of the input.
const startIndex = startStatement?.start?.startIndex ?? 0;
const stopIndex = stopStatement?.stop?.stopIndex ?? input.length - 1;
/**
* Save offset of the tokenIndex in the range of input
* compared to the tokenIndex in the whole input * compared to the tokenIndex in the whole input
*/ */
tokenIndexOffset = lastStatementToken?.tokenIndex; tokenIndexOffset = startStatement?.start?.tokenIndex ?? 0;
// Correct caretTokenIndex
caretTokenIndex = caretTokenIndex - tokenIndexOffset; caretTokenIndex = caretTokenIndex - tokenIndexOffset;
const inputSlice = input.slice(lastStatementToken.startIndex); /**
* Reparse the input fragment
* and c3 will collect candidates in the newly generated parseTree.
*/
const inputSlice = input.slice(startIndex, stopIndex);
const lexer = this.createLexer(inputSlice); const lexer = this.createLexer(inputSlice);
lexer.removeErrorListeners(); lexer.removeErrorListeners();
const tokenStream = new CommonTokenStream(lexer); const tokenStream = new CommonTokenStream(lexer);
tokenStream.fill(); tokenStream.fill();
const parser = this.createParserFromTokenStream(tokenStream); const parser = this.createParserFromTokenStream(tokenStream);
parser.removeErrorListeners(); parser.removeErrorListeners();
parser.buildParseTree = true; parser.buildParseTree = true;
@ -325,8 +346,6 @@ export default abstract class BasicParser<
sqlParserIns = parser; sqlParserIns = parser;
c3Context = parser.program(); c3Context = parser.program();
} }
}
}
const core = new CodeCompletionCore(sqlParserIns); const core = new CodeCompletionCore(sqlParserIns);
core.preferredRules = this.preferredRules; core.preferredRules = this.preferredRules;

View File

@ -1,19 +0,0 @@
CREATE TABLE orders (
order_uid BIGINT,
product_id BIGINT,
price DECIMAL(32, 2),
order_time TIMESTAMP(3)
) WITH (
'connector' = 'datagen'
);
CREATE TABLE orders (
order_uid BIGINT,
product_id BIGINT,
price DECIMAL(32, 2),
order_time TIMESTAMP(3)
) WITH (
'connector' = 'datagen'
);
use cat1.

View File

@ -0,0 +1,31 @@
SELECT * FROM -- unfinished
CREATE TEMPORARY VIEW IF NOT EXISTS v AS SELECT col1 FROM tbl;
CREATE TEMPORARY TABLE client_errors (
log_time TIMESTAMP(3),
request_line STRING,
status_code STRING,
size INT
) WITH (
'connector' = 'stream-x'
);
ALTER VIEW v1 RENAME TO v2;
CREATE TABLE db. ; -- unfinished
LOAD MODULE CORE;
REMOVE JAR '<path_to_filename>.jar'
INSERT INTO VALUES (100, 99.9 / 10, 'abc', true, now ()); -- unfinished
CREATE DATABASE IF NOT EXISTS dataApi COMMENT 'test create database' WITH ('key1' = 'value1', 'key2.a' = 'value2.a');
DROP DATABASE IF EXISTS Orders RESTRICT;
INSERT INTO country_page_view
SELECT `user`,
cnt
FROM db. ; -- unfinished

View File

@ -0,0 +1,69 @@
import fs from 'fs';
import path from 'path';
import { CaretPosition, SyntaxContextType } from '../../../../src/parser/common/basic-parser-types';
import FlinkSQL from '../../../../src/parser/flinksql';
const syntaxSql = fs.readFileSync(
path.join(__dirname, 'fixtures', 'multipleStatement.sql'),
'utf-8'
);
describe('FlinkSQL Multiple Statements Syntax Suggestion', () => {
const parser = new FlinkSQL();
test('Select from table ', () => {
const pos: CaretPosition = {
lineNumber: 1,
column: 15,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Create table ', () => {
const pos: CaretPosition = {
lineNumber: 16,
column: 17,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE_CREATE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.']);
});
test('Insert into table ', () => {
const pos: CaretPosition = {
lineNumber: 22,
column: 13,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Insert into select from table ', () => {
const pos: CaretPosition = {
lineNumber: 31,
column: 9,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.']);
});
});

View File

@ -8,7 +8,6 @@ const syntaxSql = fs.readFileSync(
path.join(__dirname, 'fixtures', 'syntaxSuggestion.sql'), path.join(__dirname, 'fixtures', 'syntaxSuggestion.sql'),
'utf-8' 'utf-8'
); );
const multipleSql = fs.readFileSync(path.join(__dirname, 'fixtures', 'multipleSql.sql'), 'utf-8');
describe('Flink SQL Syntax Suggestion', () => { describe('Flink SQL Syntax Suggestion', () => {
const parser = new FlinkSQL(); const parser = new FlinkSQL();
@ -19,20 +18,6 @@ describe('Flink SQL Syntax Suggestion', () => {
expect(parser.validate(syntaxSql).length).not.toBe(0); expect(parser.validate(syntaxSql).length).not.toBe(0);
}); });
test('Multiple SQL use database', () => {
const pos: CaretPosition = {
lineNumber: 19,
column: 10,
};
const syntaxes = parser.getSuggestionAtCaretPosition(multipleSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.DATABASE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['cat1', '.']);
});
test('Drop catalog', () => { test('Drop catalog', () => {
const pos: CaretPosition = { const pos: CaretPosition = {
lineNumber: 1, lineNumber: 1,

View File

@ -2,6 +2,7 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import { CaretPosition } from '../../../../src/parser/common/basic-parser-types'; import { CaretPosition } from '../../../../src/parser/common/basic-parser-types';
import FlinkSQL from '../../../../src/parser/flinksql'; import FlinkSQL from '../../../../src/parser/flinksql';
import { commentOtherLine } from '../../../helper';
const tokenSql = fs.readFileSync(path.join(__dirname, 'fixtures', 'tokenSuggestion.sql'), 'utf-8'); const tokenSql = fs.readFileSync(path.join(__dirname, 'fixtures', 'tokenSuggestion.sql'), 'utf-8');
@ -13,7 +14,10 @@ describe('Flink SQL Token Suggestion', () => {
lineNumber: 3, lineNumber: 3,
column: 5, column: 5,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual(['MODULES', 'CATALOG']); expect(suggestion).toEqual(['MODULES', 'CATALOG']);
}); });
@ -23,7 +27,10 @@ describe('Flink SQL Token Suggestion', () => {
lineNumber: 5, lineNumber: 5,
column: 8, column: 8,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual([ expect(suggestion).toEqual([
'CATALOG', 'CATALOG',
@ -40,7 +47,10 @@ describe('Flink SQL Token Suggestion', () => {
lineNumber: 7, lineNumber: 7,
column: 6, column: 6,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual([ expect(suggestion).toEqual([
'MODULES', 'MODULES',

View File

@ -0,0 +1,21 @@
SELECT * FROM -- unfinished
CREATE VIEW mydb.bro_view AS SELECT * FROM mydb.sale_tbl;
CREATE TEMPORARY EXTERNAL TABLE list_bucket_multiple (col1 STRING, col2 INT, col3 STRING);
ALTER VIEW myview1 SET TBLPROPERTIES ('author'='hayden','date'='2023-09-04')
CREATE TABLE db. ; -- unfinished
DROP CONNECTOR connector1;
SET ROLE `admin`;
INSERT INTO VALUES (100, 99.9 / 10, 'abc', true, now ()); -- unfinished
ALTER TABLE tbl1 RENAME TO tbl2;
ALTER SCHEMA database_name SET OWNER USER `admin`;
INSERT OVERWRITE LOCAL DIRECTORY '/path/to/output' SELECT col1, col2 FROM ; -- unfinished

View File

@ -0,0 +1,69 @@
import fs from 'fs';
import path from 'path';
import { CaretPosition, SyntaxContextType } from '../../../../src/parser/common/basic-parser-types';
import HiveSQL from '../../../../src/parser/hive';
const syntaxSql = fs.readFileSync(
path.join(__dirname, 'fixtures', 'multipleStatement.sql'),
'utf-8'
);
describe('HiveSQL Multiple Statements Syntax Suggestion', () => {
const parser = new HiveSQL();
test('Select from table ', () => {
const pos: CaretPosition = {
lineNumber: 1,
column: 15,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Create table ', () => {
const pos: CaretPosition = {
lineNumber: 9,
column: 17,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE_CREATE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.']);
});
test('Insert into table ', () => {
const pos: CaretPosition = {
lineNumber: 15,
column: 13,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Insert into select from table ', () => {
const pos: CaretPosition = {
lineNumber: 21,
column: 75,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
});

View File

@ -2,6 +2,7 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import { CaretPosition } from '../../../../src/parser/common/basic-parser-types'; import { CaretPosition } from '../../../../src/parser/common/basic-parser-types';
import HiveSQL from '../../../../src/parser/hive'; import HiveSQL from '../../../../src/parser/hive';
import { commentOtherLine } from '../../../helper';
const tokenSql = fs.readFileSync(path.join(__dirname, 'fixtures', 'tokenSuggestion.sql'), 'utf-8'); const tokenSql = fs.readFileSync(path.join(__dirname, 'fixtures', 'tokenSuggestion.sql'), 'utf-8');
@ -13,7 +14,10 @@ describe('Hive SQL Token Suggestion', () => {
lineNumber: 1, lineNumber: 1,
column: 7, column: 7,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual([ expect(suggestion).toEqual([
'APPLICATION', 'APPLICATION',
'GROUP', 'GROUP',
@ -37,7 +41,10 @@ describe('Hive SQL Token Suggestion', () => {
lineNumber: 3, lineNumber: 3,
column: 8, column: 8,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual([ expect(suggestion).toEqual([
'CONNECTOR', 'CONNECTOR',
'APPLICATION', 'APPLICATION',
@ -69,7 +76,10 @@ describe('Hive SQL Token Suggestion', () => {
lineNumber: 5, lineNumber: 5,
column: 8, column: 8,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual(['FROM']); expect(suggestion).toEqual(['FROM']);
}); });
@ -78,7 +88,10 @@ describe('Hive SQL Token Suggestion', () => {
lineNumber: 7, lineNumber: 7,
column: 10, column: 10,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual([ expect(suggestion).toEqual([
'EXTENDED', 'EXTENDED',
'FORMATTED', 'FORMATTED',
@ -94,7 +107,10 @@ describe('Hive SQL Token Suggestion', () => {
lineNumber: 9, lineNumber: 9,
column: 6, column: 6,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual([ expect(suggestion).toEqual([
'CONNECTOR', 'CONNECTOR',
'APPLICATION', 'APPLICATION',
@ -121,7 +137,10 @@ describe('Hive SQL Token Suggestion', () => {
lineNumber: 11, lineNumber: 11,
column: 8, column: 8,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual(['TABLE']); expect(suggestion).toEqual(['TABLE']);
}); });
@ -130,7 +149,10 @@ describe('Hive SQL Token Suggestion', () => {
lineNumber: 13, lineNumber: 13,
column: 8, column: 8,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual(['FROM', 'TABLE', 'EXTERNAL']); expect(suggestion).toEqual(['FROM', 'TABLE', 'EXTERNAL']);
}); });
@ -139,7 +161,10 @@ describe('Hive SQL Token Suggestion', () => {
lineNumber: 15, lineNumber: 15,
column: 8, column: 8,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual(['INTO', 'OVERWRITE']); expect(suggestion).toEqual(['INTO', 'OVERWRITE']);
}); });
@ -148,7 +173,10 @@ describe('Hive SQL Token Suggestion', () => {
lineNumber: 17, lineNumber: 17,
column: 6, column: 6,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual(['DATA']); expect(suggestion).toEqual(['DATA']);
}); });
@ -157,7 +185,10 @@ describe('Hive SQL Token Suggestion', () => {
lineNumber: 19, lineNumber: 19,
column: 6, column: 6,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual([ expect(suggestion).toEqual([
'CURRENT', 'CURRENT',
'ROLES', 'ROLES',

View File

@ -0,0 +1,21 @@
SELECT * FROM -- unfinished
CREATE VIEW my_view AS SELECT * FROM my_table;
create table census (name string, census_year int) partitioned by (year int);
ALTER VIEW v1 RENAME TO db2.v2;
CREATE TABLE db. ; -- unfinished
INSERT INTO target_table (col1, col2, col3) PARTITION (year = 2016, month IN (10, 11, 12)) SELECT * FROM dual;
EXPLAIN INSERT INTO t1 VALUES (1, 'one'), (2, 'two'), (3, 'three');
INSERT INTO VALUES (100, 99.9 / 10, 'abc', true, now ()); -- unfinished
ALTER TABLE my_table REPLACE COLUMNS (age INT COMMENT 'Updated Age');
ALTER DATABASE my_db SET OWNER USER 'impala';
INSERT INTO t2 (c1) SELECT c1 FROM t1.; -- unfinished

View File

@ -0,0 +1,69 @@
import fs from 'fs';
import path from 'path';
import { CaretPosition, SyntaxContextType } from '../../../../src/parser/common/basic-parser-types';
import ImpalaSQL from '../../../../src/parser/impala';
const syntaxSql = fs.readFileSync(
path.join(__dirname, 'fixtures', 'multipleStatement.sql'),
'utf-8'
);
describe('ImpalaSQL Multiple Statements Syntax Suggestion', () => {
const parser = new ImpalaSQL();
test('Select from table ', () => {
const pos: CaretPosition = {
lineNumber: 1,
column: 15,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Create table ', () => {
const pos: CaretPosition = {
lineNumber: 9,
column: 17,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE_CREATE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.']);
});
test('Insert into table ', () => {
const pos: CaretPosition = {
lineNumber: 15,
column: 13,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Insert into select from table ', () => {
const pos: CaretPosition = {
lineNumber: 21,
column: 39,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['t1', '.']);
});
});

View File

@ -2,6 +2,7 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import { CaretPosition, SyntaxContextType } from '../../../../src/parser/common/basic-parser-types'; import { CaretPosition, SyntaxContextType } from '../../../../src/parser/common/basic-parser-types';
import ImpalaSQL from '../../../../src/parser/impala'; import ImpalaSQL from '../../../../src/parser/impala';
import { commentOtherLine } from '../../../helper';
const syntaxSql = fs.readFileSync( const syntaxSql = fs.readFileSync(
path.join(__dirname, 'fixtures', 'syntaxSuggestion.sql'), path.join(__dirname, 'fixtures', 'syntaxSuggestion.sql'),
@ -16,7 +17,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 1, lineNumber: 1,
column: 20, column: 20,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE (syn) => syn.syntaxContextType === SyntaxContextType.TABLE
); );
@ -30,7 +34,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 3, lineNumber: 3,
column: 27, column: 27,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.FUNCTION (syn) => syn.syntaxContextType === SyntaxContextType.FUNCTION
); );
@ -44,7 +51,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 5, lineNumber: 5,
column: 19, column: 19,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.DATABASE (syn) => syn.syntaxContextType === SyntaxContextType.DATABASE
); );
@ -58,7 +68,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 7, lineNumber: 7,
column: 21, column: 21,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE (syn) => syn.syntaxContextType === SyntaxContextType.TABLE
); );
@ -72,7 +85,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 7, lineNumber: 7,
column: 39, column: 39,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN (syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
); );
@ -86,7 +102,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 9, lineNumber: 9,
column: 19, column: 19,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.VIEW (syn) => syn.syntaxContextType === SyntaxContextType.VIEW
); );
@ -100,7 +119,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 11, lineNumber: 11,
column: 12, column: 12,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.VIEW (syn) => syn.syntaxContextType === SyntaxContextType.VIEW
); );
@ -114,7 +136,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 13, lineNumber: 13,
column: 20, column: 20,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.DATABASE (syn) => syn.syntaxContextType === SyntaxContextType.DATABASE
); );
@ -128,7 +153,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 15, lineNumber: 15,
column: 20, column: 20,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE (syn) => syn.syntaxContextType === SyntaxContextType.TABLE
); );
@ -142,7 +170,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 17, lineNumber: 17,
column: 22, column: 22,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.FUNCTION (syn) => syn.syntaxContextType === SyntaxContextType.FUNCTION
); );
@ -156,7 +187,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 19, lineNumber: 19,
column: 21, column: 21,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE (syn) => syn.syntaxContextType === SyntaxContextType.TABLE
); );
@ -170,7 +204,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 21, lineNumber: 21,
column: 15, column: 15,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.VIEW_CREATE (syn) => syn.syntaxContextType === SyntaxContextType.VIEW_CREATE
); );
@ -184,7 +221,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 23, lineNumber: 23,
column: 20, column: 20,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE_CREATE (syn) => syn.syntaxContextType === SyntaxContextType.TABLE_CREATE
); );
@ -198,7 +238,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 25, lineNumber: 25,
column: 20, column: 20,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.FUNCTION_CREATE (syn) => syn.syntaxContextType === SyntaxContextType.FUNCTION_CREATE
); );
@ -212,7 +255,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 27, lineNumber: 27,
column: 25, column: 25,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.DATABASE_CREATE (syn) => syn.syntaxContextType === SyntaxContextType.DATABASE_CREATE
); );
@ -226,7 +272,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 29, lineNumber: 29,
column: 19, column: 19,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE (syn) => syn.syntaxContextType === SyntaxContextType.TABLE
); );
@ -240,7 +289,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 31, lineNumber: 31,
column: 22, column: 22,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE (syn) => syn.syntaxContextType === SyntaxContextType.TABLE
); );
@ -254,7 +306,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 33, lineNumber: 33,
column: 22, column: 22,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE (syn) => syn.syntaxContextType === SyntaxContextType.TABLE
); );
@ -268,7 +323,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 35, lineNumber: 35,
column: 20, column: 20,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.VIEW (syn) => syn.syntaxContextType === SyntaxContextType.VIEW
); );
@ -282,7 +340,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 37, lineNumber: 37,
column: 22, column: 22,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN (syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
); );
@ -296,7 +357,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 39, lineNumber: 39,
column: 22, column: 22,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN (syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
); );
@ -310,7 +374,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 41, lineNumber: 41,
column: 36, column: 36,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN_CREATE (syn) => syn.syntaxContextType === SyntaxContextType.COLUMN_CREATE
); );
@ -324,7 +391,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 43, lineNumber: 43,
column: 36, column: 36,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN_CREATE (syn) => syn.syntaxContextType === SyntaxContextType.COLUMN_CREATE
); );
@ -338,7 +408,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 45, lineNumber: 45,
column: 45, column: 45,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN_CREATE (syn) => syn.syntaxContextType === SyntaxContextType.COLUMN_CREATE
); );
@ -352,7 +425,10 @@ describe('Impala SQL Syntax Suggestion', () => {
lineNumber: 47, lineNumber: 47,
column: 49, column: 49,
}; };
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax; const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN_CREATE (syn) => syn.syntaxContextType === SyntaxContextType.COLUMN_CREATE
); );

View File

@ -14,7 +14,10 @@ describe('Impala SQL Token Suggestion', () => {
lineNumber: 1, lineNumber: 1,
column: 7, column: 7,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual(['TABLE', 'VIEW', 'DATABASE']); expect(suggestion).toEqual(['TABLE', 'VIEW', 'DATABASE']);
}); });

View File

@ -0,0 +1,21 @@
SELECT * FROM -- unfinished
CREATE SCHEMA IF NOT EXISTS db_name DEFAULT ENCRYPTION 'Y';
CREATE TABLE test (a INT NOT NULL AUTO_INCREMENT, PRIMARY KEY (a), KEY(b)) ENGINE=InnoDB SELECT b,c FROM test2;
ALTER LOGFILE GROUP lg_3 ADD UNDOFILE 'undo_10.dat' INITIAL_SIZE=32M ENGINE=NDBCLUSTER;
CREATE TABLE db. LIKE orig_tbl; -- unfinished
INSERT HIGH_PRIORITY IGNORE INTO tbl_temp2 (fld_id) VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8);
CHANGE REPLICATION FILTER REPLICATE_DO_DB = (db3, db4);
INSERT INTO VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b); -- unfinished
ALTER FUNCTION function_name LANGUAGE SQL;
ALTER FUNCTION function_name NOT DETERMINISTIC;
INSERT LOW_PRIORITY IGNORE INTO tbl_temp2 (fld_id) SELECT tbl_temp1.fld_order_id FROM -- unfinished

View File

@ -0,0 +1,69 @@
import fs from 'fs';
import path from 'path';
import { CaretPosition, SyntaxContextType } from '../../../../src/parser/common/basic-parser-types';
import MySQL from '../../../../src/parser/mysql';
const syntaxSql = fs.readFileSync(
path.join(__dirname, 'fixtures', 'multipleStatement.sql'),
'utf-8'
);
describe('MySQL Multiple Statements Syntax Suggestion', () => {
const parser = new MySQL();
test('Select from table ', () => {
const pos: CaretPosition = {
lineNumber: 1,
column: 15,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Create table ', () => {
const pos: CaretPosition = {
lineNumber: 9,
column: 17,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE_CREATE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.']);
});
test('Insert into table ', () => {
const pos: CaretPosition = {
lineNumber: 15,
column: 13,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Insert into select from table ', () => {
const pos: CaretPosition = {
lineNumber: 21,
column: 87,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
});

View File

@ -50,23 +50,22 @@ describe('MySQL Syntax Suggestion', () => {
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.']); expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.']);
}); });
// TODO: fix bug of basic parser and decomment following test test('Create table ', () => {
// test('Create table ', () => { const pos: CaretPosition = {
// const pos: CaretPosition = { lineNumber: 5,
// lineNumber: 5, column: 17,
// column: 17, };
// }; const syntaxes = parser.getSuggestionAtCaretPosition(
// const syntaxes = parser.getSuggestionAtCaretPosition( commentOtherLine(syntaxSql, pos.lineNumber),
// commentOtherLine(syntaxSql, pos.lineNumber), pos
// pos )?.syntax;
// )?.syntax; const suggestion = syntaxes?.find(
// const suggestion = syntaxes?.find( (syn) => syn.syntaxContextType === SyntaxContextType.TABLE_CREATE
// (syn) => syn.syntaxContextType === SyntaxContextType.TABLE_CREATE );
// );
// expect(suggestion).not.toBeUndefined(); expect(suggestion).not.toBeUndefined();
// expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.']); expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.']);
// }); });
test('DROP table ', () => { test('DROP table ', () => {
const pos: CaretPosition = { const pos: CaretPosition = {

View File

@ -42,7 +42,7 @@ describe('MySQL Database Administration Syntax Tests', () => {
it(sql, () => { it(sql, () => {
const result = parser.validate(sql); const result = parser.validate(sql);
if (result.length) { if (result.length) {
console.log(result, `\n请检查 sql: ${sql}`); console.log(result, `\nPlease check sql: ${sql}`);
} }
expect(result.length).toBe(0); expect(result.length).toBe(0);
}); });

View File

@ -49,7 +49,7 @@ describe('MySQL DDL Syntax Tests', () => {
it(sql, () => { it(sql, () => {
const result = parser.validate(sql); const result = parser.validate(sql);
if (result.length) { if (result.length) {
console.log(result, `\n请检查 sql: ${sql}`); console.log(result, `\nPlease check sql: ${sql}`);
} }
expect(result.length).toBe(0); expect(result.length).toBe(0);
}); });

View File

@ -33,7 +33,7 @@ describe('MySQL DML Syntax Tests', () => {
it(sql, () => { it(sql, () => {
const result = parser.validate(sql); const result = parser.validate(sql);
if (result.length) { if (result.length) {
console.log(result, `\n请检查 sql: ${sql}`); console.log(result, `\nPlease check sql: ${sql}`);
} }
expect(result.length).toBe(0); expect(result.length).toBe(0);
}); });

View File

@ -27,7 +27,7 @@ describe('MySQL Transactional and Locking, Replication, Prepared Compound and Ut
it(sql, () => { it(sql, () => {
const result = parser.validate(sql); const result = parser.validate(sql);
if (result.length) { if (result.length) {
console.log(result, `\n请检查 sql: ${sql}`); console.log(result, `\nPlease check sql: ${sql}`);
} }
expect(result.length).toBe(0); expect(result.length).toBe(0);
}); });

View File

@ -0,0 +1,21 @@
CREATE TABLE VALUES -- unfinished
CREATE UNLOGGED TABLE table1 (col1 int) INHERITS (table_parent) WITHOUT OIDS ON COMMIT DROP;
CREATE SCHEMA schemaname AUTHORIZATION username;
ALTER TABLE products ADD FOREIGN KEY (product_group_id) REFERENCES product_groups;
SELECT * FROM db. ; -- unfinished
INSERT INTO weather (date, city, temp_hi, temp_lo) VALUES ('1994-11-29', 'Hayward', 54, 37);
ANALYZE VERBOSE table_name ( column_name, column_name2);
INSERT INTO weather (date, city, temp_hi, temp_lo) VALUES ('1994-11-29', 'Hayward', 54, 37); -- unfinished
DROP TABLE products CASCADE;
DROP AGGREGATE aggname2(int);
INSERT INTO products (product_no, name, price) SELECT * FROM db. ; -- unfinished

View File

@ -0,0 +1,69 @@
import fs from 'fs';
import path from 'path';
import { CaretPosition, SyntaxContextType } from '../../../../src/parser/common/basic-parser-types';
import PgSQL from '../../../../src/parser/pgsql';
const syntaxSql = fs.readFileSync(
path.join(__dirname, 'fixtures', 'multipleStatement.sql'),
'utf-8'
);
describe('PgSQL Multiple Statements Syntax Suggestion', () => {
const parser = new PgSQL();
test('Create table ', () => {
const pos: CaretPosition = {
lineNumber: 1,
column: 14,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE_CREATE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Select from table', () => {
const pos: CaretPosition = {
lineNumber: 9,
column: 18,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.']);
});
test('Insert into table ', () => {
const pos: CaretPosition = {
lineNumber: 15,
column: 13,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Insert into select from table ', () => {
const pos: CaretPosition = {
lineNumber: 21,
column: 65,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.']);
});
});

View File

@ -638,29 +638,28 @@ describe('Postgre SQL Syntax Suggestion', () => {
lineNumber: 59, lineNumber: 59,
column: 48, column: 48,
}; };
// const pos1: CaretPosition = { const pos1: CaretPosition = {
// lineNumber: 59, lineNumber: 59,
// column: 93, column: 93,
// }; };
const syntaxes = parser.getSuggestionAtCaretPosition( const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber), commentOtherLine(syntaxSql, pos.lineNumber),
pos pos
)?.syntax; )?.syntax;
// const syntaxes1 = parser.getSuggestionAtCaretPosition( const syntaxes1 = parser.getSuggestionAtCaretPosition(
// commentOtherLine(syntaxSql, pos1.lineNumber), commentOtherLine(syntaxSql, pos1.lineNumber),
// pos1 pos1
// )?.syntax; )?.syntax;
const suggestion = syntaxes?.find( const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN (syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
); );
// const suggestion1 = syntaxes1?.find( const suggestion1 = syntaxes1?.find(
// (syn) => syn.syntaxContextType === SyntaxContextType.FUNCTION (syn) => syn.syntaxContextType === SyntaxContextType.FUNCTION
// ); );
expect(suggestion).not.toBeUndefined(); expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['column_name']); expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['column_name']);
// TODO: fix bug of basic parser and decomment following case expect(suggestion1).not.toBeUndefined();
// expect(suggestion1).not.toBeUndefined(); expect(suggestion1?.wordRanges.map((token) => token.text)).toEqual(['function_name']);
// expect(suggestion1?.wordRanges.map((token) => token.text)).toEqual(['function_name']);
}); });
test('GRANT With Column', () => { test('GRANT With Column', () => {

View File

@ -0,0 +1,21 @@
CREATE TABLE VALUES -- unfinished
CREATE TABLE student (id INT, name STRING, age INT) STORED AS ORC;
CREATE SCHEMA customer_db WITH DBPROPERTIES (ID=001, Name='John');
ALTER TABLE StudentInfo ADD COLUMNS (LastName string, DOB timestamp);
SELECT * FROM db. ; -- unfinished
INSERT INTO weather (date, city, temp_hi, temp_lo) VALUES ('1994-11-29', 'Hayward', 54, 37);
DESC EXTENDED students name;
INSERT INTO weather (date, city, temp_hi, temp_lo) VALUES ('1994-11-29', 'Hayward', 54, 37); -- unfinished
DROP TABLE IF EXISTS employable;
DROP TEMPORARY FUNCTION test_avg;
INSERT INTO products (product_no, name, price) SELECT * FROM db. ; -- unfinished

View File

@ -0,0 +1,69 @@
import fs from 'fs';
import path from 'path';
import { CaretPosition, SyntaxContextType } from '../../../../src/parser/common/basic-parser-types';
import SparkSQL from '../../../../src/parser/spark';
const syntaxSql = fs.readFileSync(
path.join(__dirname, 'fixtures', 'multipleStatement.sql'),
'utf-8'
);
describe('SparkSQL Multiple Statements Syntax Suggestion', () => {
const parser = new SparkSQL();
test('Create table ', () => {
const pos: CaretPosition = {
lineNumber: 1,
column: 14,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE_CREATE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Select from table', () => {
const pos: CaretPosition = {
lineNumber: 9,
column: 18,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.']);
});
test('Insert into table ', () => {
const pos: CaretPosition = {
lineNumber: 15,
column: 13,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Insert into select from table ', () => {
const pos: CaretPosition = {
lineNumber: 21,
column: 65,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.']);
});
});

View File

@ -2,6 +2,7 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import { CaretPosition } from '../../../../src/parser/common/basic-parser-types'; import { CaretPosition } from '../../../../src/parser/common/basic-parser-types';
import SparkSQL from '../../../../src/parser/spark'; import SparkSQL from '../../../../src/parser/spark';
import { commentOtherLine } from '../../../helper';
const tokenSql = fs.readFileSync(path.join(__dirname, 'fixtures', 'tokenSuggestion.sql'), 'utf-8'); const tokenSql = fs.readFileSync(path.join(__dirname, 'fixtures', 'tokenSuggestion.sql'), 'utf-8');
@ -13,7 +14,10 @@ describe('Spark SQL Token Suggestion', () => {
lineNumber: 1, lineNumber: 1,
column: 7, column: 7,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual(['TABLE', 'INDEX', 'VIEW', 'DATABASE', 'NAMESPACE', 'SCHEMA']); expect(suggestion).toEqual(['TABLE', 'INDEX', 'VIEW', 'DATABASE', 'NAMESPACE', 'SCHEMA']);
}); });
@ -23,7 +27,10 @@ describe('Spark SQL Token Suggestion', () => {
lineNumber: 3, lineNumber: 3,
column: 8, column: 8,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual([ expect(suggestion).toEqual([
'TEMPORARY', 'TEMPORARY',
@ -46,7 +53,10 @@ describe('Spark SQL Token Suggestion', () => {
lineNumber: 5, lineNumber: 5,
column: 8, column: 8,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual(['FROM']); expect(suggestion).toEqual(['FROM']);
}); });
@ -56,7 +66,10 @@ describe('Spark SQL Token Suggestion', () => {
lineNumber: 7, lineNumber: 7,
column: 10, column: 10,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual([ expect(suggestion).toEqual([
'WITH', 'WITH',
@ -79,7 +92,10 @@ describe('Spark SQL Token Suggestion', () => {
lineNumber: 9, lineNumber: 9,
column: 6, column: 6,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual([ expect(suggestion).toEqual([
'TEMPORARY', 'TEMPORARY',
@ -99,7 +115,10 @@ describe('Spark SQL Token Suggestion', () => {
lineNumber: 11, lineNumber: 11,
column: 8, column: 8,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual(['OVERWRITE', 'INTO']); expect(suggestion).toEqual(['OVERWRITE', 'INTO']);
}); });
@ -109,7 +128,10 @@ describe('Spark SQL Token Suggestion', () => {
lineNumber: 13, lineNumber: 13,
column: 6, column: 6,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual(['DATA']); expect(suggestion).toEqual(['DATA']);
}); });
@ -119,7 +141,10 @@ describe('Spark SQL Token Suggestion', () => {
lineNumber: 15, lineNumber: 15,
column: 6, column: 6,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual([ expect(suggestion).toEqual([
'LOCKS', 'LOCKS',
@ -154,7 +179,10 @@ describe('Spark SQL Token Suggestion', () => {
lineNumber: 17, lineNumber: 17,
column: 8, column: 8,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual(['TABLE']); expect(suggestion).toEqual(['TABLE']);
}); });

View File

@ -0,0 +1,22 @@
CREATE TABLE VALUES -- unfinished
ALTER SCHEMA foo RENAME TO bar;
DELETE FROM t;
DENY SELECT ON SCHEMA s TO USER u;
SELECT ids FROM db. ; -- unfinished
INSERT INTO weather (date, city, temp_hi, temp_lo) VALUES ('1994-11-29', 'Hayward', 54, 37);
EXPLAIN ANALYZE VERBOSE SELECT * FROM t;
INSERT INTO weather (date, city, temp_hi, temp_lo) VALUES ('1994-11-29', 'Hayward', 54, 37); -- unfinished
DENY SELECT ON SCHEMA s TO USER u;
CALL catalog.schema.test();
INSERT INTO products (product_no, name, price) SELECT * FROM db. ; -- unfinished

View File

@ -0,0 +1,69 @@
import fs from 'fs';
import path from 'path';
import { CaretPosition, SyntaxContextType } from '../../../../src/parser/common/basic-parser-types';
import TrinoSQL from '../../../../src/parser/trinosql';
const syntaxSql = fs.readFileSync(
path.join(__dirname, 'fixtures', 'multipleStatement.sql'),
'utf-8'
);
describe('TrinoSQL Multiple Statements Syntax Suggestion', () => {
const parser = new TrinoSQL();
test('Create table ', () => {
const pos: CaretPosition = {
lineNumber: 1,
column: 14,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE_CREATE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Select from table', () => {
const pos: CaretPosition = {
lineNumber: 9,
column: 20,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.']);
});
test('Insert into table ', () => {
const pos: CaretPosition = {
lineNumber: 15,
column: 13,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Insert into select from table ', () => {
const pos: CaretPosition = {
lineNumber: 21,
column: 65,
};
const syntaxes = parser.getSuggestionAtCaretPosition(syntaxSql, pos)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.']);
});
});

View File

@ -14,7 +14,10 @@ describe('Trino SQL Token Suggestion', () => {
lineNumber: 1, lineNumber: 1,
column: 7, column: 7,
}; };
const suggestion = parser.getSuggestionAtCaretPosition(tokenSql, pos)?.keywords; const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual(['VIEW', 'MATERIALIZED', 'TABLE', 'SCHEMA']); expect(suggestion).toEqual(['VIEW', 'MATERIALIZED', 'TABLE', 'SCHEMA']);
}); });