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  }