github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/row/fk_existence_insert.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 "github.com/cockroachdb/errors" 21 ) 22 23 // fkExistenceCheckForInsert is an auxiliary object that facilitates the existence 24 // checks on the referenced table when inserting new rows in 25 // a referencing table. 26 type fkExistenceCheckForInsert struct { 27 // fks maps mutated index id to slice of fkExistenceCheckBaseHelper, the outgoing 28 // foreign key existence checkers for each mutated index. 29 // 30 // In an fkInsertHelper, these slices will have at most one entry, 31 // since there can be (currently) at most one outgoing foreign key 32 // per mutated index. We use this data structure instead of a 33 // one-to-one map for consistency with the other helpers. 34 // 35 // TODO(knz): this limitation in CockroachDB is arbitrary and 36 // incompatible with PostgreSQL. pg supports potentially multiple 37 // referencing FK constraints for a single column tuple. 38 fks map[sqlbase.IndexID][]fkExistenceCheckBaseHelper 39 40 // checker is the object that actually carries out the lookups in 41 // KV. 42 checker *fkExistenceBatchChecker 43 } 44 45 // makeFkExistenceCheckHelperForInsert instantiates an insert helper. 46 func makeFkExistenceCheckHelperForInsert( 47 ctx context.Context, 48 txn *kv.Txn, 49 codec keys.SQLCodec, 50 table *sqlbase.ImmutableTableDescriptor, 51 otherTables FkTableMetadata, 52 colMap map[sqlbase.ColumnID]int, 53 alloc *sqlbase.DatumAlloc, 54 ) (fkExistenceCheckForInsert, error) { 55 h := fkExistenceCheckForInsert{ 56 checker: &fkExistenceBatchChecker{ 57 txn: txn, 58 }, 59 } 60 61 // We need an existence check helper for every referenced table. 62 for i := range table.OutboundFKs { 63 ref := &table.OutboundFKs[i] 64 // Look up the searched table. 65 searchTable := otherTables[ref.ReferencedTableID].Desc 66 if searchTable == nil { 67 return h, errors.AssertionFailedf("referenced table %d not in provided table map %+v", ref.ReferencedTableID, 68 otherTables) 69 } 70 searchIdx, err := sqlbase.FindFKReferencedIndex(searchTable.TableDesc(), ref.ReferencedColumnIDs) 71 if err != nil { 72 return h, errors.NewAssertionErrorWithWrappedErrf(err, 73 "failed to find suitable search index for fk %q", ref.Name) 74 } 75 mutatedIdx, err := sqlbase.FindFKOriginIndex(table.TableDesc(), ref.OriginColumnIDs) 76 if err != nil { 77 // TODO (rohany): Remove once #48224 is resolved. 78 assertionError := errors.NewAssertionErrorWithWrappedErrf(err, 79 "failed to find suitable search index for fk %q", ref.Name) 80 issueLink := errors.IssueLink{IssueURL: "https://github.com/cockroachdb/cockroach/issues/48224"} 81 withLink := errors.WithIssueLink(assertionError, issueLink) 82 return h, withLink 83 } 84 fk, err := makeFkExistenceCheckBaseHelper(txn, codec, otherTables, ref, searchIdx, mutatedIdx, colMap, alloc, CheckInserts) 85 if errors.Is(err, errSkipUnusedFK) { 86 continue 87 } 88 if err != nil { 89 return h, err 90 } 91 if h.fks == nil { 92 h.fks = make(map[sqlbase.IndexID][]fkExistenceCheckBaseHelper) 93 } 94 h.fks[mutatedIdx.ID] = append(h.fks[mutatedIdx.ID], fk) 95 } 96 97 if len(h.fks) > 0 { 98 // TODO(knz,radu): FK existence checks need to see the writes 99 // performed by the mutation. 100 // 101 // In order to make this true, we need to split the existence 102 // checks into a separate sequencing step, and have the first 103 // check happen no early than the end of all the "main" part of 104 // the statement. Unfortunately, the organization of the code does 105 // not allow this today. 106 // 107 // See: https://github.com/cockroachdb/cockroach/issues/33475 108 // 109 // In order to "make do" and preserve a modicum of FK semantics we 110 // thus need to disable step-wise execution here. The result is that 111 // it will also enable any interleaved read part to observe the 112 // mutation, and thus introduce the risk of a Halloween problem for 113 // any mutation that uses FK relationships. 114 _ = txn.ConfigureStepping(ctx, kv.SteppingDisabled) 115 } 116 117 return h, nil 118 } 119 120 // addAllIdxChecks queues a FK existence check for every referenced table. 121 func (h fkExistenceCheckForInsert) addAllIdxChecks( 122 ctx context.Context, row tree.Datums, traceKV bool, 123 ) error { 124 for idx := range h.fks { 125 if err := queueFkExistenceChecksForRow(ctx, h.checker, h.fks[idx], row, traceKV); err != nil { 126 return err 127 } 128 } 129 return nil 130 }