mirror of
https://github.com/dolthub/dolt.git
synced 2026-04-21 02:57:46 -05:00
go/{commands, doltcore/sqle}: added schema conflict resolution
This commit is contained in:
@@ -51,8 +51,7 @@ func AutoResolveAll(ctx context.Context, dEnv *env.DoltEnv, strategy AutoResolve
|
||||
return err
|
||||
}
|
||||
|
||||
tbls, err := root.TablesInConflict(ctx)
|
||||
|
||||
tbls, err := root.GetTableNames(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -63,26 +62,77 @@ func AutoResolveAll(ctx context.Context, dEnv *env.DoltEnv, strategy AutoResolve
|
||||
// AutoResolveTables resolves all conflicts in the given tables according to the
|
||||
// given |strategy|.
|
||||
func AutoResolveTables(ctx context.Context, dEnv *env.DoltEnv, strategy AutoResolveStrategy, tbls []string) error {
|
||||
root, err := dEnv.WorkingRoot(ctx)
|
||||
ws, err := dEnv.WorkingSet(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, tblName := range tbls {
|
||||
err = ResolveTable(ctx, dEnv, root, tblName, strategy)
|
||||
// schema conflicts
|
||||
if ws.MergeActive() {
|
||||
ws, err = ResolveSchemaConflicts(ctx, dEnv.DoltDB, ws, tbls, strategy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = dEnv.UpdateWorkingSet(ctx, ws); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// data conflicts
|
||||
for _, tblName := range tbls {
|
||||
err = ResolveDataConflicts(ctx, dEnv, ws, tblName, strategy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResolveTable resolves all conflicts in the given table according to the given
|
||||
func ResolveSchemaConflicts(ctx context.Context, ddb *doltdb.DoltDB, ws *doltdb.WorkingSet, tables []string, strategy AutoResolveStrategy) (*doltdb.WorkingSet, error) {
|
||||
tblSet := set.NewStrSet(tables)
|
||||
updates := make(map[string]*doltdb.Table)
|
||||
err := ws.MergeState().IterSchemaConflicts(ctx, ddb, func(table string, conflict doltdb.SchemaConflict) error {
|
||||
if !tblSet.Contains(table) {
|
||||
return nil
|
||||
}
|
||||
ours, theirs := conflict.GetConflictingTables()
|
||||
switch strategy {
|
||||
case AutoResolveStrategyOurs:
|
||||
updates[table] = ours
|
||||
case AutoResolveStrategyTheirs:
|
||||
updates[table] = theirs
|
||||
default:
|
||||
panic("unhandled auto resolve strategy")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
root := ws.WorkingRoot()
|
||||
for name, tbl := range updates {
|
||||
if root, err = root.PutTable(ctx, name, tbl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// clear resolved schema conflicts
|
||||
var unmerged []string
|
||||
for _, tbl := range ws.MergeState().TablesWithSchemaConflicts() {
|
||||
if tblSet.Contains(tbl) {
|
||||
continue
|
||||
}
|
||||
unmerged = append(unmerged, tbl)
|
||||
}
|
||||
return ws.WithWorkingRoot(root).WithUnmergableTables(unmerged), nil
|
||||
}
|
||||
|
||||
// ResolveDataConflicts resolves all conflicts in the given table according to the given
|
||||
// |strategy|. It errors if the schema of the conflict version you are choosing
|
||||
// differs from the current schema.
|
||||
func ResolveTable(ctx context.Context, dEnv *env.DoltEnv, root *doltdb.RootValue, tblName string, strategy AutoResolveStrategy) (err error) {
|
||||
tbl, ok, err := root.GetTable(ctx, tblName)
|
||||
func ResolveDataConflicts(ctx context.Context, dEnv *env.DoltEnv, ws *doltdb.WorkingSet, tblName string, strategy AutoResolveStrategy) (err error) {
|
||||
tbl, ok, err := ws.WorkingRoot().GetTable(ctx, tblName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -92,8 +142,7 @@ func ResolveTable(ctx context.Context, dEnv *env.DoltEnv, root *doltdb.RootValue
|
||||
has, err := tbl.HasConflicts(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !has {
|
||||
} else if !has {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ func performCommit(ctx context.Context, commandStr string, args []string, dEnv *
|
||||
mergeParentCommits = parentsHeadForAmend
|
||||
}
|
||||
|
||||
pendingCommit, err := actions.GetCommitStaged(ctx, roots, ws.MergeActive(), mergeParentCommits, dEnv.DbData().Ddb, actions.CommitStagedProps{
|
||||
pendingCommit, err := actions.GetCommitStaged(ctx, roots, ws, mergeParentCommits, dEnv.DbData().Ddb, actions.CommitStagedProps{
|
||||
Message: msg,
|
||||
Date: t,
|
||||
AllowEmpty: apr.Contains(cli.AllowEmptyFlag) || apr.Contains(cli.AmendFlag),
|
||||
|
||||
@@ -205,7 +205,7 @@ func getUnmergedTableCount(ctx context.Context, ws *doltdb.WorkingSet) (int, err
|
||||
unmerged.Add(ws.MergeState().TablesWithSchemaConflicts()...)
|
||||
}
|
||||
|
||||
conflicted, err := ws.WorkingRoot().TablesInConflict(ctx)
|
||||
conflicted, err := ws.WorkingRoot().TablesWithDataConflicts(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -520,7 +520,7 @@ func executeNoFFMergeAndCommit(ctx context.Context, dEnv *env.DoltEnv, spec *mer
|
||||
return tblToStats, err
|
||||
}
|
||||
|
||||
pendingCommit, err := actions.GetCommitStaged(ctx, roots, ws.MergeActive(), mergeParentCommits, dEnv.DbData().Ddb, actions.CommitStagedProps{
|
||||
pendingCommit, err := actions.GetCommitStaged(ctx, roots, ws, mergeParentCommits, dEnv.DbData().Ddb, actions.CommitStagedProps{
|
||||
Message: msg,
|
||||
Date: spec.Date,
|
||||
AllowEmpty: spec.AllowEmpty,
|
||||
|
||||
@@ -694,7 +694,7 @@ func (root *RootValue) getTableMap(ctx context.Context) (tableMap, error) {
|
||||
return root.st.GetTablesMap(ctx, root.vrw, root.ns)
|
||||
}
|
||||
|
||||
func (root *RootValue) TablesInConflict(ctx context.Context) ([]string, error) {
|
||||
func (root *RootValue) TablesWithDataConflicts(ctx context.Context) ([]string, error) {
|
||||
names, err := root.GetTableNames(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -747,7 +747,7 @@ func (root *RootValue) TablesWithConstraintViolations(ctx context.Context) ([]st
|
||||
}
|
||||
|
||||
func (root *RootValue) HasConflicts(ctx context.Context) (bool, error) {
|
||||
cnfTbls, err := root.TablesInConflict(ctx)
|
||||
cnfTbls, err := root.TablesWithDataConflicts(ctx)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
||||
@@ -43,6 +43,11 @@ type SchemaConflict struct {
|
||||
ToFks, FromFks []ForeignKey
|
||||
ToParentSchemas map[string]schema.Schema
|
||||
FromParentSchemas map[string]schema.Schema
|
||||
toTbl, fromTbl *Table
|
||||
}
|
||||
|
||||
func (sc SchemaConflict) GetConflictingTables() (ours, theirs *Table) {
|
||||
return sc.toTbl, sc.fromTbl
|
||||
}
|
||||
|
||||
// TodoWorkingSetMeta returns an incomplete WorkingSetMeta, suitable for methods that don't have the means to construct
|
||||
|
||||
@@ -248,7 +248,7 @@ func (mr *MultiRepoTestSetup) CommitWithWorkingSet(dbName string) *doltdb.Commit
|
||||
if err != nil {
|
||||
panic("couldn't get roots: " + err.Error())
|
||||
}
|
||||
pendingCommit, err := actions.GetCommitStaged(ctx, roots, ws.MergeActive(), mergeParentCommits, dEnv.DbData().Ddb, actions.CommitStagedProps{
|
||||
pendingCommit, err := actions.GetCommitStaged(ctx, roots, ws, mergeParentCommits, dEnv.DbData().Ddb, actions.CommitStagedProps{
|
||||
Message: "auto commit",
|
||||
Date: t,
|
||||
AllowEmpty: true,
|
||||
|
||||
+10
-3
@@ -37,7 +37,7 @@ type CommitStagedProps struct {
|
||||
func GetCommitStaged(
|
||||
ctx context.Context,
|
||||
roots doltdb.Roots,
|
||||
mergeActive bool,
|
||||
ws *doltdb.WorkingSet,
|
||||
mergeParents []*doltdb.Commit,
|
||||
db *doltdb.DoltDB,
|
||||
props CommitStagedProps,
|
||||
@@ -61,13 +61,13 @@ func GetCommitStaged(
|
||||
}
|
||||
|
||||
isEmpty := len(staged) == 0
|
||||
allowEmpty := mergeActive || props.AllowEmpty || props.Amend
|
||||
allowEmpty := ws.MergeActive() || props.AllowEmpty || props.Amend
|
||||
if isEmpty && !allowEmpty {
|
||||
return nil, NothingStaged{notStaged}
|
||||
}
|
||||
|
||||
if !props.Force {
|
||||
inConflict, err := roots.Working.TablesInConflict(ctx)
|
||||
inConflict, err := roots.Working.TablesWithDataConflicts(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -81,6 +81,13 @@ func GetCommitStaged(
|
||||
if len(violatesConstraints) > 0 {
|
||||
return nil, NewTblHasConstraintViolations(violatesConstraints)
|
||||
}
|
||||
|
||||
if ws.MergeActive() {
|
||||
schConflicts := ws.MergeState().TablesWithSchemaConflicts()
|
||||
if len(schConflicts) > 0 {
|
||||
return nil, NewTblSchemaConflictError(schConflicts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !props.Force {
|
||||
|
||||
+9
-4
@@ -23,10 +23,11 @@ import (
|
||||
type tblErrorType string
|
||||
|
||||
const (
|
||||
tblErrInvalid tblErrorType = "invalid"
|
||||
tblErrTypeNotExist tblErrorType = "do not exist"
|
||||
tblErrTypeInConflict tblErrorType = "are in conflict"
|
||||
tblErrTypeConstViols tblErrorType = "have constraint violations"
|
||||
tblErrInvalid tblErrorType = "invalid"
|
||||
tblErrTypeNotExist tblErrorType = "do not exist"
|
||||
tblErrTypeInConflict tblErrorType = "are in conflict"
|
||||
tblErrTypeSchConflict tblErrorType = "have schema conflicts"
|
||||
tblErrTypeConstViols tblErrorType = "have constraint violations"
|
||||
)
|
||||
|
||||
type TblError struct {
|
||||
@@ -42,6 +43,10 @@ func NewTblInConflictError(tbls []string) TblError {
|
||||
return TblError{tbls, tblErrTypeInConflict}
|
||||
}
|
||||
|
||||
func NewTblSchemaConflictError(tbls []string) TblError {
|
||||
return TblError{tbls, tblErrTypeSchConflict}
|
||||
}
|
||||
|
||||
func NewTblHasConstraintViolations(tbls []string) TblError {
|
||||
return TblError{tbls, tblErrTypeConstViols}
|
||||
}
|
||||
|
||||
-10
@@ -788,16 +788,6 @@ func (dEnv *DoltEnv) NewWorkingSetMeta(message string) *datas.WorkingSetMeta {
|
||||
}
|
||||
}
|
||||
|
||||
func (dEnv *DoltEnv) GetTablesWithConflicts(ctx context.Context) ([]string, error) {
|
||||
root, err := dEnv.WorkingRoot(ctx)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return root.TablesInConflict(ctx)
|
||||
}
|
||||
|
||||
func (dEnv *DoltEnv) CredsDir() (string, error) {
|
||||
return getCredsDir(dEnv.hdp)
|
||||
}
|
||||
|
||||
@@ -395,7 +395,7 @@ func GetMergeArtifactStatus(ctx context.Context, working *doltdb.WorkingSet) (as
|
||||
as.SchemaConflictsTables = working.MergeState().TablesWithSchemaConflicts()
|
||||
}
|
||||
|
||||
as.DataConflictTables, err = working.WorkingRoot().TablesInConflict(ctx)
|
||||
as.DataConflictTables, err = working.WorkingRoot().TablesWithDataConflicts(ctx)
|
||||
if err != nil {
|
||||
return as, err
|
||||
}
|
||||
|
||||
@@ -339,7 +339,48 @@ func clearTableAndUpdateRoot(ctx *sql.Context, root *doltdb.RootValue, tbl *dolt
|
||||
return newRoot, nil
|
||||
}
|
||||
|
||||
func ResolveConflicts(ctx *sql.Context, dSess *dsess.DoltSession, root *doltdb.RootValue, dbName string, ours bool, tblNames []string) error {
|
||||
func ResolveSchemaConflicts(ctx *sql.Context, ddb *doltdb.DoltDB, ws *doltdb.WorkingSet, resolveOurs bool, tables []string) (*doltdb.WorkingSet, error) {
|
||||
if !ws.MergeActive() {
|
||||
return ws, nil // no schema conflicts
|
||||
}
|
||||
|
||||
tblSet := set.NewStrSet(tables)
|
||||
updates := make(map[string]*doltdb.Table)
|
||||
err := ws.MergeState().IterSchemaConflicts(ctx, ddb, func(table string, conflict doltdb.SchemaConflict) error {
|
||||
if !tblSet.Contains(table) {
|
||||
return nil
|
||||
}
|
||||
ours, theirs := conflict.GetConflictingTables()
|
||||
if resolveOurs {
|
||||
updates[table] = ours
|
||||
} else {
|
||||
updates[table] = theirs
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
root := ws.WorkingRoot()
|
||||
for name, tbl := range updates {
|
||||
if root, err = root.PutTable(ctx, name, tbl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// clear resolved schema conflicts
|
||||
var unmerged []string
|
||||
for _, tbl := range ws.MergeState().TablesWithSchemaConflicts() {
|
||||
if tblSet.Contains(tbl) {
|
||||
continue
|
||||
}
|
||||
unmerged = append(unmerged, tbl)
|
||||
}
|
||||
return ws.WithWorkingRoot(root).WithUnmergableTables(unmerged), nil
|
||||
}
|
||||
|
||||
func ResolveDataConflicts(ctx *sql.Context, dSess *dsess.DoltSession, root *doltdb.RootValue, dbName string, ours bool, tblNames []string) error {
|
||||
for _, tblName := range tblNames {
|
||||
tbl, ok, err := root.GetTable(ctx, tblName)
|
||||
if err != nil {
|
||||
@@ -352,7 +393,7 @@ func ResolveConflicts(ctx *sql.Context, dSess *dsess.DoltSession, root *doltdb.R
|
||||
if has, err := tbl.HasConflicts(ctx); err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return nil
|
||||
continue
|
||||
}
|
||||
|
||||
sch, err := tbl.GetSchema(ctx)
|
||||
@@ -413,9 +454,14 @@ func DoDoltConflictsResolve(ctx *sql.Context, args []string) (int, error) {
|
||||
}
|
||||
|
||||
dSess := dsess.DSessFromSess(ctx.Session)
|
||||
roots, ok := dSess.GetRoots(ctx, dbName)
|
||||
if !ok {
|
||||
return 1, fmt.Errorf("Could not load database %s", dbName)
|
||||
ws, err := dSess.WorkingSet(ctx, dbName)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ddb, _ := dSess.GetDoltDB(ctx, dbName)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ours := apr.Contains(cli.OursFlag)
|
||||
@@ -431,17 +477,21 @@ func DoDoltConflictsResolve(ctx *sql.Context, args []string) (int, error) {
|
||||
}
|
||||
|
||||
// get all tables in conflict
|
||||
root := roots.Working
|
||||
tbls := apr.Args
|
||||
if len(tbls) == 1 && tbls[0] == "." {
|
||||
if allTables, err := root.TablesInConflict(ctx); err != nil {
|
||||
return 1, err
|
||||
} else {
|
||||
tbls = allTables
|
||||
all, err := ws.WorkingRoot().GetTableNames(ctx)
|
||||
if err != nil {
|
||||
return 1, nil
|
||||
}
|
||||
tbls = all
|
||||
}
|
||||
|
||||
err = ResolveConflicts(ctx, dSess, root, dbName, ours, tbls)
|
||||
ws, err = ResolveSchemaConflicts(ctx, ddb, ws, ours, tbls)
|
||||
if err != nil {
|
||||
return 1, err
|
||||
}
|
||||
|
||||
err = ResolveDataConflicts(ctx, dSess, ws.WorkingRoot(), dbName, ours, tbls)
|
||||
if err != nil {
|
||||
return 1, err
|
||||
}
|
||||
|
||||
@@ -586,7 +586,7 @@ func (d *DoltSession) NewPendingCommit(ctx *sql.Context, dbName string, roots do
|
||||
roots.Head = newRoots.Head
|
||||
}
|
||||
|
||||
pendingCommit, err := actions.GetCommitStaged(ctx, roots, sessionState.WorkingSet.MergeActive(), mergeParentCommits, sessionState.dbData.Ddb, props)
|
||||
pendingCommit, err := actions.GetCommitStaged(ctx, roots, sessionState.WorkingSet, mergeParentCommits, sessionState.dbData.Ddb, props)
|
||||
if err != nil {
|
||||
if props.Amend {
|
||||
_, err = actions.ResetSoftToRef(ctx, sessionState.dbData, headHash.String())
|
||||
|
||||
@@ -88,7 +88,7 @@ type MergeStatusIter struct {
|
||||
func newMergeStatusItr(ctx context.Context, ws *doltdb.WorkingSet) (*MergeStatusIter, error) {
|
||||
wr := ws.WorkingRoot()
|
||||
|
||||
inConflict, err := wr.TablesInConflict(ctx)
|
||||
inConflict, err := wr.TablesWithDataConflicts(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -98,8 +98,14 @@ func newMergeStatusItr(ctx context.Context, ws *doltdb.WorkingSet) (*MergeStatus
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var schConflicts []string
|
||||
if ws.MergeActive() {
|
||||
schConflicts = ws.MergeState().TablesWithSchemaConflicts()
|
||||
}
|
||||
|
||||
unmergedTblNames := set.NewStrSet(inConflict)
|
||||
unmergedTblNames.Add(tblsWithViolations...)
|
||||
unmergedTblNames.Add(schConflicts...)
|
||||
|
||||
var sourceCommitSpecStr *string
|
||||
var sourceCommitHash *string
|
||||
|
||||
@@ -124,7 +124,7 @@ func newStatusItr(ctx *sql.Context, st *StatusTable) (*StatusItr, error) {
|
||||
}
|
||||
}
|
||||
|
||||
cnfTables, err := roots.Working.TablesInConflict(ctx)
|
||||
cnfTables, err := roots.Working.TablesWithDataConflicts(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -122,11 +122,16 @@ func (dt *TableOfTablesInConflict) Partitions(ctx *sql.Context) (sql.PartitionIt
|
||||
}
|
||||
|
||||
root := ws.WorkingRoot()
|
||||
tblNames, err := root.TablesInConflict(ctx)
|
||||
tblNames, err := root.TablesWithDataConflicts(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ws.MergeActive() {
|
||||
schConflicts := ws.MergeState().TablesWithSchemaConflicts()
|
||||
tblNames = append(tblNames, schConflicts...)
|
||||
}
|
||||
|
||||
var partitions []*tableInConflict
|
||||
for _, tblName := range tblNames {
|
||||
tbl, ok, err := root.GetTable(ctx, tblName)
|
||||
|
||||
@@ -493,9 +493,6 @@ teardown() {
|
||||
dolt commit -m "added conflicting test row"
|
||||
dolt checkout main
|
||||
dolt merge test-branch
|
||||
run dolt checkout test
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$output" = "" ]
|
||||
run dolt sql -q "select * from test"
|
||||
[[ "$output" =~ \|[[:space:]]+5 ]] || false
|
||||
run dolt conflicts cat test
|
||||
|
||||
Reference in New Issue
Block a user