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  }