github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/syncer/expr_filter_group.go (about)

     1  // Copyright 2021 PingCAP, 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 syncer
    15  
    16  import (
    17  	"github.com/pingcap/tidb/pkg/expression"
    18  	"github.com/pingcap/tidb/pkg/parser/model"
    19  	"github.com/pingcap/tidb/pkg/sessionctx"
    20  	"github.com/pingcap/tidb/pkg/util/chunk"
    21  	"github.com/pingcap/tidb/pkg/util/dbterror/plannererrors"
    22  	"github.com/pingcap/tidb/pkg/util/dbutil"
    23  	"github.com/pingcap/tidb/pkg/util/filter"
    24  	"github.com/pingcap/tiflow/dm/config"
    25  	tcontext "github.com/pingcap/tiflow/dm/pkg/context"
    26  	"github.com/pingcap/tiflow/dm/pkg/log"
    27  	"github.com/pingcap/tiflow/dm/pkg/utils"
    28  	"go.uber.org/zap"
    29  )
    30  
    31  // ExprFilterGroup groups many related fields about expression filter.
    32  type ExprFilterGroup struct {
    33  	configs        map[string][]*config.ExpressionFilter // tableName -> raw config
    34  	insertExprs    map[string][]expression.Expression    // tableName -> expr
    35  	updateOldExprs map[string][]expression.Expression    // tableName -> expr
    36  	updateNewExprs map[string][]expression.Expression    // tableName -> expr
    37  	deleteExprs    map[string][]expression.Expression    // tableName -> expr
    38  
    39  	hasInsertFilter map[string]struct{} // set(tableName)
    40  	hasUpdateFilter map[string]struct{} // set(tableName)
    41  	hasDeleteFilter map[string]struct{} // set(tableName)
    42  
    43  	tidbCtx sessionctx.Context
    44  	logCtx  *tcontext.Context
    45  }
    46  
    47  // NewExprFilterGroup creates an ExprFilterGroup.
    48  func NewExprFilterGroup(logCtx *tcontext.Context, tidbCtx sessionctx.Context, exprConfig []*config.ExpressionFilter) *ExprFilterGroup {
    49  	ret := &ExprFilterGroup{
    50  		configs:         map[string][]*config.ExpressionFilter{},
    51  		insertExprs:     map[string][]expression.Expression{},
    52  		updateOldExprs:  map[string][]expression.Expression{},
    53  		updateNewExprs:  map[string][]expression.Expression{},
    54  		deleteExprs:     map[string][]expression.Expression{},
    55  		hasInsertFilter: map[string]struct{}{},
    56  		hasUpdateFilter: map[string]struct{}{},
    57  		hasDeleteFilter: map[string]struct{}{},
    58  		tidbCtx:         tidbCtx,
    59  		logCtx:          logCtx,
    60  	}
    61  	for _, c := range exprConfig {
    62  		tableName := dbutil.TableName(c.Schema, c.Table)
    63  		ret.configs[tableName] = append(ret.configs[tableName], c)
    64  
    65  		if c.InsertValueExpr != "" {
    66  			ret.hasInsertFilter[tableName] = struct{}{}
    67  		}
    68  		if c.UpdateOldValueExpr != "" || c.UpdateNewValueExpr != "" {
    69  			ret.hasUpdateFilter[tableName] = struct{}{}
    70  		}
    71  		if c.DeleteValueExpr != "" {
    72  			ret.hasDeleteFilter[tableName] = struct{}{}
    73  		}
    74  	}
    75  	return ret
    76  }
    77  
    78  // GetInsertExprs returns the expression filters for given table to filter INSERT events.
    79  // This function will lazy calculate expressions if not initialized.
    80  func (g *ExprFilterGroup) GetInsertExprs(table *filter.Table, ti *model.TableInfo) ([]expression.Expression, error) {
    81  	tableID := utils.GenTableID(table)
    82  
    83  	if ret, ok := g.insertExprs[tableID]; ok {
    84  		return ret, nil
    85  	}
    86  	if _, ok := g.hasInsertFilter[tableID]; !ok {
    87  		return nil, nil
    88  	}
    89  
    90  	for _, c := range g.configs[tableID] {
    91  		if c.InsertValueExpr != "" {
    92  			expr, err2 := getSimpleExprOfTable(g.tidbCtx, c.InsertValueExpr, ti, g.logCtx.L())
    93  			if err2 != nil {
    94  				// TODO: terror
    95  				return nil, err2
    96  			}
    97  			g.insertExprs[tableID] = append(g.insertExprs[tableID], expr)
    98  		}
    99  	}
   100  	return g.insertExprs[tableID], nil
   101  }
   102  
   103  // GetUpdateExprs returns two lists of expression filters for given table, to filter UPDATE events by old values and new
   104  // values respectively. The two lists should have same length, and the corresponding expressions is AND logic.
   105  // This function will lazy calculate expressions if not initialized.
   106  func (g *ExprFilterGroup) GetUpdateExprs(table *filter.Table, ti *model.TableInfo) ([]expression.Expression, []expression.Expression, error) {
   107  	tableID := utils.GenTableID(table)
   108  
   109  	retOld, ok1 := g.updateOldExprs[tableID]
   110  	retNew, ok2 := g.updateNewExprs[tableID]
   111  	if ok1 || ok2 {
   112  		return retOld, retNew, nil
   113  	}
   114  
   115  	if _, ok := g.hasUpdateFilter[tableID]; ok {
   116  		for _, c := range g.configs[tableID] {
   117  			if c.UpdateOldValueExpr == "" && c.UpdateNewValueExpr == "" {
   118  				continue
   119  			}
   120  			if c.UpdateOldValueExpr != "" {
   121  				expr, err := getSimpleExprOfTable(g.tidbCtx, c.UpdateOldValueExpr, ti, g.logCtx.L())
   122  				if err != nil {
   123  					// TODO: terror
   124  					return nil, nil, err
   125  				}
   126  				g.updateOldExprs[tableID] = append(g.updateOldExprs[tableID], expr)
   127  			} else {
   128  				g.updateOldExprs[tableID] = append(g.updateOldExprs[tableID], expression.NewOne())
   129  			}
   130  
   131  			if c.UpdateNewValueExpr != "" {
   132  				expr, err := getSimpleExprOfTable(g.tidbCtx, c.UpdateNewValueExpr, ti, g.logCtx.L())
   133  				if err != nil {
   134  					// TODO: terror
   135  					return nil, nil, err
   136  				}
   137  				g.updateNewExprs[tableID] = append(g.updateNewExprs[tableID], expr)
   138  			} else {
   139  				g.updateNewExprs[tableID] = append(g.updateNewExprs[tableID], expression.NewOne())
   140  			}
   141  		}
   142  	}
   143  
   144  	return g.updateOldExprs[tableID], g.updateNewExprs[tableID], nil
   145  }
   146  
   147  // GetDeleteExprs returns the expression filters for given table to filter DELETE events.
   148  // This function will lazy calculate expressions if not initialized.
   149  func (g *ExprFilterGroup) GetDeleteExprs(table *filter.Table, ti *model.TableInfo) ([]expression.Expression, error) {
   150  	tableID := utils.GenTableID(table)
   151  
   152  	if ret, ok := g.deleteExprs[tableID]; ok {
   153  		return ret, nil
   154  	}
   155  	if _, ok := g.hasDeleteFilter[tableID]; !ok {
   156  		return nil, nil
   157  	}
   158  
   159  	for _, c := range g.configs[tableID] {
   160  		if c.DeleteValueExpr != "" {
   161  			expr, err2 := getSimpleExprOfTable(g.tidbCtx, c.DeleteValueExpr, ti, g.logCtx.L())
   162  			if err2 != nil {
   163  				// TODO: terror
   164  				return nil, err2
   165  			}
   166  			g.deleteExprs[tableID] = append(g.deleteExprs[tableID], expr)
   167  		}
   168  	}
   169  	return g.deleteExprs[tableID], nil
   170  }
   171  
   172  // ResetExprs deletes the expressions generated before. This should be called after table structure changed.
   173  func (g *ExprFilterGroup) ResetExprs(table *filter.Table) {
   174  	tableID := utils.GenTableID(table)
   175  	delete(g.insertExprs, tableID)
   176  	delete(g.updateOldExprs, tableID)
   177  	delete(g.updateNewExprs, tableID)
   178  	delete(g.deleteExprs, tableID)
   179  }
   180  
   181  // SkipDMLByExpression returns true when given row matches the expr, which means this row should be skipped.
   182  func SkipDMLByExpression(ctx sessionctx.Context, row []interface{}, expr expression.Expression, upstreamCols []*model.ColumnInfo) (bool, error) {
   183  	// TODO: add MetricsProxies
   184  	log.L().Debug("will evaluate the expression", zap.Stringer("expression", expr), zap.Any("raw row", row))
   185  	data, err := utils.AdjustBinaryProtocolForDatum(ctx, row, upstreamCols)
   186  	if err != nil {
   187  		return false, err
   188  	}
   189  	r := chunk.MutRowFromDatums(data).ToRow()
   190  
   191  	d, err := expr.Eval(ctx.GetExprCtx().GetEvalCtx(), r)
   192  	if err != nil {
   193  		return false, err
   194  	}
   195  	return d.GetInt64() == 1, nil
   196  }
   197  
   198  // getSimpleExprOfTable returns an expression of given `expr` string, using the table structure that is tracked before.
   199  func getSimpleExprOfTable(ctx sessionctx.Context, expr string, ti *model.TableInfo, logger log.Logger) (expression.Expression, error) {
   200  	// TODO: use upstream timezone?
   201  	e, err := expression.ParseSimpleExprWithTableInfo(ctx.GetExprCtx(), expr, ti)
   202  	if err != nil {
   203  		// if expression contains an unknown column, we return an expression that skips nothing
   204  		if plannererrors.ErrUnknownColumn.Equal(err) {
   205  			logger.Warn("meet unknown column when generating expression, return a FALSE expression instead",
   206  				zap.String("expression", expr),
   207  				zap.Error(err))
   208  			e = expression.NewZero()
   209  		} else {
   210  			return nil, err
   211  		}
   212  	}
   213  
   214  	return e, nil
   215  }