vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/delete.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 engine
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"vitess.io/vitess/go/vt/vtgate/evalengine"
    24  
    25  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    26  
    27  	"vitess.io/vitess/go/sqltypes"
    28  	"vitess.io/vitess/go/vt/srvtopo"
    29  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    30  
    31  	querypb "vitess.io/vitess/go/vt/proto/query"
    32  )
    33  
    34  var _ Primitive = (*Delete)(nil)
    35  
    36  // Delete represents the instructions to perform a delete.
    37  type Delete struct {
    38  	*DML
    39  
    40  	// Delete does not take inputs
    41  	noInputs
    42  }
    43  
    44  // TryExecute performs a non-streaming exec.
    45  func (del *Delete) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, _ bool) (*sqltypes.Result, error) {
    46  	ctx, cancelFunc := addQueryTimeout(ctx, vcursor, del.QueryTimeout)
    47  	defer cancelFunc()
    48  
    49  	rss, _, err := del.findRoute(ctx, vcursor, bindVars)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  	err = allowOnlyPrimary(rss...)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	switch del.Opcode {
    59  	case Unsharded:
    60  		return del.execUnsharded(ctx, del, vcursor, bindVars, rss)
    61  	case Equal, IN, Scatter, ByDestination, SubShard, EqualUnique, MultiEqual:
    62  		return del.execMultiDestination(ctx, del, vcursor, bindVars, rss, del.deleteVindexEntries)
    63  	default:
    64  		// Unreachable.
    65  		return nil, fmt.Errorf("unsupported opcode: %v", del.Opcode)
    66  	}
    67  }
    68  
    69  // TryStreamExecute performs a streaming exec.
    70  func (del *Delete) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error {
    71  	res, err := del.TryExecute(ctx, vcursor, bindVars, wantfields)
    72  	if err != nil {
    73  		return err
    74  	}
    75  	return callback(res)
    76  }
    77  
    78  // GetFields fetches the field info.
    79  func (del *Delete) GetFields(context.Context, VCursor, map[string]*querypb.BindVariable) (*sqltypes.Result, error) {
    80  	return nil, fmt.Errorf("BUG: unreachable code for %q", del.Query)
    81  }
    82  
    83  // deleteVindexEntries performs an delete if table owns vindex.
    84  // Note: the commit order may be different from the DML order because it's possible
    85  // for DMLs to reuse existing transactions.
    86  func (del *Delete) deleteVindexEntries(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, rss []*srvtopo.ResolvedShard) error {
    87  	if del.OwnedVindexQuery == "" {
    88  		return nil
    89  	}
    90  	queries := make([]*querypb.BoundQuery, len(rss))
    91  	for i := range rss {
    92  		queries[i] = &querypb.BoundQuery{Sql: del.OwnedVindexQuery, BindVariables: bindVars}
    93  	}
    94  	subQueryResults, errors := vcursor.ExecuteMultiShard(ctx, del, rss, queries, false /* rollbackOnError */, false /* canAutocommit */)
    95  	for _, err := range errors {
    96  		if err != nil {
    97  			return err
    98  		}
    99  	}
   100  
   101  	if len(subQueryResults.Rows) == 0 {
   102  		return nil
   103  	}
   104  
   105  	for _, row := range subQueryResults.Rows {
   106  		ksid, err := resolveKeyspaceID(ctx, vcursor, del.KsidVindex, row[0:del.KsidLength])
   107  		if err != nil {
   108  			return err
   109  		}
   110  		colnum := del.KsidLength
   111  		vindexTable, err := del.GetSingleTable()
   112  		if err != nil {
   113  			return err
   114  		}
   115  		for _, colVindex := range vindexTable.Owned {
   116  			// Fetch the column values. colnum must keep incrementing.
   117  			fromIds := make([]sqltypes.Value, 0, len(colVindex.Columns))
   118  			for range colVindex.Columns {
   119  				fromIds = append(fromIds, row[colnum])
   120  				colnum++
   121  			}
   122  			if err := colVindex.Vindex.(vindexes.Lookup).Delete(ctx, vcursor, [][]sqltypes.Value{fromIds}, ksid); err != nil {
   123  				return err
   124  			}
   125  		}
   126  	}
   127  
   128  	return nil
   129  }
   130  
   131  func (del *Delete) description() PrimitiveDescription {
   132  	other := map[string]any{
   133  		"Query":                del.Query,
   134  		"Table":                del.GetTableName(),
   135  		"OwnedVindexQuery":     del.OwnedVindexQuery,
   136  		"MultiShardAutocommit": del.MultiShardAutocommit,
   137  		"QueryTimeout":         del.QueryTimeout,
   138  	}
   139  
   140  	addFieldsIfNotEmpty(del.DML, other)
   141  
   142  	return PrimitiveDescription{
   143  		OperatorType:     "Delete",
   144  		Keyspace:         del.Keyspace,
   145  		Variant:          del.Opcode.String(),
   146  		TargetTabletType: topodatapb.TabletType_PRIMARY,
   147  		Other:            other,
   148  	}
   149  }
   150  
   151  func addFieldsIfNotEmpty(dml *DML, other map[string]any) {
   152  	if dml.Vindex != nil {
   153  		other["Vindex"] = dml.Vindex.String()
   154  	}
   155  	if dml.KsidVindex != nil {
   156  		other["KsidVindex"] = dml.KsidVindex.String()
   157  		other["KsidLength"] = dml.KsidLength
   158  	}
   159  	if len(dml.Values) > 0 {
   160  		s := []string{}
   161  		for _, value := range dml.Values {
   162  			s = append(s, evalengine.FormatExpr(value))
   163  		}
   164  		other["Values"] = s
   165  	}
   166  }