github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/soundex.go (about)

     1  // Copyright 2020-2021 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package function
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  	"unicode"
    21  
    22  	"github.com/dolthub/go-mysql-server/sql"
    23  	"github.com/dolthub/go-mysql-server/sql/expression"
    24  	"github.com/dolthub/go-mysql-server/sql/types"
    25  )
    26  
    27  // Soundex is a function that returns the soundex of a string. Two strings that
    28  // sound almost the same should have identical soundex strings. A standard
    29  // soundex string is four characters long, but the SOUNDEX() function returns
    30  // an arbitrarily long string.
    31  type Soundex struct {
    32  	expression.UnaryExpression
    33  }
    34  
    35  var _ sql.FunctionExpression = (*Soundex)(nil)
    36  var _ sql.CollationCoercible = (*Soundex)(nil)
    37  
    38  // NewSoundex creates a new Soundex expression.
    39  func NewSoundex(e sql.Expression) sql.Expression {
    40  	return &Soundex{expression.UnaryExpression{Child: e}}
    41  }
    42  
    43  // FunctionName implements sql.FunctionExpression
    44  func (s *Soundex) FunctionName() string {
    45  	return "soundex"
    46  }
    47  
    48  // Description implements sql.FunctionExpression
    49  func (s *Soundex) Description() string {
    50  	return "returns the soundex of a string."
    51  }
    52  
    53  // Eval implements the Expression interface.
    54  func (s *Soundex) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
    55  	v, err := s.Child.Eval(ctx, row)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	if v == nil {
    61  		return nil, nil
    62  	}
    63  
    64  	v, _, err = types.LongText.Convert(v)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	var b strings.Builder
    70  	var last rune
    71  	for _, c := range strings.ToUpper(v.(string)) {
    72  		if last == 0 && !unicode.IsLetter(c) {
    73  			continue
    74  		}
    75  		code := s.code(c)
    76  		if last == 0 {
    77  			b.WriteRune(c)
    78  			last = code
    79  			continue
    80  		}
    81  		if code == '0' || code == last {
    82  			continue
    83  		}
    84  		b.WriteRune(code)
    85  		last = code
    86  	}
    87  	if b.Len() == 0 {
    88  		return "0000", nil
    89  	}
    90  	for i := len([]rune(b.String())); i < 4; i++ {
    91  		b.WriteRune('0')
    92  	}
    93  	return b.String(), nil
    94  }
    95  
    96  func (s *Soundex) code(c rune) rune {
    97  	switch c {
    98  	case 'B', 'F', 'P', 'V':
    99  		return '1'
   100  	case 'C', 'G', 'J', 'K', 'Q', 'S', 'X', 'Z':
   101  		return '2'
   102  	case 'D', 'T':
   103  		return '3'
   104  	case 'L':
   105  		return '4'
   106  	case 'M', 'N':
   107  		return '5'
   108  	case 'R':
   109  		return '6'
   110  	}
   111  	return '0'
   112  }
   113  
   114  func (s *Soundex) String() string {
   115  	return fmt.Sprintf("%s(%s)", s.FunctionName(), s.Child)
   116  }
   117  
   118  // WithChildren implements the Expression interface.
   119  func (s *Soundex) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   120  	if len(children) != 1 {
   121  		return nil, sql.ErrInvalidChildrenNumber.New(s, len(children), 1)
   122  	}
   123  	return NewSoundex(children[0]), nil
   124  }
   125  
   126  // Type implements the Expression interface.
   127  func (s *Soundex) Type() sql.Type {
   128  	return types.LongText
   129  }
   130  
   131  // CollationCoercibility implements the interface sql.CollationCoercible.
   132  func (*Soundex) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   133  	return ctx.GetCollation(), 4
   134  }