github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/row/fk_existence_update.go (about) 1 // Copyright 2019 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 16 "github.com/cockroachdb/cockroach/pkg/keys" 17 "github.com/cockroachdb/cockroach/pkg/kv" 18 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 19 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 20 ) 21 22 // fkExistenceCheckForUpdate is an auxiliary object with two purposes: 23 // 24 // - its main purpose is to facilitate the existence checks on both 25 // referencing and referenced tables when modifying rows in a table. 26 // 27 // Note that users of this purpose are responsible for calling 28 // addCheckForIndex() on all mutated indexes, to register a mutated 29 // index for FK checking. 30 // 31 // TODO(knz): why cannot the fkExistenceCheckForUpdate make this determination 32 // itself, like the other helpers? The asymmetry is concerning. 33 // 34 // - its secondary purpose is to serve the boolean "hasFk()" for "does 35 // the mutated table have any FK constraints, either forward or 36 // backward?" This boolean is used by the row writer and the 37 // CASCADEing close. 38 // 39 // TODO(knz): this responsibility should be carried by another 40 // object, so that the helper can specialize to only do existence 41 // checks! 42 // 43 type fkExistenceCheckForUpdate struct { 44 // inbound is responsible for existence checks in referencing tables. 45 inbound fkExistenceCheckForDelete 46 // output is responsible for existence checks in referenced tables. 47 outbound fkExistenceCheckForInsert 48 49 // indexIDsToCheck determines the list of indexes in the mutated 50 // table for which to perform FK checks. 51 // 52 // This may be a subset of all constraints on the mutated table. 53 // The inbound and outbound checkers look at all constraints by default; 54 // the update helper needs to maintain its own list of index IDs 55 // to operate on only a subset, and also define its own addIndexChecks() 56 // logic instead of deferring to addAllIdxChecks(). 57 indexIDsToCheck map[sqlbase.IndexID]struct{} 58 59 // checker is the object that actually carries out the lookups in 60 // KV. 61 checker *fkExistenceBatchChecker 62 } 63 64 // makeFkExistenceCheckHelperForUpdate instantiates an update helper. 65 func makeFkExistenceCheckHelperForUpdate( 66 ctx context.Context, 67 txn *kv.Txn, 68 codec keys.SQLCodec, 69 table *sqlbase.ImmutableTableDescriptor, 70 otherTables FkTableMetadata, 71 updateCols []sqlbase.ColumnDescriptor, 72 colMap map[sqlbase.ColumnID]int, 73 alloc *sqlbase.DatumAlloc, 74 ) (fkExistenceCheckForUpdate, error) { 75 ret := fkExistenceCheckForUpdate{ 76 indexIDsToCheck: map[sqlbase.IndexID]struct{}{0: {}}, 77 } 78 79 // Instantiate a helper for the referencing tables. 80 var err error 81 if ret.inbound, err = makeFkExistenceCheckHelperForDelete(ctx, txn, codec, table, otherTables, colMap, 82 alloc); err != nil { 83 return ret, err 84 } 85 86 // Instantiate a helper for the referenced table(s). 87 ret.outbound, err = makeFkExistenceCheckHelperForInsert(ctx, txn, codec, table, otherTables, colMap, alloc) 88 ret.outbound.checker = ret.inbound.checker 89 90 // We need *some* KV batch checker to perform the checks. It doesn't 91 // matter which; so we use the one instantiated by the inbound 92 // checker and simply disregard/ignore the one instantiated by the 93 // outbound checker. 94 ret.checker = ret.inbound.checker 95 96 return ret, err 97 } 98 99 // addCheckForIndex registers a mutated index to perform FK existence checks for. 100 func (fks fkExistenceCheckForUpdate) addCheckForIndex( 101 indexID sqlbase.IndexID, descriptorType sqlbase.IndexDescriptor_Type, 102 ) { 103 if fks.checker == nil { 104 return 105 } 106 if descriptorType == sqlbase.IndexDescriptor_FORWARD { 107 // We ignore FK existence checks for inverted indexes. 108 // 109 // TODO(knz): verify that this is indeed correct. 110 fks.indexIDsToCheck[indexID] = struct{}{} 111 } 112 } 113 114 // hasFKs determines whether the table being mutated has any forward 115 // or backward FK constraints. This is the secondary purpose of the helper 116 // and is unrelated to the task of FK existence checks. 117 func (fks fkExistenceCheckForUpdate) hasFKs() bool { 118 return len(fks.inbound.fks) > 0 || len(fks.outbound.fks) > 0 119 } 120 121 // addAllIdxChecks queues a FK existence check for the backward and forward 122 // constraints for the indexes 123 func (fks fkExistenceCheckForUpdate) addIndexChecks( 124 ctx context.Context, oldValues, newValues tree.Datums, traceKV bool, 125 ) error { 126 for indexID := range fks.indexIDsToCheck { 127 if err := queueFkExistenceChecksForRow(ctx, fks.checker, fks.inbound.fks[indexID], oldValues, traceKV); err != nil { 128 return err 129 } 130 if err := queueFkExistenceChecksForRow(ctx, fks.checker, fks.outbound.fks[indexID], newValues, traceKV); err != nil { 131 return err 132 } 133 } 134 return nil 135 }