github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/variables.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 expression
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  
    21  	"github.com/dolthub/go-mysql-server/sql"
    22  	"github.com/dolthub/go-mysql-server/sql/types"
    23  )
    24  
    25  // SystemVar is an expression that returns the value of a system variable. It's also used as the expression on the left
    26  // hand side of a SET statement for a system variable.
    27  type SystemVar struct {
    28  	Name           string
    29  	Collation      sql.CollationID
    30  	Scope          sql.SystemVariableScope
    31  	SpecifiedScope string
    32  }
    33  
    34  var _ sql.Expression = (*SystemVar)(nil)
    35  var _ sql.CollationCoercible = (*SystemVar)(nil)
    36  
    37  // NewSystemVar creates a new SystemVar expression for the system variable named |name| with the specified |scope|.
    38  // The |specifiedScope| parameter indicates the exact scope that was specified in the original reference to this
    39  // system variable, and is used to ensure we output a column name in a result set that exactly matches how the
    40  // system variable was originally referenced. If the |specifiedScope| parameter is empty, then the scope was not
    41  // originally specified and any scope has been inferred.
    42  func NewSystemVar(name string, scope sql.SystemVariableScope, specifiedScope string) *SystemVar {
    43  	return &SystemVar{name, sql.CollationID(0), scope, specifiedScope}
    44  }
    45  
    46  // Children implements the sql.Expression interface.
    47  func (v *SystemVar) Children() []sql.Expression { return nil }
    48  
    49  // Eval implements the sql.Expression interface.
    50  func (v *SystemVar) Eval(ctx *sql.Context, _ sql.Row) (interface{}, error) {
    51  	switch v.Scope {
    52  	case sql.SystemVariableScope_Session:
    53  		// "character_set_database" and "collation_database" are special system variables, in that they're set whenever
    54  		// the current database is changed. Rather than attempting to synchronize the session variables of all
    55  		// outstanding contexts whenever a database's collation is updated, we just pull the values from the database
    56  		// directly. MySQL also plans to make these system variables immutable (from the user's perspective). This isn't
    57  		// exactly the same as MySQL's behavior, but this is the intent of their behavior, which is also way easier to
    58  		// implement.
    59  		switch strings.ToLower(v.Name) {
    60  		case "character_set_database":
    61  			return v.Collation.CharacterSet().String(), nil
    62  		case "collation_database":
    63  			return v.Collation.String(), nil
    64  		default:
    65  			val, err := ctx.GetSessionVariable(ctx, v.Name)
    66  			if err != nil {
    67  				return nil, err
    68  			}
    69  			return val, nil
    70  		}
    71  	case sql.SystemVariableScope_Global:
    72  		_, val, ok := sql.SystemVariables.GetGlobal(v.Name)
    73  		if !ok {
    74  			return nil, sql.ErrUnknownSystemVariable.New(v.Name)
    75  		}
    76  		return val, nil
    77  	default: // should never happen
    78  		return nil, fmt.Errorf("unknown scope `%v` on system variable `%s`", v.Scope, v.Name)
    79  	}
    80  }
    81  
    82  // Type implements the sql.Expression interface.
    83  func (v *SystemVar) Type() sql.Type {
    84  	if sysVar, _, ok := sql.SystemVariables.GetGlobal(v.Name); ok {
    85  		return sysVar.Type
    86  	}
    87  	return types.Null
    88  }
    89  
    90  // CollationCoercibility implements the interface sql.CollationCoercible.
    91  func (v *SystemVar) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
    92  	typ := v.Type()
    93  	if types.IsText(typ) {
    94  		collation, _ = typ.CollationCoercibility(ctx)
    95  		return collation, 3
    96  	}
    97  	return typ.CollationCoercibility(ctx)
    98  }
    99  
   100  // IsNullable implements the sql.Expression interface.
   101  func (v *SystemVar) IsNullable() bool { return false }
   102  
   103  // Resolved implements the sql.Expression interface.
   104  func (v *SystemVar) Resolved() bool { return true }
   105  
   106  // String implements the sql.Expression interface.
   107  func (v *SystemVar) String() string {
   108  	// If the scope wasn't explicitly provided, then don't include it in the string representation
   109  	if v.SpecifiedScope == "" {
   110  		return fmt.Sprintf("@@%s", v.Name)
   111  	} else {
   112  		return fmt.Sprintf("@@%s.%s", v.SpecifiedScope, v.Name)
   113  	}
   114  }
   115  
   116  // WithChildren implements the Expression interface.
   117  func (v *SystemVar) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   118  	if len(children) != 0 {
   119  		return nil, sql.ErrInvalidChildrenNumber.New(v, len(children), 0)
   120  	}
   121  	return v, nil
   122  }
   123  
   124  // UserVar is an expression that returns the value of a user variable. It's also used as the expression on the left hand
   125  // side of a SET statement for a user var.
   126  type UserVar struct {
   127  	Name     string
   128  	exprType sql.Type
   129  }
   130  
   131  var _ sql.Expression = (*UserVar)(nil)
   132  var _ sql.CollationCoercible = (*UserVar)(nil)
   133  
   134  // NewUserVar creates a UserVar with a name, but no type information, for use as the left-hand value
   135  // in a SetField assignment Expression. This method should not be used when the user variable is
   136  // being used as a value, since the correct type information will not be available.
   137  func NewUserVar(name string) *UserVar {
   138  	return &UserVar{Name: name, exprType: types.Null}
   139  }
   140  
   141  // NewUserVarWithType creates a UserVar with its type resolved, so that it can be used as a value
   142  // in other expressions.
   143  func NewUserVarWithType(name string, t sql.Type) *UserVar {
   144  	return &UserVar{Name: name, exprType: t}
   145  }
   146  
   147  // Children implements the sql.Expression interface.
   148  func (v *UserVar) Children() []sql.Expression { return nil }
   149  
   150  // Eval implements the sql.Expression interface.
   151  func (v *UserVar) Eval(ctx *sql.Context, _ sql.Row) (interface{}, error) {
   152  	_, val, err := ctx.GetUserVariable(ctx, v.Name)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	return val, nil
   158  }
   159  
   160  // Type implements the sql.Expression interface.
   161  func (v *UserVar) Type() sql.Type {
   162  	return v.exprType
   163  }
   164  
   165  // CollationCoercibility implements the interface sql.CollationCoercible.
   166  func (v *UserVar) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   167  	collation, _ = v.exprType.CollationCoercibility(ctx)
   168  	return collation, 2
   169  }
   170  
   171  // IsNullable implements the sql.Expression interface.
   172  func (v *UserVar) IsNullable() bool { return true }
   173  
   174  // Resolved implements the sql.Expression interface.
   175  func (v *UserVar) Resolved() bool { return true }
   176  
   177  // String implements the sql.Expression interface.
   178  func (v *UserVar) String() string { return "@" + v.Name }
   179  
   180  // WithChildren implements the Expression interface.
   181  func (v *UserVar) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   182  	if len(children) != 0 {
   183  		return nil, sql.ErrInvalidChildrenNumber.New(v, len(children), 0)
   184  	}
   185  	return v, nil
   186  }