github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/is_ip.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  	"net"
    20  	"strings"
    21  
    22  	"github.com/dolthub/go-mysql-server/sql/expression"
    23  	"github.com/dolthub/go-mysql-server/sql/types"
    24  
    25  	"github.com/dolthub/go-mysql-server/sql"
    26  )
    27  
    28  type IsIPv4 struct {
    29  	expression.UnaryExpression
    30  }
    31  
    32  var _ sql.FunctionExpression = (*IsIPv4)(nil)
    33  var _ sql.CollationCoercible = (*IsIPv4)(nil)
    34  
    35  func NewIsIPv4(val sql.Expression) sql.Expression {
    36  	return &IsIPv4{expression.UnaryExpression{Child: val}}
    37  }
    38  
    39  // FunctionName implements sql.FunctionExpression
    40  func (i *IsIPv4) FunctionName() string {
    41  	return "is_ipv4"
    42  }
    43  
    44  // Description implements sql.FunctionExpression
    45  func (i *IsIPv4) Description() string {
    46  	return "returns whether argument is an IPv4 address."
    47  }
    48  
    49  func (i *IsIPv4) String() string {
    50  	return fmt.Sprintf("%s(%s)", i.FunctionName(), i.Child.String())
    51  }
    52  
    53  func (i *IsIPv4) Type() sql.Type { return types.Boolean }
    54  
    55  // CollationCoercibility implements the interface sql.CollationCoercible.
    56  func (*IsIPv4) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
    57  	return sql.Collation_binary, 5
    58  }
    59  
    60  func (i *IsIPv4) WithChildren(children ...sql.Expression) (sql.Expression, error) {
    61  	if len(children) != 1 {
    62  		return nil, sql.ErrInvalidChildrenNumber.New(i, len(children), 1)
    63  	}
    64  	return NewIsIPv4(children[0]), nil
    65  }
    66  
    67  // Eval implements the Expression interface
    68  func (i *IsIPv4) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
    69  	// Evaluate value
    70  	val, err := i.Child.Eval(ctx, row)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	// If null, return nul
    76  	if val == nil {
    77  		return nil, nil
    78  	}
    79  
    80  	// Must be of type string
    81  	switch val.(type) {
    82  	case string:
    83  		// Parse IP address, return false if not valid ip
    84  		ip := net.ParseIP(val.(string))
    85  		if ip == nil {
    86  			return false, nil
    87  		}
    88  
    89  		// Check if ip address is valid IPv4 address
    90  		return ip.To4() != nil, nil
    91  	default:
    92  		return false, nil
    93  	}
    94  }
    95  
    96  type IsIPv6 struct {
    97  	expression.UnaryExpression
    98  }
    99  
   100  var _ sql.FunctionExpression = (*IsIPv6)(nil)
   101  var _ sql.CollationCoercible = (*IsIPv6)(nil)
   102  
   103  func NewIsIPv6(val sql.Expression) sql.Expression {
   104  	return &IsIPv6{expression.UnaryExpression{Child: val}}
   105  }
   106  
   107  // FunctionName implements sql.FunctionExpression
   108  func (i *IsIPv6) FunctionName() string {
   109  	return "is_ipv6"
   110  }
   111  
   112  // Description implements sql.FunctionExpression
   113  func (i *IsIPv6) Description() string {
   114  	return "returns whether argument is an IPv6 address."
   115  }
   116  
   117  func (i *IsIPv6) String() string {
   118  	return fmt.Sprintf("%s(%s)", i.FunctionName(), i.Child.String())
   119  }
   120  
   121  func (i *IsIPv6) Type() sql.Type { return types.Boolean }
   122  
   123  // CollationCoercibility implements the interface sql.CollationCoercible.
   124  func (*IsIPv6) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   125  	return sql.Collation_binary, 5
   126  }
   127  
   128  func (i *IsIPv6) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   129  	if len(children) != 1 {
   130  		return nil, sql.ErrInvalidChildrenNumber.New(i, len(children), 1)
   131  	}
   132  	return NewIsIPv6(children[0]), nil
   133  }
   134  
   135  // Eval implements the Expression interface
   136  func (i *IsIPv6) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
   137  	// Evaluate value
   138  	val, err := i.Child.Eval(ctx, row)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	// If null, return nul
   144  	if val == nil {
   145  		return nil, nil
   146  	}
   147  
   148  	// Must be of type string
   149  	switch val.(type) {
   150  	case string:
   151  		// Parse IP address, return false if not valid ip
   152  		ip := net.ParseIP(val.(string))
   153  		if ip == nil {
   154  			return false, nil
   155  		}
   156  
   157  		// Check if ip address is valid IPv6 address
   158  		return ip.To16() != nil && (strings.Count(val.(string), ":") >= 2), nil
   159  	default:
   160  		return false, nil
   161  	}
   162  }
   163  
   164  type IsIPv4Compat struct {
   165  	expression.UnaryExpression
   166  }
   167  
   168  var _ sql.FunctionExpression = (*IsIPv4Compat)(nil)
   169  var _ sql.CollationCoercible = (*IsIPv4Compat)(nil)
   170  
   171  func NewIsIPv4Compat(val sql.Expression) sql.Expression {
   172  	return &IsIPv4Compat{expression.UnaryExpression{Child: val}}
   173  }
   174  
   175  // FunctionName implements sql.FunctionExpression
   176  func (i *IsIPv4Compat) FunctionName() string {
   177  	return "is_ipv4_compat"
   178  }
   179  
   180  // Description implements sql.FunctionExpression
   181  func (i *IsIPv4Compat) Description() string {
   182  	return "returns whether argument is an IPv4-compatible address."
   183  }
   184  
   185  func (i *IsIPv4Compat) String() string {
   186  	return fmt.Sprintf("%s(%s)", i.FunctionName(), i.Child.String())
   187  }
   188  
   189  func (i *IsIPv4Compat) Type() sql.Type { return types.Boolean }
   190  
   191  // CollationCoercibility implements the interface sql.CollationCoercible.
   192  func (*IsIPv4Compat) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   193  	return sql.Collation_binary, 5
   194  }
   195  
   196  func (i *IsIPv4Compat) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   197  	if len(children) != 1 {
   198  		return nil, sql.ErrInvalidChildrenNumber.New(i, len(children), 1)
   199  	}
   200  	return NewIsIPv4Compat(children[0]), nil
   201  }
   202  
   203  // Eval implements the Expression interface
   204  func (i *IsIPv4Compat) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
   205  	// Evaluate value
   206  	val, err := i.Child.Eval(ctx, row)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  
   211  	// If null, return nul
   212  	if val == nil {
   213  		return nil, nil
   214  	}
   215  
   216  	// Expect to receive a hex encoded string
   217  	switch val.(type) {
   218  	case []byte:
   219  		// Must be of length 16
   220  		if len(val.([]byte)) != 16 {
   221  			return false, nil
   222  		}
   223  
   224  		// Check if first 12 bytes are all 0
   225  		for _, b := range val.([]byte)[:12] {
   226  			if b != 0 {
   227  				return false, nil
   228  			}
   229  		}
   230  		return true, nil
   231  	default:
   232  		return false, nil
   233  	}
   234  }
   235  
   236  type IsIPv4Mapped struct {
   237  	expression.UnaryExpression
   238  }
   239  
   240  var _ sql.FunctionExpression = (*IsIPv4Mapped)(nil)
   241  var _ sql.CollationCoercible = (*IsIPv4Mapped)(nil)
   242  
   243  func NewIsIPv4Mapped(val sql.Expression) sql.Expression {
   244  	return &IsIPv4Mapped{expression.UnaryExpression{Child: val}}
   245  }
   246  
   247  // FunctionName implements sql.FunctionExpression
   248  func (i *IsIPv4Mapped) FunctionName() string {
   249  	return "is_ipv4_mapped"
   250  }
   251  
   252  // Description implements sql.FunctionExpression
   253  func (i *IsIPv4Mapped) Description() string {
   254  	return "returns whether argument is an IPv4-mapped address."
   255  }
   256  
   257  func (i *IsIPv4Mapped) String() string {
   258  	return fmt.Sprintf("%s(%s)", i.FunctionName(), i.Child.String())
   259  }
   260  
   261  func (i *IsIPv4Mapped) Type() sql.Type { return types.Boolean }
   262  
   263  // CollationCoercibility implements the interface sql.CollationCoercible.
   264  func (*IsIPv4Mapped) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   265  	return sql.Collation_binary, 5
   266  }
   267  
   268  func (i *IsIPv4Mapped) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   269  	if len(children) != 1 {
   270  		return nil, sql.ErrInvalidChildrenNumber.New(i, len(children), 1)
   271  	}
   272  	return NewIsIPv4Mapped(children[0]), nil
   273  }
   274  
   275  // Eval implements the Expression interface
   276  func (i *IsIPv4Mapped) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
   277  	// Evaluate value
   278  	val, err := i.Child.Eval(ctx, row)
   279  	if err != nil {
   280  		return nil, err
   281  	}
   282  
   283  	// If null, return nul
   284  	if val == nil {
   285  		return nil, nil
   286  	}
   287  
   288  	// Expect to receive a hex encoded string
   289  	switch val.(type) {
   290  	case []byte:
   291  		// Must be of length 16
   292  		if len(val.([]byte)) != 16 {
   293  			return false, nil
   294  		}
   295  
   296  		// Check if first 10 bytes are all 0
   297  		for _, b := range val.([]byte)[:10] {
   298  			if b != 0 {
   299  				return false, nil
   300  			}
   301  		}
   302  
   303  		// Bytes 11 and 12 must be 0xFF
   304  		return val.([]byte)[10] == 0xFF && val.([]byte)[11] == 0xFF, nil
   305  	default:
   306  		return false, nil
   307  	}
   308  }