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

Constant folding should not assume arithmetic is associative or commutative in the general case

    • Fully Compatible
    • ALL
    • v6.0
    • Hide
      db.coll.drop()
      db.coll.insert({_id: 0, v: NumberDecimal("917.6875119062092")})
      db.coll.insert({_id: 1, v: NumberDecimal("927.3345924210555")})
      const pipeline = [
      {$project: {product: {$multiply: [
      	-3.14159265859,
      	"$v",
      	-314159255
      ]}}}
      ]
      db.coll.aggregate(pipeline)
      db.adminCommand({
      	configureFailPoint: 'disablePipelineOptimization',
      	mode: 'alwaysOn',
      })
      db.coll.aggregate(pipeline)
      db.adminCommand({
      	configureFailPoint: 'disablePipelineOptimization',
      	mode: 'off',
      })
      
      Show
      db.coll.drop() db.coll.insert({_id: 0, v: NumberDecimal( "917.6875119062092" )}) db.coll.insert({_id: 1, v: NumberDecimal( "927.3345924210555" )}) const pipeline = [ {$project: {product: {$multiply: [ -3.14159265859, "$v" , -314159255 ]}}} ] db.coll.aggregate(pipeline) db.adminCommand({ configureFailPoint: 'disablePipelineOptimization' , mode: 'alwaysOn' , }) db.coll.aggregate(pipeline) db.adminCommand({ configureFailPoint: 'disablePipelineOptimization' , mode: 'off' , })
    • QO 2022-02-07, QO 2022-02-21
    • 149

      Currently, arithmetic aggregation expressions like $multiply are marked as associative and commutative which allows the constant folding optimization to rearrange constants to combine as many of them as possible during optimization.

      There are three reasons this is not a valid optimization in general:

      1. For integers, this is not a safe optimization due to overflow. For example, 1073741824*-1*2 == -2147483648, but 1073741824*2*-1 overflows.

      2. Floating point arithmetic is not generally associative. See this paper, specifically:

      Due to roundoff errors, the associative laws of algebra do not necessarily hold for floating-point numbers. For example, the expression (x+y)z has a totally different answer than x(y+z) when x = 1030, y = -1030 and z = 1 (it is 1 in the former case, 0 in the latter). The importance of preserving parentheses cannot be overemphasized.

      3. Type coercions occur when performing an operation on multiple types. For example, 1 + 0.5 + 2 will convert 1 from an integer to a float before adding it to 0.5. These type coercions are dependent on the order of operations and could change the result. See the "steps to reproduce" section for an example with `NumberDecimal`.

      Because we can't know if a fieldpath will resolve to a specific number type or a value within a specific range, we cannot rule out any kind of overflow or type coercion.

      After investigation, this only applies to ExpressionAdd and ExpressionMultiply.

      As part of the fix for this ticket, we'll implement a left-to-right constant folding optimization that respects the order of operations during query execution while folding as many constants as is safe. For example, [c1, c2, c3, "$v", c5, c6] will be folded to [c', "$v", c5, c6].

            Assignee:
            davis.haupt@mongodb.com Davis Haupt (Inactive)
            Reporter:
            davis.haupt@mongodb.com Davis Haupt (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            16 Start watching this issue

              Created:
              Updated:
              Resolved: