vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/set.go (about) 1 /* 2 Copyright 2020 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package planbuilder 18 19 import ( 20 "fmt" 21 "strconv" 22 "strings" 23 24 "vitess.io/vitess/go/vt/sysvars" 25 "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" 26 27 "vitess.io/vitess/go/vt/vtgate/evalengine" 28 29 "vitess.io/vitess/go/vt/vtgate/vindexes" 30 31 "vitess.io/vitess/go/vt/vterrors" 32 33 "vitess.io/vitess/go/vt/key" 34 "vitess.io/vitess/go/vt/sqlparser" 35 "vitess.io/vitess/go/vt/vtgate/engine" 36 ) 37 38 type ( 39 planFunc = func(expr *sqlparser.SetExpr, vschema plancontext.VSchema, ec *expressionConverter) (engine.SetOp, error) 40 41 setting struct { 42 name string 43 boolean bool 44 defaultValue evalengine.Expr 45 46 // this allows identifiers (a.k.a. ColName) from the AST to be handled as if they are strings. 47 // SET transaction_mode = two_pc => SET transaction_mode = 'two_pc' 48 identifierAsString bool 49 supportSetVar bool 50 storageCase sysvars.StorageCase 51 } 52 ) 53 54 func buildSetPlan(stmt *sqlparser.Set, vschema plancontext.VSchema) (*planResult, error) { 55 var setOps []engine.SetOp 56 var err error 57 58 ec := new(expressionConverter) 59 60 for _, expr := range stmt.Exprs { 61 // AST struct has been prepared before getting here, so no scope here means that 62 // we have a UDV. If the original query didn't explicitly specify the scope, it 63 // would have been explictly set to sqlparser.SessionStr before reaching this 64 // phase of planning 65 switch expr.Var.Scope { 66 case sqlparser.GlobalScope: 67 setOp, err := planSysVarCheckIgnore(expr, vschema, true) 68 if err != nil { 69 return nil, err 70 } 71 setOps = append(setOps, setOp) 72 case sqlparser.VariableScope: 73 evalExpr, err := ec.convert(expr.Expr /*boolean*/, false /*identifierAsString*/, false) 74 if err != nil { 75 return nil, err 76 } 77 setOp := &engine.UserDefinedVariable{ 78 Name: expr.Var.Name.Lowered(), 79 Expr: evalExpr, 80 } 81 setOps = append(setOps, setOp) 82 case sqlparser.NextTxScope, sqlparser.SessionScope: 83 planFunc, err := sysvarPlanningFuncs.Get(expr) 84 if err != nil { 85 return nil, err 86 } 87 setOp, err := planFunc(expr, vschema, ec) 88 if err != nil { 89 return nil, err 90 } 91 setOps = append(setOps, setOp) 92 if expr.Var.Scope == sqlparser.NextTxScope { 93 // This is to keep the backward compatibility. 94 // 'transaction_isolation' was added as a reserved connection system variable, so it used to change the setting at session level already. 95 // logging warning now to 96 vschema.PlannerWarning("converted 'next transaction' scope to 'session' scope") 97 } 98 case sqlparser.VitessMetadataScope: 99 value, err := getValueFor(expr) 100 if err != nil { 101 return nil, err 102 } 103 val, ok := value.(string) 104 if !ok { 105 return nil, vterrors.VT03009(expr.Var.Name, value) 106 } 107 108 setOps = append(setOps, 109 &engine.VitessMetadata{Name: expr.Var.Name.Lowered(), Value: val}) 110 default: 111 return nil, vterrors.VT13001(fmt.Sprintf("undefined set type: %v", expr.Var.Scope.ToString())) 112 } 113 } 114 115 input, err := ec.source(vschema) 116 if err != nil { 117 return nil, err 118 } 119 120 return newPlanResult(&engine.Set{ 121 Ops: setOps, 122 Input: input, 123 }), nil 124 } 125 126 func buildSetOpReadOnly(setting) planFunc { 127 return func(expr *sqlparser.SetExpr, schema plancontext.VSchema, _ *expressionConverter) (engine.SetOp, error) { 128 return nil, vterrors.VT03010(expr.Var.Name) 129 } 130 } 131 132 func buildNotSupported(setting) planFunc { 133 return func(expr *sqlparser.SetExpr, schema plancontext.VSchema, _ *expressionConverter) (engine.SetOp, error) { 134 return nil, vterrors.VT12001(fmt.Sprintf("system setting: %s", expr.Var.Name)) 135 } 136 } 137 138 func buildSetOpIgnore(s setting) planFunc { 139 return func(expr *sqlparser.SetExpr, vschema plancontext.VSchema, _ *expressionConverter) (engine.SetOp, error) { 140 value, err := extractValue(expr, s.boolean) 141 if err != nil { 142 return nil, err 143 } 144 return &engine.SysVarIgnore{ 145 Name: expr.Var.Name.Lowered(), 146 Expr: value, 147 }, nil 148 } 149 } 150 151 func buildSetOpCheckAndIgnore(s setting) planFunc { 152 return func(expr *sqlparser.SetExpr, schema plancontext.VSchema, _ *expressionConverter) (engine.SetOp, error) { 153 return planSysVarCheckIgnore(expr, schema, s.boolean) 154 } 155 } 156 157 func planSysVarCheckIgnore(expr *sqlparser.SetExpr, schema plancontext.VSchema, boolean bool) (engine.SetOp, error) { 158 keyspace, dest, err := resolveDestination(schema) 159 if err != nil { 160 return nil, err 161 } 162 value, err := extractValue(expr, boolean) 163 if err != nil { 164 return nil, err 165 } 166 167 return &engine.SysVarCheckAndIgnore{ 168 Name: expr.Var.Name.Lowered(), 169 Keyspace: keyspace, 170 TargetDestination: dest, 171 Expr: value, 172 }, nil 173 } 174 175 func buildSetOpReservedConn(s setting) planFunc { 176 return func(expr *sqlparser.SetExpr, vschema plancontext.VSchema, _ *expressionConverter) (engine.SetOp, error) { 177 if !vschema.SysVarSetEnabled() { 178 return planSysVarCheckIgnore(expr, vschema, s.boolean) 179 } 180 ks, err := vschema.AnyKeyspace() 181 if err != nil { 182 return nil, err 183 } 184 value, err := extractValue(expr, s.boolean) 185 if err != nil { 186 return nil, err 187 } 188 189 value = provideAppliedCase(value, s.storageCase) 190 191 return &engine.SysVarReservedConn{ 192 Name: expr.Var.Name.Lowered(), 193 Keyspace: ks, 194 TargetDestination: vschema.Destination(), 195 Expr: value, 196 SupportSetVar: s.supportSetVar, 197 }, nil 198 } 199 } 200 201 func provideAppliedCase(value string, storageCase sysvars.StorageCase) string { 202 switch storageCase { 203 case sysvars.SCUpper: 204 return strings.ToUpper(value) 205 case sysvars.SCLower: 206 return strings.ToLower(value) 207 } 208 return value 209 } 210 211 const defaultNotSupportedErrFmt = "DEFAULT for @@%s" 212 213 func buildSetOpVitessAware(s setting) planFunc { 214 return func(astExpr *sqlparser.SetExpr, vschema plancontext.VSchema, ec *expressionConverter) (engine.SetOp, error) { 215 var err error 216 var runtimeExpr evalengine.Expr 217 218 _, isDefault := astExpr.Expr.(*sqlparser.Default) 219 if isDefault { 220 if s.defaultValue == nil { 221 return nil, vterrors.VT12001(fmt.Sprintf(defaultNotSupportedErrFmt, astExpr.Var.Name)) 222 } 223 runtimeExpr = s.defaultValue 224 } else { 225 runtimeExpr, err = ec.convert(astExpr.Expr, s.boolean, s.identifierAsString) 226 if err != nil { 227 return nil, err 228 } 229 } 230 231 return &engine.SysVarSetAware{ 232 Name: astExpr.Var.Name.Lowered(), 233 Expr: runtimeExpr, 234 }, nil 235 } 236 } 237 238 func resolveDestination(vschema plancontext.VSchema) (*vindexes.Keyspace, key.Destination, error) { 239 keyspace, err := vschema.AnyKeyspace() 240 if err != nil { 241 return nil, nil, err 242 } 243 244 dest := vschema.Destination() 245 if dest == nil { 246 dest = key.DestinationAnyShard{} 247 } 248 return keyspace, dest, nil 249 } 250 251 func extractValue(expr *sqlparser.SetExpr, boolean bool) (string, error) { 252 switch node := expr.Expr.(type) { 253 case *sqlparser.Literal: 254 if node.Type == sqlparser.StrVal && boolean { 255 switch strings.ToLower(node.Val) { 256 case "on": 257 return "1", nil 258 case "off": 259 return "0", nil 260 } 261 } 262 case *sqlparser.ColName: 263 // this is a little of a hack. it's used when the setting is not a normal expression, but rather 264 // an enumeration, such as utf8, utf8mb4, etc 265 switch node.Name.Lowered() { 266 case "on": 267 return "1", nil 268 case "off": 269 return "0", nil 270 } 271 return fmt.Sprintf("'%s'", sqlparser.String(expr.Expr)), nil 272 273 case *sqlparser.Default: 274 return "", vterrors.VT12001(defaultNotSupportedErrFmt, expr.Var.Name) 275 } 276 277 return sqlparser.String(expr.Expr), nil 278 } 279 280 func getValueFor(expr *sqlparser.SetExpr) (any, error) { 281 switch expr := expr.Expr.(type) { 282 case *sqlparser.Literal: 283 switch expr.Type { 284 case sqlparser.StrVal: 285 return strings.ToLower(expr.Val), nil 286 case sqlparser.IntVal: 287 num, err := strconv.ParseInt(expr.Val, 0, 64) 288 if err != nil { 289 return nil, err 290 } 291 return num, nil 292 case sqlparser.FloatVal, sqlparser.DecimalVal: 293 num, err := strconv.ParseFloat(expr.Val, 64) 294 if err != nil { 295 return nil, err 296 } 297 return num, nil 298 default: 299 return nil, vterrors.VT03011(sqlparser.String(expr)) 300 } 301 case sqlparser.BoolVal: 302 var val int64 303 if expr { 304 val = 1 305 } 306 return val, nil 307 case *sqlparser.NullVal: 308 return nil, nil 309 case *sqlparser.ColName: 310 return expr.Name.String(), nil 311 case *sqlparser.Default: 312 return "default", nil 313 default: 314 return nil, vterrors.VT03012(sqlparser.String(expr)) 315 } 316 }