vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletmanager/vreplication/controller_plan.go (about)

     1  /*
     2  Copyright 2019 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 vreplication
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"vitess.io/vitess/go/vt/sqlparser"
    23  )
    24  
    25  // controllerPlan is the plan for vreplication control statements.
    26  type controllerPlan struct {
    27  	query  string
    28  	opcode int
    29  
    30  	// numInserts is set for insertQuery.
    31  	numInserts int
    32  
    33  	// selector and applier are set for updateQuery and deleteQuery.
    34  	selector string
    35  	applier  *sqlparser.ParsedQuery
    36  
    37  	// delCopyState deletes related copy state.
    38  	delCopyState *sqlparser.ParsedQuery
    39  
    40  	// delPostCopyAction deletes related post copy actions.
    41  	delPostCopyAction *sqlparser.ParsedQuery
    42  }
    43  
    44  const (
    45  	insertQuery = iota
    46  	updateQuery
    47  	deleteQuery
    48  	selectQuery
    49  	reshardingJournalQuery
    50  )
    51  
    52  // buildControllerPlan parses the input query and returns an appropriate plan.
    53  func buildControllerPlan(query string) (*controllerPlan, error) {
    54  	stmt, err := sqlparser.Parse(query)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	var plan *controllerPlan
    59  	switch stmt := stmt.(type) {
    60  	case *sqlparser.Insert:
    61  		plan, err = buildInsertPlan(stmt)
    62  	case *sqlparser.Update:
    63  		plan, err = buildUpdatePlan(stmt)
    64  	case *sqlparser.Delete:
    65  		plan, err = buildDeletePlan(stmt)
    66  	case *sqlparser.Select:
    67  		plan, err = buildSelectPlan(stmt)
    68  	default:
    69  		return nil, fmt.Errorf("unsupported construct: %s", sqlparser.String(stmt))
    70  	}
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	plan.query = query
    75  	return plan, nil
    76  }
    77  
    78  func buildInsertPlan(ins *sqlparser.Insert) (*controllerPlan, error) {
    79  	switch sqlparser.String(ins.Table) {
    80  	case reshardingJournalTableName:
    81  		return &controllerPlan{
    82  			opcode: reshardingJournalQuery,
    83  		}, nil
    84  	case vreplicationTableName:
    85  		// no-op
    86  	default:
    87  		return nil, fmt.Errorf("invalid table name: %v", sqlparser.String(ins.Table))
    88  	}
    89  	if ins.Action != sqlparser.InsertAct {
    90  		return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(ins))
    91  	}
    92  	if ins.Ignore {
    93  		return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(ins))
    94  	}
    95  	if ins.Partitions != nil {
    96  		return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(ins))
    97  	}
    98  	if ins.OnDup != nil {
    99  		return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(ins))
   100  	}
   101  	rows, ok := ins.Rows.(sqlparser.Values)
   102  	if !ok {
   103  		return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(ins))
   104  	}
   105  	idPos := 0
   106  	if len(ins.Columns) != 0 {
   107  		idPos = -1
   108  		for i, col := range ins.Columns {
   109  			if col.EqualString("id") {
   110  				idPos = i
   111  				break
   112  			}
   113  		}
   114  	}
   115  	if idPos >= 0 {
   116  		for _, row := range rows {
   117  			if idPos >= len(row) {
   118  				return nil, fmt.Errorf("malformed statement: %v", sqlparser.String(ins))
   119  			}
   120  			if _, ok := row[idPos].(*sqlparser.NullVal); !ok {
   121  				return nil, fmt.Errorf("id should not have a value: %v", sqlparser.String(ins))
   122  			}
   123  		}
   124  	}
   125  	return &controllerPlan{
   126  		opcode:     insertQuery,
   127  		numInserts: len(rows),
   128  	}, nil
   129  }
   130  
   131  func buildUpdatePlan(upd *sqlparser.Update) (*controllerPlan, error) {
   132  	switch sqlparser.String(upd.TableExprs) {
   133  	case reshardingJournalTableName:
   134  		return &controllerPlan{
   135  			opcode: reshardingJournalQuery,
   136  		}, nil
   137  	case vreplicationTableName:
   138  		// no-op
   139  	default:
   140  		return nil, fmt.Errorf("invalid table name: %v", sqlparser.String(upd.TableExprs))
   141  	}
   142  	if upd.OrderBy != nil || upd.Limit != nil {
   143  		return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(upd))
   144  	}
   145  	for _, expr := range upd.Exprs {
   146  		if expr.Name.Name.EqualString("id") {
   147  			return nil, fmt.Errorf("id cannot be changed: %v", sqlparser.String(expr))
   148  		}
   149  	}
   150  
   151  	buf1 := sqlparser.NewTrackedBuffer(nil)
   152  	buf1.Myprintf("select id from %s%v", vreplicationTableName, upd.Where)
   153  	upd.Where = &sqlparser.Where{
   154  		Type: sqlparser.WhereClause,
   155  		Expr: &sqlparser.ComparisonExpr{
   156  			Left:     &sqlparser.ColName{Name: sqlparser.NewIdentifierCI("id")},
   157  			Operator: sqlparser.InOp,
   158  			Right:    sqlparser.ListArg("ids"),
   159  		},
   160  	}
   161  
   162  	buf2 := sqlparser.NewTrackedBuffer(nil)
   163  	buf2.Myprintf("%v", upd)
   164  
   165  	return &controllerPlan{
   166  		opcode:   updateQuery,
   167  		selector: buf1.String(),
   168  		applier:  buf2.ParsedQuery(),
   169  	}, nil
   170  }
   171  
   172  func buildDeletePlan(del *sqlparser.Delete) (*controllerPlan, error) {
   173  	switch sqlparser.String(del.TableExprs) {
   174  	case reshardingJournalTableName:
   175  		return &controllerPlan{
   176  			opcode: reshardingJournalQuery,
   177  		}, nil
   178  	case vreplicationTableName:
   179  		// no-op
   180  	default:
   181  		return nil, fmt.Errorf("invalid table name: %v", sqlparser.String(del.TableExprs))
   182  	}
   183  	if del.Targets != nil {
   184  		return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(del))
   185  	}
   186  	if del.Partitions != nil {
   187  		return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(del))
   188  	}
   189  	if del.OrderBy != nil || del.Limit != nil {
   190  		return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(del))
   191  	}
   192  
   193  	buf1 := sqlparser.NewTrackedBuffer(nil)
   194  	buf1.Myprintf("select id from %s%v", vreplicationTableName, del.Where)
   195  	del.Where = &sqlparser.Where{
   196  		Type: sqlparser.WhereClause,
   197  		Expr: &sqlparser.ComparisonExpr{
   198  			Left:     &sqlparser.ColName{Name: sqlparser.NewIdentifierCI("id")},
   199  			Operator: sqlparser.InOp,
   200  			Right:    sqlparser.ListArg("ids"),
   201  		},
   202  	}
   203  
   204  	buf2 := sqlparser.NewTrackedBuffer(nil)
   205  	buf2.Myprintf("%v", del)
   206  
   207  	copyStateWhere := &sqlparser.Where{
   208  		Type: sqlparser.WhereClause,
   209  		Expr: &sqlparser.ComparisonExpr{
   210  			Left:     &sqlparser.ColName{Name: sqlparser.NewIdentifierCI("vrepl_id")},
   211  			Operator: sqlparser.InOp,
   212  			Right:    sqlparser.ListArg("ids"),
   213  		},
   214  	}
   215  	buf3 := sqlparser.NewTrackedBuffer(nil)
   216  	buf3.Myprintf("delete from %s%v", copyStateTableName, copyStateWhere)
   217  
   218  	buf4 := sqlparser.NewTrackedBuffer(nil)
   219  	buf4.Myprintf("delete from %s%v", postCopyActionTableName, copyStateWhere)
   220  
   221  	return &controllerPlan{
   222  		opcode:            deleteQuery,
   223  		selector:          buf1.String(),
   224  		applier:           buf2.ParsedQuery(),
   225  		delCopyState:      buf3.ParsedQuery(),
   226  		delPostCopyAction: buf4.ParsedQuery(),
   227  	}, nil
   228  }
   229  
   230  func buildSelectPlan(sel *sqlparser.Select) (*controllerPlan, error) {
   231  	switch sqlparser.ToString(sel.From) {
   232  	case vreplicationTableName, reshardingJournalTableName, copyStateTableName, vreplicationLogTableName:
   233  		return &controllerPlan{
   234  			opcode: selectQuery,
   235  		}, nil
   236  	default:
   237  		return nil, fmt.Errorf("invalid table name: %v", sqlparser.ToString(sel.From))
   238  	}
   239  }