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 }