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

Equality to null can incorrectly use a cached partial ixscan which does not cover the predicate

    XMLWordPrintableJSON

Details

    • Query Optimization
    • Fully Compatible
    • ALL
    • v6.0, v5.0, v4.4, v4.2
    • Hide

      (function() {
      "use strict";
       
      load("jstests/aggregation/extras/utils.js");  // documentEq
       
      db.test.drop();
       
      const indexList = [
          {"num": 1, },
          {"num": -1},
      ];
       
      const indexOptions = [
          {partialFilterExpression: {$or: [{"num": {$exists: true}}, {"num": {$type: 'number'}}]}},
          {}, 
      ];
       
      const documentList = [
          {_id: 0},
          {_id: 1, num: null},
      ];
       
      assert.commandWorked(db.test.insert(documentList));
       
      for (let i = 0; i < indexList.length; i++) {
          db.test.createIndex(indexList[i], indexOptions[i]);
      }
       
      // Run the first query a few times to ensure an entry is active in the cache.
      for (let i = 0; i < 10; i++) {
          assert.eq(db.test.find({num: 5}).itcount(), 0);
      }
       
      // Now run with eq to null and expect it not to use the cached plan because of the partial filter.
      /** THIS FAILS */
      assert.eq(2, db.test.find({num: null}).itcount(), db.test.aggregate([{$planCacheStats: {}}]).toArray());
       
      })();
      

      Show
      (function() { "use strict" ;   load( "jstests/aggregation/extras/utils.js" ); // documentEq   db.test.drop();   const indexList = [ { "num" : 1 , }, { "num" : - 1 }, ];   const indexOptions = [ {partialFilterExpression: {$or: [{ "num" : {$exists: true }}, { "num" : {$type: 'number' }}]}}, {}, ];   const documentList = [ {_id: 0 }, {_id: 1 , num: null }, ];   assert .commandWorked(db.test.insert(documentList));   for (let i = 0 ; i < indexList.length; i++) { db.test.createIndex(indexList[i], indexOptions[i]); }   // Run the first query a few times to ensure an entry is active in the cache. for (let i = 0 ; i < 10 ; i++) { assert .eq(db.test.find({num: 5 }).itcount(), 0 ); }   // Now run with eq to null and expect it not to use the cached plan because of the partial filter. /** THIS FAILS */ assert .eq( 2 , db.test.find({num: null }).itcount(), db.test.aggregate([{$planCacheStats: {}}]).toArray());   })();
    • QO 2023-02-06, QO 2023-02-20
    • 16

    Description

      Given an index with a partial filter containing a $or and one branch being {<path>: {$exists: true}}. If a plan cache entry gets created (and is active) for a parameterized query such as {<path>: {$eq: 5}}, then a subsequent query of the form {<path>: {$eq: null}} will incorrectly use the cached plan even though $eq with null does not satisfy the $exists filter since it also matches missing. The $or is significant as I was not able to reproduce this with a single $exists partial index filter.

      Attached is a minimal repro script. Note that I verified this happens on 6.0 and master, but not on 5.0. 

      Attachments

        Activity

          People

            nicholas.zolnierz@mongodb.com Nicholas Zolnierz
            nicholas.zolnierz@mongodb.com Nicholas Zolnierz
            Votes:
            0 Vote for this issue
            Watchers:
            7 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: