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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,76 @@
/*
PostgreSQL grammar.
The MIT License (MIT).
Copyright (c) 2021-2023, Oleksii Kovalov (Oleksii.Kovalov@outlook.com).
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package org.springframework.ide.vscode.parser.postgresql;
import java.util.BitSet;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.dfa.*;
public class LexerDispatchingErrorListener implements ANTLRErrorListener
{
Lexer _parent;
public LexerDispatchingErrorListener(Lexer parent)
{
_parent = parent;
}
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e)
{
ProxyErrorListener foo = new ProxyErrorListener(_parent.getErrorListeners());
foo.syntaxError(recognizer, offendingSymbol, line, charPositionInLine, msg, e);
}
public void reportAmbiguity(Parser recognizer,
DFA dfa,
int startIndex,
int stopIndex,
boolean exact,
BitSet ambigAlts,
ATNConfigSet configs)
{
ProxyErrorListener foo = new ProxyErrorListener(_parent.getErrorListeners());
foo.reportAmbiguity(recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs);
}
public void reportAttemptingFullContext(Parser recognizer,
DFA dfa,
int startIndex,
int stopIndex,
BitSet conflictingAlts,
ATNConfigSet configs)
{
ProxyErrorListener foo = new ProxyErrorListener(_parent.getErrorListeners());
foo.reportAttemptingFullContext(recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs);
}
public void reportContextSensitivity(Parser recognizer,
DFA dfa,
int startIndex,
int stopIndex,
int prediction,
ATNConfigSet configs)
{
ProxyErrorListener foo = new ProxyErrorListener(_parent.getErrorListeners());
foo.reportContextSensitivity(recognizer, dfa, startIndex, stopIndex, prediction, configs);
}
}

View File

@@ -0,0 +1,76 @@
/*
PostgreSQL grammar.
The MIT License (MIT).
Copyright (c) 2021-2023, Oleksii Kovalov (Oleksii.Kovalov@outlook.com).
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package org.springframework.ide.vscode.parser.postgresql;
import java.util.BitSet;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.dfa.*;
public class ParserDispatchingErrorListener implements ANTLRErrorListener
{
Parser _parent;
public ParserDispatchingErrorListener(Parser parent)
{
_parent = parent;
}
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e)
{
var foo = new ProxyErrorListener(_parent.getErrorListeners());
foo.syntaxError(recognizer, offendingSymbol, line, charPositionInLine, msg, e);
}
public void reportAmbiguity(Parser recognizer,
DFA dfa,
int startIndex,
int stopIndex,
boolean exact,
BitSet ambigAlts,
ATNConfigSet configs)
{
ProxyErrorListener foo = new ProxyErrorListener(_parent.getErrorListeners());
foo.reportAmbiguity(recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs);
}
public void reportAttemptingFullContext(Parser recognizer,
DFA dfa,
int startIndex,
int stopIndex,
BitSet conflictingAlts,
ATNConfigSet configs)
{
ProxyErrorListener foo = new ProxyErrorListener(_parent.getErrorListeners());
foo.reportAttemptingFullContext(recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs);
}
public void reportContextSensitivity(Parser recognizer,
DFA dfa,
int startIndex,
int stopIndex,
int prediction,
ATNConfigSet configs)
{
ProxyErrorListener foo = new ProxyErrorListener(_parent.getErrorListeners());
foo.reportContextSensitivity(recognizer, dfa, startIndex, stopIndex, prediction, configs);
}
}

View File

@@ -0,0 +1,82 @@
/*
PostgreSQL grammar.
The MIT License (MIT).
Copyright (c) 2021-2023, Oleksii Kovalov (Oleksii.Kovalov@outlook.com).
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package org.springframework.ide.vscode.parser.postgresql;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.Lexer;
import java.util.ArrayDeque;
import java.util.Deque;
public abstract class PostgreSqlLexerBase extends Lexer {
protected final Deque<String> tags = new ArrayDeque<>();
protected PostgreSqlLexerBase(CharStream input) {
super(input);
}
public void pushTag() {
tags.push(getText());
}
public boolean isTag() {
return getText().equals(tags.peek());
}
public void popTag() {
tags.pop();
}
public boolean checkLA(int c) {
return getInputStream().LA(1) != c;
}
public boolean charIsLetter() {
return Character.isLetter(getInputStream().LA(-1));
}
public void HandleNumericFail() {
getInputStream().seek(getInputStream().index() - 2);
setType(PostgreSqlLexer.Integral);
}
public void HandleLessLessGreaterGreater() {
if (getText() == "<<") setType(PostgreSqlLexer.LESS_LESS);
if (getText() == ">>") setType(PostgreSqlLexer.GREATER_GREATER);
}
public void UnterminatedBlockCommentDebugAssert() {
//Debug.Assert(InputStream.LA(1) == -1 /*EOF*/);
}
public boolean CheckIfUtf32Letter() {
int codePoint = getInputStream().LA(-2) << 8 + getInputStream().LA(-1);
char[] c;
if (codePoint < 0x10000) {
c = new char[]{(char) codePoint};
} else {
codePoint -= 0x10000;
c = new char[]{(char) (codePoint / 0x400 + 0xd800), (char) (codePoint % 0x400 + 0xdc00)};
}
return Character.isLetter(c[0]);
}
}

View File

@@ -0,0 +1,123 @@
/*
PostgreSQL grammar.
The MIT License (MIT).
Copyright (c) 2021-2023, Oleksii Kovalov (Oleksii.Kovalov@outlook.com).
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package org.springframework.ide.vscode.parser.postgresql;
import java.util.List;
import org.antlr.v4.runtime.*;
public abstract class PostgreSqlParserBase extends Parser {
public PostgreSqlParserBase(TokenStream input) {
super(input);
}
ParserRuleContext GetParsedSqlTree(String script, int line) {
PostgreSqlParser ph = getPostgreSqlParser(script);
ParserRuleContext result = ph.root();
return result;
}
public void ParseRoutineBody(PostgreSqlParser.Createfunc_opt_listContext _localctx) {
String lang = null;
for (PostgreSqlParser.Createfunc_opt_itemContext coi : _localctx.createfunc_opt_item()) {
if (coi.LANGUAGE() != null) {
if (coi.nonreservedword_or_sconst() != null)
if (coi.nonreservedword_or_sconst().nonreservedword() != null)
if (coi.nonreservedword_or_sconst().nonreservedword().identifier() != null)
if (coi.nonreservedword_or_sconst().nonreservedword().identifier()
.Identifier() != null) {
lang = coi.nonreservedword_or_sconst().nonreservedword().identifier()
.Identifier().getText();
break;
}
}
}
if (null == lang) return;
PostgreSqlParser.Createfunc_opt_itemContext func_as = null;
for (PostgreSqlParser.Createfunc_opt_itemContext a : _localctx.createfunc_opt_item()) {
if (a.func_as() != null) {
func_as = a;
break;
}
}
if (func_as != null) {
String txt = GetRoutineBodyString(func_as.func_as().sconst(0));
PostgreSqlParser ph = getPostgreSqlParser(txt);
switch (lang) {
case "plpgsql":
func_as.func_as().Definition = ph.plsqlroot();
break;
case "sql":
func_as.func_as().Definition = ph.root();
break;
}
}
}
private String TrimQuotes(String s) {
return (s == null || s.isEmpty()) ? s : s.substring(1, s.length() - 1);
}
public String unquote(String s) {
int slength = s.length();
StringBuilder r = new StringBuilder(slength);
int i = 0;
while (i < slength) {
Character c = s.charAt(i);
r.append(c);
if (c == '\'' && i < slength - 1 && (s.charAt(i + 1) == '\'')) i++;
i++;
}
return r.toString();
}
public String GetRoutineBodyString(PostgreSqlParser.SconstContext rule) {
PostgreSqlParser.AnysconstContext anysconst = rule.anysconst();
org.antlr.v4.runtime.tree.TerminalNode StringConstant = anysconst.StringConstant();
if (null != StringConstant) return unquote(TrimQuotes(StringConstant.getText()));
org.antlr.v4.runtime.tree.TerminalNode UnicodeEscapeStringConstant = anysconst.UnicodeEscapeStringConstant();
if (null != UnicodeEscapeStringConstant) return TrimQuotes(UnicodeEscapeStringConstant.getText());
org.antlr.v4.runtime.tree.TerminalNode EscapeStringConstant = anysconst.EscapeStringConstant();
if (null != EscapeStringConstant) return TrimQuotes(EscapeStringConstant.getText());
String result = "";
List<org.antlr.v4.runtime.tree.TerminalNode> dollartext = anysconst.DollarText();
for (org.antlr.v4.runtime.tree.TerminalNode s : dollartext) {
result += s.getText();
}
return result;
}
public PostgreSqlParser getPostgreSqlParser(String script) {
CharStream charStream = CharStreams.fromString(script);
Lexer lexer = new PostgreSqlLexer(charStream);
CommonTokenStream tokens = new CommonTokenStream(lexer);
PostgreSqlParser parser = new PostgreSqlParser(tokens);
lexer.removeErrorListeners();
parser.removeErrorListeners();
LexerDispatchingErrorListener listener_lexer = new LexerDispatchingErrorListener((Lexer)(((CommonTokenStream)(this.getInputStream())).getTokenSource()));
ParserDispatchingErrorListener listener_parser = new ParserDispatchingErrorListener(this);
lexer.addErrorListener(listener_lexer);
parser.addErrorListener(listener_parser);
return parser;
}
}

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