diff --git a/go/cmd/dolt/commands/archive.go b/go/cmd/dolt/commands/archive.go index 64cde107d9a..50e482db0d1 100644 --- a/go/cmd/dolt/commands/archive.go +++ b/go/cmd/dolt/commands/archive.go @@ -250,7 +250,7 @@ func historicalFuzzyMatching(ctx context.Context, heads hash.HashSet, groupings return err } for { - h, _, err := iterator.Next(ctx) + h, _, _, _, err := iterator.Next(ctx) if err != nil { if err == io.EOF { break diff --git a/go/libraries/doltcore/doltdb/commit_itr.go b/go/libraries/doltcore/doltdb/commit_itr.go index 54d471c89df..a92648bb795 100644 --- a/go/libraries/doltcore/doltdb/commit_itr.go +++ b/go/libraries/doltcore/doltdb/commit_itr.go @@ -28,9 +28,11 @@ import ( // CommitItr is an interface for iterating over a set of unique commits type CommitItr[C Context] interface { - // Next returns the hash of the next commit, and a pointer to that commit. Implementations of Next must handle - // making sure the list of commits returned are unique. When complete Next will return hash.Hash{}, nil, io.EOF - Next(ctx C) (hash.Hash, *OptionalCommit, error) + // Next returns the next commit's hash, pointer, metadata, and height. + // Implementations of Next must handle making sure the list of commits returned are unique. + // When complete Next will return hash.Hash{}, nil, nil, 0, io.EOF + // Note: Some implementations may always return nil for the metadata and height return values. + Next(ctx C) (hash.Hash, *OptionalCommit, *datas.CommitMeta, uint64, error) // Reset the commit iterator back to the start Reset(ctx context.Context) error @@ -88,25 +90,27 @@ func (cmItr *commitItr[C]) Reset(ctx context.Context) error { return nil } -// Next returns the hash of the next commit, and a pointer to that commit. It handles making sure the list of commits -// returned are unique. When complete Next will return hash.Hash{}, nil, io.EOF -func (cmItr *commitItr[C]) Next(ctx C) (hash.Hash, *OptionalCommit, error) { +// Next returns the hash of the next commit, and a pointer to that commit. +// It handles making sure the list of commits returned are unique. +// When complete Next will return hash.Hash{}, nil, nil, 0, io.EOF. +// Note: This implementation always returns nil for the metadata and height return values. +func (cmItr *commitItr[C]) Next(ctx C) (hash.Hash, *OptionalCommit, *datas.CommitMeta, uint64, error) { for cmItr.curr == nil { if cmItr.currentRoot >= len(cmItr.rootCommits) { - return hash.Hash{}, nil, io.EOF + return hash.Hash{}, nil, nil, 0, io.EOF } cm := cmItr.rootCommits[cmItr.currentRoot] h, err := cm.HashOf() if err != nil { - return hash.Hash{}, nil, err + return hash.Hash{}, nil, nil, 0, err } if !cmItr.added[h] { cmItr.added[h] = true cmItr.curr = cm - return h, &OptionalCommit{cmItr.curr, h}, nil + return h, &OptionalCommit{cmItr.curr, h}, nil, 0, nil } cmItr.currentRoot++ @@ -115,7 +119,7 @@ func (cmItr *commitItr[C]) Next(ctx C) (hash.Hash, *OptionalCommit, error) { parents, err := cmItr.curr.ParentHashes(ctx) if err != nil { - return hash.Hash{}, nil, err + return hash.Hash{}, nil, nil, 0, err } for _, h := range parents { @@ -137,13 +141,13 @@ func (cmItr *commitItr[C]) Next(ctx C) (hash.Hash, *OptionalCommit, error) { cmItr.unprocessed = cmItr.unprocessed[:numUnprocessed-1] cmItr.curr, err = HashToCommit(ctx, cmItr.ddb.ValueReadWriter(), cmItr.ddb.ns, next) if err != nil && err != ErrGhostCommitEncountered { - return hash.Hash{}, nil, err + return hash.Hash{}, nil, nil, 0, err } if err == ErrGhostCommitEncountered { cmItr.curr = nil } - return next, &OptionalCommit{cmItr.curr, next}, nil + return next, &OptionalCommit{cmItr.curr, next}, nil, 0, nil } func HashToCommit(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, h hash.Hash) (*Commit, error) { @@ -183,19 +187,18 @@ func NewFilteringCommitItr[C Context](itr CommitItr[C], filter CommitFilter[C]) // Next returns the hash of the next commit, and a pointer to that commit. Implementations of Next must handle // making sure the list of commits returned are unique. When complete Next will return hash.Hash{}, nil, io.EOF -func (itr FilteringCommitItr[C]) Next(ctx C) (hash.Hash, *OptionalCommit, error) { +func (itr FilteringCommitItr[C]) Next(ctx C) (hash.Hash, *OptionalCommit, *datas.CommitMeta, uint64, error) { // iteration will terminate on io.EOF or a commit that is !filteredOut for { - h, cm, err := itr.itr.Next(ctx) - + h, cm, meta, height, err := itr.itr.Next(ctx) if err != nil { - return hash.Hash{}, nil, err + return hash.Hash{}, nil, nil, 0, err } if filterOut, err := itr.filter(ctx, h, cm); err != nil { - return hash.Hash{}, nil, err + return hash.Hash{}, nil, nil, 0, err } else if !filterOut { - return h, cm, nil + return h, cm, meta, height, nil } } } @@ -217,12 +220,14 @@ type CommitSliceIter[C Context] struct { var _ CommitItr[context.Context] = (*CommitSliceIter[context.Context])(nil) -func (i *CommitSliceIter[C]) Next(ctx C) (hash.Hash, *OptionalCommit, error) { +// Next implements the CommitItr interface. +// Note: This implementation always returns nil for the metadata and height return values. +func (i *CommitSliceIter[C]) Next(ctx C) (hash.Hash, *OptionalCommit, *datas.CommitMeta, uint64, error) { if i.i >= len(i.h) { - return hash.Hash{}, nil, io.EOF + return hash.Hash{}, nil, nil, 0, io.EOF } i.i++ - return i.h[i.i-1], &OptionalCommit{i.cm[i.i-1], i.h[i.i-1]}, nil + return i.h[i.i-1], &OptionalCommit{i.cm[i.i-1], i.h[i.i-1]}, nil, 0, nil } @@ -232,7 +237,7 @@ func (i *CommitSliceIter[C]) Reset(ctx context.Context) error { } func NewOneCommitIter[C Context](cm *Commit, h hash.Hash, meta *datas.CommitMeta) *OneCommitIter[C] { - return &OneCommitIter[C]{cm: &OptionalCommit{cm, h}, h: h} + return &OneCommitIter[C]{cm: &OptionalCommit{cm, h}, h: h, m: meta} } type OneCommitIter[C Context] struct { @@ -244,13 +249,14 @@ type OneCommitIter[C Context] struct { var _ CommitItr[context.Context] = (*OneCommitIter[context.Context])(nil) -func (i *OneCommitIter[C]) Next(_ C) (hash.Hash, *OptionalCommit, error) { +// Next implements the CommitItr interface. +// Note: This implementation always returns nil for the metadata and height return values. +func (i *OneCommitIter[C]) Next(_ C) (hash.Hash, *OptionalCommit, *datas.CommitMeta, uint64, error) { if i.done { - return hash.Hash{}, nil, io.EOF + return hash.Hash{}, nil, nil, 0, io.EOF } i.done = true - return i.h, i.cm, nil - + return i.h, i.cm, i.m, 0, nil } func (i *OneCommitIter[C]) Reset(_ context.Context) error { diff --git a/go/libraries/doltcore/doltdb/doltdb.go b/go/libraries/doltcore/doltdb/doltdb.go index 7ed3e0dc6a4..95a8aac2a38 100644 --- a/go/libraries/doltcore/doltdb/doltdb.go +++ b/go/libraries/doltcore/doltdb/doltdb.go @@ -474,6 +474,23 @@ func (ddb *DoltDB) Resolve(ctx context.Context, cs *CommitSpec, cwb ref.DoltRef) return commit.GetAncestor(ctx, cs.aSpec) } +// ResolveHash takes a hash and returns an OptionalCommit directly. +// This assumes no ancestor spec resolution and no current working branch (cwb) needed. +func (ddb *DoltDB) ResolveHash(ctx context.Context, hash hash.Hash) (*OptionalCommit, error) { + commitValue, err := datas.LoadCommitAddr(ctx, ddb.vrw, hash) + if err != nil { + return nil, err + } + if commitValue.IsGhost() { + return &OptionalCommit{nil, hash}, nil + } + commit, err := NewCommit(ctx, ddb.vrw, ddb.ns, commitValue) + if err != nil { + return nil, err + } + return &OptionalCommit{commit, hash}, nil +} + // BootstrapShallowResolve is a special case of Resolve that is used to resolve a commit prior to pulling it's history // in a shallow clone. In general, application code should call Resolve and get an OptionalCommit. This is a special case // where we need to get the head commit for the commit closure used to determine what commits should skipped. diff --git a/go/libraries/doltcore/env/actions/commitwalk/commitwalk.go b/go/libraries/doltcore/env/actions/commitwalk/commitwalk.go index b411b306a9a..a230d4e658d 100644 --- a/go/libraries/doltcore/env/actions/commitwalk/commitwalk.go +++ b/go/libraries/doltcore/env/actions/commitwalk/commitwalk.go @@ -69,23 +69,25 @@ func (q *q) Swap(i, j int) { // the commit with the newer timestamp is "less". Finally if both commits are ghost commits, we don't really have enough // information to compare on, so we just compare the hashes to ensure that the results are stable. func (q *q) Less(i, j int) bool { - _, okI := q.pending[i].commit.ToCommit() - _, okJ := q.pending[i].commit.ToCommit() + cI := q.pending[i] + cJ := q.pending[j] + _, okI := cI.commit.ToCommit() + _, okJ := cJ.commit.ToCommit() if !okI && okJ { return true } else if okI && !okJ { return false } else if !okI && !okJ { - return q.pending[i].hash.String() < q.pending[j].hash.String() + return cI.hash.String() < cJ.hash.String() } - if q.pending[i].height > q.pending[j].height { + if cI.height > cJ.height { return true } - if q.pending[i].height == q.pending[j].height { - return q.pending[i].meta.UserTimestamp > q.pending[j].meta.UserTimestamp + if cI.height == cJ.height { + return cI.meta.UserTimestamp > cJ.meta.UserTimestamp } return false } @@ -128,15 +130,11 @@ func (q *q) SetInvisible(ctx context.Context, ddb *doltdb.DoltDB, id hash.Hash) } func load(ctx context.Context, ddb *doltdb.DoltDB, h hash.Hash) (*doltdb.OptionalCommit, error) { - cs, err := doltdb.NewCommitSpec(h.String()) + oc, err := ddb.ResolveHash(ctx, h) if err != nil { return nil, err } - c, err := ddb.Resolve(ctx, cs, nil) - if err != nil { - return nil, err - } - return c, nil + return oc, nil } func (q *q) Get(ctx context.Context, ddb *doltdb.DoltDB, id hash.Hash) (*c, error) { @@ -158,14 +156,21 @@ func (q *q) Get(ctx context.Context, ddb *doltdb.DoltDB, id hash.Hash) (*c, erro if err != nil { return nil, err } + meta, err := commit.GetCommitMeta(ctx) if err != nil { return nil, err } - c := &c{ddb: ddb, commit: &doltdb.OptionalCommit{Commit: commit, Addr: id}, meta: meta, height: h, hash: id} - q.loaded[id] = c - return c, nil + lc := &c{ + ddb: ddb, + commit: &doltdb.OptionalCommit{Commit: commit, Addr: id}, + meta: meta, + height: h, + hash: id, + } + q.loaded[id] = lc + return lc, nil } func newQueue() *q { @@ -190,7 +195,7 @@ func GetDotDotRevisions(ctx context.Context, includedDB *doltdb.DoltDB, included var commitList []*doltdb.OptionalCommit for num < 0 || len(commitList) < num { - _, commit, err := itr.Next(ctx) + _, commit, _, _, err := itr.Next(ctx) if err == io.EOF { break } else if err != nil { @@ -234,43 +239,35 @@ func newCommiterator[C doltdb.Context](ctx context.Context, ddb *doltdb.DoltDB, } // Next implements doltdb.CommitItr -func (iter *commiterator[C]) Next(ctx C) (hash.Hash, *doltdb.OptionalCommit, error) { +func (iter *commiterator[C]) Next(ctx C) (hash.Hash, *doltdb.OptionalCommit, *datas.CommitMeta, uint64, error) { if iter.q.NumVisiblePending() > 0 { nextC := iter.q.PopPending() - - var err error - parents := []hash.Hash{} commit, ok := nextC.commit.ToCommit() if ok { - parents, err = commit.ParentHashes(ctx) - if err != nil { - return hash.Hash{}, nil, err - } - } - - for _, parentID := range parents { - if err := iter.q.AddPendingIfUnseen(ctx, nextC.ddb, parentID); err != nil { - return hash.Hash{}, nil, err + for _, parentCommit := range commit.DatasParents() { + if err := iter.q.AddPendingIfUnseen(ctx, nextC.ddb, parentCommit.Addr()); err != nil { + return hash.Hash{}, nil, nil, 0, err + } } } + var err error matches := true if iter.matchFn != nil { matches, err = iter.matchFn(nextC.commit) - if err != nil { - return hash.Hash{}, nil, err + return hash.Hash{}, nil, nil, 0, err } } if matches { - return nextC.hash, &doltdb.OptionalCommit{Commit: commit, Addr: nextC.hash}, nil + return nextC.hash, &doltdb.OptionalCommit{Commit: commit, Addr: nextC.hash}, nextC.meta, nextC.height, nil } return iter.Next(ctx) } - return hash.Hash{}, nil, io.EOF + return hash.Hash{}, nil, nil, 0, io.EOF } // Reset implements doltdb.CommitItr @@ -301,7 +298,7 @@ func GetTopNTopoOrderedCommitsMatching(ctx context.Context, ddb *doltdb.DoltDB, var commitList []*doltdb.Commit for n < 0 || len(commitList) < n { - _, optCmt, err := itr.Next(ctx) + _, optCmt, _, _, err := itr.Next(ctx) if err == io.EOF { break } else if err != nil { @@ -345,28 +342,28 @@ func newDotDotCommiterator[C doltdb.Context](ctx context.Context, includedDdb *d } // Next implements doltdb.CommitItr -func (i *dotDotCommiterator[C]) Next(ctx C) (hash.Hash, *doltdb.OptionalCommit, error) { +func (i *dotDotCommiterator[C]) Next(ctx C) (hash.Hash, *doltdb.OptionalCommit, *datas.CommitMeta, uint64, error) { if i.q.NumVisiblePending() > 0 { nextC := i.q.PopPending() commit, ok := nextC.commit.ToCommit() if !ok { - return nextC.hash, nextC.commit, nil + return nextC.hash, nextC.commit, nextC.meta, nextC.height, nil } parents, err := commit.ParentHashes(ctx) if err != nil { - return hash.Hash{}, nil, err + return hash.Hash{}, nil, nil, 0, err } for _, parentID := range parents { if nextC.invisible { if err := i.q.SetInvisible(ctx, nextC.ddb, parentID); err != nil { - return hash.Hash{}, nil, err + return hash.Hash{}, nil, nil, 0, err } } if err := i.q.AddPendingIfUnseen(ctx, nextC.ddb, parentID); err != nil { - return hash.Hash{}, nil, err + return hash.Hash{}, nil, nil, 0, err } } @@ -374,18 +371,18 @@ func (i *dotDotCommiterator[C]) Next(ctx C) (hash.Hash, *doltdb.OptionalCommit, if i.matchFn != nil { matches, err = i.matchFn(nextC.commit) if err != nil { - return hash.Hash{}, nil, err + return hash.Hash{}, nil, nil, 0, err } } - // If not invisible, return commit. Otherwise get next commit + // If not invisible, return commit. Otherwise, get next commit if !nextC.invisible && matches { - return nextC.hash, nextC.commit, nil + return nextC.hash, nextC.commit, nextC.meta, nextC.height, nil } return i.Next(ctx) } - return hash.Hash{}, nil, io.EOF + return hash.Hash{}, nil, nil, 0, io.EOF } // Reset implements doltdb.CommitItr diff --git a/go/libraries/doltcore/rebase/rebase.go b/go/libraries/doltcore/rebase/rebase.go index 432c0445b50..9254c1bf958 100644 --- a/go/libraries/doltcore/rebase/rebase.go +++ b/go/libraries/doltcore/rebase/rebase.go @@ -200,7 +200,7 @@ func findRebaseCommits(ctx *sql.Context, currentBranchCommit, upstreamBranchComm // Drain the iterator into a slice so that we can easily reverse the order of the commits // so that the oldest commit is first in the generated rebase plan. for { - _, optCmt, err := commitItr.Next(ctx) + _, optCmt, _, _, err := commitItr.Next(ctx) if err == io.EOF { return commits, nil } else if err != nil { diff --git a/go/libraries/doltcore/sqle/database.go b/go/libraries/doltcore/sqle/database.go index 0b9787ef177..07e7e1f076c 100644 --- a/go/libraries/doltcore/sqle/database.go +++ b/go/libraries/doltcore/sqle/database.go @@ -919,7 +919,7 @@ func resolveAsOfTime(ctx *sql.Context, ddb *doltdb.DoltDB, head ref.DoltRef, asO } for { - _, optCmt, err := cmItr.Next(ctx) + _, optCmt, meta, _, err := cmItr.Next(ctx) if err == io.EOF { break } else if err != nil { @@ -930,9 +930,11 @@ func resolveAsOfTime(ctx *sql.Context, ddb *doltdb.DoltDB, head ref.DoltRef, asO return nil, nil, doltdb.ErrGhostCommitEncountered } - meta, err := curr.GetCommitMeta(ctx) - if err != nil { - return nil, nil, err + if meta == nil { + meta, err = curr.GetCommitMeta(ctx) + if err != nil { + return nil, nil, err + } } if meta.Time().Equal(asOf) || meta.Time().Before(asOf) { diff --git a/go/libraries/doltcore/sqle/dolt_procedures_diff_table.go b/go/libraries/doltcore/sqle/dolt_procedures_diff_table.go index b5cb6846a0c..d6005f75095 100644 --- a/go/libraries/doltcore/sqle/dolt_procedures_diff_table.go +++ b/go/libraries/doltcore/sqle/dolt_procedures_diff_table.go @@ -146,7 +146,7 @@ var _ sql.PartitionIter = (*DoltProceduresDiffPartitionItr)(nil) func (dpdp *DoltProceduresDiffPartitionItr) Next(ctx *sql.Context) (sql.Partition, error) { // First iterate through commit history, then add working partition as the final step for { - cmHash, optCmt, err := dpdp.cmItr.Next(ctx) + cmHash, optCmt, _, _, err := dpdp.cmItr.Next(ctx) if err == io.EOF { // Finished with commit history, now add working partition if not done if !dpdp.workingPartitionDone { diff --git a/go/libraries/doltcore/sqle/dolt_schemas_diff_table.go b/go/libraries/doltcore/sqle/dolt_schemas_diff_table.go index 534ef8f774c..05c6a55f201 100644 --- a/go/libraries/doltcore/sqle/dolt_schemas_diff_table.go +++ b/go/libraries/doltcore/sqle/dolt_schemas_diff_table.go @@ -146,7 +146,7 @@ var _ sql.PartitionIter = (*DoltSchemasDiffPartitionItr)(nil) func (dsdp *DoltSchemasDiffPartitionItr) Next(ctx *sql.Context) (sql.Partition, error) { // First iterate through commit history, then add working partition as the final step for { - cmHash, optCmt, err := dsdp.cmItr.Next(ctx) + cmHash, optCmt, _, _, err := dsdp.cmItr.Next(ctx) if err == io.EOF { // Finished with commit history, now add working partition if not done if !dsdp.workingPartitionDone { diff --git a/go/libraries/doltcore/sqle/dprocedures/dolt_count_commits.go b/go/libraries/doltcore/sqle/dprocedures/dolt_count_commits.go index 959073c7e9f..2e221999da1 100644 --- a/go/libraries/doltcore/sqle/dprocedures/dolt_count_commits.go +++ b/go/libraries/doltcore/sqle/dprocedures/dolt_count_commits.go @@ -146,7 +146,7 @@ func countCommitsInRange(ctx context.Context, ddb *doltdb.DoltDB, startCommitHas } count := 0 for { - nextHash, _, err := itr.Next(ctx) + nextHash, _, _, _, err := itr.Next(ctx) if err == io.EOF { return 0, fmt.Errorf("no match found to ancestor commit") } else if err != nil { diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_log.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_log.go index 3e5785ced02..9ffe0076ade 100644 --- a/go/libraries/doltcore/sqle/dtablefunctions/dolt_log.go +++ b/go/libraries/doltcore/sqle/dtablefunctions/dolt_log.go @@ -30,6 +30,7 @@ import ( "github.com/dolthub/dolt/go/libraries/doltcore/schema" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess" "github.com/dolthub/dolt/go/libraries/utils/gpg" + "github.com/dolthub/dolt/go/store/datas" "github.com/dolthub/dolt/go/store/hash" ) @@ -688,9 +689,11 @@ func (itr *logTableFunctionRowIter) Next(ctx *sql.Context) (sql.Row, error) { var commitHash hash.Hash var commit *doltdb.Commit var optCmt *doltdb.OptionalCommit + var meta *datas.CommitMeta + var height uint64 var err error for { - commitHash, optCmt, err = itr.child.Next(ctx) + commitHash, optCmt, meta, height, err = itr.child.Next(ctx) if err != nil { return nil, err } @@ -762,14 +765,19 @@ func (itr *logTableFunctionRowIter) Next(ctx *sql.Context) (sql.Row, error) { } } - meta, err := commit.GetCommitMeta(ctx) - if err != nil { - return nil, err + if meta == nil { + meta, err = commit.GetCommitMeta(ctx) + if err != nil { + return nil, err + } } - height, err := commit.Height() - if err != nil { - return nil, err + // TODO: will retrieve height again if it's 0 + if height == 0 { + height, err = commit.Height() + if err != nil { + return nil, err + } } row := sql.NewRow(commitHash.String(), meta.Name, meta.Email, meta.Time(), meta.Description, height) diff --git a/go/libraries/doltcore/sqle/dtables/column_diff_table.go b/go/libraries/doltcore/sqle/dtables/column_diff_table.go index 4f32a207c54..4ffbfe258cd 100644 --- a/go/libraries/doltcore/sqle/dtables/column_diff_table.go +++ b/go/libraries/doltcore/sqle/dtables/column_diff_table.go @@ -337,7 +337,7 @@ func (itr *doltColDiffCommitHistoryRowItr) Next(ctx *sql.Context) (sql.Row, erro } itr.commits = nil } else if itr.child != nil { - _, optCmt, err := itr.child.Next(ctx) + _, optCmt, _, _, err := itr.child.Next(ctx) if err != nil { return nil, err } diff --git a/go/libraries/doltcore/sqle/dtables/commit_ancestors_table.go b/go/libraries/doltcore/sqle/dtables/commit_ancestors_table.go index ee7e9a06100..25cdb7b1921 100644 --- a/go/libraries/doltcore/sqle/dtables/commit_ancestors_table.go +++ b/go/libraries/doltcore/sqle/dtables/commit_ancestors_table.go @@ -159,7 +159,7 @@ func NewCommitAncestorsRowItr(sqlCtx *sql.Context, ddb *doltdb.DoltDB) (*CommitA // After retrieving the last row, Close will be automatically closed. func (itr *CommitAncestorsRowItr) Next(ctx *sql.Context) (sql.Row, error) { if len(itr.cache) == 0 { - ch, optCmt, err := itr.itr.Next(ctx) + ch, optCmt, _, _, err := itr.itr.Next(ctx) if err != nil { // When complete itr.Next will return io.EOF return nil, err diff --git a/go/libraries/doltcore/sqle/dtables/commits_table.go b/go/libraries/doltcore/sqle/dtables/commits_table.go index b3b45444092..8528d06100f 100644 --- a/go/libraries/doltcore/sqle/dtables/commits_table.go +++ b/go/libraries/doltcore/sqle/dtables/commits_table.go @@ -152,7 +152,7 @@ func NewCommitsRowItr(ctx *sql.Context, ddb *doltdb.DoltDB) (CommitsRowItr, erro // Next retrieves the next row. It will return io.EOF if it's the last row. // After retrieving the last row, Close will be automatically closed. func (itr CommitsRowItr) Next(ctx *sql.Context) (sql.Row, error) { - h, optCmt, err := itr.itr.Next(ctx) + h, optCmt, _, _, err := itr.itr.Next(ctx) if err != nil { return nil, err } diff --git a/go/libraries/doltcore/sqle/dtables/diff_table.go b/go/libraries/doltcore/sqle/dtables/diff_table.go index 962743ac7c9..a60b52a062e 100644 --- a/go/libraries/doltcore/sqle/dtables/diff_table.go +++ b/go/libraries/doltcore/sqle/dtables/diff_table.go @@ -183,7 +183,7 @@ func (dt *DiffTable) PartitionRanges(ctx *sql.Context, ranges []prolly.Range) (s return nil, err } - cmHash, _, err := cmItr.Next(ctx) + cmHash, _, _, _, err := cmItr.Next(ctx) if err != nil { return nil, err } @@ -424,7 +424,7 @@ func (dt *DiffTable) scanHeightForChild(ctx *sql.Context, parent hash.Hash, heig func (dt *DiffTable) reverseIterForChild(ctx *sql.Context, parent hash.Hash) (*doltdb.Commit, hash.Hash, error) { iter := doltdb.CommitItrForRoots[*sql.Context](dt.ddb, dt.head) for { - childHs, optCmt, err := iter.Next(ctx) + childHs, optCmt, _, _, err := iter.Next(ctx) if errors.Is(err, io.EOF) { return nil, hash.Hash{}, nil } else if err != nil { @@ -839,7 +839,7 @@ func (dps *DiffPartitions) Next(ctx *sql.Context) (sql.Partition, error) { } for { - cmHash, optCmt, err := dps.cmItr.Next(ctx) + cmHash, optCmt, _, _, err := dps.cmItr.Next(ctx) if err != nil { return nil, err } diff --git a/go/libraries/doltcore/sqle/dtables/log_table.go b/go/libraries/doltcore/sqle/dtables/log_table.go index 78b5b5608a7..2757bec49ef 100644 --- a/go/libraries/doltcore/sqle/dtables/log_table.go +++ b/go/libraries/doltcore/sqle/dtables/log_table.go @@ -235,7 +235,7 @@ func NewLogItr(ctx *sql.Context, ddb *doltdb.DoltDB, head *doltdb.Commit) (*LogI // Next retrieves the next row. It will return io.EOF if it's the last row. // After retrieving the last row, Close will be automatically closed. func (itr *LogItr) Next(ctx *sql.Context) (sql.Row, error) { - h, optCmt, err := itr.child.Next(ctx) + h, optCmt, meta, height, err := itr.child.Next(ctx) if err != nil { return nil, err } @@ -246,14 +246,18 @@ func (itr *LogItr) Next(ctx *sql.Context) (sql.Row, error) { return nil, doltdb.ErrGhostCommitRuntimeFailure } - meta, err := cm.GetCommitMeta(ctx) - if err != nil { - return nil, err + if meta == nil { + meta, err = cm.GetCommitMeta(ctx) + if err != nil { + return nil, err + } } - height, err := cm.Height() - if err != nil { - return nil, err + if height == 0 { + height, err = cm.Height() + if err != nil { + return nil, err + } } return sql.NewRow(h.String(), meta.Name, meta.Email, meta.Time(), meta.Description, height), nil diff --git a/go/libraries/doltcore/sqle/dtables/unscoped_diff_table.go b/go/libraries/doltcore/sqle/dtables/unscoped_diff_table.go index ced05ba7871..4192a2e8e0d 100644 --- a/go/libraries/doltcore/sqle/dtables/unscoped_diff_table.go +++ b/go/libraries/doltcore/sqle/dtables/unscoped_diff_table.go @@ -338,7 +338,7 @@ func (itr *doltDiffCommitHistoryRowItr) Next(ctx *sql.Context) (sql.Row, error) } itr.commits = nil } else if itr.child != nil { - _, optCmt, err := itr.child.Next(ctx) + _, optCmt, _, _, err := itr.child.Next(ctx) if err != nil { return nil, err } @@ -347,6 +347,7 @@ func (itr *doltDiffCommitHistoryRowItr) Next(ctx *sql.Context) (sql.Row, error) return nil, io.EOF } + // TODO: we already have meta and height err = itr.loadTableChanges(ctx, commit) if err == doltdb.ErrGhostCommitEncountered { // When showing the diff table in a shallow clone, we show as much of the dolt_history_{table} as we can, diff --git a/go/libraries/doltcore/sqle/history_table.go b/go/libraries/doltcore/sqle/history_table.go index 3be850b5cfc..3dd9dc6ee30 100644 --- a/go/libraries/doltcore/sqle/history_table.go +++ b/go/libraries/doltcore/sqle/history_table.go @@ -456,7 +456,7 @@ type commitPartitioner struct { // Next returns the next partition and nil, io.EOF when complete func (cp commitPartitioner) Next(ctx *sql.Context) (sql.Partition, error) { - h, optCmt, err := cp.cmItr.Next(ctx) + h, optCmt, _, _, err := cp.cmItr.Next(ctx) if err != nil { return nil, err } diff --git a/go/performance/dolt_log_bench/.gitignore b/go/performance/dolt_log_bench/.gitignore new file mode 100644 index 00000000000..d8085e04202 --- /dev/null +++ b/go/performance/dolt_log_bench/.gitignore @@ -0,0 +1,5 @@ +*.test +*.pprof +*.out +*.pdf +*.exe \ No newline at end of file diff --git a/go/performance/dolt_log_bench/dolt_log_test.go b/go/performance/dolt_log_bench/dolt_log_test.go new file mode 100644 index 00000000000..4c9c3124a02 --- /dev/null +++ b/go/performance/dolt_log_bench/dolt_log_test.go @@ -0,0 +1,94 @@ +// 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 dolt_log_bench + +import ( + "context" + "fmt" + "io" + "testing" + + "github.com/dolthub/go-mysql-server/sql" + "github.com/stretchr/testify/require" + + "github.com/dolthub/dolt/go/cmd/dolt/commands" + "github.com/dolthub/dolt/go/cmd/dolt/commands/engine" + "github.com/dolthub/dolt/go/libraries/doltcore/dtestutils" + "github.com/dolthub/dolt/go/libraries/doltcore/env" +) + +var dEnv *env.DoltEnv + +func init() { + dEnv = dtestutils.CreateTestEnv() + populateCommitGraph(dEnv) +} + +func setupBenchmark(t *testing.B, dEnv *env.DoltEnv) (*sql.Context, *engine.SqlEngine) { + ctx := context.Background() + config := &engine.SqlEngineConfig{ + ServerUser: "root", + Autocommit: true, + } + + mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv) + require.NoError(t, err) + + eng, err := engine.NewSqlEngine(ctx, mrEnv, config) + require.NoError(t, err) + + sqlCtx, err := eng.NewLocalContext(ctx) + require.NoError(t, err) + + sqlCtx.SetCurrentDatabase("dolt") + return sqlCtx, eng +} + +func populateCommitGraph(dEnv *env.DoltEnv) { + ctx := context.Background() + cliCtx, err := commands.NewArgFreeCliContext(ctx, dEnv, dEnv.FS) + if err != nil { + panic(err) + } + defer cliCtx.Close() + execSql := func(dEnv *env.DoltEnv, q string) int { + args := []string{"-r", "null", "-q", q} + return commands.SqlCmd{}.Exec(ctx, "sql", args, dEnv, cliCtx) + } + for i := 0; i < 500; i++ { + execSql(dEnv, fmt.Sprintf("call dolt_commit('--allow-empty', '-m', '%d') ", i)) + } +} + +func BenchmarkDoltLog(b *testing.B) { + ctx, eng := setupBenchmark(b, dEnv) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, iter, _, err := eng.Query(ctx, "select * from dolt_log()") + require.NoError(b, err) + for { + _, err := iter.Next(ctx) + if err != nil { + break + } + } + require.Error(b, io.EOF) + err = iter.Close(ctx) + require.NoError(b, err) + } + _ = eng.Close() + b.ReportAllocs() +} diff --git a/go/store/datas/database_common.go b/go/store/datas/database_common.go index 26e139e700a..e81a01da497 100644 --- a/go/store/datas/database_common.go +++ b/go/store/datas/database_common.go @@ -106,7 +106,7 @@ func (db *database) loadDatasetsNomsMap(ctx context.Context, rootHash hash.Hash) } func (db *database) loadDatasetsRefmap(ctx context.Context, rootHash hash.Hash) (prolly.AddressMap, error) { - if rootHash == (hash.Hash{}) { + if rootHash.IsEmpty() { return prolly.NewEmptyAddressMap(db.ns) } @@ -151,7 +151,7 @@ func (m nomsDatasetsMap) IterAll(ctx context.Context, cb func(string, hash.Hash) } // Datasets returns the Map of Datasets in the current root. If you intend to edit the map and commit changes back, -// then you should fetch the current root, then call DatasetsInRoot with that hash. Otherwise another writer could +// then you should fetch the current root, then call DatasetsInRoot with that hash. Otherwise, another writer could // change the root value between when you get the root hash and call this method. func (db *database) Datasets(ctx context.Context) (DatasetsMap, error) { rootHash, err := db.rt.Root(ctx)