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  }