github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/merge/fulltext_table.go (about)

     1  // Copyright 2023 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  	"fmt"
    19  	"io"
    20  
    21  	"github.com/dolthub/go-mysql-server/memory"
    22  	"github.com/dolthub/go-mysql-server/sql"
    23  	"github.com/dolthub/go-mysql-server/sql/fulltext"
    24  
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
    29  	"github.com/dolthub/dolt/go/store/pool"
    30  	"github.com/dolthub/dolt/go/store/prolly/tree"
    31  	"github.com/dolthub/dolt/go/store/val"
    32  )
    33  
    34  var sharePool = pool.NewBuffPool()
    35  
    36  type fulltextTable struct {
    37  	GMSTable *memory.Table
    38  	Table    *doltdb.Table
    39  	Sch      schema.Schema
    40  	SqlSch   sql.Schema
    41  }
    42  
    43  var _ fulltext.EditableTable = (*fulltextTable)(nil)
    44  
    45  // createFulltextTable creates an in-memory Full-Text table from the given table name on the given root. This table will
    46  // be used to read/write data from/to the underlying Dolt table.
    47  func createFulltextTable(ctx *sql.Context, name string, root doltdb.RootValue) (*fulltextTable, error) {
    48  	tbl, ok, err := root.GetTable(ctx, doltdb.TableName{Name: name})
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  	if !ok {
    53  		return nil, fmt.Errorf("attempted to load Full-Text table `%s` during Full-Text merge but it could not be found", name)
    54  	}
    55  	sch, err := tbl.GetSchema(ctx)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  	sqlSch, err := sqlutil.FromDoltSchema("", name, sch)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	gmsDb := memory.NewDatabase("gms_db")
    65  	gmsTable := memory.NewLocalTable(gmsDb, name, sqlSch, nil)
    66  	gmsTable.EnablePrimaryKeyIndexes()
    67  	return &fulltextTable{
    68  		GMSTable: gmsTable,
    69  		Table:    tbl,
    70  		Sch:      sch,
    71  		SqlSch:   sqlSch.Schema,
    72  	}, nil
    73  }
    74  
    75  // Name implements the interface fulltext.EditableTable.
    76  func (table *fulltextTable) Name() string {
    77  	return table.GMSTable.Name()
    78  }
    79  
    80  // String implements the interface fulltext.EditableTable.
    81  func (table *fulltextTable) String() string {
    82  	return table.GMSTable.String()
    83  }
    84  
    85  // Schema implements the interface fulltext.EditableTable.
    86  func (table *fulltextTable) Schema() sql.Schema {
    87  	return table.GMSTable.Schema()
    88  }
    89  
    90  // Collation implements the interface fulltext.EditableTable.
    91  func (table *fulltextTable) Collation() sql.CollationID {
    92  	return table.GMSTable.Collation()
    93  }
    94  
    95  // Partitions implements the interface fulltext.EditableTable.
    96  func (table *fulltextTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) {
    97  	return table.GMSTable.Partitions(ctx)
    98  }
    99  
   100  // PartitionRows implements the interface fulltext.EditableTable.
   101  func (table *fulltextTable) PartitionRows(ctx *sql.Context, partition sql.Partition) (sql.RowIter, error) {
   102  	return table.GMSTable.PartitionRows(ctx, partition)
   103  }
   104  
   105  // Inserter implements the interface fulltext.EditableTable.
   106  func (table *fulltextTable) Inserter(ctx *sql.Context) sql.RowInserter {
   107  	return table.GMSTable.Inserter(ctx)
   108  }
   109  
   110  // Updater implements the interface fulltext.EditableTable.
   111  func (table *fulltextTable) Updater(ctx *sql.Context) sql.RowUpdater {
   112  	return table.GMSTable.Updater(ctx)
   113  }
   114  
   115  // Deleter implements the interface fulltext.EditableTable.
   116  func (table *fulltextTable) Deleter(ctx *sql.Context) sql.RowDeleter {
   117  	return table.GMSTable.Deleter(ctx)
   118  }
   119  
   120  // IndexedAccess implements the interface fulltext.EditableTable.
   121  func (table *fulltextTable) IndexedAccess(lookup sql.IndexLookup) sql.IndexedTable {
   122  	return table.GMSTable.IndexedAccess(lookup)
   123  }
   124  
   125  // GetIndexes implements the interface fulltext.EditableTable.
   126  func (table *fulltextTable) GetIndexes(ctx *sql.Context) ([]sql.Index, error) {
   127  	return table.GMSTable.GetIndexes(ctx)
   128  }
   129  
   130  // PreciseMatch implements the interface fulltext.EditableTable.
   131  func (table *fulltextTable) PreciseMatch() bool {
   132  	return false
   133  }
   134  
   135  // ApplyToTable writes the data from the internal GMS table to the internal Dolt table, then returns the updated Dolt
   136  // table. The updated Dolt table is not stored.
   137  func (table *fulltextTable) ApplyToTable(ctx *sql.Context) (*doltdb.Table, error) {
   138  	partIter, err := table.GMSTable.Partitions(ctx)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  	rowIter := sql.NewTableRowIter(ctx, table.GMSTable, partIter)
   143  
   144  	idx, err := table.Table.GetRowData(ctx)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  	m := durable.ProllyMapFromIndex(idx)
   149  	keyDesc, valDesc := m.Descriptors()
   150  	keyMap, valMap := ordinalMappingsFromSchema(table.SqlSch, table.Sch)
   151  	mut := m.Mutate()
   152  	keyBld := val.NewTupleBuilder(keyDesc)
   153  	valBld := val.NewTupleBuilder(valDesc)
   154  
   155  	sqlRow, err := rowIter.Next(ctx)
   156  	for ; err == nil; sqlRow, err = rowIter.Next(ctx) {
   157  		for to := range keyMap {
   158  			from := keyMap.MapOrdinal(to)
   159  			if err = tree.PutField(ctx, mut.NodeStore(), keyBld, to, sqlRow[from]); err != nil {
   160  				return nil, err
   161  			}
   162  		}
   163  		k := keyBld.Build(sharePool)
   164  
   165  		for to := range valMap {
   166  			from := valMap.MapOrdinal(to)
   167  			if err = tree.PutField(ctx, mut.NodeStore(), valBld, to, sqlRow[from]); err != nil {
   168  				return nil, err
   169  			}
   170  		}
   171  		v := valBld.Build(sharePool)
   172  
   173  		if err = mut.Put(ctx, k, v); err != nil {
   174  			return nil, err
   175  		}
   176  	}
   177  	if err != nil && err != io.EOF {
   178  		return nil, err
   179  	}
   180  
   181  	mapped, err := mut.Map(ctx)
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  	return table.Table.UpdateRows(ctx, durable.IndexFromProllyMap(mapped))
   186  }
   187  
   188  func ordinalMappingsFromSchema(from sql.Schema, to schema.Schema) (km, vm val.OrdinalMapping) {
   189  	km = makeOrdinalMapping(from, to.GetPKCols())
   190  	vm = makeOrdinalMapping(from, to.GetNonPKCols())
   191  	return
   192  }
   193  
   194  func makeOrdinalMapping(from sql.Schema, to *schema.ColCollection) (m val.OrdinalMapping) {
   195  	m = make(val.OrdinalMapping, len(to.GetColumns()))
   196  	for i := range m {
   197  		name := to.GetByIndex(i).Name
   198  		for j, col := range from {
   199  			if col.Name == name {
   200  				m[i] = j
   201  			}
   202  		}
   203  	}
   204  	return
   205  }