github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/writer/prolly_table_writer.go (about) 1 // Copyright 2019 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 "context" 19 20 "github.com/dolthub/go-mysql-server/sql" 21 22 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 23 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable" 24 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 25 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/globalstate" 26 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/index" 27 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil" 28 "github.com/dolthub/dolt/go/store/pool" 29 "github.com/dolthub/dolt/go/store/prolly" 30 "github.com/dolthub/dolt/go/store/val" 31 ) 32 33 // todo(andy): get from NodeStore 34 var sharePool = pool.NewBuffPool() 35 36 type prollyTableWriter struct { 37 tableName doltdb.TableName 38 dbName string 39 40 primary indexWriter 41 secondary map[string]indexWriter 42 43 tbl *doltdb.Table 44 sch schema.Schema 45 sqlSch sql.Schema 46 47 aiCol schema.Column 48 aiTracker globalstate.AutoIncrementTracker 49 nextAutoIncrementValue map[string]uint64 50 setAutoIncrement bool 51 52 flusher WriteSessionFlusher 53 setter SessionRootSetter 54 55 errEncountered error 56 } 57 58 var _ TableWriter = &prollyTableWriter{} 59 var _ AutoIncrementGetter = &prollyTableWriter{} 60 61 func getSecondaryProllyIndexWriters(ctx context.Context, t *doltdb.Table, sqlSch sql.Schema, sch schema.Schema) (map[string]indexWriter, error) { 62 s, err := t.GetIndexSet(ctx) 63 if err != nil { 64 return nil, err 65 } 66 pkDesc, _ := sch.GetMapDescriptors() 67 68 definitions := sch.Indexes().AllIndexes() 69 writers := make(map[string]indexWriter) 70 71 for _, def := range definitions { 72 if def.IsFullText() { 73 continue 74 } 75 defName := def.Name() 76 idxRows, err := s.GetIndex(ctx, sch, defName) 77 if err != nil { 78 return nil, err 79 } 80 idxMap := durable.ProllyMapFromIndex(idxRows) 81 82 keyMap, _ := ordinalMappingsFromSchema(sqlSch, def.Schema()) 83 keyDesc, _ := idxMap.Descriptors() 84 85 // mapping from secondary index key to primary key 86 pkMap := makeIndexToIndexMapping(def.Schema().GetPKCols(), sch.GetPKCols()) 87 writers[defName] = prollySecondaryIndexWriter{ 88 name: defName, 89 mut: idxMap.Mutate(), 90 unique: def.IsUnique(), 91 prefixLengths: def.PrefixLengths(), 92 idxCols: def.Count(), 93 keyMap: keyMap, 94 keyBld: val.NewTupleBuilder(keyDesc), 95 pkMap: pkMap, 96 pkBld: val.NewTupleBuilder(pkDesc), 97 } 98 } 99 100 return writers, nil 101 } 102 103 func getSecondaryKeylessProllyWriters(ctx context.Context, t *doltdb.Table, sqlSch sql.Schema, sch schema.Schema, primary prollyKeylessWriter) (map[string]indexWriter, error) { 104 s, err := t.GetIndexSet(ctx) 105 if err != nil { 106 return nil, err 107 } 108 109 definitions := sch.Indexes().AllIndexes() 110 writers := make(map[string]indexWriter) 111 112 for _, def := range definitions { 113 if def.IsFullText() { 114 continue 115 } 116 defName := def.Name() 117 idxRows, err := s.GetIndex(ctx, sch, defName) 118 if err != nil { 119 return nil, err 120 } 121 m := durable.ProllyMapFromIndex(idxRows) 122 m = prolly.ConvertToSecondaryKeylessIndex(m) 123 124 keyMap, _ := ordinalMappingsFromSchema(sqlSch, def.Schema()) 125 keyDesc, _ := m.Descriptors() 126 127 writers[defName] = prollyKeylessSecondaryWriter{ 128 name: defName, 129 mut: m.Mutate(), 130 primary: primary, 131 unique: def.IsUnique(), 132 spatial: def.IsSpatial(), 133 prefixLengths: def.PrefixLengths(), 134 keyBld: val.NewTupleBuilder(keyDesc), 135 prefixBld: val.NewTupleBuilder(keyDesc.PrefixDesc(def.Count())), 136 hashBld: val.NewTupleBuilder(val.NewTupleDescriptor(val.Type{Enc: val.Hash128Enc})), 137 keyMap: keyMap, 138 } 139 } 140 141 return writers, nil 142 } 143 144 // Insert implements TableWriter. 145 func (w *prollyTableWriter) Insert(ctx *sql.Context, sqlRow sql.Row) (err error) { 146 if err = w.primary.ValidateKeyViolations(ctx, sqlRow); err != nil { 147 return err 148 } 149 for _, wr := range w.secondary { 150 if err = wr.ValidateKeyViolations(ctx, sqlRow); err != nil { 151 if uke, ok := err.(secondaryUniqueKeyError); ok { 152 return w.primary.(primaryIndexErrBuilder).errForSecondaryUniqueKeyError(ctx, uke) 153 } 154 } 155 } 156 for _, wr := range w.secondary { 157 if err = wr.Insert(ctx, sqlRow); err != nil { 158 if uke, ok := err.(secondaryUniqueKeyError); ok { 159 return w.primary.(primaryIndexErrBuilder).errForSecondaryUniqueKeyError(ctx, uke) 160 } 161 return err 162 } 163 } 164 if err = w.primary.Insert(ctx, sqlRow); err != nil { 165 return err 166 } 167 168 w.setAutoIncrement = true 169 170 // TODO: need schema name in ai tracker 171 w.aiTracker.Next(w.tableName.Name, sqlRow) 172 return nil 173 } 174 175 // Delete implements TableWriter. 176 func (w *prollyTableWriter) Delete(ctx *sql.Context, sqlRow sql.Row) (err error) { 177 for _, wr := range w.secondary { 178 if err := wr.Delete(ctx, sqlRow); err != nil { 179 return err 180 } 181 } 182 if err := w.primary.Delete(ctx, sqlRow); err != nil { 183 return err 184 } 185 return nil 186 } 187 188 // Update implements TableWriter. 189 func (w *prollyTableWriter) Update(ctx *sql.Context, oldRow sql.Row, newRow sql.Row) (err error) { 190 for _, wr := range w.secondary { 191 if err := wr.Update(ctx, oldRow, newRow); err != nil { 192 if uke, ok := err.(secondaryUniqueKeyError); ok { 193 return w.primary.(primaryIndexErrBuilder).errForSecondaryUniqueKeyError(ctx, uke) 194 } 195 return err 196 } 197 } 198 if err := w.primary.Update(ctx, oldRow, newRow); err != nil { 199 return err 200 } 201 202 w.setAutoIncrement = true 203 return nil 204 } 205 206 // GetNextAutoIncrementValue implements TableWriter. 207 func (w *prollyTableWriter) GetNextAutoIncrementValue(ctx *sql.Context, insertVal interface{}) (uint64, error) { 208 return w.aiTracker.Next(w.tableName.Name, insertVal) 209 } 210 211 // SetAutoIncrementValue implements AutoIncrementSetter. 212 func (w *prollyTableWriter) SetAutoIncrementValue(ctx *sql.Context, val uint64) error { 213 seq, err := w.aiTracker.CoerceAutoIncrementValue(val) 214 if err != nil { 215 return err 216 } 217 218 w.nextAutoIncrementValue = make(map[string]uint64) 219 w.nextAutoIncrementValue[w.tableName.Name] = seq 220 221 // The work above is persisted in flush 222 return w.flush(ctx) 223 } 224 225 func (w *prollyTableWriter) AcquireAutoIncrementLock(ctx *sql.Context) (func(), error) { 226 return w.aiTracker.AcquireTableLock(ctx, w.tableName.Name) 227 } 228 229 // Close implements Closer 230 func (w *prollyTableWriter) Close(ctx *sql.Context) error { 231 // We discard data changes in DiscardChanges, but this doesn't include schema changes, which we don't want to flush 232 if w.errEncountered == nil { 233 return w.flush(ctx) 234 } 235 return nil 236 } 237 238 // StatementBegin implements TableWriter. 239 func (w *prollyTableWriter) StatementBegin(ctx *sql.Context) { 240 // Table writers are reused in a session, which means we need to reset the error state resulting from previous 241 // errors on every new statement. 242 w.errEncountered = nil 243 return 244 } 245 246 // DiscardChanges implements TableWriter. 247 func (w *prollyTableWriter) DiscardChanges(ctx *sql.Context, errorEncountered error) error { 248 if _, ignored := errorEncountered.(sql.IgnorableError); !ignored { 249 w.errEncountered = errorEncountered 250 } 251 err := w.primary.Discard(ctx) 252 for _, secondary := range w.secondary { 253 sErr := secondary.Discard(ctx) 254 if sErr != nil && err == nil { 255 err = sErr 256 } 257 } 258 return err 259 } 260 261 // StatementComplete implements TableWriter. 262 func (w *prollyTableWriter) StatementComplete(ctx *sql.Context) error { 263 err := w.primary.Commit(ctx) 264 for _, secondary := range w.secondary { 265 sErr := secondary.Commit(ctx) 266 if sErr != nil && err == nil { 267 err = sErr 268 } 269 } 270 return err 271 } 272 273 // GetIndexes implements sql.IndexAddressableTable. 274 func (w *prollyTableWriter) GetIndexes(ctx *sql.Context) ([]sql.Index, error) { 275 indexes := ctx.GetIndexRegistry().IndexesByTable(w.dbName, w.tableName.Name) 276 ret := make([]sql.Index, len(indexes)) 277 for i := range indexes { 278 ret[i] = indexes[i] 279 } 280 return ret, nil 281 } 282 283 func (w *prollyTableWriter) PreciseMatch() bool { 284 return true 285 } 286 287 // IndexedAccess implements sql.IndexAddressableTable. 288 func (w *prollyTableWriter) IndexedAccess(i sql.IndexLookup) sql.IndexedTable { 289 idx := index.DoltIndexFromSqlIndex(i.Index) 290 return &prollyFkIndexer{ 291 writer: w, 292 index: idx, 293 } 294 } 295 296 // Reset puts the writer into a fresh state, updating the schema and index writers according to the newly given table. 297 func (w *prollyTableWriter) Reset(ctx context.Context, sess *prollyWriteSession, tbl *doltdb.Table, sch schema.Schema) error { 298 sqlSch, err := sqlutil.FromDoltSchema("", w.tableName.Name, sch) 299 if err != nil { 300 return err 301 } 302 aiCol := autoIncrementColFromSchema(sch) 303 var newPrimary indexWriter 304 305 var newSecondaries map[string]indexWriter 306 if schema.IsKeyless(sch) { 307 newPrimary, err = getPrimaryKeylessProllyWriter(ctx, tbl, sqlSch.Schema, sch) 308 if err != nil { 309 return err 310 } 311 newSecondaries, err = getSecondaryKeylessProllyWriters(ctx, tbl, sqlSch.Schema, sch, newPrimary.(prollyKeylessWriter)) 312 if err != nil { 313 return err 314 } 315 } else { 316 newPrimary, err = getPrimaryProllyWriter(ctx, tbl, sqlSch.Schema, sch) 317 if err != nil { 318 return err 319 } 320 newSecondaries, err = getSecondaryProllyIndexWriters(ctx, tbl, sqlSch.Schema, sch) 321 if err != nil { 322 return err 323 } 324 } 325 326 w.tbl = tbl 327 w.sch = sch 328 w.sqlSch = sqlSch.Schema 329 w.primary = newPrimary 330 w.secondary = newSecondaries 331 w.aiCol = aiCol 332 w.flusher = sess 333 334 return nil 335 } 336 337 func (w *prollyTableWriter) table(ctx context.Context) (t *doltdb.Table, err error) { 338 // flush primary row storage 339 pm, err := w.primary.Map(ctx) 340 if err != nil { 341 return nil, err 342 } 343 344 t, err = w.tbl.UpdateRows(ctx, durable.IndexFromProllyMap(pm)) 345 if err != nil { 346 return nil, err 347 } 348 349 // flush secondary index storage 350 s, err := t.GetIndexSet(ctx) 351 if err != nil { 352 return nil, err 353 } 354 355 for _, wrSecondary := range w.secondary { 356 sm, err := wrSecondary.Map(ctx) 357 if err != nil { 358 return nil, err 359 } 360 idx := durable.IndexFromProllyMap(sm) 361 362 s, err = s.PutIndex(ctx, wrSecondary.Name(), idx) 363 if err != nil { 364 return nil, err 365 } 366 } 367 368 t, err = t.SetIndexSet(ctx, s) 369 if err != nil { 370 return nil, err 371 } 372 373 return t, nil 374 } 375 376 func (w *prollyTableWriter) flush(ctx *sql.Context) error { 377 ws, err := w.flusher.FlushWithAutoIncrementOverrides(ctx, w.setAutoIncrement, w.nextAutoIncrementValue) 378 if err != nil { 379 return err 380 } 381 return w.setter(ctx, w.dbName, ws.WorkingRoot()) 382 } 383 384 func ordinalMappingsFromSchema(from sql.Schema, to schema.Schema) (km, vm val.OrdinalMapping) { 385 km = makeOrdinalMapping(from, to.GetPKCols()) 386 vm = makeOrdinalMapping(from, to.GetNonPKCols()) 387 return 388 } 389 390 func makeOrdinalMapping(from sql.Schema, to *schema.ColCollection) (m val.OrdinalMapping) { 391 m = make(val.OrdinalMapping, to.StoredSize()) 392 for i := range m { 393 col := to.GetByStoredIndex(i) 394 name := col.Name 395 colIdx := from.IndexOfColName(name) 396 m[i] = colIdx 397 } 398 return 399 } 400 401 // NB: only works for primary-key tables/indexes 402 func makeIndexToIndexMapping(from, to *schema.ColCollection) (m val.OrdinalMapping) { 403 m = make(val.OrdinalMapping, len(to.GetColumns())) 404 for i, col := range to.GetColumns() { 405 m[i] = from.TagToIdx[col.Tag] 406 } 407 return 408 }