Replace PostgreSQL grammar to support COLLATE

This commit is contained in:
aboyko
2024-11-01 11:20:42 -04:00
parent 0f335b26e9
commit b31e63468f
13 changed files with 130141 additions and 60488 deletions

View File

@@ -37,16 +37,16 @@ import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.Sem
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokensDataProvider;
import org.springframework.ide.vscode.parser.postgresql.PostgreSqlLexer;
import org.springframework.ide.vscode.parser.postgresql.PostgreSqlParser;
import org.springframework.ide.vscode.parser.postgresql.PostgreSqlParser.Data_typeContext;
import org.springframework.ide.vscode.parser.postgresql.PostgreSqlParser.Attr_nameContext;
import org.springframework.ide.vscode.parser.postgresql.PostgreSqlParser.Func_nameContext;
import org.springframework.ide.vscode.parser.postgresql.PostgreSqlParser.IdentifierContext;
import org.springframework.ide.vscode.parser.postgresql.PostgreSqlParser.ParameterContext;
import org.springframework.ide.vscode.parser.postgresql.PostgreSqlParser.TypenameContext;
import org.springframework.ide.vscode.parser.postgresql.PostgreSqlParserBaseListener;
public class PostgreSqlSemanticTokens implements SemanticTokensDataProvider {
private static List<String> TOKEN_TYPES = List.of("keyword", "type", "string", "number", "operator",
"variable", "regexp", "comment", "parameter", "method");
"variable", "comment", "parameter", "method");
private Optional<SpelSemanticTokens> optSpelTokens;
private Optional<Consumer<RecognitionException>> parseErrorHandler;
@@ -90,24 +90,23 @@ public class PostgreSqlSemanticTokens implements SemanticTokensDataProvider {
private void processTerminalNode(TerminalNode node) {
Token token = node.getSymbol();
int type = token.getType();
if (type >= PostgreSqlLexer.A_ && type <= PostgreSqlLexer.OWNED) {
if (type >= PostgreSqlLexer.ALL && type <= PostgreSqlLexer.TOP) {
semantics.put(token, "keyword");
} else if (type >= PostgreSqlLexer.ABSTIME && type <= PostgreSqlLexer.XML) {
semantics.put(token, "type");
} else if ((type >= PostgreSqlLexer.AMP && type <= PostgreSqlLexer.SEMI)
} else if (type >= PostgreSqlLexer.ABS && type <= PostgreSqlLexer.TO_NUMBER) {
semantics.put(token, "method");
} else if ((type >= PostgreSqlLexer.Dollar && type <= PostgreSqlLexer.Operator)
|| (type >= PostgreSqlLexer.COMMA && type <= PostgreSqlLexer.CLOSE_BRACKET)
|| type == PostgreSqlLexer.DOT) {
semantics.put(token, "operator");
} else if (type == PostgreSqlLexer.REGEX_STRING) {
semantics.put(token, "regexp");
} else if (type >= PostgreSqlLexer.SINGLEQ_STRING_LITERAL && type <= PostgreSqlLexer.DOUBLEQ_STRING_LITERAL) {
} else if (type >= PostgreSqlLexer.StringConstant && type <= PostgreSqlLexer.BeginDollarStringConstant) {
semantics.put(token, "string");
} else if ((type >= PostgreSqlLexer.NUMERIC_LITERAL && type <= PostgreSqlLexer.HEX_INTEGER_LITERAL)
|| type == PostgreSqlLexer.DOLLAR_DEC || type == PostgreSqlLexer.BIT_STRING) {
} else if ((type >= PostgreSqlLexer.Integral && type <= PostgreSqlLexer.Numeric)
|| (type >= PostgreSqlLexer.BinaryStringConstant && type <= PostgreSqlLexer.InvalidUnterminatedHexadecimalStringConstant)) {
semantics.put(token, "number");
} else if (type == PostgreSqlLexer.IDENTIFIER) {
} else if ((type >= PostgreSqlLexer.Identifier && type <= PostgreSqlLexer.InvalidUnterminatedHexadecimalStringConstant)
|| type == PostgreSqlLexer.PLSQLIDENTIFIER) {
semantics.put(token, "variable");
} else if (type >= PostgreSqlLexer.BLOCK_COMMENT && type <= PostgreSqlLexer.LINE_COMMENT) {
} else if (type >= PostgreSqlLexer.LineComment && type <= PostgreSqlLexer.UnterminatedBlockComment) {
semantics.put(token, "comment");
} else if (type == PostgreSqlLexer.SPEL) {
tokens.addAll(JpqlSemanticTokens.computeTokensFromSpelNode(node, initialOffset, optSpelTokens));
@@ -124,40 +123,30 @@ public class PostgreSqlSemanticTokens implements SemanticTokensDataProvider {
processTerminalNode(node);
}
@Override
public void exitIdentifier(IdentifierContext ctx) {
if (ctx.identifier() != null) {
for (int i = 1; i < ctx.identifier().size(); i++) {
IdentifierContext identifier = ctx.identifier(i);
if (identifier.IDENTIFIER() != null) {
semantics.put(identifier.IDENTIFIER().getSymbol(), "property");
}
}
}
}
@Override
public void exitFunc_name(Func_nameContext funcName) {
if (funcName.identifier() != null && funcName.identifier().IDENTIFIER() != null) {
semantics.put(funcName.identifier().IDENTIFIER().getSymbol(), "method");
}
AntlrUtils.getAllLeafs(funcName).filter(t -> t.getType() != PostgreSqlLexer.DOT)
.forEach(t -> semantics.put(t, "method"));
}
@Override
public void exitData_type(Data_typeContext dataType) {
if (dataType.identifier() != null) {
AntlrUtils.getAllLeafs(dataType.identifier())
.filter(t -> t.getType() == PostgreSqlLexer.IDENTIFIER)
public void exitAttr_name(Attr_nameContext ctx) {
AntlrUtils.getAllLeafs(ctx)
.forEach(t -> semantics.put(t, "proeprty"));
}
@Override
public void exitTypename(TypenameContext ctx) {
AntlrUtils.getAllLeafs(ctx).filter(t -> t.getType() != PostgreSqlLexer.DOT)
.forEach(t -> semantics.put(t, "type"));
}
}
@Override
public void exitParameter(ParameterContext param) {
if (param.identifier() != null) {
AntlrUtils.getAllLeafs(param.identifier()).forEach(t -> semantics.put(t, "parameter"));
} else if (param.INTEGER_LITERAL() != null) {
semantics.put(param.INTEGER_LITERAL().getSymbol(), "parameter");
if (param.colid() != null) {
AntlrUtils.getAllLeafs(param.colid()).forEach(t -> semantics.put(t, "parameter"));
} else if (param.Integral() != null) {
semantics.put(param.Integral().getSymbol(), "parameter");
} else if (param.reserved_keyword() != null) {
AntlrUtils.getAllLeafs(param.reserved_keyword()).forEach(t -> semantics.put(t, "parameter"));
}

View File

@@ -58,10 +58,10 @@ public class PostgreSqlSemanticTokensTest {
assertThat(tokens.get(26)).isEqualTo(new SemanticTokenData(154, 155, "parameter", new String[0])); // 1 from ?1
assertThat(tokens.get(28)).isEqualTo(new SemanticTokenData(161, 177, "method", new String[0])); // json_path_exists
assertThat(tokens.get(33)).isEqualTo(new SemanticTokenData(194, 196, "operator", new String[0])); // ::
assertThat(tokens.get(34)).isEqualTo(new SemanticTokenData(196, 201, "type", new String[0])); // ::
assertThat(tokens.get(34)).isEqualTo(new SemanticTokenData(196, 201, "type", new String[0])); // jsonb
assertThat(tokens.get(37)).isEqualTo(new SemanticTokenData(204, 281, "string", new String[0])); // 'strict $.content.**.id ? (@ == "\\' || representation.targetobjectid || \\'")'
assertThat(tokens.get(39)).isEqualTo(new SemanticTokenData(282, 284, "operator", new String[0])); // ::
assertThat(tokens.get(40)).isEqualTo(new SemanticTokenData(284, 292, "type", new String[0])); // ::
assertThat(tokens.get(40)).isEqualTo(new SemanticTokenData(284, 292, "type", new String[0])); // jsonpath
}
@Test
@@ -85,7 +85,7 @@ public class PostgreSqlSemanticTokensTest {
void semiColonAtEnd() {
List<SemanticTokenData> tokens = provider.computeTokens(" select count(*) from anecdote where anecdote_id=:anecdote ; ", 0);
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(1, 7, "keyword", new String[0])); // select
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(8, 13, "keyword", new String[0])); // count
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(8, 13, "method", new String[0])); // count
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(13, 14, "operator", new String[0])); // (
assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(14, 15, "operator", new String[0])); // *
assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(15, 16, "operator", new String[0])); // )
@@ -103,7 +103,7 @@ public class PostgreSqlSemanticTokensTest {
@Test
void parameterInLimitClause_1() {
List<SemanticTokenData> tokens = provider.computeTokens("SELECT * FROM cards ORDER BY random() LIMIT :2", 0);
List<SemanticTokenData> tokens = provider.computeTokens("SELECT * FROM cards ORDER BY random() LIMIT ?2", 0);
assertThat(tokens.size()).isEqualTo(12);
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 6, "keyword", new String[0]));
@@ -203,7 +203,7 @@ public class PostgreSqlSemanticTokensTest {
assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(15, 19, "keyword", new String[0]));
assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(20, 32, "variable", new String[0]));
assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(33, 38, "keyword", new String[0]));
assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(39, 50, "keyword", new String[0]));
assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(39, 50, "variable", new String[0]));
assertThat(tokens.get(8)).isEqualTo(new SemanticTokenData(51, 52, "operator", new String[0]));
assertThat(tokens.get(9)).isEqualTo(new SemanticTokenData(53, 54, "operator", new String[0]));
assertThat(tokens.get(10)).isEqualTo(new SemanticTokenData(54, 55, "parameter", new String[0]));
@@ -285,4 +285,36 @@ public class PostgreSqlSemanticTokensTest {
""", 0);
assertThat(tokens.size()).isEqualTo(74);
}
@Test
void collate_1() {
List<SemanticTokenData> tokens = provider.computeTokens("""
SELECT DISTINCT test COLLATE "numeric" FROM Test
""", 0);
assertThat(tokens.size()).isEqualTo(7);
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 6, "keyword", new String[0])); // SELECT
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(7, 15, "keyword", new String[0])); // DISTICT
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(16, 20, "variable", new String[0])); // test
assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(21, 28, "keyword", new String[0])); // COLLATE
assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(29, 38, "variable", new String[0])); // "numeric"
assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(39, 43, "keyword", new String[0])); // FROM
assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(44, 48, "variable", new String[0])); // Test
}
@Test
void collate_2() {
List<SemanticTokenData> tokens = provider.computeTokens("""
SELECT a COLLATE "de_DE" < b FROM test1
""", 0);
assertThat(tokens.size()).isEqualTo(8);
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 6, "keyword", new String[0])); // SELECT
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(7, 8, "variable", new String[0])); // a
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(9, 16, "keyword", new String[0])); // COLLATE
assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(17, 24, "variable", new String[0])); // "de_DE"
assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(25, 26, "operator", new String[0])); // <
assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(27, 28, "variable", new String[0])); // b
assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(29, 33, "keyword", new String[0])); // FROM
assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(34, 39, "variable", new String[0])); // test1
}
}

View File

@@ -252,7 +252,7 @@ public class QueryReconcilerTest {
String docUri = directory.toPath().resolve("src/main/java/example/demo/OwnerRepository.java").toUri()
.toString();
Editor editor = harness.newEditor(LanguageId.JAVA, source, docUri);
editor.assertProblems("ptype|PostgreSQL: no viable alternative at input 'SELECTXptype'");
editor.assertProblems("SELECTX|PostgreSQL: mismatched input 'SELECTX'");
}
@Test