github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/row/errors.go (about)

     1  // Copyright 2018 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package row
    12  
    13  import (
    14  	"context"
    15  	"strings"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/keys"
    18  	"github.com/cockroachdb/cockroach/pkg/kv"
    19  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    23  	"github.com/cockroachdb/cockroach/pkg/util"
    24  	"github.com/cockroachdb/errors"
    25  )
    26  
    27  // singleKVFetcher is a kvBatchFetcher that returns a single kv.
    28  type singleKVFetcher struct {
    29  	kvs  [1]roachpb.KeyValue
    30  	done bool
    31  }
    32  
    33  // nextBatch implements the kvBatchFetcher interface.
    34  func (f *singleKVFetcher) nextBatch(
    35  	_ context.Context,
    36  ) (ok bool, kvs []roachpb.KeyValue, batchResponse []byte, span roachpb.Span, err error) {
    37  	if f.done {
    38  		return false, nil, nil, roachpb.Span{}, nil
    39  	}
    40  	f.done = true
    41  	return true, f.kvs[:], nil, roachpb.Span{}, nil
    42  }
    43  
    44  // GetRangesInfo implements the kvBatchFetcher interface.
    45  func (f *singleKVFetcher) GetRangesInfo() []roachpb.RangeInfo {
    46  	panic(errors.AssertionFailedf("GetRangesInfo() called on singleKVFetcher"))
    47  }
    48  
    49  // ConvertBatchError returns a user friendly constraint violation error.
    50  func ConvertBatchError(
    51  	ctx context.Context, tableDesc *sqlbase.ImmutableTableDescriptor, b *kv.Batch,
    52  ) error {
    53  	origPErr := b.MustPErr()
    54  	if origPErr.Index == nil {
    55  		return origPErr.GoError()
    56  	}
    57  	j := origPErr.Index.Index
    58  	if j >= int32(len(b.Results)) {
    59  		return errors.AssertionFailedf("index %d outside of results: %+v", j, b.Results)
    60  	}
    61  	result := b.Results[j]
    62  	if cErr, ok := origPErr.GetDetail().(*roachpb.ConditionFailedError); ok && len(result.Rows) > 0 {
    63  		key := result.Rows[0].Key
    64  		return NewUniquenessConstraintViolationError(ctx, tableDesc, key, cErr.ActualValue)
    65  	}
    66  	return origPErr.GoError()
    67  }
    68  
    69  // NewUniquenessConstraintViolationError creates an error that represents a
    70  // violation of a UNIQUE constraint.
    71  func NewUniquenessConstraintViolationError(
    72  	ctx context.Context,
    73  	tableDesc *sqlbase.ImmutableTableDescriptor,
    74  	key roachpb.Key,
    75  	value *roachpb.Value,
    76  ) error {
    77  	// Strip the tenant prefix and pretend use the system tenant's SQL codec for
    78  	// the rest of this function. This is safe because the key is just used to
    79  	// decode the corresponding datums and never escapes this function.
    80  	codec := keys.SystemSQLCodec
    81  	key, _, err := keys.DecodeTenantPrefix(key)
    82  	if err != nil {
    83  		return err
    84  	}
    85  	indexID, _, err := sqlbase.DecodeIndexKeyPrefix(codec, tableDesc.TableDesc(), key)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	index, err := tableDesc.FindIndexByID(indexID)
    90  	if err != nil {
    91  		return err
    92  	}
    93  	var rf Fetcher
    94  
    95  	var valNeededForCol util.FastIntSet
    96  	valNeededForCol.AddRange(0, len(index.ColumnIDs)-1)
    97  
    98  	colIdxMap := make(map[sqlbase.ColumnID]int, len(index.ColumnIDs))
    99  	cols := make([]sqlbase.ColumnDescriptor, len(index.ColumnIDs))
   100  	for i, colID := range index.ColumnIDs {
   101  		colIdxMap[colID] = i
   102  		col, err := tableDesc.FindColumnByID(colID)
   103  		if err != nil {
   104  			return err
   105  		}
   106  		cols[i] = *col
   107  	}
   108  
   109  	tableArgs := FetcherTableArgs{
   110  		Desc:             tableDesc,
   111  		Index:            index,
   112  		ColIdxMap:        colIdxMap,
   113  		IsSecondaryIndex: indexID != tableDesc.PrimaryIndex.ID,
   114  		Cols:             cols,
   115  		ValNeededForCol:  valNeededForCol,
   116  	}
   117  	if err := rf.Init(
   118  		codec,
   119  		false, /* reverse */
   120  		sqlbase.ScanLockingStrength_FOR_NONE,
   121  		false, /* returnRangeInfo */
   122  		false, /* isCheck */
   123  		&sqlbase.DatumAlloc{},
   124  		tableArgs,
   125  	); err != nil {
   126  		return err
   127  	}
   128  	f := singleKVFetcher{kvs: [1]roachpb.KeyValue{{Key: key}}}
   129  	if value != nil {
   130  		f.kvs[0].Value = *value
   131  	}
   132  	// Use the Fetcher to decode the single kv pair above by passing in
   133  	// this singleKVFetcher implementation, which doesn't actually hit KV.
   134  	if err := rf.StartScanFrom(ctx, &f); err != nil {
   135  		return err
   136  	}
   137  	datums, _, _, err := rf.NextRowDecoded(ctx)
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	valStrs := make([]string, 0, len(datums))
   143  	for _, val := range datums {
   144  		valStrs = append(valStrs, val.String())
   145  	}
   146  
   147  	return pgerror.Newf(pgcode.UniqueViolation,
   148  		"duplicate key value (%s)=(%s) violates unique constraint %q",
   149  		strings.Join(index.ColumnNames, ","),
   150  		strings.Join(valStrs, ","),
   151  		index.Name)
   152  }