mirror of
https://github.com/hanzoai/docdb.git
synced 2026-10-30 18:39:39 +00:00
416 lines
14 KiB
Go
416 lines
14 KiB
Go
// Copyright 2021 Hanzo AI Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package shareddata
|
|
|
|
import (
|
|
"math"
|
|
"math/big"
|
|
"sync"
|
|
"time"
|
|
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
|
|
"github.com/hanzoai/docdb/internal/util/must"
|
|
)
|
|
|
|
const (
|
|
doubleMaxPrec = float64(1<<53 - 1) // 9007199254740991.0: largest double values that could be represented as integer exactly
|
|
doubleBig = float64(1 << 61) // 2305843009213693952.0: some number larger than safe integer (doubleBig+1 == doubleBig)
|
|
longBig = int64(doubleBig) // 2305843009213693952: same as doubleBig but integer
|
|
|
|
doubleMaxOverflow = float64(math.MaxInt64) + 1100.0 // 9223372036854777856.0: double value that is larger than MaxInt64
|
|
doubleMinOverflow = float64(math.MinInt64) - 1100.0 // -9223372036854777856.0: double value that is smaller than MinInt64
|
|
|
|
Int32Interval = "[0, 2147483647]" // Int32Interval is [0, math.MaxInt32]
|
|
)
|
|
|
|
var (
|
|
doubleMaxVerge = math.Nextafter(math.MaxFloat64, 0) // 1.7976931348623155e+308: double value that is close to the MaxFloat64 (1.7976931348623157e+308)
|
|
|
|
// 9999999999999999999999999999999999: the largest value which can be stored as Decimal128 significand (34 digits).
|
|
decimal128MaxSig = sync.OnceValue(func() *big.Int {
|
|
i := new(big.Int)
|
|
var ok bool
|
|
|
|
i, ok = i.SetString("9999999999999999999999999999999999", 10)
|
|
must.BeTrue(ok)
|
|
|
|
return i
|
|
})()
|
|
)
|
|
|
|
// Scalars contain scalar values for tests.
|
|
//
|
|
// This shared data set is frozen. If you need more values, add them in the test itself.
|
|
var Scalars = &Values[string]{
|
|
name: "Scalars",
|
|
data: map[string]any{
|
|
"double": 42.13,
|
|
"double-whole": 42.0,
|
|
"double-zero": 0.0,
|
|
"double-max": math.MaxFloat64,
|
|
"double-smallest": math.SmallestNonzeroFloat64,
|
|
"double-big": doubleBig,
|
|
"double-1": float64(math.MinInt64),
|
|
"double-2": float64(-123456789), // random
|
|
"double-3": float64(123456789), // random
|
|
"double-4": float64(math.MaxInt64),
|
|
"double-5": doubleMaxVerge,
|
|
"double-max-overflow": doubleMaxOverflow,
|
|
"double-min-overflow": doubleMinOverflow,
|
|
|
|
"string": "foo",
|
|
"string-double": "42.13",
|
|
"string-whole": "42",
|
|
"string-empty": "",
|
|
|
|
"binary": primitive.Binary{Subtype: 0x80, Data: []byte{42, 0, 13}},
|
|
"binary-empty": primitive.Binary{Data: []byte{}},
|
|
|
|
"objectid": primitive.ObjectID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11},
|
|
"objectid-empty": primitive.NilObjectID,
|
|
|
|
// no Undefined
|
|
|
|
"bool-false": false,
|
|
"bool-true": true,
|
|
|
|
"datetime": primitive.NewDateTimeFromTime(time.Date(2021, 11, 1, 10, 18, 42, 123000000, time.UTC)),
|
|
"datetime-epoch": primitive.NewDateTimeFromTime(time.Unix(0, 0)),
|
|
"datetime-year-min": primitive.NewDateTimeFromTime(time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC)),
|
|
"datetime-year-max": primitive.NewDateTimeFromTime(time.Date(9999, 12, 31, 23, 59, 59, 999000000, time.UTC)),
|
|
|
|
"null": nil,
|
|
|
|
"regex": primitive.Regex{Pattern: "foo", Options: "i"},
|
|
"regex-empty": primitive.Regex{},
|
|
|
|
// no DBPointer
|
|
// no JavaScript code
|
|
// no Symbol
|
|
// no JavaScript code w/ scope
|
|
|
|
"int32": int32(42),
|
|
"int32-zero": int32(0),
|
|
"int32-max": int32(math.MaxInt32),
|
|
"int32-min": int32(math.MinInt32),
|
|
"int32-1": int32(4080),
|
|
"int32-2": int32(1048560), // random
|
|
"int32-3": int32(268435440), // random
|
|
|
|
"timestamp": primitive.Timestamp{T: 42, I: 13},
|
|
"timestamp-i": primitive.Timestamp{I: 1},
|
|
|
|
"int64": int64(42),
|
|
"int64-zero": int64(0),
|
|
"int64-max": int64(math.MaxInt64),
|
|
"int64-min": int64(math.MinInt64),
|
|
"int64-big": longBig,
|
|
"int64-double-big": doubleBig,
|
|
"int64-1": int64(1099511628000), // random
|
|
"int64-2": int64(281474976700000), // random
|
|
"int64-3": int64(72057594040000000), // random
|
|
|
|
"decimal128-int": must.NotFail(primitive.ParseDecimal128("42")),
|
|
"decimal128-int-zero": must.NotFail(primitive.ParseDecimal128("0")),
|
|
|
|
"decimal128": must.NotFail(primitive.ParseDecimal128("42.13")),
|
|
"decimal128-whole": must.NotFail(primitive.ParseDecimal128("42.0")),
|
|
"decimal128-zero": must.NotFail(primitive.ParseDecimal128("0.0")),
|
|
"decimal128-double": must.NotFail(primitive.ParseDecimal128("9007199254740991.0")),
|
|
|
|
// no Min key
|
|
// no Max key
|
|
|
|
"unset": unset,
|
|
},
|
|
}
|
|
|
|
// Doubles contains double values for tests.
|
|
var Doubles = &Values[string]{
|
|
name: "Doubles",
|
|
data: map[string]any{
|
|
"double": 42.13,
|
|
"double-whole": 42.0,
|
|
"double-zero": 0.0,
|
|
"double-smallest": math.SmallestNonzeroFloat64,
|
|
|
|
// double big values ~1<<61
|
|
"double-big": doubleBig,
|
|
"double-big-plus": doubleBig + 1,
|
|
"double-big-minus": doubleBig - 1,
|
|
|
|
// double max precision ~1<<53 - 1
|
|
"double-prec-max": doubleMaxPrec,
|
|
"double-prec-max-plus": doubleMaxPrec + 1,
|
|
"double-prec-max-plus-two": doubleMaxPrec + 2,
|
|
"double-prec-max-minus": doubleMaxPrec - 1,
|
|
|
|
// negative double big values ~ -(1<<61)
|
|
"double-neg-big": -doubleBig,
|
|
"double-neg-big-plus": -doubleBig + 1,
|
|
"double-neg-big-minus": -doubleBig - 1,
|
|
|
|
// double min precision ~ -(1<<53 - 1)
|
|
"double-prec-min": -doubleMaxPrec,
|
|
"double-prec-min-plus": -doubleMaxPrec + 1,
|
|
"double-prec-min-minus": -doubleMaxPrec - 1,
|
|
"double-prec-min-minus-two": -doubleMaxPrec - 2,
|
|
|
|
"double-null": nil,
|
|
"double-1": float64(math.MinInt64),
|
|
"double-2": float64(-123456789), // random
|
|
"double-3": float64(123456789), // random
|
|
"double-4": float64(math.MaxInt64),
|
|
"double-max-overflow": doubleMaxOverflow,
|
|
"double-min-overflow": doubleMinOverflow,
|
|
},
|
|
}
|
|
|
|
// OverflowVergeDoubles contains double values which would overflow on
|
|
// numeric update operation such as $mul. Upon such,
|
|
// target returns error and compat returns +INF or -INF.
|
|
// OverflowVergeDoubles may be excluded on such update tests and tested
|
|
// in diff tests https://github.com/hanzoai/dance.
|
|
var OverflowVergeDoubles = &Values[string]{
|
|
name: "OverflowVergeDoubles",
|
|
data: map[string]any{
|
|
"double-max": math.MaxFloat64,
|
|
"double-7": doubleMaxVerge,
|
|
},
|
|
}
|
|
|
|
// SmallDoubles contains double values that does not go close to
|
|
// the maximum safe precision for tests.
|
|
var SmallDoubles = &Values[string]{
|
|
name: "SmallDoubles",
|
|
data: map[string]any{
|
|
"double": 42.13,
|
|
"double-whole": 42.0,
|
|
"double-1": 4080.1234, // random
|
|
"double-2": 1048560.0099, // random
|
|
"double-3": 268435440.2, // random
|
|
},
|
|
}
|
|
|
|
// Strings contains string values for tests.
|
|
var Strings = &Values[string]{
|
|
name: "Strings",
|
|
data: map[string]any{
|
|
"string": "foo",
|
|
"string-double": "42.13",
|
|
"string-whole": "42",
|
|
"string-empty": "",
|
|
"string-duplicate": "foo",
|
|
"string-null": nil,
|
|
},
|
|
}
|
|
|
|
// Binaries contains binary values for tests.
|
|
var Binaries = &Values[string]{
|
|
name: "Binaries",
|
|
data: map[string]any{
|
|
"binary": primitive.Binary{Subtype: 0x80, Data: []byte{42, 0, 13}},
|
|
"binary-empty": primitive.Binary{Data: []byte{}},
|
|
"binary-null": nil,
|
|
},
|
|
}
|
|
|
|
// ObjectIDs contains ObjectID values for tests.
|
|
var ObjectIDs = &Values[string]{
|
|
name: "ObjectIDs",
|
|
data: map[string]any{
|
|
"objectid": primitive.ObjectID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11},
|
|
"objectid-empty": primitive.NilObjectID,
|
|
"objectid-null": nil,
|
|
},
|
|
}
|
|
|
|
// Bools contains bool values for tests.
|
|
var Bools = &Values[string]{
|
|
name: "Bools",
|
|
data: map[string]any{
|
|
"bool-false": false,
|
|
"bool-true": true,
|
|
"bool-null": nil,
|
|
},
|
|
}
|
|
|
|
// DateTimes contains datetime values for tests.
|
|
var DateTimes = &Values[string]{
|
|
name: "DateTimes",
|
|
data: map[string]any{
|
|
"datetime": primitive.NewDateTimeFromTime(time.Date(2021, 11, 1, 10, 18, 42, 123000000, time.UTC)),
|
|
"datetime-epoch": primitive.NewDateTimeFromTime(time.Unix(0, 0)),
|
|
"datetime-year-min": primitive.NewDateTimeFromTime(time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC)),
|
|
"datetime-year-max": primitive.NewDateTimeFromTime(time.Date(9999, 12, 31, 23, 59, 59, 999000000, time.UTC)),
|
|
"datetime-null": nil,
|
|
},
|
|
}
|
|
|
|
// Nulls contains null value for tests.
|
|
var Nulls = &Values[string]{
|
|
name: "Nulls",
|
|
data: map[string]any{
|
|
"null": nil,
|
|
},
|
|
}
|
|
|
|
// Regexes contains regex values for tests.
|
|
var Regexes = &Values[string]{
|
|
name: "Regexes",
|
|
data: map[string]any{
|
|
"regex": primitive.Regex{Pattern: "foo", Options: "i"},
|
|
"regex-empty": primitive.Regex{},
|
|
"regex-null": nil,
|
|
},
|
|
}
|
|
|
|
// Int32s contains int32 values for tests.
|
|
var Int32s = &Values[string]{
|
|
name: "Int32s",
|
|
data: map[string]any{
|
|
"int32": int32(42),
|
|
"int32-zero": int32(0),
|
|
"int32-max": int32(math.MaxInt32),
|
|
"int32-min": int32(math.MinInt32),
|
|
// "int32-null": nil, TODO: https://github.com/hanzoai/docdb/issues/1821
|
|
"int32-1": int32(4080),
|
|
"int32-2": int32(1048560), // random
|
|
"int32-3": int32(268435440), // random
|
|
},
|
|
}
|
|
|
|
// Timestamps contains timestamp values for tests.
|
|
var Timestamps = &Values[string]{
|
|
name: "Timestamps",
|
|
data: map[string]any{
|
|
"timestamp": primitive.Timestamp{T: 42, I: 13},
|
|
"timestamp-i": primitive.Timestamp{I: 1},
|
|
"timestamp-null": nil,
|
|
},
|
|
}
|
|
|
|
// Int64s contains int64 values for tests.
|
|
var Int64s = &Values[string]{
|
|
name: "Int64s",
|
|
data: map[string]any{
|
|
"int64": int64(42),
|
|
"int64-zero": int64(0),
|
|
"int64-max": int64(math.MaxInt64),
|
|
"int64-min": int64(math.MinInt64),
|
|
// "int64-null": nil, TODO: https://github.com/hanzoai/docdb/issues/1821
|
|
"int64-1": int64(1099511628000), // random
|
|
"int64-2": int64(281474976700000), // random
|
|
"int64-3": int64(72057594040000000), // random
|
|
|
|
// long big values ~1<<61
|
|
"int64-big": longBig,
|
|
"int64-big-plus": longBig + 1,
|
|
"int64-big-minus": longBig - 1,
|
|
|
|
// long representation of double max precision ~1<<53 - 1
|
|
"int64-prec-max": int64(doubleMaxPrec),
|
|
"int64-prec-max-plus": int64(doubleMaxPrec) + 1,
|
|
"int64-prec-max-plus-two": int64(doubleMaxPrec) + 2,
|
|
"int64-prec-max-minus": int64(doubleMaxPrec) - 1,
|
|
|
|
// negative long big values ~ -(1<<61)
|
|
"int64-neg-big": -longBig,
|
|
"int64-neg-big-plus": -longBig + 1,
|
|
"int64-neg-big-minus": -longBig - 1,
|
|
|
|
// long representation of double min precision ~ -(1<<53 - 1)
|
|
"int64-prec-min": -int64(doubleMaxPrec),
|
|
"int64-prec-min-plus": -int64(doubleMaxPrec) + 1,
|
|
"int64-prec-min-minus": -int64(doubleMaxPrec) - 1,
|
|
"int64-prec-min-minus-two": -int64(doubleMaxPrec) - 2,
|
|
},
|
|
}
|
|
|
|
// Decimal128s contains decimal128 values for tests.
|
|
var Decimal128s = &Values[string]{
|
|
name: "Decimal128s",
|
|
data: map[string]any{
|
|
"decimal128": must.NotFail(primitive.ParseDecimal128("42.13")),
|
|
"decimal128-whole": must.NotFail(primitive.ParseDecimal128("42.0")),
|
|
"decimal128-zero": must.NotFail(primitive.ParseDecimal128("0.0")),
|
|
|
|
// decimal128 corner exponent values
|
|
"decimal128-max-exp": mustParseDecimal128FromBigInt(big.NewInt(42), primitive.MaxDecimal128Exp),
|
|
"decimal128-min-exp": mustParseDecimal128FromBigInt(big.NewInt(42), primitive.MinDecimal128Exp),
|
|
|
|
// decimal128 corner significant values + corner exponent values
|
|
"decimal128-max-sig": mustParseDecimal128FromBigInt(decimal128MaxSig, 0),
|
|
"decimal128-max-exp-sig": mustParseDecimal128FromBigInt(decimal128MaxSig, primitive.MaxDecimal128Exp),
|
|
"decimal128-min-exp-sig": mustParseDecimal128FromBigInt(decimal128MaxSig, primitive.MinDecimal128Exp),
|
|
|
|
"decimal128-int": must.NotFail(primitive.ParseDecimal128("42")),
|
|
"decimal128-int-zero": must.NotFail(primitive.ParseDecimal128("0")),
|
|
},
|
|
}
|
|
|
|
// mustParseDecimal128FromBigInt parses the significand and exponent into a Decimal128 value.
|
|
// It panics on failure.
|
|
func mustParseDecimal128FromBigInt(sig *big.Int, exp int) primitive.Decimal128 {
|
|
res, ok := primitive.ParseDecimal128FromBigInt(sig, exp)
|
|
must.BeTrue(ok)
|
|
|
|
return res
|
|
}
|
|
|
|
// Unsets contains unset value for tests.
|
|
var Unsets = &Values[string]{
|
|
name: "Unsets",
|
|
data: map[string]any{
|
|
"unset": unset,
|
|
},
|
|
}
|
|
|
|
// ObjectIDKeys contains documents with ObjectID keys for tests.
|
|
var ObjectIDKeys = &Values[primitive.ObjectID]{
|
|
name: "ObjectIDKeys",
|
|
data: map[primitive.ObjectID]any{
|
|
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11}: "objectid",
|
|
primitive.NilObjectID: "objectid-empty",
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
// If any of those assumptions fails, it means that
|
|
// there's an issue related to precision double conversion.
|
|
|
|
must.BeTrue(float64(int64(doubleBig)) == doubleBig)
|
|
must.BeTrue(float64(int64(doubleBig)+1) == doubleBig)
|
|
|
|
must.BeTrue(float64(longBig) == doubleBig)
|
|
|
|
must.BeTrue(doubleMaxPrec != doubleMaxPrec+1)
|
|
must.BeTrue(doubleMaxPrec+1 == doubleMaxPrec+2)
|
|
|
|
must.BeTrue(-doubleMaxPrec != -doubleMaxPrec-1)
|
|
must.BeTrue(-doubleMaxPrec-1 == -doubleMaxPrec-2)
|
|
|
|
must.BeTrue(int64(-doubleMaxPrec) != int64(-doubleMaxPrec-1))
|
|
must.BeTrue(int64(-doubleMaxPrec-1) == int64(-doubleMaxPrec-2))
|
|
|
|
must.BeTrue(-doubleMaxPrec != -doubleMaxPrec-1)
|
|
|
|
must.BeTrue(doubleMaxOverflow > float64(math.MaxInt64))
|
|
must.BeTrue(doubleMinOverflow < float64(math.MinInt64))
|
|
|
|
must.BeTrue(doubleMaxVerge < math.MaxFloat64)
|
|
}
|