[JAVA-5265] Java driver throws exception for asNumber for BsonDecimal128 Created: 08/Dec/23  Updated: 03/Jan/24  Resolved: 03/Jan/24

Status: Closed
Project: Java Driver
Component/s: BSON
Affects Version/s: None
Fix Version/s: 5.0.0

Type: Bug Priority: Minor - P4
Reporter: Evan Darke Assignee: Jeffrey Yemin
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Documented
Backwards Compatibility: Minor Change
Documentation Changes: Needed
Documentation Changes Summary:

1. What would you like to communicate to the user about this feature?
2. Would you like the user to see examples of the syntax and/or executable code and its output?
3. Which versions of the driver/connector does this apply to?


 Description   

Summary

The Javadoc for BsonValue.isNumber and BsonValue.asNumber indicates that true is returned "if the value is a BsonNumber". However, BsonDecimal128 extends BsonNumber and returns false for isNumber and throws an Exception for asNumber()

How to Reproduce

 

Java Driver version: "4.4.2"

 
 

@Test
public void asNumber() {
  new BsonDecimal128(new Decimal128(1L)).asNumber();
}
 
@Test
public void isNumber() {
  Assert.assertTrue(new BsonDecimal128(new Decimal128(1L)).isNumber());
} 

Produces failure:

There were 2 failures:
1) asNumber(com.xgen.mongot.index.query.pushdown.TestDocumentMatcher)
org.bson.BsonInvalidOperationException: Value expected to be of a numerical BSON type is of unexpected type DECIMAL128
  at org.bson.BsonValue.asNumber(BsonValue.java:81)
  at com.xgen.mongot.index.query.pushdown.TestDocumentMatcher.asNumber(TestDocumentMatcher.java:60)
2) isNumber(com.xgen.mongot.index.query.pushdown.TestDocumentMatcher)
java.lang.AssertionError
  at org.junit.Assert.fail(Assert.java:87)
  at org.junit.Assert.assertTrue(Assert.java:42)
  at org.junit.Assert.assertTrue(Assert.java:53)
  at com.xgen.mongot.index.query.pushdown.TestDocumentMatcher.isNumber(TestDocumentMatcher.java:65) 

Additional Background

 



 Comments   
Comment by Githook User [ 03/Jan/24 ]

Author:

{'name': 'Jeff Yemin', 'email': 'jeff.yemin@mongodb.com', 'username': 'jyemin'}

Message: Improve BsonNumber support for Decimal128 (#1283)

  • Change implementation of BsonDecimal128#intValue/longValue/doubleValue
    to just call the corresponding methods in Decimal128, which handle
    infinity and NaN similarly to JDK classes
  • Change BsonValue#isNumber/asNumber to treat BsonDecimal128 as a number

JAVA-5265

Co-authored-by: Valentin Kovalenko <valentin.male.kovalenko@gmail.com>
Branch: master
https://github.com/mongodb/mongo-java-driver/commit/89512cd91533f4ee193bfc398dd5f8ad28f55e73

Comment by Evan Darke [ 12/Dec/23 ]

jeff.yemin@mongodb.com I think 5.0.0 would be fine. We're already using JDK 11 and I don't think we rely on any deprecated APIs.

Comment by Jeffrey Yemin [ 08/Dec/23 ]

We should also fix BsonDecimal128 implementation of Number interface. It should just be:

    @Override
    public int intValue() {
        return value.intValue();
    }
 
    @Override
    public long longValue() {
        return value.longValue();
    }
 
    @Override
    public double doubleValue() {
        return value.doubleValue();
    }

That will guard against Decimal128#bigDecimalValue throwing for NaN, Infinity, etc.

Comment by Jeffrey Yemin [ 08/Dec/23 ]

This looks like a bug, and I can't think why the code was written the way it is. Even the first version of BsonValue, which didn't have Decimal128, has:

    public BsonNumber asNumber() {
        if (getBsonType() != BSONType.INT32 && getBsonType() != BSONType.INT64 && getBsonType() != BSONType.DOUBLE) {
            throw new BSONInvalidOperationException(String.format("Value expected to be of a numerical BSON type is of unexpected type %s",
                                                                  getBsonType()));
        }
        return (BsonNumber) this;
    }

instead of just:

    public BsonNumber asNumber() {
        if (!(this instanceof BsonNumber)) {
            throw new BSONInvalidOperationException(String.format("Value expected to be of a numerical BSON type is of unexpected type %s",
                                                                  getBsonType()));
        }
        return (BsonNumber) this;
    }

Comment by Evan Darke [ 08/Dec/23 ]

For context, Mongot relies on the Java driver and we're currently working on support for Decimal128 and mixed-type numeric comparisons. Luckily we discovered this behavior through unit tests, and we're working on replacing `value.asNumber()` with `((BsonNumber) value)` throughout our codebase. However, this seems like a really error-prone construct and we don't have a lot of tests using Decimal128, so I'm hoping we can get this patched at the driver level.

Generated at Thu Feb 08 09:04:08 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.