allow init with empty working set (#4255)

This commit is contained in:
jennifersp
2022-09-02 13:21:27 -07:00
committed by GitHub
parent f6a8b6fd05
commit 6910ac96b7
11 changed files with 126 additions and 121 deletions

View File

@@ -243,6 +243,12 @@ func createBranch(ctx context.Context, dbData env.DbData, newBranch, startingPoi
// should be the pre-checkout head. The returned roots struct has |Head| set to |branchRoot|.
func UpdateRootsForBranch(ctx context.Context, roots doltdb.Roots, branchRoot *doltdb.RootValue, force bool) (doltdb.Roots, error) {
conflicts := set.NewStrSet([]string{})
if roots.Head == nil {
roots.Working = branchRoot
roots.Staged = branchRoot
roots.Head = branchRoot
return roots, nil
}
wrkTblHashes, err := moveModifiedTables(ctx, roots.Head, branchRoot, roots.Working, conflicts, force)
if err != nil {
@@ -314,10 +320,10 @@ func CheckoutBranch(ctx context.Context, dEnv *env.DoltEnv, brName string, force
}
roots, err := dEnv.Roots(ctx)
if errors.Is(err, doltdb.ErrBranchNotFound) {
// roots will be empty/nil if the working set is not set (working set is not set if the current branch was deleted)
if errors.Is(err, doltdb.ErrBranchNotFound) || errors.Is(err, doltdb.ErrWorkingSetNotFound) {
roots, err = dEnv.RecoveryRoots(ctx)
}
if err != nil {
} else if err != nil {
return err
}

View File

@@ -175,11 +175,8 @@ func loadWithFormat(ctx context.Context, hdp HomeDirProvider, fs filesys.Filesys
if rsErr == nil && dbLoadErr == nil && dbFormatErr == nil {
// If the working set isn't present in the DB, create it from the repo state. This step can be removed post 1.0.
_, err := dEnv.WorkingSet(ctx)
if err == doltdb.ErrWorkingSetNotFound {
err := dEnv.initWorkingSetFromRepoState(ctx)
if err != nil {
dEnv.RSLoadErr = err
}
if errors.Is(err, doltdb.ErrWorkingSetNotFound) {
_ = dEnv.initWorkingSetFromRepoState(ctx)
} else if err != nil {
dEnv.RSLoadErr = err
}

View File

@@ -96,9 +96,9 @@ func DoDoltBranch(ctx *sql.Context, args []string) (int, error) {
case apr.Contains(cli.CopyFlag):
err = copyBranch(ctx, dbData, apr)
case apr.Contains(cli.MoveFlag):
err = renameBranch(ctx, dbData, apr)
err = renameBranch(ctx, dbData, apr, dSess, dbName)
case apr.Contains(cli.DeleteFlag), apr.Contains(cli.DeleteForceFlag):
err = deleteBranches(ctx, apr, dbData)
err = deleteBranches(ctx, dbData, apr, dSess, dbName)
default:
err = createNewBranch(ctx, dbData, apr)
}
@@ -110,7 +110,9 @@ func DoDoltBranch(ctx *sql.Context, args []string) (int, error) {
}
}
func renameBranch(ctx *sql.Context, dbData env.DbData, apr *argparser.ArgParseResults) error {
// renameBranch takes DoltSession and database name to try accessing file system for dolt database.
// If the oldBranch being renamed is the current branch on CLI, then RepoState head will be updated with the newBranch ref.
func renameBranch(ctx *sql.Context, dbData env.DbData, apr *argparser.ArgParseResults, sess *dsess.DoltSession, dbName string) error {
if apr.NArg() != 2 {
return InvalidArgErr
}
@@ -127,14 +129,46 @@ func renameBranch(ctx *sql.Context, dbData env.DbData, apr *argparser.ArgParseRe
}
}
return actions.RenameBranch(ctx, dbData, loadConfig(ctx), oldBranchName, newBranchName, force)
err := actions.RenameBranch(ctx, dbData, loadConfig(ctx), oldBranchName, newBranchName, force)
if err != nil {
return err
}
// The current branch on CLI can be deleted as user can be on different branch on SQL and delete it from SQL session.
// To update current head info on RepoState, we need DoltEnv to load CLI environment.
if fs, err := sess.Provider().FileSystemForDatabase(dbName); err == nil {
if repoState, err := env.LoadRepoState(fs); err == nil {
if repoState.Head.Ref.GetPath() == oldBranchName {
repoState.Head.Ref = ref.NewBranchRef(newBranchName)
repoState.Save(fs)
}
}
}
return nil
}
func deleteBranches(ctx *sql.Context, apr *argparser.ArgParseResults, dbData env.DbData) error {
// deleteBranches takes DoltSession and database name to try accessing file system for dolt database.
// If the database is not session state db and the branch being deleted is the current branch on CLI, it will update
// the RepoState to set head as empty branchRef.
func deleteBranches(ctx *sql.Context, dbData env.DbData, apr *argparser.ArgParseResults, sess *dsess.DoltSession, dbName string) error {
if apr.NArg() == 0 {
return InvalidArgErr
}
// The current branch on CLI can be deleted as user can be on different branch on SQL and delete it from SQL session.
// To update current head info on RepoState, we need DoltEnv to load CLI environment.
var rs *env.RepoState
var headOnCLI string
fs, err := sess.Provider().FileSystemForDatabase(dbName)
if err == nil {
if repoState, err := env.LoadRepoState(fs); err == nil {
rs = repoState
headOnCLI = repoState.Head.Ref.GetPath()
}
}
var updateFS = false
for _, branchName := range apr.Args {
if len(branchName) == 0 {
return EmptyBranchNameErr
@@ -142,19 +176,27 @@ func deleteBranches(ctx *sql.Context, apr *argparser.ArgParseResults, dbData env
force := apr.Contains(cli.DeleteForceFlag) || apr.Contains(cli.ForceFlag)
if !force {
err := validateBranchNotActiveInAnySession(ctx, branchName)
err = validateBranchNotActiveInAnySession(ctx, branchName)
if err != nil {
return err
}
}
err := actions.DeleteBranch(ctx, dbData, loadConfig(ctx), branchName, actions.DeleteOptions{
err = actions.DeleteBranch(ctx, dbData, loadConfig(ctx), branchName, actions.DeleteOptions{
Force: force,
})
if err != nil {
return err
}
if headOnCLI == branchName {
updateFS = true
}
}
if fs != nil && updateFS {
rs.Head.Ref = ref.NewBranchRef("")
rs.Save(fs)
}
return nil
}
@@ -220,16 +262,23 @@ func loadConfig(ctx *sql.Context) *env.DoltCliConfig {
}
func createNewBranch(ctx *sql.Context, dbData env.DbData, apr *argparser.ArgParseResults) error {
if apr.NArg() != 1 {
return InvalidArgErr
var branchName string
var startPt = "HEAD"
if apr.NArg() == 1 {
branchName = apr.Arg(0)
} else if apr.NArg() == 2 {
branchName = apr.Arg(0)
startPt = apr.Arg(1)
if len(startPt) == 0 {
return InvalidArgErr
}
}
branchName := apr.Arg(0)
if len(branchName) == 0 {
return EmptyBranchNameErr
}
return actions.CreateBranchWithStartPt(ctx, dbData, branchName, "HEAD", apr.Contains(cli.ForceFlag))
return actions.CreateBranchWithStartPt(ctx, dbData, branchName, startPt, apr.Contains(cli.ForceFlag))
}
func copyBranch(ctx *sql.Context, dbData env.DbData, apr *argparser.ArgParseResults) error {

View File

@@ -15,13 +15,12 @@
package dtables
import (
"errors"
"fmt"
"io"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/ref"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
)
@@ -177,86 +176,23 @@ type branchWriter struct {
bt *BranchesTable
}
func branchAndHashFromRow(r sql.Row) (string, string, error) {
branchName, ok := r[0].(string)
if !ok {
return "", "", errors.New("invalid value type for branch")
} else if !doltdb.IsValidUserBranchName(branchName) {
return "", "", doltdb.ErrInvBranchName
}
commitHash, ok := r[1].(string)
if !ok {
return "", "", errors.New("invalid value type for hash")
}
return branchName, commitHash, nil
}
// Insert inserts the row given, returning an error if it cannot. Insert will be called once for each row to process
// for the insert operation, which may involve many rows. After all rows in an operation have been processed, Close
// is called.
func (bWr branchWriter) Insert(ctx *sql.Context, r sql.Row) error {
branchName, commitHash, err := branchAndHashFromRow(r)
if err != nil {
return err
}
cs, err := doltdb.NewCommitSpec(commitHash)
if err != nil {
return err
}
ddb := bWr.bt.ddb
cm, err := ddb.Resolve(ctx, cs, nil)
if err != nil {
return err
}
branchRef := ref.NewBranchRef(branchName)
// TODO: this isn't safe in a SQL context, since we have to update the working set of the new branch and it's a
// race. It needs to be able to retry the same as committing a transaction.
err = ddb.NewBranchAtCommit(ctx, branchRef, cm)
if err != nil {
return err
}
return bWr.bt.ddb.ExecuteCommitHooks(ctx, branchRef.String())
return fmt.Errorf("the dolt_branches table is read-only; use the dolt_branch stored procedure to edit remotes")
}
// Update the given row. Provides both the old and new rows.
func (bWr branchWriter) Update(ctx *sql.Context, old sql.Row, new sql.Row) error {
return bWr.Insert(ctx, new)
return fmt.Errorf("the dolt_branches table is read-only; use the dolt_branch stored procedure to edit remotes")
}
// Delete deletes the given row. Returns ErrDeleteRowNotFound if the row was not found. Delete will be called once for
// each row to process for the delete operation, which may involve many rows. After all rows have been processed,
// Close is called.
func (bWr branchWriter) Delete(ctx *sql.Context, r sql.Row) error {
branchName, _, err := branchAndHashFromRow(r)
if err != nil {
return err
}
brRef := ref.NewBranchRef(branchName)
exists, err := bWr.bt.ddb.HasRef(ctx, brRef)
if err != nil {
return err
}
if !exists {
return sql.ErrDeleteRowNotFound.New()
}
return bWr.bt.ddb.DeleteBranch(ctx, brRef)
return fmt.Errorf("the dolt_branches table is read-only; use the dolt_branch stored procedure to edit remotes")
}
// StatementBegin implements the interface sql.TableEditor. Currently a no-op.

View File

@@ -453,7 +453,7 @@ func (d *DoltHarness) SnapshotTable(db sql.VersionedDatabase, name string, asOf
// TODO: there's a bug in test setup with transactions, where the HEAD session var gets overwritten on transaction
// start, so we quote it here instead
// query := "insert into dolt_branches (name, hash) values ('" + asOfString + "', @@" + dsess.HeadKey(ddb.Name()) + ")"
query := "insert into dolt_branches (name, hash) values ('" + asOfString + "', '" + headHash.(string) + "')"
query := "CALL dolt_branch('" + asOfString + "', '" + headHash.(string) + "')"
_, iter, err = e.Query(ctx,
query)

View File

@@ -2199,7 +2199,7 @@ var MergeScripts = []queries.ScriptTest{
"CREATE TRIGGER trigger1 BEFORE INSERT ON x FOR EACH ROW SET new.a = new.a + 1",
"CALL dolt_add('-A')",
"CALL dolt_commit('-m', 'added table with trigger')",
"INSERT INTO dolt_branches (name, hash) VALUES ('other',hashof('main'))",
"CALL dolt_branch('-c', 'main', 'other')",
// create trigger2 on main
"CREATE TRIGGER trigger2 BEFORE INSERT ON x FOR EACH ROW SET new.a = (new.a * 2) + 10",
"CALL dolt_commit('-am', 'created trigger2 on main')",

View File

@@ -21,16 +21,45 @@ make_it() {
dolt branch -c main to_keep
}
@test "deleted-branches: can checkout existing branch after checked out branch is deleted" {
@test "deleted-branches: can checkout existing branch after checked out branch on CLI is deleted" {
make_it
dolt sql -q 'delete from dolt_branches where name = "main"'
run dolt status
[[ "$output" =~ "On branch main" ]] || false
run dolt sql -q 'call dolt_branch("-D", "main");'
[ $status -eq 1 ]
[[ "$output" =~ "attempted to delete checked out branch" ]] || false
dolt sql -q 'call dolt_checkout("to_keep"); call dolt_branch("-D", "main");'
dolt branch -av
dolt checkout to_keep
}
@test "deleted-branches: attempt to delete the last branch when currently on no branch" {
make_it
dolt sql -q 'call dolt_checkout("to_keep"); call dolt_branch("-D", "main");'
dolt branch -av
run dolt branch -D to_keep
[[ "$output" =~ "cannot delete the last branch" ]] || false
}
@test "deleted-branches: renaming current branch on CLI deletes that branch and sets the current branch to the new branch on CLI" {
make_it
dolt sql -q 'call dolt_checkout("to_keep"); call dolt_branch("-m", "main", "master");'
dolt branch -av
run dolt status
[[ "$output" =~ "On branch master" ]] || false
}
@test "deleted-branches: can SQL connect with dolt_default_branch set to existing branch when checked out branch is deleted" {
make_it
@@ -38,7 +67,7 @@ make_it() {
server_query "dolt_repo_$$" 1 dolt "" "SET @@GLOBAL.dolt_repo_$$_default_branch = 'to_keep'" ""
server_query "dolt_repo_$$" 1 dolt "" 'delete from dolt_branches where name = "main"' ""
server_query "dolt_repo_$$" 1 dolt "" 'call dolt_checkout("to_keep"); call dolt_branch("-D", "main");' ""
server_query "dolt_repo_$$" 1 dolt "" "SELECT * FROM test" "id\n" ""
}
@@ -48,7 +77,7 @@ make_it() {
start_sql_server "dolt_repo_$$"
server_query "dolt_repo_$$" 1 dolt "" 'delete from dolt_branches where name = "main"' ""
server_query "dolt_repo_$$" 1 dolt "" 'call dolt_checkout("to_keep"); call dolt_branch("-D", "main");' ""
# Against the default branch it fails
run server_query "dolt_repo_$$" 1 "" dolt "" "SELECT * FROM test" "id\n" ""
@@ -93,7 +122,7 @@ make_it() {
start_sql_server "dolt_repo_$$"
server_query "dolt_repo_$$" 1 dolt "" 'delete from dolt_branches where name = "main"' ""
server_query "dolt_repo_$$" 1 dolt "" 'call dolt_checkout("to_keep"); call dolt_branch("-D", "main");' ""
# We are able to use a database branch revision in the connection string
server_query "dolt_repo_$$/to_keep" 1 dolt "" "SELECT * FROM test;"
@@ -111,7 +140,7 @@ make_it() {
server_query "dolt_repo_$$" 1 dolt "" "SET @@GLOBAL.dolt_repo_$$_default_branch = 'to_keep'" ""
server_query "dolt_repo_$$" 1 dolt "" 'delete from dolt_branches where name = "main"' ""
server_query "dolt_repo_$$" 1 dolt "" 'call dolt_checkout("to_keep"); call dolt_branch("-D", "main");' ""
server_query "dolt_repo_$$" 1 dolt "" "SELECT * FROM test" ""

View File

@@ -160,22 +160,6 @@ teardown() {
[[ "$output" =~ "t1" ]] || false
}
@test "replication: push on branch table update" {
cd repo1
dolt config --local --add sqlserver.global.dolt_replicate_to_remote backup1
dolt sql -q "create table t1 (a int primary key)"
dolt add -A
dolt sql -q "UPDATE dolt_branches SET hash = COMMIT('--author', '{user_name} <{email_address}>','-m', 'cm') WHERE name = 'main' AND hash = @@repo1_head"
cd ..
dolt clone file://./bac1 repo2
cd repo2
run dolt ls
[ "$status" -eq 0 ]
[ "${#lines[@]}" -eq 2 ]
[[ "$output" =~ "t1" ]] || false
}
@test "replication: push on call dolt_branch(..." {
cd repo1
dolt config --local --add sqlserver.global.dolt_replicate_to_remote backup1

View File

@@ -119,7 +119,6 @@ SQL
dolt sql -b <<SQL
SELECT dolt_reset('--hard');
REPLACE INTO dolt_branches (hash,name) VALUES (@@test_head,'main');
SQL
run dolt status
@@ -129,7 +128,6 @@ SQL
dolt sql -b <<SQL
INSERT INTO test VALUES (1,1);
SELECT dolt_reset('--hard');
REPLACE INTO dolt_branches (hash,name) VALUES (@@test_head,'main');
SQL
run dolt status

View File

@@ -423,9 +423,7 @@ SQL
[ "$status" -eq 0 ]
[[ "$output" =~ "test" ]] || false
server_query repo1 1 dolt "" "
SELECT DOLT_RESET('--hard');
REPLACE INTO dolt_branches (name,hash) VALUES ('main', @@repo1_head);"
server_query repo1 1 dolt "" "SELECT DOLT_RESET('--hard');"
run dolt status
[ "$status" -eq 0 ]
@@ -436,8 +434,7 @@ SQL
server_query repo1 1 dolt "" "
INSERT INTO test VALUES (8,8);
SELECT DOLT_RESET('--hard');
REPLACE INTO dolt_branches (name,hash) VALUES ('main', @@repo1_head);"
SELECT DOLT_RESET('--hard');"
run dolt status
[ "$status" -eq 0 ]
@@ -777,7 +774,7 @@ SQL
INSERT INTO one_pk (pk,c1,c2) VALUES (2,2,2),(3,3,3);
CALL DOLT_ADD('.');
SELECT commit('-am', 'test commit message', '--author', 'John Doe <john@example.com>');
INSERT INTO dolt_branches (name,hash) VALUES ('main', @@repo1_head);"
CALL DOLT_BRANCH('main', @@repo1_head);"
server_query repo1 1 dolt "" "call dolt_add('.')" "status\n0"
run dolt ls

View File

@@ -528,9 +528,18 @@ SQL
[[ "$output" =~ "1" ]] || false
}
@test "system-tables: cannot delete last branch in dolt_branches" {
@test "system-tables: dolt_branches is read-only" {
run dolt sql -q "DELETE FROM dolt_branches"
[ "$status" -ne 0 ]
[[ "$output" =~ "read-only" ]] || false
run dolt sql -q "INSERT INTO dolt_branches (name,hash) VALUES ('branch1', 'main');"
[ "$status" -ne 0 ]
[[ "$output" =~ "read-only" ]] || false
run dolt sql -q "UPDATE dolt_branches SET name = 'branch1' WHERE name = 'main'"
[ "$status" -ne 0 ]
[[ "$output" =~ "read-only" ]] || false
}
@test "system-tables: dolt diff includes changes from initial commit" {