diff --git a/CHANGELOG.md b/CHANGELOG.md index f0143f3d049d95f9f66034772e847d6ecdc34155..613ee3150ee520199828e2bc7c875c4a924983e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 returned without ID but with a notice now. - #11 - pam_authentication leaks the password to unprivileged processes on the same machine. +- #39 - quotes around datetimes in queries ### Security (in case of vulnerabilities) diff --git a/src/main/java/caosdb/server/query/CQLParser.g4 b/src/main/java/caosdb/server/query/CQLParser.g4 index af981d6a2d027892c144070323aa924a5d7674a5..8b5e5a94aaea5d62ba60068a1ad652f7b51f1bd4 100644 --- a/src/main/java/caosdb/server/query/CQLParser.g4 +++ b/src/main/java/caosdb/server/query/CQLParser.g4 @@ -4,6 +4,8 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen + * Copyright (C) 2020 Indiscale GmbH <info@indiscale.com> + * Copyright (C) 2020 Florian Spreckelsen <f.spreckelsen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -188,11 +190,11 @@ username returns [Query.Pattern ep] locals [int type] transaction_time returns [String tqp] : - ( - (ON | IN) - (datetime {$tqp = $datetime.text;} - | entity {$tqp = $entity.ep.toString();}) - ) | TODAY {$tqp = TransactionFilter.TODAY;} + ( + (ON | IN) + (value {$tqp = $value.text;} + | entity {$tqp = $entity.ep.toString();}) + ) | TODAY {$tqp = TransactionFilter.TODAY;} ; /* @@ -244,8 +246,8 @@ pov returns [POV filter] locals [Query.Pattern p, String o, String v, String a] ) | IS_NULL {$o = "0";} | IS_NOT_NULL {$o = "!0";} - | IN datetime {$o = "("; $v=$datetime.text;} - | NEGATION IN datetime {$o = "!("; $v=$datetime.text;} + | IN value {$o = "("; $v=$value.str;} + | NEGATION IN value {$o = "!("; $v=$value.str;} )? ) | @@ -443,7 +445,7 @@ minmax returns [String agg] value returns [String str] : number {$str = $text;} - | datetime {$str = $text;} + | datetime {$str = $datetime.text;} | atom {$str = $atom.ep.toString();} ; diff --git a/src/main/java/caosdb/server/query/POV.java b/src/main/java/caosdb/server/query/POV.java index a50be918da30d6cb69b457ef4b7801ea3019d58d..924c1293bfea78ef15f1b6ab10e5d9d91a2a98cc 100644 --- a/src/main/java/caosdb/server/query/POV.java +++ b/src/main/java/caosdb/server/query/POV.java @@ -167,6 +167,9 @@ public class POV implements EntityFilterInterface { } catch (final ClassCastException e) { this.vDatetime = null; } catch (final IllegalArgumentException e) { + if (this.operator.contains("(")) { + throw new Query.ParsingException("the value is expected to be a date time"); + } this.vDatetime = null; } } else { diff --git a/src/test/java/caosdb/server/query/TestCQL.java b/src/test/java/caosdb/server/query/TestCQL.java index 6006297dac0cd029ee451c0349f2b11e7ac2f86e..402cddd2be631291d772d494804cf21bc9dbc6b7 100644 --- a/src/test/java/caosdb/server/query/TestCQL.java +++ b/src/test/java/caosdb/server/query/TestCQL.java @@ -167,8 +167,11 @@ public class TestCQL { String ticket187 = "FIND Entity WITH pname=-1"; String ticket207 = "FIND RECORD WHICH REFERENCES 10594"; String query33 = "FIND ename WITH a date IN 2015"; + String query33a = "FIND ename WITH a date IN \"2015\""; String query34 = "FIND ename WITH a date NOT IN 2015"; + String query34a = "FIND ename WITH a date NOT IN \"2015\""; String query35 = "FIND ename WITH a date IN 2015-01-01"; + String query35a = "FIND ename WITH a date IN \"2015-01-01\""; String query36 = "FIND ename.pname LIKE \"wil*card\""; String query37 = "FIND ename.pname LIKE wil*card"; String query38 = "FIND ename WHICH HAS A pname LIKE \"wil*\""; @@ -4671,6 +4674,45 @@ public class TestCQL { assertEquals("2015", pov.getValue()); } + /** String query33a = "FIND ename WITH a date IN \"2015\""; */ + @Test + public void testQuery33a() { + CQLLexer lexer; + lexer = new CQLLexer(CharStreams.fromString(this.query33a)); + final CommonTokenStream tokens = new CommonTokenStream(lexer); + + final CQLParser parser = new CQLParser(tokens); + final CqContext sfq = parser.cq(); + + System.out.println(sfq.toStringTree(parser)); + + // 4 children: FIND, role, WHICHCLAUSE, EOF + assertEquals(4, sfq.getChildCount()); + assertEquals("FIND", sfq.getChild(0).getText()); + assertEquals("ename", sfq.getChild(1).getText()); + assertEquals("WITHadateIN\"2015\"", sfq.getChild(2).getText()); + assertEquals("ename", sfq.e.toString()); + assertNull(sfq.r); + assertEquals("POV", sfq.filter.getClass().getSimpleName()); + + final ParseTree whichclause = sfq.getChild(2); + // 2 children; WHICH, transaction + assertEquals(2, whichclause.getChildCount()); + assertEquals("WITHa", whichclause.getChild(0).getText()); + assertEquals("dateIN\"2015\"", whichclause.getChild(1).getText()); + + final ParseTree transactionFilter = whichclause.getChild(1).getChild(0); + assertEquals(3, transactionFilter.getChildCount()); + assertEquals("date", transactionFilter.getChild(0).getText()); + assertEquals("IN", transactionFilter.getChild(1).getText()); + assertEquals("\"2015\"", transactionFilter.getChild(2).getText()); + + assertTrue(sfq.filter instanceof POV); + final POV pov = (POV) sfq.filter; + assertEquals("(", pov.getOperator()); + assertEquals("2015", pov.getValue()); + } + /** String query34 = "FIND ename WITH a date NOT IN 2015"; */ @Test public void testQuery34() { @@ -4711,6 +4753,46 @@ public class TestCQL { assertEquals("2015", pov.getValue()); } + /** String query34a = "FIND ename WITH a date NOT IN \"2015\""; */ + @Test + public void testQuery34a() { + CQLLexer lexer; + lexer = new CQLLexer(CharStreams.fromString(this.query34a)); + final CommonTokenStream tokens = new CommonTokenStream(lexer); + + final CQLParser parser = new CQLParser(tokens); + final CqContext sfq = parser.cq(); + + System.out.println(sfq.toStringTree(parser)); + + // 4 children: FIND, role, WHICHCLAUSE, EOF + assertEquals(4, sfq.getChildCount()); + assertEquals("FIND", sfq.getChild(0).getText()); + assertEquals("ename", sfq.getChild(1).getText()); + assertEquals("WITHadateNOTIN\"2015\"", sfq.getChild(2).getText()); + assertEquals("ename", sfq.e.toString()); + assertNull(sfq.r); + assertEquals("POV", sfq.filter.getClass().getSimpleName()); + + final ParseTree whichclause = sfq.getChild(2); + // 2 children: WHICH, POV + assertEquals(2, whichclause.getChildCount()); + assertEquals("WITHa", whichclause.getChild(0).getText()); + assertEquals("dateNOTIN\"2015\"", whichclause.getChild(1).getText()); + + final ParseTree transactionFilter = whichclause.getChild(1).getChild(0); + assertEquals(4, transactionFilter.getChildCount()); + assertEquals("date", transactionFilter.getChild(0).getText()); + assertEquals("NOT", transactionFilter.getChild(1).getText()); + assertEquals("IN", transactionFilter.getChild(2).getText()); + assertEquals("\"2015\"", transactionFilter.getChild(3).getText()); + + assertTrue(sfq.filter instanceof POV); + final POV pov = (POV) sfq.filter; + assertEquals("!(", pov.getOperator()); + assertEquals("2015", pov.getValue()); + } + /** String query35 = "FIND ename WITH a date IN 2015-01-01"; */ @Test public void testQuery35() { @@ -4750,6 +4832,45 @@ public class TestCQL { assertEquals("2015-01-01", pov.getValue()); } + /** String query35a = "FIND ename WITH a date IN \"2015-01-01\""; */ + @Test + public void testQuery35a() { + CQLLexer lexer; + lexer = new CQLLexer(CharStreams.fromString(this.query35a)); + final CommonTokenStream tokens = new CommonTokenStream(lexer); + + final CQLParser parser = new CQLParser(tokens); + final CqContext sfq = parser.cq(); + + System.out.println(sfq.toStringTree(parser)); + + // 4 children: FIND, role, WHICHCLAUSE, EOF + assertEquals(4, sfq.getChildCount()); + assertEquals("FIND", sfq.getChild(0).getText()); + assertEquals("ename", sfq.getChild(1).getText()); + assertEquals("WITHadateIN\"2015-01-01\"", sfq.getChild(2).getText()); + assertEquals("ename", sfq.e.toString()); + assertNull(sfq.r); + assertEquals("POV", sfq.filter.getClass().getSimpleName()); + + final ParseTree whichclause = sfq.getChild(2); + // 2 children: WHICH, POV + assertEquals(2, whichclause.getChildCount()); + assertEquals("WITHa", whichclause.getChild(0).getText()); + assertEquals("dateIN\"2015-01-01\"", whichclause.getChild(1).getText()); + + final ParseTree transactionFilter = whichclause.getChild(1).getChild(0); + assertEquals(3, transactionFilter.getChildCount()); + assertEquals("date", transactionFilter.getChild(0).getText()); + assertEquals("IN", transactionFilter.getChild(1).getText()); + assertEquals("\"2015-01-01\"", transactionFilter.getChild(2).getText()); + + assertTrue(sfq.filter instanceof POV); + final POV pov = (POV) sfq.filter; + assertEquals("(", pov.getOperator()); + assertEquals("2015-01-01", pov.getValue()); + } + /** String query36 = "FIND ename.pname LIKE \"wil*card\""; */ @Test public void testQuery36() {