Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iter tags paginated #8768

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
36 changes: 36 additions & 0 deletions go/libraries/doltcore/doltdb/doltdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ type DoltDB struct {
databaseName string
}

type TagRefWithMeta struct {
TagRef ref.TagRef
Meta *datas.TagMeta
CommitAddr hash.Hash
MaybeHeadAddr hash.Hash
}

// DoltDBFromCS creates a DoltDB from a noms chunks.ChunkStore
func DoltDBFromCS(cs chunks.ChunkStore, databaseName string) *DoltDB {
vrw := types.NewValueStore(cs)
Expand Down Expand Up @@ -594,6 +601,35 @@ func (ddb *DoltDB) ResolveTag(ctx context.Context, tagRef ref.TagRef) (*Tag, err
return NewTag(ctx, tagRef.GetPath(), ds, ddb.vrw, ddb.ns)
}

// ResolveTag takes a TagRef and returns the corresponding Tag object.
func (ddb *DoltDB) ResolveTagFromTagRefWithMeta(ctx context.Context, tagRefWithMeta *TagRefWithMeta) (*Tag, error) {
return NewTagFromTagRefWithMeta(ctx, tagRefWithMeta.TagRef.GetPath(), tagRefWithMeta, ddb.vrw, ddb.ns)
}

// ResolveTagMeta takes a TagRef and returns the corresponding TagMeta object.
func (ddb *DoltDB) ResolveTagMeta(ctx context.Context, tagRef ref.TagRef) (*TagRefWithMeta, error) {
ds, err := ddb.db.GetDataset(ctx, tagRef.String())
if err != nil {
return nil, ErrTagNotFound
}

if !ds.HasHead() {
return nil, ErrTagNotFound
}

if !ds.IsTag() {
return nil, fmt.Errorf("tagRef head is not a tag")
}

meta, commitAddr, err := ds.HeadTag()
if err != nil {
return nil, err
}

addr, _ := ds.MaybeHeadAddr()
return &TagRefWithMeta{TagRef: tagRef, Meta: meta, CommitAddr: commitAddr, MaybeHeadAddr: addr}, nil
}

// ResolveWorkingSet takes a WorkingSetRef and returns the corresponding WorkingSet object.
func (ddb *DoltDB) ResolveWorkingSet(ctx context.Context, workingSetRef ref.WorkingSetRef) (*WorkingSet, error) {
ds, err := ddb.db.GetDataset(ctx, workingSetRef.String())
Expand Down
25 changes: 25 additions & 0 deletions go/libraries/doltcore/doltdb/tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,31 @@ type Tag struct {
Commit *Commit
}

// NewTagFromTagRefWithMeta creates a new Tag object from a TagRefWithMeta.
func NewTagFromTagRefWithMeta(ctx context.Context, name string, tagRefWithMeta *TagRefWithMeta, vrw types.ValueReadWriter, ns tree.NodeStore) (*Tag, error) {
dc, err := datas.LoadCommitAddr(ctx, vrw, tagRefWithMeta.CommitAddr)
if err != nil {
return nil, err
}

if dc.IsGhost() {
return nil, ErrGhostCommitEncountered
}

commit, err := NewCommit(ctx, vrw, ns, dc)
if err != nil {
return nil, err
}

return &Tag{
Name: name,
vrw: vrw,
addr: tagRefWithMeta.MaybeHeadAddr,
Meta: tagRefWithMeta.Meta,
Commit: commit,
}, nil
}

// NewTag creates a new Tag object.
func NewTag(ctx context.Context, name string, ds datas.Dataset, vrw types.ValueReadWriter, ns tree.NodeStore) (*Tag, error) {
meta, commitAddr, err := ds.HeadTag()
Expand Down
172 changes: 172 additions & 0 deletions go/libraries/doltcore/env/actions/tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (
"fmt"
"sort"

"golang.org/x/sync/errgroup"

"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/doltcore/ref"
Expand Down Expand Up @@ -138,3 +140,173 @@ func IterResolvedTags(ctx context.Context, ddb *doltdb.DoltDB, cb func(tag *dolt

return nil
}

const DefaultPageSize = 100

// IterResolvedTagsPaginated iterates over tags in dEnv.DoltDB from newest to oldest, resolving the tag to a commit and calling cb().
// Returns the next tag name if there are more results available.
func IterResolvedTagsPaginated(ctx context.Context, ddb *doltdb.DoltDB, startTag string, cb func(tag *doltdb.Tag) (stop bool, err error)) (string, error) {
tagRefs, err := ddb.GetTags(ctx)
if err != nil {
return "", err
}

eg, egCtx := errgroup.WithContext(ctx)
eg.SetLimit(5)

// for each tag, get the meta
tagMetas := make([]*doltdb.TagRefWithMeta, len(tagRefs))
for idx, r := range tagRefs {
idx, r := idx, r

eg.Go(func() error {
if egCtx.Err() != nil {
return egCtx.Err()
}

tr, ok := r.(ref.TagRef)
if !ok {
return fmt.Errorf("DoltDB.GetTags() returned non-tag DoltRef")
}

tm, err := ddb.ResolveTagMeta(ctx, tr)
if err != nil {
return err
}

tagMetas[idx] = tm
return nil
})

}

if err := eg.Wait(); err != nil {
return "", err
}

// sort by meta timestamp
sort.Slice(tagMetas, func(i, j int) bool {
return tagMetas[i].Meta.Timestamp > tagMetas[j].Meta.Timestamp
})

// find starting index based on start tag
startIdx := 0
if startTag != "" {
for i, tm := range tagMetas {
if tm.TagRef.GetPath() == startTag {
startIdx = i + 1 // start after the given tag
break
}
}
}

// get page of results
endIdx := startIdx + DefaultPageSize
if endIdx > len(tagMetas) {
endIdx = len(tagMetas)
}

pageTagMetas := tagMetas[startIdx:endIdx]

// resolve tags for this page
for _, tm := range pageTagMetas {
tag, err := ddb.ResolveTagFromTagRefWithMeta(ctx, tm)
if err != nil {
return "", err
}

stop, err := cb(tag)
if err != nil {
return "", err
}
if stop {
break
}
}

// return next tag name if there are more results
if endIdx < len(tagMetas) {
lastTag := pageTagMetas[len(pageTagMetas)-1]
return lastTag.TagRef.GetPath(), nil
}

return "", nil
}

// IterResolvedTagsByNamePaginated iterates over tags in dEnv.DoltDB from newest to oldest, resolving the tag to a commit and calling cb().
// Returns the next tag name if there are more results available.
func IterResolvedTagsByNamePaginated(ctx context.Context, ddb *doltdb.DoltDB, startTag string, cb func(tag *doltdb.Tag) (stop bool, err error)) (string, error) {
// tags returned here are sorted lexicographically
tagRefs, err := ddb.GetTags(ctx)
if err != nil {
return "", err
}

// find starting index based on start tag
startIdx := 0
if startTag != "" {
for i, tr := range tagRefs {
if tr.GetPath() == startTag {
startIdx = i + 1 // start after the given tag
break
}
}
}

// get page of results
endIdx := startIdx + DefaultPageSize
if endIdx > len(tagRefs) {
endIdx = len(tagRefs)
}

pageTagRefs := tagRefs[startIdx:endIdx]

// resolve tags for this page
for _, tr := range pageTagRefs {
tag, err := ddb.ResolveTag(ctx, tr.(ref.TagRef))
if err != nil {
return "", err
}

stop, err := cb(tag)
if err != nil {
return "", err
}
if stop {
break
}
}

// return next tag name if there are more results
if endIdx < len(tagRefs) {
lastTag := pageTagRefs[len(pageTagRefs)-1]
return lastTag.GetPath(), nil
}

return "", nil
}

// VisitResolvedTag iterates over tags in ddb until the given tag name is found, then calls cb() with the resolved tag.
func VisitResolvedTag(ctx context.Context, ddb *doltdb.DoltDB, tagName string, cb func(tag *doltdb.Tag) error) error {
tagRefs, err := ddb.GetTags(ctx)
if err != nil {
return err
}

for _, r := range tagRefs {
tr, ok := r.(ref.TagRef)
if !ok {
return fmt.Errorf("DoltDB.GetTags() returned non-tag DoltRef")
}

if tr.GetPath() == tagName {
tag, err := ddb.ResolveTag(ctx, tr)
if err != nil {
return err
}
return cb(tag)
}
}

return doltdb.ErrTagNotFound
}
Loading
Loading