github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/tree/decimal.go (about) 1 // Copyright 2016 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package tree 12 13 import ( 14 "fmt" 15 "math" 16 17 "github.com/cockroachdb/apd" 18 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 19 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 20 ) 21 22 var ( 23 // DecimalCtx is the default context for decimal operations. Any change 24 // in the exponent limits must still guarantee a safe conversion to the 25 // postgres binary decimal format in the wire protocol, which uses an 26 // int16. See pgwire/types.go. 27 DecimalCtx = &apd.Context{ 28 Precision: 20, 29 Rounding: apd.RoundHalfUp, 30 MaxExponent: 2000, 31 MinExponent: -2000, 32 // Don't error on invalid operation, return NaN instead. 33 Traps: apd.DefaultTraps &^ apd.InvalidOperation, 34 } 35 // ExactCtx is a decimal context with exact precision. 36 ExactCtx = DecimalCtx.WithPrecision(0) 37 // HighPrecisionCtx is a decimal context with high precision. 38 HighPrecisionCtx = DecimalCtx.WithPrecision(2000) 39 // IntermediateCtx is a decimal context with additional precision for 40 // intermediate calculations to protect against order changes that can 41 // happen in dist SQL. The additional 5 allows the stress test to pass. 42 // See #13689 for more analysis and other algorithms. 43 IntermediateCtx = DecimalCtx.WithPrecision(DecimalCtx.Precision + 5) 44 // RoundCtx is a decimal context with high precision and RoundHalfEven 45 // rounding. 46 RoundCtx = func() *apd.Context { 47 ctx := *HighPrecisionCtx 48 ctx.Rounding = apd.RoundHalfEven 49 return &ctx 50 }() 51 52 errScaleOutOfRange = pgerror.New(pgcode.NumericValueOutOfRange, "scale out of range") 53 ) 54 55 // LimitDecimalWidth limits d's precision (total number of digits) and scale 56 // (number of digits after the decimal point). Note that this any limiting will 57 // modify the decimal in-place. 58 func LimitDecimalWidth(d *apd.Decimal, precision, scale int) error { 59 if d.Form != apd.Finite || precision <= 0 { 60 return nil 61 } 62 // Use +1 here because it is inverted later. 63 if scale < math.MinInt32+1 || scale > math.MaxInt32 { 64 return errScaleOutOfRange 65 } 66 if scale > precision { 67 return pgerror.Newf(pgcode.InvalidParameterValue, "scale (%d) must be between 0 and precision (%d)", scale, precision) 68 } 69 70 // http://www.postgresql.org/docs/9.5/static/datatype-numeric.html 71 // "If the scale of a value to be stored is greater than 72 // the declared scale of the column, the system will round the 73 // value to the specified number of fractional digits. Then, 74 // if the number of digits to the left of the decimal point 75 // exceeds the declared precision minus the declared scale, an 76 // error is raised." 77 78 c := DecimalCtx.WithPrecision(uint32(precision)) 79 c.Traps = apd.InvalidOperation 80 81 if _, err := c.Quantize(d, d, -int32(scale)); err != nil { 82 var lt string 83 switch v := precision - scale; v { 84 case 0: 85 lt = "1" 86 default: 87 lt = fmt.Sprintf("10^%d", v) 88 } 89 return pgerror.Newf(pgcode.NumericValueOutOfRange, "value with precision %d, scale %d must round to an absolute value less than %s", precision, scale, lt) 90 } 91 return nil 92 }