From 00625daa83dd803b65a19c95eaac8422a94abf87 Mon Sep 17 00:00:00 2001
From: Timm Fitschen <t.fitschen@indiscale.com>
Date: Mon, 7 Jun 2021 11:50:07 +0000
Subject: [PATCH] FIX: Resolve #131 and #134 (white spaces in query)

---
 CHANGELOG.md                                  |  2 ++
 .../java/org/caosdb/server/query/CQLLexer.g4  |  2 +-
 .../java/org/caosdb/server/query/CQLParser.g4 |  6 +++--
 .../java/org/caosdb/server/query/POV.java     | 18 ++++++++-------
 .../java/org/caosdb/server/query/TestCQL.java | 23 +++++++++++++++++++
 5 files changed, 40 insertions(+), 11 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2974b08d..7996c53a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,6 +32,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Fixed
 
+* #131 - CQL Parsing error when white space characters before some units.
+* #134 - CQL Parsing error when multiple white space characters after `FROM`.
 * #130 - Error during `FIND ENTITY` when
   `QUERY_FILTER_ENTITIES_WITHOUT_RETRIEVE_PERMISSIONS=False`.
 * #125 - `bend_symlinks` script did not allow whitespace in filename.
diff --git a/src/main/java/org/caosdb/server/query/CQLLexer.g4 b/src/main/java/org/caosdb/server/query/CQLLexer.g4
index 74331e0f..71b41d48 100644
--- a/src/main/java/org/caosdb/server/query/CQLLexer.g4
+++ b/src/main/java/org/caosdb/server/query/CQLLexer.g4
@@ -502,7 +502,7 @@ mode DOUBLE_QUOTE_MODE;
 mode SELECT_MODE;
 
     FROM:
-         [Ff][Rr][Oo][Mm]([ \t\n\r])? -> mode(DEFAULT_MODE)
+         [Ff][Rr][Oo][Mm]([ \t\n\r])* -> mode(DEFAULT_MODE)
     ;
 
     SELECT_DOT:
diff --git a/src/main/java/org/caosdb/server/query/CQLParser.g4 b/src/main/java/org/caosdb/server/query/CQLParser.g4
index 6c6aa748..6aea8c55 100644
--- a/src/main/java/org/caosdb/server/query/CQLParser.g4
+++ b/src/main/java/org/caosdb/server/query/CQLParser.g4
@@ -495,8 +495,10 @@ number_with_unit
 
 unit
 :
-	TXT
-	| NUM SLASH TXT
+	(~(WHITE_SPACE | WHICH | HAS_A | WITH | WHERE | DOT | AND | OR ))
+	(~(WHITE_SPACE))*
+	|
+	NUM SLASH (~(WHITE_SPACE))+
 ;
 
 location returns [String str]
diff --git a/src/main/java/org/caosdb/server/query/POV.java b/src/main/java/org/caosdb/server/query/POV.java
index a1008bd6..b1a45752 100644
--- a/src/main/java/org/caosdb/server/query/POV.java
+++ b/src/main/java/org/caosdb/server/query/POV.java
@@ -70,8 +70,8 @@ public class POV implements EntityFilterInterface {
   private String propertiesTable = null;
   private String refIdsTable = null;
   private final HashMap<String, String> statistics = new HashMap<>();
-  private Logger logger = LoggerFactory.getLogger(getClass());
-  private Stack<String> prefix = new Stack<>();
+  private final Logger logger = LoggerFactory.getLogger(getClass());
+  private final Stack<String> prefix = new Stack<>();
 
   private Unit getUnit(final String s) throws ParserException {
     return CaosDBSystemOfUnits.getUnit(s);
@@ -116,7 +116,7 @@ public class POV implements EntityFilterInterface {
 
       // try and parse as integer
       try {
-        final Pattern dp = Pattern.compile("^(-?[0-9]++)([^(\\.[0-9])-][^-]*)?$");
+        final Pattern dp = Pattern.compile("^(-?[0-9]++)\\s*([^(\\.[0-9])-][^-]*)?$");
         final Matcher m = dp.matcher(value);
         if (!m.matches()) {
           throw new NumberFormatException();
@@ -133,7 +133,7 @@ public class POV implements EntityFilterInterface {
         this.vDouble = (double) this.vInt;
       } else {
         try {
-          final Pattern dp = Pattern.compile("^(-?[0-9]+(?:\\.[0-9]+))([^-]*)$");
+          final Pattern dp = Pattern.compile("^(-?[0-9]+(?:\\.[0-9]+))\\s*([^-]*)$");
           final Matcher m = dp.matcher(value);
           if (!m.matches()) {
             throw new NumberFormatException();
@@ -142,7 +142,7 @@ public class POV implements EntityFilterInterface {
           unitStr = m.group(2);
 
           this.vDouble = Double.parseDouble(vDoubleStr);
-          if ((this.vDouble % 1) == 0) {
+          if (this.vDouble % 1 == 0) {
             this.vInt = (int) Math.floor(this.vDouble);
           }
         } catch (final NumberFormatException e) {
@@ -509,14 +509,16 @@ public class POV implements EntityFilterInterface {
     return this.aggregate;
   }
 
-  private String measurement(String m) {
+  private String measurement(final String m) {
     return String.join("", prefix) + m;
   }
 
   @Override
   public String getCacheKey() {
-    StringBuilder sb = new StringBuilder();
-    if (this.getAggregate() != null) sb.append(this.aggregate);
+    final StringBuilder sb = new StringBuilder();
+    if (this.getAggregate() != null) {
+      sb.append(this.aggregate);
+    }
     sb.append(toString());
     if (this.hasSubProperty()) {
       sb.append(getSubProperty().getCacheKey());
diff --git a/src/test/java/org/caosdb/server/query/TestCQL.java b/src/test/java/org/caosdb/server/query/TestCQL.java
index 92e82902..154977a9 100644
--- a/src/test/java/org/caosdb/server/query/TestCQL.java
+++ b/src/test/java/org/caosdb/server/query/TestCQL.java
@@ -239,6 +239,8 @@ public class TestCQL {
   String queryIssue116 = "FIND *";
   String queryIssue132a = "FIND ENTITY WHICH HAS BEEN INSERTED AFTER TODAY";
   String queryIssue132b = "FIND ENTITY WHICH HAS BEEN CREATED TODAY BY ME";
+  String queryIssue134 = "SELECT pname FROM  ename";
+  String queryIssue131 = "FIND ENTITY WITH pname = 13 €";
 
   // File paths ///////////////////////////////////////////////////////////////
   String filepath_verb01 = "/foo/";
@@ -6718,4 +6720,25 @@ public class TestCQL {
 
     assertEquals("TRANS(Insert,(,Today,Transactor(null,=))", sfq.filter.toString());
   }
+
+  /**
+   * Multiple white space chars after `FROM`.
+   *
+   * <p>String queryIssue134 = "SELECT pname FROM ename";
+   */
+  @Test
+  public void testIssue134() {
+    // must not throw ParsingException
+    new Query(this.queryIssue134).parse();
+  }
+  /**
+   * Space before special character unit
+   *
+   * <p>String queryIssue131= "FIND ENTITY WITH pname = 13 €";
+   */
+  @Test
+  public void testIssue131() {
+    // must not throw ParsingException
+    new Query(this.queryIssue131).parse();
+  }
 }
-- 
GitLab