github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/dtables/conflicts_tables.go (about)

     1  package dtables
     2  
     3  // Copyright 2019 Dolthub, Inc.
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  
    21  	"github.com/dolthub/go-mysql-server/sql"
    22  
    23  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    24  	"github.com/dolthub/dolt/go/libraries/doltcore/merge"
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
    27  	"github.com/dolthub/dolt/go/store/types"
    28  )
    29  
    30  // NewConflictsTable returns a new ConflictsTable instance
    31  func NewConflictsTable(ctx *sql.Context, tblName string, srcTbl sql.Table, root doltdb.RootValue, rs RootSetter) (sql.Table, error) {
    32  	tbl, tblName, ok, err := doltdb.GetTableInsensitive(ctx, root, tblName)
    33  	if err != nil {
    34  		return nil, err
    35  	} else if !ok {
    36  		return nil, sql.ErrTableNotFound.New(tblName)
    37  	}
    38  
    39  	if types.IsFormat_DOLT(tbl.Format()) {
    40  		upd, ok := srcTbl.(sql.UpdatableTable)
    41  		if !ok {
    42  			return nil, fmt.Errorf("%s can not have conflicts because it is not updateable", tblName)
    43  		}
    44  		return newProllyConflictsTable(ctx, tbl, upd, tblName, root, rs)
    45  	}
    46  
    47  	return newNomsConflictsTable(ctx, tbl, tblName, root, rs)
    48  }
    49  
    50  func newNomsConflictsTable(ctx *sql.Context, tbl *doltdb.Table, tblName string, root doltdb.RootValue, rs RootSetter) (sql.Table, error) {
    51  	rd, err := merge.NewConflictReader(ctx, tbl, tblName)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	confSch := rd.GetSchema()
    56  
    57  	sqlSch, err := sqlutil.FromDoltSchema("", doltdb.DoltConfTablePrefix+tblName, confSch)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	return ConflictsTable{
    63  		tblName: tblName,
    64  		sqlSch:  sqlSch,
    65  		root:    root,
    66  		tbl:     tbl,
    67  		rd:      rd,
    68  		rs:      rs,
    69  	}, nil
    70  }
    71  
    72  var _ sql.Table = ConflictsTable{}
    73  var _ sql.DeletableTable = ConflictsTable{}
    74  
    75  // ConflictsTable is a sql.Table implementation that provides access to the conflicts that exist for a user table
    76  type ConflictsTable struct {
    77  	tblName string
    78  	sqlSch  sql.PrimaryKeySchema
    79  	root    doltdb.RootValue
    80  	tbl     *doltdb.Table
    81  	rd      *merge.ConflictReader
    82  	rs      RootSetter
    83  }
    84  
    85  type RootSetter interface {
    86  	SetRoot(ctx *sql.Context, root doltdb.RootValue) error
    87  }
    88  
    89  // Name returns the name of the table
    90  func (ct ConflictsTable) Name() string {
    91  	return doltdb.DoltConfTablePrefix + ct.tblName
    92  }
    93  
    94  // String returns a string identifying the table
    95  func (ct ConflictsTable) String() string {
    96  	return doltdb.DoltConfTablePrefix + ct.tblName
    97  }
    98  
    99  // Schema returns the sql.Schema of the table
   100  func (ct ConflictsTable) Schema() sql.Schema {
   101  	return ct.sqlSch.Schema
   102  }
   103  
   104  // Collation implements the sql.Table interface.
   105  func (ct ConflictsTable) Collation() sql.CollationID {
   106  	return sql.Collation_Default
   107  }
   108  
   109  // Partitions returns a PartitionIter which can be used to get all the data partitions
   110  func (ct ConflictsTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) {
   111  	return index.SinglePartitionIterFromNomsMap(nil), nil
   112  }
   113  
   114  // PartitionRows returns a RowIter for the given partition
   115  func (ct ConflictsTable) PartitionRows(ctx *sql.Context, part sql.Partition) (sql.RowIter, error) {
   116  	// conflict reader must be reset each time partitionRows is called.
   117  	rd, err := merge.NewConflictReader(ctx, ct.tbl, ct.tblName)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	return conflictRowIter{rd}, nil
   122  }
   123  
   124  // Deleter returns a RowDeleter for this table. The RowDeleter will get one call to Delete for each row to be deleted,
   125  // and will end with a call to Close() to finalize the delete operation.
   126  func (ct ConflictsTable) Deleter(ctx *sql.Context) sql.RowDeleter {
   127  	return &conflictDeleter{ct: ct, rs: ct.rs}
   128  }
   129  
   130  type conflictRowIter struct {
   131  	rd *merge.ConflictReader
   132  }
   133  
   134  // Next retrieves the next row. It will return io.EOF if it's the last row.
   135  // After retrieving the last row, Close will be automatically closed.
   136  func (itr conflictRowIter) Next(ctx *sql.Context) (sql.Row, error) {
   137  	cnf, err := itr.rd.NextConflict(ctx)
   138  
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	return sqlutil.DoltRowToSqlRow(cnf, itr.rd.GetSchema())
   144  }
   145  
   146  // Close the iterator.
   147  func (itr conflictRowIter) Close(*sql.Context) error {
   148  	return itr.rd.Close()
   149  }
   150  
   151  type conflictDeleter struct {
   152  	ct  ConflictsTable
   153  	rs  RootSetter
   154  	pks []types.Value
   155  }
   156  
   157  var _ sql.RowDeleter = &conflictDeleter{}
   158  
   159  // Delete deletes the given row. Returns ErrDeleteRowNotFound if the row was not found. Delete will be called once for
   160  // each row to process for the delete operation, which may involve many rows. After all rows have been processed,
   161  // Close is called.
   162  func (cd *conflictDeleter) Delete(ctx *sql.Context, r sql.Row) error {
   163  	cnfSch := cd.ct.rd.GetSchema()
   164  	// We could use a test VRW, but as any values which use VRWs will already exist, we can potentially save on memory usage
   165  	cnfRow, err := sqlutil.SqlRowToDoltRow(ctx, cd.ct.tbl.ValueReadWriter(), r, cnfSch)
   166  
   167  	if err != nil {
   168  		return err
   169  	}
   170  
   171  	pkVal, err := cd.ct.rd.GetKeyForConflict(ctx, cnfRow)
   172  
   173  	if err != nil {
   174  		return err
   175  	}
   176  
   177  	cd.pks = append(cd.pks, pkVal)
   178  	return nil
   179  }
   180  
   181  // StatementBegin implements the interface sql.TableEditor. Currently a no-op.
   182  func (cd *conflictDeleter) StatementBegin(ctx *sql.Context) {}
   183  
   184  // DiscardChanges implements the interface sql.TableEditor. Currently a no-op.
   185  func (cd *conflictDeleter) DiscardChanges(ctx *sql.Context, errorEncountered error) error {
   186  	return nil
   187  }
   188  
   189  // StatementComplete implements the interface sql.TableEditor. Currently a no-op.
   190  func (cd *conflictDeleter) StatementComplete(ctx *sql.Context) error {
   191  	return nil
   192  }
   193  
   194  // Close finalizes the delete operation, persisting the result.
   195  func (cd *conflictDeleter) Close(ctx *sql.Context) error {
   196  	_, _, updatedTbl, err := cd.ct.tbl.ResolveConflicts(ctx, cd.pks)
   197  
   198  	if err != nil {
   199  		if errors.Is(err, doltdb.ErrNoConflictsResolved) {
   200  			return nil
   201  		}
   202  
   203  		return err
   204  	}
   205  
   206  	updatedRoot, err := cd.ct.root.PutTable(ctx, doltdb.TableName{Name: cd.ct.tblName}, updatedTbl)
   207  
   208  	if err != nil {
   209  		return err
   210  	}
   211  
   212  	return cd.rs.SetRoot(ctx, updatedRoot)
   213  }