github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/distinct.go (about) 1 package expression 2 3 import ( 4 "fmt" 5 6 "github.com/cespare/xxhash/v2" 7 8 "github.com/dolthub/go-mysql-server/sql" 9 "github.com/dolthub/go-mysql-server/sql/types" 10 ) 11 12 type DistinctExpression struct { 13 seen sql.KeyValueCache 14 dispose sql.DisposeFunc 15 Child sql.Expression 16 seenNil bool 17 } 18 19 var _ sql.Expression = (*DistinctExpression)(nil) 20 var _ sql.Disposable = (*DistinctExpression)(nil) 21 var _ sql.CollationCoercible = (*DistinctExpression)(nil) 22 23 func NewDistinctExpression(e sql.Expression) *DistinctExpression { 24 return &DistinctExpression{ 25 Child: e, 26 } 27 } 28 29 func (de *DistinctExpression) seenValue(ctx *sql.Context, value interface{}) (bool, error) { 30 if de.seen == nil { 31 cache, dispose := ctx.Memory.NewHistoryCache() 32 de.seen = cache 33 de.dispose = dispose 34 } 35 36 // nil values can't be hashed, so we need a member variable to track them 37 if value == nil { 38 if de.seenNil { 39 return false, nil 40 } 41 de.seenNil = true 42 return true, nil 43 } 44 45 v, _, err := types.Text.Convert(value) 46 if err != nil { 47 return false, err 48 } 49 str, ok := v.(string) 50 if !ok { 51 return false, fmt.Errorf("distinct unable to hash value: %s", err) 52 } 53 54 hash := xxhash.New() 55 _, err = hash.WriteString(str) 56 if err != nil { 57 return false, err 58 } 59 h := hash.Sum64() 60 61 if _, err = de.seen.Get(h); err == nil { 62 return false, nil 63 } 64 65 if err = de.seen.Put(h, struct{}{}); err != nil { 66 return false, err 67 } 68 69 return true, nil 70 } 71 72 func (de *DistinctExpression) Dispose() { 73 if de.dispose != nil { 74 de.dispose() 75 } 76 77 de.dispose = nil 78 de.seen = nil 79 de.seenNil = false 80 } 81 82 func (de *DistinctExpression) Resolved() bool { 83 return de.Child.Resolved() 84 } 85 86 func (de *DistinctExpression) String() string { 87 return fmt.Sprintf("DISTINCT %s", de.Child.String()) 88 } 89 90 func (de *DistinctExpression) Type() sql.Type { 91 return de.Child.Type() 92 } 93 94 // CollationCoercibility implements the interface sql.CollationCoercible. 95 func (de *DistinctExpression) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 96 return sql.GetCoercibility(ctx, de.Child) 97 } 98 99 func (de *DistinctExpression) IsNullable() bool { 100 return false 101 } 102 103 // Returns the child value if the cache hasn't seen the value before otherwise returns nil. 104 // Since NULLs are ignored in aggregate expressions that use DISTINCT this is a valid return scheme. 105 func (de *DistinctExpression) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 106 val, err := de.Child.Eval(ctx, row) 107 if err != nil { 108 return nil, err 109 } 110 111 should, err := de.seenValue(ctx, val) 112 if err != nil { 113 return nil, err 114 } 115 116 if should { 117 return val, nil 118 } 119 120 return nil, nil 121 } 122 123 func (de *DistinctExpression) Children() []sql.Expression { 124 return []sql.Expression{de.Child} 125 } 126 127 func (de *DistinctExpression) WithChildren(children ...sql.Expression) (sql.Expression, error) { 128 if len(children) != 1 { 129 return nil, fmt.Errorf("DistinctExpression has an invalid number of children") 130 } 131 132 return &DistinctExpression{Child: children[0]}, nil 133 }