github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/petri/acyclic/causet/embedded/cacheable_checker.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 embedded
    15  
    16  import (
    17  	"github.com/whtcorpsinc/BerolinaSQL/ast"
    18  	"github.com/whtcorpsinc/milevadb/memex"
    19  	"github.com/whtcorpsinc/milevadb/schemareplicant"
    20  	"github.com/whtcorpsinc/milevadb/soliton/logutil"
    21  	"github.com/whtcorpsinc/milevadb/types/BerolinaSQL_driver"
    22  	"go.uber.org/zap"
    23  )
    24  
    25  // Cacheable checks whether the input ast is cacheable.
    26  // Handle "ignore_plan_cache()" hint
    27  // If there are multiple hints, only one will take effect
    28  func Cacheable(node ast.Node, is schemareplicant.SchemaReplicant) bool {
    29  	_, isSelect := node.(*ast.SelectStmt)
    30  	_, isUFIDelate := node.(*ast.UFIDelateStmt)
    31  	_, isInsert := node.(*ast.InsertStmt)
    32  	_, isDelete := node.(*ast.DeleteStmt)
    33  	_, isSetOpr := node.(*ast.SetOprStmt)
    34  	if !(isSelect || isUFIDelate || isInsert || isDelete || isSetOpr) {
    35  		return false
    36  	}
    37  	checker := cacheableChecker{
    38  		cacheable:       true,
    39  		schemaReplicant: is,
    40  	}
    41  	node.Accept(&checker)
    42  	return checker.cacheable
    43  }
    44  
    45  // cacheableChecker checks whether a query's plan can be cached, querys that:
    46  //	 1. have ExistsSubqueryExpr, or
    47  //	 2. have VariableExpr
    48  // will not be cached currently.
    49  // NOTE: we can add more rules in the future.
    50  type cacheableChecker struct {
    51  	cacheable       bool
    52  	schemaReplicant schemareplicant.SchemaReplicant
    53  }
    54  
    55  // Enter implements Visitor interface.
    56  func (checker *cacheableChecker) Enter(in ast.Node) (out ast.Node, skipChildren bool) {
    57  	switch node := in.(type) {
    58  	case *ast.SelectStmt:
    59  		for _, hints := range node.BlockHints {
    60  			if hints.HintName.L == HintIgnoreCausetCache {
    61  				checker.cacheable = false
    62  				return in, true
    63  			}
    64  		}
    65  	case *ast.DeleteStmt:
    66  		for _, hints := range node.BlockHints {
    67  			if hints.HintName.L == HintIgnoreCausetCache {
    68  				checker.cacheable = false
    69  				return in, true
    70  			}
    71  		}
    72  	case *ast.UFIDelateStmt:
    73  		for _, hints := range node.BlockHints {
    74  			if hints.HintName.L == HintIgnoreCausetCache {
    75  				checker.cacheable = false
    76  				return in, true
    77  			}
    78  		}
    79  	case *ast.VariableExpr, *ast.ExistsSubqueryExpr, *ast.SubqueryExpr:
    80  		checker.cacheable = false
    81  		return in, true
    82  	case *ast.FuncCallExpr:
    83  		if _, found := memex.UnCacheableFunctions[node.FnName.L]; found {
    84  			checker.cacheable = false
    85  			return in, true
    86  		}
    87  	case *ast.OrderByClause:
    88  		for _, item := range node.Items {
    89  			if _, isParamMarker := item.Expr.(*driver.ParamMarkerExpr); isParamMarker {
    90  				checker.cacheable = false
    91  				return in, true
    92  			}
    93  		}
    94  	case *ast.GroupByClause:
    95  		for _, item := range node.Items {
    96  			if _, isParamMarker := item.Expr.(*driver.ParamMarkerExpr); isParamMarker {
    97  				checker.cacheable = false
    98  				return in, true
    99  			}
   100  		}
   101  	case *ast.Limit:
   102  		if node.Count != nil {
   103  			if _, isParamMarker := node.Count.(*driver.ParamMarkerExpr); isParamMarker {
   104  				checker.cacheable = false
   105  				return in, true
   106  			}
   107  		}
   108  		if node.Offset != nil {
   109  			if _, isParamMarker := node.Offset.(*driver.ParamMarkerExpr); isParamMarker {
   110  				checker.cacheable = false
   111  				return in, true
   112  			}
   113  		}
   114  	case *ast.FrameBound:
   115  		if _, ok := node.Expr.(*driver.ParamMarkerExpr); ok {
   116  			checker.cacheable = false
   117  			return in, true
   118  		}
   119  	case *ast.BlockName:
   120  		if checker.schemaReplicant != nil {
   121  			if checker.isPartitionBlock(node) {
   122  				checker.cacheable = false
   123  				return in, true
   124  			}
   125  		}
   126  	}
   127  	return in, false
   128  }
   129  
   130  func (checker *cacheableChecker) isPartitionBlock(tn *ast.BlockName) bool {
   131  	tb, err := checker.schemaReplicant.BlockByName(tn.Schema, tn.Name)
   132  	if err != nil {
   133  		logutil.BgLogger().Error("Error occur in checking cacheable", zap.Error(err))
   134  		return false
   135  	}
   136  	if tb.Meta().Partition != nil {
   137  		return true
   138  	}
   139  	return false
   140  }
   141  
   142  // Leave implements Visitor interface.
   143  func (checker *cacheableChecker) Leave(in ast.Node) (out ast.Node, ok bool) {
   144  	return in, checker.cacheable
   145  }