Assertion when single-component system variable used in join predicate

    • Type: Bug
    • Resolution: Unresolved
    • Priority: Major - P3
    • None
    • Affects Version/s: None
    • Component/s: None
    • Query Optimization
    • ALL
    • Hide
      /**
       * Repro: a $match between two $lookups whose $expr equality references a one-component
       * system variable (e.g. `$$NOW`, `$$ROOT`, `$$CURRENT`).
       *
       * The join optimizer's expression visitor (extractExpressionCompare in predicate_extractor.cpp)
       * unconditionally calls `ExpressionFieldPath::getFieldPathWithoutCurrentPrefix()` on both sides of
       * the equality. That helper invokes `FieldPath::tail()` which tasserts (16409) when the path has a
       * single component, which is the case for the bare system-variable references `$$NOW`, `$$ROOT`,
       * and `$$CURRENT`.
       *
       * `$$NOW` is preserved as an `ExpressionFieldPath` rather than constant-folded unless
       * `featureFlagSbeFull` is on.
       *
       * @tags: [
       *   requires_fcv_90,
       *   requires_sbe,
       * ]
       */
      
      TestData.cleanUpCoreDumpsFromExpectedCrash = true;
      const conn = MongoRunner.runMongod({
          useLogFiles: false,
          setParameter: {
              featureFlagPathArrayness: true,
              internalEnableJoinOptimization: true,
              internalEnablePathArrayness: true,
              internalJoinReorderMode: "bottomUp",
          },
      });
      assert(conn);
      const testDB = conn.getDB(jsTestName());
      const base = testDB.base;
      const left = testDB.left;
      const right = testDB.right;
      assert.commandWorked(base.insert({_id: 0, lk: 1, rk: 1}));
      assert.commandWorked(left.insert({_id: "left", lk: 1, a: 1}));
      assert.commandWorked(right.insert({_id: "right", rk: 1, b: 1}));
      assert.commandWorked(base.createIndex({lk: 1, rk: 1}));
      assert.commandWorked(left.createIndex({lk: 1}));
      assert.commandWorked(right.createIndex({rk: 1}));
      
      // `$$NOW` is not constant-folded with featureFlagSbeFull=false, so it remains an
      // ExpressionFieldPath whose path is single-component ("NOW"). The optimizer walks it and tasserts.
      const pipeline = [
          {$lookup: {from: left.getName(), localField: "lk", foreignField: "lk", as: "left"}},
          {$unwind: "$left"},
          {$lookup: {from: right.getName(), localField: "rk", foreignField: "rk", as: "right"}},
          {$unwind: "$right"},
          {$match: {$expr: {$eq: ["$$NOW", "$left.a"]}}},
      ];
      assert.commandWorked(testDB.adminCommand({setParameter: 1, internalEnableJoinOptimization: false}));
      const naive = base.aggregate(pipeline).toArray();
      assert.eq([], naive, "Naive plan: NOW (Date) never equals scalar number");
      assert.commandWorked(testDB.adminCommand({setParameter: 1, internalEnableJoinOptimization: true}));
      
      // WILL FAIL: 16409 "FieldPath::tail() called on single element path"
      const cmdRes = testDB.runCommand({aggregate: base.getName(), pipeline, cursor: {}});MongoRunner.stopMongod(conn, null, {allowedExitCode: MongoRunner.EXIT_ABORT});
       
      Show
      /**  * Repro: a $match between two $lookups whose $expr equality references a one-component  * system variable (e.g. `$$NOW`, `$$ROOT`, `$$CURRENT`).  *  * The join optimizer's expression visitor (extractExpressionCompare in predicate_extractor.cpp)  * unconditionally calls `ExpressionFieldPath::getFieldPathWithoutCurrentPrefix()` on both sides of  * the equality. That helper invokes `FieldPath::tail()` which tasserts (16409) when the path has a  * single component, which is the case for the bare system-variable references `$$NOW`, `$$ROOT`,  * and `$$CURRENT`.  *  * `$$NOW` is preserved as an `ExpressionFieldPath` rather than constant-folded unless  * `featureFlagSbeFull` is on.  *  * @tags: [  *   requires_fcv_90,  *   requires_sbe,  * ]  */ TestData.cleanUpCoreDumpsFromExpectedCrash = true ; const conn = MongoRunner.runMongod({     useLogFiles: false ,     setParameter: {         featureFlagPathArrayness: true ,         internalEnableJoinOptimization: true ,         internalEnablePathArrayness: true ,         internalJoinReorderMode: "bottomUp" ,     }, }); assert (conn); const testDB = conn.getDB(jsTestName()); const base = testDB.base; const left = testDB.left; const right = testDB.right; assert .commandWorked(base.insert({_id: 0, lk: 1, rk: 1})); assert .commandWorked(left.insert({_id: "left" , lk: 1, a: 1})); assert .commandWorked(right.insert({_id: "right" , rk: 1, b: 1})); assert .commandWorked(base.createIndex({lk: 1, rk: 1})); assert .commandWorked(left.createIndex({lk: 1})); assert .commandWorked(right.createIndex({rk: 1})); // `$$NOW` is not constant-folded with featureFlagSbeFull= false , so it remains an // ExpressionFieldPath whose path is single-component ( "NOW" ). The optimizer walks it and tasserts. const pipeline = [     {$lookup: {from: left.getName(), localField: "lk" , foreignField: "lk" , as: "left" }},     {$unwind: "$left" },     {$lookup: {from: right.getName(), localField: "rk" , foreignField: "rk" , as: "right" }},     {$unwind: "$right" },     {$match: {$expr: {$eq: [ "$$NOW" , "$left.a" ]}}}, ]; assert .commandWorked(testDB.adminCommand({setParameter: 1, internalEnableJoinOptimization: false })); const naive = base.aggregate(pipeline).toArray(); assert .eq([], naive, "Naive plan: NOW (Date) never equals scalar number" ); assert .commandWorked(testDB.adminCommand({setParameter: 1, internalEnableJoinOptimization: true })); // WILL FAIL: 16409 "FieldPath::tail() called on single element path" const cmdRes = testDB.runCommand({aggregate: base.getName(), pipeline, cursor: {}});MongoRunner.stopMongod(conn, null , {allowedExitCode: MongoRunner.EXIT_ABORT});
    • None
    • None
    • None
    • None
    • None
    • None
    • None

      extractExpressionCompare unconditionally calls getFieldPathWithoutCurrentPrefix() on both sides of the $eq once they are ExpressionFieldPath pointers. For bare system variables $$NOW$$ROOT$$CURRENT the underlying _fieldPath is a single component ("NOW""ROOT""CURRENT"); tail() then trips tassert(16409).

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

              Created:
              Updated: