Skip to content

Commit

Permalink
pg: fix uuid stats
Browse files Browse the repository at this point in the history
  • Loading branch information
jchappelow committed Sep 5, 2024
1 parent 45cfe16 commit 3864569
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 22 deletions.
5 changes: 5 additions & 0 deletions internal/sql/pg/histo.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,11 @@ func interpUUID(f float64, a, b types.UUID) types.UUID {
return types.UUID(interpBts(f, a[:], b[:]))
}

func interpUUIDPtr(f float64, a, b *types.UUID) *types.UUID {
uuid := interpUUID(f, *a, *b)
return &uuid
}

// interpBool is largely nonsense and should be unused as there should not ever
// be a boolean histogram, but it is here for completeness. Could just panic...
func interpBool(f float64, a, b bool) bool {
Expand Down
40 changes: 30 additions & 10 deletions internal/sql/pg/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,28 @@ func colStatsInternal(ctx context.Context, firstPass []sql.ColumnStatistics,

ins(stat, getLast(i), v.Clone(), types.CmpUint256, interpUint256)

case ColTypeUUID:

var varUUID types.UUID
switch v := val.(type) {
case pgtype.UUID:
if !v.Valid {
stat.NullCount++
continue
}
copy(varUUID[:], v.Bytes[:])
case *pgtype.UUID:
if !v.Valid {
stat.NullCount++
continue
}
copy(varUUID[:], v.Bytes[:])
default:
return fmt.Errorf("invalid uuid (%T)", val)
}

ins(stat, getLast(i), &varUUID, cmpUUIDPtr, interpUUIDPtr)

case ColTypeFloat: // we don't want, don't have
var varFloat float64
switch v := val.(type) {
Expand Down Expand Up @@ -410,8 +432,6 @@ func colStatsInternal(ctx context.Context, firstPass []sql.ColumnStatistics,

ins(stat, getLast(i), varFloat, cmp.Compare[float64], interpNum)

case ColTypeUUID:
fallthrough // TODO
default: // arrays and such
// fmt.Println("unknown", colTypes[i])
}
Expand Down Expand Up @@ -598,7 +618,7 @@ func compareStatsVal(a, b any) int { //nolint:unused
case []*types.Uint256:
return slices.CompareFunc(at, b.([]*types.Uint256), types.CmpUint256)
case *types.UUID:
return types.CmpUUID(*at, *(b.(*types.UUID)))
return cmpUUIDPtr(at, b.(*types.UUID))
case types.UUID:
return types.CmpUUID(at, b.(types.UUID))
case []*types.UUID:
Expand Down Expand Up @@ -654,11 +674,9 @@ func upColStatsWithInsert(stats *sql.ColumnStatistics, val any) error {
return ins(stats, nil, nt.Clone(), types.CmpUint256, interpUint256)

case types.UUID:
return ins(stats, nil, nt, types.CmpUUID, interpUUID)
return ins(stats, nil, &nt, cmpUUIDPtr, interpUUIDPtr)
case *types.UUID:
return del(stats, nt, func(a, b *types.UUID) int {
return types.CmpUUID(*a, *b)
})
return ins(stats, nil, nt, cmpUUIDPtr, interpUUIDPtr)

case decimal.DecimalArray: // TODO
case types.Uint256Array: // TODO
Expand All @@ -676,6 +694,10 @@ func upColStatsWithInsert(stats *sql.ColumnStatistics, val any) error {
return nil // known type, just no stats handling
}

func cmpUUIDPtr(a, b *types.UUID) int {
return types.CmpUUID(*a, *b)
}

func upColStatsWithDelete(stats *sql.ColumnStatistics, old any) error {
// DELETE:
// - unset min max if removing it. reference mincount and maxcount to know
Expand Down Expand Up @@ -719,9 +741,7 @@ func upColStatsWithDelete(stats *sql.ColumnStatistics, old any) error {
case types.UUID:
return del(stats, nt, types.CmpUUID)
case *types.UUID:
return del(stats, nt, func(a, b *types.UUID) int {
return types.CmpUUID(*a, *b)
})
return del(stats, nt, cmpUUIDPtr)

case decimal.DecimalArray: // TODO
case types.Uint256Array: // TODO
Expand Down
91 changes: 79 additions & 12 deletions internal/sql/pg/stats_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import (
"strconv"
"testing"

"github.com/kwilteam/kwil-db/common/sql"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/kwilteam/kwil-db/common/sql"
"github.com/kwilteam/kwil-db/core/types"
)

func mkTestTableDB(t *testing.T) *DB {
Expand All @@ -35,7 +37,7 @@ func mkStatsTestTableTx(t *testing.T, db *DB) sql.PreparedTx {
}
tbl := "colcheck"
t.Cleanup(func() {
defer tx.Rollback(ctx)
tx.Rollback(ctx)
if t.Failed() {
db.AutoCommit(true)
db.Execute(ctx, `drop table if exists `+tbl)
Expand All @@ -47,7 +49,8 @@ func mkStatsTestTableTx(t *testing.T, db *DB) sql.PreparedTx {
t.Fatal(err)
}
_, err = tx.Execute(ctx, `create table if not exists `+tbl+
` (a int8 primary key, b int8 default 42, c text, d bytea, e numeric(20,5), f int4[], g uint256, h uint256[])`)
` (a int8 primary key, b int8 default 42, c text, d bytea,
e numeric(20,5), f int4[], g uint256, h uint256[], i uuid)`)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -76,28 +79,92 @@ func TestTableStats(t *testing.T) {
{Pos: 6, Name: "f", DataType: "integer", Array: true, Nullable: true},
{Pos: 7, Name: "g", DataType: "uint256", Nullable: true},
{Pos: 8, Name: "h", DataType: "uint256", Array: true, Nullable: true},
{Pos: 9, Name: "i", DataType: "uuid", Nullable: true},
}

assert.Equal(t, wantCols, cols)
// t.Logf("%#v", cols)

/* equivalent insert:
_, err = tx.Execute(ctx, `insert into `+tbl+` values `+
`(5, null, '', '\xabab', 12.6, '{99}', 30, '{}'), `+
`(-1, 0, 'B', '\x01', -7, '{1, 2}', 20, '{184467440737095516150}'), `+
`(3, 1, null, '\x', 8.1, NULL, NULL, NULL), `+
`(0, 0, 'Q', NULL, NULL, NULL, NULL, NULL), `+
`(7, -4, 'c', '\x0001', 0.3333, '{2,3,4}', 40, '{5,4,3}')`)
`(5, null, '', '\xabab', 12.6, '{99}', 30, '{}', '0000857c-8671-4f4e-99bd-fcc621f9d3d1'), `+
`(-1, 0, 'B', '\x01', -7, '{1, 2}', 20, '{184467440737095516150}', '9000857c-8671-4f4e-99bd-fcc621f9d3d1'), `+
`(3, 1, null, '\x', 8.1, NULL, NULL, NULL, NULL), `+
`(0, 0, 'Q', NULL, NULL, NULL, NULL, NULL, NULL), `+
`(7, -4, 'c', '\x0001', 0.3333, '{2,3,4}', 40, '{5,4,3}', '0000157c-8671-4f4e-99bd-fcc621f9d3d1')`)
if err != nil {
t.Fatal(err)
}*/
rows := [][]any{
{int64(5), nil, "", []byte{0xab, 0xab}, mustDecimal("12.6"), []int64{99}, mustUint256("30"),
types.Uint256Array{}, mustParseUUID("0000857c-8671-4f4e-99bd-fcc621f9d3d1")},
{int64(-1), int64(0), "B", []byte{0x1}, mustDecimal("-7"), []int64{1, 2}, mustUint256("20"),
types.Uint256Array{mustUint256("184467440737095516150")}, mustParseUUID("9000857c-8671-4f4e-99bd-fcc621f9d3d1")},
{int64(3), int64(1), nil, []byte{}, mustDecimal("8.1"), nil, nil,
nil, nil},
{int64(0), int64(0), "Q", nil, nil, nil, nil,
nil, nil},
{int64(7), int64(-4), "c", []byte{0x0, 0x1}, mustDecimal("0.333"), []int64{2, 3, 4}, mustUint256("40"),
types.Uint256Array{mustUint256("5"), mustUint256("4"), mustUint256("3")}, mustParseUUID("0000157c-8671-4f4e-99bd-fcc621f9d3d1")},
}
for _, row := range rows {
_, err = tx.Execute(ctx, `insert into `+tbl+` values ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, row...)
if err != nil {
t.Fatal(err)
}
}

wantStats := []struct {
Min any
Max any
}{
{ // a
Min: int64(-1),
Max: int64(7),
},
{ // b
Min: int64(-4),
Max: int64(1),
},
{ // c
Min: "",
Max: "c",
},
{ // d
Min: []byte{}, // []
Max: []byte{0xab, 0xab}, // [171 171]
},
{ // e
Min: mustDecimal("-7.00000"), // we don't control the precision we get back, only scale
Max: mustDecimal("12.60000"),
},
{ // f -- TODO array min/max!
Min: nil,
Max: nil,
},
{ // g
Min: mustUint256("20"),
Max: mustUint256("40"),
},
{ // h -- TODO array min/max!
Min: nil,
Max: nil,
},
{ // i -- UUID
Min: mustParseUUID("0000157c-8671-4f4e-99bd-fcc621f9d3d1"),
Max: mustParseUUID("9000857c-8671-4f4e-99bd-fcc621f9d3d1"),
},
}
//t.Log(wantStats)

stats, err := TableStats(ctx, "", tbl, tx)
require.NoError(t, err)

t.Log(stats)
for i := range stats.ColumnStatistics {
cs := &stats.ColumnStatistics[i]

fmt.Println(stats.ColumnStatistics[4].Min)
fmt.Println(stats.ColumnStatistics[4].Max)
require.Equal(t, wantStats[i].Min, cs.Min)
require.Equal(t, wantStats[i].Max, cs.Max)
}
}

// Test_scanSineBig is similar to Test_updates_demo, but actually uses a DB,
Expand Down

0 comments on commit 3864569

Please sign in to comment.