diff --git a/.github/workflows/ci-bats-macos.yaml b/.github/workflows/ci-bats-macos.yaml index 7203d47a38b..e77b5c6aa4e 100644 --- a/.github/workflows/ci-bats-macos.yaml +++ b/.github/workflows/ci-bats-macos.yaml @@ -85,9 +85,9 @@ jobs: - name: Install Maven working-directory: ./.ci_bin run: | - curl -LO https://dlcdn.apache.org/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz - tar -xf apache-maven-3.9.9-bin.tar.gz - echo "$(pwd)/apache-maven-3.9.9/bin" >> $GITHUB_PATH + curl -LO https://dlcdn.apache.org/maven/maven-3/3.9.10/binaries/apache-maven-3.9.10-bin.tar.gz + tar -xf apache-maven-3.9.10-bin.tar.gz + echo "$(pwd)/apache-maven-3.9.10/bin" >> $GITHUB_PATH - name: Install Hadoop working-directory: ./.ci_bin run: | diff --git a/.github/workflows/ci-bats-unix-remote.yaml b/.github/workflows/ci-bats-unix-remote.yaml index 43df98f649f..af251a590bc 100644 --- a/.github/workflows/ci-bats-unix-remote.yaml +++ b/.github/workflows/ci-bats-unix-remote.yaml @@ -78,9 +78,9 @@ jobs: if: ${{ env.use_credentials != 'true' }} working-directory: ./.ci_bin run: | - curl -LO https://dlcdn.apache.org/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz - tar -xf apache-maven-3.9.9-bin.tar.gz - echo "$(pwd)/apache-maven-3.9.9/bin" >> $GITHUB_PATH + curl -LO https://dlcdn.apache.org/maven/maven-3/3.9.10/binaries/apache-maven-3.9.10-bin.tar.gz + tar -xf apache-maven-3.9.10-bin.tar.gz + echo "$(pwd)/apache-maven-3.9.10/bin" >> $GITHUB_PATH - name: Install Hadoop if: ${{ env.use_credentials != 'true' }} working-directory: ./.ci_bin diff --git a/.github/workflows/ci-bats-unix.yaml b/.github/workflows/ci-bats-unix.yaml index 659564dc5da..1c9c19a6b68 100644 --- a/.github/workflows/ci-bats-unix.yaml +++ b/.github/workflows/ci-bats-unix.yaml @@ -91,9 +91,9 @@ jobs: - name: Install Maven working-directory: ./.ci_bin run: | - curl -LO https://dlcdn.apache.org/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz - tar -xf apache-maven-3.9.9-bin.tar.gz - echo "$(pwd)/apache-maven-3.9.9/bin" >> $GITHUB_PATH + curl -LO https://dlcdn.apache.org/maven/maven-3/3.9.10/binaries/apache-maven-3.9.10-bin.tar.gz + tar -xf apache-maven-3.9.10-bin.tar.gz + echo "$(pwd)/apache-maven-3.9.10/bin" >> $GITHUB_PATH - name: Install Hadoop working-directory: ./.ci_bin run: | diff --git a/.github/workflows/merge-perf.yaml b/.github/workflows/merge-perf.yaml index 1153ba2c12c..e48e7348fa9 100644 --- a/.github/workflows/merge-perf.yaml +++ b/.github/workflows/merge-perf.yaml @@ -77,7 +77,7 @@ jobs: TMPDIR=$gw/tmp ./${{ env.SCRIPT_DIR}}/setup.sh $TMPDIR $DATADIR - # small python script times merge, we suppres errcodes but print error messages + # small python script times merge, we suppress errcodes but print error messages cd $TMPDIR python3 -c "import time, subprocess, sys; start = time.time(); res=subprocess.run(['dolt', 'merge', '--squash', 'main'], capture_output=True); err = res.stdout + res.stderr if res.returncode != 0 else ''; latency = time.time() -start; print(latency); sys.stderr.write(str(err))" 1> lat.log 2>err.log latency=$(cat lat.log) diff --git a/go/libraries/doltcore/merge/merge_prolly_rows.go b/go/libraries/doltcore/merge/merge_prolly_rows.go index 1c58351ae2e..39a5aa9fc0a 100644 --- a/go/libraries/doltcore/merge/merge_prolly_rows.go +++ b/go/libraries/doltcore/merge/merge_prolly_rows.go @@ -78,7 +78,7 @@ func mergeProllyTable( if err != nil { return nil, nil, err } - valueMerger := newValueMerger(mergedSch, tm.leftSch, tm.rightSch, tm.ancSch, leftRows.Pool(), tm.ns) + valueMerger := NewValueMerger(mergedSch, tm.leftSch, tm.rightSch, tm.ancSch, leftRows.Pool(), tm.ns) if !valueMerger.leftMapping.IsIdentityMapping() { mergeInfo.LeftNeedsRewrite = true @@ -397,7 +397,7 @@ func threeWayDiffer(ctx context.Context, tm *TableMerger, valueMerger *valueMerg leftRows.Tuples(), rightRows.Tuples(), ancRows.Tuples(), - valueMerger.tryMerge, + valueMerger.TryMerge, valueMerger.keyless, diffInfo, leftRows.Tuples().Order, @@ -1681,7 +1681,7 @@ type valueMerger struct { ns tree.NodeStore } -func newValueMerger(merged, leftSch, rightSch, baseSch schema.Schema, syncPool pool.BuffPool, ns tree.NodeStore) *valueMerger { +func NewValueMerger(merged, leftSch, rightSch, baseSch schema.Schema, syncPool pool.BuffPool, ns tree.NodeStore) *valueMerger { leftMapping, rightMapping, baseMapping := generateSchemaMappings(merged, leftSch, rightSch, baseSch) baseToLeftMapping, baseToRightMapping, baseToResultMapping := generateSchemaMappings(baseSch, leftSch, rightSch, merged) @@ -1753,11 +1753,11 @@ func findNonPKColumnMappingByTagOrName(sch schema.Schema, col schema.Column) int } } -// tryMerge performs a cell-wise merge given left, right, and base cell value +// TryMerge performs a cell-wise merge given left, right, and base cell value // tuples. It returns the merged cell value tuple and a bool indicating if a -// conflict occurred. tryMerge should only be called if left and right produce +// conflict occurred. TryMerge should only be called if left and right produce // non-identical diffs against base. -func (m *valueMerger) tryMerge(ctx *sql.Context, left, right, base val.Tuple) (val.Tuple, bool, error) { +func (m *valueMerger) TryMerge(ctx *sql.Context, left, right, base val.Tuple) (val.Tuple, bool, error) { // If we're merging a keyless table and the keys match, but the values are different, // that means that the row data is the same, but the cardinality has changed, and if the // cardinality has changed in different ways on each merge side, we can't auto resolve. diff --git a/go/libraries/doltcore/merge/merge_rows.go b/go/libraries/doltcore/merge/merge_rows.go index 9fa93590c65..b56a85642e6 100644 --- a/go/libraries/doltcore/merge/merge_rows.go +++ b/go/libraries/doltcore/merge/merge_rows.go @@ -29,6 +29,7 @@ import ( "github.com/dolthub/dolt/go/libraries/utils/set" "github.com/dolthub/dolt/go/store/atomicerr" "github.com/dolthub/dolt/go/store/hash" + "github.com/dolthub/dolt/go/store/prolly" "github.com/dolthub/dolt/go/store/prolly/tree" "github.com/dolthub/dolt/go/store/types" ) @@ -83,6 +84,34 @@ type TableMerger struct { recordViolations bool } +func (tm TableMerger) GetNewValueMerger(mergeSch schema.Schema, leftRows prolly.Map) *valueMerger { + return NewValueMerger(mergeSch, tm.leftSch, tm.rightSch, tm.ancSch, leftRows.Pool(), leftRows.NodeStore()) +} + +func rowsFromTable(ctx context.Context, tbl *doltdb.Table) (prolly.Map, error) { + rd, err := tbl.GetRowData(ctx) + if err != nil { + return prolly.Map{}, err + } + rows, err := durable.ProllyMapFromIndex(rd) + if err != nil { + return prolly.Map{}, err + } + return rows, nil +} + +func (tm TableMerger) LeftRows(ctx context.Context) (prolly.Map, error) { + return rowsFromTable(ctx, tm.leftTbl) +} + +func (tm TableMerger) RightRows(ctx context.Context) (prolly.Map, error) { + return rowsFromTable(ctx, tm.rightTbl) +} + +func (tm TableMerger) AncRows(ctx context.Context) (prolly.Map, error) { + return rowsFromTable(ctx, tm.ancTbl) +} + func (tm TableMerger) tableHashes(ctx context.Context) (left, right, anc hash.Hash, err error) { if tm.leftTbl != nil { if left, err = tm.leftTbl.HashOf(); err != nil { @@ -114,6 +143,10 @@ func (tm TableMerger) tableHashes(ctx context.Context) (left, right, anc hash.Ha return } +func (tm TableMerger) SchemaMerge(ctx *sql.Context, tblName doltdb.TableName) (schema.Schema, SchemaConflict, MergeInfo, tree.ThreeWayDiffInfo, error) { + return SchemaMerge(ctx, tm.vrw.Format(), tm.leftSch, tm.rightSch, tm.ancSch, tblName) +} + type RootMerger struct { left doltdb.RootValue right doltdb.RootValue @@ -171,19 +204,19 @@ func (rm *RootMerger) MergeTable( opts editor.Options, mergeOpts MergeOpts, ) (*MergedResult, *MergeStats, error) { - tm, err := rm.makeTableMerger(ctx, tblName, mergeOpts) + tm, err := rm.MakeTableMerger(ctx, tblName, mergeOpts) if err != nil { return nil, nil, err } // short-circuit here if we can - finished, finishedRootObj, stats, err := rm.maybeShortCircuit(ctx, tm, mergeOpts) + finished, finishedRootObj, stats, err := rm.MaybeShortCircuit(ctx, tm, mergeOpts) if finished != nil || stats != nil || err != nil { return &MergedResult{table: finished, rootObj: finishedRootObj}, stats, err } // Calculate a merge of the schemas, but don't apply it yet - mergeSch, schConflicts, mergeInfo, diffInfo, err := SchemaMerge(ctx, tm.vrw.Format(), tm.leftSch, tm.rightSch, tm.ancSch, tblName) + mergeSch, schConflicts, mergeInfo, diffInfo, err := tm.SchemaMerge(ctx, tblName) if err != nil { return nil, nil, err } @@ -233,7 +266,7 @@ func (rm *RootMerger) MergeTable( return &MergedResult{table: tbl, rootObj: rootObj}, stats, nil } -func (rm *RootMerger) makeTableMerger(ctx context.Context, tblName doltdb.TableName, mergeOpts MergeOpts) (*TableMerger, error) { +func (rm *RootMerger) MakeTableMerger(ctx context.Context, tblName doltdb.TableName, mergeOpts MergeOpts) (*TableMerger, error) { recordViolations := true if mergeOpts.RecordViolationsForTables != nil { if _, ok := mergeOpts.RecordViolationsForTables[tblName.ToLower()]; !ok { @@ -335,7 +368,7 @@ func (rm *RootMerger) makeTableMerger(ctx context.Context, tblName doltdb.TableN return &tm, nil } -func (rm *RootMerger) maybeShortCircuit(ctx context.Context, tm *TableMerger, opts MergeOpts) (*doltdb.Table, doltdb.RootObject, *MergeStats, error) { +func (rm *RootMerger) MaybeShortCircuit(ctx context.Context, tm *TableMerger, opts MergeOpts) (*doltdb.Table, doltdb.RootObject, *MergeStats, error) { // If we need to re-verify all constraints as part of this merge, then we can't short // circuit considering any tables, so return immediately if opts.ReverifyAllConstraints { diff --git a/go/libraries/doltcore/merge/row_merge_test.go b/go/libraries/doltcore/merge/row_merge_test.go index d73cdab0748..694396e49bb 100644 --- a/go/libraries/doltcore/merge/row_merge_test.go +++ b/go/libraries/doltcore/merge/row_merge_test.go @@ -211,9 +211,9 @@ func TestRowMerge(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - v := newValueMerger(test.mergedSch, test.leftSch, test.rightSch, test.baseSch, syncPool, nil) + v := NewValueMerger(test.mergedSch, test.leftSch, test.rightSch, test.baseSch, syncPool, nil) - merged, ok, err := v.tryMerge(ctx, test.row, test.mergeRow, test.ancRow) + merged, ok, err := v.TryMerge(ctx, test.row, test.mergeRow, test.ancRow) assert.NoError(t, err) assert.Equal(t, test.expectConflict, !ok) vD := test.mergedSch.GetValueDescriptor(v.ns) diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_table_function.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff.go similarity index 100% rename from go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_table_function.go rename to go/libraries/doltcore/sqle/dtablefunctions/dolt_diff.go diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_stat_table_function.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_stat.go similarity index 100% rename from go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_stat_table_function.go rename to go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_stat.go diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_summary_table_function.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_summary.go similarity index 100% rename from go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_summary_table_function.go rename to go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_summary.go diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_log_table_function.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_log.go similarity index 100% rename from go/libraries/doltcore/sqle/dtablefunctions/dolt_log_table_function.go rename to go/libraries/doltcore/sqle/dtablefunctions/dolt_log.go diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_patch_table_function.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_patch.go similarity index 100% rename from go/libraries/doltcore/sqle/dtablefunctions/dolt_patch_table_function.go rename to go/libraries/doltcore/sqle/dtablefunctions/dolt_patch.go diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_preview_merge_conflicts_summary.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_preview_merge_conflicts_summary.go new file mode 100644 index 00000000000..4b23c217c4f --- /dev/null +++ b/go/libraries/doltcore/sqle/dtablefunctions/dolt_preview_merge_conflicts_summary.go @@ -0,0 +1,460 @@ +// Copyright 2025 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dtablefunctions + +import ( + "errors" + "fmt" + "io" + + "github.com/dolthub/go-mysql-server/sql" + "github.com/dolthub/go-mysql-server/sql/expression" + "github.com/dolthub/go-mysql-server/sql/types" + + "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" + "github.com/dolthub/dolt/go/libraries/doltcore/merge" + "github.com/dolthub/dolt/go/libraries/doltcore/schema" + "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess" + "github.com/dolthub/dolt/go/store/prolly/tree" +) + +const previewMergeConflictsDefaultRowCount = 100 + +var _ sql.TableFunction = (*PreviewMergeConflictsSummaryTableFunction)(nil) +var _ sql.ExecSourceRel = (*PreviewMergeConflictsSummaryTableFunction)(nil) +var _ sql.AuthorizationCheckerNode = (*PreviewMergeConflictsSummaryTableFunction)(nil) + +type PreviewMergeConflictsSummaryTableFunction struct { + ctx *sql.Context + leftBranchExpr sql.Expression + rightBranchExpr sql.Expression + database sql.Database +} + +var previewMergeConflictsSummarySchema = sql.Schema{ + &sql.Column{Name: "table", Type: types.Text, Nullable: false}, + &sql.Column{Name: "num_data_conflicts", Type: types.Uint64, Nullable: true}, + &sql.Column{Name: "num_schema_conflicts", Type: types.Uint64, Nullable: true}, +} + +// NewInstance creates a new instance of TableFunction interface +func (pm *PreviewMergeConflictsSummaryTableFunction) NewInstance(ctx *sql.Context, db sql.Database, expressions []sql.Expression) (sql.Node, error) { + newInstance := &PreviewMergeConflictsSummaryTableFunction{ + ctx: ctx, + database: db, + } + + node, err := newInstance.WithExpressions(expressions...) + if err != nil { + return nil, err + } + + return node, nil +} + +func (pm *PreviewMergeConflictsSummaryTableFunction) DataLength(ctx *sql.Context) (uint64, error) { + numBytesPerRow := schema.SchemaAvgLength(pm.Schema()) + numRows, _, err := pm.RowCount(ctx) + if err != nil { + return 0, err + } + return numBytesPerRow * numRows, nil +} + +func (pm *PreviewMergeConflictsSummaryTableFunction) RowCount(_ *sql.Context) (uint64, bool, error) { + return previewMergeConflictsDefaultRowCount, false, nil +} + +// Database implements the sql.Databaser interface +func (pm *PreviewMergeConflictsSummaryTableFunction) Database() sql.Database { + return pm.database +} + +// WithDatabase implements the sql.Databaser interface +func (pm *PreviewMergeConflictsSummaryTableFunction) WithDatabase(database sql.Database) (sql.Node, error) { + npm := *pm + npm.database = database + return &npm, nil +} + +// Name implements the sql.TableFunction interface +func (pm *PreviewMergeConflictsSummaryTableFunction) Name() string { + return "dolt_preview_merge_conflicts_summary" +} + +// Resolved implements the sql.Resolvable interface +func (pm *PreviewMergeConflictsSummaryTableFunction) Resolved() bool { + return pm.leftBranchExpr.Resolved() && pm.rightBranchExpr.Resolved() +} + +func (pm *PreviewMergeConflictsSummaryTableFunction) IsReadOnly() bool { + return true +} + +// String implements the Stringer interface +func (pm *PreviewMergeConflictsSummaryTableFunction) String() string { + return fmt.Sprintf("DOLT_PREVIEW_MERGE_CONFLICTS_SUMMARY(%s, %s)", pm.leftBranchExpr.String(), pm.rightBranchExpr.String()) +} + +// Schema implements the sql.Node interface. +func (pm *PreviewMergeConflictsSummaryTableFunction) Schema() sql.Schema { + return previewMergeConflictsSummarySchema +} + +// Children implements the sql.Node interface. +func (pm *PreviewMergeConflictsSummaryTableFunction) Children() []sql.Node { + return nil +} + +// WithChildren implements the sql.Node interface. +func (pm *PreviewMergeConflictsSummaryTableFunction) WithChildren(children ...sql.Node) (sql.Node, error) { + if len(children) != 0 { + return nil, fmt.Errorf("unexpected children") + } + return pm, nil +} + +// CheckAuth implements the interface sql.AuthorizationCheckerNode. +func (pm *PreviewMergeConflictsSummaryTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { + tblNames, err := pm.database.GetTableNames(ctx) + if err != nil { + return false + } + + var operations []sql.PrivilegedOperation + for _, tblName := range tblNames { + subject := sql.PrivilegeCheckSubject{Database: pm.database.Name(), Table: tblName} + operations = append(operations, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Select)) + } + + return opChecker.UserHasPrivileges(ctx, operations...) +} + +// Expressions implements the sql.Expressioner interface. +func (pm *PreviewMergeConflictsSummaryTableFunction) Expressions() []sql.Expression { + return []sql.Expression{pm.leftBranchExpr, pm.rightBranchExpr} +} + +// WithExpressions implements the sql.Expressioner interface. +func (pm *PreviewMergeConflictsSummaryTableFunction) WithExpressions(exprs ...sql.Expression) (sql.Node, error) { + if len(exprs) != 2 { + return nil, sql.ErrInvalidArgumentNumber.New(pm.Name(), "2", len(exprs)) + } + + for _, expr := range exprs { + if !expr.Resolved() { + return nil, ErrInvalidNonLiteralArgument.New(pm.Name(), expr.String()) + } + // prepared statements resolve functions beforehand, so above check fails + if _, ok := expr.(sql.FunctionExpression); ok { + return nil, ErrInvalidNonLiteralArgument.New(pm.Name(), expr.String()) + } + } + + newPmcs := *pm + newPmcs.leftBranchExpr = exprs[0] + newPmcs.rightBranchExpr = exprs[1] + + // validate the expressions + if !types.IsText(newPmcs.leftBranchExpr.Type()) && !expression.IsBindVar(newPmcs.leftBranchExpr) { + return nil, sql.ErrInvalidArgumentDetails.New(newPmcs.Name(), newPmcs.leftBranchExpr.String()) + } + if !types.IsText(newPmcs.rightBranchExpr.Type()) && !expression.IsBindVar(newPmcs.rightBranchExpr) { + return nil, sql.ErrInvalidArgumentDetails.New(newPmcs.Name(), newPmcs.rightBranchExpr.String()) + } + + return &newPmcs, nil +} + +// RowIter implements the sql.Node interface +func (pm *PreviewMergeConflictsSummaryTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.RowIter, error) { + leftBranchVal, rightBranchVal, err := pm.evaluateArguments() + if err != nil { + return nil, err + } + + leftBranch, err := interfaceToString(leftBranchVal) + if err != nil { + return nil, fmt.Errorf("invalid left branch parameter: %w", err) + } + rightBranch, err := interfaceToString(rightBranchVal) + if err != nil { + return nil, fmt.Errorf("invalid right branch parameter: %w", err) + } + + // Validate branch names are not empty + if leftBranch == "" { + return nil, fmt.Errorf("left branch name cannot be empty") + } + if rightBranch == "" { + return nil, fmt.Errorf("right branch name cannot be empty") + } + + sqledb, ok := pm.database.(dsess.SqlDatabase) + if !ok { + return nil, fmt.Errorf("unexpected database type: %T", pm.database) + } + + conflicts, err := getTablesWithConflicts(ctx, sqledb, leftBranch, rightBranch) + if err != nil { + return nil, err + } + + return NewPreviewMergeConflictsSummaryTableFunctionRowIter(conflicts), nil +} + +// evaluateArguments returns leftBranchVal and rightBranchVal. +// It evaluates the argument expressions to turn them into values this PreviewMergeConflictsSummaryTableFunction +// can use. Note that this method only evals the expressions, and doesn't validate the values. +func (pm *PreviewMergeConflictsSummaryTableFunction) evaluateArguments() (interface{}, interface{}, error) { + leftBranchVal, err := pm.leftBranchExpr.Eval(pm.ctx, nil) + if err != nil { + return nil, nil, fmt.Errorf("failed to evaluate left branch expression: %w", err) + } + + rightBranchVal, err := pm.rightBranchExpr.Eval(pm.ctx, nil) + if err != nil { + return nil, nil, fmt.Errorf("failed to evaluate right branch expression: %w", err) + } + + return leftBranchVal, rightBranchVal, nil +} + +//-------------------------------------------------- +// previewMergeConflictsSummaryTableFunctionRowIter +//-------------------------------------------------- + +var _ sql.RowIter = &previewMergeConflictsSummaryTableFunctionRowIter{} + +type previewMergeConflictsSummaryTableFunctionRowIter struct { + conflicts []tableConflict + conIdx int +} + +func NewPreviewMergeConflictsSummaryTableFunctionRowIter(pm []tableConflict) sql.RowIter { + return &previewMergeConflictsSummaryTableFunctionRowIter{ + conflicts: pm, + } +} + +func (iter *previewMergeConflictsSummaryTableFunctionRowIter) Next(ctx *sql.Context) (sql.Row, error) { + if iter.conIdx >= len(iter.conflicts) { + return nil, io.EOF + } + + conflict := iter.conflicts[iter.conIdx] + iter.conIdx++ + return getRowFromConflict(conflict), nil +} + +func (iter *previewMergeConflictsSummaryTableFunctionRowIter) Close(context *sql.Context) error { + return nil +} + +func getRowFromConflict(conflict tableConflict) sql.Row { + row := sql.Row{ + conflict.tableName.String(), // table + } + // num_data_conflicts + if conflict.numSchemaConflicts > 0 { + row = append(row, nil) + } else { + row = append(row, conflict.numDataConflicts) + } + // num_schema_conflicts + row = append(row, conflict.numSchemaConflicts) + return row +} + +// resolveBranchesToRoots resolves branch names to their corresponding root values +// and finds the common merge base. Returns left root, right root, and base root. +func resolveBranchesToRoots(ctx *sql.Context, db dsess.SqlDatabase, leftBranch, rightBranch string) (doltdb.RootValue, doltdb.RootValue, doltdb.RootValue, error) { + sess := dsess.DSessFromSess(ctx.Session) + + headRef, err := sess.CWBHeadRef(ctx, db.Name()) + if err != nil { + return nil, nil, nil, err + } + + leftCm, err := resolveCommit(ctx, db.DbData().Ddb, headRef, leftBranch) + if err != nil { + return nil, nil, nil, err + } + + rightCm, err := resolveCommit(ctx, db.DbData().Ddb, headRef, rightBranch) + if err != nil { + return nil, nil, nil, err + } + + optCmt, err := doltdb.GetCommitAncestor(ctx, leftCm, rightCm) + if err != nil { + return nil, nil, nil, err + } + + mergeBase, ok := optCmt.ToCommit() + if !ok { + return nil, nil, nil, doltdb.ErrGhostCommitEncountered + } + + rightRoot, err := rightCm.GetRootValue(ctx) + if err != nil { + return nil, nil, nil, err + } + leftRoot, err := leftCm.GetRootValue(ctx) + if err != nil { + return nil, nil, nil, err + } + baseRoot, err := mergeBase.GetRootValue(ctx) + if err != nil { + return nil, nil, nil, err + } + + return leftRoot, rightRoot, baseRoot, nil +} + +type tableConflict struct { + tableName doltdb.TableName + numDataConflicts uint64 // ignored if schema conflicts exist + numSchemaConflicts uint64 +} + +// getTablesWithConflicts analyzes the merge between two branches and returns +// a list of tables that would have conflicts. It performs a dry-run merge +// to identify both schema and data conflicts without modifying the database. +func getTablesWithConflicts(ctx *sql.Context, db dsess.SqlDatabase, baseBranch, mergeBranch string) ([]tableConflict, error) { + leftRoot, rightRoot, baseRoot, err := resolveBranchesToRoots(ctx, db, baseBranch, mergeBranch) + if err != nil { + return nil, err + } + + merger, err := merge.NewMerger(leftRoot, rightRoot, baseRoot, rightRoot, baseRoot, leftRoot.VRW(), leftRoot.NodeStore()) + if err != nil { + return nil, err + } + + tblNames, err := doltdb.UnionTableNames(ctx, leftRoot, rightRoot) + if err != nil { + return nil, err + } + + mergeOpts := merge.MergeOpts{ + IsCherryPick: false, + KeepSchemaConflicts: true, + ReverifyAllConstraints: false, + } + + var conflicted []tableConflict + + for _, tblName := range tblNames { + tm, err := merger.MakeTableMerger(ctx, tblName, mergeOpts) + if err != nil { + return nil, err + } + + // short-circuit here if we can + finished, _, stats, err := merger.MaybeShortCircuit(ctx, tm, mergeOpts) + if err != nil { + return nil, err + } + if finished != nil || stats != nil { + continue + } + + // Calculate a merge of the schemas, but don't apply it + mergeSch, schConflicts, _, diffInfo, err := tm.SchemaMerge(ctx, tblName) + if err != nil { + return nil, err + } + numSchemaConflicts := uint64(schConflicts.Count()) + if numSchemaConflicts > 0 { + conflicted = append(conflicted, tableConflict{tableName: tblName, numSchemaConflicts: numSchemaConflicts}) + // Cannot calculate data conflicts if there are schema conflicts + continue + } + + dataConflicts, err := getDataConflictsForTable(ctx, tm, tblName, mergeSch, diffInfo) + if err != nil { + return nil, err + } + if dataConflicts != nil { + conflicted = append(conflicted, *dataConflicts) + } + } + + return conflicted, nil +} + +// getDataConflictsForTable calculates the number of data conflicts for a specific table. +// It performs a three-way diff to identify rows that cannot be automatically merged. +// Returns nil if no data conflicts are found. +func getDataConflictsForTable(ctx *sql.Context, tm *merge.TableMerger, tblName doltdb.TableName, mergeSch schema.Schema, diffInfo tree.ThreeWayDiffInfo) (*tableConflict, error) { + keyless := schema.IsKeyless(mergeSch) + + leftRows, err := tm.LeftRows(ctx) + if err != nil { + return nil, err + } + rightRows, err := tm.RightRows(ctx) + if err != nil { + return nil, err + } + ancRows, err := tm.AncRows(ctx) + if err != nil { + return nil, err + } + + valueMerger := tm.GetNewValueMerger(mergeSch, leftRows) + + differ, err := tree.NewThreeWayDiffer( + ctx, + leftRows.NodeStore(), + leftRows.Tuples(), + rightRows.Tuples(), + ancRows.Tuples(), + valueMerger.TryMerge, + keyless, + diffInfo, + leftRows.Tuples().Order, + ) + if err != nil { + return nil, err + } + + var numDataConflicts uint64 = 0 + for { + diff, err := differ.Next(ctx) + if errors.Is(err, io.EOF) { + break + } else if err != nil { + return nil, err + } + switch diff.Op { + case tree.DiffOpDivergentModifyConflict, tree.DiffOpDivergentDeleteConflict: + // In this case, a modification or delete was made to one side, and a conflicting delete or modification + // was made to the other side, so these cannot be automatically resolved. + numDataConflicts++ + case tree.DiffOpConvergentAdd, tree.DiffOpConvergentModify, tree.DiffOpConvergentDelete: + if keyless { + numDataConflicts++ + } + } + } + + if numDataConflicts > 0 { + return &tableConflict{tableName: tblName, numSchemaConflicts: uint64(0), numDataConflicts: numDataConflicts}, nil + } + + return nil, nil +} diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_query_diff_table_function.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_query_diff.go similarity index 100% rename from go/libraries/doltcore/sqle/dtablefunctions/dolt_query_diff_table_function.go rename to go/libraries/doltcore/sqle/dtablefunctions/dolt_query_diff.go diff --git a/go/libraries/doltcore/sqle/dtablefunctions/reflog_table_function.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_reflog.go similarity index 100% rename from go/libraries/doltcore/sqle/dtablefunctions/reflog_table_function.go rename to go/libraries/doltcore/sqle/dtablefunctions/dolt_reflog.go diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_schema_diff_table_function.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_schema_diff.go similarity index 100% rename from go/libraries/doltcore/sqle/dtablefunctions/dolt_schema_diff_table_function.go rename to go/libraries/doltcore/sqle/dtablefunctions/dolt_schema_diff.go diff --git a/go/libraries/doltcore/sqle/dtablefunctions/init.go b/go/libraries/doltcore/sqle/dtablefunctions/init.go index ea284dff5e3..218d6af1b7b 100644 --- a/go/libraries/doltcore/sqle/dtablefunctions/init.go +++ b/go/libraries/doltcore/sqle/dtablefunctions/init.go @@ -23,6 +23,7 @@ var DoltTableFunctions = []sql.TableFunction{ &BranchStatusTableFunction{}, &LogTableFunction{}, &PatchTableFunction{}, + &PreviewMergeConflictsSummaryTableFunction{}, &SchemaDiffTableFunction{}, &ReflogTableFunction{}, &QueryDiffTableFunction{}, diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_engine_test.go b/go/libraries/doltcore/sqle/enginetest/dolt_engine_test.go index 2971986b29d..d8e69854f6c 100644 --- a/go/libraries/doltcore/sqle/enginetest/dolt_engine_test.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_engine_test.go @@ -1342,6 +1342,16 @@ func TestDoltMergeArtifacts(t *testing.T) { RunDoltMergeArtifacts(t, h) } +func TestDoltPreviewMergeConflicts(t *testing.T) { + h := newDoltEnginetestHarness(t) + RunDoltPreviewMergeConflictsTests(t, h) +} + +func TestDoltPreviewMergeConflictsPrepared(t *testing.T) { + h := newDoltEnginetestHarness(t) + RunDoltPreviewMergeConflictsPreparedTests(t, h) +} + // these tests are temporary while there is a difference between the old format // and new format merge behaviors. func TestOldFormatMergeConflictsAndCVs(t *testing.T) { diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_engine_tests.go b/go/libraries/doltcore/sqle/enginetest/dolt_engine_tests.go index aeb61a19083..38865eed432 100755 --- a/go/libraries/doltcore/sqle/enginetest/dolt_engine_tests.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_engine_tests.go @@ -988,6 +988,28 @@ func RunDoltMergeArtifacts(t *testing.T, h DoltEnginetestHarness) { } } +func RunDoltPreviewMergeConflictsTests(t *testing.T, h DoltEnginetestHarness) { + for _, script := range PreviewMergeConflictsFunctionScripts { + // harness can't reset effectively. Use a new harness for each script + func() { + h := h.NewHarness(t) + defer h.Close() + enginetest.TestScript(t, h, script) + }() + } +} + +func RunDoltPreviewMergeConflictsPreparedTests(t *testing.T, h DoltEnginetestHarness) { + for _, script := range PreviewMergeConflictsFunctionScripts { + // harness can't reset effectively. Use a new harness for each script + func() { + h := h.NewHarness(t) + defer h.Close() + enginetest.TestScript(t, h, script) + }() + } +} + func RunDoltResetTest(t *testing.T, h DoltEnginetestHarness) { for _, script := range DoltResetTestScripts { // dolt versioning conflicts with reset harness -- use new harness every time diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_queries_merge.go b/go/libraries/doltcore/sqle/enginetest/dolt_queries_merge.go index 23e815c4df6..a168837ab20 100644 --- a/go/libraries/doltcore/sqle/enginetest/dolt_queries_merge.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_queries_merge.go @@ -27,6 +27,7 @@ import ( "github.com/dolthub/dolt/go/libraries/doltcore/merge" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess" + "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dtablefunctions" ) type MergeScriptTest struct { @@ -260,6 +261,10 @@ var MergeScripts = []queries.ScriptTest{ "CALL DOLT_CHECKOUT('main');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'feature-branch')", + Expected: []sql.Row{}, + }, { // FF-Merge Query: "CALL DOLT_MERGE('feature-branch')", @@ -338,6 +343,10 @@ var MergeScripts = []queries.ScriptTest{ "use `mydb/main~`", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'feature-branch')", + ExpectedErrStr: "this operation is not supported while in a detached head state", + }, { Query: "CALL DOLT_MERGE('feature-branch')", ExpectedErrStr: "this operation is not supported while in a detached head state", @@ -535,6 +544,18 @@ var MergeScripts = []queries.ScriptTest{ "CALL DOLT_COMMIT('-a', '-m', 'update a value', '--date', '2022-08-06T12:00:03');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'feature-branch')", + Expected: []sql.Row{{"test", uint64(1), uint64(0)}}, + }, + { + Query: "SELECT is_merging, source, target, unmerged_tables FROM DOLT_MERGE_STATUS;", + Expected: []sql.Row{{false, nil, nil, nil}}, + }, + { + Query: "SELECT * from dolt_status", + Expected: []sql.Row{}, + }, { Query: "CALL DOLT_MERGE('feature-branch', '-m', 'this is a merge')", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -556,8 +577,8 @@ var MergeScripts = []queries.ScriptTest{ Expected: []sql.Row{{"update a value"}}, }, { - Query: "SELECT COUNT(*) FROM dolt_conflicts", - Expected: []sql.Row{{1}}, + Query: "SELECT * FROM dolt_conflicts", + Expected: []sql.Row{{"test", uint64(1)}}, }, { Query: "DELETE FROM dolt_conflicts_test", @@ -575,6 +596,26 @@ var MergeScripts = []queries.ScriptTest{ Query: "SELECT * from test ORDER BY pk", Expected: []sql.Row{{0, 1001}, {1, 1}}, }, + { + Query: "SELECT is_merging, source, target, unmerged_tables FROM DOLT_MERGE_STATUS;", + Expected: []sql.Row{{true, "feature-branch", "refs/heads/main", ""}}, + }, + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'feature-branch')", + Expected: []sql.Row{{"test", uint64(1), uint64(0)}}, // merge wasn't committed yet, so still shows conflict between branches + }, + { + Query: "CALL DOLT_COMMIT('-m', 'merged');", + Expected: []sql.Row{{doltCommit}}, + }, + { + Query: "SELECT is_merging, source, target, unmerged_tables FROM DOLT_MERGE_STATUS;", + Expected: []sql.Row{{false, nil, nil, nil}}, + }, + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'feature-branch')", + Expected: []sql.Row{}, + }, }, }, { @@ -1042,10 +1083,18 @@ var MergeScripts = []queries.ScriptTest{ "CALL DOLT_COMMIT('-A', '-m', 'commit');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('HEAD', 'HEAD~1')", + Expected: []sql.Row{}, + }, { Query: "CALL DOLT_MERGE('HEAD~1')", Expected: []sql.Row{{"", 0, 0, "cannot fast forward from a to b. a is ahead of b already"}}, }, + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('HEAD', 'HEAD')", + Expected: []sql.Row{}, + }, { Query: "CALL DOLT_MERGE('HEAD')", Expected: []sql.Row{{"", 0, 0, "Everything up-to-date"}}, @@ -1069,17 +1118,25 @@ var MergeScripts = []queries.ScriptTest{ "set dolt_allow_commit_conflicts = on", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'feature-branch')", + Expected: []sql.Row{{"test", uint64(1), uint64(0)}}, + }, { Query: "CALL DOLT_MERGE('feature-branch')", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, }, { - Query: "SELECT count(*) from dolt_conflicts_test", + Query: "SELECT * FROM dolt_conflicts", + Expected: []sql.Row{{"test", uint64(1)}}, + }, + { + Query: "SELECT COUNT(*) FROM dolt_conflicts_test", Expected: []sql.Row{{1}}, }, { // Test case-insensitive table name - Query: "SELECT count(*) from dolt_conflicts_TeST", + Query: "SELECT count(*) FROM dolt_conflicts_TeST", Expected: []sql.Row{{1}}, }, { @@ -1214,6 +1271,10 @@ var MergeScripts = []queries.ScriptTest{ "UPDATE test SET val=1001 WHERE pk=0;", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'feature-branch')", + Expected: []sql.Row{}, + }, { Query: "CALL DOLT_MERGE('feature-branch', '-m', 'this is a merge')", ExpectedErrStr: "error: local changes would be stomped by merge:\n\ttest\n Please commit your changes before you merge.", @@ -1242,6 +1303,10 @@ var MergeScripts = []queries.ScriptTest{ "call dolt_commit('-am', 'main primary key change')", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'b1')", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('b1')", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -1548,6 +1613,10 @@ var MergeScripts = []queries.ScriptTest{ "CALL DOLT_COMMIT('-am', 'left');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'other')", + ExpectedErrStr: "table with same name 't' added in 2 commits can't be merged", + }, { Query: "CALL DOLT_MERGE('other');", ExpectedErrStr: "table with same name 't' added in 2 commits can't be merged", @@ -1571,6 +1640,10 @@ var MergeScripts = []queries.ScriptTest{ "CALL DOLT_COMMIT('-am', 'left');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'other')", + Expected: []sql.Row{}, + }, { Query: "CALL DOLT_MERGE('other');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -1598,6 +1671,10 @@ var MergeScripts = []queries.ScriptTest{ "CALL DOLT_COMMIT('-am', 'left');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'other')", + Expected: []sql.Row{{"t", uint64(1), uint64(0)}}, + }, { Query: "CALL DOLT_MERGE('other');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -1656,6 +1733,10 @@ var MergeScripts = []queries.ScriptTest{ "CALL dolt_checkout('main');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'test')", + Expected: []sql.Row{}, + }, { Query: "CALL dolt_merge('test');", Expected: []sql.Row{{doltCommit, 1, 0, "merge successful"}}, @@ -1693,6 +1774,10 @@ var MergeScripts = []queries.ScriptTest{ "CALL dolt_commit('-a', '-m', 'cm3');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'test')", + Expected: []sql.Row{}, + }, { Query: "CALL dolt_merge('test');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -1764,6 +1849,10 @@ var MergeScripts = []queries.ScriptTest{ "CALL dolt_commit('-am', 'cm3');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'test')", + Expected: []sql.Row{}, + }, { Query: "CALL dolt_merge('test');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -1803,6 +1892,10 @@ var MergeScripts = []queries.ScriptTest{ "CALL DOLT_COMMIT('-am', 'left cm');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'right')", + Expected: []sql.Row{}, + }, { Query: "CALL DOLT_MERGE('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -1950,6 +2043,11 @@ var MergeScripts = []queries.ScriptTest{ "set dolt_force_transaction_commit = on;", }, Assertions: []queries.ScriptTestAssertion{ + { + Skip: true, // TODO: constraint violations + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'other')", + Expected: []sql.Row{{"test", uint64(1), uint64(0)}}, + }, { Query: "CALL DOLT_MERGE('other');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -2099,6 +2197,10 @@ var MergeScripts = []queries.ScriptTest{ "call dolt_commit('-am', 'left update')", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'feature')", + Expected: []sql.Row{{"xyz", uint64(1), uint64(0)}}, + }, { Query: "CALL DOLT_MERGE('feature');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -2126,6 +2228,10 @@ var MergeScripts = []queries.ScriptTest{ "call dolt_commit('-am', 'left update')", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'feature')", + Expected: []sql.Row{{"xyz", uint64(1), uint64(0)}}, + }, { Query: "CALL DOLT_MERGE('feature');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -2153,6 +2259,10 @@ var MergeScripts = []queries.ScriptTest{ "CALL DOLT_COMMIT('-am', 'left commit');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'right')", + ExpectedErrStr: "error: cannot merge because table t has different primary keys", + }, { Query: "CALL DOLT_MERGE('right');", ExpectedErrStr: "error: cannot merge because table t has different primary keys", @@ -2384,8 +2494,13 @@ var MergeScripts = []queries.ScriptTest{ }, Assertions: []queries.ScriptTestAssertion{ { - Query: "call dolt_merge('other')", - SkipResultsCheck: true, // contains commit hash, we just need it to not error + Skip: true, // TODO: conflict: table with same name deleted and modified + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'other')", + Expected: []sql.Row{}, + }, + { + Query: "call dolt_merge('other')", + Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, }, { Query: "SELECT v1 FROM test WHERE MATCH(v1) AGAINST ('abc def ghi');", @@ -2419,6 +2534,10 @@ var MergeScripts = []queries.ScriptTest{ "SET @PreMergeBranch1Commit = dolt_hashof('HEAD');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('branch1', 'main')", + Expected: []sql.Row{}, + }, { // We can merge from main -> branch1, even though the column tags are not identical Query: "call dolt_merge('main')", @@ -2437,6 +2556,10 @@ var MergeScripts = []queries.ScriptTest{ Query: "CALL dolt_checkout('main');", Expected: []sql.Row{{0, "Switched to branch 'main'"}}, }, + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'branch1')", + Expected: []sql.Row{}, + }, { // We can merge from branch1 -> main, even though the column tags are not identical Query: "call dolt_merge('branch1')", @@ -2522,6 +2645,10 @@ var MergeScripts = []queries.ScriptTest{ "call dolt_commit('-Am', 'change default on other');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('other', 'main')", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('main')", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -2686,6 +2813,10 @@ var DoltConflictTableNameTableTests = []queries.ScriptTest{ "CALL DOLT_COMMIT('-am', 'left edit');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'other')", + Expected: []sql.Row{{"t", uint64(4), uint64(0)}}, + }, { Query: "CALL DOLT_MERGE('other');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -2730,6 +2861,10 @@ var DoltConflictTableNameTableTests = []queries.ScriptTest{ "CALL DOLT_COMMIT('-am', 'left');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'right')", + Expected: []sql.Row{{"t", uint64(6), uint64(0)}}, + }, { Query: "CALL DOLT_MERGE('right');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -3273,6 +3408,10 @@ var MergeArtifactsScripts = []queries.ScriptTest{ "CALL DOLT_COMMIT('-afm', 'update pk 1 to 1000');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'other2')", + Expected: []sql.Row{{"t", uint64(1), uint64(0)}}, + }, { Query: "CALL DOLT_MERGE('other2');", ExpectedErrStr: "the existing conflicts are of a different schema than the conflicts generated by this merge. Please resolve them and try again", @@ -3435,6 +3574,11 @@ var MergeArtifactsScripts = []queries.ScriptTest{ "CALL DOLT_COMMIT('-am', 'left insert');", }, Assertions: []queries.ScriptTestAssertion{ + { + Skip: true, // TODO: constraint violations + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'left2')", + Expected: []sql.Row{{"t", uint64(1), uint64(0)}}, + }, { Query: "CALL DOLT_MERGE('left2');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -3836,6 +3980,10 @@ var SchemaConflictScripts = []queries.ScriptTest{ "call dolt_commit('-am', 'altered t on branch main')", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'other')", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, { Query: "call dolt_merge('other')", ExpectedErrStr: dsess.ErrUnresolvedConflictsAutoCommit.Error(), @@ -3864,6 +4012,10 @@ var SchemaConflictScripts = []queries.ScriptTest{ "call dolt_commit('-am', 'altered t on branch main')", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'other')", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, { Query: "call dolt_merge('other')", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -4261,6 +4413,10 @@ var GeneratedColumnMergeTestScripts = []queries.ScriptTest{ "call dolt_checkout('main')", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'branch1')", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('branch1')", Expected: []sql.Row{{doltCommit, 1, 0, "merge successful"}}, @@ -4313,6 +4469,10 @@ var GeneratedColumnMergeTestScripts = []queries.ScriptTest{ "call dolt_checkout('main')", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'branch1')", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('branch1')", Expected: []sql.Row{{doltCommit, 1, 0, "merge successful"}}, @@ -4328,6 +4488,10 @@ var GeneratedColumnMergeTestScripts = []queries.ScriptTest{ Query: "select id from t1 where v3 = 5", Expected: []sql.Row{{1}}, }, + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'branch2')", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('branch2')", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -4401,6 +4565,10 @@ var GeneratedColumnMergeTestScripts = []queries.ScriptTest{ "call dolt_checkout('main')", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'branch1')", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('branch1')", Expected: []sql.Row{{doltCommit, 1, 0, "merge successful"}}, @@ -4478,6 +4646,539 @@ var GeneratedColumnMergeTestScripts = []queries.ScriptTest{ }, } +var PreviewMergeConflictsFunctionScripts = []queries.ScriptTest{ + { + Name: "invalid arguments", + SetUpScript: []string{ + "create table t (pk int primary key, c1 varchar(20), c2 varchar(20));", + "insert into t values (1, 'one', 'two'), (2, 'two', 'three');", + "call dolt_add('.')", + "call dolt_commit('-am', 'creating table t');", + + "call dolt_branch('branch1')", + "call dolt_branch('branch2')", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary();", + ExpectedErr: sql.ErrInvalidArgumentNumber, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('t');", + ExpectedErr: sql.ErrInvalidArgumentNumber, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch1', 't');", + ExpectedErr: sql.ErrInvalidArgumentNumber, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary(null, null);", + ExpectedErr: sql.ErrInvalidArgumentDetails, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 123);", + ExpectedErr: sql.ErrInvalidArgumentDetails, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary(123, 'branch1');", + ExpectedErr: sql.ErrInvalidArgumentDetails, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('fake-branch', 'main');", + ExpectedErrStr: "branch not found: fake-branch", + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'fake-branch');", + ExpectedErrStr: "branch not found: fake-branch", + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main...branch1', 'branch2');", + ExpectedErrStr: "string is not a valid branch or hash", + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', concat('branch', '1'));", + ExpectedErr: dtablefunctions.ErrInvalidNonLiteralArgument, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary(hashof('main'), 'branch1');", + ExpectedErr: dtablefunctions.ErrInvalidNonLiteralArgument, + }, + }, + }, + { + Name: "basic case with single table", + SetUpScript: []string{ + "create table t (pk int primary key, c1 varchar(20), c2 varchar(20));", + "insert into t values (1, 'one', 'two'), (2, 'two', 'three');", + "call dolt_add('.')", + "set @Commit1 = '';", + "call dolt_commit_hash_out(@Commit1, '-am', 'creating table t');", + + "call dolt_branch('branch1')", + "call dolt_checkout('-b', 'branch2')", + "update t set c1='one!' where pk=1", + "set @Commit2 = '';", + "call dolt_commit_hash_out(@Commit2, '-am', 'update row 1 on branch2');", + + "call dolt_checkout('branch1')", + "update t set c1='one?' where pk=1", + "set @Commit3 = '';", + "call dolt_commit_hash_out(@Commit3, '-am', 'update row 1 on branch1');", + + "call dolt_checkout('main')", + "call dolt_merge('branch1')", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch1')", + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch2')", + Expected: []sql.Row{{"t", uint64(1), uint64(0)}}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('branch1', 'branch2')", + Expected: []sql.Row{{"t", uint64(1), uint64(0)}}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary(@Commit1, @Commit2)", // not branches + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('branch2', 'main')", + Expected: []sql.Row{{"t", uint64(1), uint64(0)}}, + }, + }, + }, + { + Name: "basic case with keyless table", + SetUpScript: []string{ + "create table t (pk int, c1 varchar(20), c2 varchar(20));", + "insert into t values (1, 'one', 'two'), (2, 'two', 'three');", + "call dolt_add('.')", + "set @Commit1 = '';", + "call dolt_commit_hash_out(@Commit1, '-am', 'creating table t');", + + "call dolt_branch('branch1')", + "call dolt_checkout('-b', 'branch2')", + "update t set c1='one!' where pk=1", + "set @Commit2 = '';", + "call dolt_commit_hash_out(@Commit2, '-am', 'update row 1 on branch2');", + + "call dolt_checkout('branch1')", + "update t set c1='one?' where pk=1", + "set @Commit3 = '';", + "call dolt_commit_hash_out(@Commit3, '-am', 'update row 1 on branch1');", + + "call dolt_checkout('main')", + "call dolt_merge('branch1')", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch1')", + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch2')", + Expected: []sql.Row{{"t", uint64(1), uint64(0)}}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('branch1', 'branch2')", + Expected: []sql.Row{{"t", uint64(1), uint64(0)}}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary(@Commit1, @Commit2)", // not branches + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('branch2', 'main')", + Expected: []sql.Row{{"t", uint64(1), uint64(0)}}, + }, + }, + }, + { + Name: "basic case with multiple tables", + SetUpScript: []string{ + "create table t (pk int primary key, c1 varchar(20), c2 varchar(20));", + "create table t2 (pk int primary key, c1 varchar(20));", + "insert into t values (1, 'one', 'two'), (2, 'two', 'three');", + "insert into t2 values(100, 'hundred');", + "call dolt_add('.')", + "set @Commit1 = '';", + "call dolt_commit_hash_out(@Commit1, '-am', 'creating table t');", + + "call dolt_branch('branch1')", + "call dolt_checkout('-b', 'branch2')", + "update t set c1='one!' where pk=1", + "alter table t2 alter column c1 set default 'default';", + "set @Commit2 = '';", + "call dolt_commit_hash_out(@Commit2, '-am', 'update row 1 on branch2');", + + "call dolt_checkout('branch1')", + "update t set c1='one?' where pk=1", + "alter table t2 alter column c1 set default 'default2';", + "set @Commit3 = '';", + "call dolt_commit_hash_out(@Commit3, '-am', 'update row 1 on branch1');", + + "call dolt_checkout('main')", + "call dolt_merge('branch1')", + + "create table keyless (id int);", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch1')", + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch2')", + Expected: []sql.Row{{"t", uint64(1), uint64(0)}, {"t2", nil, uint64(1)}}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('branch1', 'branch2')", + Expected: []sql.Row{{"t", uint64(1), uint64(0)}, {"t2", nil, uint64(1)}}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary(@Commit1, @Commit2)", // not branches + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('branch2', 'main')", + Expected: []sql.Row{{"t", uint64(1), uint64(0)}, {"t2", nil, uint64(1)}}, + }, + }, + }, + { + Name: "schema-only conflicts", + SetUpScript: []string{ + "create table t (pk int primary key, c1 varchar(20) default 'orig', c2 varchar(20));", + "insert into t values (1, 'one', 'two'), (2, 'two', 'three');", + "call dolt_add('.')", + "call dolt_commit('-am', 'creating table t');", + + "call dolt_branch('branch1')", + "call dolt_checkout('-b', 'branch2')", + "alter table t alter column c1 set default 'default1';", + "call dolt_commit('-am', 'change default on branch2');", + + "call dolt_checkout('branch1')", + "alter table t alter column c1 set default 'default2';", + "call dolt_commit('-am', 'change default on branch1');", + + "call dolt_checkout('main')", + "call dolt_merge('branch1')", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch1')", + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch2')", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('branch1', 'branch2')", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, + }, + }, + { + Name: "mixed schema and data conflicts in same table", + SetUpScript: []string{ + "create table t (pk int primary key, c1 varchar(20) default 'orig', c2 varchar(20));", + "insert into t values (1, 'one', 'two'), (2, 'two', 'three');", + "call dolt_add('.')", + "call dolt_commit('-am', 'creating table t');", + + "call dolt_branch('branch1')", + "call dolt_checkout('-b', 'branch2')", + "update t set c1='one!' where pk=1", + "alter table t alter column c1 set default 'default1';", + "call dolt_commit('-am', 'data and schema changes on branch2');", + + "call dolt_checkout('branch1')", + "update t set c1='one?' where pk=1", + "alter table t alter column c1 set default 'default2';", + "call dolt_commit('-am', 'data and schema changes on branch1');", + + "call dolt_checkout('main')", + "call dolt_merge('branch1')", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch1')", + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch2')", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('branch1', 'branch2')", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, + }, + }, + { + Name: "column type conflicts", + SetUpScript: []string{ + "create table t (pk int primary key, c1 varchar(20));", + "insert into t values (1, 'one');", + "call dolt_add('.')", + "call dolt_commit('-am', 'initial commit');", + + "call dolt_branch('branch1')", + "call dolt_checkout('-b', 'branch2')", + "alter table t modify column c1 varchar(50);", + "call dolt_commit('-am', 'change column to varchar(50) on branch2');", + + "call dolt_checkout('branch1')", + "alter table t modify column c1 text;", + "call dolt_commit('-am', 'change column to text on branch1');", + + "call dolt_checkout('main')", + "call dolt_merge('branch1')", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch1')", + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch2')", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('branch1', 'branch2')", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, + }, + }, + { + Name: "foreign key constraint conflicts", + SetUpScript: []string{ + "create table parent (pk int primary key, name varchar(20));", + "create table child (pk int primary key, parent_pk int, data varchar(20));", + "insert into parent values (1, 'parent1'), (2, 'parent2');", + "insert into child values (1, 1, 'child1'), (2, 2, 'child2');", + "call dolt_add('.')", + "call dolt_commit('-am', 'initial tables');", + + "call dolt_branch('branch1')", + "call dolt_checkout('-b', 'branch2')", + "alter table child add constraint fk1 foreign key (parent_pk) references parent(pk) on delete cascade;", + "call dolt_commit('-am', 'add fk with cascade on branch2');", + + "call dolt_checkout('branch1')", + "alter table child add constraint fk1 foreign key (parent_pk) references parent(pk) on delete restrict;", + "call dolt_commit('-am', 'add fk with restrict on branch1');", + + "call dolt_checkout('main')", + "call dolt_merge('branch1')", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch1')", + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch2')", + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('branch1', 'branch2')", + Expected: []sql.Row{}, + }, + }, + }, + { + Name: "check constraint conflicts", + SetUpScript: []string{ + "create table t (pk int primary key, score int);", + "insert into t values (1, 85), (2, 92);", + "call dolt_add('.')", + "call dolt_commit('-am', 'initial table');", + + "call dolt_branch('branch1')", + "call dolt_checkout('-b', 'branch2')", + "alter table t add constraint chk1 check (score >= 0);", + "call dolt_commit('-am', 'add check >= 0 on branch2');", + + "call dolt_checkout('branch1')", + "alter table t add constraint chk1 check (score > 0);", + "call dolt_commit('-am', 'add check > 0 on branch1');", + + "call dolt_checkout('main')", + "call dolt_merge('branch1')", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch1')", + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch2')", + Expected: []sql.Row{{"t", nil, uint64(2)}}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('branch1', 'branch2')", + Expected: []sql.Row{{"t", nil, uint64(2)}}, + }, + }, + }, + { + Name: "index conflicts", + SetUpScript: []string{ + "create table t (pk int primary key, name varchar(20), email varchar(50));", + "insert into t values (1, 'alice', 'alice@email.com'), (2, 'bob', 'bob@email.com');", + "call dolt_add('.')", + "call dolt_commit('-am', 'initial table');", + + "call dolt_branch('branch1')", + "call dolt_checkout('-b', 'branch2')", + "create index idx_name on t(name);", + "call dolt_commit('-am', 'add name index on branch2');", + + "call dolt_checkout('branch1')", + "create unique index idx_name on t(name);", + "call dolt_commit('-am', 'add unique name index on branch1');", + + "call dolt_checkout('main')", + "call dolt_merge('branch1')", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch1')", + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch2')", + Expected: []sql.Row{{"t", nil, uint64(2)}}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('branch1', 'branch2')", + Expected: []sql.Row{{"t", nil, uint64(2)}}, + }, + }, + }, + { + Name: "many data conflicts", + SetUpScript: []string{ + "create table t (pk int primary key, c1 varchar(20), c2 varchar(20), c3 varchar(20));", + "insert into t values (1, 'a1', 'b1', 'c1'), (2, 'a2', 'b2', 'c2'), (3, 'a3', 'b3', 'c3'), (4, 'a4', 'b4', 'c4'), (5, 'a5', 'b5', 'c5');", + "call dolt_add('.')", + "call dolt_commit('-am', 'initial data');", + + "call dolt_branch('branch1')", + "call dolt_checkout('-b', 'branch2')", + "update t set c1=concat(c1, '_branch2') where pk in (1,2,3);", + "update t set c2=concat(c2, '_branch2') where pk in (2,4);", + "call dolt_commit('-am', 'modify multiple rows on branch2');", + + "call dolt_checkout('branch1')", + "update t set c1=concat(c1, '_branch1') where pk in (1,2,3);", + "update t set c2=concat(c2, '_branch1') where pk in (2,4);", + "update t set c3=concat(c3, '_branch1') where pk = 5;", + "call dolt_commit('-am', 'modify multiple rows on branch1');", + + "call dolt_checkout('main')", + "call dolt_merge('branch1')", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch1')", + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch2')", + Expected: []sql.Row{{"t", uint64(4), uint64(0)}}, // 4 rows have conflicts (1,2,3,4) + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('branch1', 'branch2')", + Expected: []sql.Row{{"t", uint64(4), uint64(0)}}, + }, + }, + }, + { + Name: "multiple tables with different conflict types", + SetUpScript: []string{ + "create table data_conflicts (pk int primary key, value varchar(20));", + "create table schema_conflicts (pk int primary key, name varchar(20) default 'default');", + "create table no_conflicts (pk int primary key, info varchar(20));", + "insert into data_conflicts values (1, 'original'), (2, 'data');", + "insert into schema_conflicts values (1, 'schema');", + "insert into no_conflicts values (1, 'unchanged');", + "call dolt_add('.')", + "call dolt_commit('-am', 'initial setup');", + + "call dolt_branch('branch1')", + "call dolt_checkout('-b', 'branch2')", + "update data_conflicts set value='branch2_value' where pk=1;", + "alter table schema_conflicts alter column name set default 'branch2_default';", + "call dolt_commit('-am', 'changes on branch2');", + + "call dolt_checkout('branch1')", + "update data_conflicts set value='branch1_value' where pk=1;", + "alter table schema_conflicts alter column name set default 'branch1_default';", + "call dolt_commit('-am', 'changes on branch1');", + + "call dolt_checkout('main')", + "call dolt_merge('branch1')", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch1')", + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch2') order by `table`", + Expected: []sql.Row{{"data_conflicts", uint64(1), uint64(0)}, {"schema_conflicts", nil, uint64(1)}}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('branch1', 'branch2') order by `table`", + Expected: []sql.Row{{"data_conflicts", uint64(1), uint64(0)}, {"schema_conflicts", nil, uint64(1)}}, + }, + }, + }, + { + Name: "empty result when no conflicts", + SetUpScript: []string{ + "create table t (pk int primary key, value varchar(20));", + "insert into t values (1, 'original');", + "call dolt_add('.')", + "call dolt_commit('-am', 'initial setup');", + + "call dolt_branch('branch1')", + "call dolt_checkout('-b', 'branch2')", + "insert into t values (2, 'branch2_new');", + "call dolt_commit('-am', 'add row on branch2');", + + "call dolt_checkout('branch1')", + "insert into t values (3, 'branch1_new');", + "call dolt_commit('-am', 'add row on branch1');", + + "call dolt_checkout('main')", + "call dolt_merge('branch1')", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch1')", + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('main', 'branch2')", + Expected: []sql.Row{}, + }, + { + Query: "SELECT * from dolt_preview_merge_conflicts_summary('branch1', 'branch2')", + Expected: []sql.Row{}, + }, + }, + }, +} + // convertMergeScriptTest converts a MergeScriptTest into a standard ScriptTest. If flipSides is true, then the // left and right setup is swapped (i.e. left setup is done on right branch and right setup is done on main branch). // This enables us to test merges in both directions, since the merge code is asymmetric and some code paths currently diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_queries_schema_merge.go b/go/libraries/doltcore/sqle/enginetest/dolt_queries_schema_merge.go index 56763a2ad4e..0bff7e870bf 100644 --- a/go/libraries/doltcore/sqle/enginetest/dolt_queries_schema_merge.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_queries_schema_merge.go @@ -41,6 +41,10 @@ var SchemaChangeTestsForDataConflicts = []MergeScriptTest{ "update t set col1=-1000 where t.pk = 1;", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{{"t", uint64(1), uint64(0)}}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -96,6 +100,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "insert into t values (5, 50, '500'), (6, 60, '600');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -124,6 +132,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "insert into t values (5, 50, '500'), (6, 60, '600');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -159,6 +171,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "insert into t values (5, 50, '500'), (6, 60, '600');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -193,6 +209,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "insert into t values (5, 50, '500'), (6, 60, '600');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -223,6 +243,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "insert into t values (3, 3);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -248,6 +272,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "insert into t values (3);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -273,6 +301,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "insert into t values (3, NULL);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -298,6 +330,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "insert into t values ('3');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -326,6 +362,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "alter table t drop column c1;", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -350,6 +390,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "insert into t values ('3', 'BAD', 'hi');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -376,6 +420,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "insert into t values (5, 50), (6, 60);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -409,6 +457,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "insert into t values (5, 50), (6, 60);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -443,6 +495,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "alter table t add index (col1);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -479,6 +535,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "insert into t values (5, 50, '500'), (6, 60, '600');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -513,6 +573,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "alter table t drop column col1;", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -549,6 +613,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "alter table t add constraint fk1 foreign key (col3) references parent(id);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -580,6 +648,11 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "insert into t values (5, 50), (6, 60);", }, Assertions: []queries.ScriptTestAssertion{ + { + Skip: true, + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Skip: true, Query: "call dolt_merge('right');", @@ -606,6 +679,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "insert into t values (5, 50), (6, 60);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -638,6 +715,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "INSERT INTO t VALUES (UUID(), NOW())", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -662,6 +743,10 @@ var SchemaChangeTestsBasicCases = []MergeScriptTest{ "INSERT INTO t VALUES (5), (6)", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -697,6 +782,10 @@ var SchemaChangeTestsCollations = []MergeScriptTest{ "insert into t values (3, '30');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -727,6 +816,10 @@ var SchemaChangeTestsCollations = []MergeScriptTest{ "insert into t values (3, '30');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + ExpectedErr: merge.ErrDefaultCollationConflict, + }, { Query: "call dolt_merge('right');", ExpectedErr: merge.ErrDefaultCollationConflict, @@ -749,6 +842,10 @@ var SchemaChangeTestsCollations = []MergeScriptTest{ "insert into t values (3, '30');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -1426,6 +1523,10 @@ var SchemaChangeTestsTypeChanges = []MergeScriptTest{ "INSERT into t values (3, '321');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -1456,6 +1557,10 @@ var SchemaChangeTestsTypeChanges = []MergeScriptTest{ "INSERT into t values (3, '321');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -1486,6 +1591,10 @@ var SchemaChangeTestsTypeChanges = []MergeScriptTest{ "INSERT into t values (3, '321');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -1516,6 +1625,10 @@ var SchemaChangeTestsTypeChanges = []MergeScriptTest{ "INSERT into t values (3, '321');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -1546,6 +1659,10 @@ var SchemaChangeTestsTypeChanges = []MergeScriptTest{ "INSERT into t values (3, '321');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -1576,6 +1693,10 @@ var SchemaChangeTestsTypeChanges = []MergeScriptTest{ "INSERT into t values (3, '321');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -1602,6 +1723,10 @@ var SchemaChangeTestsTypeChanges = []MergeScriptTest{ "INSERT into t values (3, '321');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -1628,6 +1753,10 @@ var SchemaChangeTestsTypeChanges = []MergeScriptTest{ "INSERT into t values (3, '321');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -1654,6 +1783,10 @@ var SchemaChangeTestsTypeChanges = []MergeScriptTest{ "INSERT into t values (3, '321');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -1684,6 +1817,10 @@ var SchemaChangeTestsTypeChanges = []MergeScriptTest{ "INSERT into t values (3, 0x010203);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -1721,6 +1858,10 @@ var SchemaChangeTestsTypeChanges = []MergeScriptTest{ "INSERT into t values (3, 0x010203);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -1754,6 +1895,10 @@ var SchemaChangeTestsTypeChanges = []MergeScriptTest{ "INSERT into t values (3, 0x010203);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -1782,6 +1927,10 @@ var SchemaChangeTestsTypeChanges = []MergeScriptTest{ "INSERT into t values (3, 'the teeniest of tiny text');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -1821,6 +1970,10 @@ var SchemaChangeTestsSchemaConflicts = []MergeScriptTest{ "INSERT into t values (2, 'green', 2.0, 2, 0.2, 'two', 'a,b', 1);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{{"t", nil, uint64(4)}}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -1862,6 +2015,10 @@ var SchemaChangeTestsSchemaConflicts = []MergeScriptTest{ "INSERT into t values (2, 'green', 2.0, 2, 0.2, 'two', 'a,b', 1);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{{"t", nil, uint64(7)}}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -1894,6 +2051,10 @@ var SchemaChangeTestsSchemaConflicts = []MergeScriptTest{ "INSERT into t values (3, '300');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -1923,6 +2084,10 @@ var SchemaChangeTestsSchemaConflicts = []MergeScriptTest{ "INSERT into t values (2, 20, '200');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{{"t", nil, uint64(2)}}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -1951,6 +2116,10 @@ var SchemaChangeTestsSchemaConflicts = []MergeScriptTest{ "alter table t modify column j varchar(24);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -1974,6 +2143,10 @@ var SchemaChangeTestsSchemaConflicts = []MergeScriptTest{ "alter table t modify column j float;", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -1999,6 +2172,10 @@ var SchemaChangeTestsSchemaConflicts = []MergeScriptTest{ "insert into t values (5, 50), (6, 60);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -2027,6 +2204,10 @@ var SchemaChangeTestsSchemaConflicts = []MergeScriptTest{ "insert into t values (5, 50), (6, 60);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{{"t", nil, uint64(1)}}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -2055,6 +2236,10 @@ var SchemaChangeTestsSchemaConflicts = []MergeScriptTest{ "UPDATE t SET a = 2;", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -2082,6 +2267,10 @@ var SchemaChangeTestsSchemaConflicts = []MergeScriptTest{ "insert into t values (2, 20);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + ExpectedErr: merge.ErrUnmergeableNewColumn, + }, { Query: "call dolt_merge('right');", ExpectedErr: merge.ErrUnmergeableNewColumn, @@ -2115,6 +2304,10 @@ var SchemaChangeTestsSchemaConflicts = []MergeScriptTest{ "insert into t values (5, 50, '500'), (6, 60, '600');", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { // See the comment above about why this should NOT report a conflict and why this is skipped Skip: true, @@ -2144,6 +2337,10 @@ var SchemaChangeTestsSchemaConflicts = []MergeScriptTest{ "insert into t values (3, 3);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -2175,6 +2372,10 @@ var SchemaChangeTestsSchemaConflicts = []MergeScriptTest{ "insert into t values (3, 3);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { SkipResultsCheck: true, Query: "call dolt_merge('right');", @@ -2325,6 +2526,10 @@ var SchemaChangeTestsGeneratedColumns = []MergeScriptTest{ "insert into t (pk, col1) values (5, 50), (6, 60);", }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -2590,6 +2795,10 @@ var SchemaChangeTestsForJsonConflicts = []MergeScriptTest{ `update t set j = '{"b": 2}';`, }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{doltCommit, 0, 0, "merge successful"}}, @@ -2619,6 +2828,10 @@ var SchemaChangeTestsForJsonConflicts = []MergeScriptTest{ `update t set j = '{"b": 2}';`, }, Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_preview_merge_conflicts_summary('main', 'right');", + Expected: []sql.Row{{"t", uint64(1), uint64(0)}}, + }, { Query: "call dolt_merge('right');", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_transaction_queries.go b/go/libraries/doltcore/sqle/enginetest/dolt_transaction_queries.go index ce30fb8efad..727090df20f 100755 --- a/go/libraries/doltcore/sqle/enginetest/dolt_transaction_queries.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_transaction_queries.go @@ -1046,6 +1046,10 @@ var DoltConflictHandlingTests = []queries.TransactionTest{ Query: "/* client b */ call dolt_commit('-am', 'commit on new-branch')", SkipResultsCheck: true, }, + { + Query: "/* client b */ select * from dolt_preview_merge_conflicts_summary('new-branch', 'main')", + Expected: []sql.Row{{"test", uint64(1), uint64(0)}}, + }, { Query: "/* client b */ call dolt_merge('main')", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -1137,6 +1141,10 @@ var DoltConflictHandlingTests = []queries.TransactionTest{ Query: "/* client b */ call dolt_commit('-am', 'commit on new-branch')", SkipResultsCheck: true, }, + { + Query: "/* client b */ select * from dolt_preview_merge_conflicts_summary('new-branch', 'main')", + Expected: []sql.Row{{"test", uint64(1), uint64(0)}}, + }, { Query: "/* client b */ call dolt_merge('main')", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -1226,6 +1234,10 @@ var DoltConflictHandlingTests = []queries.TransactionTest{ Query: "/* client b */ call dolt_commit('-am', 'commit on new-branch')", SkipResultsCheck: true, }, + { + Query: "/* client b */ select * from dolt_preview_merge_conflicts_summary('new-branch', 'main')", + Expected: []sql.Row{{"test", uint64(1), uint64(0)}}, + }, { Query: "/* client b */ call dolt_merge('main')", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -1459,6 +1471,14 @@ var DoltStoredProcedureTransactionTests = []queries.TransactionTest{ Query: "/* client b */ start transaction", Expected: []sql.Row{}, }, + { + Query: "/* client b */ select * from dolt_preview_merge_conflicts_summary('main', 'feature-branch')", + Expected: []sql.Row{{"test", uint64(1), uint64(0)}}, + }, + { + Query: "/* client a */ select * from dolt_preview_merge_conflicts_summary('main', 'feature-branch')", + Expected: []sql.Row{{"test", uint64(1), uint64(0)}}, + }, { Query: "/* client a */ CALL DOLT_MERGE('feature-branch')", Expected: []sql.Row{{"", 0, 1, "conflicts found"}}, @@ -1507,6 +1527,10 @@ var DoltStoredProcedureTransactionTests = []queries.TransactionTest{ Query: "/* client a */ SET @@dolt_allow_commit_conflicts = 0", Expected: []sql.Row{{}}, }, + { + Query: "/* client a */ select * from dolt_preview_merge_conflicts_summary('main', 'feature-branch')", + Expected: []sql.Row{{"test", uint64(1), uint64(0)}}, + }, { Query: "/* client a */ CALL DOLT_MERGE('feature-branch')", ExpectedErrStr: dsess.ErrUnresolvedConflictsAutoCommit.Error(), diff --git a/integration-tests/ORMDockerfile b/integration-tests/ORMDockerfile index 1d60acbeefb..b9316b3bc68 100644 --- a/integration-tests/ORMDockerfile +++ b/integration-tests/ORMDockerfile @@ -4,23 +4,23 @@ FROM --platform=${BUILDPLATFORM} ubuntu:20.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt update -y && \ apt install -y \ - curl \ - gnupg \ - software-properties-common && \ + curl \ + gnupg \ + software-properties-common && \ curl -sL https://deb.nodesource.com/setup_22.x | bash - RUN apt update -y && \ apt install -y \ - nodejs \ - python3.9 \ - python3-pip \ - git \ - mysql-client \ - libmysqlclient-dev \ - # weird issue: installing openjdk-17-jdk errors if `maven` or possibly any other package is not installed after it - openjdk-17-jdk \ - # currently, `apt install maven` installs v3.6.0 which does not work with openjdk-17-jdk - maven \ - bats && \ + nodejs \ + python3.9 \ + python3-pip \ + git \ + mysql-client \ + libmysqlclient-dev \ + # weird issue: installing openjdk-17-jdk errors if `maven` or possibly any other package is not installed after it + openjdk-17-jdk \ + # currently, `apt install maven` installs v3.6.0 which does not work with openjdk-17-jdk + maven \ + bats && \ update-ca-certificates -f # install go @@ -42,14 +42,14 @@ RUN pip3 install mysql-connector-python PyMySQL sqlalchemy # Setup JAVA_HOME -- useful for docker commandline ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64/ -# install the current latest maven version, `v3.9.9`, because apt installed one does not work with jdk 17 -ADD https://dlcdn.apache.org/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz apache-maven-3.9.9-bin.tar.gz -RUN tar zxvf apache-maven-3.9.9-bin.tar.gz && \ - cp -r apache-maven-3.9.9 /opt && \ - rm -rf apache-maven-3.9.9 apache-maven-3.9.9-bin.tar.gz +# install the current latest maven version, `v3.9.10`, because apt installed one does not work with jdk 17 +ADD https://dlcdn.apache.org/maven/maven-3/3.9.10/binaries/apache-maven-3.9.10-bin.tar.gz apache-maven-3.9.10-bin.tar.gz +RUN tar zxvf apache-maven-3.9.10-bin.tar.gz && \ + cp -r apache-maven-3.9.10 /opt && \ + rm -rf apache-maven-3.9.10 apache-maven-3.9.10-bin.tar.gz # add maven binary -ENV PATH=/opt/apache-maven-3.9.9/bin:$PATH +ENV PATH=/opt/apache-maven-3.9.10/bin:$PATH # install dolt from source WORKDIR /root/building