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  }