Pipeline pushdown exposes leading $match

    • Type: Bug
    • Resolution: Unresolved
    • Priority: Major - P3
    • None
    • Affects Version/s: None
    • Component/s: None
    • Query Optimization
    • ALL
    • Hide
      /**
       * Reproduces a join optimizer tassert when pipeline pushdown exposes a leading $match after the
       * optimizer has already built the base collection CanonicalQuery.
       *
       * @tags: [
       *   requires_fcv_90,
       *   requires_sbe,
       * ]
       */
      
      TestData.cleanUpCoreDumpsFromExpectedCrash = true;
      const conn = MongoRunner.runMongod({
          useLogFiles: true,
          setParameter: {
              featureFlagPathArrayness: true,
              internalEnableJoinOptimization: true,
              internalJoinReorderMode: "bottomUp",
          },
      });
      assert(conn);
      const db = conn.getDB(jsTestName());
      const base = db.base;
      const foreign = db.foreign;
      assert.commandWorked(base.insert({x: 1}));
      assert.commandWorked(foreign.insert({y: 1}));
      assert.commandWorked(base.createIndex({x: 1}));
      assert.commandWorked(foreign.createIndex({y: 1}));
      
      // WILL FAIL: 11116400 "unexpected $match"
      assert.commandWorked(
          db.runCommand({
              aggregate: base.getName(),
              pipeline: [
                  {$limit: 1},
                  {$match: {x: 1}},
                  {$lookup: {from: foreign.getName(), localField: "x", foreignField: "y", as: "joined"}},
                  {$unwind: "$joined"},
              ],
              cursor: {},
          }),
      );
      
      MongoRunner.stopMongod(conn, null, {allowedExitCode: MongoRunner.EXIT_ABRUPT});
       
      Show
      /**  * Reproduces a join optimizer tassert when pipeline pushdown exposes a leading $match after the  * optimizer has already built the base collection CanonicalQuery.  *  * @tags: [  *   requires_fcv_90,  *   requires_sbe,  * ]  */ TestData.cleanUpCoreDumpsFromExpectedCrash = true ; const conn = MongoRunner.runMongod({     useLogFiles: true ,     setParameter: {         featureFlagPathArrayness: true ,         internalEnableJoinOptimization: true ,         internalJoinReorderMode: "bottomUp" ,     }, }); assert (conn); const db = conn.getDB(jsTestName()); const base = db.base; const foreign = db.foreign; assert .commandWorked(base.insert({x: 1})); assert .commandWorked(foreign.insert({y: 1})); assert .commandWorked(base.createIndex({x: 1})); assert .commandWorked(foreign.createIndex({y: 1})); // WILL FAIL: 11116400 "unexpected $match" assert .commandWorked(     db.runCommand({         aggregate: base.getName(),         pipeline: [             {$limit: 1},             {$match: {x: 1}},             {$lookup: {from: foreign.getName(), localField: "x" , foreignField: "y" , as: "joined" }},             {$unwind: "$joined" },         ],         cursor: {},     }), ); MongoRunner.stopMongod(conn, null , {allowedExitCode: MongoRunner.EXIT_ABRUPT});
    • None
    • None
    • None
    • None
    • None
    • None
    • None

      Leading $match can remain after createCanonicalQuery() pushdown, tripping 11116400AggJoinModel::constructJoinModel() assumes any $match seen in its join-prefix loop must come after at least one absorbed $lookup. But createCanonicalQuery() can remove a leading $limit/$skip/projection from suffix without re-consuming a newly exposed $match.

            Assignee:
            Unassigned
            Reporter:
            Max Verbinnen
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: