github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/dbs/generated_column.go (about) 1 // Copyright 2020 WHTCORPS INC, 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 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package dbs 15 16 import ( 17 "github.com/whtcorpsinc/errors" 18 "github.com/whtcorpsinc/BerolinaSQL/ast" 19 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 20 "github.com/whtcorpsinc/milevadb/memex" 21 "github.com/whtcorpsinc/milevadb/schemareplicant" 22 "github.com/whtcorpsinc/milevadb/causet" 23 ) 24 25 // defCausumnGenerationInDBS is a struct for validating generated defCausumns in DBS. 26 type defCausumnGenerationInDBS struct { 27 position int 28 generated bool 29 dependences map[string]struct{} 30 } 31 32 // verifyDeferredCausetGeneration is for CREATE TABLE, because we need verify all defCausumns in the causet. 33 func verifyDeferredCausetGeneration(defCausName2Generation map[string]defCausumnGenerationInDBS, defCausName string) error { 34 attribute := defCausName2Generation[defCausName] 35 if attribute.generated { 36 for depDefCaus := range attribute.dependences { 37 if attr, ok := defCausName2Generation[depDefCaus]; ok { 38 if attr.generated && attribute.position <= attr.position { 39 // A generated defCausumn definition can refer to other 40 // generated defCausumns occurring earlier in the causet. 41 err := errGeneratedDeferredCausetNonPrior.GenWithStackByArgs() 42 return errors.Trace(err) 43 } 44 } else { 45 err := ErrBadField.GenWithStackByArgs(depDefCaus, "generated defCausumn function") 46 return errors.Trace(err) 47 } 48 } 49 } 50 return nil 51 } 52 53 // verifyDeferredCausetGenerationSingle is for ADD GENERATED COLUMN, we just need verify one defCausumn itself. 54 func verifyDeferredCausetGenerationSingle(dependDefCausNames map[string]struct{}, defcaus []*causet.DeferredCauset, position *ast.DeferredCausetPosition) error { 55 // Since the added defCausumn does not exist yet, we should derive it's offset from DeferredCausetPosition. 56 pos, err := findPositionRelativeDeferredCauset(defcaus, position) 57 if err != nil { 58 return errors.Trace(err) 59 } 60 // should check unknown defCausumn first, then the prior ones. 61 for _, defCaus := range defcaus { 62 if _, ok := dependDefCausNames[defCaus.Name.L]; ok { 63 if defCaus.IsGenerated() && defCaus.Offset >= pos { 64 // Generated defCausumn can refer only to generated defCausumns defined prior to it. 65 return errGeneratedDeferredCausetNonPrior.GenWithStackByArgs() 66 } 67 } 68 } 69 return nil 70 } 71 72 // checkDependedDefCausExist ensure all depended defCausumns exist and not hidden. 73 // NOTE: this will MODIFY parameter `dependDefCauss`. 74 func checkDependedDefCausExist(dependDefCauss map[string]struct{}, defcaus []*causet.DeferredCauset) error { 75 for _, defCaus := range defcaus { 76 if !defCaus.Hidden { 77 delete(dependDefCauss, defCaus.Name.L) 78 } 79 } 80 if len(dependDefCauss) != 0 { 81 for arbitraryDefCaus := range dependDefCauss { 82 return ErrBadField.GenWithStackByArgs(arbitraryDefCaus, "generated defCausumn function") 83 } 84 } 85 return nil 86 } 87 88 // findPositionRelativeDeferredCauset returns a pos relative to added generated defCausumn position. 89 func findPositionRelativeDeferredCauset(defcaus []*causet.DeferredCauset, pos *ast.DeferredCausetPosition) (int, error) { 90 position := len(defcaus) 91 // Get the defCausumn position, default is defcaus's length means appending. 92 // For "alter causet ... add defCausumn(...)", the position will be nil. 93 // For "alter causet ... add defCausumn ... ", the position will be default one. 94 if pos == nil { 95 return position, nil 96 } 97 if pos.Tp == ast.DeferredCausetPositionFirst { 98 position = 0 99 } else if pos.Tp == ast.DeferredCausetPositionAfter { 100 var defCaus *causet.DeferredCauset 101 for _, c := range defcaus { 102 if c.Name.L == pos.RelativeDeferredCauset.Name.L { 103 defCaus = c 104 break 105 } 106 } 107 if defCaus == nil { 108 return -1, ErrBadField.GenWithStackByArgs(pos.RelativeDeferredCauset, "generated defCausumn function") 109 } 110 // Inserted position is after the mentioned defCausumn. 111 position = defCaus.Offset + 1 112 } 113 return position, nil 114 } 115 116 // findDependedDeferredCausetNames returns a set of string, which indicates 117 // the names of the defCausumns that are depended by defCausDef. 118 func findDependedDeferredCausetNames(defCausDef *ast.DeferredCausetDef) (generated bool, defcausMap map[string]struct{}) { 119 defcausMap = make(map[string]struct{}) 120 for _, option := range defCausDef.Options { 121 if option.Tp == ast.DeferredCausetOptionGenerated { 122 generated = true 123 defCausNames := findDeferredCausetNamesInExpr(option.Expr) 124 for _, depDefCaus := range defCausNames { 125 defcausMap[depDefCaus.Name.L] = struct{}{} 126 } 127 break 128 } 129 } 130 return 131 } 132 133 // findDeferredCausetNamesInExpr returns a slice of ast.DeferredCausetName which is referred in expr. 134 func findDeferredCausetNamesInExpr(expr ast.ExprNode) []*ast.DeferredCausetName { 135 var c generatedDeferredCausetChecker 136 expr.Accept(&c) 137 return c.defcaus 138 } 139 140 type generatedDeferredCausetChecker struct { 141 defcaus []*ast.DeferredCausetName 142 } 143 144 func (c *generatedDeferredCausetChecker) Enter(inNode ast.Node) (outNode ast.Node, skipChildren bool) { 145 return inNode, false 146 } 147 148 func (c *generatedDeferredCausetChecker) Leave(inNode ast.Node) (node ast.Node, ok bool) { 149 switch x := inNode.(type) { 150 case *ast.DeferredCausetName: 151 c.defcaus = append(c.defcaus, x) 152 } 153 return inNode, true 154 } 155 156 // checkModifyGeneratedDeferredCauset checks the modification between 157 // old and new is valid or not by such rules: 158 // 1. the modification can't change stored status; 159 // 2. if the new is generated, check its refer rules. 160 // 3. check if the modified expr contains non-deterministic functions 161 // 4. check whether new defCausumn refers to any auto-increment defCausumns. 162 // 5. check if the new defCausumn is indexed or stored 163 func checkModifyGeneratedDeferredCauset(tbl causet.Block, oldDefCaus, newDefCaus *causet.DeferredCauset, newDefCausDef *ast.DeferredCausetDef) error { 164 // rule 1. 165 oldDefCausIsStored := !oldDefCaus.IsGenerated() || oldDefCaus.GeneratedStored 166 newDefCausIsStored := !newDefCaus.IsGenerated() || newDefCaus.GeneratedStored 167 if oldDefCausIsStored != newDefCausIsStored { 168 return ErrUnsupportedOnGeneratedDeferredCauset.GenWithStackByArgs("Changing the STORED status") 169 } 170 171 // rule 2. 172 originDefCauss := tbl.DefCauss() 173 var defCausName2Generation = make(map[string]defCausumnGenerationInDBS, len(originDefCauss)) 174 for i, defCausumn := range originDefCauss { 175 // We can compare the pointers simply. 176 if defCausumn == oldDefCaus { 177 defCausName2Generation[newDefCaus.Name.L] = defCausumnGenerationInDBS{ 178 position: i, 179 generated: newDefCaus.IsGenerated(), 180 dependences: newDefCaus.Dependences, 181 } 182 } else if !defCausumn.IsGenerated() { 183 defCausName2Generation[defCausumn.Name.L] = defCausumnGenerationInDBS{ 184 position: i, 185 generated: false, 186 } 187 } else { 188 defCausName2Generation[defCausumn.Name.L] = defCausumnGenerationInDBS{ 189 position: i, 190 generated: true, 191 dependences: defCausumn.Dependences, 192 } 193 } 194 } 195 // We always need test all defCausumns, even if it's not changed 196 // because other can depend on it so its name can't be changed. 197 for _, defCausumn := range originDefCauss { 198 var defCausName string 199 if defCausumn == oldDefCaus { 200 defCausName = newDefCaus.Name.L 201 } else { 202 defCausName = defCausumn.Name.L 203 } 204 if err := verifyDeferredCausetGeneration(defCausName2Generation, defCausName); err != nil { 205 return errors.Trace(err) 206 } 207 } 208 209 if newDefCaus.IsGenerated() { 210 // rule 3. 211 if err := checkIllegalFn4GeneratedDeferredCauset(newDefCaus.Name.L, newDefCaus.GeneratedExpr); err != nil { 212 return errors.Trace(err) 213 } 214 215 // rule 4. 216 if err := checkGeneratedWithAutoInc(tbl.Meta(), newDefCausDef); err != nil { 217 return errors.Trace(err) 218 } 219 220 // rule 5. 221 if err := checHoTTexOrStored(tbl, oldDefCaus, newDefCaus); err != nil { 222 return errors.Trace(err) 223 } 224 } 225 return nil 226 } 227 228 type illegalFunctionChecker struct { 229 hasIllegalFunc bool 230 hasAggFunc bool 231 } 232 233 func (c *illegalFunctionChecker) Enter(inNode ast.Node) (outNode ast.Node, skipChildren bool) { 234 switch node := inNode.(type) { 235 case *ast.FuncCallExpr: 236 // Blocked functions & non-builtin functions is not allowed 237 _, IsFunctionBlocked := memex.IllegalFunctions4GeneratedDeferredCausets[node.FnName.L] 238 if IsFunctionBlocked || !memex.IsFunctionSupported(node.FnName.L) { 239 c.hasIllegalFunc = true 240 return inNode, true 241 } 242 case *ast.SubqueryExpr, *ast.ValuesExpr, *ast.VariableExpr: 243 // Subquery & `values(x)` & variable is not allowed 244 c.hasIllegalFunc = true 245 return inNode, true 246 case *ast.AggregateFuncExpr: 247 // Aggregate function is not allowed 248 c.hasAggFunc = true 249 return inNode, true 250 } 251 return inNode, false 252 } 253 254 func (c *illegalFunctionChecker) Leave(inNode ast.Node) (node ast.Node, ok bool) { 255 return inNode, true 256 } 257 258 func checkIllegalFn4GeneratedDeferredCauset(defCausName string, expr ast.ExprNode) error { 259 if expr == nil { 260 return nil 261 } 262 var c illegalFunctionChecker 263 expr.Accept(&c) 264 if c.hasIllegalFunc { 265 return ErrGeneratedDeferredCausetFunctionIsNotAllowed.GenWithStackByArgs(defCausName) 266 } 267 if c.hasAggFunc { 268 return ErrInvalidGroupFuncUse 269 } 270 return nil 271 } 272 273 // Check whether newDeferredCausetDef refers to any auto-increment defCausumns. 274 func checkGeneratedWithAutoInc(blockInfo *perceptron.BlockInfo, newDeferredCausetDef *ast.DeferredCausetDef) error { 275 _, dependDefCausNames := findDependedDeferredCausetNames(newDeferredCausetDef) 276 if err := checkAutoIncrementRef(newDeferredCausetDef.Name.Name.L, dependDefCausNames, blockInfo); err != nil { 277 return errors.Trace(err) 278 } 279 return nil 280 } 281 282 func checHoTTexOrStored(tbl causet.Block, oldDefCaus, newDefCaus *causet.DeferredCauset) error { 283 if oldDefCaus.GeneratedExprString == newDefCaus.GeneratedExprString { 284 return nil 285 } 286 287 if newDefCaus.GeneratedStored { 288 return ErrUnsupportedOnGeneratedDeferredCauset.GenWithStackByArgs("modifying a stored defCausumn") 289 } 290 291 for _, idx := range tbl.Indices() { 292 for _, defCaus := range idx.Meta().DeferredCausets { 293 if defCaus.Name.L == newDefCaus.Name.L { 294 return ErrUnsupportedOnGeneratedDeferredCauset.GenWithStackByArgs("modifying an indexed defCausumn") 295 } 296 } 297 } 298 return nil 299 } 300 301 // checkAutoIncrementRef checks if an generated defCausumn depends on an auto-increment defCausumn and raises an error if so. 302 // See https://dev.allegrosql.com/doc/refman/5.7/en/create-causet-generated-defCausumns.html for details. 303 func checkAutoIncrementRef(name string, dependencies map[string]struct{}, tbInfo *perceptron.BlockInfo) error { 304 exists, autoIncrementDeferredCauset := schemareplicant.HasAutoIncrementDeferredCauset(tbInfo) 305 if exists { 306 if _, found := dependencies[autoIncrementDeferredCauset]; found { 307 return ErrGeneratedDeferredCausetRefAutoInc.GenWithStackByArgs(name) 308 } 309 } 310 return nil 311 }