Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-4218

update key set difference implementation does not account for equal but inequivalent numeric values

    • Query
    • Minor Change
    • ALL

      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.

        1. server4218.js
          0.4 kB
          Benety Goh

            Assignee:
            backlog-server-query Backlog - Query Team (Inactive)
            Reporter:
            guanqun Guanqun Lu
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: