From a5b1b3f78cd98a5df580406b0f040f09a9e15059 Mon Sep 17 00:00:00 2001 From: Theodor Kvalsvik Lauritzen Date: Fri, 22 Nov 2024 16:31:20 +0100 Subject: [PATCH] fix: Parsing of YQL queries is not caseinsensitive --- .../src/main/ccc/yqlplus/YQLPlus.ccc | 8 ++++---- .../vespa/schemals/schemadocument/YQLDocument.java | 11 ++++++----- .../main/java/ai/vespa/schemals/tree/YQLNode.java | 13 +++++++++++++ .../test/java/ai/vespa/schemals/YQLParserTest.java | 2 +- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/integration/schema-language-server/language-server/src/main/ccc/yqlplus/YQLPlus.ccc b/integration/schema-language-server/language-server/src/main/ccc/yqlplus/YQLPlus.ccc index 8137db74335..36c989a8e98 100644 --- a/integration/schema-language-server/language-server/src/main/ccc/yqlplus/YQLPlus.ccc +++ b/integration/schema-language-server/language-server/src/main/ccc/yqlplus/YQLPlus.ccc @@ -77,9 +77,9 @@ TOKEN : | < EQ: '=' > | < LIKE: 'like' > | < CONTAINS: 'contains' > -| < NOTLIKE: 'not like' > +| < NOTLIKE: 'not ' > | < MATCHES: 'matches' > -| < NOTMATCHES: 'not matches' > +| < NOTMATCHES: 'not ' > | < PLUS: '+' > | < MINUS: '-' > @@ -89,8 +89,8 @@ TOKEN : // effectively unary operators | < NULL: 'null' > -| < IS_NULL: 'is' > -| < IS_NOT_NULL: 'is not' > +| < IS_NULL: 'is ' > +| < IS_NOT_NULL: 'is not ' > // dereference | < DOT: '.' > diff --git a/integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/schemadocument/YQLDocument.java b/integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/schemadocument/YQLDocument.java index ad01a753c0f..e0ee071b8c0 100644 --- a/integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/schemadocument/YQLDocument.java +++ b/integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/schemadocument/YQLDocument.java @@ -95,9 +95,8 @@ public void updateFileContent(String content, Integer version) { updateFileContent(content); } - private static YQLPartParseResult parseYQLPart(CharSequence content, ClientLogger logger, Position offset, int charOffset) { - // CharSequence charSequence = content.toLowerCase(); - YQLPlusParser parser = new YQLPlusParser(content); + private static YQLPartParseResult parseYQLPart(String content, ClientLogger logger, Position offset, int charOffset) { + YQLPlusParser parser = new YQLPlusParser(content.toLowerCase()); try { parser.statement(); @@ -110,7 +109,7 @@ private static YQLPartParseResult parseYQLPart(CharSequence content, ClientLogge if (charsRead == 0) return new YQLPartParseResult(List.of(), Optional.empty(), charsRead); ai.vespa.schemals.parser.yqlplus.Node node = parser.rootNode(); - YQLNode retNode = new YQLNode(node, offset, charOffset); + YQLNode retNode = new YQLNode(node, offset, charOffset, content); // YQLUtils.printTree(logger, node); return new YQLPartParseResult(List.of(), Optional.of(retNode), charsRead); @@ -215,6 +214,8 @@ public static ParseResult parseContent(ParseContext context) { YQLNode ret = new YQLNode(StringUtils.getStringRange(content)); ArrayList diagnostics = new ArrayList<>(); + if (content.trim().length() == 0) return new ParseResult(diagnostics, Optional.of(ret)); + int charsRead = 0; int linesRead = 0; @@ -243,7 +244,7 @@ public static ParseResult parseContent(ParseContext context) { charsRead = newOffset; } - YQLUtils.printTree(context.logger(), ret); + // YQLUtils.printTree(context.logger(), ret); return new ParseResult(diagnostics, Optional.of(ret)); } diff --git a/integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/tree/YQLNode.java b/integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/tree/YQLNode.java index 4d496255188..dd21c943585 100644 --- a/integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/tree/YQLNode.java +++ b/integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/tree/YQLNode.java @@ -16,6 +16,17 @@ public class YQLNode extends ai.vespa.schemals.tree.Node { private String customText; private int startCharOffset; + public YQLNode(Node node, Position offset, int startCharOffset, String originalString) { + super(LanguageType.YQLPlus, CSTUtils.addPositionToRange(offset, YQLUtils.getNodeRange(node)), node.isDirty()); + originalYQLNode = node; + this.startCharOffset = startCharOffset; + this.customText = originalString.substring(node.getBeginOffset(), node.getEndOffset()); + + for (Node child : node.children()) { + addChild(new YQLNode(child, offset, startCharOffset, originalString)); + } + } + public YQLNode(Node node, Position offset, int startCharOffset) { super(LanguageType.YQLPlus, CSTUtils.addPositionToRange(offset, YQLUtils.getNodeRange(node)), node.isDirty()); originalYQLNode = node; @@ -53,6 +64,7 @@ public Range setRange(Range range) { public String getText() { if (language == LanguageType.YQLPlus) { + if (customText != null) return customText; return originalYQLNode.getSource(); } @@ -76,6 +88,7 @@ public String getText() { var child = get(i); ret += " " + child.getText(); } + if (ret.length() == 0) return ""; return ret.substring(1); } diff --git a/integration/schema-language-server/language-server/src/test/java/ai/vespa/schemals/YQLParserTest.java b/integration/schema-language-server/language-server/src/test/java/ai/vespa/schemals/YQLParserTest.java index b86f27c44ad..3bb386cbd91 100644 --- a/integration/schema-language-server/language-server/src/test/java/ai/vespa/schemals/YQLParserTest.java +++ b/integration/schema-language-server/language-server/src/test/java/ai/vespa/schemals/YQLParserTest.java @@ -189,7 +189,7 @@ Stream generateGoodTests() { "select * from music where myUrlField.hostname contains uri(\"vespa.ai\")", "select * from music where myUrlField.hostname contains ({startAnchor: true}uri(\"vespa.ai\"))", "select * from music where title contains ({weight:200}\"heads\")", - "select * from sources * where ({stem: false}(foo contains \"a\" and bar contains \"b\")) or foo contains {stem: false}\"c\"", + "select * from sources * where ({stem: false}(foo contains \"a\" and bar contains \"b\")) or foo contains ({stem: false}\"c\")", "select * from sources * where foo contains @animal and foo contains phrase(@animal, @syntaxExample, @animal)", "select * from sources * where sddocname contains 'purchase' | all(group(customer) each(output(sum(price))))", };