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

@ -1016,16 +1016,6 @@ BIT_STRING: BIT_STRING_L;
STRING_CHARSET_NAME: '_' CHARSET_NAME;
// Hack for dotID
// Prevent recognize string: .123somelatin AS ((.123), FLOAT_LITERAL), ((somelatin), ID)
// it must recoginze: .123somelatin AS ((.), DOT), (123somelatin, ID)
DOT_ID: '.' ID_LITERAL;
// Identifiers
ID: ID_LITERAL;

View File

@ -24,7 +24,8 @@ THE SOFTWARE.
*/
// https://github.com/antlr/grammars-v4/blob/master/sql/mysql/Positive-Technologies/MySqlParser.g4
// SQL Statements: https://dev.mysql.com/doc/refman/8.0/en/sql-statements.html
// SQL Statements v8.0: https://dev.mysql.com/doc/refman/8.0/en/sql-statements.html
// SQL Statements v5.7: https://dev.mysql.com/doc/refman/5.7/en/sql-statements.html
parser grammar MySqlParser;
@ -1136,10 +1137,10 @@ selectElements
;
selectElement
: select_element=fullId '.' '*' #selectStarElement
| columnName (KW_AS? alias=uid)? #selectColumnElement
| functionCall (KW_AS? alias=uid)? #selectFunctionElement
| (LOCAL_ID VAR_ASSIGN)? expression (KW_AS? alias=uid)? #selectExpressionElement
: select_element=fullId '.' '*' #selectStarElement
| columnName (KW_AS? alias=uid)? #selectColumnElement
| functionCall (KW_AS? alias=uid)? #selectFunctionElement
| (LOCAL_ID VAR_ASSIGN)? expression (KW_AS? alias=uid)? #selectExpressionElement
;
intoClause
@ -2335,7 +2336,7 @@ simpleId
;
dottedId
: DOT_ID
: DOT ID
| '.' uid
;
@ -2530,7 +2531,7 @@ functionCall
| aggregateWindowedFunction #aggregateFunctionCall
| nonAggregateWindowedFunction #nonAggregateFunctionCall
| scalarFunctionName '(' functionArgs? ')' #scalarFunctionCall
| fullId '(' functionArgs? ')' #udfFunctionCall
| functionName '(' functionArgs? ')' #udfFunctionCall
| passwordFunctionClause #passwordFunctionCall
;

File diff suppressed because one or more lines are too long

View File

@ -886,14 +886,13 @@ REAL_LITERAL=885
NULL_SPEC_LITERAL=886
BIT_STRING=887
STRING_CHARSET_NAME=888
DOT_ID=889
ID=890
REVERSE_QUOTE_ID=891
HOST_IP_ADDRESS=892
LOCAL_ID=893
GLOBAL_ID=894
PERSIST_ID=895
ERROR_RECONGNIGION=896
ID=889
REVERSE_QUOTE_ID=890
HOST_IP_ADDRESS=891
LOCAL_ID=892
GLOBAL_ID=893
PERSIST_ID=894
ERROR_RECONGNIGION=895
'ACTIVE'=5
'ADD'=6
'ALL'=7

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -886,14 +886,13 @@ REAL_LITERAL=885
NULL_SPEC_LITERAL=886
BIT_STRING=887
STRING_CHARSET_NAME=888
DOT_ID=889
ID=890
REVERSE_QUOTE_ID=891
HOST_IP_ADDRESS=892
LOCAL_ID=893
GLOBAL_ID=894
PERSIST_ID=895
ERROR_RECONGNIGION=896
ID=889
REVERSE_QUOTE_ID=890
HOST_IP_ADDRESS=891
LOCAL_ID=892
GLOBAL_ID=893
PERSIST_ID=894
ERROR_RECONGNIGION=895
'ACTIVE'=5
'ADD'=6
'ALL'=7

File diff suppressed because one or more lines are too long

View File

@ -1,9 +1,10 @@
import { Token } from 'antlr4ts';
import { CandidatesCollection } from 'antlr4-c3';
import { MySqlLexer } from '../lib/mysql/MySqlLexer';
import { MySqlParser, ProgramContext } from '../lib/mysql/MySqlParser';
import { MySqlParser, ProgramContext, SqlStatementsContext } from '../lib/mysql/MySqlParser';
import BasicParser from './common/basicParser';
import { Suggestions } from './common/basic-parser-types';
import { Suggestions, SyntaxContextType, SyntaxSuggestion } from './common/basic-parser-types';
import { MySqlParserListener } from 'src/lib/mysql/MySqlParserListener';
export default class MySQL extends BasicParser<MySqlLexer, ProgramContext, MySqlParser> {
protected createLexerFormCharStream(charStreams): MySqlLexer {
@ -15,20 +16,123 @@ export default class MySQL extends BasicParser<MySqlLexer, ProgramContext, MySql
return new MySqlParser(tokenStream);
}
protected preferredRules: Set<number> = new Set();
protected preferredRules: Set<number> = new Set([
MySqlParser.RULE_databaseName,
MySqlParser.RULE_databaseNameCreate,
MySqlParser.RULE_tableName,
MySqlParser.RULE_tableNameCreate,
MySqlParser.RULE_viewName,
MySqlParser.RULE_viewNameCreate,
MySqlParser.RULE_functionName,
MySqlParser.RULE_functionNameCreate,
MySqlParser.RULE_columnName,
MySqlParser.RULE_columnNameCreate,
]);
protected get splitListener() {
return null as any;
return new mysqlSplitListener();
}
protected processCandidates(
candidates: CandidatesCollection,
allTokens: Token[],
caretTokenIndex: number
caretTokenIndex: number,
tokenIndexOffset: number
): Suggestions<Token> {
const originalSyntaxSuggestions: SyntaxSuggestion<Token>[] = [];
const keywords: string[] = [];
for (const candidate of candidates.rules) {
const [ruleType, candidateRule] = candidate;
const startTokenIndex = candidateRule.startTokenIndex + tokenIndexOffset;
const tokenRanges = allTokens.slice(
startTokenIndex,
caretTokenIndex + tokenIndexOffset + 1
);
let syntaxContextType: SyntaxContextType;
switch (ruleType) {
case MySqlParser.RULE_databaseName: {
syntaxContextType = SyntaxContextType.DATABASE;
break;
}
case MySqlParser.RULE_databaseNameCreate: {
syntaxContextType = SyntaxContextType.DATABASE_CREATE;
break;
}
case MySqlParser.RULE_tableName: {
syntaxContextType = SyntaxContextType.TABLE;
break;
}
case MySqlParser.RULE_tableNameCreate: {
syntaxContextType = SyntaxContextType.TABLE_CREATE;
break;
}
case MySqlParser.RULE_viewName: {
syntaxContextType = SyntaxContextType.VIEW;
break;
}
case MySqlParser.RULE_viewNameCreate: {
syntaxContextType = SyntaxContextType.VIEW_CREATE;
break;
}
case MySqlParser.RULE_functionName: {
syntaxContextType = SyntaxContextType.FUNCTION;
break;
}
case MySqlParser.RULE_functionNameCreate: {
syntaxContextType = SyntaxContextType.FUNCTION_CREATE;
break;
}
case MySqlParser.RULE_columnName: {
syntaxContextType = SyntaxContextType.COLUMN;
break;
}
case MySqlParser.RULE_columnNameCreate: {
syntaxContextType = SyntaxContextType.COLUMN_CREATE;
break;
}
default:
break;
}
if (syntaxContextType) {
originalSyntaxSuggestions.push({
syntaxContextType,
wordRanges: tokenRanges,
});
}
}
for (const candidate of candidates.tokens) {
const symbolicName = this._parser.vocabulary.getSymbolicName(candidate[0]);
const displayName = this._parser.vocabulary.getDisplayName(candidate[0]);
if (symbolicName && symbolicName.startsWith('KW_')) {
const keyword =
displayName.startsWith("'") && displayName.endsWith("'")
? displayName.slice(1, -1)
: displayName;
keywords.push(keyword);
}
}
return {
syntax: [],
keywords: [],
syntax: originalSyntaxSuggestions,
keywords,
};
}
}
export class mysqlSplitListener implements MySqlParserListener {
private _statementsContext: SqlStatementsContext[] = [];
exitSqlStatements = (ctx: SqlStatementsContext) => {
this._statementsContext.push(ctx);
};
enterSqlStatements = (ctx: SqlStatementsContext) => {};
get statementsContext() {
return this._statementsContext;
}
}