vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/vstream.go (about)

     1  /*
     2  Copyright 2021 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 engine
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  
    24  	"vitess.io/vitess/go/vt/vterrors"
    25  
    26  	"vitess.io/vitess/go/sqltypes"
    27  	"vitess.io/vitess/go/vt/key"
    28  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    29  
    30  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    31  	querypb "vitess.io/vitess/go/vt/proto/query"
    32  )
    33  
    34  var _ Primitive = (*VStream)(nil)
    35  
    36  // VStream is an operator for streaming specific keyspace, destination
    37  type VStream struct {
    38  	Keyspace          *vindexes.Keyspace
    39  	TargetDestination key.Destination
    40  	TableName         string
    41  	Position          string
    42  	Limit             int
    43  
    44  	noTxNeeded
    45  	noInputs
    46  }
    47  
    48  // RouteType implements the Primitive interface
    49  func (v *VStream) RouteType() string {
    50  	return "VStream"
    51  }
    52  
    53  // GetKeyspaceName implements the Primitive interface
    54  func (v *VStream) GetKeyspaceName() string {
    55  	return v.Keyspace.Name
    56  }
    57  
    58  // GetTableName implements the Primitive interface
    59  func (v *VStream) GetTableName() string {
    60  	return v.TableName
    61  }
    62  
    63  // TryExecute implements the Primitive interface
    64  func (v *VStream) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) {
    65  	return nil, vterrors.VT13001("TryExecute is not supported for VStream")
    66  }
    67  
    68  // TryStreamExecute implements the Primitive interface
    69  func (v *VStream) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error {
    70  	rss, _, err := vcursor.ResolveDestinations(ctx, v.Keyspace.Name, nil, []key.Destination{v.TargetDestination})
    71  	if err != nil {
    72  		return err
    73  	}
    74  	filter := &binlogdatapb.Filter{
    75  		Rules: []*binlogdatapb.Rule{{
    76  			Match:  v.TableName,
    77  			Filter: fmt.Sprintf("select * from %s", v.TableName),
    78  		}},
    79  	}
    80  	var lastFields []*querypb.Field
    81  	numRows := 0
    82  	totalRows := 0
    83  	send := func(evs []*binlogdatapb.VEvent) error {
    84  		result := &sqltypes.Result{
    85  			Fields: nil,
    86  			Rows:   [][]sqltypes.Value{},
    87  		}
    88  		for _, ev := range evs {
    89  			if totalRows+numRows >= v.Limit {
    90  				break
    91  			}
    92  			switch ev.Type {
    93  			case binlogdatapb.VEventType_FIELD:
    94  				lastFields = []*querypb.Field{{
    95  					Name: "op",
    96  					Type: querypb.Type_VARCHAR,
    97  				}}
    98  				lastFields = append(lastFields, ev.FieldEvent.Fields...)
    99  			case binlogdatapb.VEventType_ROW:
   100  				result.Fields = lastFields
   101  				eventFields := lastFields[1:]
   102  				for _, change := range ev.RowEvent.RowChanges {
   103  					newVals := addRowChangeIndicatorColumn(change, eventFields)
   104  					result.Rows = append(result.Rows, newVals)
   105  					numRows++
   106  					if totalRows+numRows >= v.Limit {
   107  						break
   108  					}
   109  				}
   110  			default:
   111  			}
   112  		}
   113  		if numRows > 0 {
   114  			err := callback(result)
   115  			if err != nil {
   116  				return err
   117  			}
   118  			totalRows += numRows
   119  			numRows = 0
   120  			if v.Limit > 0 && totalRows >= v.Limit {
   121  				return io.EOF
   122  			}
   123  		}
   124  		return nil
   125  	}
   126  
   127  	return vcursor.VStream(ctx, rss, filter, v.Position, send)
   128  }
   129  
   130  // for demo purposes we prefix the row with a column with a single char +/*/- to indicate why the row changed
   131  // + => insert, - => delete, * => update. This will be removed/improved as we iterate over this functionality
   132  const (
   133  	RowChangeInsert string = "+"
   134  	RowChangeDelete string = "-"
   135  	RowChangeUpdate string = "*"
   136  )
   137  
   138  func addRowChangeIndicatorColumn(change *binlogdatapb.RowChange, eventFields []*querypb.Field) []sqltypes.Value {
   139  	op := ""
   140  	var vals []sqltypes.Value
   141  	if change.After != nil && change.Before == nil {
   142  		op = RowChangeInsert
   143  		vals = sqltypes.MakeRowTrusted(eventFields, change.After)
   144  	} else if change.After != nil && change.Before != nil {
   145  		op = RowChangeUpdate
   146  		vals = sqltypes.MakeRowTrusted(eventFields, change.After)
   147  	} else {
   148  		op = RowChangeDelete
   149  		vals = sqltypes.MakeRowTrusted(eventFields, change.Before)
   150  	}
   151  	newVals := append([]sqltypes.Value{sqltypes.NewVarChar(op)}, vals...)
   152  	return newVals
   153  }
   154  
   155  // GetFields implements the Primitive interface
   156  func (v *VStream) GetFields(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) {
   157  	return nil, vterrors.VT13001("GetFields is not supported for VStream")
   158  }
   159  
   160  func (v *VStream) description() PrimitiveDescription {
   161  	other := map[string]any{
   162  		"Table":    v.TableName,
   163  		"Limit":    v.Limit,
   164  		"Position": v.Position,
   165  	}
   166  	return PrimitiveDescription{
   167  		OperatorType:      "VStream",
   168  		Keyspace:          v.Keyspace,
   169  		TargetDestination: v.TargetDestination,
   170  		Other:             other,
   171  	}
   172  }