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 }