github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/coalesce.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 21 "github.com/dolthub/go-mysql-server/sql" 22 "github.com/dolthub/go-mysql-server/sql/expression" 23 "github.com/dolthub/go-mysql-server/sql/types" 24 ) 25 26 // Coalesce returns the first non-NULL value in the list, or NULL if there are no non-NULL values. 27 type Coalesce struct { 28 args []sql.Expression 29 } 30 31 var _ sql.FunctionExpression = (*Coalesce)(nil) 32 var _ sql.CollationCoercible = (*Coalesce)(nil) 33 34 // NewCoalesce creates a new Coalesce sql.Expression. 35 func NewCoalesce(args ...sql.Expression) (sql.Expression, error) { 36 if len(args) == 0 { 37 return nil, sql.ErrInvalidArgumentNumber.New("COALESCE", "1 or more", 0) 38 } 39 40 return &Coalesce{args}, nil 41 } 42 43 // FunctionName implements sql.FunctionExpression 44 func (c *Coalesce) FunctionName() string { 45 return "coalesce" 46 } 47 48 // Description implements sql.FunctionExpression 49 func (c *Coalesce) Description() string { 50 return "returns the first non-null value in a list." 51 } 52 53 // Type implements the sql.Expression interface. 54 // The return type of Type() is the aggregated type of the argument types. 55 func (c *Coalesce) Type() sql.Type { 56 typ := types.Null 57 for _, arg := range c.args { 58 if arg == nil { 59 continue 60 } 61 t := arg.Type() 62 // special case for signed and unsigned integers 63 if (types.IsSigned(typ) && types.IsUnsigned(t)) || (types.IsUnsigned(typ) && types.IsSigned(t)) { 64 typ = types.MustCreateDecimalType(20, 0) 65 continue 66 } 67 68 if t != nil && t != types.Null { 69 convType := expression.GetConvertToType(typ, t) 70 switch convType { 71 case expression.ConvertToChar: 72 // special case for float64s 73 if (t == types.Float64 || typ == types.Float64) && !types.IsText(t) && !types.IsText(typ) { 74 typ = types.Float64 75 continue 76 } 77 // Can't get any larger than this 78 return types.LongText 79 case expression.ConvertToDecimal: 80 if typ == types.Float64 || t == types.Float64 { 81 typ = types.Float64 82 } else if types.IsDecimal(t) { 83 typ = t 84 } else if !types.IsDecimal(typ) { 85 typ = types.MustCreateDecimalType(10, 0) 86 } 87 case expression.ConvertToUnsigned: 88 if typ == types.Uint64 || t == types.Uint64 { 89 typ = types.Uint64 90 } else { 91 typ = types.Uint32 92 } 93 case expression.ConvertToSigned: 94 if typ == types.Int64 || t == types.Int64 { 95 typ = types.Int64 96 } else { 97 typ = types.Int32 98 } 99 case expression.ConvertToFloat: 100 if typ == types.Float64 || t == types.Float64 { 101 typ = types.Float64 102 } else { 103 typ = types.Float32 104 } 105 default: 106 } 107 } 108 } 109 110 return typ 111 } 112 113 // CollationCoercibility implements the interface sql.CollationCoercible. 114 func (c *Coalesce) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 115 // Preferably, this would be done during evaluation, but that's not possible with the current abstraction 116 if typ := c.Type(); typ != nil { 117 return typ.CollationCoercibility(ctx) 118 } 119 return sql.Collation_binary, 6 120 } 121 122 // IsNullable implements the sql.Expression interface. 123 // Returns true if all arguments are nil 124 // or of the first non-nil argument is nullable, otherwise false. 125 func (c *Coalesce) IsNullable() bool { 126 for _, arg := range c.args { 127 if arg == nil { 128 continue 129 } 130 return arg.IsNullable() 131 } 132 return true 133 } 134 135 func (c *Coalesce) String() string { 136 var args = make([]string, len(c.args)) 137 for i, arg := range c.args { 138 args[i] = arg.String() 139 } 140 return fmt.Sprintf("%s(%s)", c.FunctionName(), strings.Join(args, ",")) 141 } 142 143 func (c *Coalesce) DebugString() string { 144 var args = make([]string, len(c.args)) 145 for i, arg := range c.args { 146 args[i] = sql.DebugString(arg) 147 } 148 return fmt.Sprintf("%s(%s)", c.FunctionName(), strings.Join(args, ",")) 149 } 150 151 // WithChildren implements the Expression interface. 152 func (*Coalesce) WithChildren(children ...sql.Expression) (sql.Expression, error) { 153 return NewCoalesce(children...) 154 } 155 156 // Resolved implements the sql.Expression interface. 157 // The function checks if first non-nil argument is resolved. 158 func (c *Coalesce) Resolved() bool { 159 for _, arg := range c.args { 160 if arg == nil { 161 continue 162 } 163 if !arg.Resolved() { 164 return false 165 } 166 } 167 return true 168 } 169 170 // Children implements the sql.Expression interface. 171 func (c *Coalesce) Children() []sql.Expression { return c.args } 172 173 // Eval implements the sql.Expression interface. 174 // The function evaluates the first non-nil argument. If the value is nil, 175 // then we keep going, otherwise we return the first non-nil value. 176 func (c *Coalesce) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 177 for _, arg := range c.args { 178 if arg == nil { 179 continue 180 } 181 182 val, err := arg.Eval(ctx, row) 183 if err != nil { 184 return nil, err 185 } 186 187 if val == nil { 188 continue 189 } 190 191 return val, nil 192 } 193 194 return nil, nil 195 }