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 }