diff --git a/go/cmd/dolt/cli/arg_parser_helpers.go b/go/cmd/dolt/cli/arg_parser_helpers.go index 0a04e0f20b..7e42c95462 100644 --- a/go/cmd/dolt/cli/arg_parser_helpers.go +++ b/go/cmd/dolt/cli/arg_parser_helpers.go @@ -281,6 +281,12 @@ func CreateCountCommitsArgParser() *argparser.ArgParser { return ap } +func CreateReflogArgParser() *argparser.ArgParser { + ap := argparser.NewArgParserWithMaxArgs("reflog", 1) + ap.SupportsFlag(AllFlag, "", "Show all refs, including hidden refs, such as DoltHub workspace refs") + return ap +} + func CreateGlobalArgParser(name string) *argparser.ArgParser { ap := argparser.NewArgParserWithVariableArgs(name) if name == "dolt" { diff --git a/go/cmd/dolt/commands/gc.go b/go/cmd/dolt/commands/gc.go index 982def3114..b8c5915d1e 100644 --- a/go/cmd/dolt/commands/gc.go +++ b/go/cmd/dolt/commands/gc.go @@ -16,9 +16,6 @@ package commands import ( "context" - "errors" - - "github.com/fatih/color" "github.com/dolthub/dolt/go/cmd/dolt/cli" "github.com/dolthub/dolt/go/cmd/dolt/errhand" @@ -82,48 +79,39 @@ func (cmd GarbageCollectionCmd) EventType() eventsapi.ClientEventType { // Version displays the version of the running dolt client // Exec executes the command func (cmd GarbageCollectionCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv, cliCtx cli.CliContext) int { - var verr errhand.VerboseError - ap := cmd.ArgParser() help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, gcDocs, ap)) apr := cli.ParseArgsOrDie(ap, args, help) - // We assert this here simply because the implementation of GC can - // delay actually trying to write to the chunk store for a long time - // after doing a lot or work. It's better to fail early. - if dEnv.IsAccessModeReadOnly() { - return HandleVErrAndExitCode(errhand.VerboseErrorFromError(env.ErrDatabaseIsLocked), help) + queryist, sqlCtx, closeFunc, err := cliCtx.QueryEngine(ctx) + if err != nil { + return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) + } + if closeFunc != nil { + defer closeFunc() } - var err error + query, err := constructDoltGCQuery(apr) + if err != nil { + return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) + } + + _, _, err = queryist.Query(sqlCtx, query) + if err != nil && err != chunks.ErrNothingToCollect { + return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) + } + + return HandleVErrAndExitCode(nil, usage) +} + +// constructDoltGCQuery generates the sql query necessary to call DOLT_GC() +func constructDoltGCQuery(apr *argparser.ArgParseResults) (string, error) { + query := "call DOLT_GC(" if apr.Contains(cli.ShallowFlag) { - err = dEnv.DoltDB.ShallowGC(ctx) - if err != nil { - if err == chunks.ErrUnsupportedOperation { - verr = errhand.BuildDError("this database does not support shallow garbage collection").Build() - return HandleVErrAndExitCode(verr, usage) - } - verr = errhand.BuildDError("an error occurred during garbage collection").AddCause(err).Build() - } - } else { - // full gc - dEnv, err = MaybeMigrateEnv(ctx, dEnv) - if err != nil { - verr = errhand.BuildDError("could not load manifest for gc").AddCause(err).Build() - return HandleVErrAndExitCode(verr, usage) - } - - err = dEnv.DoltDB.GC(ctx, nil) - if err != nil { - if errors.Is(err, chunks.ErrNothingToCollect) { - cli.PrintErrln(color.YellowString("Nothing to collect.")) - } else { - verr = errhand.BuildDError("an error occurred during garbage collection").AddCause(err).Build() - } - } + query += "'--shallow'" } - - return HandleVErrAndExitCode(verr, usage) + query += ")" + return query, nil } func MaybeMigrateEnv(ctx context.Context, dEnv *env.DoltEnv) (*env.DoltEnv, error) { diff --git a/go/cmd/dolt/commands/ls.go b/go/cmd/dolt/commands/ls.go index e2fd7da785..80cec64d11 100644 --- a/go/cmd/dolt/commands/ls.go +++ b/go/cmd/dolt/commands/ls.go @@ -32,7 +32,7 @@ import ( var lsDocs = cli.CommandDocumentationContent{ ShortDesc: "List tables", - LongDesc: `With no arguments lists the tables in the current working set but if a commit is specified it will list the tables in that commit. If the {{.EmphasisLeft}}--verbose{{.EmphasisRight}} flag is provided a row count and a hash of the table will also be displayed. + LongDesc: `With no arguments lists the tables in the current working set but if a commit is specified it will list the tables in that commit. If the {{.EmphasisLeft}}--verbose{{.EmphasisRight}} flag is provided a row count of the table will also be displayed. If the {{.EmphasisLeft}}--system{{.EmphasisRight}} flag is supplied this will show the dolt system tables which are queryable with SQL. diff --git a/go/cmd/dolt/commands/query_diff.go b/go/cmd/dolt/commands/query_diff.go index 42b1616d0d..9291150802 100644 --- a/go/cmd/dolt/commands/query_diff.go +++ b/go/cmd/dolt/commands/query_diff.go @@ -55,7 +55,7 @@ func (q QueryDiff) Name() string { } func (q QueryDiff) Description() string { - return "description" + return "Shows table diff between two queries." } func (q QueryDiff) Docs() *cli.CommandDocumentation { diff --git a/go/cmd/dolt/commands/reflog.go b/go/cmd/dolt/commands/reflog.go new file mode 100644 index 0000000000..4c07cfe773 --- /dev/null +++ b/go/cmd/dolt/commands/reflog.go @@ -0,0 +1,197 @@ +// Copyright 2023 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 commands + +import ( + "context" + "fmt" + "strings" + + "github.com/dolthub/go-mysql-server/sql" + "github.com/gocraft/dbr/v2" + "github.com/gocraft/dbr/v2/dialect" + + "github.com/dolthub/dolt/go/cmd/dolt/cli" + "github.com/dolthub/dolt/go/cmd/dolt/errhand" + eventsapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi/v1alpha1" + "github.com/dolthub/dolt/go/libraries/doltcore/env" + "github.com/dolthub/dolt/go/libraries/utils/argparser" + "github.com/dolthub/dolt/go/store/util/outputpager" +) + +var reflogDocs = cli.CommandDocumentationContent{ + ShortDesc: "Shows a history of named refs", + LongDesc: `Shows the history of named refs (e.g. branches and tags), which is useful for understanding how a branch +or tag changed over time to reference different commits, particularly for information not surfaced through {{.EmphasisLeft}}dolt log{{.EmphasisRight}}. +The data from Dolt's reflog comes from [Dolt's journaling chunk store](https://www.dolthub.com/blog/2023-03-08-dolt-chunk-journal/). +This data is local to a Dolt database and never included when pushing, pulling, or cloning a Dolt database. This means when you clone a Dolt database, it will not have any reflog data until you perform operations that change what commit branches or tags reference. + +Dolt's reflog is similar to [Git's reflog](https://git-scm.com/docs/git-reflog), but there are a few differences: +- The Dolt reflog currently only supports named references, such as branches and tags, and not any of Git's special refs (e.g. {{.EmphasisLeft}}HEAD{{.EmphasisRight}}, {{.EmphasisLeft}}FETCH-HEAD{{.EmphasisRight}}, {{.EmphasisLeft}}MERGE-HEAD{{.EmphasisRight}}). +- The Dolt reflog can be queried for the log of references, even after a reference has been deleted. In Git, once a branch or tag is deleted, the reflog for that ref is also deleted and to find the last commit a branch or tag pointed to you have to use Git's special {{.EmphasisLeft}}HEAD{{.EmphasisRight}} reflog to find the commit, which can sometimes be challenging. Dolt makes this much easier by allowing you to see the history for a deleted ref so you can easily see the last commit a branch or tag pointed to before it was deleted.`, + Synopsis: []string{ + `[--all] {{.LessThan}}ref{{.GreaterThan}}`, + }, +} + +type ReflogCmd struct{} + +// Name is returns the name of the Dolt cli command. This is what is used on the command line to invoke the command +func (cmd ReflogCmd) Name() string { + return "reflog" +} + +// Description returns a description of the command +func (cmd ReflogCmd) Description() string { + return "Show history of named refs." +} + +// EventType returns the type of the event to log +func (cmd ReflogCmd) EventType() eventsapi.ClientEventType { + return eventsapi.ClientEventType_REFLOG +} + +func (cmd ReflogCmd) Docs() *cli.CommandDocumentation { + ap := cmd.ArgParser() + return cli.NewCommandDocumentation(reflogDocs, ap) +} + +func (cmd ReflogCmd) ArgParser() *argparser.ArgParser { + return cli.CreateReflogArgParser() +} + +func (cmd ReflogCmd) RequiresRepo() bool { + return false +} + +// Exec executes the command +func (cmd ReflogCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv, cliCtx cli.CliContext) int { + ap := cmd.ArgParser() + help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, reflogDocs, ap)) + apr := cli.ParseArgsOrDie(ap, args, help) + + queryist, sqlCtx, closeFunc, err := cliCtx.QueryEngine(ctx) + if err != nil { + return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) + } + if closeFunc != nil { + defer closeFunc() + } + + query, err := constructInterpolatedDoltReflogQuery(apr) + if err != nil { + return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) + } + + rows, err := GetRowsForSql(queryist, sqlCtx, query) + if err != nil { + return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) + } + + return printReflog(rows, queryist, sqlCtx) +} + +// constructInterpolatedDoltReflogQuery generates the sql query necessary to call the DOLT_REFLOG() function. +// Also interpolates this query to prevent sql injection +func constructInterpolatedDoltReflogQuery(apr *argparser.ArgParseResults) (string, error) { + var params []interface{} + var args []string + + if apr.NArg() == 1 { + params = append(params, apr.Arg(0)) + args = append(args, "?") + } + if apr.Contains(cli.AllFlag) { + args = append(args, "'--all'") + } + + query := fmt.Sprintf("SELECT ref, commit_hash, commit_message FROM DOLT_REFLOG(%s)", strings.Join(args, ", ")) + interpolatedQuery, err := dbr.InterpolateForDialect(query, params, dialect.MySQL) + if err != nil { + return "", err + } + + return interpolatedQuery, nil +} + +type ReflogInfo struct { + ref string + commitHash string + commitMessage string +} + +// printReflog takes a list of sql rows with columns ref, commit hash, commit message. Prints the reflog to stdout +func printReflog(rows []sql.Row, queryist cli.Queryist, sqlCtx *sql.Context) int { + var reflogInfo []ReflogInfo + + // Get the hash of HEAD for the `HEAD ->` decoration + headHash := "" + res, err := GetRowsForSql(queryist, sqlCtx, "SELECT hashof('HEAD')") + if err == nil { + // still print the reflog even if we can't get the hash + headHash = res[0][0].(string) + } + + for _, row := range rows { + ref := row[0].(string) + commitHash := row[1].(string) + commitMessage := row[2].(string) + reflogInfo = append(reflogInfo, ReflogInfo{ref, commitHash, commitMessage}) + } + + reflogToStdOut(reflogInfo, headHash) + + return 0 +} + +// reflogToStdOut takes a list of ReflogInfo and prints the reflog to stdout +func reflogToStdOut(reflogInfo []ReflogInfo, headHash string) { + if cli.ExecuteWithStdioRestored == nil { + return + } + cli.ExecuteWithStdioRestored(func() { + pager := outputpager.Start() + defer pager.Stop() + + for _, info := range reflogInfo { + // TODO: use short hash instead + line := []string{fmt.Sprintf("\033[33m%s\033[0m", info.commitHash)} // commit hash in yellow (33m) + + processedRef := processRefForReflog(info.ref) + if headHash != "" && headHash == info.commitHash { + line = append(line, fmt.Sprintf("\033[33m(\033[36;1mHEAD -> %s\033[33m)\033[0m", processedRef)) // HEAD in cyan (36;1) + } else { + line = append(line, fmt.Sprintf("\033[33m(%s\033[33m)\033[0m", processedRef)) // () in yellow (33m) + } + line = append(line, fmt.Sprintf("%s\n", info.commitMessage)) + pager.Writer.Write([]byte(strings.Join(line, " "))) + } + }) +} + +// processRefForReflog takes a full ref (e.g. refs/heads/master) or tag name and returns the ref name (e.g. master) with relevant decoration. +func processRefForReflog(fullRef string) string { + if strings.HasPrefix(fullRef, "refs/heads/") { + return fmt.Sprintf("\033[32;1m%s\033[0m", strings.TrimPrefix(fullRef, "refs/heads/")) // branch in green (32;1m) + } else if strings.HasPrefix(fullRef, "refs/tags/") { + return fmt.Sprintf("\033[33mtag: %s\033[0m", strings.TrimPrefix(fullRef, "refs/tags/")) // tag in yellow (33m) + } else if strings.HasPrefix(fullRef, "refs/remotes/") { + return fmt.Sprintf("\033[31;1m%s\033[0m", strings.TrimPrefix(fullRef, "refs/remotes/")) // remote in red (31;1m) + } else if strings.HasPrefix(fullRef, "refs/workspaces/") { + return fmt.Sprintf("\033[35;1mworkspace: %s\033[0m", strings.TrimPrefix(fullRef, "refs/workspaces/")) // workspace in magenta (35;1m) + } else { + return fullRef + } +} diff --git a/go/cmd/dolt/commands/sqlserver/creds.go b/go/cmd/dolt/commands/sqlserver/creds.go index 17fe046ac0..285c1955f1 100644 --- a/go/cmd/dolt/commands/sqlserver/creds.go +++ b/go/cmd/dolt/commands/sqlserver/creds.go @@ -96,7 +96,7 @@ func FindAndLoadLocalCreds(fs filesys.Filesys) (creds *LocalCreds, err error) { if err != nil { return nil, err } - for root != "" && root[len(root)-1] != '/' { + for root != "" && root[len(root)-1] != filepath.Separator { creds, err := LoadLocalCreds(fs) if err == nil { return creds, err diff --git a/go/cmd/dolt/dolt.go b/go/cmd/dolt/dolt.go index 0208d0402c..ad64ec2a8f 100644 --- a/go/cmd/dolt/dolt.go +++ b/go/cmd/dolt/dolt.go @@ -122,6 +122,7 @@ var doltSubCommands = []cli.Command{ &commands.Assist{}, commands.ProfileCmd{}, commands.QueryDiff{}, + commands.ReflogCmd{}, } var commandsWithoutCliCtx = []cli.Command{ @@ -138,7 +139,6 @@ var commandsWithoutCliCtx = []cli.Command{ commands.MigrateCmd{}, indexcmds.Commands, commands.ReadTablesCmd{}, - commands.GarbageCollectionCmd{}, commands.FilterBranchCmd{}, commands.RootsCmd{}, commands.VersionCmd{VersionStr: Version}, diff --git a/go/gen/proto/dolt/services/eventsapi/v1alpha1/event_constants.pb.go b/go/gen/proto/dolt/services/eventsapi/v1alpha1/event_constants.pb.go index 188068cfc1..c561933fea 100644 --- a/go/gen/proto/dolt/services/eventsapi/v1alpha1/event_constants.pb.go +++ b/go/gen/proto/dolt/services/eventsapi/v1alpha1/event_constants.pb.go @@ -155,6 +155,7 @@ const ( ClientEventType_STASH_POP ClientEventType = 60 ClientEventType_SHOW ClientEventType = 61 ClientEventType_PROFILE ClientEventType = 62 + ClientEventType_REFLOG ClientEventType = 63 ) // Enum value maps for ClientEventType. diff --git a/go/go.mod b/go/go.mod index e24c83c68d..6e97ce243b 100644 --- a/go/go.mod +++ b/go/go.mod @@ -59,7 +59,7 @@ require ( github.com/cespare/xxhash v1.1.0 github.com/creasty/defaults v1.6.0 github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 - github.com/dolthub/go-mysql-server v0.17.1-0.20231201192511-a3ee71b20029 + github.com/dolthub/go-mysql-server v0.17.1-0.20231201211641-8889517a6d60 github.com/dolthub/swiss v0.1.0 github.com/goccy/go-json v0.10.2 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 diff --git a/go/go.sum b/go/go.sum index dfa44b7261..be89dd7609 100644 --- a/go/go.sum +++ b/go/go.sum @@ -181,8 +181,8 @@ github.com/dolthub/fslock v0.0.3 h1:iLMpUIvJKMKm92+N1fmHVdxJP5NdyDK5bK7z7Ba2s2U= github.com/dolthub/fslock v0.0.3/go.mod h1:QWql+P17oAAMLnL4HGB5tiovtDuAjdDTPbuqx7bYfa0= github.com/dolthub/go-icu-regex v0.0.0-20230524105445-af7e7991c97e h1:kPsT4a47cw1+y/N5SSCkma7FhAPw7KeGmD6c9PBZW9Y= github.com/dolthub/go-icu-regex v0.0.0-20230524105445-af7e7991c97e/go.mod h1:KPUcpx070QOfJK1gNe0zx4pA5sicIK1GMikIGLKC168= -github.com/dolthub/go-mysql-server v0.17.1-0.20231201192511-a3ee71b20029 h1:Y8fx0dGnjI6QU+SAzyytzExQQP53dpcedMdwFNxIcxc= -github.com/dolthub/go-mysql-server v0.17.1-0.20231201192511-a3ee71b20029/go.mod h1:vXlRKS39WHav9N51VsfYphKhmSA2t5FkhHmW3BtwH5I= +github.com/dolthub/go-mysql-server v0.17.1-0.20231201211641-8889517a6d60 h1:4oj5xEB5anIEfFa2PXSMnb2jELIcnB4mexIeozmKyZM= +github.com/dolthub/go-mysql-server v0.17.1-0.20231201211641-8889517a6d60/go.mod h1:vXlRKS39WHav9N51VsfYphKhmSA2t5FkhHmW3BtwH5I= github.com/dolthub/ishell v0.0.0-20221214210346-d7db0b066488 h1:0HHu0GWJH0N6a6keStrHhUAK5/o9LVfkh44pvsV4514= github.com/dolthub/ishell v0.0.0-20221214210346-d7db0b066488/go.mod h1:ehexgi1mPxRTk0Mok/pADALuHbvATulTh6gzr7NzZto= github.com/dolthub/jsonpath v0.0.2-0.20230525180605-8dc13778fd72 h1:NfWmngMi1CYUWU4Ix8wM+USEhjc+mhPlT9JUR/anvbQ= diff --git a/go/libraries/doltcore/merge/merge_prolly_rows.go b/go/libraries/doltcore/merge/merge_prolly_rows.go index 62a6c9181b..fb2779582d 100644 --- a/go/libraries/doltcore/merge/merge_prolly_rows.go +++ b/go/libraries/doltcore/merge/merge_prolly_rows.go @@ -130,7 +130,12 @@ func mergeProllyTableData(ctx *sql.Context, tm *TableMerger, finalSch schema.Sch keyless := schema.IsKeyless(tm.leftSch) - pri, err := newPrimaryMerger(leftEditor, tm, valueMerger, finalSch, mergeInfo) + defaults, err := resolveDefaults(ctx, tm.name, finalSch, tm.leftSch) + if err != nil { + return nil, nil, err + } + + pri, err := newPrimaryMerger(leftEditor, tm, valueMerger, finalSch, mergeInfo, defaults) if err != nil { return nil, nil, err } @@ -1045,15 +1050,17 @@ type primaryMerger struct { tableMerger *TableMerger finalSch schema.Schema mergeInfo MergeInfo + defaults []sql.Expression } -func newPrimaryMerger(leftEditor *prolly.MutableMap, tableMerger *TableMerger, valueMerger *valueMerger, finalSch schema.Schema, mergeInfo MergeInfo) (*primaryMerger, error) { +func newPrimaryMerger(leftEditor *prolly.MutableMap, tableMerger *TableMerger, valueMerger *valueMerger, finalSch schema.Schema, mergeInfo MergeInfo, defaults []sql.Expression) (*primaryMerger, error) { return &primaryMerger{ mut: leftEditor, valueMerger: valueMerger, tableMerger: tableMerger, finalSch: finalSch, mergeInfo: mergeInfo, + defaults: defaults, }, nil } @@ -1150,13 +1157,8 @@ func (m *primaryMerger) merge(ctx *sql.Context, diff tree.ThreeWayDiff, sourceSc return fmt.Errorf("cannot merge keyless tables with reordered columns") } } else { - defaults, err := resolveDefaults(ctx, m.tableMerger.name, m.finalSch, m.tableMerger.leftSch) - if err != nil { - return err - } - tempTupleValue, err := remapTupleWithColumnDefaults(ctx, diff.Key, newTupleValue, sourceSch.GetValueDescriptor(), - m.valueMerger.leftMapping, m.tableMerger, m.tableMerger.leftSch, m.finalSch, defaults, m.valueMerger.syncPool, false) + m.valueMerger.leftMapping, m.tableMerger, m.tableMerger.leftSch, m.finalSch, m.defaults, m.valueMerger.syncPool, false) if err != nil { return err } diff --git a/go/libraries/doltcore/sqle/dolt_diff_stat_table_function.go b/go/libraries/doltcore/sqle/dolt_diff_stat_table_function.go index 984910354e..4a0368feb1 100644 --- a/go/libraries/doltcore/sqle/dolt_diff_stat_table_function.go +++ b/go/libraries/doltcore/sqle/dolt_diff_stat_table_function.go @@ -45,8 +45,6 @@ type DiffStatTableFunction struct { dotCommitExpr sql.Expression tableNameExpr sql.Expression database sql.Database - tabId sql.TableId - colset sql.ColSet } var diffStatTableSchema = sql.Schema{ @@ -79,26 +77,6 @@ func (ds *DiffStatTableFunction) NewInstance(ctx *sql.Context, db sql.Database, return node, nil } -func (ds *DiffStatTableFunction) WithId(id sql.TableId) sql.TableIdNode { - ret := *ds - ret.tabId = id - return &ret -} - -func (ds *DiffStatTableFunction) Id() sql.TableId { - return ds.tabId -} - -func (ds *DiffStatTableFunction) WithColumns(set sql.ColSet) sql.TableIdNode { - ret := *ds - ds.colset = set - return &ret -} - -func (ds *DiffStatTableFunction) Columns() sql.ColSet { - return ds.colset -} - func (ds *DiffStatTableFunction) DataLength(ctx *sql.Context) (uint64, error) { numBytesPerRow := schema.SchemaAvgLength(ds.Schema()) numRows, _, err := ds.RowCount(ctx) diff --git a/go/libraries/doltcore/sqle/dolt_diff_summary_table_function.go b/go/libraries/doltcore/sqle/dolt_diff_summary_table_function.go index 58c12ed72a..f46243da93 100644 --- a/go/libraries/doltcore/sqle/dolt_diff_summary_table_function.go +++ b/go/libraries/doltcore/sqle/dolt_diff_summary_table_function.go @@ -42,8 +42,6 @@ type DiffSummaryTableFunction struct { dotCommitExpr sql.Expression tableNameExpr sql.Expression database sql.Database - tabId sql.TableId - colset sql.ColSet } var diffSummaryTableSchema = sql.Schema{ @@ -69,26 +67,6 @@ func (ds *DiffSummaryTableFunction) NewInstance(ctx *sql.Context, db sql.Databas return node, nil } -func (ds *DiffSummaryTableFunction) WithId(id sql.TableId) sql.TableIdNode { - ret := *ds - ret.tabId = id - return &ret -} - -func (ds *DiffSummaryTableFunction) Id() sql.TableId { - return ds.tabId -} - -func (ds *DiffSummaryTableFunction) WithColumns(set sql.ColSet) sql.TableIdNode { - ret := *ds - ret.colset = set - return &ret -} - -func (ds *DiffSummaryTableFunction) Columns() sql.ColSet { - return ds.colset -} - func (ds *DiffSummaryTableFunction) DataLength(ctx *sql.Context) (uint64, error) { numBytesPerRow := schema.SchemaAvgLength(ds.Schema()) numRows, _, err := ds.RowCount(ctx) diff --git a/go/libraries/doltcore/sqle/dolt_diff_table_function.go b/go/libraries/doltcore/sqle/dolt_diff_table_function.go index 589a084819..1e00654aa0 100644 --- a/go/libraries/doltcore/sqle/dolt_diff_table_function.go +++ b/go/libraries/doltcore/sqle/dolt_diff_table_function.go @@ -54,29 +54,6 @@ type DiffTableFunction struct { tableDelta diff.TableDelta fromDate *types.Timestamp toDate *types.Timestamp - - tabId sql.TableId - colset sql.ColSet -} - -func (dtf *DiffTableFunction) WithId(id sql.TableId) sql.TableIdNode { - ret := *dtf - ret.tabId = id - return &ret -} - -func (dtf *DiffTableFunction) Id() sql.TableId { - return dtf.tabId -} - -func (dtf *DiffTableFunction) WithColumns(set sql.ColSet) sql.TableIdNode { - ret := *dtf - ret.colset = set - return &ret -} - -func (dtf *DiffTableFunction) Columns() sql.ColSet { - return dtf.colset } // NewInstance creates a new instance of TableFunction interface diff --git a/go/libraries/doltcore/sqle/dolt_log_table_function.go b/go/libraries/doltcore/sqle/dolt_log_table_function.go index 4bd45f95fe..2ee03a3680 100644 --- a/go/libraries/doltcore/sqle/dolt_log_table_function.go +++ b/go/libraries/doltcore/sqle/dolt_log_table_function.go @@ -48,9 +48,6 @@ type LogTableFunction struct { showParents bool decoration string - tabId sql.TableId - colset sql.ColSet - database sql.Database } @@ -77,26 +74,6 @@ func (ltf *LogTableFunction) NewInstance(ctx *sql.Context, db sql.Database, expr return node, nil } -func (ltf *LogTableFunction) WithId(id sql.TableId) sql.TableIdNode { - ret := *ltf - ret.tabId = id - return &ret -} - -func (ltf *LogTableFunction) Id() sql.TableId { - return ltf.tabId -} - -func (ltf *LogTableFunction) WithColumns(set sql.ColSet) sql.TableIdNode { - ret := *ltf - ret.colset = set - return &ret -} - -func (ltf *LogTableFunction) Columns() sql.ColSet { - return ltf.colset -} - // Database implements the sql.Databaser interface func (ltf *LogTableFunction) Database() sql.Database { return ltf.database diff --git a/go/libraries/doltcore/sqle/dolt_patch_table_function.go b/go/libraries/doltcore/sqle/dolt_patch_table_function.go index 00631e5f52..d047ab799d 100644 --- a/go/libraries/doltcore/sqle/dolt_patch_table_function.go +++ b/go/libraries/doltcore/sqle/dolt_patch_table_function.go @@ -74,28 +74,6 @@ type PatchTableFunction struct { dotCommitExpr sql.Expression tableNameExpr sql.Expression database sql.Database - tabId sql.TableId - colset sql.ColSet -} - -func (p *PatchTableFunction) WithId(id sql.TableId) sql.TableIdNode { - ret := *p - ret.tabId = id - return &ret -} - -func (p *PatchTableFunction) Id() sql.TableId { - return p.tabId -} - -func (p *PatchTableFunction) WithColumns(set sql.ColSet) sql.TableIdNode { - ret := *p - ret.colset = set - return &ret -} - -func (p *PatchTableFunction) Columns() sql.ColSet { - return p.colset } func (p *PatchTableFunction) DataLength(ctx *sql.Context) (uint64, error) { diff --git a/go/libraries/doltcore/sqle/dolt_query_diff_table_function.go b/go/libraries/doltcore/sqle/dolt_query_diff_table_function.go index 777c62fe6b..64e1adb45f 100644 --- a/go/libraries/doltcore/sqle/dolt_query_diff_table_function.go +++ b/go/libraries/doltcore/sqle/dolt_query_diff_table_function.go @@ -45,29 +45,6 @@ type QueryDiffTableFunction struct { rowIter2 sql.RowIter schema1 sql.Schema schema2 sql.Schema - - tabId sql.TableId - colset sql.ColSet -} - -func (tf *QueryDiffTableFunction) WithId(id sql.TableId) sql.TableIdNode { - ret := *tf - ret.tabId = id - return &ret -} - -func (tf *QueryDiffTableFunction) Id() sql.TableId { - return tf.tabId -} - -func (tf *QueryDiffTableFunction) WithColumns(set sql.ColSet) sql.TableIdNode { - ret := *tf - ret.colset = set - return &ret -} - -func (tf *QueryDiffTableFunction) Columns() sql.ColSet { - return tf.colset } // NewInstance creates a new instance of TableFunction interface diff --git a/go/libraries/doltcore/sqle/dolt_schema_diff_table_function.go b/go/libraries/doltcore/sqle/dolt_schema_diff_table_function.go index 8b3dbaa619..4427ef01f5 100644 --- a/go/libraries/doltcore/sqle/dolt_schema_diff_table_function.go +++ b/go/libraries/doltcore/sqle/dolt_schema_diff_table_function.go @@ -60,9 +60,6 @@ type SchemaDiffTableFunction struct { tableNameExpr sql.Expression database sql.Database - - tabId sql.TableId - colset sql.ColSet } var schemaDiffTableSchema = sql.Schema{ @@ -87,25 +84,6 @@ func (ds *SchemaDiffTableFunction) NewInstance(ctx *sql.Context, db sql.Database return node, nil } -func (ds *SchemaDiffTableFunction) WithId(id sql.TableId) sql.TableIdNode { - //TODO implement me - panic("implement me") -} - -func (ds *SchemaDiffTableFunction) Id() sql.TableId { - return ds.tabId -} - -func (ds *SchemaDiffTableFunction) WithColumns(set sql.ColSet) sql.TableIdNode { - ret := *ds - ds.colset = set - return &ret -} - -func (ds *SchemaDiffTableFunction) Columns() sql.ColSet { - return ds.colset -} - func (ds *SchemaDiffTableFunction) DataLength(ctx *sql.Context) (uint64, error) { numBytesPerRow := schema.SchemaAvgLength(ds.Schema()) numRows, _, err := ds.RowCount(ctx) diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_engine_test.go b/go/libraries/doltcore/sqle/enginetest/dolt_engine_test.go index 85ad41ce2f..71d0a9f8be 100644 --- a/go/libraries/doltcore/sqle/enginetest/dolt_engine_test.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_engine_test.go @@ -1984,7 +1984,7 @@ func TestBrokenSystemTableQueries(t *testing.T) { } func TestHistorySystemTable(t *testing.T) { - harness := newDoltHarness(t).WithParallelism(1) + harness := newDoltHarness(t).WithParallelism(2) defer harness.Close() harness.Setup(setup.MydbData) for _, test := range HistorySystemTableScriptTests { diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_queries.go b/go/libraries/doltcore/sqle/enginetest/dolt_queries.go index 91e97f461d..57e1d2ca70 100644 --- a/go/libraries/doltcore/sqle/enginetest/dolt_queries.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_queries.go @@ -4317,7 +4317,7 @@ var DoltReflogTestScripts = []queries.ScriptTest{ Assertions: []queries.ScriptTestAssertion{ { Query: "select * from dolt_reflog('foo', 'bar');", - ExpectedErrStr: "function 'dolt_reflog' expected 0 or 1 arguments, 2 received", + ExpectedErrStr: "error: dolt_reflog has too many positional arguments. Expected at most 1, found 2: ['foo' 'bar']", }, { Query: "select * from dolt_reflog(NULL);", diff --git a/go/libraries/doltcore/sqle/history_table.go b/go/libraries/doltcore/sqle/history_table.go index d79bb44a7a..e3986ab5ca 100644 --- a/go/libraries/doltcore/sqle/history_table.go +++ b/go/libraries/doltcore/sqle/history_table.go @@ -419,7 +419,6 @@ func (ht *HistoryTable) Collation() sql.CollationID { // Partitions returns a PartitionIter which will be used in getting partitions each of which is used to create RowIter. func (ht *HistoryTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) { - // TODO reset ht.cmItr on close iter, err := ht.filterIter(ctx, ht.cmItr) if err != nil { return nil, err diff --git a/go/libraries/doltcore/sqle/reflog_table_function.go b/go/libraries/doltcore/sqle/reflog_table_function.go index b6a868215e..121054f752 100644 --- a/go/libraries/doltcore/sqle/reflog_table_function.go +++ b/go/libraries/doltcore/sqle/reflog_table_function.go @@ -29,11 +29,9 @@ import ( ) type ReflogTableFunction struct { - ctx *sql.Context - database sql.Database - refExpr sql.Expression - tabId sql.TableId - colset sql.ColSet + ctx *sql.Context + database sql.Database + refAndArgExprs []sql.Expression } var _ sql.TableFunction = (*ReflogTableFunction)(nil) @@ -60,26 +58,6 @@ func (rltf *ReflogTableFunction) NewInstance(ctx *sql.Context, database sql.Data return node, nil } -func (rltf *ReflogTableFunction) WithId(id sql.TableId) sql.TableIdNode { - ret := *rltf - ret.tabId = id - return &ret -} - -func (rltf *ReflogTableFunction) Id() sql.TableId { - return rltf.tabId -} - -func (rltf *ReflogTableFunction) WithColumns(set sql.ColSet) sql.TableIdNode { - ret := *rltf - ret.colset = set - return &ret -} - -func (rltf *ReflogTableFunction) Columns() sql.ColSet { - return rltf.colset -} - func (rltf *ReflogTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.RowIter, error) { sqlDb, ok := rltf.database.(dsess.SqlDatabase) if !ok { @@ -87,17 +65,30 @@ func (rltf *ReflogTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.Row } var refName string - if rltf.refExpr != nil { - target, err := rltf.refExpr.Eval(ctx, row) + showAll := false + for _, expr := range rltf.refAndArgExprs { + target, err := expr.Eval(ctx, row) if err != nil { return nil, fmt.Errorf("error evaluating expression (%s): %s", - rltf.refExpr.String(), err.Error()) + expr.String(), err.Error()) } - - refName, ok = target.(string) + targetStr, ok := target.(string) if !ok { return nil, fmt.Errorf("argument (%v) is not a string value, but a %T", target, target) } + + if targetStr == "--all" { + if showAll { + return nil, fmt.Errorf("error: multiple values provided for `all`") + } + showAll = true + } else { + if refName != "" { + return nil, fmt.Errorf("error: %s has too many positional arguments. Expected at most %d, found %d: %s", + rltf.Name(), 1, 2, rltf.refAndArgExprs) + } + refName = targetStr + } } ddb := sqlDb.DbData().Ddb @@ -131,9 +122,15 @@ func (rltf *ReflogTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.Row if doltRef.GetType() == ref.InternalRefType { return nil } + // skip workspace refs by default + if doltRef.GetType() == ref.WorkspaceRefType { + if !showAll { + return nil + } + } // If a ref expression to filter on was specified, see if we match the current ref - if rltf.refExpr != nil { + if refName != "" { // If the caller has supplied a branch or tag name, without the fully qualified ref path, // take the first match and use that as the canonical ref to filter on if strings.HasSuffix(strings.ToLower(id), "/"+strings.ToLower(refName)) { @@ -194,14 +191,21 @@ func (rltf *ReflogTableFunction) Schema() sql.Schema { } func (rltf *ReflogTableFunction) Resolved() bool { - if rltf.refExpr != nil { - return rltf.refExpr.Resolved() + for _, expr := range rltf.refAndArgExprs { + if !expr.Resolved() { + return false + } } return true } func (rltf *ReflogTableFunction) String() string { - return fmt.Sprintf("DOLT_REFLOG(%s)", rltf.refExpr.String()) + var args []string + + for _, expr := range rltf.refAndArgExprs { + args = append(args, expr.String()) + } + return fmt.Sprintf("DOLT_REFLOG(%s)", strings.Join(args, ", ")) } func (rltf *ReflogTableFunction) Children() []sql.Node { @@ -226,21 +230,17 @@ func (rltf *ReflogTableFunction) IsReadOnly() bool { } func (rltf *ReflogTableFunction) Expressions() []sql.Expression { - if rltf.refExpr != nil { - return []sql.Expression{rltf.refExpr} - } - return []sql.Expression{} + return rltf.refAndArgExprs } func (rltf *ReflogTableFunction) WithExpressions(expression ...sql.Expression) (sql.Node, error) { - if len(expression) > 1 { - return nil, sql.ErrInvalidArgumentNumber.New(rltf.Name(), "0 or 1", len(expression)) + if len(expression) > 2 { + return nil, sql.ErrInvalidArgumentNumber.New(rltf.Name(), "0 to 2", len(expression)) } new := *rltf - if len(expression) > 0 { - new.refExpr = expression[0] - } + new.refAndArgExprs = expression + return &new, nil } diff --git a/integration-tests/bats/performance-repo/.dolt/config.json b/integration-tests/bats/performance-repo/.dolt/config.json new file mode 100755 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/integration-tests/bats/performance-repo/.dolt/config.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/integration-tests/bats/performance-repo/.dolt/noms/LOCK b/integration-tests/bats/performance-repo/.dolt/noms/LOCK new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration-tests/bats/performance-repo/.dolt/noms/journal.idx b/integration-tests/bats/performance-repo/.dolt/noms/journal.idx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration-tests/bats/performance-repo/.dolt/noms/manifest b/integration-tests/bats/performance-repo/.dolt/noms/manifest new file mode 100644 index 0000000000..f9789bff57 --- /dev/null +++ b/integration-tests/bats/performance-repo/.dolt/noms/manifest @@ -0,0 +1 @@ +5:__DOLT__:4t8rtld9ul7l137jac93phj1o6qinq6o:8ep2ilbq0lb461mqibkqp80nd0skdend:00000000000000000000000000000000:vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv:3722 \ No newline at end of file diff --git a/integration-tests/bats/performance-repo/.dolt/noms/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv b/integration-tests/bats/performance-repo/.dolt/noms/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv new file mode 100644 index 0000000000..775ad59626 Binary files /dev/null and b/integration-tests/bats/performance-repo/.dolt/noms/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv differ diff --git a/integration-tests/bats/performance-repo/.dolt/repo_state.json b/integration-tests/bats/performance-repo/.dolt/repo_state.json new file mode 100755 index 0000000000..4c1d5a13a1 --- /dev/null +++ b/integration-tests/bats/performance-repo/.dolt/repo_state.json @@ -0,0 +1,6 @@ +{ + "head": "refs/heads/full", + "remotes": {}, + "backups": {}, + "branches": {} +} \ No newline at end of file diff --git a/integration-tests/bats/performance.bats b/integration-tests/bats/performance.bats new file mode 100644 index 0000000000..a3b397b432 --- /dev/null +++ b/integration-tests/bats/performance.bats @@ -0,0 +1,153 @@ +#!/usr/bin/env bats +load $BATS_TEST_DIRNAME/helper/common.bash + +# This BATS test attempts to detect performance regressions when using standard workflows on large datasets. +# Please note that this is a rough approach that is not designed to detect all performance issues, merely an extra +# safeguard against bugs that cause large (order-of-magnitude+) regressions. + +# BATS_TEST_TIMEOUT is measured in seconds and is chosen to be high enough that all tests in this suite pass +# when running on GitHub's CI, but low enough that an order-of magnitude regression will cause them to fail. +BATS_TEST_TIMEOUT=50 + +# This function was used to create the dolt repo used for this test. It is not run during testing. +create_repo() { + dolt init + dolt checkout -b full + + dolt sql -q 'create table t (pk int primary key, c0 text default "1", c3 text default "2", c4 text default "3", c5 text default "4", c6 text default "5", c7 text default "6");' + dolt commit -Am "new table t" + + echo "insert into t(pk) values" > import.sql + for i in {1..100000} + do + echo " ($i)," >> import.sql + done + echo " (104857);" >> import.sql + + dolt sql < import.sql + + dolt add . + dolt commit -m "Add all rows" +} + +setup() { + cp -r $BATS_TEST_DIRNAME/performance-repo/ $BATS_TMPDIR/dolt-repo-$$ + cd $BATS_TMPDIR/dolt-repo-$$ +} + +@test "performance: merge with no schema change and no conflict" { + dolt checkout full + dolt checkout -b mod2 + dolt reset --soft HEAD^ + + dolt sql -q "delete from t where pk % 2 = 0" + + dolt add . + dolt commit -m "Add mod2 rows" + + dolt checkout full + dolt checkout -b mod3 + dolt reset --soft HEAD^ + + dolt sql -q "delete from t where pk % 3 = 0" + + dolt add . + dolt commit -m "Add mod3 rows" + + run dolt merge mod2 + log_status_eq 0 +} + +@test "performance: merge with no schema change and conflict" { + dolt checkout full + dolt checkout -b mod2 + dolt reset --soft HEAD^ + + dolt sql -q "delete from t where pk % 2 = 0" + + dolt add . + dolt commit -m "Add mod2 rows" + + dolt checkout full + dolt checkout -b mod3 + dolt reset --soft HEAD^ + + dolt sql -q "delete from t where pk % 3 = 0" + dolt sql -q 'update t set c0 = "conflict" where pk = 91' + + dolt add . + dolt commit -m "Add mod3 rows" + + run dolt merge mod2 + log_status_eq 1 + [[ "$output" =~ "Merge conflict in t" ]] || false + + dolt conflicts resolve --ours t + BATS_TEST_TIMEOUT=1 +} + + +@test "performance: merge with schema change and no conflict" { + dolt checkout full + dolt checkout -b mod2 + dolt reset --soft HEAD^ + + dolt sql -q "delete from t where pk % 2 = 0" + + dolt add . + dolt commit -m "Add mod2 rows" + + dolt sql -q "alter table t add column c1 int default 1" + dolt add . + dolt commit -m "Add column c1" + + dolt checkout full + dolt checkout -b mod3 + dolt reset --soft HEAD^ + + dolt sql -q "delete from t where pk % 3 = 0" + + dolt add . + dolt commit -m "Add mod3 rows" + + dolt sql -q "alter table t add column c2 int default 2" + dolt add . + dolt commit -m "Add column c2" + + run dolt merge mod2 + log_status_eq 0 +} + +@test "performance: merge with schema change and conflict" { + dolt checkout full + dolt checkout -b mod2 + dolt reset --soft HEAD^ + + dolt sql -q "delete from t where pk % 2 = 0" + + dolt add . + dolt commit -m "Add mod2 rows" + + dolt sql -q "alter table t add column c1 int default 1" + dolt add . + dolt commit -m "Add column c1" + + dolt checkout full + dolt checkout -b mod3 + dolt reset --soft HEAD^ + + dolt sql -q "delete from t where pk % 3 = 0" + dolt sql -q 'update t set c0 = "conflict" where pk = 91' + + dolt add . + dolt commit -m "Add mod3 rows" + + dolt sql -q "alter table t add column c2 int default 2" + dolt add . + dolt commit -m "Add column c2" + + run dolt merge mod2 + log_status_eq 1 + + dolt conflicts resolve --ours t +} diff --git a/integration-tests/bats/reflog.bats b/integration-tests/bats/reflog.bats old mode 100644 new mode 100755 index 38d5a7e34e..daec38d5a0 --- a/integration-tests/bats/reflog.bats +++ b/integration-tests/bats/reflog.bats @@ -6,40 +6,27 @@ teardown() { teardown_common } -# Asserts that when DOLT_DISABLE_REFLOG is set, the dolt_reflog() table -# function returns an empty result set with no error. +# Asserts that when DOLT_DISABLE_REFLOG is set, dolt reflog returns nothing with no error. @test "reflog: disabled with DOLT_DISABLE_REFLOG" { export DOLT_DISABLE_REFLOG=true - setup_common + setup_common # need to set env vars before setup_common for remote tests + dolt sql -q "create table t (i int primary key, j int);" dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; dolt commit -Am "initial commit" dolt commit --allow-empty -m "test commit 1" - run dolt sql -q "select * from dolt_reflog();" + run dolt reflog [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -# Sanity check for the most basic case of querying the Dolt reflog -@test "reflog: enabled by default" { - setup_common - dolt sql -q "create table t (i int primary key, j int);" - dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; - dolt commit -Am "initial commit" - - run dolt sql -q "select * from dolt_reflog();" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 6 ] - [[ "$output" =~ "initial commit" ]] || false - [[ "$output" =~ "Initialize data repository" ]] || false -} - # Asserts that when DOLT_REFLOG_RECORD_LIMIT has been set, the reflog only contains the # most recent entries and is limited by the env var's value. @test "reflog: set DOLT_REFLOG_RECORD_LIMIT" { export DOLT_REFLOG_RECORD_LIMIT=2 - setup_common + setup_common # need to set env vars before setup_common for remote tests + dolt sql -q "create table t (i int primary key, j int);" dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; dolt commit -Am "initial commit" @@ -47,10 +34,237 @@ teardown() { dolt commit --allow-empty -m "test commit 2" # Only the most recent two ref changes should appear in the log - run dolt sql -q "select * from dolt_reflog();" + run dolt reflog [ "$status" -eq 0 ] [[ "$output" =~ "test commit 1" ]] || false [[ "$output" =~ "test commit 2" ]] || false [[ ! "$output" =~ "initial commit" ]] || false [[ ! "$output" =~ "Initialize data repository" ]] || false } + +@test "reflog: simple reflog" { + setup_common + + dolt sql -q "create table t (i int primary key, j int);" + dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; + dolt commit -Am "initial commit" + + run dolt reflog + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 2 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(HEAD -> main) initial commit" ]] || false + [[ "$out" =~ "Initialize data repository" ]] || false +} + +@test "reflog: reflog with ref given" { + setup_common + + dolt sql < main) inserting row 1" ]] || false + [[ "$out" =~ "creating table t1" ]] || false + [[ "$out" =~ "Initialize data repository" ]] || false + + # ref is case-insensitive + run dolt reflog rEFs/heAdS/MAIN + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(HEAD -> main) inserting row 1" ]] || false + [[ "$out" =~ "creating table t1" ]] || false + [[ "$out" =~ "Initialize data repository" ]] || false + + run dolt reflog main + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(HEAD -> main) inserting row 1" ]] || false + [[ "$out" =~ "creating table t1" ]] || false + [[ "$out" =~ "Initialize data repository" ]] || false + + # ref is case-insensitive + run dolt reflog MaIn + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(HEAD -> main) inserting row 1" ]] || false + [[ "$out" =~ "creating table t1" ]] || false + [[ "$out" =~ "Initialize data repository" ]] || false + + run dolt reflog refs/heads/branch1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(branch1) inserting row 3" ]] || false + [[ "$out" =~ "inserting row 2" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false + + run dolt reflog branch1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(branch1) inserting row 3" ]] || false + [[ "$out" =~ "inserting row 2" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false + + run dolt reflog refs/tags/tag1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 2 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(tag: tag1) inserting row 3" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false + + # ref is case-insensitive + run dolt reflog Refs/tAGs/TaG1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 2 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(tag: tag1) inserting row 3" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false + + run dolt reflog tag1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 2 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(tag: tag1) inserting row 3" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false + + # ref is case-insensitive + run dolt reflog TAg1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 2 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(tag: tag1) inserting row 3" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false + + dolt branch -D branch1 + + run dolt reflog branch1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(branch1) inserting row 3" ]] || false + [[ "$out" =~ "inserting row 2" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false + + dolt tag -d tag1 + run dolt reflog tag1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 2 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(tag: tag1) inserting row 3" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false +} + +@test "reflog: garbage collection with no newgen data" { + setup_common + + run dolt reflog + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 1 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(HEAD -> main) Initialize data repository" ]] || false + + dolt gc + + run dolt reflog + [ "$status" -eq 0 ] + if [ "$SQL_ENGINE" = "remote-engine" ]; then + [ "${#lines[@]}" -eq 1 ] + [[ "$output" =~ "Initialize data repository" ]] || false + else + [ "${#lines[@]}" -eq 0 ] + fi +} + +@test "reflog: garbage collection with newgen data" { + setup_common + + dolt sql < main) inserting row 2" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false + [[ "$out" =~ "creating table t1" ]] || false + [[ "$out" =~ "Initialize data repository" ]] || false + + dolt gc + + run dolt reflog main + [ "$status" -eq 0 ] + if [ "$SQL_ENGINE" = "remote-engine" ]; then + [ "${#lines[@]}" -eq 1 ] + [[ "$output" =~ "inserting row 2" ]] || false + else + [ "${#lines[@]}" -eq 0 ] + fi +} + +@test "reflog: too many arguments given" { + setup_common + + run dolt reflog foo bar + [ "$status" -eq 1 ] + [[ "$output" =~ "error: reflog has too many positional arguments" ]] || false +} + +@test "reflog: unknown ref returns nothing" { + setup_common + + dolt sql -q "create table t (i int primary key, j int);" + dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; + dolt commit -Am "initial commit" + + run dolt reflog foo + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "reflog: 'HEAD -> ' decoration only appears on HEAD entries" { + setup_common + + dolt sql -q "create table t (i int primary key, j int);" + dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; + dolt commit -Am "initial commit" + + run dolt reflog + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 2 ] + line1=$(echo "${lines[0]}" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + line2=$(echo "${lines[1]}" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$line1" =~ "(HEAD -> main) initial commit" ]] || false + [[ "$line2" =~ "Initialize data repository" ]] || false + [[ ! "$line2" =~ "HEAD" ]] || false +} diff --git a/integration-tests/bats/sql-local-remote.bats b/integration-tests/bats/sql-local-remote.bats index 95366a0239..9c5dfd545a 100644 --- a/integration-tests/bats/sql-local-remote.bats +++ b/integration-tests/bats/sql-local-remote.bats @@ -1270,3 +1270,71 @@ SQL [[ "$localOutput" == "$remoteOutput" ]] || false } + +@test "sql-local-remote: verify dolt reflog behavior" { + cd altDB + dolt sql -q "create table t (i int primary key, j int);" + dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; + dolt commit -Am "initial commit" + + run dolt --verbose-engine-setup reflog + [ $status -eq 0 ] + [[ "$output" =~ "starting local mode" ]] || false + [[ "$output" =~ "initial commit" ]] || false + run dolt reflog + localOutput=$output + + start_sql_server altDB + run dolt --verbose-engine-setup reflog + [ $status -eq 0 ] + [[ "$output" =~ "starting remote mode" ]] || false + [[ "$output" =~ "initial commit" ]] || false + run dolt reflog + remoteOutput=$output + + [[ "$localOutput" == "$remoteOutput" ]] || false +} + +@test "sql-local-remote: verify dolt gc behavior" { + cd altDB + dolt sql <