diff --git a/go/libraries/doltcore/sqle/dfunctions/has_ancestor.go b/go/libraries/doltcore/sqle/dfunctions/has_ancestor.go index 03a9d927413..d11b0d2852c 100644 --- a/go/libraries/doltcore/sqle/dfunctions/has_ancestor.go +++ b/go/libraries/doltcore/sqle/dfunctions/has_ancestor.go @@ -69,19 +69,10 @@ func (a *HasAncestor) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { if err != nil { return nil, err } - - cs, err := doltdb.NewCommitSpec(headStr.(string)) + headCommit, err = resolveRefSpec(ctx, headRef, ddb, headStr.(string)) if err != nil { return nil, err } - optCmt, err := ddb.Resolve(ctx, cs, headRef) - if err != nil { - return nil, fmt.Errorf("error during has_ancestor check: ref not found '%s'", headStr) - } - headCommit, ok = optCmt.ToCommit() - if !ok { - return nil, doltdb.ErrGhostCommitEncountered - } } var ancCommit *doltdb.Commit @@ -94,19 +85,10 @@ func (a *HasAncestor) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { if err != nil { return nil, err } - cs, err := doltdb.NewCommitSpec(ancStr.(string)) + ancCommit, err = resolveRefSpec(ctx, headRef, ddb, ancStr.(string)) if err != nil { return nil, err } - optCmt, err := ddb.Resolve(ctx, cs, headRef) - if err != nil { - return nil, fmt.Errorf("error during has_ancestor check: ref not found '%s'", ancStr) - } - ancCommit, ok = optCmt.ToCommit() - if !ok { - return nil, doltdb.ErrGhostCommitEncountered - } - } headHash, err := headCommit.HashOf() diff --git a/go/libraries/doltcore/sqle/dfunctions/dolt_merge_base.go b/go/libraries/doltcore/sqle/dfunctions/merge_base.go similarity index 78% rename from go/libraries/doltcore/sqle/dfunctions/dolt_merge_base.go rename to go/libraries/doltcore/sqle/dfunctions/merge_base.go index e8d139ca2dd..95014aed722 100644 --- a/go/libraries/doltcore/sqle/dfunctions/dolt_merge_base.go +++ b/go/libraries/doltcore/sqle/dfunctions/merge_base.go @@ -24,6 +24,7 @@ import ( "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" "github.com/dolthub/dolt/go/libraries/doltcore/merge" + "github.com/dolthub/dolt/go/libraries/doltcore/ref" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess" ) @@ -40,6 +41,21 @@ func NewMergeBase(left, right sql.Expression) sql.Expression { // Eval implements the sql.Expression interface. func (d MergeBase) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { + sess := dsess.DSessFromSess(ctx.Session) + dbName := ctx.GetCurrentDatabase() + dbData, ok := sess.GetDbData(ctx, dbName) + if !ok { + return nil, sql.ErrDatabaseNotFound.New(dbName) + } + doltDB, ok := sess.GetDoltDB(ctx, dbName) + if !ok { + return nil, sql.ErrDatabaseNotFound.New(dbName) + } + headRef, err := dbData.Rsr.CWBHeadRef(ctx) + if err != nil { + return nil, err + } + leftSpec, err := d.Left().Eval(ctx, row) if err != nil { return nil, err @@ -48,7 +64,6 @@ func (d MergeBase) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { if err != nil { return nil, err } - if leftSpec == nil || rightSpec == nil { return nil, nil } @@ -57,13 +72,16 @@ func (d MergeBase) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { if !ok { return nil, errors.New("left value is not a string") } - rightStr, ok := rightSpec.(string) if !ok { return nil, errors.New("right value is not a string") } - left, right, err := resolveRefSpecs(ctx, leftStr, rightStr) + left, err := resolveRefSpec(ctx, headRef, doltDB, leftStr) + if err != nil { + return nil, err + } + right, err := resolveRefSpec(ctx, headRef, doltDB, rightStr) if err != nil { return nil, err } @@ -76,52 +94,20 @@ func (d MergeBase) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { return mergeBase.String(), nil } -func resolveRefSpecs(ctx *sql.Context, leftSpec, rightSpec string) (left, right *doltdb.Commit, err error) { - lcs, err := doltdb.NewCommitSpec(leftSpec) - if err != nil { - return nil, nil, err - } - rcs, err := doltdb.NewCommitSpec(rightSpec) +func resolveRefSpec(ctx *sql.Context, headRef ref.DoltRef, doltDB *doltdb.DoltDB, spec string) (*doltdb.Commit, error) { + cs, err := doltdb.NewCommitSpec(spec) if err != nil { - return nil, nil, err - } - - sess := dsess.DSessFromSess(ctx.Session) - dbName := ctx.GetCurrentDatabase() - - dbData, ok := sess.GetDbData(ctx, dbName) - if !ok { - return nil, nil, sql.ErrDatabaseNotFound.New(dbName) - } - doltDB, ok := sess.GetDoltDB(ctx, dbName) - if !ok { - return nil, nil, sql.ErrDatabaseNotFound.New(dbName) - } - - headRef, err := dbData.Rsr.CWBHeadRef(ctx) - if err != nil { - return nil, nil, err - } - - optCmt, err := doltDB.Resolve(ctx, lcs, headRef) - if err != nil { - return nil, nil, err - } - left, ok = optCmt.ToCommit() - if !ok { - return nil, nil, doltdb.ErrGhostCommitEncountered + return nil, err } - - optCmt, err = doltDB.Resolve(ctx, rcs, headRef) + optCmt, err := doltDB.Resolve(ctx, cs, headRef) if err != nil { - return nil, nil, err + return nil, err } - right, ok = optCmt.ToCommit() + commit, ok := optCmt.ToCommit() if !ok { - return nil, nil, doltdb.ErrGhostCommitEncountered + return nil, doltdb.ErrGhostCommitEncountered } - - return + return commit, err } // String implements the sql.Expression interface. diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_branch_status_table_function.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_branch_status_table_function.go new file mode 100644 index 00000000000..8a2ff0b46f7 --- /dev/null +++ b/go/libraries/doltcore/sqle/dtablefunctions/dolt_branch_status_table_function.go @@ -0,0 +1,215 @@ +// 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 ( + "fmt" + "strings" + + "github.com/dolthub/go-mysql-server/sql" + "github.com/dolthub/go-mysql-server/sql/types" + + "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" + "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess" +) + +var _ sql.TableFunction = (*BranchStatusTableFunction)(nil) + +type BranchStatusTableFunction struct { + db sql.Database + exprs []sql.Expression +} + +// NewInstance creates a new instance of TableFunction interface +func (b *BranchStatusTableFunction) NewInstance(ctx *sql.Context, db sql.Database, args []sql.Expression) (sql.Node, error) { + if len(args) == 0 { + return nil, sql.ErrInvalidArgumentNumber.New(b.Name(), "at least 1", len(args)) + } + return &BranchStatusTableFunction{ + db: db, + exprs: args, + }, nil +} + +// Name implements the sql.Node interface +func (b *BranchStatusTableFunction) Name() string { + return "DOLT_BRANCH_STATUS" +} + +// String implements the Stringer interface +func (b *BranchStatusTableFunction) String() string { + exprStrs := make([]string, len(b.exprs)) + for i, expr := range b.exprs { + exprStrs[i] = expr.String() + } + return fmt.Sprintf("%s(%s)", b.Name(), strings.Join(exprStrs, ", ")) +} + +// Resolved implements the sql.Resolvable interface +func (b *BranchStatusTableFunction) Resolved() bool { + for _, expr := range b.exprs { + if !expr.Resolved() { + return false + } + } + return true +} + +// Expressions implements the sql.Expressioner interface +func (b *BranchStatusTableFunction) Expressions() []sql.Expression { + return b.exprs +} + +// WithExpressions implements the sql.Expressioner interface +func (b *BranchStatusTableFunction) WithExpressions(exprs ...sql.Expression) (sql.Node, error) { + nd := *b + nd.exprs = exprs + return &nd, nil +} + +// Database implements the sql.Databaser interface +func (b *BranchStatusTableFunction) Database() sql.Database { + return b.db +} + +// WithDatabase implements the sql.Databaser interface +func (b *BranchStatusTableFunction) WithDatabase(db sql.Database) (sql.Node, error) { + nd := *b + nd.db = db + return &nd, nil +} + +// IsReadOnly implements the sql.Node interface +func (b *BranchStatusTableFunction) IsReadOnly() bool { + return true +} + +// Schema implements the sql.Node interface +func (b *BranchStatusTableFunction) Schema() sql.Schema { + return sql.Schema{ + &sql.Column{Name: "branch", Type: types.Text, Nullable: false}, + &sql.Column{Name: "commits_ahead", Type: types.Uint64, Nullable: false}, + &sql.Column{Name: "commits_behind", Type: types.Uint64, Nullable: false}, + } +} + +// Children implements the sql.Node interface +func (b *BranchStatusTableFunction) Children() []sql.Node { + return nil +} + +// WithChildren implements the sql.Node interface +func (b *BranchStatusTableFunction) WithChildren(children ...sql.Node) (sql.Node, error) { + return b, nil +} + +// RowIter implements the sql.Node interface +func (b *BranchStatusTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.RowIter, error) { + sqlDb, ok := b.db.(dsess.SqlDatabase) + if !ok { + return nil, fmt.Errorf("unable to get dolt database") + } + ddb := sqlDb.DbData().Ddb + + sess := dsess.DSessFromSess(ctx.Session) + dbName := sess.Session.GetCurrentDatabase() + headRef, err := sess.CWBHeadRef(ctx, dbName) + if err != nil { + return nil, err + } + + specs, err := mustExpressionsToString(ctx, b.exprs) + if err != nil { + return nil, err + } + if len(specs) == 0 { + return nil, sql.ErrInvalidArgumentNumber.New(b.Name(), "at least 1", 0) + } + if len(specs) == 1 { + return sql.RowsToRowIter(), nil + } + + commits := make([]*doltdb.Commit, len(specs)) + for i, spec := range specs { + cs, cErr := doltdb.NewCommitSpec(spec) + if cErr != nil { + return nil, cErr + } + optCmt, oErr := ddb.Resolve(ctx, cs, headRef) + if oErr != nil { + return nil, oErr + } + commit, optCommitOk := optCmt.ToCommit() + if !optCommitOk { + return nil, doltdb.ErrGhostCommitEncountered + } + commits[i] = commit + } + + baseCommit := commits[0] + branchCommits := commits[1:] + + baseHash, err := baseCommit.HashOf() + if err != nil { + return nil, err + } + baseCommitClosure, err := baseCommit.GetCommitClosure(ctx) + if err != nil { + return nil, err + } + baseAncestors, err := baseCommitClosure.AsHashSet(ctx) + if err != nil { + return nil, err + } + baseAncestors.Insert(baseHash) + + var rows []sql.Row + for i, branchCommit := range branchCommits { + branchHash, hErr := branchCommit.HashOf() + if hErr != nil { + return nil, hErr + } + + // same commit will have no differences + var ahead, behind uint64 + if branchHash.Equal(baseHash) { + rows = append(rows, sql.Row{specs[i+1], ahead, behind}) + continue + } + + branchCommitClosure, bErr := branchCommit.GetCommitClosure(ctx) + if bErr != nil { + return nil, bErr + } + branchAncestors, bErr := branchCommitClosure.AsHashSet(ctx) + if bErr != nil { + return nil, bErr + } + branchAncestors.Insert(branchHash) + for branchAncestor := range branchAncestors { + if !baseAncestors.Has(branchAncestor) { + ahead++ + } + } + for baseAncestor := range baseAncestors { + if !branchAncestors.Has(baseAncestor) { + behind++ + } + } + rows = append(rows, sql.Row{specs[i+1], ahead, behind}) + } + + return sql.RowsToRowIter(rows...), nil +} diff --git a/go/libraries/doltcore/sqle/dtablefunctions/init.go b/go/libraries/doltcore/sqle/dtablefunctions/init.go index 01af6367a93..ea284dff5e3 100644 --- a/go/libraries/doltcore/sqle/dtablefunctions/init.go +++ b/go/libraries/doltcore/sqle/dtablefunctions/init.go @@ -20,6 +20,7 @@ var DoltTableFunctions = []sql.TableFunction{ &DiffTableFunction{}, &DiffStatTableFunction{}, &DiffSummaryTableFunction{}, + &BranchStatusTableFunction{}, &LogTableFunction{}, &PatchTableFunction{}, &SchemaDiffTableFunction{}, diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_engine_test.go b/go/libraries/doltcore/sqle/enginetest/dolt_engine_test.go index 53a310b7fe7..2971986b29d 100644 --- a/go/libraries/doltcore/sqle/enginetest/dolt_engine_test.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_engine_test.go @@ -113,7 +113,7 @@ func TestSchemaOverridesWithAdaptiveEncoding(t *testing.T) { // Convenience test for debugging a single query. Unskip and set to the desired query. func TestSingleScript(t *testing.T) { - //t.Skip() + t.Skip() var scripts = []queries.ScriptTest{ { Name: "Database syntax properly handles inter-CALL communication", @@ -1569,6 +1569,16 @@ func TestLogTableFunctionPrepared(t *testing.T) { RunLogTableFunctionTestsPrepared(t, harness) } +func TestBranchStatusTableFunction(t *testing.T) { + harness := newDoltEnginetestHarness(t) + RunBranchStatusTableFunctionTests(t, harness) +} + +func TestBranchStatusTableFunctionPrepared(t *testing.T) { + harness := newDoltEnginetestHarness(t) + RunBranchStatusTableFunctionTestsPrepared(t, harness) +} + func TestDoltReflog(t *testing.T) { h := newDoltEnginetestHarness(t) RunDoltReflogTests(t, h) diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_engine_tests.go b/go/libraries/doltcore/sqle/enginetest/dolt_engine_tests.go index afedeee59ba..aeb61a19083 100755 --- a/go/libraries/doltcore/sqle/enginetest/dolt_engine_tests.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_engine_tests.go @@ -1272,6 +1272,30 @@ func RunLogTableFunctionTestsPrepared(t *testing.T, harness DoltEnginetestHarnes } } +func RunBranchStatusTableFunctionTests(t *testing.T, harness DoltEnginetestHarness) { + for _, test := range BranchStatusTableFunctionScriptTests { + t.Run(test.Name, func(t *testing.T) { + harness = harness.NewHarness(t) + defer harness.Close() + harness.Setup(setup.MydbData) + harness.SkipSetupCommit() + enginetest.TestScript(t, harness, test) + }) + } +} + +func RunBranchStatusTableFunctionTestsPrepared(t *testing.T, harness DoltEnginetestHarness) { + for _, test := range BranchStatusTableFunctionScriptTests { + t.Run(test.Name, func(t *testing.T) { + harness = harness.NewHarness(t) + defer harness.Close() + harness.Setup(setup.MydbData) + harness.SkipSetupCommit() + enginetest.TestScriptPrepared(t, harness, test) + }) + } +} + func RunCommitDiffSystemTableTests(t *testing.T, harness DoltEnginetestHarness) { for _, test := range CommitDiffSystemTableScriptTests { t.Run(test.Name, func(t *testing.T) { diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_queries.go b/go/libraries/doltcore/sqle/enginetest/dolt_queries.go index 22a5a77f117..49d0c3b617f 100644 --- a/go/libraries/doltcore/sqle/enginetest/dolt_queries.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_queries.go @@ -4847,6 +4847,283 @@ var LogTableFunctionScriptTests = []queries.ScriptTest{ }, } +var BranchStatusTableFunctionScriptTests = []queries.ScriptTest{ + { + // * anc + // |\ + // | * b1 + // | | + // | * b2 + // | + // * main + // \ + // * b3 + // | + // * b4 + // \ + // * b5 + Name: "test dolt_branch_status(...)", + SetUpScript: []string{ + "call dolt_branch('b1');", + + "call dolt_commit('-m', 'main', '--allow-empty');", + + "call dolt_checkout('b1');", + "call dolt_commit('-m', 'b1', '--allow-empty');", + + "call dolt_branch('b2');", + "call dolt_checkout('b2');", + "call dolt_commit('-m', 'b2', '--allow-empty');", + + "call dolt_checkout('main');", + "call dolt_branch('b3');", + + "call dolt_checkout('b3');", + "call dolt_commit('-m', 'b3', '--allow-empty');", + + "call dolt_branch('b4');", + "call dolt_checkout('b4');", + "call dolt_commit('-m', 'b4', '--allow-empty');", + + "call dolt_branch('b5');", + "call dolt_checkout('b5');", + "call dolt_commit('-m', 'b5', '--allow-empty');", + + "call dolt_tag('t1', 'b1');", + "call dolt_tag('t2', 'b2');", + "call dolt_tag('t3', 'b3');", + "call dolt_tag('t4', 'b4');", + "call dolt_tag('t5', 'b5');", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_branch_status('main', '');", + ExpectedErrStr: "string is not a valid branch or hash", + }, + { + Query: "select * from dolt_branch_status('main', 'non-existent-branch');", + ExpectedErrStr: "branch not found: non-existent-branch", + }, + { + Query: "select * from dolt_branch_status();", + ExpectedErr: sql.ErrInvalidArgumentNumber, + }, + { + Query: "select * from dolt_branch_status('main');", + Expected: []sql.Row{}, + }, + { + Query: "select * from dolt_branch_status('main', 'main', 'b1', 'b2', 'b3', 'b4', 'b5');", + Expected: []sql.Row{ + {"main", uint64(0), uint64(0)}, + {"b1", uint64(1), uint64(1)}, + {"b2", uint64(2), uint64(1)}, + {"b3", uint64(1), uint64(0)}, + {"b4", uint64(2), uint64(0)}, + {"b5", uint64(3), uint64(0)}, + }, + }, + { + Query: "select * from dolt_branch_status('main', 't1', 't2', 't3', 't4', 't5');", + Expected: []sql.Row{ + {"t1", uint64(1), uint64(1)}, + {"t2", uint64(2), uint64(1)}, + {"t3", uint64(1), uint64(0)}, + {"t4", uint64(2), uint64(0)}, + {"t5", uint64(3), uint64(0)}, + }, + }, + { + Query: "select * from dolt_branch_status('b2', 'b5');", + Expected: []sql.Row{ + {"b5", uint64(4), uint64(2)}, + }, + }, + { + Query: "select * from dolt_branch_status('main', 'b5', 'HEAD', 'HEAD~1', 'HEAD~2');", + Expected: []sql.Row{ + {"b5", uint64(3), uint64(0)}, + {"HEAD", uint64(3), uint64(0)}, + {"HEAD~1", uint64(2), uint64(0)}, + {"HEAD~2", uint64(1), uint64(0)}, + }, + }, + { + Query: "select commits_ahead, commits_behind from dolt_branch_status('main', dolt_hashof('b5'));", + Expected: []sql.Row{ + {uint64(3), uint64(0)}, + }, + }, + }, + }, + { + // * ----------- + // |\ \ + // | * b1c1 * b2c1 + // | | | + // | * b2c2 (b1) * b2c2 + // | | + // * m1 * b2c3 (b2) + // | + // * m2 (main) + Name: "test dolt_branch_status with merge", + SetUpScript: []string{ + "call dolt_branch('b1');", + "call dolt_branch('b2');", + "call dolt_commit('-m', 'm1', '--allow-empty');", + "call dolt_commit('-m', 'm2', '--allow-empty');", + + "call dolt_checkout('b1');", + "call dolt_commit('-m', 'b1c1', '--allow-empty');", + "call dolt_commit('-m', 'b1c2', '--allow-empty');", + + "call dolt_checkout('b2');", + "call dolt_commit('-m', 'b2c1', '--allow-empty');", + "call dolt_commit('-m', 'b2c2', '--allow-empty');", + "call dolt_commit('-m', 'b2c3', '--allow-empty');", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_branch_status('main', 'b1', 'b2');", + Expected: []sql.Row{ + {"b1", uint64(2), uint64(2)}, + {"b2", uint64(3), uint64(2)}, + }, + }, + { + Query: "select * from dolt_branch_status('b1', 'b2');", + Expected: []sql.Row{ + {"b2", uint64(3), uint64(2)}, + }, + }, + { + SkipResultsCheck: true, + Query: "call dolt_merge('b1')", // merge b1 into b2 + }, + { + // * ------------ + // |\ \ + // | \ \ + // | * b1c1 * b2c1 + // | | | + // | * b2c2 (b1) * b1c1 + // | | + // * m1 * b2c2 (b1) + // | | + // | * b1c2 + // | | + // | * b2c3 + // | | + // | * merge b1 (b2) + // * m2 (main) + Query: "select message from dolt_log;", + Expected: []sql.Row{ + {"Merge branch 'b1' into b2"}, + {"b2c3"}, + {"b1c2"}, + {"b2c2"}, + {"b1c1"}, + {"b2c1"}, + {"Initialize data repository"}, + }, + }, + { + Query: "select * from dolt_branch_status('main', 'b1', 'b2');", + Expected: []sql.Row{ + {"b1", uint64(2), uint64(2)}, + {"b2", uint64(6), uint64(2)}, + }, + }, + { + Query: "select * from dolt_branch_status('b1', 'b2');", + Expected: []sql.Row{ + {"b2", uint64(4), uint64(0)}, + }, + }, + }, + }, + { + // * anc + // |\ + // "C1" * \---- * "C7" + // | | + // "C2" * | + // | | + // "C3" * t1 | + // |\ | + // | \---- * t2 "M1" + // | | + // "C4" * * b1 "C8" + // |\ / + // "C5" * \ / + // | * b2 "M2" + // | + // "C6" * main + + Name: "test dolt_branch_status(...)", + SetUpScript: []string{ + "call dolt_tag('anc', 'HEAD');", + "call dolt_branch('b1');", + "call dolt_commit('-m', 'C1', '--allow-empty');", + "call dolt_commit('-m', 'C2', '--allow-empty');", + "call dolt_commit('-m', 'C3', '--allow-empty');", + "call dolt_tag('t1', 'HEAD');", + "call dolt_commit('-m', 'C4', '--allow-empty');", + "call dolt_branch('b2');", + "call dolt_commit('-m', 'C5', '--allow-empty');", + "call dolt_commit('-m', 'C6', '--allow-empty');", + + "call dolt_checkout('b1');", + "call dolt_commit('-m', 'C7', '--allow-empty');", + "call dolt_merge('t1', '-m', 'M1');", + "call dolt_tag('t2', 'HEAD');", + "call dolt_commit('-m', 'C8', '--allow-empty');", + + "call dolt_checkout('b2');", + "call dolt_merge('b1', '-m', 'M2');", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "select * from dolt_branch_status('main', 'b2');", + Expected: []sql.Row{ + {"b2", uint64(4), uint64(2)}, + }, + }, + { + Query: "select * from dolt_branch_status('main', 'b1');", + Expected: []sql.Row{ + {"b1", uint64(3), uint64(3)}, + }, + }, + { + Query: "select * from dolt_branch_status('anc', 'b2');", + Expected: []sql.Row{ + {"b2", uint64(8), uint64(0)}, + }, + }, + { + Query: "select * from dolt_branch_status('b2', 'anc');", + Expected: []sql.Row{ + {"anc", uint64(0), uint64(8)}, + }, + }, + { + Query: "select * from dolt_branch_status('t1', 'anc', 't2','t2~1', 'main', 'b1', 'b2', 'b2^1', 'b2^2' );", + Expected: []sql.Row{ + {"anc", uint64(0), uint64(3)}, + {"t2", uint64(2), uint64(0)}, + {"t2~1", uint64(1), uint64(3)}, + {"main", uint64(3), uint64(0)}, + {"b1", uint64(3), uint64(0)}, + {"b2", uint64(5), uint64(0)}, + {"b2^1", uint64(1), uint64(0)}, + {"b2^2", uint64(3), uint64(0)}, + }, + }, + }, + }, +} + var LargeJsonObjectScriptTests = []queries.ScriptTest{ { Name: "JSON under max length limit",