diff --git a/CHANGELOG.md b/CHANGELOG.md index f0904bed9de8d1176931e366fc7e978d66c7b186..83f1c3ffc682b71e1e67d74e713f1997dfb255d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* [caosdb-server#145](https://gitlab.com/caosdb/caosdb-server/-/issues/145) Searching for large numbers results in wrong results if integer values are + used. + ### Security ## [0.7.3] - 2022-05-03 diff --git a/src/main/java/org/caosdb/server/query/POV.java b/src/main/java/org/caosdb/server/query/POV.java index b1a457529a0199edcc5061110ee97e416a264fff..89ea9ad46e6205dfe0583e8f54551effd82a2658 100644 --- a/src/main/java/org/caosdb/server/query/POV.java +++ b/src/main/java/org/caosdb/server/query/POV.java @@ -63,6 +63,7 @@ public class POV implements EntityFilterInterface { private DateTimeInterface vDatetime; private final String aggregate; private String targetSet = null; + private String unitStr = null; private Unit unit = null; private Long stdUnitSig = null; private Double vDoubleConvertedToStdUnit = null; @@ -112,7 +113,6 @@ public class POV implements EntityFilterInterface { // parse value to int/double/datetime if (this.value != null) { - String unitStr = null; // try and parse as integer try { @@ -122,44 +122,41 @@ public class POV implements EntityFilterInterface { throw new NumberFormatException(); } final String vIntStr = m.group(1); - unitStr = m.group(2); + this.unitStr = m.group(2); this.vInt = Integer.parseInt(vIntStr); } catch (final NumberFormatException e) { this.vInt = null; } - // try and parse as double - if (this.vInt != null) { - this.vDouble = (double) this.vInt; - } else { + // Try and parse as double, if integer parsing was unsuccessful. + if (this.vInt == null) { try { - final Pattern dp = Pattern.compile("^(-?[0-9]+(?:\\.[0-9]+))\\s*([^-]*)$"); + // Doubles are allowed without dots, for example when the integer overflows. + final Pattern dp = Pattern.compile("^(-?[0-9]+(?:\\.)?(?:[0-9]+))\\s*([^-]*)$"); final Matcher m = dp.matcher(value); if (!m.matches()) { throw new NumberFormatException(); } final String vDoubleStr = m.group(1); - unitStr = m.group(2); + this.unitStr = m.group(2); this.vDouble = Double.parseDouble(vDoubleStr); - if (this.vDouble % 1 == 0) { - this.vInt = (int) Math.floor(this.vDouble); - } } catch (final NumberFormatException e) { this.vDouble = null; } } - if (this.vDouble != null && unitStr != null && unitStr.length() > 0) { + if ((this.vDouble != null || this.vInt != null) + && this.unitStr != null + && this.unitStr.length() > 0) { try { - this.unit = getUnit(unitStr); + this.unit = getUnit(this.unitStr); } catch (final ParserException e) { e.printStackTrace(); throw new UnsupportedOperationException("Could not parse the unit."); } this.stdUnitSig = this.unit.normalize().getSignature(); - this.vDoubleConvertedToStdUnit = this.unit.convert(this.vDouble); } // try and parse as datetime try { @@ -219,6 +216,11 @@ public class POV implements EntityFilterInterface { "Versioned queries are not supported for subqueries yet. Please file a feature request."); } final long t1 = System.currentTimeMillis(); + // Add type-converted substitutes for ints/doubles. + final Integer vIntSubst = + (this.vDouble != null && this.vDouble % 1 == 0) ? (int) Math.rint(this.vDouble) : null; + final Double vDoubleSubst = (this.vInt != null) ? (double) this.vInt : null; + try { this.connection = query.getConnection(); this.targetSet = query.getTargetSet(); @@ -272,14 +274,25 @@ public class POV implements EntityFilterInterface { } else { callPOV.setNull(6, VARCHAR); } - if (this.vInt != null) { // vInt - callPOV.setInt(7, this.vInt); + if (this.vInt != null || this.vDouble != null) { // Some numeric + if (this.vInt != null) { + callPOV.setInt(7, this.vInt); + callPOV.setDouble(8, vDoubleSubst); + } else { + if (vIntSubst == null) { + callPOV.setNull(7, INTEGER); + } else { + callPOV.setInt(7, vIntSubst); + } + callPOV.setDouble(8, this.vDouble); + } + // finally: do unit conversion + if (this.unitStr != null && this.unitStr.length() > 0) { + this.vDoubleConvertedToStdUnit = + this.unit.convert(this.vDouble != null ? this.vDouble : vDoubleSubst); + } } else { callPOV.setNull(7, INTEGER); - } - if (this.vDouble != null) { // vDouble - callPOV.setDouble(8, this.vDouble); - } else { callPOV.setNull(8, DOUBLE); } if (this.unit != null) { @@ -505,6 +518,27 @@ public class POV implements EntityFilterInterface { return ret; } + /** Return the Int value, which may be null. */ + public Integer getVInt() { + if (this.vInt != null) { + return Integer.valueOf(vInt); + } + return null; + } + + /** Return the Double value, which may be null. */ + public Double getVDouble() { + if (this.vDouble != null) { + return Double.valueOf(vDouble); + } + return null; + } + + /** Return the Datetime value, which may be null. */ + public DateTimeInterface getVDatetime() { + return this.vDatetime; + } + public String getAggregate() { return this.aggregate; } diff --git a/src/test/java/org/caosdb/server/query/TestCQL.java b/src/test/java/org/caosdb/server/query/TestCQL.java index ff1be776b041490aac7c434acdee73c96a9e88f9..3a151fc3b7b908936aa5087b5b7509be73abc278 100644 --- a/src/test/java/org/caosdb/server/query/TestCQL.java +++ b/src/test/java/org/caosdb/server/query/TestCQL.java @@ -240,6 +240,7 @@ public class TestCQL { String queryIssue132b = "FIND ENTITY WHICH HAS BEEN CREATED TODAY BY ME"; String queryIssue134 = "SELECT pname FROM ename"; String queryIssue131 = "FIND ENTITY WITH pname = 13 €"; + String queryIssue145 = "FIND ENTITY WITH pname145 = 10000000000"; // File paths /////////////////////////////////////////////////////////////// String filepath_verb01 = "/foo/"; @@ -3781,6 +3782,7 @@ public class TestCQL { System.out.println(sfq.toStringTree(parser)); assertEquals("POV(pname,=,1.02m)", sfq.filter.toString()); + assertEquals(1.02, ((POV) sfq.filter).getVDouble(), 0.0); // 5 children: FIND, entity, WHITE_SPACE, filter, EOF assertEquals(5, sfq.getChildCount()); @@ -6906,4 +6908,31 @@ public class TestCQL { assertEquals("POV(pname2,>,30)", pov1.getSubProperty().getFilter().toString()); assertEquals("POV(pname2,<,40)", pov2.getSubProperty().getFilter().toString()); } + + /** + * Integer values which are too large for Int32 + * + * <p>String queryIssue145= "FIND ENTITY WITH pname145 = 10000000000"; + */ + @Test + public void testIssue145() { + // must yield a valid value + CQLLexer lexer; + lexer = new CQLLexer(CharStreams.fromString(this.queryIssue145)); + final CommonTokenStream tokens = new CommonTokenStream(lexer); + + final CQLParser parser = new CQLParser(tokens); + final CqContext sfq = parser.cq(); + + System.out.println(sfq.toStringTree(parser)); + assertEquals("POV(pname145,=,10000000000)", sfq.filter.toString()); + + // assert value + POV pov = ((POV) sfq.filter); + System.out.println(pov.getValue()); + assertEquals("10000000000", pov.getValue()); + assertNotNull(pov.getVDouble()); + assertNull(pov.getVInt()); + assertEquals(1e10, pov.getVDouble().doubleValue(), 0.0); + } }