[SERVER-4218] update key set difference implementation does not account for equal but inequivalent numeric values Created: 07/Nov/11  Updated: 06/Dec/22  Resolved: 27/Dec/16

Status: Closed
Project: Core Server
Component/s: Index Maintenance, Write Ops
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Guanqun Lu Assignee: Backlog - Query Team (Inactive)
Resolution: Duplicate Votes: 0
Labels: pull-request
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Attachments: File server4218.js    
Assigned Teams:
Query
Backwards Compatibility: Minor Change
Operating System: ALL
Participants:

 Description   

The DataFileMgr::updateRecord() and getIndexChanges() code finds the index key sets for a document both pre and post update. Values present in one but not both of these key sets are added / removed from the appropriate indexes, while values present in both are untouched in the indexes. The implementation uses key equality rather than bit equivalence to determine if a key exists in both the pre and post update key set. So if a key is changed from one numeric type to another but its new value is equal to its original value, the key will not be updated in the index. As a result a covered index query can return a key with the wrong data type.

Test:

c = db.c;
c.drop();
 
c.ensureIndex( { a:1 } );
c.save( { a:5 } );
 
printjson( c.find( { a:5 }, { _id:0, a:1 } ).hint( { a:1 } ).toArray() );
printjson( c.find( { a:5 }, { _id:0, a:1 } ).hint( { $natural:1 } ).toArray() );
 
c.update( {}, { $set:{ a:NumberLong( 5 ) } } );
// Because NumberLong( 5 ) == 5, the index key is not updated.                                           
printjson( c.find( { a:5 }, { _id:0, a:1 } ).hint( { a:1 } ).toArray() );
printjson( c.find( { a:5 }, { _id:0, a:1 } ).hint( { $natural:1 } ).toArray() );
 
c.remove();
c.save( { a:5 } );
obj = c.findOne();
obj.a = NumberLong( 5 );
c.update( {}, obj );
// Because NumberLong( 5 ) == 5, the index key is not updated.                                           
printjson( c.find( { a:5 }, { _id:0, a:1 } ).hint( { a:1 } ).toArray() );
printjson( c.find( { a:5 }, { _id:0, a:1 } ).hint( { $natural:1 } ).toArray() );

– Aaron

-------------------------------------------------------

run the following script:

t = db.users;
t.drop();
t.insert( { age: 5, name : "xxx" } );
t.insert( { age: 5, name : "yyy" } );
t.insert( { age: 5, name : "zzz" } );
t.ensureIndex( {age: 1} );
rec = t.findOne( {name: "yyy"} );
delete rec.name;
rec.age = NumberLong(5);
t.update( {name: "yyy"}, rec );
t.find( {age:5}, {_id:0, age:1} )

The last output should be:

{ "age" : 5 }
{ "age" : NumberLong(5) }
{ "age" : 5 }

but actually, the output is:

{ "age" : 5 }
{ "age" : 5 }
{ "age" : 5 }

The type info is lost.



 Comments   
Comment by Asya Kamsky [ 27/Dec/16 ]

This was fixed for 3.4 with work on SERVER-16801

Comment by Benety Goh [ 28/Aug/14 ]

server4218.js - previously updatej.js in the pull request:

https://github.com/mongodb/mongo/pull/143

Comment by Guanqun Lu [ 07/Nov/11 ]

I've sent a pull request here: https://github.com/mongodb/mongo/pull/143
I've run the jstests, and it passed. So I'm afraid I'm not having regression yet. I'll write more test cases.

Generated at Thu Feb 08 03:05:19 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.