github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/merge/mutable_secondary_index.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 merge
    16  
    17  import (
    18  	"context"
    19  
    20  	"github.com/dolthub/go-mysql-server/sql"
    21  
    22  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
    23  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    24  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
    25  	"github.com/dolthub/dolt/go/store/prolly"
    26  	"github.com/dolthub/dolt/go/store/val"
    27  )
    28  
    29  // GetMutableSecondaryIdxs returns a MutableSecondaryIdx for each secondary index in |indexes|.
    30  func GetMutableSecondaryIdxs(ctx *sql.Context, sch schema.Schema, tableName string, indexes durable.IndexSet) ([]MutableSecondaryIdx, error) {
    31  	mods := make([]MutableSecondaryIdx, sch.Indexes().Count())
    32  	for i, index := range sch.Indexes().AllIndexes() {
    33  		idx, err := indexes.GetIndex(ctx, sch, index.Name())
    34  		if err != nil {
    35  			return nil, err
    36  		}
    37  		m := durable.ProllyMapFromIndex(idx)
    38  		if schema.IsKeyless(sch) {
    39  			m = prolly.ConvertToSecondaryKeylessIndex(m)
    40  		}
    41  		mods[i], err = NewMutableSecondaryIdx(ctx, m, sch, tableName, index)
    42  		if err != nil {
    43  			return nil, err
    44  		}
    45  	}
    46  	return mods, nil
    47  }
    48  
    49  // GetMutableSecondaryIdxsWithPending returns a MutableSecondaryIdx for each secondary index in |indexes|. If an index
    50  // is listed in the given |sch|, but does not exist in the given |indexes|, then it is skipped. This is useful when
    51  // merging a schema that has a new index, but the index does not exist on the index set being modified.
    52  func GetMutableSecondaryIdxsWithPending(ctx *sql.Context, sch schema.Schema, tableName string, indexes durable.IndexSet, pendingSize int) ([]MutableSecondaryIdx, error) {
    53  	mods := make([]MutableSecondaryIdx, 0, sch.Indexes().Count())
    54  	for _, index := range sch.Indexes().AllIndexes() {
    55  
    56  		// If an index isn't found on the left side, we know it must be a new index added on the right side,
    57  		// so just skip it, and we'll rebuild the full index at the end of merging when we notice it's missing.
    58  		// TODO: GetMutableSecondaryIdxs should get this same treatment, or we should have a flag that
    59  		//       allows skipping over missing indexes. Seems like we could refactor this code to remove
    60  		//       the duplication.
    61  		hasIndex, err := indexes.HasIndex(ctx, index.Name())
    62  		if err != nil {
    63  			return nil, err
    64  		}
    65  		if !hasIndex {
    66  			continue
    67  		}
    68  
    69  		idx, err := indexes.GetIndex(ctx, sch, index.Name())
    70  		if err != nil {
    71  			return nil, err
    72  		}
    73  		m := durable.ProllyMapFromIndex(idx)
    74  
    75  		// If the schema has changed, don't reuse the index.
    76  		// TODO: This isn't technically required, but correctly handling updating secondary indexes when only some
    77  		// of the table's rows have been updated is difficult to get right.
    78  		// Dropping the index is potentially slower but guarenteed to be correct.
    79  		if !m.KeyDesc().Equals(index.Schema().GetKeyDescriptorWithNoConversion()) {
    80  			continue
    81  		}
    82  
    83  		if !m.ValDesc().Equals(index.Schema().GetValueDescriptor()) {
    84  			continue
    85  		}
    86  
    87  		if schema.IsKeyless(sch) {
    88  			m = prolly.ConvertToSecondaryKeylessIndex(m)
    89  		}
    90  		newMutableSecondaryIdx, err := NewMutableSecondaryIdx(ctx, m, sch, tableName, index)
    91  		if err != nil {
    92  			return nil, err
    93  		}
    94  
    95  		newMutableSecondaryIdx.mut = newMutableSecondaryIdx.mut.WithMaxPending(pendingSize)
    96  		mods = append(mods, newMutableSecondaryIdx)
    97  	}
    98  	return mods, nil
    99  }
   100  
   101  // MutableSecondaryIdx wraps a prolly.MutableMap of a secondary table index. It
   102  // provides the InsertEntry, UpdateEntry, and DeleteEntry functions which can be
   103  // used to modify the index based on a modification to corresponding primary row.
   104  type MutableSecondaryIdx struct {
   105  	Name    string
   106  	mut     *prolly.MutableMap
   107  	builder index.SecondaryKeyBuilder
   108  }
   109  
   110  // NewMutableSecondaryIdx returns a MutableSecondaryIdx. |m| is the secondary idx data.
   111  func NewMutableSecondaryIdx(ctx *sql.Context, idx prolly.Map, sch schema.Schema, tableName string, def schema.Index) (MutableSecondaryIdx, error) {
   112  	b, err := index.NewSecondaryKeyBuilder(ctx, tableName, sch, def, idx.KeyDesc(), idx.Pool(), idx.NodeStore())
   113  	if err != nil {
   114  		return MutableSecondaryIdx{}, err
   115  	}
   116  
   117  	return MutableSecondaryIdx{
   118  		Name:    def.Name(),
   119  		mut:     idx.Mutate(),
   120  		builder: b,
   121  	}, nil
   122  }
   123  
   124  // InsertEntry inserts a secondary index entry given the key and new value
   125  // of the primary row.
   126  func (m MutableSecondaryIdx) InsertEntry(ctx context.Context, key, newValue val.Tuple) error {
   127  	newKey, err := m.builder.SecondaryKeyFromRow(ctx, key, newValue)
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	// secondary indexes only use their key tuple
   133  	err = m.mut.Put(ctx, newKey, val.EmptyTuple)
   134  	if err != nil {
   135  		return err
   136  	}
   137  	return nil
   138  }
   139  
   140  // UpdateEntry modifies the corresponding secondary index entry given the key
   141  // and curr/new values of the primary row.
   142  func (m MutableSecondaryIdx) UpdateEntry(ctx context.Context, key, currValue, newValue val.Tuple) error {
   143  	currKey, err := m.builder.SecondaryKeyFromRow(ctx, key, currValue)
   144  	if err != nil {
   145  		return err
   146  	}
   147  
   148  	newKey, err := m.builder.SecondaryKeyFromRow(ctx, key, newValue)
   149  	if err != nil {
   150  		return err
   151  	}
   152  
   153  	err = m.mut.Delete(ctx, currKey)
   154  	if err != nil {
   155  		return err
   156  	}
   157  	return m.mut.Put(ctx, newKey, val.EmptyTuple)
   158  }
   159  
   160  // DeleteEntry deletes a secondary index entry given they key and value of the primary row.
   161  func (m MutableSecondaryIdx) DeleteEntry(ctx context.Context, key val.Tuple, value val.Tuple) error {
   162  	currKey, err := m.builder.SecondaryKeyFromRow(ctx, key, value)
   163  	if err != nil {
   164  		return err
   165  	}
   166  
   167  	return m.mut.Delete(ctx, currKey)
   168  }
   169  
   170  // Map returns the finalized prolly.Map of the underlying prolly.MutableMap.
   171  func (m MutableSecondaryIdx) Map(ctx context.Context) (prolly.Map, error) {
   172  	return m.mut.Map(ctx)
   173  }