github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/dtables/conflicts_tables_prolly.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 dtables
    16  
    17  import (
    18  	"context"
    19  	"encoding/base64"
    20  	"fmt"
    21  
    22  	"github.com/dolthub/go-mysql-server/sql"
    23  	"github.com/zeebo/xxh3"
    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/merge"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    29  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
    30  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
    31  	"github.com/dolthub/dolt/go/store/hash"
    32  	"github.com/dolthub/dolt/go/store/pool"
    33  	"github.com/dolthub/dolt/go/store/prolly"
    34  	"github.com/dolthub/dolt/go/store/prolly/tree"
    35  	"github.com/dolthub/dolt/go/store/types"
    36  	"github.com/dolthub/dolt/go/store/val"
    37  )
    38  
    39  func newProllyConflictsTable(ctx *sql.Context, tbl *doltdb.Table, sourceUpdatableTbl sql.UpdatableTable, tblName string, root doltdb.RootValue, rs RootSetter) (sql.Table, error) {
    40  	arts, err := tbl.GetArtifacts(ctx)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	m := durable.ProllyMapFromArtifactIndex(arts)
    45  
    46  	baseSch, ourSch, theirSch, err := tbl.GetConflictSchemas(ctx, tblName)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	confSch, versionMappings, err := calculateConflictSchema(baseSch, ourSch, theirSch)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	sqlSch, err := sqlutil.FromDoltSchema("", doltdb.DoltConfTablePrefix+tblName, confSch)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	return ProllyConflictsTable{
    60  		tblName:         tblName,
    61  		sqlSch:          sqlSch,
    62  		baseSch:         baseSch,
    63  		ourSch:          ourSch,
    64  		theirSch:        theirSch,
    65  		root:            root,
    66  		tbl:             tbl,
    67  		rs:              rs,
    68  		artM:            m,
    69  		sqlTable:        sourceUpdatableTbl,
    70  		versionMappings: versionMappings,
    71  	}, nil
    72  }
    73  
    74  // ProllyConflictsTable is a sql.Table implementation that uses the merge
    75  // artifacts table to persist and read conflicts.
    76  type ProllyConflictsTable struct {
    77  	tblName                   string
    78  	sqlSch                    sql.PrimaryKeySchema
    79  	baseSch, ourSch, theirSch schema.Schema
    80  	root                      doltdb.RootValue
    81  	tbl                       *doltdb.Table
    82  	rs                        RootSetter
    83  	artM                      prolly.ArtifactMap
    84  	sqlTable                  sql.UpdatableTable
    85  	versionMappings           *versionMappings
    86  }
    87  
    88  var _ sql.UpdatableTable = ProllyConflictsTable{}
    89  var _ sql.DeletableTable = ProllyConflictsTable{}
    90  
    91  func (ct ProllyConflictsTable) Name() string {
    92  	return doltdb.DoltConfTablePrefix + ct.tblName
    93  }
    94  
    95  func (ct ProllyConflictsTable) String() string {
    96  	return doltdb.DoltConfTablePrefix + ct.tblName
    97  }
    98  
    99  func (ct ProllyConflictsTable) Schema() sql.Schema {
   100  	return ct.sqlSch.Schema
   101  }
   102  
   103  func (ct ProllyConflictsTable) Collation() sql.CollationID {
   104  	return sql.Collation_Default
   105  }
   106  
   107  func (ct ProllyConflictsTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) {
   108  	return index.SinglePartitionIterFromNomsMap(nil), nil
   109  }
   110  
   111  func (ct ProllyConflictsTable) PartitionRows(ctx *sql.Context, part sql.Partition) (sql.RowIter, error) {
   112  	return newProllyConflictRowIter(ctx, ct)
   113  }
   114  
   115  func (ct ProllyConflictsTable) Updater(ctx *sql.Context) sql.RowUpdater {
   116  	ourUpdater := ct.sqlTable.Updater(ctx)
   117  	return newProllyConflictOurTableUpdater(ourUpdater, ct.versionMappings, ct.baseSch, ct.ourSch, ct.theirSch)
   118  }
   119  
   120  func (ct ProllyConflictsTable) Deleter(ctx *sql.Context) sql.RowDeleter {
   121  	return newProllyConflictDeleter(ct)
   122  }
   123  
   124  type prollyConflictRowIter struct {
   125  	itr     prolly.ConflictArtifactIter
   126  	tblName string
   127  	vrw     types.ValueReadWriter
   128  	ns      tree.NodeStore
   129  	ourRows prolly.Map
   130  	keyless bool
   131  	ourSch  schema.Schema
   132  
   133  	kd                       val.TupleDesc
   134  	baseVD, oursVD, theirsVD val.TupleDesc
   135  	// offsets for each version
   136  	b, o, t int
   137  	n       int
   138  
   139  	baseHash, theirHash hash.Hash
   140  	baseRows            prolly.Map
   141  	theirRows           prolly.Map
   142  }
   143  
   144  var _ sql.RowIter = (*prollyConflictRowIter)(nil)
   145  
   146  // base_cols, our_cols, our_diff_type, their_cols, their_diff_type
   147  func newProllyConflictRowIter(ctx *sql.Context, ct ProllyConflictsTable) (*prollyConflictRowIter, error) {
   148  	idx, err := ct.tbl.GetRowData(ctx)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	ourRows := durable.ProllyMapFromIndex(idx)
   153  
   154  	itr, err := ct.artM.IterAllConflicts(ctx)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  
   159  	keyless := schema.IsKeyless(ct.ourSch)
   160  
   161  	kd := ct.baseSch.GetKeyDescriptor()
   162  	baseVD := ct.baseSch.GetValueDescriptor()
   163  	oursVD := ct.ourSch.GetValueDescriptor()
   164  	theirsVD := ct.theirSch.GetValueDescriptor()
   165  
   166  	b := 1
   167  	var o, t, n int
   168  	if !keyless {
   169  		o = b + kd.Count() + baseVD.Count()
   170  		t = o + kd.Count() + oursVD.Count() + 1
   171  		n = t + kd.Count() + theirsVD.Count() + 2
   172  	} else {
   173  		o = b + baseVD.Count() - 1
   174  		t = o + oursVD.Count()
   175  		n = t + theirsVD.Count() + 4
   176  	}
   177  
   178  	return &prollyConflictRowIter{
   179  		itr:      itr,
   180  		tblName:  ct.tblName,
   181  		vrw:      ct.tbl.ValueReadWriter(),
   182  		ns:       ct.tbl.NodeStore(),
   183  		ourRows:  ourRows,
   184  		keyless:  keyless,
   185  		ourSch:   ct.ourSch,
   186  		kd:       kd,
   187  		baseVD:   baseVD,
   188  		oursVD:   oursVD,
   189  		theirsVD: theirsVD,
   190  		b:        b,
   191  		o:        o,
   192  		t:        t,
   193  		n:        n,
   194  	}, nil
   195  }
   196  
   197  func (itr *prollyConflictRowIter) Next(ctx *sql.Context) (sql.Row, error) {
   198  	c, err := itr.nextConflictVals(ctx)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	r := make(sql.Row, itr.n)
   204  	r[0] = c.h.String()
   205  
   206  	if !itr.keyless {
   207  		for i := 0; i < itr.kd.Count(); i++ {
   208  			f, err := tree.GetField(ctx, itr.kd, i, c.k, itr.baseRows.NodeStore())
   209  			if err != nil {
   210  				return nil, err
   211  			}
   212  			if c.bV != nil {
   213  				r[itr.b+i] = f
   214  			}
   215  			if c.oV != nil {
   216  				r[itr.o+i] = f
   217  			}
   218  			if c.tV != nil {
   219  				r[itr.t+i] = f
   220  			}
   221  		}
   222  
   223  		err = itr.putConflictRowVals(ctx, c, r)
   224  		if err != nil {
   225  			return nil, err
   226  		}
   227  	} else {
   228  
   229  		err = itr.putKeylessConflictRowVals(ctx, c, r)
   230  		if err != nil {
   231  			return nil, err
   232  		}
   233  	}
   234  
   235  	return r, nil
   236  }
   237  
   238  func (itr *prollyConflictRowIter) putConflictRowVals(ctx *sql.Context, c conf, r sql.Row) error {
   239  	if c.bV != nil {
   240  		for i := 0; i < itr.baseVD.Count(); i++ {
   241  			f, err := tree.GetField(ctx, itr.baseVD, i, c.bV, itr.baseRows.NodeStore())
   242  			if err != nil {
   243  				return err
   244  			}
   245  			r[itr.b+itr.kd.Count()+i] = f
   246  		}
   247  	}
   248  
   249  	if c.oV != nil {
   250  		for i := 0; i < itr.oursVD.Count(); i++ {
   251  			f, err := tree.GetField(ctx, itr.oursVD, i, c.oV, itr.baseRows.NodeStore())
   252  			if err != nil {
   253  				return err
   254  			}
   255  			r[itr.o+itr.kd.Count()+i] = f
   256  		}
   257  	}
   258  	r[itr.o+itr.kd.Count()+itr.oursVD.Count()] = getDiffType(c.bV, c.oV)
   259  
   260  	if c.tV != nil {
   261  		for i := 0; i < itr.theirsVD.Count(); i++ {
   262  			f, err := tree.GetField(ctx, itr.theirsVD, i, c.tV, itr.baseRows.NodeStore())
   263  			if err != nil {
   264  				return err
   265  			}
   266  			r[itr.t+itr.kd.Count()+i] = f
   267  		}
   268  	}
   269  	r[itr.t+itr.kd.Count()+itr.theirsVD.Count()] = getDiffType(c.bV, c.tV)
   270  	r[itr.t+itr.kd.Count()+itr.theirsVD.Count()+1] = c.id
   271  
   272  	return nil
   273  }
   274  
   275  func getDiffType(base val.Tuple, other val.Tuple) string {
   276  	if base == nil {
   277  		return merge.ConflictDiffTypeAdded
   278  	} else if other == nil {
   279  		return merge.ConflictDiffTypeRemoved
   280  	}
   281  
   282  	// There has to be some edit, otherwise it wouldn't be a conflict...
   283  	return merge.ConflictDiffTypeModified
   284  }
   285  
   286  func (itr *prollyConflictRowIter) putKeylessConflictRowVals(ctx *sql.Context, c conf, r sql.Row) (err error) {
   287  	ns := itr.baseRows.NodeStore()
   288  
   289  	if c.bV != nil {
   290  		// Cardinality
   291  		r[itr.n-3], err = tree.GetField(ctx, itr.baseVD, 0, c.bV, ns)
   292  		if err != nil {
   293  			return err
   294  		}
   295  
   296  		for i := 0; i < itr.baseVD.Count()-1; i++ {
   297  			f, err := tree.GetField(ctx, itr.baseVD, i+1, c.bV, ns)
   298  			if err != nil {
   299  				return err
   300  			}
   301  			r[itr.b+i] = f
   302  		}
   303  	} else {
   304  		r[itr.n-3] = uint64(0)
   305  	}
   306  
   307  	if c.oV != nil {
   308  		r[itr.n-2], err = tree.GetField(ctx, itr.oursVD, 0, c.oV, ns)
   309  		if err != nil {
   310  			return err
   311  		}
   312  
   313  		for i := 0; i < itr.oursVD.Count()-1; i++ {
   314  			f, err := tree.GetField(ctx, itr.oursVD, i+1, c.oV, ns)
   315  			if err != nil {
   316  				return err
   317  			}
   318  			r[itr.o+i] = f
   319  		}
   320  	} else {
   321  		r[itr.n-2] = uint64(0)
   322  	}
   323  
   324  	r[itr.o+itr.oursVD.Count()-1] = getDiffType(c.bV, c.oV)
   325  
   326  	if c.tV != nil {
   327  		r[itr.n-1], err = tree.GetField(ctx, itr.theirsVD, 0, c.tV, ns)
   328  		if err != nil {
   329  			return err
   330  		}
   331  
   332  		for i := 0; i < itr.theirsVD.Count()-1; i++ {
   333  			f, err := tree.GetField(ctx, itr.theirsVD, i+1, c.tV, ns)
   334  			if err != nil {
   335  				return err
   336  			}
   337  			r[itr.t+i] = f
   338  		}
   339  	} else {
   340  		r[itr.n-1] = uint64(0)
   341  	}
   342  
   343  	o := itr.t + itr.theirsVD.Count() - 1
   344  	r[o] = getDiffType(c.bV, c.tV)
   345  	r[itr.n-4] = c.id
   346  
   347  	return nil
   348  }
   349  
   350  type conf struct {
   351  	k, bV, oV, tV val.Tuple
   352  	h             hash.Hash
   353  	id            string
   354  }
   355  
   356  func (itr *prollyConflictRowIter) nextConflictVals(ctx *sql.Context) (c conf, err error) {
   357  	ca, err := itr.itr.Next(ctx)
   358  	if err != nil {
   359  		return conf{}, err
   360  	}
   361  	c.k = ca.Key
   362  	c.h = ca.TheirRootIsh
   363  
   364  	// To ensure that the conflict id is unique, we hash both TheirRootIsh and the key of the table.
   365  	b := xxh3.Hash128(append(ca.Key, c.h[:]...)).Bytes()
   366  	c.id = base64.RawStdEncoding.EncodeToString(b[:])
   367  
   368  	err = itr.loadTableMaps(ctx, ca.Metadata.BaseRootIsh, ca.TheirRootIsh)
   369  	if err != nil {
   370  		return conf{}, err
   371  	}
   372  
   373  	err = itr.baseRows.Get(ctx, ca.Key, func(_, v val.Tuple) error {
   374  		c.bV = v
   375  		return nil
   376  	})
   377  	if err != nil {
   378  		return conf{}, err
   379  	}
   380  	err = itr.ourRows.Get(ctx, ca.Key, func(_, v val.Tuple) error {
   381  		c.oV = v
   382  		return nil
   383  	})
   384  	if err != nil {
   385  		return conf{}, err
   386  	}
   387  	err = itr.theirRows.Get(ctx, ca.Key, func(_, v val.Tuple) error {
   388  		c.tV = v
   389  		return nil
   390  	})
   391  	if err != nil {
   392  		return conf{}, err
   393  	}
   394  
   395  	return c, nil
   396  }
   397  
   398  // loadTableMaps loads the maps specified in the metadata if they are different from
   399  // the currently loaded maps. |baseHash| and |theirHash| are table hashes.
   400  func (itr *prollyConflictRowIter) loadTableMaps(ctx context.Context, baseHash, theirHash hash.Hash) error {
   401  	if itr.baseHash.Compare(baseHash) != 0 {
   402  		rv, err := doltdb.LoadRootValueFromRootIshAddr(ctx, itr.vrw, itr.ns, baseHash)
   403  		if err != nil {
   404  			return err
   405  		}
   406  		baseTbl, ok, err := rv.GetTable(ctx, doltdb.TableName{Name: itr.tblName})
   407  		if err != nil {
   408  			return err
   409  		}
   410  
   411  		var idx durable.Index
   412  		if !ok {
   413  			idx, err = durable.NewEmptyIndex(ctx, itr.vrw, itr.ns, itr.ourSch)
   414  		} else {
   415  			idx, err = baseTbl.GetRowData(ctx)
   416  		}
   417  
   418  		if err != nil {
   419  			return err
   420  		}
   421  
   422  		itr.baseRows = durable.ProllyMapFromIndex(idx)
   423  		itr.baseHash = baseHash
   424  	}
   425  
   426  	if itr.theirHash.Compare(theirHash) != 0 {
   427  		rv, err := doltdb.LoadRootValueFromRootIshAddr(ctx, itr.vrw, itr.ns, theirHash)
   428  		if err != nil {
   429  			return err
   430  		}
   431  		theirTbl, ok, err := rv.GetTable(ctx, doltdb.TableName{Name: itr.tblName})
   432  		if err != nil {
   433  			return err
   434  		}
   435  		if !ok {
   436  			return fmt.Errorf("failed to find table %s in right root value", itr.tblName)
   437  		}
   438  
   439  		idx, err := theirTbl.GetRowData(ctx)
   440  		if err != nil {
   441  			return err
   442  		}
   443  		itr.theirRows = durable.ProllyMapFromIndex(idx)
   444  		itr.theirHash = theirHash
   445  	}
   446  
   447  	return nil
   448  }
   449  
   450  func (itr *prollyConflictRowIter) Close(ctx *sql.Context) error {
   451  	return nil
   452  }
   453  
   454  // prollyConflictOurTableUpdater allows users to update the "our table" by
   455  // modifying rows in the conflict table. Any updates to the conflict table our
   456  // columns are applied on the source table.
   457  type prollyConflictOurTableUpdater struct {
   458  	baseSch, ourSch, theirSch schema.Schema
   459  	srcUpdater                sql.RowUpdater
   460  	versionMappings           *versionMappings
   461  	pkOrdinals                []int
   462  	schemaOK                  bool
   463  }
   464  
   465  func newProllyConflictOurTableUpdater(ourUpdater sql.RowUpdater, versionMappings *versionMappings, baseSch, ourSch, theirSch schema.Schema) *prollyConflictOurTableUpdater {
   466  	return &prollyConflictOurTableUpdater{
   467  		srcUpdater:      ourUpdater,
   468  		versionMappings: versionMappings,
   469  		pkOrdinals:      ourSch.GetPkOrdinals(),
   470  	}
   471  }
   472  
   473  // Update implements sql.RowUpdater. It translates updates on the conflict table to the source table.
   474  func (cu *prollyConflictOurTableUpdater) Update(ctx *sql.Context, oldRow sql.Row, newRow sql.Row) error {
   475  
   476  	// Apply updates to columns prefixed with our_
   477  	// Updates to other columns are no-ops.
   478  	ourOldRow := make(sql.Row, len(cu.versionMappings.ourMapping))
   479  	ourNewRow := make(sql.Row, len(cu.versionMappings.ourMapping))
   480  	for i, j := range cu.versionMappings.ourMapping {
   481  		ourOldRow[i] = oldRow[j]
   482  	}
   483  	for i, j := range cu.versionMappings.ourMapping {
   484  		ourNewRow[i] = newRow[j]
   485  	}
   486  
   487  	return cu.srcUpdater.Update(ctx, ourOldRow, ourNewRow)
   488  }
   489  
   490  // StatementBegin implements sql.RowUpdater.
   491  func (cu *prollyConflictOurTableUpdater) StatementBegin(ctx *sql.Context) {
   492  	cu.srcUpdater.StatementBegin(ctx)
   493  }
   494  
   495  // DiscardChanges implements sql.RowUpdater.
   496  func (cu *prollyConflictOurTableUpdater) DiscardChanges(ctx *sql.Context, errorEncountered error) error {
   497  	return cu.srcUpdater.DiscardChanges(ctx, errorEncountered)
   498  }
   499  
   500  // StatementComplete implements sql.RowUpdater.
   501  func (cu *prollyConflictOurTableUpdater) StatementComplete(ctx *sql.Context) error {
   502  	return cu.srcUpdater.StatementComplete(ctx)
   503  }
   504  
   505  // Close implements sql.RowUpdater.
   506  func (cu *prollyConflictOurTableUpdater) Close(c *sql.Context) error {
   507  	return cu.srcUpdater.Close(c)
   508  }
   509  
   510  type prollyConflictDeleter struct {
   511  	kd, vd         val.TupleDesc
   512  	kB, vB         *val.TupleBuilder
   513  	pool           pool.BuffPool
   514  	ed             *prolly.ArtifactsEditor
   515  	ct             ProllyConflictsTable
   516  	rs             RootSetter
   517  	ourDiffTypeIdx int
   518  	baseColSize    int
   519  	ourColSize     int
   520  }
   521  
   522  func newProllyConflictDeleter(ct ProllyConflictsTable) *prollyConflictDeleter {
   523  	kd, _ := ct.artM.Descriptors()
   524  	ed := ct.artM.Editor()
   525  	kB := val.NewTupleBuilder(kd)
   526  
   527  	vd := ct.ourSch.GetValueDescriptor()
   528  	vB := val.NewTupleBuilder(vd)
   529  	p := ct.artM.Pool()
   530  
   531  	baseColSize := ct.baseSch.GetAllCols().Size()
   532  	ourColSize := ct.ourSch.GetAllCols().Size()
   533  	// root_ish, base_cols..., our_cols, our_diff_type
   534  	ourDiffTypeIdx := 1 + baseColSize + ourColSize
   535  
   536  	return &prollyConflictDeleter{
   537  		kd:             kd,
   538  		vd:             vd,
   539  		kB:             kB,
   540  		vB:             vB,
   541  		pool:           p,
   542  		ed:             ed,
   543  		ct:             ct,
   544  		ourDiffTypeIdx: ourDiffTypeIdx,
   545  		baseColSize:    baseColSize,
   546  		ourColSize:     ourColSize,
   547  	}
   548  }
   549  
   550  func (cd *prollyConflictDeleter) Delete(ctx *sql.Context, r sql.Row) (err error) {
   551  	// first part of the artifact key is the keys of the source table
   552  	if !schema.IsKeyless(cd.ct.ourSch) {
   553  		err = cd.putPrimaryKeys(ctx, r)
   554  	} else {
   555  		err = cd.putKeylessHash(ctx, r)
   556  	}
   557  	if err != nil {
   558  		return err
   559  	}
   560  
   561  	// then the hash follows. It is the first column of the row and the second to last in the key
   562  	h := hash.Parse(r[0].(string))
   563  	cd.kB.PutCommitAddr(cd.kd.Count()-2, h)
   564  
   565  	// Finally the artifact type which is always a conflict
   566  	cd.kB.PutUint8(cd.kd.Count()-1, uint8(prolly.ArtifactTypeConflict))
   567  
   568  	key := cd.kB.Build(cd.pool)
   569  	err = cd.ed.Delete(ctx, key)
   570  	if err != nil {
   571  		return err
   572  	}
   573  
   574  	return nil
   575  }
   576  
   577  func (cd *prollyConflictDeleter) putPrimaryKeys(ctx *sql.Context, r sql.Row) error {
   578  	// get keys from either base, ours, or theirs
   579  	o := func() int {
   580  		if o := 1; r[o] != nil {
   581  			return o
   582  		} else if o = 1 + cd.kd.Count() - 2 + cd.vd.Count(); r[o] != nil {
   583  			return o
   584  		} else if o = 1 + (cd.kd.Count()-2+cd.vd.Count())*2 + 1; r[o] != nil {
   585  			return o
   586  		} else {
   587  			panic("neither base, ours, or theirs had a key")
   588  		}
   589  	}()
   590  
   591  	for i := 0; i < cd.kd.Count()-2; i++ {
   592  		err := tree.PutField(ctx, cd.ed.NodeStore(), cd.kB, i, r[o+i])
   593  
   594  		if err != nil {
   595  			return err
   596  		}
   597  	}
   598  
   599  	return nil
   600  }
   601  
   602  func (cd *prollyConflictDeleter) putKeylessHash(ctx *sql.Context, r sql.Row) error {
   603  	var rowVals sql.Row
   604  	if r[cd.ourDiffTypeIdx] == merge.ConflictDiffTypeAdded {
   605  		// use our cols
   606  		rowVals = r[1+cd.baseColSize : 1+cd.baseColSize+cd.ourColSize]
   607  	} else {
   608  		// use base cols
   609  		rowVals = r[1 : 1+cd.baseColSize]
   610  	}
   611  
   612  	// init cardinality to 0
   613  	cd.vB.PutUint64(0, 0)
   614  	for i, v := range rowVals {
   615  		err := tree.PutField(ctx, cd.ed.NodeStore(), cd.vB, i+1, v)
   616  		if err != nil {
   617  			return err
   618  		}
   619  	}
   620  
   621  	v := cd.vB.Build(cd.pool)
   622  	k := val.HashTupleFromValue(cd.pool, v)
   623  	cd.kB.PutHash128(0, k.GetField(0))
   624  	return nil
   625  }
   626  
   627  // StatementBegin implements the interface sql.TableEditor. Currently a no-op.
   628  func (cd *prollyConflictDeleter) StatementBegin(ctx *sql.Context) {}
   629  
   630  // DiscardChanges implements the interface sql.TableEditor. Currently a no-op.
   631  func (cd *prollyConflictDeleter) DiscardChanges(ctx *sql.Context, errorEncountered error) error {
   632  	return nil
   633  }
   634  
   635  // StatementComplete implements the interface sql.TableEditor. Currently a no-op.
   636  func (cd *prollyConflictDeleter) StatementComplete(ctx *sql.Context) error {
   637  	return nil
   638  }
   639  
   640  // Close finalizes the delete operation, persisting the result.
   641  func (cd *prollyConflictDeleter) Close(ctx *sql.Context) error {
   642  	arts, err := cd.ed.Flush(ctx)
   643  	if err != nil {
   644  		return err
   645  	}
   646  
   647  	// TODO: We can delete from more than one table in a single statement. Root
   648  	// updates should be restricted to write session and not individual table
   649  	// editors.
   650  
   651  	updatedTbl, err := cd.ct.tbl.SetArtifacts(ctx, durable.ArtifactIndexFromProllyMap(arts))
   652  	if err != nil {
   653  		return err
   654  	}
   655  
   656  	updatedRoot, err := cd.ct.root.PutTable(ctx, doltdb.TableName{Name: cd.ct.tblName}, updatedTbl)
   657  	if err != nil {
   658  		return err
   659  	}
   660  
   661  	return cd.ct.rs.SetRoot(ctx, updatedRoot)
   662  }
   663  
   664  type versionMappings struct {
   665  	ourMapping, theirMapping, baseMapping val.OrdinalMapping
   666  }
   667  
   668  // returns the schema of the rows returned by the conflicts table and a mappings between each version and the source table.
   669  func calculateConflictSchema(base, ours, theirs schema.Schema) (schema.Schema, *versionMappings, error) {
   670  	keyless := schema.IsKeyless(ours)
   671  	n := 4 + ours.GetAllCols().Size() + theirs.GetAllCols().Size() + base.GetAllCols().Size()
   672  	if keyless {
   673  		n += 3
   674  	}
   675  
   676  	cols := make([]schema.Column, n)
   677  
   678  	// the commit hash or working set hash of the right side during merge
   679  	cols[0] = schema.NewColumn("from_root_ish", 0, types.StringKind, false)
   680  
   681  	i := 1
   682  	putWithPrefix := func(prefix string, sch schema.Schema, stripConstraints bool) (val.OrdinalMapping, error) {
   683  		allCols := sch.GetAllCols()
   684  		mapping := make(val.OrdinalMapping, allCols.Size())
   685  		err := sch.GetPKCols().Iter(func(tag uint64, col schema.Column) (stop bool, err error) {
   686  			var cons []schema.ColConstraint
   687  			if !stripConstraints {
   688  				cons = col.Constraints
   689  			}
   690  			c, err := schema.NewColumnWithTypeInfo(prefix+col.Name, uint64(i), col.TypeInfo, false, col.Default, false, col.Comment, cons...)
   691  			if err != nil {
   692  				return true, err
   693  			}
   694  			cols[i] = c
   695  			mapping[allCols.TagToIdx[tag]] = i
   696  			i++
   697  			return false, nil
   698  		})
   699  		if err != nil {
   700  			return nil, err
   701  		}
   702  		err = sch.GetNonPKCols().Iter(func(tag uint64, col schema.Column) (stop bool, err error) {
   703  			var cons []schema.ColConstraint
   704  			if !stripConstraints {
   705  				cons = col.Constraints
   706  			}
   707  			c, err := schema.NewColumnWithTypeInfo(prefix+col.Name, uint64(i), col.TypeInfo, false, col.Default, false, col.Comment, cons...)
   708  			if err != nil {
   709  				return true, err
   710  			}
   711  			cols[i] = c
   712  			mapping[allCols.TagToIdx[tag]] = i
   713  			i++
   714  			return false, nil
   715  		})
   716  		return mapping, err
   717  	}
   718  
   719  	baseColMapping, err := putWithPrefix("base_", base, true)
   720  	if err != nil {
   721  		return nil, nil, err
   722  	}
   723  	ourColMapping, err := putWithPrefix("our_", ours, false)
   724  	if err != nil {
   725  		return nil, nil, err
   726  	}
   727  	cols[i] = schema.NewColumn("our_diff_type", uint64(i), types.StringKind, false)
   728  	i++
   729  	theirColMapping, err := putWithPrefix("their_", theirs, true)
   730  	if err != nil {
   731  		return nil, nil, err
   732  	}
   733  	cols[i] = schema.NewColumn("their_diff_type", uint64(i), types.StringKind, false)
   734  	i++
   735  
   736  	cols[i] = schema.NewColumn("dolt_conflict_id", uint64(i), types.StringKind, false)
   737  	i++
   738  
   739  	if keyless {
   740  		cols[i] = schema.NewColumn("base_cardinality", uint64(i), types.UintKind, false)
   741  		i++
   742  		cols[i] = schema.NewColumn("our_cardinality", uint64(i), types.UintKind, false)
   743  		i++
   744  		cols[i] = schema.NewColumn("their_cardinality", uint64(i), types.UintKind, false)
   745  		i++
   746  	}
   747  
   748  	sch, err := schema.NewSchema(schema.NewColCollection(cols...), nil, schema.Collation_Default, nil, nil)
   749  	if err != nil {
   750  		return nil, nil, err
   751  	}
   752  
   753  	return sch,
   754  		&versionMappings{
   755  			ourMapping:   ourColMapping,
   756  			theirMapping: theirColMapping,
   757  			baseMapping:  baseColMapping},
   758  		nil
   759  }