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

Nested $add operators may be improperly reordered by constant folding implementation

    • ALL

      Observed behavior: When a nested $add is used for string concatenation, the arguments may be concatenated in the wrong order.
      Expected behavior: Concatenation in the proper order.

      There is some code to detect string $add operands and avoid an improper constant folding, but this is not applied for nested $add expressions:

                      /*
                        If the child operand is the same type as this, then we can
                        extract its operands and inline them here because we already
                        know this is commutative and associative because it has a
                        factory.  We can detect sameness of the child operator by
                        checking for equality of the factory
      
                        Note we don't have to do this recursively, because we
                        called optimize() on all the children first thing in
                        this call to optimize().
                      */
                      ExpressionNary *pNary =
                          dynamic_cast<ExpressionNary *>(pE.get());
                      if (!pNary)
                          pNew->addOperand(pE);
                      else {
                          intrusive_ptr<ExpressionNary> (*const pChildFactory)() =
                              pNary->getFactory();
                          if (pChildFactory != pFactory)
                              pNew->addOperand(pE);
                          else {
                              /* same factory, so flatten */
                              size_t nChild = pNary->vpOperand.size();
                              for(size_t iChild = 0; iChild < nChild; ++iChild) {
                                  intrusive_ptr<Expression> pCE(
                                      pNary->vpOperand[iChild]);
                                  if (dynamic_cast<ExpressionConstant *>(pCE.get()))
                                      pConst->addOperand(pCE);
                                  else
                                      pNew->addOperand(pCE);
                              }
                          }
                      }
      

      Test:

      c = db.c;
      c.drop();
      
      c.save( { x:3 } );
      
      project = { $project:{ a:{ $add:[ 1, 2, { $add:[ 'foo', '$x' ] } ] } } };
      
      printjson( c.runCommand( 'aggregate', { pipeline:[ project ], explain:true } ) );
      
      // Actual result is '312foo'.
      assert.eq( '12foo3', c.aggregate( project ).result[ 0 ].a );
      

            Assignee:
            matt.dannenberg Matt Dannenberg
            Reporter:
            aaron Aaron Staple
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved: