github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/doltdb/doltdb.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package doltdb
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"fmt"
    21  	"math/rand"
    22  	"strings"
    23  	"time"
    24  
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/dbfactory"
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/ref"
    27  	"github.com/dolthub/dolt/go/libraries/utils/filesys"
    28  	"github.com/dolthub/dolt/go/store/chunks"
    29  	"github.com/dolthub/dolt/go/store/datas"
    30  	"github.com/dolthub/dolt/go/store/hash"
    31  	"github.com/dolthub/dolt/go/store/spec"
    32  	"github.com/dolthub/dolt/go/store/types"
    33  	"github.com/dolthub/dolt/go/store/types/edits"
    34  )
    35  
    36  func init() {
    37  	types.CreateEditAccForMapEdits = func(nbf *types.NomsBinFormat) types.EditAccumulator {
    38  		return edits.NewAsyncSortedEdits(nbf, 16*1024, 4, 2)
    39  	}
    40  }
    41  
    42  const (
    43  	creationBranch   = "create"
    44  	MasterBranch     = "master"
    45  	CommitStructName = "Commit"
    46  
    47  	defaultChunksPerTF = 256 * 1024
    48  )
    49  
    50  // LocalDirDoltDB stores the db in the current directory
    51  var LocalDirDoltDB = "file://./" + dbfactory.DoltDataDir
    52  
    53  // InMemDoltDB stores the DoltDB db in memory and is primarily used for testing
    54  var InMemDoltDB = "mem://"
    55  
    56  var ErrNoRootValAtHash = errors.New("there is no dolt root value at that hash")
    57  
    58  // DoltDB wraps access to the underlying noms database and hides some of the details of the underlying storage.
    59  // Additionally the noms codebase uses panics in a way that is non idiomatic and We've opted to recover and return
    60  // errors in many cases.
    61  type DoltDB struct {
    62  	db datas.Database
    63  }
    64  
    65  // DoltDBFromCS creates a DoltDB from a noms chunks.ChunkStore
    66  func DoltDBFromCS(cs chunks.ChunkStore) *DoltDB {
    67  	db := datas.NewDatabase(cs)
    68  
    69  	return &DoltDB{db}
    70  }
    71  
    72  // LoadDoltDB will acquire a reference to the underlying noms db.  If the Location is InMemDoltDB then a reference
    73  // to a newly created in memory database will be used. If the location is LocalDirDoltDB, the directory must exist or
    74  // this returns nil.
    75  func LoadDoltDB(ctx context.Context, nbf *types.NomsBinFormat, urlStr string) (*DoltDB, error) {
    76  	return LoadDoltDBWithParams(ctx, nbf, urlStr, nil)
    77  }
    78  
    79  func LoadDoltDBWithParams(ctx context.Context, nbf *types.NomsBinFormat, urlStr string, params map[string]string) (*DoltDB, error) {
    80  	if urlStr == LocalDirDoltDB {
    81  		exists, isDir := filesys.LocalFS.Exists(dbfactory.DoltDataDir)
    82  
    83  		if !exists {
    84  			return nil, errors.New("missing dolt data directory")
    85  		} else if !isDir {
    86  			return nil, errors.New("file exists where the dolt data directory should be")
    87  		}
    88  	}
    89  
    90  	db, err := dbfactory.CreateDB(ctx, nbf, urlStr, params)
    91  
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	return &DoltDB{db}, nil
    97  }
    98  
    99  func (ddb *DoltDB) CSMetricsSummary() string {
   100  	return datas.GetCSStatSummaryForDB(ddb.db)
   101  }
   102  
   103  // WriteEmptyRepo will create initialize the given db with a master branch which points to a commit which has valid
   104  // metadata for the creation commit, and an empty RootValue.
   105  func (ddb *DoltDB) WriteEmptyRepo(ctx context.Context, name, email string) error {
   106  	return ddb.WriteEmptyRepoWithCommitTime(ctx, name, email, CommitNowFunc())
   107  }
   108  
   109  func (ddb *DoltDB) WriteEmptyRepoWithCommitTime(ctx context.Context, name, email string, t time.Time) error {
   110  	// precondition checks
   111  	name = strings.TrimSpace(name)
   112  	email = strings.TrimSpace(email)
   113  
   114  	if name == "" || email == "" {
   115  		panic("Passed bad name or email.  Both should be valid")
   116  	}
   117  
   118  	ds, err := ddb.db.GetDataset(ctx, creationBranch)
   119  
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	if ds.HasHead() {
   125  		return errors.New("database already exists")
   126  	}
   127  
   128  	rv, err := EmptyRootValue(ctx, ddb.db)
   129  
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	_, err = ddb.WriteRootValue(ctx, rv)
   135  
   136  	if err != nil {
   137  		return err
   138  	}
   139  
   140  	cm, _ := NewCommitMetaWithUserTS(name, email, "Initialize data repository", t)
   141  
   142  	parents, err := types.NewList(ctx, ddb.db)
   143  	if err != nil {
   144  		return err
   145  	}
   146  
   147  	meta, err := cm.toNomsStruct(ddb.db.Format())
   148  
   149  	if err != nil {
   150  		return err
   151  	}
   152  
   153  	commitOpts := datas.CommitOptions{ParentsList: parents, Meta: meta, Policy: nil}
   154  
   155  	dref := ref.NewInternalRef(creationBranch)
   156  	ds, err = ddb.db.GetDataset(ctx, dref.String())
   157  
   158  	if err != nil {
   159  		return err
   160  	}
   161  
   162  	firstCommit, err := ddb.db.Commit(ctx, ds, rv.valueSt, commitOpts)
   163  
   164  	if err != nil {
   165  		return err
   166  	}
   167  
   168  	dref = ref.NewBranchRef(MasterBranch)
   169  	ds, err = ddb.db.GetDataset(ctx, dref.String())
   170  
   171  	if err != nil {
   172  		return err
   173  	}
   174  
   175  	headRef, ok, err := firstCommit.MaybeHeadRef()
   176  
   177  	if err != nil {
   178  		return err
   179  	}
   180  
   181  	if !ok {
   182  		return errors.New("commit without head")
   183  	}
   184  
   185  	_, err = ddb.db.SetHead(ctx, ds, headRef)
   186  
   187  	return err
   188  }
   189  
   190  func getCommitStForRefStr(ctx context.Context, db datas.Database, ref string) (types.Struct, error) {
   191  	if !datas.DatasetFullRe.MatchString(ref) {
   192  		return types.EmptyStruct(db.Format()), fmt.Errorf("invalid ref format: %s", ref)
   193  	}
   194  
   195  	ds, err := db.GetDataset(ctx, ref)
   196  
   197  	if err != nil {
   198  		return types.EmptyStruct(db.Format()), err
   199  	}
   200  
   201  	dsHead, hasHead := ds.MaybeHead()
   202  
   203  	if !hasHead {
   204  		return types.EmptyStruct(db.Format()), ErrBranchNotFound
   205  	}
   206  
   207  	if dsHead.Name() == datas.CommitName {
   208  		return dsHead, nil
   209  	}
   210  
   211  	if dsHead.Name() == datas.TagName {
   212  		commitRef, ok, err := dsHead.MaybeGet(datas.TagCommitRefField)
   213  		if err != nil {
   214  			return types.EmptyStruct(db.Format()), err
   215  		}
   216  		if !ok {
   217  			err = fmt.Errorf("tag struct does not have field %s", datas.TagCommitRefField)
   218  			return types.EmptyStruct(db.Format()), err
   219  		}
   220  
   221  		commitSt, err := commitRef.(types.Ref).TargetValue(ctx, db)
   222  		if err != nil {
   223  			return types.EmptyStruct(db.Format()), err
   224  		}
   225  
   226  		return commitSt.(types.Struct), nil
   227  	}
   228  
   229  	err = fmt.Errorf("dataset head is neither commit nor tag")
   230  	return types.EmptyStruct(db.Format()), err
   231  }
   232  
   233  func getCommitStForHash(ctx context.Context, db datas.Database, c string) (types.Struct, error) {
   234  	prefixed := c
   235  
   236  	if !strings.HasPrefix(c, "#") {
   237  		prefixed = "#" + c
   238  	}
   239  
   240  	ap, err := spec.NewAbsolutePath(prefixed)
   241  
   242  	if err != nil {
   243  		return types.EmptyStruct(db.Format()), err
   244  	}
   245  
   246  	val := ap.Resolve(ctx, db)
   247  
   248  	if val == nil {
   249  		return types.EmptyStruct(db.Format()), ErrHashNotFound
   250  	}
   251  
   252  	valSt, ok := val.(types.Struct)
   253  
   254  	if !ok || valSt.Name() != CommitStructName {
   255  		return types.EmptyStruct(db.Format()), ErrFoundHashNotACommit
   256  	}
   257  
   258  	return valSt, nil
   259  }
   260  
   261  func getAncestor(ctx context.Context, vrw types.ValueReadWriter, commitSt types.Struct, aSpec *AncestorSpec) (types.Struct, error) {
   262  	if aSpec == nil || len(aSpec.Instructions) == 0 {
   263  		return commitSt, nil
   264  	}
   265  
   266  	instructions := aSpec.Instructions
   267  	for _, inst := range instructions {
   268  		cm := NewCommit(vrw, commitSt)
   269  
   270  		numPars, err := cm.NumParents()
   271  
   272  		if err != nil {
   273  			return types.EmptyStruct(vrw.Format()), err
   274  		}
   275  
   276  		if inst < numPars {
   277  			commitStPtr, err := cm.getParent(ctx, inst)
   278  
   279  			if err != nil {
   280  				return types.EmptyStruct(vrw.Format()), err
   281  			}
   282  
   283  			if commitStPtr == nil {
   284  				return types.EmptyStruct(vrw.Format()), ErrInvalidAncestorSpec
   285  			}
   286  			commitSt = *commitStPtr
   287  		} else {
   288  			return types.EmptyStruct(vrw.Format()), ErrInvalidAncestorSpec
   289  		}
   290  	}
   291  
   292  	return commitSt, nil
   293  }
   294  
   295  // Resolve takes a CommitSpec and returns a Commit, or an error if the commit cannot be found.
   296  // If the CommitSpec is HEAD, Resolve also needs the DoltRef of the current working branch.
   297  func (ddb *DoltDB) Resolve(ctx context.Context, cs *CommitSpec, cwb ref.DoltRef) (*Commit, error) {
   298  	if cs == nil {
   299  		panic("nil commit spec")
   300  	}
   301  
   302  	var commitSt types.Struct
   303  	var err error
   304  	switch cs.csType {
   305  	case hashCommitSpec:
   306  		commitSt, err = getCommitStForHash(ctx, ddb.db, cs.baseSpec)
   307  	case refCommitSpec:
   308  		// For a ref in a CommitSpec, we have the following behavior.
   309  		// If it starts with `refs/`, we look for an exact match before
   310  		// we try any suffix matches. After that, we try a match on the
   311  		// user supplied input, with the following four prefixes, in
   312  		// order: `refs/`, `refs/heads/`, `refs/tags/`, `refs/remotes/`.
   313  		candidates := []string{
   314  			"refs/" + cs.baseSpec,
   315  			"refs/heads/" + cs.baseSpec,
   316  			"refs/tags/" + cs.baseSpec,
   317  			"refs/remotes/" + cs.baseSpec,
   318  		}
   319  		if strings.HasPrefix(cs.baseSpec, "refs/") {
   320  			candidates = []string{
   321  				cs.baseSpec,
   322  				"refs/" + cs.baseSpec,
   323  				"refs/heads/" + cs.baseSpec,
   324  				"refs/tags/" + cs.baseSpec,
   325  				"refs/remotes/" + cs.baseSpec,
   326  			}
   327  		}
   328  		for _, candidate := range candidates {
   329  			commitSt, err = getCommitStForRefStr(ctx, ddb.db, candidate)
   330  			if err == nil {
   331  				break
   332  			}
   333  			if err != ErrBranchNotFound {
   334  				return nil, err
   335  			}
   336  		}
   337  	case headCommitSpec:
   338  		commitSt, err = getCommitStForRefStr(ctx, ddb.db, cwb.String())
   339  	default:
   340  		panic("unrecognized commit spec csType: " + cs.csType)
   341  	}
   342  
   343  	if err != nil {
   344  		return nil, err
   345  	}
   346  
   347  	commitSt, err = getAncestor(ctx, ddb.db, commitSt, cs.aSpec)
   348  
   349  	if err != nil {
   350  		return nil, err
   351  	}
   352  
   353  	return NewCommit(ddb.db, commitSt), nil
   354  }
   355  
   356  // ResolveCommitRef takes a DoltRef and returns a Commit, or an error if the commit cannot be found. The ref given must
   357  // point to a Commit.
   358  func (ddb *DoltDB) ResolveCommitRef(ctx context.Context, ref ref.DoltRef) (*Commit, error) {
   359  	commitSt, err := getCommitStForRefStr(ctx, ddb.db, ref.String())
   360  	if err != nil {
   361  		return nil, err
   362  	}
   363  	return NewCommit(ddb.db, commitSt), nil
   364  }
   365  
   366  // ResolveTag takes a TagRef and returns the corresponding Tag object.
   367  func (ddb *DoltDB) ResolveTag(ctx context.Context, tagRef ref.TagRef) (*Tag, error) {
   368  	ds, err := ddb.db.GetDataset(ctx, tagRef.String())
   369  
   370  	if err != nil {
   371  		return nil, ErrTagNotFound
   372  	}
   373  
   374  	tagSt, hasHead := ds.MaybeHead()
   375  
   376  	if !hasHead {
   377  		return nil, ErrTagNotFound
   378  	}
   379  
   380  	if tagSt.Name() != datas.TagName {
   381  		return nil, fmt.Errorf("tagRef head is not a tag")
   382  	}
   383  
   384  	return NewTag(ctx, tagRef.GetPath(), ddb.db, tagSt)
   385  }
   386  
   387  // ResolveWorkingSet takes a WorkingSetRef and returns the corresponding WorkingSet object.
   388  func (ddb *DoltDB) ResolveWorkingSet(ctx context.Context, workingSetRef ref.WorkingSetRef) (*WorkingSet, error) {
   389  	ds, err := ddb.db.GetDataset(ctx, workingSetRef.String())
   390  
   391  	if err != nil {
   392  		return nil, ErrWorkingSetNotFound
   393  	}
   394  
   395  	wsSt, hasHead := ds.MaybeHead()
   396  
   397  	if !hasHead {
   398  		return nil, ErrWorkingSetNotFound
   399  	}
   400  
   401  	if wsSt.Name() != datas.WorkingSetName {
   402  		return nil, fmt.Errorf("workingSetRef head is not a workingSetRef")
   403  	}
   404  
   405  	return NewWorkingSet(ctx, workingSetRef.GetPath(), ddb.db, wsSt)
   406  }
   407  
   408  // TODO: convenience method to resolve the head commit of a branch.
   409  
   410  // WriteRootValue will write a doltdb.RootValue instance to the database.  This value will not be associated with a commit
   411  // and can be committed by hash at a later time.  Returns the hash of the value written.
   412  func (ddb *DoltDB) WriteRootValue(ctx context.Context, rv *RootValue) (hash.Hash, error) {
   413  	var err error
   414  	rv.valueSt, err = rv.valueSt.Set(featureVersKey, types.Int(DoltFeatureVersion))
   415  	if err != nil {
   416  		return hash.Hash{}, err
   417  	}
   418  
   419  	valRef, err := ddb.db.WriteValue(ctx, rv.valueSt)
   420  
   421  	if err != nil {
   422  		return hash.Hash{}, err
   423  	}
   424  
   425  	err = ddb.db.Flush(ctx)
   426  
   427  	if err != nil {
   428  		return hash.Hash{}, err
   429  	}
   430  
   431  	valHash := valRef.TargetHash()
   432  
   433  	return valHash, err
   434  }
   435  
   436  // ReadRootValue reads the RootValue associated with the hash given and returns it. Returns an error if the value cannot
   437  // be read, or if the hash given doesn't represent a dolt RootValue.
   438  func (ddb *DoltDB) ReadRootValue(ctx context.Context, h hash.Hash) (*RootValue, error) {
   439  	val, err := ddb.db.ReadValue(ctx, h)
   440  
   441  	if err != nil {
   442  		return nil, err
   443  	}
   444  	if val == nil {
   445  		return nil, ErrNoRootValAtHash
   446  	}
   447  
   448  	rootSt, ok := val.(types.Struct)
   449  	if !ok || rootSt.Name() != ddbRootStructName {
   450  		return nil, ErrNoRootValAtHash
   451  	}
   452  
   453  	return newRootValue(ddb.db, rootSt)
   454  }
   455  
   456  // Commit will update a branch's head value to be that of a previously committed root value hash
   457  func (ddb *DoltDB) Commit(ctx context.Context, valHash hash.Hash, dref ref.DoltRef, cm *CommitMeta) (*Commit, error) {
   458  	if dref.GetType() != ref.BranchRefType {
   459  		panic("can't commit to ref that isn't branch atm.  will probably remove this.")
   460  	}
   461  
   462  	return ddb.CommitWithParentSpecs(ctx, valHash, dref, nil, cm)
   463  }
   464  
   465  // FastForward fast-forwards the branch given to the commit given.
   466  func (ddb *DoltDB) FastForward(ctx context.Context, branch ref.DoltRef, commit *Commit) error {
   467  	ds, err := ddb.db.GetDataset(ctx, branch.String())
   468  
   469  	if err != nil {
   470  		return err
   471  	}
   472  
   473  	rf, err := types.NewRef(commit.commitSt, ddb.db.Format())
   474  
   475  	if err != nil {
   476  		return err
   477  	}
   478  
   479  	_, err = ddb.db.FastForward(ctx, ds, rf)
   480  
   481  	return err
   482  }
   483  
   484  // CanFastForward returns whether the given branch can be fast-forwarded to the commit given.
   485  func (ddb *DoltDB) CanFastForward(ctx context.Context, branch ref.DoltRef, new *Commit) (bool, error) {
   486  	current, err := ddb.ResolveCommitRef(ctx, branch)
   487  
   488  	if err != nil {
   489  		if err == ErrBranchNotFound {
   490  			return true, nil
   491  		}
   492  
   493  		return false, err
   494  	}
   495  
   496  	return current.CanFastForwardTo(ctx, new)
   497  }
   498  
   499  // SetHeadToCommit sets the given ref to point at the given commit. It is used in the course of 'force' updates.
   500  func (ddb *DoltDB) SetHeadToCommit(ctx context.Context, ref ref.DoltRef, cm *Commit) error {
   501  
   502  	stRef, err := types.NewRef(cm.commitSt, ddb.db.Format())
   503  
   504  	if err != nil {
   505  		return err
   506  	}
   507  
   508  	return ddb.SetHead(ctx, ref, stRef)
   509  }
   510  
   511  func (ddb *DoltDB) SetHead(ctx context.Context, ref ref.DoltRef, stRef types.Ref) error {
   512  	ds, err := ddb.db.GetDataset(ctx, ref.String())
   513  
   514  	if err != nil {
   515  		return err
   516  	}
   517  
   518  	_, err = ddb.db.SetHead(ctx, ds, stRef)
   519  	return err
   520  }
   521  
   522  // CommitWithParentSpecs commits the value hash given to the branch given, using the list of parent hashes given. Returns an
   523  // error if the value or any parents can't be resolved, or if anything goes wrong accessing the underlying storage.
   524  func (ddb *DoltDB) CommitWithParentSpecs(ctx context.Context, valHash hash.Hash, dref ref.DoltRef, parentCmSpecs []*CommitSpec, cm *CommitMeta) (*Commit, error) {
   525  	var parentCommits []*Commit
   526  	for _, parentCmSpec := range parentCmSpecs {
   527  		cm, err := ddb.Resolve(ctx, parentCmSpec, nil)
   528  
   529  		if err != nil {
   530  			return nil, err
   531  		}
   532  		parentCommits = append(parentCommits, cm)
   533  	}
   534  	return ddb.CommitWithParentCommits(ctx, valHash, dref, parentCommits, cm)
   535  }
   536  
   537  func (ddb *DoltDB) CommitWithParentCommits(ctx context.Context, valHash hash.Hash, dref ref.DoltRef, parentCommits []*Commit, cm *CommitMeta) (*Commit, error) {
   538  	var commitSt types.Struct
   539  	val, err := ddb.db.ReadValue(ctx, valHash)
   540  
   541  	if err != nil {
   542  		return nil, err
   543  	}
   544  
   545  	if st, ok := val.(types.Struct); !ok || st.Name() != ddbRootStructName {
   546  		return nil, errors.New("can't commit a value that is not a valid root value")
   547  	}
   548  
   549  	ds, err := ddb.db.GetDataset(ctx, dref.String())
   550  
   551  	if err != nil {
   552  		return nil, err
   553  	}
   554  
   555  	l, err := types.NewList(ctx, ddb.db)
   556  
   557  	if err != nil {
   558  		return nil, err
   559  	}
   560  
   561  	parentEditor := l.Edit()
   562  
   563  	headRef, hasHead, err := ds.MaybeHeadRef()
   564  
   565  	if err != nil {
   566  		return nil, err
   567  	}
   568  
   569  	if hasHead {
   570  		parentEditor = parentEditor.Append(headRef)
   571  	}
   572  
   573  	for _, cm := range parentCommits {
   574  		rf, err := types.NewRef(cm.commitSt, ddb.db.Format())
   575  
   576  		if err != nil {
   577  			return nil, err
   578  		}
   579  
   580  		parentEditor = parentEditor.Append(rf)
   581  	}
   582  
   583  	parents, err := parentEditor.List(ctx)
   584  
   585  	if err != nil {
   586  		return nil, err
   587  	}
   588  
   589  	st, err := cm.toNomsStruct(ddb.db.Format())
   590  
   591  	if err != nil {
   592  		return nil, err
   593  	}
   594  
   595  	commitOpts := datas.CommitOptions{ParentsList: parents, Meta: st, Policy: nil}
   596  	ds, err = ddb.db.GetDataset(ctx, dref.String())
   597  
   598  	if err != nil {
   599  		return nil, err
   600  	}
   601  
   602  	ds, err = ddb.db.Commit(ctx, ds, val, commitOpts)
   603  
   604  	if err != nil {
   605  		return nil, err
   606  	}
   607  
   608  	var ok bool
   609  	commitSt, ok = ds.MaybeHead()
   610  	if !ok {
   611  		return nil, errors.New("commit has no head but commit succeeded (How?!?!?)")
   612  	}
   613  
   614  	return NewCommit(ddb.db, commitSt), nil
   615  }
   616  
   617  // dangling commits are unreferenced by any branch or ref. They are created in the course of programmatic updates
   618  // such as rebase. You must create a ref to a dangling commit for it to be reachable
   619  func (ddb *DoltDB) CommitDanglingWithParentCommits(ctx context.Context, valHash hash.Hash, parentCommits []*Commit, cm *CommitMeta) (*Commit, error) {
   620  	var commitSt types.Struct
   621  	val, err := ddb.db.ReadValue(ctx, valHash)
   622  	if err != nil {
   623  		return nil, err
   624  	}
   625  	if st, ok := val.(types.Struct); !ok || st.Name() != ddbRootStructName {
   626  		return nil, errors.New("can't commit a value that is not a valid root value")
   627  	}
   628  
   629  	l, err := types.NewList(ctx, ddb.db)
   630  	if err != nil {
   631  		return nil, err
   632  	}
   633  
   634  	parentEditor := l.Edit()
   635  
   636  	for _, cm := range parentCommits {
   637  		rf, err := types.NewRef(cm.commitSt, ddb.db.Format())
   638  		if err != nil {
   639  			return nil, err
   640  		}
   641  
   642  		parentEditor = parentEditor.Append(rf)
   643  	}
   644  
   645  	parents, err := parentEditor.List(ctx)
   646  	if err != nil {
   647  		return nil, err
   648  	}
   649  
   650  	st, err := cm.toNomsStruct(ddb.db.Format())
   651  	if err != nil {
   652  		return nil, err
   653  	}
   654  
   655  	commitOpts := datas.CommitOptions{ParentsList: parents, Meta: st, Policy: nil}
   656  	commitSt, err = ddb.db.CommitDangling(ctx, val, commitOpts)
   657  	if err != nil {
   658  		return nil, err
   659  	}
   660  
   661  	return NewCommit(ddb.db, commitSt), nil
   662  }
   663  
   664  // ValueReadWriter returns the underlying noms database as a types.ValueReadWriter.
   665  func (ddb *DoltDB) ValueReadWriter() types.ValueReadWriter {
   666  	return ddb.db
   667  }
   668  
   669  func (ddb *DoltDB) Format() *types.NomsBinFormat {
   670  	return ddb.db.Format()
   671  }
   672  
   673  func WriteValAndGetRef(ctx context.Context, vrw types.ValueReadWriter, val types.Value) (types.Ref, error) {
   674  	valRef, err := types.NewRef(val, vrw.Format())
   675  
   676  	if err != nil {
   677  		return types.Ref{}, err
   678  	}
   679  
   680  	targetVal, err := valRef.TargetValue(ctx, vrw)
   681  
   682  	if err != nil {
   683  		return types.Ref{}, err
   684  	}
   685  
   686  	if targetVal == nil {
   687  		_, err = vrw.WriteValue(ctx, val)
   688  
   689  		if err != nil {
   690  			return types.Ref{}, err
   691  		}
   692  	}
   693  
   694  	return valRef, err
   695  }
   696  
   697  // ResolveParent returns the n-th ancestor of a given commit (direct parent is index 0). error return value will be
   698  // non-nil in the case that the commit cannot be resolved, there aren't as many ancestors as requested, or the
   699  // underlying storage cannot be accessed.
   700  func (ddb *DoltDB) ResolveParent(ctx context.Context, commit *Commit, parentIdx int) (*Commit, error) {
   701  	parentCommitSt, err := commit.getParent(ctx, parentIdx)
   702  	if err != nil {
   703  		return nil, err
   704  	}
   705  	return NewCommit(ddb.ValueReadWriter(), *parentCommitSt), nil
   706  }
   707  
   708  func (ddb *DoltDB) ResolveAllParents(ctx context.Context, commit *Commit) ([]*Commit, error) {
   709  	num, err := commit.NumParents()
   710  	if err != nil {
   711  		return nil, err
   712  	}
   713  	resolved := make([]*Commit, num)
   714  	for i := 0; i < num; i++ {
   715  		parent, err := ddb.ResolveParent(ctx, commit, i)
   716  		if err != nil {
   717  			return nil, err
   718  		}
   719  		resolved[i] = parent
   720  	}
   721  	return resolved, nil
   722  }
   723  
   724  // HasRef returns whether the branch given exists in this database.
   725  func (ddb *DoltDB) HasRef(ctx context.Context, doltRef ref.DoltRef) (bool, error) {
   726  	dss, err := ddb.db.Datasets(ctx)
   727  
   728  	if err != nil {
   729  		return false, err
   730  	}
   731  
   732  	return dss.Has(ctx, types.String(doltRef.String()))
   733  }
   734  
   735  var branchRefFilter = map[ref.RefType]struct{}{ref.BranchRefType: {}}
   736  
   737  // GetBranches returns a list of all branches in the database.
   738  func (ddb *DoltDB) GetBranches(ctx context.Context) ([]ref.DoltRef, error) {
   739  	return ddb.GetRefsOfType(ctx, branchRefFilter)
   740  }
   741  
   742  var tagsRefFilter = map[ref.RefType]struct{}{ref.TagRefType: {}}
   743  
   744  // GetTags returns a list of all tags in the database.
   745  func (ddb *DoltDB) GetTags(ctx context.Context) ([]ref.DoltRef, error) {
   746  	return ddb.GetRefsOfType(ctx, tagsRefFilter)
   747  }
   748  
   749  var workspacesRefFilter = map[ref.RefType]struct{}{ref.WorkspaceRefType: {}}
   750  
   751  // GetWorkspaces returns a list of all workspaces in the database.
   752  func (ddb *DoltDB) GetWorkspaces(ctx context.Context) ([]ref.DoltRef, error) {
   753  	return ddb.GetRefsOfType(ctx, workspacesRefFilter)
   754  }
   755  
   756  // GetHeadRefs returns a list of all refs that point to a Commit
   757  func (ddb *DoltDB) GetHeadRefs(ctx context.Context) ([]ref.DoltRef, error) {
   758  	return ddb.GetRefsOfType(ctx, ref.HeadRefTypes)
   759  }
   760  
   761  func (ddb *DoltDB) GetRefsOfType(ctx context.Context, refTypeFilter map[ref.RefType]struct{}) ([]ref.DoltRef, error) {
   762  	var refs []ref.DoltRef
   763  	dss, err := ddb.db.Datasets(ctx)
   764  
   765  	if err != nil {
   766  		return nil, err
   767  	}
   768  
   769  	err = dss.IterAll(ctx, func(key, _ types.Value) error {
   770  		keyStr := string(key.(types.String))
   771  
   772  		var dref ref.DoltRef
   773  		if ref.IsRef(keyStr) {
   774  			dref, err = ref.Parse(keyStr)
   775  			if err != nil {
   776  				return err
   777  			}
   778  
   779  			if _, ok := refTypeFilter[dref.GetType()]; ok {
   780  				refs = append(refs, dref)
   781  			}
   782  		}
   783  
   784  		return nil
   785  	})
   786  
   787  	if err != nil {
   788  		return nil, err
   789  	}
   790  
   791  	return refs, nil
   792  }
   793  
   794  // NewBranchAtCommit creates a new branch with HEAD at the commit given. Branch names must pass IsValidUserBranchName.
   795  func (ddb *DoltDB) NewBranchAtCommit(ctx context.Context, dref ref.DoltRef, commit *Commit) error {
   796  	if !IsValidBranchRef(dref) {
   797  		panic(fmt.Sprintf("invalid branch name %s, use IsValidUserBranchName check", dref.String()))
   798  	}
   799  
   800  	ds, err := ddb.db.GetDataset(ctx, dref.String())
   801  
   802  	if err != nil {
   803  		return err
   804  	}
   805  
   806  	rf, err := types.NewRef(commit.commitSt, ddb.db.Format())
   807  
   808  	if err != nil {
   809  		return err
   810  	}
   811  
   812  	_, err = ddb.db.SetHead(ctx, ds, rf)
   813  
   814  	return err
   815  }
   816  
   817  // DeleteBranch deletes the branch given, returning an error if it doesn't exist.
   818  func (ddb *DoltDB) DeleteBranch(ctx context.Context, branch ref.DoltRef) error {
   819  	return ddb.deleteRef(ctx, branch)
   820  }
   821  
   822  func (ddb *DoltDB) deleteRef(ctx context.Context, dref ref.DoltRef) error {
   823  	ds, err := ddb.db.GetDataset(ctx, dref.String())
   824  
   825  	if err != nil {
   826  		return err
   827  	}
   828  
   829  	if !ds.HasHead() {
   830  		return ErrBranchNotFound
   831  	}
   832  
   833  	_, err = ddb.db.Delete(ctx, ds)
   834  	return err
   835  }
   836  
   837  // NewTagAtCommit create a new tag at the commit given.
   838  func (ddb *DoltDB) NewTagAtCommit(ctx context.Context, tagRef ref.DoltRef, c *Commit, meta *TagMeta) error {
   839  	if !IsValidTagRef(tagRef) {
   840  		panic(fmt.Sprintf("invalid tag name %s, use IsValidUserTagName check", tagRef.String()))
   841  	}
   842  
   843  	ds, err := ddb.db.GetDataset(ctx, tagRef.String())
   844  
   845  	if err != nil {
   846  		return err
   847  	}
   848  
   849  	_, hasHead, err := ds.MaybeHeadRef()
   850  
   851  	if err != nil {
   852  		return err
   853  	}
   854  	if hasHead {
   855  		return fmt.Errorf("dataset already exists for tag %s", tagRef.String())
   856  	}
   857  
   858  	r, err := types.NewRef(c.commitSt, ddb.Format())
   859  
   860  	if err != nil {
   861  		return err
   862  	}
   863  
   864  	st, err := meta.toNomsStruct(ddb.db.Format())
   865  
   866  	if err != nil {
   867  		return err
   868  	}
   869  
   870  	tag := datas.TagOptions{Meta: st}
   871  
   872  	ds, err = ddb.db.Tag(ctx, ds, r, tag)
   873  
   874  	return err
   875  }
   876  
   877  // UpdateWorkingSet updates the working set with the ref given to the root value given
   878  // |prevHash| is the hash of the expected WorkingSet struct stored in the ref, not the hash of the RootValue there.
   879  func (ddb *DoltDB) UpdateWorkingSet(ctx context.Context, workingSetRef ref.WorkingSetRef, rootVal *RootValue, prevHash hash.Hash) error {
   880  	ds, err := ddb.db.GetDataset(ctx, workingSetRef.String())
   881  	if err != nil {
   882  		return err
   883  	}
   884  
   885  	// st, err := NewWorkingSetMeta().toNomsStruct(ddb.Format())
   886  	// if err != nil {
   887  	// 	return err
   888  	// }
   889  
   890  	rootRef, err := ddb.db.WriteValue(ctx, rootVal.valueSt)
   891  	if err != nil {
   892  		return err
   893  	}
   894  
   895  	// workspaceStruct, err := datas.NewWorkspace(ctx, rootRef, st)
   896  	// if err != nil {
   897  	// 	return err
   898  	// }
   899  	//
   900  	// wsRef, err := types.NewRef(workspaceStruct, ddb.Format())
   901  	// if err != nil {
   902  	// 	return err
   903  	// }
   904  
   905  	// h, err = wsRef.Hash(wsRef.Format())
   906  	// fmt.Sprintf("%v", h)
   907  
   908  	_, err = ddb.db.UpdateWorkingSet(ctx, ds, rootRef, datas.WorkingSetMeta{}, prevHash)
   909  	return err
   910  }
   911  
   912  func (ddb *DoltDB) DeleteTag(ctx context.Context, tag ref.DoltRef) error {
   913  	err := ddb.deleteRef(ctx, tag)
   914  
   915  	if err == ErrBranchNotFound {
   916  		return ErrTagNotFound
   917  	}
   918  
   919  	return err
   920  }
   921  
   922  // NewWorkspaceAtCommit create a new workspace at the commit given.
   923  func (ddb *DoltDB) NewWorkspaceAtCommit(ctx context.Context, workRef ref.DoltRef, c *Commit) error {
   924  	ds, err := ddb.db.GetDataset(ctx, workRef.String())
   925  	if err != nil {
   926  		return err
   927  	}
   928  
   929  	r, err := types.NewRef(c.commitSt, ddb.Format())
   930  	if err != nil {
   931  		return err
   932  	}
   933  
   934  	ds, err = ddb.db.SetHead(ctx, ds, r)
   935  
   936  	return err
   937  }
   938  
   939  func (ddb *DoltDB) DeleteWorkspace(ctx context.Context, workRef ref.DoltRef) error {
   940  	err := ddb.deleteRef(ctx, workRef)
   941  
   942  	if err == ErrBranchNotFound {
   943  		return ErrWorkspaceNotFound
   944  	}
   945  
   946  	return err
   947  }
   948  
   949  // GC performs garbage collection on this ddb. Values passed in |uncommitedVals| will be temporarily saved during gc.
   950  func (ddb *DoltDB) GC(ctx context.Context, uncommitedVals ...hash.Hash) error {
   951  	collector, ok := ddb.db.(datas.GarbageCollector)
   952  	if !ok {
   953  		return fmt.Errorf("this database does not support garbage collection")
   954  	}
   955  
   956  	err := ddb.pruneUnreferencedDatasets(ctx)
   957  	if err != nil {
   958  		return err
   959  	}
   960  
   961  	rand.Seed(time.Now().UnixNano())
   962  	tmpDatasets := make([]datas.Dataset, len(uncommitedVals))
   963  	for i, h := range uncommitedVals {
   964  		v, err := ddb.db.ReadValue(ctx, h)
   965  		if err != nil {
   966  			return err
   967  		}
   968  		if v == nil {
   969  			return fmt.Errorf("empty value for value hash %s", h.String())
   970  		}
   971  
   972  		ds, err := ddb.db.GetDataset(ctx, fmt.Sprintf("tmp/%d", rand.Int63()))
   973  		if err != nil {
   974  			return err
   975  		}
   976  
   977  		r, err := WriteValAndGetRef(ctx, ddb.db, v)
   978  		if err != nil {
   979  			return err
   980  		}
   981  
   982  		ds, err = ddb.db.CommitValue(ctx, ds, r)
   983  		if err != nil {
   984  			return err
   985  		}
   986  		if !ds.HasHead() {
   987  			return fmt.Errorf("could not save value %s", h.String())
   988  		}
   989  
   990  		tmpDatasets[i] = ds
   991  	}
   992  
   993  	err = collector.GC(ctx)
   994  	if err != nil {
   995  		return err
   996  	}
   997  
   998  	for _, ds := range tmpDatasets {
   999  		ds, err = ddb.db.Delete(ctx, ds)
  1000  		if err != nil {
  1001  			return err
  1002  		}
  1003  
  1004  		if ds.HasHead() {
  1005  			return fmt.Errorf("unsuccessful delete for dataset %s", ds.ID())
  1006  		}
  1007  	}
  1008  
  1009  	return nil
  1010  }
  1011  
  1012  func (ddb *DoltDB) pruneUnreferencedDatasets(ctx context.Context) error {
  1013  	dd, err := ddb.db.Datasets(ctx)
  1014  	if err != nil {
  1015  		return err
  1016  	}
  1017  
  1018  	var deletes []string
  1019  	_ = dd.Iter(ctx, func(ds, _ types.Value) (stop bool, err error) {
  1020  		dsID := string(ds.(types.String))
  1021  		if !ref.IsRef(dsID) && !ref.IsWorkingSet(dsID) {
  1022  			deletes = append(deletes, dsID)
  1023  		}
  1024  		return false, nil
  1025  	})
  1026  
  1027  	// e.g. flushes
  1028  	for _, dsID := range deletes {
  1029  		ds, err := ddb.db.GetDataset(ctx, dsID)
  1030  		if err != nil {
  1031  			return err
  1032  		}
  1033  
  1034  		ds, err = ddb.db.Delete(ctx, ds)
  1035  		if err != nil {
  1036  			return err
  1037  		}
  1038  
  1039  		if ds.HasHead() {
  1040  			return fmt.Errorf("unsuccessful delete for dataset %s", ds.ID())
  1041  		}
  1042  	}
  1043  
  1044  	return nil
  1045  }
  1046  
  1047  // PushChunks initiates a push into a database from the source database given, at the Value ref given. Pull progress is
  1048  // communicated over the provided channel.
  1049  func (ddb *DoltDB) PushChunks(ctx context.Context, tempDir string, srcDB *DoltDB, rf types.Ref, progChan chan datas.PullProgress, pullerEventCh chan datas.PullerEvent) error {
  1050  	if datas.CanUsePuller(srcDB.db) && datas.CanUsePuller(ddb.db) {
  1051  		puller, err := datas.NewPuller(ctx, tempDir, defaultChunksPerTF, srcDB.db, ddb.db, rf.TargetHash(), pullerEventCh)
  1052  
  1053  		if err == datas.ErrDBUpToDate {
  1054  			return nil
  1055  		} else if err != nil {
  1056  			return err
  1057  		}
  1058  
  1059  		return puller.Pull(ctx)
  1060  	} else {
  1061  		return datas.Pull(ctx, srcDB.db, ddb.db, rf, progChan)
  1062  	}
  1063  }
  1064  
  1065  func (ddb *DoltDB) PushChunksForRefHash(ctx context.Context, tempDir string, srcDB *DoltDB, h hash.Hash, pullerEventCh chan datas.PullerEvent) error {
  1066  	if datas.CanUsePuller(srcDB.db) && datas.CanUsePuller(ddb.db) {
  1067  		puller, err := datas.NewPuller(ctx, tempDir, defaultChunksPerTF, srcDB.db, ddb.db, h, pullerEventCh)
  1068  
  1069  		if err == datas.ErrDBUpToDate {
  1070  			return nil
  1071  		} else if err != nil {
  1072  			return err
  1073  		}
  1074  
  1075  		return puller.Pull(ctx)
  1076  	} else {
  1077  		return errors.New("this type of chunk store does not support this operation")
  1078  	}
  1079  }
  1080  
  1081  // PullChunks initiates a pull into a database from the source database given, at the commit given. Progress is
  1082  // communicated over the provided channel.
  1083  func (ddb *DoltDB) PullChunks(ctx context.Context, tempDir string, srcDB *DoltDB, stRef types.Ref, progChan chan datas.PullProgress, pullerEventCh chan datas.PullerEvent) error {
  1084  	if datas.CanUsePuller(srcDB.db) && datas.CanUsePuller(ddb.db) {
  1085  		puller, err := datas.NewPuller(ctx, tempDir, 256*1024, srcDB.db, ddb.db, stRef.TargetHash(), pullerEventCh)
  1086  
  1087  		if err == datas.ErrDBUpToDate {
  1088  			return nil
  1089  		} else if err != nil {
  1090  			return err
  1091  		}
  1092  
  1093  		return puller.Pull(ctx)
  1094  	} else {
  1095  		return datas.PullWithoutBatching(ctx, srcDB.db, ddb.db, stRef, progChan)
  1096  	}
  1097  }
  1098  
  1099  func (ddb *DoltDB) Clone(ctx context.Context, destDB *DoltDB, eventCh chan<- datas.TableFileEvent) error {
  1100  	return datas.Clone(ctx, ddb.db, destDB.db, eventCh)
  1101  }
  1102  
  1103  func (ddb *DoltDB) GetStorageVersion(ctx context.Context) (string, error) {
  1104  	return datas.GetManifestStorageVersion(ctx, ddb.db)
  1105  }