• Query Execution
    • ALL
    • Hide

      Deployment: single-node replica set, default configuration (internalQueryFrameworkControl is trySbeRestricted by default). The script switches the parameter with setParameter and leaves it at the default.

      db.c.insertMany([{_id: 1, k: 1, a: -0.0, b: -0.0}]);
      const pipeline = [{$match: {k: 1}}, {$group: {_id: {$atan2: [{$add: ["$a", "$b"]}, -1]}}}];
      
      // The same aggregation with the classic engine forced, then with the default engine selection.
      for (const mode of ["forceClassicEngine", "trySbeRestricted"]) {
          db.adminCommand({setParameter: 1, internalQueryFrameworkControl: mode});
          const e = db.c.explain().aggregate(pipeline);
          const qp = e.queryPlanner || e.stages[0].$cursor.queryPlanner;
          const plan = [];
          for (let s = qp.winningPlan.queryPlan || qp.winningPlan; s; s = s.inputStage) plan.push(s.stage);
          const rest = (e.stages || []).slice(1).map(s => Object.keys(s)[0]);
          print(`// ${mode}: explainVersion ${e.explainVersion}, plan ${plan.join(" <- ")}` + (rest.length ? `, then ${rest.join(", ")}` : ""));
          printjson(db.c.aggregate(pipeline).toArray());
      }
      

      Output:

      // forceClassicEngine: explainVersion 1, plan PROJECTION_SIMPLE <- COLLSCAN, then $group
      [
        {
          _id: 3.141592653589793
        }
      ]
      // trySbeRestricted: explainVersion 2, plan GROUP <- COLLSCAN
      [
        {
          _id: -3.141592653589793
        }
      ]
      
      Show
      Deployment: single-node replica set, default configuration ( internalQueryFrameworkControl is trySbeRestricted by default). The script switches the parameter with setParameter and leaves it at the default. db.c.insertMany([{_id: 1, k: 1, a: -0.0, b: -0.0}]); const pipeline = [{$match: {k: 1}}, {$group: {_id: {$atan2: [{$add: [ "$a" , "$b" ]}, -1]}}}]; // The same aggregation with the classic engine forced, then with the default engine selection. for ( const mode of [ "forceClassicEngine" , "trySbeRestricted" ]) { db.adminCommand({setParameter: 1, internalQueryFrameworkControl: mode}); const e = db.c.explain().aggregate(pipeline); const qp = e.queryPlanner || e.stages[0].$cursor.queryPlanner; const plan = []; for (let s = qp.winningPlan.queryPlan || qp.winningPlan; s; s = s.inputStage) plan.push(s.stage); const rest = (e.stages || []).slice(1).map(s => Object .keys(s)[0]); print( ` // ${mode}: explainVersion ${e.explainVersion}, plan ${plan.join( " <- " )}` + (rest.length ? `, then ${rest.join( ", " )}` : "")); printjson(db.c.aggregate(pipeline).toArray()); } Output: // forceClassicEngine: explainVersion 1, plan PROJECTION_SIMPLE <- COLLSCAN, then $group [ { _id: 3.141592653589793 } ] // trySbeRestricted: explainVersion 2, plan GROUP <- COLLSCAN [ { _id: -3.141592653589793 } ]
    • None
    • None
    • None
    • None
    • None
    • None
    • None

      $add of two -0.0 doubles gives 0.0 in the classic engine and -0.0 in SBE. Inside $group the expression runs in SBE under the default engine selection, so the result depends on the engine; $atan2 with -1 as the second argument makes the sign visible: π with the classic engine, -π with SBE.

      • classic engine: internalQueryFrameworkControl: forceClassicEngine. Check: the explain line in the output shows explainVersion 1, with $group after $cursor.
      • SBE: default internalQueryFrameworkControl: trySbeRestricted: the whole pipeline, including $group, runs in SBE. Check: the explain line shows explainVersion 2 with GROUP in the SBE plan.

      Expected: The same value with both engines; under IEEE 754 -0.0 + -0.0 is -0.0, so _id: -3.141592653589793.
      Actual: Classic engine: _id: 3.141592653589793. Default (trySbeRestricted, SBE): _id: -3.141592653589793.

      The classic AddState starts with longTotal = 0 and doubleTotal = 0; the first double operand converts the long total to a double, giving +0.0, and +0.0 + -0.0 is +0.0. SBE adds the operands pairwise (genericAdd), so -0.0 + -0.0 stays -0.0. Decimal -0 behaves the same way. $subtract and $multiply agree between the engines on the same inputs. master has the same code.

      Found with AI assistance.

            Assignee:
            [DO NOT USE] Backlog - Query Execution
            Reporter:
            r33s3n6 N/A (EXT)
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: