[SERVER-32072] DBRef with NumberInt ID changing to float in the shell Created: 22/Nov/17  Updated: 30/Oct/23  Resolved: 01/Feb/18

Status: Closed
Project: Core Server
Component/s: Shell
Affects Version/s: 3.2.7, 3.4.10, 3.6.0-rc4
Fix Version/s: 3.6.3, 3.7.2

Type: Bug Priority: Minor - P4
Reporter: simon hall Assignee: Mira Carey
Resolution: Fixed Votes: 1
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Backports
Backwards Compatibility: Fully Compatible
Operating System: ALL
Backport Requested:
v3.6, v3.4, v3.2
Steps To Reproduce:

Run the following commands from the shell or as shell scripts

  • Step 1

    //insert a new row
    var currentId= ObjectId();
    db.test.insert({ 
        "_id" : currentId, 
        "string" : "1", 
        "int" : NumberInt(9087), 
        "float" : 1.1, 
        "refLong" : DBRef("ReferenceList", NumberLong(9087), "Reference"), 
        "refInt" : DBRef("ReferenceList", NumberInt(9087), "Reference")
    });
    

  • STEP2

    //fetch the row and save it again
    var doc = db.test.findOne({_id:currentId});
    //doc.refInt.$id = NumberInt(row.refInt.$id);
    db.test.save(doc);
    

Sprint: Platforms 2018-02-12
Participants:

 Description   

I have a issue when fetching and saving documents in the shell have a DBRef id that is a NumberInt

If you insert this document and then view the document json is fine

{ 
    "_id" : ObjectId(), 
    "string" : "1", 
    "int" : NumberInt(9087), 
    "float" : 1.1, 
    "refLong" : DBRef("ReferenceList", NumberLong(9087), "Reference"), 
    "refInt" : DBRef("ReferenceList", NumberInt(9087), "Reference")
}

But if you run a shell script that finds the document and then saves it again, the field "refInt" changes from

  • "refInt" : DBRef("ReferenceList", NumberInt(9087), "Reference")
    To
  • "refInt" : DBRef("ReferenceList", 9087.0, "Reference")

The field "int" does not change

I have checked a few versions and have the same issue

When running the steps to reproduce, if you run only step one, the data is fine, if you run Step 2, dbref id changes from int to float



 Comments   
Comment by Githook User [ 09/Feb/18 ]

Author:

{'email': 'jcarey@argv.me', 'name': 'Jason Carey', 'username': 'hanumantmk'}

Message: SERVER-32072 Always roundtrip dbrefs in the shell

dbrefs in the shell can see silent casts from int -> float due to a lack
of special case logic that regular bson objects receive.

For a fix, hook up the special lookup routines in js bsoninfo type into
the js dbrefinfo types.

(cherry picked from commit edebe4d632290b991c291d5e0e0d8bb7e3f0428b)
Branch: v3.6
https://github.com/mongodb/mongo/commit/d9e01f4eb569495b63b459e05e38609d3427976c

Comment by Githook User [ 01/Feb/18 ]

Author:

{'email': 'jcarey@argv.me', 'name': 'Jason Carey', 'username': 'hanumantmk'}

Message: SERVER-32072 Always roundtrip dbrefs in the shell

dbrefs in the shell can see silent casts from int -> float due to a lack
of special case logic that regular bson objects receive.

For a fix, hook up the special lookup routines in js bsoninfo type into
the js dbrefinfo types.
Branch: master
https://github.com/mongodb/mongo/commit/edebe4d632290b991c291d5e0e0d8bb7e3f0428b

Comment by Mira Carey [ 05/Dec/17 ]

After some code inspection, this is a bug dating back at least to the v8 -> spidermonkey upgrade (3.0). We have special logic inside of objects tagged as "bson" that we don't have for dbref and simply don't try as hard to preserve integer values.

It can show up with some variability because an unrelated optimization avoids re-serialization of objects we haven't descended down into (i.e. if the print one of these objects, you'll definitely see the bug. If you never look at it, it will depend on how save/insert was implemented up above the c++ in js land).

Comment by simon hall [ 28/Nov/17 ]

Hello, the output of your commands are the same on my system but I would like to focus more on the field 'refInt' for my issues with DBRef

Looking at your example of searching by type I can better explain my issue

Lets drop the collection, insert a fresh row and search for all documents that have a 32 bit integer for 'refInt.$id'

> db.test.drop()
true
> var currentId= ObjectId();
> db.test.insert({     "_id" : currentId,      "string" : "1",      "int" : NumberInt(9087),      "float" : 1.1,      "refLong" : DBRef("ReferenceList", NumberLong(9087), "Reference"),      "refInt" : DBRef("ReferenceList", NumberInt(9087), "Reference") });
WriteResult({ "nInserted" : 1 })
> db.test.find({'refInt.$id': {$type: 16}}).pretty()
{
        "_id" : ObjectId("5a1d79dab385a8ff51e115f1"),
        "string" : "1",
        "int" : 9087,
        "float" : 1.1,
        "refLong" : DBRef("ReferenceList", NumberLong(9087), "Reference"),
        "refInt" : DBRef("ReferenceList", 9087, "Reference")
}
>

This brings back the record as expected

Now lets fetch the record to 'var doc' and then save it

> var doc = db.test.findOne({'refInt.$id': {$type: 16}});
> db.test.save(doc);
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

Now I use the same search as I used in the first block of code and it does not find any records

> db.test.find({'refInt.$id': {$type: 16}}).pretty()

but an empty query will bring back the result

> db.test.find({}).pretty()
{
        "_id" : ObjectId("5a1d79dab385a8ff51e115f1"),
        "string" : "1",
        "int" : 9087,
        "float" : 1.1,
        "refLong" : DBRef("ReferenceList", NumberLong(9087), "Reference"),
        "refInt" : DBRef("ReferenceList", 9087, "Reference")
}
>

So it look like saving is changing the data type of DBRef ids if that id type starts off as NumberInt (int32)

Comment by Ramon Fernandez Marina [ 27/Nov/17 ]

Thanks for your report plasticsnake. This is what I see in 3.4.10:

> db.serverBuildInfo().version
3.4.10
> db.test.drop()
true
> var currentId= ObjectId();
> db.test.insert({     "_id" : currentId,      "string" : "1",      "int" : NumberInt(9087),      "float" : 1.1,      "refLong" : DBRef("ReferenceList", NumberLong(9087), "Reference"),      "refInt" : DBRef("ReferenceList", NumberInt(9087), "Reference") });
WriteResult({ "nInserted" : 1 })
> db.test.findOne()
{
        "_id" : ObjectId("5a1c8c3106f7fd905ee06db1"),
        "string" : "1",
        "int" : 9087,
        "float" : 1.1,
        "refLong" : DBRef("ReferenceList", NumberLong(9087), "Reference"),
        "refInt" : DBRef("ReferenceList", 9087, "Reference")
}
> var doc = db.test.findOne({_id:currentId});
> db.test.save(doc);
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.findOne()
{
        "_id" : ObjectId("5a1c8c3106f7fd905ee06db1"),
        "string" : "1",
        "int" : 9087,
        "float" : 1.1,
        "refLong" : DBRef("ReferenceList", NumberLong(9087), "Reference"),
        "refInt" : DBRef("ReferenceList", 9087, "Reference")
}

This is the behavior described in SERVER-5234 (the NumberInt() object wrapper is not displayed). The field int is affected too, which differs from the behavior you describe. Can you please elaborate on the meaning of

The field "int" does not change

above? Also, I checked the BSON type of the int field, and it's still an integer:

> db.test.find({int: {$type: 16}}).pretty()
{
        "_id" : ObjectId("5a1c8c3106f7fd905ee06db1"),
        "string" : "1",
        "int" : 9087,
        "float" : 1.1,
        "refLong" : DBRef("ReferenceList", NumberLong(9087), "Reference"),
        "refInt" : DBRef("ReferenceList", 9087, "Reference")
}

What are the outputs of the last two commands above for you? Does SERVER-5234 explain what you're seeing?

Thanks,
Ramón.

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