mirror of
https://github.com/chaisql/chai.git
synced 2025-10-05 15:46:55 +08:00
296 lines
11 KiB
Go
296 lines
11 KiB
Go
package encoding_test
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"math"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/chaisql/chai/internal/encoding"
|
|
"github.com/chaisql/chai/internal/environment"
|
|
"github.com/chaisql/chai/internal/testutil"
|
|
"github.com/chaisql/chai/internal/tree"
|
|
"github.com/chaisql/chai/internal/types"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestCompare(t *testing.T) {
|
|
tests := []struct {
|
|
k1, k2 string
|
|
cmp int
|
|
}{
|
|
// null
|
|
{`(null)`, `(null)`, 0},
|
|
|
|
// booleans
|
|
{`(true)`, `(true)`, 0},
|
|
{`(false)`, `(true)`, -1},
|
|
{`(true)`, `(false)`, 1},
|
|
{`(false)`, `(false)`, 0},
|
|
|
|
// ints
|
|
{`(1)`, `(1)`, 0},
|
|
{`(1)`, `(2)`, -1},
|
|
{`(2)`, `(1)`, 1},
|
|
{`(1000000000)`, `(1)`, 33},
|
|
{`(254)`, `(255)`, -1}, // 2x uint8
|
|
{`(255)`, `(254)`, 1}, // 2x uint8
|
|
{`(10000)`, `(10001)`, -1}, // 2x uint16
|
|
{`(10001)`, `(10000)`, 1}, // 2x uint16
|
|
{`(1000000)`, `(1000001)`, -1}, // 2x uint32
|
|
{`(1000001)`, `(1000000)`, 1}, // 2x uint32
|
|
{`(1000000000000000)`, `(1000000000000001)`, -1}, // 2x uint64
|
|
{`(1000000000000001)`, `(1000000000000000)`, 1}, // 2x uint64
|
|
{`(-126)`, `(-127)`, 1}, // 2x int8
|
|
{`(-127)`, `(-126)`, -1}, // 2x int8
|
|
{`(-10000)`, `(-10001)`, 1}, // 2x int16
|
|
{`(-10001)`, `(-10000)`, -1}, // 2x int16
|
|
{`(-1000000)`, `(-1000001)`, 1}, // 2x int32
|
|
{`(-1000001)`, `(-1000000)`, -1}, // 2x int32
|
|
{`(-1000000000000000)`, `(-1000000000000001)`, 1}, // 2x int64
|
|
{`(-1000000000000001)`, `(-1000000000000000)`, -1}, // 2x int64
|
|
{`(-1)`, `(1)`, -2}, // neg fixint < fixuint
|
|
{`(1)`, `(31)`, -30}, // neg fixint < fixuint
|
|
{`(-127)`, `(1)`, -34}, // int8 < fixuint
|
|
{`(-10000)`, `(1)`, -35}, // int16 < fixuint
|
|
{`(-1000000)`, `(1)`, -36}, // int32 < fixuint
|
|
{`(-1000000000000000)`, `(1)`, -37}, // int64 < fixuint
|
|
{`(-127)`, `(255)`, -65}, // int8 < uint8
|
|
{`(-60000)`, `(255)`, -67}, // int16 < uint8
|
|
{`(-1000000)`, `(255)`, -67}, // int32 < uint8
|
|
{`(-1000000000000000)`, `(255)`, -68}, // int64 < uint8
|
|
|
|
// floats
|
|
{`(1.0)`, `(1.0)`, 0},
|
|
{`(1.1)`, `(1.0)`, 1},
|
|
{`(1.0)`, `(1.1)`, -1},
|
|
{`(-1.0)`, `(-1.1)`, 1},
|
|
// doubles
|
|
{`(1e50)`, `(1e50)`, 0},
|
|
{`(1e51)`, `(1e50)`, 1},
|
|
{`(1e50)`, `(1e51)`, -1},
|
|
{`(-1e50)`, `(-1e51)`, 1},
|
|
// floats and doubles
|
|
{`(1.0)`, `(1e50)`, -1},
|
|
{`(1e50)`, `(1.0)`, 1},
|
|
|
|
// text
|
|
{`('a')`, `('a')`, 0},
|
|
{`('b')`, `('a')`, 1},
|
|
{`('a')`, `('b')`, -1},
|
|
{`('a')`, `('aa')`, -1},
|
|
{`('aaaa')`, `('aab')`, -1},
|
|
|
|
// bytea
|
|
{`('\xaa')`, `('\xaa')`, 0},
|
|
{`('\xab')`, `('\xaa')`, 1},
|
|
{`('\xaa')`, `('\xab')`, -1},
|
|
{`('\xaa')`, `('\xaaaa')`, -1},
|
|
|
|
// different types
|
|
{`(null)`, `(true)`, -4},
|
|
{`(true)`, `(1)`, -43},
|
|
{`(1)`, `(1.0)`, -41},
|
|
{`(1.0)`, `('a')`, -8},
|
|
{`('a')`, `('\x00')`, -5},
|
|
|
|
// consecutive values
|
|
{`(1, 2, 3)`, `(1, 2, 3)`, 0},
|
|
{`(1, 2, 3)`, `(1, 2, 4)`, -1},
|
|
{`(1, 2, 3)`, `(1, 2, 3, 4)`, -1},
|
|
// consecutive mixed values
|
|
{`(1, true, 3.4)`, `(1, true, 3.4)`, 0},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("Compare(%s, %s)", test.k1, test.k2), func(t *testing.T) {
|
|
a1, err := testutil.ParseExprList(t, test.k1).EvalAll(&environment.Environment{})
|
|
require.NoError(t, err)
|
|
k1 := mustNewKey(t, 0, 0, a1...)
|
|
|
|
a2, err := testutil.ParseExprList(t, test.k2).EvalAll(&environment.Environment{})
|
|
require.NoError(t, err)
|
|
k2 := mustNewKey(t, 0, 0, a2...)
|
|
|
|
require.Equal(t, test.cmp, encoding.Compare(k1, k2))
|
|
|
|
// compare abbreviated keys
|
|
|
|
// prepend namespace
|
|
kk1 := mustNewKey(t, 1, 0, a1...)
|
|
kk2 := mustNewKey(t, 1, 0, a2...)
|
|
|
|
cmp := int64(encoding.AbbreviatedKey(kk1) - encoding.AbbreviatedKey(kk2))
|
|
if test.cmp < 0 {
|
|
require.False(t, cmp > 0)
|
|
} else if test.cmp > 0 {
|
|
require.False(t, cmp < 0)
|
|
} else {
|
|
require.True(t, cmp == 0)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCompareOrder(t *testing.T) {
|
|
tests := []struct {
|
|
k1, k2 string
|
|
cmp int
|
|
order []bool
|
|
}{
|
|
// null
|
|
{`(null)`, `(null)`, 0, []bool{false}},
|
|
{`(null)`, `(null)`, 0, []bool{true}},
|
|
{`(1)`, `(2)`, -1, []bool{false}},
|
|
{`(1)`, `(2)`, 1, []bool{true}},
|
|
{`(60)`, `(30)`, 2, []bool{false}},
|
|
{`(60)`, `(30)`, -2, []bool{true}},
|
|
{`(30, 'hello')`, `(30, 'bye')`, 1, []bool{true, false}},
|
|
{`(30, 'hello')`, `(30, 'bye')`, -1, []bool{true, true}},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("CompareOrder(%s, %s)", test.k1, test.k2), func(t *testing.T) {
|
|
a1, err := testutil.ParseExprList(t, test.k1).EvalAll(&environment.Environment{})
|
|
require.NoError(t, err)
|
|
order := tree.SortOrder(0)
|
|
for i := range a1 {
|
|
if test.order[i] {
|
|
order = order.SetDesc(i)
|
|
}
|
|
}
|
|
|
|
k1 := mustNewKey(t, 0, order, a1...)
|
|
|
|
a2, err := testutil.ParseExprList(t, test.k2).EvalAll(&environment.Environment{})
|
|
require.NoError(t, err)
|
|
k2 := mustNewKey(t, 0, order, a2...)
|
|
|
|
require.Equal(t, test.cmp, encoding.Compare(k1, k2))
|
|
|
|
// compare abbreviated keys
|
|
|
|
// prepend namespace
|
|
kk1 := mustNewKey(t, 1, order, a1...)
|
|
kk2 := mustNewKey(t, 1, order, a2...)
|
|
|
|
cmp := int64(encoding.AbbreviatedKey(kk1) - encoding.AbbreviatedKey(kk2))
|
|
if test.cmp < 0 {
|
|
require.False(t, cmp > 0)
|
|
} else if test.cmp > 0 {
|
|
require.False(t, cmp < 0)
|
|
} else {
|
|
require.True(t, cmp == 0)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAbbreviatedKey(t *testing.T) {
|
|
i64 := int64(-5000000000)
|
|
i32 := int32(-60000000)
|
|
i16 := int16(-10000)
|
|
i8 := int8(-127)
|
|
tests := []struct {
|
|
k string
|
|
want uint64
|
|
}{
|
|
// namespace only
|
|
{`(1)`, 0b_0000000000000001_00000000_0000000000000000000000000000000000000000},
|
|
{`(400)`, 0b_0000000110010000_00000000_0000000000000000000000000000000000000000},
|
|
{`(1000000)`, 0b_1111111111111111_00000000_0000000000000000000000000000000000000000}, // > 1 << 16
|
|
|
|
// null
|
|
{`(1, null)`, 1<<48 | uint64(encoding.NullValue)<<40},
|
|
|
|
// bool
|
|
{`(1, false)`, 1<<48 | uint64(encoding.FalseValue)<<40},
|
|
{`(1, true)`, 1<<48 | uint64(encoding.TrueValue)<<40},
|
|
|
|
// int
|
|
{`(1, 1)`, 1<<48 | (uint64(encoding.IntSmallValue)+32+1)<<40}, // positive int -> small value
|
|
{`(1, -10)`, 1<<48 | (uint64(encoding.IntSmallValue)+32-10)<<40}, // negative int -> small value
|
|
{`(1, 31)`, 1<<48 | (uint64(encoding.IntSmallValue)+32+31)<<40}, // positive int -> small value
|
|
{`(1, 100)`, 1<<48 | uint64(encoding.Uint8Value)<<40 | 100}, // positive int -> uint8
|
|
{`(1, 128)`, 1<<48 | uint64(encoding.Uint8Value)<<40 | 128}, // positive int -> uint8
|
|
{`(1, 255)`, 1<<48 | uint64(encoding.Uint8Value)<<40 | 255}, // positive int -> uint8
|
|
{`(1, 256)`, 1<<48 | uint64(encoding.Uint16Value)<<40 | 256}, // positive int -> uint16
|
|
{`(1, 999)`, 1<<48 | uint64(encoding.Uint16Value)<<40 | 999}, // positive int -> uint16
|
|
{`(1, -5000000000)`, 1<<48 | uint64(encoding.Int64Value)<<40 | (uint64(i64)+math.MaxInt64+1)>>24}, // int64
|
|
{`(1, -60000000)`, 1<<48 | uint64(encoding.Int32Value)<<40 | (uint64(i32) + math.MaxInt32 + 1)}, // int32
|
|
{`(1, -10000)`, 1<<48 | uint64(encoding.Int16Value)<<40 | (uint64(i16) + math.MaxInt16 + 1)}, // int16
|
|
{`(1, -127)`, 1<<48 | uint64(encoding.Int8Value)<<40 | (uint64(i8) + math.MaxInt8 + 1)}, // int8
|
|
{`(1, 255)`, 1<<48 | uint64(encoding.Uint8Value)<<40 | 255}, // uint8
|
|
{`(1, 50000)`, 1<<48 | uint64(encoding.Uint16Value)<<40 | 50000}, // uint16
|
|
{`(1, 500000)`, 1<<48 | uint64(encoding.Uint32Value)<<40 | 500000}, // uint32
|
|
{`(1, 5000000000)`, 1<<48 | uint64(encoding.Uint64Value)<<40 | 5000000000>>24}, // uint64
|
|
|
|
// float / double
|
|
{`(1, 1.0)`, 1<<48 | uint64(encoding.Float64Value)<<40 | uint64(math.Float64bits(1)^(1<<63))>>24},
|
|
{`(1, -1.0)`, 1<<48 | uint64(encoding.Float64Value)<<40 | uint64(math.Float64bits(-1)^(1<<64-1))>>24},
|
|
{`(1, 1e50)`, 1<<48 | uint64(encoding.Float64Value)<<40 | uint64(math.Float64bits(1e50)^1<<63)>>24},
|
|
{`(1, -1e50)`, 1<<48 | uint64(encoding.Float64Value)<<40 | uint64(math.Float64bits(-1e50)^(1<<64-1))>>24},
|
|
|
|
// text
|
|
{`(1, 'abc')`, 1<<48 | uint64(encoding.TextValue)<<40 | uint64('a')<<32 | uint64('b')<<24 | uint64('c')<<16},
|
|
{`(1, 'abcdefghijkl')`, 1<<48 | uint64(encoding.TextValue)<<40 | uint64('a')<<32 | uint64('b')<<24 | uint64('c')<<16 | uint64('d')<<8 | uint64('e')},
|
|
{`(1, 'abcdefghijkl` + strings.Repeat("m", 100) + `')`, 1<<48 | uint64(encoding.TextValue)<<40 | uint64('a')<<32 | uint64('b')<<24 | uint64('c')<<16 | uint64('d')<<8 | uint64('e')},
|
|
{`(1, 'abcdefghijkl` + strings.Repeat("m", 10000) + `')`, 1<<48 | uint64(encoding.TextValue)<<40 | uint64('a')<<32 | uint64('b')<<24 | uint64('c')<<16 | uint64('d')<<8 | uint64('e')},
|
|
|
|
// bytea
|
|
{`(1, '\xab')`, 1<<48 | uint64(encoding.ByteaValue)<<40 | uint64(0xab)<<32},
|
|
{`(1, '\xabcdefabcdef')`, 1<<48 | uint64(encoding.ByteaValue)<<40 | uint64(0xab)<<32 | uint64(0xcd)<<24 | uint64(0xef)<<16 | uint64(0xab)<<8 | uint64(0xcd)},
|
|
{`(1, '\xabcdefabcdef` + strings.Repeat("c", 100) + `')`, 1<<48 | uint64(encoding.ByteaValue)<<40 | uint64(0xab)<<32 | uint64(0xcd)<<24 | uint64(0xef)<<16 | uint64(0xab)<<8 | uint64(0xcd)},
|
|
{`(1, '\xabcdefabcdef` + strings.Repeat("c", 1000) + `')`, 1<<48 | uint64(encoding.ByteaValue)<<40 | uint64(0xab)<<32 | uint64(0xcd)<<24 | uint64(0xef)<<16 | uint64(0xab)<<8 | uint64(0xcd)},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("AbbreviatedKey(%s)", test.k), func(t *testing.T) {
|
|
a, err := testutil.ParseExprList(t, test.k).EvalAll(&environment.Environment{})
|
|
require.NoError(t, err)
|
|
k := mustNewKey(t, 0, 0, a...)
|
|
|
|
require.Equal(t, test.want, encoding.AbbreviatedKey(k))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSeparator(t *testing.T) {
|
|
tests := []struct {
|
|
k1, k2 string
|
|
}{
|
|
{`(1, 1)`, `(1, 2)`},
|
|
{`(1, 1)`, `(1, 3)`},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("Separator(%v, %v)", test.k1, test.k2), func(t *testing.T) {
|
|
v1, err := testutil.ParseExprList(t, test.k1).EvalAll(&environment.Environment{})
|
|
require.NoError(t, err)
|
|
v2, err := testutil.ParseExprList(t, test.k2).EvalAll(&environment.Environment{})
|
|
require.NoError(t, err)
|
|
k1 := mustNewKey(t, 0, 0, v1...)
|
|
k2 := mustNewKey(t, 0, 0, v2...)
|
|
sep := encoding.Separator(nil, k1, k2)
|
|
require.LessOrEqual(t, encoding.Compare(k1, sep), 0)
|
|
require.Less(t, encoding.Compare(sep, k2), 0)
|
|
})
|
|
}
|
|
}
|
|
|
|
func makeUvarint(n int) []byte {
|
|
var buf [10]byte
|
|
i := binary.PutUvarint(buf[:], uint64(n))
|
|
return buf[:i]
|
|
}
|
|
|
|
func mustNewKey(t testing.TB, namespace tree.Namespace, order tree.SortOrder, values ...types.Value) []byte {
|
|
k := tree.NewKey(values...)
|
|
|
|
b, err := k.Encode(namespace, order)
|
|
require.NoError(t, err)
|
|
|
|
return b
|
|
}
|