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  }