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

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

    XMLWordPrintableJSON

Details

    • ALL

    Description

      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 );

      Attachments

        Activity

          People

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

            Dates

              Created:
              Updated:
              Resolved: