github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/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  
    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/merge"
    24  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
    25  	"github.com/dolthub/dolt/go/store/types"
    26  )
    27  
    28  var _ sql.Table = ConflictsTable{}
    29  
    30  // ConflictsTable is a sql.Table implementation that provides access to the conflicts that exist for a user table
    31  type ConflictsTable struct {
    32  	tblName string
    33  	sqlSch  sql.Schema
    34  	root    *doltdb.RootValue
    35  	tbl     *doltdb.Table
    36  	rd      *merge.ConflictReader
    37  	rs      RootSetter
    38  }
    39  
    40  type RootSetter interface {
    41  	SetRoot(ctx *sql.Context, root *doltdb.RootValue) error
    42  }
    43  
    44  // NewConflictsTable returns a new ConflictsTableTable instance
    45  func NewConflictsTable(ctx *sql.Context, tblName string, root *doltdb.RootValue, rs RootSetter) (sql.Table, error) {
    46  	tbl, ok, err := root.GetTable(ctx, tblName)
    47  
    48  	if err != nil {
    49  		return nil, err
    50  	} else if !ok {
    51  		return nil, sql.ErrTableNotFound.New(tblName)
    52  	}
    53  
    54  	rd, err := merge.NewConflictReader(ctx, tbl)
    55  
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	sqlSch, err := sqlutil.FromDoltSchema(doltdb.DoltConfTablePrefix+tblName, rd.GetSchema())
    61  
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	return ConflictsTable{
    67  		tblName: tblName,
    68  		sqlSch:  sqlSch,
    69  		root:    root,
    70  		tbl:     tbl,
    71  		rd:      rd,
    72  		rs:      rs,
    73  	}, nil
    74  }
    75  
    76  // Name returns the name of the table
    77  func (ct ConflictsTable) Name() string {
    78  	return doltdb.DoltConfTablePrefix + ct.tblName
    79  }
    80  
    81  // String returns a string identifying the table
    82  func (ct ConflictsTable) String() string {
    83  	return doltdb.DoltConfTablePrefix + ct.tblName
    84  }
    85  
    86  // Schema returns the sql.Schema of the table
    87  func (ct ConflictsTable) Schema() sql.Schema {
    88  	return ct.sqlSch
    89  }
    90  
    91  // Partitions returns a PartitionIter which can be used to get all the data partitions
    92  func (ct ConflictsTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) {
    93  	return sqlutil.NewSinglePartitionIter(types.Map{}), nil
    94  }
    95  
    96  // PartitionRows returns a RowIter for the given partition
    97  func (ct ConflictsTable) PartitionRows(ctx *sql.Context, part sql.Partition) (sql.RowIter, error) {
    98  	return conflictRowIter{ctx, ct.rd}, nil
    99  }
   100  
   101  // Deleter returns a RowDeleter for this table. The RowDeleter will get one call to Delete for each row to be deleted,
   102  // and will end with a call to Close() to finalize the delete operation.
   103  func (ct ConflictsTable) Deleter(*sql.Context) sql.RowDeleter {
   104  	return &conflictDeleter{ct: ct, rs: ct.rs}
   105  }
   106  
   107  type conflictRowIter struct {
   108  	ctx *sql.Context
   109  	rd  *merge.ConflictReader
   110  }
   111  
   112  // Next retrieves the next row. It will return io.EOF if it's the last row.
   113  // After retrieving the last row, Close will be automatically closed.
   114  func (itr conflictRowIter) Next() (sql.Row, error) {
   115  	cnf, _, err := itr.rd.NextConflict(itr.ctx)
   116  
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	return sqlutil.DoltRowToSqlRow(cnf, itr.rd.GetSchema())
   122  }
   123  
   124  // Close the iterator.
   125  func (itr conflictRowIter) Close(*sql.Context) error {
   126  	return itr.rd.Close()
   127  }
   128  
   129  var _ sql.RowDeleter = &conflictDeleter{}
   130  
   131  type conflictDeleter struct {
   132  	ct  ConflictsTable
   133  	rs  RootSetter
   134  	pks []types.Value
   135  }
   136  
   137  // Delete deletes the given row. Returns ErrDeleteRowNotFound if the row was not found. Delete will be called once for
   138  // each row to process for the delete operation, which may involve many rows. After all rows have been processed,
   139  // Close is called.
   140  func (cd *conflictDeleter) Delete(ctx *sql.Context, r sql.Row) error {
   141  	cnfSch := cd.ct.rd.GetSchema()
   142  	// We could use a test VRW, but as any values which use VRWs will already exist, we can potentially save on memory usage
   143  	cnfRow, err := sqlutil.SqlRowToDoltRow(ctx, cd.ct.tbl.ValueReadWriter(), r, cnfSch)
   144  
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	pkVal, err := cd.ct.rd.GetKeyForConflict(ctx, cnfRow)
   150  
   151  	if err != nil {
   152  		return err
   153  	}
   154  
   155  	cd.pks = append(cd.pks, pkVal)
   156  	return nil
   157  }
   158  
   159  // StatementBegin implements the interface sql.TableEditor. Currently a no-op.
   160  func (cd *conflictDeleter) StatementBegin(ctx *sql.Context) {}
   161  
   162  // DiscardChanges implements the interface sql.TableEditor. Currently a no-op.
   163  func (cd *conflictDeleter) DiscardChanges(ctx *sql.Context, errorEncountered error) error {
   164  	return nil
   165  }
   166  
   167  // StatementComplete implements the interface sql.TableEditor. Currently a no-op.
   168  func (cd *conflictDeleter) StatementComplete(ctx *sql.Context) error {
   169  	return nil
   170  }
   171  
   172  // Close finalizes the delete operation, persisting the result.
   173  func (cd *conflictDeleter) Close(ctx *sql.Context) error {
   174  	_, _, updatedTbl, err := cd.ct.tbl.ResolveConflicts(ctx, cd.pks)
   175  
   176  	if err != nil {
   177  		if errors.Is(err, doltdb.ErrNoConflictsResolved) {
   178  			return nil
   179  		}
   180  
   181  		return err
   182  	}
   183  
   184  	updatedRoot, err := cd.ct.root.PutTable(ctx, cd.ct.tblName, updatedTbl)
   185  
   186  	if err != nil {
   187  		return err
   188  	}
   189  
   190  	return cd.rs.SetRoot(ctx, updatedRoot)
   191  }