feat: mysql auto complete (#219)

* refactor: mysql auto complete

* test: mysql auto complete

---------

Co-authored-by: liuyi <liuyi@dtstack.com>
This commit is contained in:
琉易
2023-11-28 21:17:18 +08:00
committed by GitHub
parent e203f1a48a
commit 3dadc0c4b5
14 changed files with 8332 additions and 7507 deletions

View File

@ -0,0 +1,49 @@
INSERT INTO db.tb ;
SELECT * FROM db.;
CREATE TABLE db. VALUES;
DROP TABLE IF EXISTS db.a;
CREATE OR REPLACE VIEW db.v;
DROP VIEW db.v;
CREATE FUNCTION fn1;
SELECT name, calculate_age(birthday) AS age FROM students;
CREATE DATABASE db;
DROP SCHEMA IF EXISTS sch;
ANALYZE TABLE t UPDATE HISTOGRAM ON c1, c2, c3 WITH 10 BUCKETS;
SELECT name, age FROM students;
ALTER TABLE StudentInfo RENAME COLUMN ;
ALTER TABLE StudentInfo RENAME COLUMN name TO t;
ALTER TABLE t1 ADD c2 INT GENERATED ALWAYS AS (c1 + 1) STORED;
ALTER TABLE StudentInfo CHANGE FirstName;
INSERT INTO students ( );
INSERT INTO students ( id, n );
SELECT ;
SELECT id, n;
SELECT FROM tbl;
SELECT id, n FROM tbl;
SELECT id, n FROM tbl GROUP BY ;
SELECT id, n FROM tbl ORDER BY name, i ;
SELECT id FROM tb1 GROUP BY ROLLUP( );

View File

@ -0,0 +1,16 @@
ALTER
;
CREATE
;
DELETE
;
DESCRIBE
;
DROP
;
INSERT
;
LOAD
;
SHOW
;

View File

@ -0,0 +1,443 @@
import fs from 'fs';
import path from 'path';
import { CaretPosition, SyntaxContextType } from '../../../../src/parser/common/basic-parser-types';
import MySQL from '../../../../src/parser/mysql';
import { commentOtherLine } from '../../../helper';
const syntaxSql = fs.readFileSync(
path.join(__dirname, 'fixtures', 'syntaxSuggestion.sql'),
'utf-8'
);
describe('MySQL Syntax Suggestion', () => {
const parser = new MySQL();
test('Validate Syntax SQL', () => {
expect(parser.validate(syntaxSql).length).not.toBe(0);
});
test('Insert table ', () => {
const pos: CaretPosition = {
lineNumber: 1,
column: 18,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.', 'tb']);
});
test('Select table ', () => {
const pos: CaretPosition = {
lineNumber: 3,
column: 18,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
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('Create table ', () => {
const pos: CaretPosition = {
lineNumber: 5,
column: 17,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
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('DROP table ', () => {
const pos: CaretPosition = {
lineNumber: 7,
column: 26,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.TABLE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.', 'a']);
});
test('Create view ', () => {
const pos: CaretPosition = {
lineNumber: 9,
column: 28,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.VIEW_CREATE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.', 'v']);
});
test('Drop view ', () => {
const pos: CaretPosition = {
lineNumber: 11,
column: 15,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.VIEW
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db', '.', 'v']);
});
test('Create function ', () => {
const pos: CaretPosition = {
lineNumber: 13,
column: 20,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.FUNCTION_CREATE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['fn1']);
});
test('Use function', () => {
const pos: CaretPosition = {
lineNumber: 15,
column: 27,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.FUNCTION
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['calculate_age']);
});
test('Create database', () => {
const pos: CaretPosition = {
lineNumber: 17,
column: 19,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.DATABASE_CREATE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['db']);
});
test('Drop database', () => {
const pos: CaretPosition = {
lineNumber: 19,
column: 26,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.DATABASE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['sch']);
});
test('Analyze table on column', () => {
const pos: CaretPosition = {
lineNumber: 21,
column: 39,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['c1']);
});
test('Select columnName', () => {
const pos: CaretPosition = {
lineNumber: 23,
column: 17,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['age']);
});
test('Alter table rename columns', () => {
const pos: CaretPosition = {
lineNumber: 25,
column: 39,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Alter table rename columns to', () => {
const pos: CaretPosition = {
lineNumber: 27,
column: 48,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN_CREATE
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['t']);
});
test('Alter table add column', () => {
const pos: CaretPosition = {
lineNumber: 29,
column: 22,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['c2']);
});
test('Alter table change columns', () => {
const pos: CaretPosition = {
lineNumber: 31,
column: 41,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['FirstName']);
});
test('Insert into table spec columns', () => {
const pos: CaretPosition = {
lineNumber: 33,
column: 24,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Insert into table spec columns2', () => {
const pos: CaretPosition = {
lineNumber: 35,
column: 29,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['n']);
});
test('Select columns case empty', () => {
const pos: CaretPosition = {
lineNumber: 37,
column: 8,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Select columns case seq', () => {
const pos: CaretPosition = {
lineNumber: 39,
column: 13,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['n']);
});
test('Select columns from table case empty', () => {
const pos: CaretPosition = {
lineNumber: 41,
column: 8,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Select columns from table case seq', () => {
const pos: CaretPosition = {
lineNumber: 43,
column: 13,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['n']);
});
test('Select group by', () => {
const pos: CaretPosition = {
lineNumber: 45,
column: 32,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
test('Select group by', () => {
const pos: CaretPosition = {
lineNumber: 47,
column: 39,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual(['i']);
});
test('Select group by rollup', () => {
const pos: CaretPosition = {
lineNumber: 49,
column: 37,
};
const syntaxes = parser.getSuggestionAtCaretPosition(
commentOtherLine(syntaxSql, pos.lineNumber),
pos
)?.syntax;
const suggestion = syntaxes?.find(
(syn) => syn.syntaxContextType === SyntaxContextType.COLUMN
);
expect(suggestion).not.toBeUndefined();
expect(suggestion?.wordRanges.map((token) => token.text)).toEqual([]);
});
});

View File

@ -0,0 +1,238 @@
import fs from 'fs';
import path from 'path';
import { CaretPosition } from '../../../../src/parser/common/basic-parser-types';
import MySQL from '../../../../src/parser/mysql';
import { commentOtherLine } from '../../../helper';
const tokenSql = fs.readFileSync(path.join(__dirname, 'fixtures', 'tokenSuggestion.sql'), 'utf-8');
describe('MySQL Token Suggestion', () => {
const parser = new MySQL();
test('After ALTER', () => {
const pos: CaretPosition = {
lineNumber: 1,
column: 7,
};
const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual([
'RESOURCE',
'USER',
'VIEW',
'SQL',
'DEFINER',
'ALGORITHM',
'TABLESPACE',
'UNDO',
'TABLE',
'SERVER',
'PROCEDURE',
'LOGFILE',
'INSTANCE',
'FUNCTION',
'EVENT',
'DATABASE',
'SCHEMA',
]);
});
test('After CREATE', () => {
const pos: CaretPosition = {
lineNumber: 3,
column: 8,
};
const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual([
'RESOURCE',
'USER',
'ROLE',
'VIEW',
'SQL',
'DEFINER',
'ALGORITHM',
'OR',
'TRIGGER',
'TABLESPACE',
'UNDO',
'TABLE',
'TEMPORARY',
'SERVER',
'FUNCTION',
'AGGREGATE',
'PROCEDURE',
'LOGFILE',
'INDEX',
'FULLTEXT',
'SPATIAL',
'UNIQUE',
'OFFLINE',
'ONLINE',
'EVENT',
'DATABASE',
'SCHEMA',
]);
});
test('After DELETE', () => {
const pos: CaretPosition = {
lineNumber: 5,
column: 8,
};
const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual(['FROM', 'IGNORE', 'QUICK', 'LOW_PRIORITY']);
});
test('After DESCRIBE', () => {
const pos: CaretPosition = {
lineNumber: 7,
column: 10,
};
const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual([
'ANALYZE',
'SELECT',
'DELETE',
'INSERT',
'REPLACE',
'UPDATE',
'FOR',
'FORMAT',
'PARTITIONS',
'EXTENDED',
]);
});
test('After DROP', () => {
const pos: CaretPosition = {
lineNumber: 9,
column: 6,
};
const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual([
'RESOURCE',
'USER',
'PREPARE',
'ROLE',
'VIEW',
'TRIGGER',
'TABLESPACE',
'UNDO',
'TABLE',
'TEMPORARY',
'SPATIAL',
'SERVER',
'FUNCTION',
'PROCEDURE',
'LOGFILE',
'INDEX',
'EVENT',
'DATABASE',
'SCHEMA',
]);
});
test('After INSERT', () => {
const pos: CaretPosition = {
lineNumber: 11,
column: 8,
};
const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual(['INTO', 'IGNORE', 'DELAYED', 'HIGH_PRIORITY', 'LOW_PRIORITY']);
});
test('After LOAD', () => {
const pos: CaretPosition = {
lineNumber: 13,
column: 6,
};
const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual(['INDEX', 'XML', 'DATA']);
});
test('After SHOW', () => {
const pos: CaretPosition = {
lineNumber: 15,
column: 6,
};
const suggestion = parser.getSuggestionAtCaretPosition(
commentOtherLine(tokenSql, pos.lineNumber),
pos
)?.keywords;
expect(suggestion).toEqual([
'REPLICAS',
'REPLICA',
'SLAVE',
'PROFILE',
'OPEN',
'INDEX',
'KEYS',
'INDEXES',
'EXTENDED',
'GRANTS',
'FUNCTION',
'PROCEDURE',
'EVENTS',
'TABLE',
'FULL',
'TABLES',
'TRIGGERS',
'COUNT',
'ERRORS',
'WARNINGS',
'STORAGE',
'ENGINES',
'MASTER',
'PLUGINS',
'PRIVILEGES',
'PROCESSLIST',
'PROFILES',
'AUTHORS',
'CONTRIBUTORS',
'ENGINE',
'CREATE',
'COLUMNS',
'FIELDS',
'CHARACTER',
'CHARSET',
'COLLATION',
'DATABASES',
'SCHEMAS',
'GLOBAL',
'SESSION',
'STATUS',
'VARIABLES',
'BINLOG',
'RELAYLOG',
'BINARY',
]);
});
});

View File

@ -68,7 +68,6 @@ ALTER TABLE t1 ADD COLUMN c2 INT GENERATED ALWAYS AS (c1 + 1) STORED;
ALTER TABLE t1 MODIFY COLUMN c2 TINYINT GENERATED ALWAYS AS (c1 + 5) STORED;
ALTER TABLE t1 CHANGE c2 c3 INT GENERATED ALWAYS AS (c1 + 1) STORED;
ALTER TABLE t1 DROP COLUMN c3;
ALTER TABLE t1 ADD COLUMN c2 INT GENERATED ALWAYS AS (c1 + 1) STORED;
ALTER TABLE t1 MODIFY COLUMN c2 INT GENERATED ALWAYS AS (c1 + 1) STORED;
ALTER TABLE t1 MODIFY COLUMN c2 INT;