github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/writer/prolly_fk_indexer.go (about) 1 // Copyright 2022 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package writer 16 17 import ( 18 "fmt" 19 "io" 20 21 "github.com/dolthub/go-mysql-server/sql" 22 23 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/index" 24 "github.com/dolthub/dolt/go/store/prolly" 25 "github.com/dolthub/dolt/go/store/prolly/tree" 26 "github.com/dolthub/dolt/go/store/val" 27 ) 28 29 type prollyFkIndexer struct { 30 writer *prollyTableWriter 31 index index.DoltIndex 32 pRange prolly.Range 33 refCheck bool 34 } 35 36 var _ sql.Table = (*prollyFkIndexer)(nil) 37 var _ sql.IndexedTable = (*prollyFkIndexer)(nil) 38 var _ sql.ReferenceChecker = (*prollyFkIndexer)(nil) 39 40 // Name implements the interface sql.Table. 41 func (n *prollyFkIndexer) Name() string { 42 return n.writer.tableName.Name 43 } 44 45 // String implements the interface sql.Table. 46 func (n *prollyFkIndexer) String() string { 47 return n.writer.tableName.Name 48 } 49 50 // Schema implements the interface sql.Table. 51 func (n *prollyFkIndexer) Schema() sql.Schema { 52 return n.writer.sqlSch 53 } 54 55 func (n *prollyFkIndexer) SetReferenceCheck() error { 56 n.refCheck = true 57 return nil 58 } 59 60 // Collation implements the interface sql.Table. 61 func (n *prollyFkIndexer) Collation() sql.CollationID { 62 return sql.CollationID(n.writer.sch.GetCollation()) 63 } 64 65 func (n *prollyFkIndexer) LookupPartitions(ctx *sql.Context, lookup sql.IndexLookup) (sql.PartitionIter, error) { 66 ranges, err := index.ProllyRangesFromIndexLookup(ctx, lookup) 67 if err != nil { 68 return nil, err 69 } 70 n.pRange = ranges[0] 71 return sql.PartitionsToPartitionIter(fkDummyPartition{}), nil 72 } 73 74 // Partitions implements the interface sql.Table. 75 func (n *prollyFkIndexer) Partitions(ctx *sql.Context) (sql.PartitionIter, error) { 76 return sql.PartitionsToPartitionIter(fkDummyPartition{}), nil 77 } 78 79 // PartitionRows implements the interface sql.Table. 80 func (n *prollyFkIndexer) PartitionRows(ctx *sql.Context, _ sql.Partition) (sql.RowIter, error) { 81 var idxWriter indexWriter 82 var ok bool 83 if n.index.IsPrimaryKey() { 84 idxWriter = n.writer.primary 85 } else if idxWriter, ok = n.writer.secondary[n.index.ID()]; !ok { 86 return nil, fmt.Errorf("unable to find writer for index `%s`", n.index.ID()) 87 } 88 89 pkToIdxMap := make(val.OrdinalMapping, n.writer.sch.GetPKCols().Size()) 90 for j, idxCol := range n.index.IndexSchema().GetPKCols().GetColumns() { 91 if i, ok := n.writer.sch.GetPKCols().TagToIdx[idxCol.Tag]; ok { 92 pkToIdxMap[i] = j 93 } 94 } 95 rangeIter, err := idxWriter.IterRange(ctx, n.pRange) 96 if err != nil { 97 return nil, err 98 } 99 if primary, ok := n.writer.primary.(prollyIndexWriter); ok { 100 return &prollyFkPkRowIter{ 101 rangeIter: rangeIter, 102 pkToIdxMap: pkToIdxMap, 103 primary: primary, 104 sqlSch: n.writer.sqlSch, 105 refCheck: n.refCheck, 106 }, nil 107 } else { 108 return &prollyFkKeylessRowIter{ 109 rangeIter: rangeIter, 110 primary: n.writer.primary.(prollyKeylessWriter), 111 sqlSch: n.writer.sqlSch, 112 }, nil 113 } 114 } 115 116 // prollyFkPkRowIter returns rows of the parent table requested by a foreign key reference. For use on tables with primary keys. 117 type prollyFkPkRowIter struct { 118 rangeIter prolly.MapIter 119 pkToIdxMap val.OrdinalMapping 120 primary prollyIndexWriter 121 sqlSch sql.Schema 122 refCheck bool 123 } 124 125 var _ sql.RowIter = prollyFkPkRowIter{} 126 127 // Next implements the interface sql.RowIter. 128 func (iter prollyFkPkRowIter) Next(ctx *sql.Context) (sql.Row, error) { 129 for { 130 // |rangeIter| iterates on the foreign key index of the parent table 131 k, _, err := iter.rangeIter.Next(ctx) 132 if err != nil { 133 return nil, err 134 } 135 if k == nil { 136 return nil, io.EOF 137 } 138 139 pkBld := iter.primary.keyBld 140 for pkPos, idxPos := range iter.pkToIdxMap { 141 pkBld.PutRaw(pkPos, k.GetField(idxPos)) 142 } 143 pkTup := pkBld.BuildPermissive(sharePool) 144 145 var tblKey, tblVal val.Tuple 146 err = iter.primary.mut.Get(ctx, pkTup, func(k, v val.Tuple) error { 147 tblKey, tblVal = k, v 148 return nil 149 }) 150 if err != nil { 151 return nil, err 152 } 153 if tblKey == nil { 154 continue // referential integrity broken 155 } 156 157 if iter.refCheck { 158 // no need to deserialize 159 return nil, nil 160 } 161 162 nextRow := make(sql.Row, len(iter.primary.keyMap)+len(iter.primary.valMap)) 163 for from := range iter.primary.keyMap { 164 to := iter.primary.keyMap.MapOrdinal(from) 165 if nextRow[to], err = tree.GetField(ctx, iter.primary.keyBld.Desc, from, tblKey, iter.primary.mut.NodeStore()); err != nil { 166 return nil, err 167 } 168 } 169 for from := range iter.primary.valMap { 170 to := iter.primary.valMap.MapOrdinal(from) 171 if nextRow[to], err = tree.GetField(ctx, iter.primary.valBld.Desc, from, tblVal, iter.primary.mut.NodeStore()); err != nil { 172 return nil, err 173 } 174 } 175 return nextRow, nil 176 } 177 } 178 179 // Close implements the interface sql.RowIter. 180 func (iter prollyFkPkRowIter) Close(ctx *sql.Context) error { 181 return nil 182 } 183 184 // prollyFkKeylessRowIter returns rows requested by a foreign key reference. For use on keyless tables. 185 type prollyFkKeylessRowIter struct { 186 rangeIter prolly.MapIter 187 primary prollyKeylessWriter 188 sqlSch sql.Schema 189 } 190 191 var _ sql.RowIter = prollyFkKeylessRowIter{} 192 193 // Next implements the interface sql.RowIter. 194 func (iter prollyFkKeylessRowIter) Next(ctx *sql.Context) (sql.Row, error) { 195 k, _, err := iter.rangeIter.Next(ctx) 196 if err != nil { 197 return nil, err 198 } 199 if k == nil { 200 return nil, io.EOF 201 } 202 hashId := k.GetField(k.Count() - 1) 203 iter.primary.keyBld.PutHash128(0, hashId) 204 primaryKey := iter.primary.keyBld.Build(sharePool) 205 206 nextRow := make(sql.Row, len(iter.primary.valMap)) 207 err = iter.primary.mut.Get(ctx, primaryKey, func(tblKey, tblVal val.Tuple) error { 208 for from := range iter.primary.valMap { 209 to := iter.primary.valMap.MapOrdinal(from) 210 if nextRow[to], err = tree.GetField(ctx, iter.primary.valBld.Desc, from+1, tblVal, iter.primary.mut.NodeStore()); err != nil { 211 return err 212 } 213 } 214 return nil 215 }) 216 if err != nil { 217 return nil, err 218 } 219 return nextRow, nil 220 } 221 222 // Close implements the interface sql.RowIter. 223 func (iter prollyFkKeylessRowIter) Close(ctx *sql.Context) error { 224 return nil 225 }