mirror of
https://github.com/dolthub/dolt.git
synced 2026-05-19 19:39:45 -05:00
Merge branch 'main' into james/alter
This commit is contained in:
@@ -50,21 +50,6 @@ jobs:
|
||||
env:
|
||||
BRANCH_NAME: ${{ github.head_ref }}
|
||||
CHANGE_TARGET: ${{ github.base_ref }}
|
||||
- name: Check generated protobufs
|
||||
working-directory: ./proto
|
||||
run: |
|
||||
(cd third_party/protobuf && bazel build //:protoc)
|
||||
(cd third_party/protobuf-go && go build -o ._protoc-gen-go ./cmd/protoc-gen-go)
|
||||
(cd third_party/grpc-go/cmd/protoc-gen-go-grpc && go build -o ._protoc-gen-go-grpc .)
|
||||
make clean all
|
||||
changes=$(git status --porcelain)
|
||||
diff=$(git diff)
|
||||
if [ ! -z "$changes" ]; then
|
||||
echo "ERROR: Generated protobuf structs are different from the checked in version."
|
||||
echo "$changes"
|
||||
echo "$diff"
|
||||
exit 1
|
||||
fi
|
||||
- name: Check generated flatbuffers
|
||||
working-directory: ./go/serial
|
||||
run: |
|
||||
@@ -121,6 +106,7 @@ jobs:
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref || github.ref }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
|
||||
submodules: true
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Run go mod tidy
|
||||
run: go mod tidy
|
||||
@@ -168,3 +154,18 @@ jobs:
|
||||
add: "."
|
||||
cwd: "."
|
||||
pull: "--ff"
|
||||
- name: Check generated protobufs
|
||||
working-directory: ./proto
|
||||
run: |
|
||||
(cd third_party/protobuf && bazel build //:protoc)
|
||||
(cd third_party/protobuf-go && go build -o ._protoc-gen-go ./cmd/protoc-gen-go)
|
||||
(cd third_party/grpc-go/cmd/protoc-gen-go-grpc && go build -o ._protoc-gen-go-grpc .)
|
||||
make clean all
|
||||
changes=$(git status --porcelain)
|
||||
diff=$(git diff)
|
||||
if [ ! -z "$changes" ]; then
|
||||
echo "ERROR: Generated protobuf structs are different from the checked in version."
|
||||
echo "$changes"
|
||||
echo "$diff"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -116,6 +116,7 @@ const (
|
||||
CachedFlag = "cached"
|
||||
ListFlag = "list"
|
||||
UserParam = "user"
|
||||
NoPrettyFlag = "no-pretty"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
|
||||
package cli
|
||||
|
||||
import "github.com/dolthub/dolt/go/libraries/utils/argparser"
|
||||
|
||||
// CliContexct is used to pass top level command information down to subcommands.
|
||||
type CliContext interface {
|
||||
// GlobalArgs returns the arguments passed before the subcommand.
|
||||
GlobalArgs() *argparser.ArgParseResults
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
"github.com/dolthub/dolt/go/store/types"
|
||||
)
|
||||
|
||||
func isHelp(str string) bool {
|
||||
func IsHelp(str string) bool {
|
||||
str = strings.TrimSpace(str)
|
||||
|
||||
if len(str) == 0 {
|
||||
@@ -50,7 +50,7 @@ func isHelp(str string) bool {
|
||||
|
||||
func hasHelpFlag(args []string) bool {
|
||||
for _, arg := range args {
|
||||
if isHelp(arg) {
|
||||
if IsHelp(arg) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -171,7 +171,7 @@ func (hc SubCommandHandler) Hidden() bool {
|
||||
|
||||
func (hc SubCommandHandler) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv, cliCtx CliContext) int {
|
||||
if len(args) < 1 && hc.Unspecified == nil {
|
||||
hc.printUsage(commandStr)
|
||||
hc.PrintUsage(commandStr)
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -190,12 +190,12 @@ func (hc SubCommandHandler) Exec(ctx context.Context, commandStr string, args []
|
||||
return hc.handleCommand(ctx, commandStr, hc.Unspecified, args, dEnv, cliCtx)
|
||||
}
|
||||
|
||||
if !isHelp(subCommandStr) {
|
||||
if !IsHelp(subCommandStr) {
|
||||
PrintErrln(color.RedString("Unknown Command " + subCommandStr))
|
||||
return 1
|
||||
}
|
||||
|
||||
hc.printUsage(commandStr)
|
||||
hc.PrintUsage(commandStr)
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -302,7 +302,7 @@ func CheckUserNameAndEmail(dEnv *env.DoltEnv) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (hc SubCommandHandler) printUsage(commandStr string) {
|
||||
func (hc SubCommandHandler) PrintUsage(commandStr string) {
|
||||
Println("Valid commands for", commandStr, "are")
|
||||
|
||||
for _, cmd := range hc.Subcommands {
|
||||
|
||||
@@ -90,5 +90,5 @@ func (cmd BlameCmd) Exec(ctx context.Context, commandStr string, args []string,
|
||||
}
|
||||
args = []string{"--" + QueryFlag, fmt.Sprintf(blameQueryTemplate, apr.Arg(0))}
|
||||
|
||||
return SqlCmd{}.Exec(ctx, "sql", args, dEnv, nil)
|
||||
return SqlCmd{}.Exec(ctx, "sql", args, dEnv, cliCtx)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ package cnfcmds
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
@@ -23,7 +24,6 @@ import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/cli"
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/commands"
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/commands/engine"
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
|
||||
eventsapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi/v1alpha1"
|
||||
@@ -39,9 +39,9 @@ import (
|
||||
|
||||
var catDocs = cli.CommandDocumentationContent{
|
||||
ShortDesc: "print conflicts",
|
||||
LongDesc: `The dolt conflicts cat command reads table conflicts and writes them to the standard output.`,
|
||||
LongDesc: `The dolt conflicts cat command reads table conflicts from the working set and writes them to the standard output.`,
|
||||
Synopsis: []string{
|
||||
"[{{.LessThan}}commit{{.GreaterThan}}] {{.LessThan}}table{{.GreaterThan}}...",
|
||||
"{{.LessThan}}table{{.GreaterThan}}...",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -88,41 +88,26 @@ func (cmd CatCmd) Exec(ctx context.Context, commandStr string, args []string, dE
|
||||
return 1
|
||||
}
|
||||
|
||||
root, verr := commands.GetWorkingWithVErr(dEnv)
|
||||
if verr != nil {
|
||||
return exitWithVerr(verr)
|
||||
ws, err := dEnv.WorkingSet(ctx)
|
||||
if err != nil {
|
||||
return exitWithVerr(errhand.VerboseErrorFromError(err))
|
||||
}
|
||||
|
||||
cm, verr := commands.MaybeGetCommitWithVErr(dEnv, args[0])
|
||||
if verr != nil {
|
||||
return exitWithVerr(verr)
|
||||
}
|
||||
|
||||
// If no commit was resolved from the first argument, assume the args are all table names and print the conflicts
|
||||
if cm == nil {
|
||||
if verr := printConflicts(ctx, dEnv, root, args); verr != nil {
|
||||
return exitWithVerr(verr)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
tblNames := args[1:]
|
||||
tblNames := args
|
||||
if len(tblNames) == 0 {
|
||||
cli.Println("No tables specified")
|
||||
usage()
|
||||
return 1
|
||||
} else if len(tblNames) == 1 && tblNames[0] == "." {
|
||||
tblNames, err = ws.WorkingRoot().GetTableNames(ctx)
|
||||
if err != nil {
|
||||
return exitWithVerr(errhand.VerboseErrorFromError(err))
|
||||
}
|
||||
}
|
||||
|
||||
root, err := cm.GetRootValue(ctx)
|
||||
if err != nil {
|
||||
return exitWithVerr(errhand.BuildDError("unable to get the root value").AddCause(err).Build())
|
||||
if verr := printConflicts(ctx, dEnv, ws, tblNames); verr != nil {
|
||||
return exitWithVerr(errhand.VerboseErrorFromError(err))
|
||||
}
|
||||
|
||||
if verr = printConflicts(ctx, dEnv, root, tblNames); verr != nil {
|
||||
return exitWithVerr(verr)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -131,79 +116,116 @@ func exitWithVerr(verr errhand.VerboseError) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func printConflicts(ctx context.Context, dEnv *env.DoltEnv, root *doltdb.RootValue, tblNames []string) errhand.VerboseError {
|
||||
if len(tblNames) == 1 && tblNames[0] == "." {
|
||||
var err error
|
||||
tblNames, err = root.GetTableNames(ctx)
|
||||
if err != nil {
|
||||
return errhand.BuildDError("unable to read tables").AddCause(err).Build()
|
||||
}
|
||||
}
|
||||
|
||||
func printConflicts(ctx context.Context, dEnv *env.DoltEnv, ws *doltdb.WorkingSet, tblNames []string) error {
|
||||
eng, dbName, err := engine.NewSqlEngineForEnv(ctx, dEnv)
|
||||
if err != nil {
|
||||
return errhand.VerboseErrorFromError(err)
|
||||
return err
|
||||
}
|
||||
stdOut := iohelp.NopWrCloser(cli.CliOut)
|
||||
|
||||
for _, tblName := range tblNames {
|
||||
verr := func() errhand.VerboseError {
|
||||
if has, err := root.HasTable(ctx, tblName); err != nil {
|
||||
return errhand.BuildDError("error: unable to read database").AddCause(err).Build()
|
||||
} else if !has {
|
||||
return errhand.BuildDError("error: unknown table '%s'", tblName).Build()
|
||||
}
|
||||
// first print schema conflicts
|
||||
if ws.MergeActive() && ws.MergeState().HasSchemaConflicts() {
|
||||
sqlCtx, err := eng.NewLocalContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sqlCtx.SetCurrentDatabase(dbName)
|
||||
|
||||
tbl, _, err := root.GetTable(ctx, tblName)
|
||||
if err != nil {
|
||||
return errhand.BuildDError("error: unable to read database").AddCause(err).Build()
|
||||
for _, table := range tblNames {
|
||||
if err = printSchemaConflicts(sqlCtx, stdOut, eng, table); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
has, err := tbl.HasConflicts(ctx)
|
||||
if err != nil {
|
||||
return errhand.BuildDError("error: unable to read database").AddCause(err).Build()
|
||||
}
|
||||
if !has {
|
||||
return nil
|
||||
}
|
||||
|
||||
baseSch, sch, mergeSch, err := tbl.GetConflictSchemas(ctx, tblName)
|
||||
if err != nil {
|
||||
return errhand.BuildDError("failed to fetch conflicts").AddCause(err).Build()
|
||||
}
|
||||
unionSch, err := untyped.UntypedSchemaUnion(baseSch, sch, mergeSch)
|
||||
if err != nil {
|
||||
return errhand.BuildDError("failed to fetch conflicts").AddCause(err).Build()
|
||||
}
|
||||
sqlUnionSch, err := sqlutil.FromDoltSchema(tblName, unionSch)
|
||||
if err != nil {
|
||||
return errhand.BuildDError("failed to fetch conflicts").AddCause(err).Build()
|
||||
}
|
||||
|
||||
sqlCtx, err := eng.NewLocalContext(ctx)
|
||||
if err != nil {
|
||||
return errhand.BuildDError("failed to fetch conflicts").AddCause(err).Build()
|
||||
}
|
||||
sqlCtx.SetCurrentDatabase(dbName)
|
||||
|
||||
confSqlSch, rowItr, err := eng.Query(sqlCtx, buildConflictQuery(baseSch, sch, mergeSch, tblName))
|
||||
if err != nil {
|
||||
return errhand.BuildDError("failed to fetch conflicts").AddCause(err).Build()
|
||||
}
|
||||
|
||||
tw := tabular.NewFixedWidthConflictTableWriter(sqlUnionSch.Schema, iohelp.NopWrCloser(cli.CliOut), 100)
|
||||
err = writeConflictResults(sqlCtx, confSqlSch, sqlUnionSch.Schema, rowItr, tw)
|
||||
if err != nil {
|
||||
return errhand.BuildDError("failed to print conflicts").AddCause(err).Build()
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
if verr != nil {
|
||||
return verr
|
||||
}
|
||||
}
|
||||
|
||||
// next print data conflicts
|
||||
root := ws.WorkingRoot()
|
||||
for _, tblName := range tblNames {
|
||||
if has, err := root.HasTable(ctx, tblName); err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return fmt.Errorf("error: unknown table '%s'", tblName)
|
||||
}
|
||||
|
||||
tbl, _, err := root.GetTable(ctx, tblName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
has, err := tbl.HasConflicts(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
continue
|
||||
}
|
||||
|
||||
base, sch, mergeSch, err := tbl.GetConflictSchemas(ctx, tblName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sqlCtx, err := eng.NewLocalContext(ctx)
|
||||
if err != nil {
|
||||
return errhand.BuildDError("failed to fetch conflicts").AddCause(err).Build()
|
||||
}
|
||||
sqlCtx.SetCurrentDatabase(dbName)
|
||||
|
||||
confSqlSch, rowItr, err := eng.Query(sqlCtx, buildDataConflictQuery(base, sch, mergeSch, tblName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
unionSch, err := untyped.UntypedSchemaUnion(base, sch, mergeSch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sqlUnionSch, err := sqlutil.FromDoltSchema(tblName, unionSch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tw := tabular.NewFixedWidthConflictTableWriter(sqlUnionSch.Schema, stdOut, 100)
|
||||
|
||||
err = writeConflictResults(sqlCtx, confSqlSch, sqlUnionSch.Schema, rowItr, tw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func printSchemaConflicts(sqlCtx *sql.Context, wrCloser io.WriteCloser, eng *engine.SqlEngine, table string) error {
|
||||
|
||||
sqlSch, rowItr, err := eng.Query(sqlCtx, buildSchemaConflictQuery(table))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if cerr := rowItr.Close(sqlCtx); err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
|
||||
tw := tabular.NewFixedWidthTableWriter(sqlSch, wrCloser, 100)
|
||||
defer func() {
|
||||
if cerr := tw.Close(sqlCtx); err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
r, err := rowItr.Next(sqlCtx)
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tw.WriteSqlRow(sqlCtx, r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -241,7 +263,12 @@ func writeConflictResults(
|
||||
}
|
||||
}
|
||||
|
||||
func buildConflictQuery(base, sch, mergeSch schema.Schema, tblName string) string {
|
||||
func buildSchemaConflictQuery(table string) string {
|
||||
return fmt.Sprintf("select our_schema, their_schema, base_schema, description "+
|
||||
"from dolt_schema_conflicts where table_name = '%s'", table)
|
||||
}
|
||||
|
||||
func buildDataConflictQuery(base, sch, mergeSch schema.Schema, tblName string) string {
|
||||
cols := quoteWithPrefix(base.GetAllCols().GetColumnNames(), "base_")
|
||||
cols = append(cols, quoteWithPrefix(sch.GetAllCols().GetColumnNames(), "our_")...)
|
||||
cols = append(cols, quoteWithPrefix(mergeSch.GetAllCols().GetColumnNames(), "their_")...)
|
||||
|
||||
+108
-16
@@ -17,9 +17,10 @@ package commands
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/cli"
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
|
||||
@@ -27,11 +28,16 @@ import (
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/env"
|
||||
"github.com/dolthub/dolt/go/libraries/utils/argparser"
|
||||
"github.com/dolthub/dolt/go/store/datas"
|
||||
"github.com/dolthub/dolt/go/store/hash"
|
||||
"github.com/dolthub/dolt/go/store/util/outputpager"
|
||||
)
|
||||
|
||||
var hashRegex = regexp.MustCompile(`^#?[0-9a-v]{32}$`)
|
||||
|
||||
type showOpts struct {
|
||||
showParents bool
|
||||
pretty bool
|
||||
decoration string
|
||||
specRefs []string
|
||||
|
||||
@@ -73,6 +79,7 @@ func (cmd ShowCmd) ArgParser() *argparser.ArgParser {
|
||||
// Flags inherited from Log
|
||||
ap.SupportsFlag(cli.ParentsFlag, "", "Shows all parents of each commit in the log.")
|
||||
ap.SupportsString(cli.DecorateFlag, "", "decorate_fmt", "Shows refs next to commits. Valid options are short, full, no, and auto")
|
||||
ap.SupportsFlag(cli.NoPrettyFlag, "", "Show the object without making it pretty.")
|
||||
|
||||
// Flags inherited from Diff
|
||||
ap.SupportsFlag(DataFlag, "d", "Show only the data changes, do not show the schema changes (Both shown by default).")
|
||||
@@ -100,9 +107,18 @@ func (cmd ShowCmd) Exec(ctx context.Context, commandStr string, args []string, d
|
||||
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
|
||||
}
|
||||
|
||||
if err := cmd.validateArgs(apr); err != nil {
|
||||
return handleErrAndExit(err)
|
||||
}
|
||||
|
||||
if !opts.pretty && !dEnv.DoltDB.Format().UsesFlatbuffers() {
|
||||
cli.PrintErrln("dolt show --no-pretty is not supported when using old LD_1 storage format.")
|
||||
return 1
|
||||
}
|
||||
|
||||
opts.diffDisplaySettings = parseDiffDisplaySettings(ctx, dEnv, apr)
|
||||
|
||||
err = showCommits(ctx, dEnv, opts)
|
||||
err = showObjects(ctx, dEnv, opts)
|
||||
|
||||
return handleErrAndExit(err)
|
||||
}
|
||||
@@ -135,25 +151,19 @@ func parseShowArgs(ctx context.Context, dEnv *env.DoltEnv, apr *argparser.ArgPar
|
||||
|
||||
return &showOpts{
|
||||
showParents: apr.Contains(cli.ParentsFlag),
|
||||
pretty: !apr.Contains(cli.NoPrettyFlag),
|
||||
decoration: decorateOption,
|
||||
specRefs: apr.Args,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func showCommits(ctx context.Context, dEnv *env.DoltEnv, opts *showOpts) error {
|
||||
func showObjects(ctx context.Context, dEnv *env.DoltEnv, opts *showOpts) error {
|
||||
if len(opts.specRefs) == 0 {
|
||||
return showCommit(ctx, dEnv, opts, dEnv.RepoStateReader().CWBHeadSpec())
|
||||
return showCommitSpec(ctx, dEnv, opts, dEnv.RepoStateReader().CWBHeadSpec())
|
||||
}
|
||||
|
||||
for _, specRef := range opts.specRefs {
|
||||
commitSpec, err := getCommitSpec(specRef)
|
||||
|
||||
if err != nil {
|
||||
cli.PrintErrln(color.HiRedString("Fatal error: invalid commit spec %s", specRef))
|
||||
return err
|
||||
}
|
||||
|
||||
err = showCommit(ctx, dEnv, opts, commitSpec)
|
||||
err := showSpecRef(ctx, dEnv, opts, specRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -162,14 +172,96 @@ func showCommits(ctx context.Context, dEnv *env.DoltEnv, opts *showOpts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func showCommit(ctx context.Context, dEnv *env.DoltEnv, opts *showOpts, commitSpec *doltdb.CommitSpec) error {
|
||||
// parseHashString converts a string representing a hash into a hash.Hash.
|
||||
func parseHashString(hashStr string) (hash.Hash, error) {
|
||||
unprefixed := strings.TrimPrefix(hashStr, "#")
|
||||
parsedHash, ok := hash.MaybeParse(unprefixed)
|
||||
if !ok {
|
||||
return hash.Hash{}, errors.New("invalid hash: " + hashStr)
|
||||
}
|
||||
return parsedHash, nil
|
||||
}
|
||||
|
||||
comm, err := dEnv.DoltDB.Resolve(ctx, commitSpec, dEnv.RepoStateReader().CWBHeadRef())
|
||||
func showSpecRef(ctx context.Context, dEnv *env.DoltEnv, opts *showOpts, specRef string) error {
|
||||
roots, err := dEnv.Roots(ctx)
|
||||
if err != nil {
|
||||
cli.PrintErrln(color.HiRedString("Fatal error: cannot resolve commit spec."))
|
||||
return err
|
||||
}
|
||||
|
||||
upperCaseSpecRef := strings.ToUpper(specRef)
|
||||
if upperCaseSpecRef == doltdb.Working || upperCaseSpecRef == doltdb.Staged || hashRegex.MatchString(specRef) {
|
||||
var refHash hash.Hash
|
||||
var err error
|
||||
if upperCaseSpecRef == doltdb.Working {
|
||||
refHash, err = roots.Working.HashOf()
|
||||
} else if upperCaseSpecRef == doltdb.Staged {
|
||||
refHash, err = roots.Staged.HashOf()
|
||||
} else {
|
||||
refHash, err = parseHashString(specRef)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value, err := dEnv.DoltDB.ValueReadWriter().ReadValue(ctx, refHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if value == nil {
|
||||
return fmt.Errorf("Unable to resolve object ref %s", specRef)
|
||||
}
|
||||
|
||||
if !opts.pretty {
|
||||
cli.Println(value.Kind(), value.HumanReadableString())
|
||||
}
|
||||
|
||||
// If this is a commit, use the pretty printer. To determine whether it's a commit, try calling NewCommitFromValue.
|
||||
commit, err := doltdb.NewCommitFromValue(ctx, dEnv.DoltDB.ValueReadWriter(), dEnv.DoltDB.NodeStore(), value)
|
||||
|
||||
if err == datas.ErrNotACommit {
|
||||
if !dEnv.DoltDB.Format().UsesFlatbuffers() {
|
||||
return fmt.Errorf("dolt show cannot show non-commit objects when using the old LD_1 storage format: %s is not a commit", specRef)
|
||||
}
|
||||
cli.Println(value.Kind(), value.HumanReadableString())
|
||||
} else if err == nil {
|
||||
showCommit(ctx, dEnv, opts, commit)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
} else { // specRef is a CommitSpec, which must resolve to a Commit.
|
||||
commitSpec, err := getCommitSpec(specRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = showCommitSpec(ctx, dEnv, opts, commitSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func showCommitSpec(ctx context.Context, dEnv *env.DoltEnv, opts *showOpts, commitSpec *doltdb.CommitSpec) error {
|
||||
|
||||
commit, err := dEnv.DoltDB.Resolve(ctx, commitSpec, dEnv.RepoStateReader().CWBHeadRef())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.pretty {
|
||||
err = showCommit(ctx, dEnv, opts, commit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
value := commit.Value()
|
||||
cli.Println(value.Kind(), value.HumanReadableString())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func showCommit(ctx context.Context, dEnv *env.DoltEnv, opts *showOpts, comm *doltdb.Commit) error {
|
||||
|
||||
cHashToRefs, err := getHashToRefs(ctx, dEnv, opts.decoration)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -214,7 +306,7 @@ func showCommit(ctx context.Context, dEnv *env.DoltEnv, opts *showOpts, commitSp
|
||||
}
|
||||
|
||||
if comm.NumParents() > 1 {
|
||||
return fmt.Errorf("Requested commit is a merge commit. 'dolt show' currently only supports viewing non-merge commits.")
|
||||
return fmt.Errorf("requested commit is a merge commit. 'dolt show' currently only supports viewing non-merge commits")
|
||||
}
|
||||
|
||||
commitRoot, err := comm.GetRootValue(ctx)
|
||||
|
||||
+48
-32
@@ -189,6 +189,12 @@ func (cmd SqlCmd) Exec(ctx context.Context, commandStr string, args []string, dE
|
||||
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
|
||||
}
|
||||
|
||||
globalArgs := cliCtx.GlobalArgs()
|
||||
err = validateSqlArgs(globalArgs)
|
||||
if err != nil {
|
||||
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
|
||||
}
|
||||
|
||||
// We need a username and password for many SQL commands, so set defaults if they don't exist
|
||||
dEnv.Config.SetFailsafes(env.DefaultFailsafeConfig)
|
||||
|
||||
@@ -198,24 +204,33 @@ func (cmd SqlCmd) Exec(ctx context.Context, commandStr string, args []string, dE
|
||||
username = user
|
||||
}
|
||||
|
||||
mrEnv, verr := getMultiRepoEnv(ctx, apr, dEnv)
|
||||
// data-dir args come either from the global args or the subcommand args. We need to check both.
|
||||
var dataDir string
|
||||
dataDirGiven := false
|
||||
if multiDbDir, ok := apr.GetValue(MultiDBDirFlag); ok {
|
||||
// When GlobalArgs migration is complete, drop this flag.
|
||||
dataDir = multiDbDir
|
||||
dataDirGiven = true
|
||||
} else if dataDirPath, ok := apr.GetValue(DataDirFlag); ok {
|
||||
// TODO: remove this once we remove the deprecated passing of data dir directly to subcommand.
|
||||
dataDir = dataDirPath
|
||||
dataDirGiven = true
|
||||
} else if dataDirPath, ok := globalArgs.GetValue(DataDirFlag); ok {
|
||||
dataDir = dataDirPath
|
||||
dataDirGiven = true
|
||||
}
|
||||
|
||||
mrEnv, dataDir, verr := getMultiRepoEnv(ctx, dataDir, dEnv)
|
||||
if verr != nil {
|
||||
return HandleVErrAndExitCode(verr, usage)
|
||||
}
|
||||
|
||||
// need to return cfgdirpath and error
|
||||
var cfgDirPath string
|
||||
var dataDir string
|
||||
if multiDbDir, ok := apr.GetValue(MultiDBDirFlag); ok {
|
||||
dataDir = multiDbDir
|
||||
} else if dataDirPath, ok := apr.GetValue(DataDirFlag); ok {
|
||||
dataDir = dataDirPath
|
||||
}
|
||||
|
||||
cfgDir, cfgDirSpecified := apr.GetValue(CfgDirFlag)
|
||||
if cfgDirSpecified {
|
||||
cfgDirPath = cfgDir
|
||||
} else if len(dataDir) != 0 {
|
||||
} else if dataDirGiven {
|
||||
cfgDirPath = filepath.Join(dataDir, DefaultCfgDirName)
|
||||
} else {
|
||||
// Look in parent directory for doltcfg
|
||||
@@ -267,10 +282,20 @@ func (cmd SqlCmd) Exec(ctx context.Context, commandStr string, args []string, dE
|
||||
}
|
||||
}
|
||||
|
||||
se, sqlCtx, err := newEngine(ctx, apr, cfgDirPath, privsFp, branchControlFilePath, username, mrEnv)
|
||||
format := engine.FormatTabular
|
||||
if formatSr, ok := apr.GetValue(FormatFlag); ok {
|
||||
var verr errhand.VerboseError
|
||||
format, verr = GetResultFormat(formatSr)
|
||||
if verr != nil {
|
||||
return HandleVErrAndExitCode(verr, usage)
|
||||
}
|
||||
}
|
||||
|
||||
se, sqlCtx, err := newEngine(ctx, format, cfgDirPath, privsFp, branchControlFilePath, username, mrEnv)
|
||||
if err != nil {
|
||||
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
|
||||
}
|
||||
|
||||
defer se.Close()
|
||||
|
||||
if query, queryOK := apr.GetValue(QueryFlag); queryOK {
|
||||
@@ -338,7 +363,7 @@ func (cmd SqlCmd) Exec(ctx context.Context, commandStr string, args []string, dE
|
||||
|
||||
func newEngine(
|
||||
ctx context.Context,
|
||||
apr *argparser.ArgParseResults,
|
||||
format engine.PrintResultFormat,
|
||||
cfgDirPath string,
|
||||
privsFp string,
|
||||
branchControlFilePath string,
|
||||
@@ -346,15 +371,6 @@ func newEngine(
|
||||
mrEnv *env.MultiRepoEnv,
|
||||
) (*engine.SqlEngine, *sql.Context, error) {
|
||||
|
||||
format := engine.FormatTabular
|
||||
if formatSr, ok := apr.GetValue(FormatFlag); ok {
|
||||
var verr errhand.VerboseError
|
||||
format, verr = GetResultFormat(formatSr)
|
||||
if verr != nil {
|
||||
return nil, nil, verr
|
||||
}
|
||||
}
|
||||
|
||||
config := &engine.SqlEngineConfig{
|
||||
DoltCfgDirPath: cfgDirPath,
|
||||
PrivFilePath: privsFp,
|
||||
@@ -501,26 +517,26 @@ func execSaveQuery(ctx *sql.Context, dEnv *env.DoltEnv, se *engine.SqlEngine, ap
|
||||
}
|
||||
|
||||
// getMultiRepoEnv returns an appropriate MultiRepoEnv for this invocation of the command
|
||||
func getMultiRepoEnv(ctx context.Context, apr *argparser.ArgParseResults, dEnv *env.DoltEnv) (*env.MultiRepoEnv, errhand.VerboseError) {
|
||||
func getMultiRepoEnv(ctx context.Context, workingDir string, dEnv *env.DoltEnv) (mrEnv *env.MultiRepoEnv, resolvedDir string, verr errhand.VerboseError) {
|
||||
var err error
|
||||
fs := dEnv.FS
|
||||
|
||||
if dataDir, ok := apr.GetValue(MultiDBDirFlag); ok {
|
||||
fs, err = fs.WithWorkingDir(dataDir)
|
||||
} else if dataDir, ok := apr.GetValue(DataDirFlag); ok {
|
||||
fs, err = fs.WithWorkingDir(dataDir)
|
||||
if len(workingDir) > 0 {
|
||||
fs, err = fs.WithWorkingDir(workingDir)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, errhand.VerboseErrorFromError(err)
|
||||
return nil, "", errhand.VerboseErrorFromError(err)
|
||||
}
|
||||
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), fs, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
resolvedDir, err = fs.Abs("")
|
||||
if err != nil {
|
||||
return nil, errhand.VerboseErrorFromError(err)
|
||||
return nil, "", errhand.VerboseErrorFromError(err)
|
||||
}
|
||||
|
||||
return mrEnv, nil
|
||||
mrEnv, err = env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), fs, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
if err != nil {
|
||||
return nil, "", errhand.VerboseErrorFromError(err)
|
||||
}
|
||||
|
||||
return mrEnv, resolvedDir, nil
|
||||
}
|
||||
|
||||
func execBatch(
|
||||
|
||||
@@ -43,6 +43,8 @@ import (
|
||||
|
||||
var tableName = "people"
|
||||
|
||||
var stubCliCtx = BuildEmptyCliContext()
|
||||
|
||||
// Smoke test: Console opens and exits
|
||||
func TestSqlConsole(t *testing.T) {
|
||||
t.Run("SQL console opens and exits", func(t *testing.T) {
|
||||
@@ -53,7 +55,7 @@ func TestSqlConsole(t *testing.T) {
|
||||
args := []string{}
|
||||
commandStr := "dolt sql"
|
||||
|
||||
result := SqlCmd{}.Exec(context.TODO(), commandStr, args, dEnv, nil)
|
||||
result := SqlCmd{}.Exec(context.TODO(), commandStr, args, dEnv, stubCliCtx)
|
||||
assert.Equal(t, 0, result)
|
||||
})
|
||||
|
||||
@@ -81,7 +83,7 @@ func TestSqlBatchMode(t *testing.T) {
|
||||
args := []string{"-b", "-q", test.query}
|
||||
|
||||
commandStr := "dolt sql"
|
||||
result := SqlCmd{}.Exec(context.TODO(), commandStr, args, dEnv, nil)
|
||||
result := SqlCmd{}.Exec(context.TODO(), commandStr, args, dEnv, stubCliCtx)
|
||||
assert.Equal(t, test.expectedRes, result)
|
||||
})
|
||||
}
|
||||
@@ -120,7 +122,7 @@ func TestSqlSelect(t *testing.T) {
|
||||
args := []string{"-q", test.query}
|
||||
|
||||
commandStr := "dolt sql"
|
||||
result := SqlCmd{}.Exec(context.TODO(), commandStr, args, dEnv, nil)
|
||||
result := SqlCmd{}.Exec(context.TODO(), commandStr, args, dEnv, stubCliCtx)
|
||||
assert.Equal(t, test.expectedRes, result)
|
||||
})
|
||||
}
|
||||
@@ -146,7 +148,7 @@ func TestSqlShow(t *testing.T) {
|
||||
args := []string{"-q", test.query}
|
||||
|
||||
commandStr := "dolt sql"
|
||||
result := SqlCmd{}.Exec(context.TODO(), commandStr, args, dEnv, nil)
|
||||
result := SqlCmd{}.Exec(context.TODO(), commandStr, args, dEnv, stubCliCtx)
|
||||
assert.Equal(t, test.expectedRes, result)
|
||||
})
|
||||
}
|
||||
@@ -179,7 +181,7 @@ func TestCreateTable(t *testing.T) {
|
||||
|
||||
args := []string{"-q", test.query}
|
||||
commandStr := "dolt sql"
|
||||
result := SqlCmd{}.Exec(context.TODO(), commandStr, args, dEnv, nil)
|
||||
result := SqlCmd{}.Exec(context.TODO(), commandStr, args, dEnv, stubCliCtx)
|
||||
assert.Equal(t, test.expectedRes, result)
|
||||
|
||||
working, err = dEnv.WorkingRoot(context.Background())
|
||||
@@ -219,7 +221,7 @@ func TestShowTables(t *testing.T) {
|
||||
|
||||
args := []string{"-q", test.query}
|
||||
commandStr := "dolt sql"
|
||||
result := SqlCmd{}.Exec(context.TODO(), commandStr, args, dEnv, nil)
|
||||
result := SqlCmd{}.Exec(context.TODO(), commandStr, args, dEnv, stubCliCtx)
|
||||
assert.Equal(t, test.expectedRes, result)
|
||||
})
|
||||
}
|
||||
@@ -250,7 +252,7 @@ func TestAlterTable(t *testing.T) {
|
||||
|
||||
args := []string{"-q", test.query}
|
||||
commandStr := "dolt sql"
|
||||
result := SqlCmd{}.Exec(context.TODO(), commandStr, args, dEnv, nil)
|
||||
result := SqlCmd{}.Exec(context.TODO(), commandStr, args, dEnv, stubCliCtx)
|
||||
assert.Equal(t, test.expectedRes, result)
|
||||
})
|
||||
}
|
||||
@@ -277,7 +279,7 @@ func TestDropTable(t *testing.T) {
|
||||
|
||||
args := []string{"-q", test.query}
|
||||
commandStr := "dolt sql"
|
||||
result := SqlCmd{}.Exec(context.TODO(), commandStr, args, dEnv, nil)
|
||||
result := SqlCmd{}.Exec(context.TODO(), commandStr, args, dEnv, stubCliCtx)
|
||||
assert.Equal(t, test.expectedRes, result)
|
||||
})
|
||||
}
|
||||
@@ -396,7 +398,7 @@ func TestInsert(t *testing.T) {
|
||||
args := []string{"-q", test.query}
|
||||
|
||||
commandStr := "dolt sql"
|
||||
result := SqlCmd{}.Exec(ctx, commandStr, args, dEnv, nil)
|
||||
result := SqlCmd{}.Exec(ctx, commandStr, args, dEnv, stubCliCtx)
|
||||
assert.Equal(t, test.expectedRes, result)
|
||||
|
||||
if result == 0 {
|
||||
@@ -477,7 +479,7 @@ func TestUpdate(t *testing.T) {
|
||||
args := []string{"-q", test.query}
|
||||
|
||||
commandStr := "dolt sql"
|
||||
result := SqlCmd{}.Exec(ctx, commandStr, args, dEnv, nil)
|
||||
result := SqlCmd{}.Exec(ctx, commandStr, args, dEnv, stubCliCtx)
|
||||
assert.Equal(t, test.expectedRes, result)
|
||||
|
||||
if result == 0 {
|
||||
@@ -552,7 +554,7 @@ func TestDelete(t *testing.T) {
|
||||
|
||||
ctx := context.Background()
|
||||
commandStr := "dolt sql"
|
||||
result := SqlCmd{}.Exec(ctx, commandStr, args, dEnv, nil)
|
||||
result := SqlCmd{}.Exec(ctx, commandStr, args, dEnv, stubCliCtx)
|
||||
assert.Equal(t, test.expectedRes, result)
|
||||
|
||||
if result == 0 {
|
||||
|
||||
@@ -43,8 +43,8 @@ import (
|
||||
|
||||
const (
|
||||
sqlClientDualFlag = "dual"
|
||||
sqlClientQueryFlag = "query"
|
||||
sqlClientUseDbFlag = "use-db"
|
||||
SqlClientQueryFlag = "query"
|
||||
SqlClientUseDbFlag = "use-db"
|
||||
sqlClientResultFormat = "result-format"
|
||||
)
|
||||
|
||||
@@ -84,10 +84,10 @@ func (cmd SqlClientCmd) Docs() *cli.CommandDocumentation {
|
||||
func (cmd SqlClientCmd) ArgParser() *argparser.ArgParser {
|
||||
ap := SqlServerCmd{}.ArgParserWithName(cmd.Name())
|
||||
ap.SupportsFlag(sqlClientDualFlag, "d", "Causes this command to spawn a dolt server that is automatically connected to.")
|
||||
ap.SupportsString(sqlClientQueryFlag, "q", "string", "Sends the given query to the server and immediately exits.")
|
||||
ap.SupportsString(sqlClientUseDbFlag, "", "db_name", fmt.Sprintf("Selects the given database before executing a query. "+
|
||||
"By default, uses the current folder's name. Must be used with the --%s flag.", sqlClientQueryFlag))
|
||||
ap.SupportsString(sqlClientResultFormat, "", "format", fmt.Sprintf("Returns the results in the given format. Must be used with the --%s flag.", sqlClientQueryFlag))
|
||||
ap.SupportsString(SqlClientQueryFlag, "q", "string", "Sends the given query to the server and immediately exits.")
|
||||
ap.SupportsString(SqlClientUseDbFlag, "", "db_name", fmt.Sprintf("Selects the given database before executing a query. "+
|
||||
"By default, uses the current folder's name. Must be used with the --%s flag.", SqlClientQueryFlag))
|
||||
ap.SupportsString(sqlClientResultFormat, "", "format", fmt.Sprintf("Returns the results in the given format. Must be used with the --%s flag.", SqlClientQueryFlag))
|
||||
return ap
|
||||
}
|
||||
|
||||
@@ -123,12 +123,12 @@ func (cmd SqlClientCmd) Exec(ctx context.Context, commandStr string, args []stri
|
||||
cli.PrintErrln(err.Error())
|
||||
return 1
|
||||
}
|
||||
if apr.Contains(sqlClientQueryFlag) {
|
||||
cli.PrintErrln(color.RedString(fmt.Sprintf("--%s flag may not be used with --%s", sqlClientDualFlag, sqlClientQueryFlag)))
|
||||
if apr.Contains(SqlClientQueryFlag) {
|
||||
cli.PrintErrln(color.RedString(fmt.Sprintf("--%s flag may not be used with --%s", sqlClientDualFlag, SqlClientQueryFlag)))
|
||||
return 1
|
||||
}
|
||||
if apr.Contains(sqlClientUseDbFlag) {
|
||||
cli.PrintErrln(color.RedString(fmt.Sprintf("--%s flag may not be used with --%s", sqlClientDualFlag, sqlClientUseDbFlag)))
|
||||
if apr.Contains(SqlClientUseDbFlag) {
|
||||
cli.PrintErrln(color.RedString(fmt.Sprintf("--%s flag may not be used with --%s", sqlClientDualFlag, SqlClientUseDbFlag)))
|
||||
return 1
|
||||
}
|
||||
if apr.Contains(sqlClientResultFormat) {
|
||||
@@ -167,14 +167,14 @@ func (cmd SqlClientCmd) Exec(ctx context.Context, commandStr string, args []stri
|
||||
}
|
||||
}
|
||||
|
||||
query, hasQuery := apr.GetValue(sqlClientQueryFlag)
|
||||
dbToUse, hasUseDb := apr.GetValue(sqlClientUseDbFlag)
|
||||
query, hasQuery := apr.GetValue(SqlClientQueryFlag)
|
||||
dbToUse, hasUseDb := apr.GetValue(SqlClientUseDbFlag)
|
||||
resultFormat, hasResultFormat := apr.GetValue(sqlClientResultFormat)
|
||||
if !hasQuery && hasUseDb {
|
||||
cli.PrintErrln(color.RedString(fmt.Sprintf("--%s may only be used with --%s", sqlClientUseDbFlag, sqlClientQueryFlag)))
|
||||
cli.PrintErrln(color.RedString(fmt.Sprintf("--%s may only be used with --%s", SqlClientUseDbFlag, SqlClientQueryFlag)))
|
||||
return 1
|
||||
} else if !hasQuery && hasResultFormat {
|
||||
cli.PrintErrln(color.RedString(fmt.Sprintf("--%s may only be used with --%s", sqlClientUseDbFlag, sqlClientResultFormat)))
|
||||
cli.PrintErrln(color.RedString(fmt.Sprintf("--%s may only be used with --%s", SqlClientUseDbFlag, sqlClientResultFormat)))
|
||||
return 1
|
||||
}
|
||||
if !hasUseDb && hasQuery {
|
||||
|
||||
@@ -94,5 +94,5 @@ func (cmd CpCmd) Exec(ctx context.Context, commandStr string, args []string, dEn
|
||||
fmt.Sprintf("--%s", commands.BatchFlag),
|
||||
fmt.Sprintf(`--%s`, commands.QueryFlag),
|
||||
queryStr,
|
||||
}, dEnv, nil)
|
||||
}, dEnv, cliCtx)
|
||||
}
|
||||
|
||||
@@ -95,5 +95,5 @@ func (cmd MvCmd) Exec(ctx context.Context, commandStr string, args []string, dEn
|
||||
fmt.Sprintf("--%s", commands.BatchFlag),
|
||||
fmt.Sprintf(`--%s`, commands.QueryFlag),
|
||||
queryStr,
|
||||
}, dEnv, nil)
|
||||
}, dEnv, cliCtx)
|
||||
}
|
||||
|
||||
@@ -93,5 +93,5 @@ func (cmd RmCmd) Exec(ctx context.Context, commandStr string, args []string, dEn
|
||||
fmt.Sprintf("--%s", commands.BatchFlag),
|
||||
fmt.Sprintf(`--%s`, commands.QueryFlag),
|
||||
queryStr,
|
||||
}, dEnv, nil)
|
||||
}, dEnv, cliCtx)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ package commands
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/cli"
|
||||
"github.com/dolthub/dolt/go/libraries/utils/argparser"
|
||||
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/env"
|
||||
@@ -68,3 +71,17 @@ func MaybeGetCommitWithVErr(dEnv *env.DoltEnv, maybeCommit string) (*doltdb.Comm
|
||||
|
||||
return cm, nil
|
||||
}
|
||||
|
||||
type cliCtx struct{}
|
||||
|
||||
func (c cliCtx) GlobalArgs() *argparser.ArgParseResults {
|
||||
ap := argparser.NewArgParserWithMaxArgs("empty", 0)
|
||||
apr, _ := ap.Parse(make([]string, 0))
|
||||
return apr
|
||||
}
|
||||
|
||||
var _ cli.CliContext = cliCtx{}
|
||||
|
||||
func BuildEmptyCliContext() cli.CliContext {
|
||||
return cliCtx{}
|
||||
}
|
||||
|
||||
+93
-3
@@ -18,12 +18,14 @@ import (
|
||||
"context"
|
||||
crand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
@@ -52,6 +54,7 @@ import (
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dfunctions"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
|
||||
"github.com/dolthub/dolt/go/libraries/events"
|
||||
"github.com/dolthub/dolt/go/libraries/utils/argparser"
|
||||
"github.com/dolthub/dolt/go/libraries/utils/filesys"
|
||||
"github.com/dolthub/dolt/go/store/nbs"
|
||||
"github.com/dolthub/dolt/go/store/util/tempfiles"
|
||||
@@ -63,7 +66,8 @@ const (
|
||||
|
||||
var dumpDocsCommand = &commands.DumpDocsCmd{}
|
||||
var dumpZshCommand = &commands.GenZshCompCmd{}
|
||||
var doltCommand = cli.NewSubCommandHandler("dolt", "it's git for data", []cli.Command{
|
||||
|
||||
var doltSubCommands = []cli.Command{
|
||||
commands.InitCmd{},
|
||||
commands.StatusCmd{},
|
||||
commands.AddCmd{},
|
||||
@@ -114,7 +118,10 @@ var doltCommand = cli.NewSubCommandHandler("dolt", "it's git for data", []cli.Co
|
||||
docscmds.Commands,
|
||||
stashcmds.StashCommands,
|
||||
&commands.Assist{},
|
||||
})
|
||||
}
|
||||
var doltCommand = cli.NewSubCommandHandler("dolt", "it's git for data", doltSubCommands)
|
||||
|
||||
var globalArgParser = buildGlobalArgs()
|
||||
|
||||
func init() {
|
||||
dumpDocsCommand.DoltCommand = doltCommand
|
||||
@@ -147,6 +154,11 @@ func main() {
|
||||
func runMain() int {
|
||||
args := os.Args[1:]
|
||||
|
||||
if len(args) == 0 {
|
||||
doltCommand.PrintUsage("dolt")
|
||||
return 1
|
||||
}
|
||||
|
||||
if os.Getenv("DOLT_VERBOSE_ASSERT_TABLE_FILES_CLOSED") == "" {
|
||||
nbs.TableIndexGCFinalizerWithStackTrace = false
|
||||
}
|
||||
@@ -401,9 +413,28 @@ func runMain() int {
|
||||
cli.Printf("error: failed to load persisted global variables: %s\n", err.Error())
|
||||
}
|
||||
|
||||
globalArgs, args, initCliContext, printUsage, err := splitArgsOnSubCommand(args)
|
||||
if printUsage {
|
||||
doltCommand.PrintUsage("dolt")
|
||||
return 0
|
||||
}
|
||||
if err != nil {
|
||||
cli.PrintErrln(color.RedString("Failure to parse arguments: %v", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
ctx, stop := context.WithCancel(ctx)
|
||||
res := doltCommand.Exec(ctx, "dolt", args, dEnv, nil)
|
||||
|
||||
var cliCtx cli.CliContext = nil
|
||||
if initCliContext {
|
||||
_, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString("dolt", doc, globalArgParser))
|
||||
apr := cli.ParseArgsOrDie(globalArgParser, globalArgs, usage)
|
||||
|
||||
cliCtx = tmpCliContext{globalArgs: apr}
|
||||
}
|
||||
|
||||
res := doltCommand.Exec(ctx, "dolt", args, dEnv, cliCtx)
|
||||
stop()
|
||||
|
||||
if err = dbfactory.CloseAllLocalDatabases(); err != nil {
|
||||
@@ -422,6 +453,56 @@ func runMain() int {
|
||||
return res
|
||||
}
|
||||
|
||||
// tmpCliContext is a temporary implementation of the CliContext interface. It is used to pass the global args to the
|
||||
// subcommands, will be replaced with implementations aware of query contexts shortly.
|
||||
type tmpCliContext struct {
|
||||
globalArgs *argparser.ArgParseResults
|
||||
}
|
||||
|
||||
func (t tmpCliContext) GlobalArgs() *argparser.ArgParseResults {
|
||||
return t.globalArgs
|
||||
}
|
||||
|
||||
var _ cli.CliContext = (*tmpCliContext)(nil)
|
||||
|
||||
// splitArgsOnSubCommand splits the args into two slices, the first containing all args before the first subcommand,
|
||||
// and the second containing all args after the first subcommand. The second slice will start with the subcommand name.
|
||||
func splitArgsOnSubCommand(args []string) (globalArgs, subArgs []string, initCliContext, printUsages bool, err error) {
|
||||
commandSet := make(map[string]bool)
|
||||
for _, cmd := range doltSubCommands {
|
||||
commandSet[cmd.Name()] = true
|
||||
}
|
||||
|
||||
for i, arg := range args {
|
||||
arg = strings.ToLower(arg)
|
||||
|
||||
if cli.IsHelp(arg) {
|
||||
// Found --help before any subcommand, so print dolt help.
|
||||
return nil, nil, false, true, nil
|
||||
}
|
||||
|
||||
if _, ok := commandSet[arg]; ok {
|
||||
// SQL is the first subcommand to support the CLIContext. We'll need a more general solution when we add more.
|
||||
// blame, table rm, and table mv commands also depend on the sql command, so they are also included here.
|
||||
initCliContext := "sql" == arg || "blame" == arg || "table" == arg
|
||||
return args[:i], args[i:], initCliContext, false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil, false, false, errors.New("No valid dolt subcommand found. See 'dolt --help' for usage.")
|
||||
}
|
||||
|
||||
// doc is currently used only when a `initCliContext` command is specified. This will include all commands in time,
|
||||
// otherwise you only see these docs if you specify a nonsense argument before the `sql` subcommand.
|
||||
var doc = cli.CommandDocumentationContent{
|
||||
ShortDesc: "Dolt is git for data",
|
||||
LongDesc: `Dolt comprises of multiple subcommands that allow users to import, export, update, and manipulate data with SQL.`,
|
||||
|
||||
Synopsis: []string{
|
||||
"<--data-dir=<path>> subcommand <subcommand arguments>",
|
||||
},
|
||||
}
|
||||
|
||||
func seedGlobalRand() {
|
||||
bs := make([]byte, 8)
|
||||
_, err := crand.Read(bs)
|
||||
@@ -466,3 +547,12 @@ func interceptSendMetrics(ctx context.Context, args []string) (bool, int) {
|
||||
dEnv := env.LoadWithoutDB(ctx, env.GetCurrentUserHomeDir, filesys.LocalFS, Version)
|
||||
return true, doltCommand.Exec(ctx, "dolt", args, dEnv, nil)
|
||||
}
|
||||
|
||||
func buildGlobalArgs() *argparser.ArgParser {
|
||||
ap := argparser.NewArgParserWithVariableArgs("dolt")
|
||||
|
||||
// Pulling this argument forward first to pave the way. Others will follow.
|
||||
ap.SupportsString(commands.DataDirFlag, "", "directory", "Defines a directory whose subdirectories should all be dolt data repositories accessible as independent databases within. Defaults to the current directory.")
|
||||
|
||||
return ap
|
||||
}
|
||||
|
||||
@@ -23,12 +23,13 @@
|
||||
package eventsapi
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
durationpb "google.golang.org/protobuf/types/known/durationpb"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -24,6 +24,7 @@ package eventsapi
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
package eventsapi
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -21,11 +21,12 @@
|
||||
package remotesapi
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -22,6 +22,7 @@ package remotesapi
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
|
||||
@@ -21,10 +21,11 @@
|
||||
package remotesapi
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -22,6 +22,7 @@ package remotesapi
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
|
||||
@@ -166,10 +166,6 @@ github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 h1:u3PMzfF8RkKd3lB9pZ2bfn0qEG+1G
|
||||
github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2/go.mod h1:mIEZOHnFx4ZMQeawhw9rhsj+0zwQj7adVsnBX7t+eKY=
|
||||
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-mysql-server v0.15.1-0.20230419220841-24dded8c61ef h1:V0FbcJR0nYgeDG1iYhrfy7SxkZsaYqlV8BRfzSkp/2A=
|
||||
github.com/dolthub/go-mysql-server v0.15.1-0.20230419220841-24dded8c61ef/go.mod h1:1/+9LW/gnZSVmXjc9DpEI+3thxjO/rSwasNw+9h3VvM=
|
||||
github.com/dolthub/go-mysql-server v0.15.1-0.20230424210837-b0555760f17e h1:677Wo/DrEn72agIX5nDSXqZ+isyF+GpFrlCJ4DOWfj8=
|
||||
github.com/dolthub/go-mysql-server v0.15.1-0.20230424210837-b0555760f17e/go.mod h1:B+vF4ambU4nWlkqEivILt0KJyvnIV22xFNWB9Xje948=
|
||||
github.com/dolthub/go-mysql-server v0.15.1-0.20230424215657-62cb35ac61a2 h1:jKtEfBQlcYknZSRpjFfo0V0sqybrWoCQTdmoVkAVdGY=
|
||||
github.com/dolthub/go-mysql-server v0.15.1-0.20230424215657-62cb35ac61a2/go.mod h1:B+vF4ambU4nWlkqEivILt0KJyvnIV22xFNWB9Xje948=
|
||||
github.com/dolthub/go-mysql-server v0.15.1-0.20230427234254-064b85c642f0 h1:xqy9NK/hqWvL4Hg6lMhAkSSEpykQWbev3uTEbDO1m6M=
|
||||
|
||||
@@ -19,10 +19,9 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/dolthub/dolt/go/store/prolly"
|
||||
|
||||
"github.com/dolthub/dolt/go/store/datas"
|
||||
"github.com/dolthub/dolt/go/store/hash"
|
||||
"github.com/dolthub/dolt/go/store/prolly"
|
||||
"github.com/dolthub/dolt/go/store/prolly/tree"
|
||||
"github.com/dolthub/dolt/go/store/types"
|
||||
)
|
||||
@@ -49,6 +48,7 @@ type Commit struct {
|
||||
|
||||
var _ Rootish = &Commit{}
|
||||
|
||||
// NewCommit generates a new Commit object that wraps a supplies datas.Commit.
|
||||
func NewCommit(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, commit *datas.Commit) (*Commit, error) {
|
||||
parents, err := datas.GetCommitParents(ctx, vrw, commit.NomsValue())
|
||||
if err != nil {
|
||||
@@ -57,11 +57,25 @@ func NewCommit(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore
|
||||
return &Commit{vrw, ns, parents, commit}, nil
|
||||
}
|
||||
|
||||
// NewCommitFromValue generates a new Commit object that wraps a supplied types.Value.
|
||||
func NewCommitFromValue(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, value types.Value) (*Commit, error) {
|
||||
commit, err := datas.CommitFromValue(vrw.Format(), value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewCommit(ctx, vrw, ns, commit)
|
||||
}
|
||||
|
||||
// HashOf returns the hash of the commit
|
||||
func (c *Commit) HashOf() (hash.Hash, error) {
|
||||
return c.dCommit.Addr(), nil
|
||||
}
|
||||
|
||||
// Value returns the types.Value that backs the commit.
|
||||
func (c *Commit) Value() types.Value {
|
||||
return c.dCommit.NomsValue()
|
||||
}
|
||||
|
||||
// GetCommitMeta gets the metadata associated with the commit
|
||||
func (c *Commit) GetCommitMeta(ctx context.Context) (*datas.CommitMeta, error) {
|
||||
return datas.GetCommitMeta(ctx, c.dCommit.NomsValue())
|
||||
|
||||
@@ -235,6 +235,7 @@ func (ddb *DoltDB) Close() error {
|
||||
return ddb.db.Close()
|
||||
}
|
||||
|
||||
// GetHashForRefStr resolves a ref string (such as a branch name or tag) and resolves it to a hash.Hash.
|
||||
func (ddb *DoltDB) GetHashForRefStr(ctx context.Context, ref string) (*hash.Hash, error) {
|
||||
if err := datas.ValidateDatasetId(ref); err != nil {
|
||||
return nil, fmt.Errorf("invalid ref format: %s", ref)
|
||||
@@ -275,15 +276,6 @@ func getCommitValForRefStr(ctx context.Context, ddb *DoltDB, ref string) (*datas
|
||||
return datas.LoadCommitAddr(ctx, ddb.vrw, *commitHash)
|
||||
}
|
||||
|
||||
func getCommitValForHash(ctx context.Context, vr types.ValueReader, c string) (*datas.Commit, error) {
|
||||
unprefixed := strings.TrimPrefix(c, "#")
|
||||
hash, ok := hash.MaybeParse(unprefixed)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid hash: " + c)
|
||||
}
|
||||
return datas.LoadCommitAddr(ctx, vr, hash)
|
||||
}
|
||||
|
||||
// Roots is a convenience struct to package up the three roots that most library functions will need to inspect and
|
||||
// modify the working set. This struct is designed to be passed by value always: functions should take a Roots as a
|
||||
// param and return a modified one.
|
||||
@@ -300,18 +292,14 @@ type Roots struct {
|
||||
Staged *RootValue
|
||||
}
|
||||
|
||||
// Resolve takes a CommitSpec and returns a Commit, or an error if the commit cannot be found.
|
||||
// If the CommitSpec is HEAD, Resolve also needs the DoltRef of the current working branch.
|
||||
func (ddb *DoltDB) Resolve(ctx context.Context, cs *CommitSpec, cwb ref.DoltRef) (*Commit, error) {
|
||||
if cs == nil {
|
||||
panic("nil commit spec")
|
||||
}
|
||||
|
||||
var commitVal *datas.Commit
|
||||
var err error
|
||||
func (ddb *DoltDB) getHashFromCommitSpec(ctx context.Context, cs *CommitSpec, cwb ref.DoltRef) (*hash.Hash, error) {
|
||||
switch cs.csType {
|
||||
case hashCommitSpec:
|
||||
commitVal, err = getCommitValForHash(ctx, ddb.vrw, cs.baseSpec)
|
||||
parsedHash, ok := hash.MaybeParse(cs.baseSpec)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid hash: " + cs.baseSpec)
|
||||
}
|
||||
return &parsedHash, nil
|
||||
case refCommitSpec:
|
||||
// For a ref in a CommitSpec, we have the following behavior.
|
||||
// If it starts with `refs/`, we look for an exact match before
|
||||
@@ -334,30 +322,43 @@ func (ddb *DoltDB) Resolve(ctx context.Context, cs *CommitSpec, cwb ref.DoltRef)
|
||||
}
|
||||
}
|
||||
for _, candidate := range candidates {
|
||||
commitVal, err = getCommitValForRefStr(ctx, ddb, candidate)
|
||||
valueHash, err := ddb.GetHashForRefStr(ctx, candidate)
|
||||
if err == nil {
|
||||
break
|
||||
return valueHash, nil
|
||||
}
|
||||
if err != ErrBranchNotFound {
|
||||
return nil, err
|
||||
} else {
|
||||
err = fmt.Errorf("%w: %s", ErrBranchNotFound, cs.baseSpec)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("%w: %s", ErrBranchNotFound, cs.baseSpec)
|
||||
case headCommitSpec:
|
||||
if cwb == nil {
|
||||
return nil, fmt.Errorf("cannot use a nil current working branch with a HEAD commit spec")
|
||||
}
|
||||
commitVal, err = getCommitValForRefStr(ctx, ddb, cwb.String())
|
||||
return ddb.GetHashForRefStr(ctx, cwb.String())
|
||||
default:
|
||||
panic("unrecognized commit spec csType: " + cs.csType)
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve takes a CommitSpec and returns a Commit, or an error if the commit cannot be found.
|
||||
// If the CommitSpec is HEAD, Resolve also needs the DoltRef of the current working branch.
|
||||
func (ddb *DoltDB) Resolve(ctx context.Context, cs *CommitSpec, cwb ref.DoltRef) (*Commit, error) {
|
||||
if cs == nil {
|
||||
panic("nil commit spec")
|
||||
}
|
||||
|
||||
hash, err := ddb.getHashFromCommitSpec(ctx, cs, cwb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commit, err := NewCommit(ctx, ddb.vrw, ddb.ns, commitVal)
|
||||
commitValue, err := datas.LoadCommitAddr(ctx, ddb.vrw, *hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commit, err := NewCommit(ctx, ddb.vrw, ddb.ns, commitValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ func (cmd fvCommand) exec(ctx context.Context, dEnv *env.DoltEnv) int {
|
||||
// execute the command using |cmd.user|'s Feature Version
|
||||
doltdb.DoltFeatureVersion = cmd.user.vers
|
||||
defer func() { doltdb.DoltFeatureVersion = DoltFeatureVersionCopy }()
|
||||
return cmd.cmd.Exec(ctx, cmd.cmd.Name(), cmd.args, dEnv, nil)
|
||||
return cmd.cmd.Exec(ctx, cmd.cmd.Name(), cmd.args, dEnv, commands.BuildEmptyCliContext())
|
||||
}
|
||||
|
||||
type fvUser struct {
|
||||
|
||||
@@ -37,6 +37,8 @@ func TestForeignKeys(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
var fkCliCtx = commands.BuildEmptyCliContext()
|
||||
|
||||
func TestForeignKeyErrors(t *testing.T) {
|
||||
skipNewFormat(t)
|
||||
cmds := []testCommand{
|
||||
@@ -49,13 +51,14 @@ func TestForeignKeyErrors(t *testing.T) {
|
||||
dEnv := dtestutils.CreateTestEnv()
|
||||
|
||||
for _, c := range cmds {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, nil)
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, fkCliCtx)
|
||||
require.Equal(t, 0, exitCode)
|
||||
}
|
||||
|
||||
exitCode := commands.SqlCmd{}.Exec(ctx, commands.SqlCmd{}.Name(), []string{"-q", `ALTER TABLE test MODIFY v1 INT;`}, dEnv, nil)
|
||||
exitCode := commands.SqlCmd{}.Exec(ctx, commands.SqlCmd{}.Name(), []string{"-q", `ALTER TABLE test MODIFY v1 INT;`}, dEnv, fkCliCtx)
|
||||
require.Equal(t, 1, exitCode)
|
||||
exitCode = commands.SqlCmd{}.Exec(ctx, commands.SqlCmd{}.Name(), []string{"-q", `ALTER TABLE test2 MODIFY v1 INT;`}, dEnv, nil)
|
||||
exitCode = commands.SqlCmd{}.Exec(ctx, commands.SqlCmd{}.Name(), []string{"-q", `ALTER TABLE test2 MODIFY v1 INT;`}, dEnv, fkCliCtx)
|
||||
|
||||
require.Equal(t, 1, exitCode)
|
||||
}
|
||||
|
||||
@@ -96,11 +99,11 @@ func testForeignKeys(t *testing.T, test foreignKeyTest) {
|
||||
dEnv := dtestutils.CreateTestEnv()
|
||||
|
||||
for _, c := range fkSetupCommon {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, nil)
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, fkCliCtx)
|
||||
require.Equal(t, 0, exitCode)
|
||||
}
|
||||
for _, c := range test.setup {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, nil)
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, fkCliCtx)
|
||||
require.Equal(t, 0, exitCode)
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,8 @@ type gcTest struct {
|
||||
postGCFunc func(ctx context.Context, t *testing.T, ddb *doltdb.DoltDB, prevRes interface{})
|
||||
}
|
||||
|
||||
var gcCliCtx = commands.BuildEmptyCliContext()
|
||||
|
||||
var gcTests = []gcTest{
|
||||
{
|
||||
name: "gc test",
|
||||
@@ -113,7 +115,7 @@ func testGarbageCollection(t *testing.T, test gcTest) {
|
||||
defer dEnv.DoltDB.Close()
|
||||
|
||||
for _, c := range gcSetupCommon {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, nil)
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, gcCliCtx)
|
||||
require.Equal(t, 0, exitCode)
|
||||
}
|
||||
|
||||
@@ -121,7 +123,7 @@ func testGarbageCollection(t *testing.T, test gcTest) {
|
||||
for _, stage := range test.stages {
|
||||
res = stage.preStageFunc(ctx, t, dEnv.DoltDB, res)
|
||||
for _, c := range stage.commands {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, nil)
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, gcCliCtx)
|
||||
require.Equal(t, 0, exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -54,6 +54,7 @@ type MultiRepoEnv struct {
|
||||
envs []NamedEnv
|
||||
fs filesys.Filesys
|
||||
cfg config.ReadWriteConfig
|
||||
dialProvider dbfactory.GRPCDialProvider
|
||||
ignoreLockFile bool
|
||||
}
|
||||
|
||||
@@ -72,6 +73,7 @@ func MultiEnvForDirectory(
|
||||
envs: make([]NamedEnv, 0),
|
||||
fs: fs,
|
||||
cfg: config,
|
||||
dialProvider: NewGRPCDialProviderFromDoltEnv(dEnv),
|
||||
ignoreLockFile: ignoreLockFile,
|
||||
}
|
||||
|
||||
@@ -192,6 +194,11 @@ func MultiEnvForPaths(
|
||||
} else if dEnv.CfgLoadErr != nil {
|
||||
return nil, fmt.Errorf("error loading environment '%s' at path '%s': %s", name, absPath, dEnv.CfgLoadErr.Error())
|
||||
}
|
||||
|
||||
if mrEnv.dialProvider == nil {
|
||||
mrEnv.dialProvider = NewGRPCDialProviderFromDoltEnv(dEnv)
|
||||
}
|
||||
|
||||
envSet[name] = dEnv
|
||||
}
|
||||
|
||||
@@ -208,10 +215,7 @@ func (mrEnv *MultiRepoEnv) FileSystem() filesys.Filesys {
|
||||
}
|
||||
|
||||
func (mrEnv *MultiRepoEnv) RemoteDialProvider() dbfactory.GRPCDialProvider {
|
||||
for _, env := range mrEnv.envs {
|
||||
return env.env
|
||||
}
|
||||
return NewGRPCDialProvider()
|
||||
return mrEnv.dialProvider
|
||||
}
|
||||
|
||||
func (mrEnv *MultiRepoEnv) Config() config.ReadWriteConfig {
|
||||
|
||||
@@ -116,7 +116,7 @@ func TestKeylessMerge(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, c := range test.setup {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, nil)
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, cmd.BuildEmptyCliContext())
|
||||
require.Equal(t, 0, exitCode)
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ func TestKeylessMergeConflicts(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, c := range cc {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, nil)
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, cmd.BuildEmptyCliContext())
|
||||
// allow merge to fail with conflicts
|
||||
if _, ok := c.cmd.(cmd.MergeCmd); !ok {
|
||||
require.Equal(t, 0, exitCode)
|
||||
|
||||
@@ -38,18 +38,6 @@ const (
|
||||
DeletedCheckCollision
|
||||
)
|
||||
|
||||
// todo: link to docs explaining how to resolve schema conflicts.
|
||||
func SchemaConflictErr(cc ...SchemaConflict) error {
|
||||
var sb strings.Builder
|
||||
sb.WriteString("merge aborted: schema conflict found for tables: \n")
|
||||
for i := range cc {
|
||||
sb.WriteRune('\t')
|
||||
sb.WriteString(cc[i].TableName)
|
||||
sb.WriteRune('\n')
|
||||
}
|
||||
return errors.New(sb.String())
|
||||
}
|
||||
|
||||
type SchemaConflict struct {
|
||||
TableName string
|
||||
ColConflicts []ColConflict
|
||||
@@ -63,21 +51,37 @@ func (sc SchemaConflict) Count() int {
|
||||
return len(sc.ColConflicts) + len(sc.IdxConflicts) + len(sc.ChkConflicts)
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer. This method is used to
|
||||
// display schema conflicts on schema conflict read paths.
|
||||
func (sc SchemaConflict) String() string {
|
||||
return strings.Join(sc.messages(), "\n")
|
||||
}
|
||||
|
||||
// Error implements error. This error will be returned to the
|
||||
// user if merge is configured to error upon schema conflicts.
|
||||
// todo: link to docs explaining how to resolve schema conflicts.
|
||||
func (sc SchemaConflict) Error() string {
|
||||
template := "merge aborted: schema conflict found for table %s \n" +
|
||||
" please resolve schema conflicts before merging: %s"
|
||||
var b strings.Builder
|
||||
b.WriteString("merge aborted: schema conflict found for table ")
|
||||
b.WriteString(sc.TableName)
|
||||
b.WriteString("\n please resolve schema conflicts before merging")
|
||||
for _, m := range sc.messages() {
|
||||
b.WriteString("\n\t")
|
||||
b.WriteString(m)
|
||||
}
|
||||
return fmt.Sprintf(template, sc.TableName, b.String())
|
||||
}
|
||||
|
||||
func (sc SchemaConflict) messages() (mm []string) {
|
||||
for _, c := range sc.ColConflicts {
|
||||
b.WriteString(fmt.Sprintf("\t%s\n", c.String()))
|
||||
mm = append(mm, c.String())
|
||||
}
|
||||
for _, c := range sc.IdxConflicts {
|
||||
b.WriteString(fmt.Sprintf("\t%s\n", c.String()))
|
||||
mm = append(mm, c.String())
|
||||
}
|
||||
for _, c := range sc.ChkConflicts {
|
||||
b.WriteString(fmt.Sprintf("\t%s\n", c.String()))
|
||||
mm = append(mm, c.String())
|
||||
}
|
||||
return b.String()
|
||||
return
|
||||
}
|
||||
|
||||
type ColConflict struct {
|
||||
@@ -88,7 +92,7 @@ type ColConflict struct {
|
||||
func (c ColConflict) String() string {
|
||||
switch c.Kind {
|
||||
case NameCollision:
|
||||
return fmt.Sprintf("two columns with the same name '%s' have different tags. See https://github.com/dolthub/dolt/issues/3963", c.Ours.Name)
|
||||
return fmt.Sprintf("incompatible column types for column '%s': %s and %s", c.Ours.Name, c.Ours.TypeInfo, c.Theirs.TypeInfo)
|
||||
case TagCollision:
|
||||
return fmt.Sprintf("different column definitions for our column %s and their column %s", c.Ours.Name, c.Theirs.Name)
|
||||
}
|
||||
@@ -148,7 +152,7 @@ func SchemaMerge(ctx context.Context, format *types.NomsBinFormat, ourSch, their
|
||||
}
|
||||
|
||||
var mergedCC *schema.ColCollection
|
||||
mergedCC, sc.ColConflicts, err = mergeColumns(ourSch.GetAllCols(), theirSch.GetAllCols(), ancSch.GetAllCols())
|
||||
mergedCC, sc.ColConflicts, err = mergeColumns(format, ourSch.GetAllCols(), theirSch.GetAllCols(), ancSch.GetAllCols())
|
||||
if err != nil {
|
||||
return nil, SchemaConflict{}, err
|
||||
}
|
||||
@@ -299,9 +303,11 @@ func ForeignKeysMerge(ctx context.Context, mergedRoot, ourRoot, theirRoot, ancRo
|
||||
// mergeColumns merges the columns from |ourCC|, |theirCC| into a single column collection, using the ancestor column
|
||||
// definitions in |ancCC| to determine on which side a column has changed. If merging is not possible because of
|
||||
// conflicting changes to the columns in |ourCC| and |theirCC|, then a set of ColConflict instances are returned
|
||||
// describing the conflicts. If any other, unexpected error occurs, then that error is returned and the other response
|
||||
// fields should be ignored.
|
||||
func mergeColumns(ourCC, theirCC, ancCC *schema.ColCollection) (*schema.ColCollection, []ColConflict, error) {
|
||||
// describing the conflicts. |format| indicates what storage format is in use, and is needed to determine compatibility
|
||||
// between types, since different storage formats have different restrictions on how much types can change and remain
|
||||
// compatible with the current stored format. If any unexpected error occurs, then that error is returned and the
|
||||
// other response fields should be ignored.
|
||||
func mergeColumns(format *types.NomsBinFormat, ourCC, theirCC, ancCC *schema.ColCollection) (*schema.ColCollection, []ColConflict, error) {
|
||||
columnMappings, err := mapColumns(ourCC, theirCC, ancCC)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -340,9 +346,25 @@ func mergeColumns(ourCC, theirCC, ancCC *schema.ColCollection) (*schema.ColColle
|
||||
if oursChanged && theirsChanged {
|
||||
// This is a schema change conflict and has already been handled by checkSchemaConflicts
|
||||
} else if theirsChanged {
|
||||
mergedColumns = append(mergedColumns, *theirs)
|
||||
if columnTypesAreCompatible(format, *ours, *theirs) {
|
||||
mergedColumns = append(mergedColumns, *theirs)
|
||||
} else {
|
||||
conflicts = append(conflicts, ColConflict{
|
||||
Kind: NameCollision,
|
||||
Ours: *ours,
|
||||
Theirs: *theirs,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
mergedColumns = append(mergedColumns, *ours)
|
||||
if columnTypesAreCompatible(format, *theirs, *ours) {
|
||||
mergedColumns = append(mergedColumns, *ours)
|
||||
} else {
|
||||
conflicts = append(conflicts, ColConflict{
|
||||
Kind: NameCollision,
|
||||
Ours: *ours,
|
||||
Theirs: *theirs,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if ours.Equals(*theirs) {
|
||||
// if the columns are identical, just use ours
|
||||
@@ -473,6 +495,34 @@ func checkSchemaConflicts(columnMappings columnMappings) ([]ColConflict, error)
|
||||
return conflicts, nil
|
||||
}
|
||||
|
||||
// columnTypesAreCompatible returns true if the change from |from| to |to| is a compatible type change.
|
||||
// Currently, no type change for the DOLT storage format is considered compatible, but over time we will
|
||||
// widen this to include safe type migrations (e.g. smallint to bigint, varchar(100) to varchar(200)), which
|
||||
// can require rewriting existing stored data to be compatible with the new type. For the older LD_1 storage
|
||||
// format, we are looser with type equality and consider them compatible as long as the types are in the
|
||||
// same type family/kind.
|
||||
func columnTypesAreCompatible(format *types.NomsBinFormat, from, to schema.Column) bool {
|
||||
if !from.TypeInfo.Equals(to.TypeInfo) {
|
||||
if types.IsFormat_DOLT(format) {
|
||||
// All type changes are incompatible, for the DOLT storage format.
|
||||
// TODO: this is overly broad, and should be narrowed down
|
||||
return false
|
||||
}
|
||||
|
||||
if from.Kind != to.Kind {
|
||||
return false
|
||||
}
|
||||
|
||||
if schema.IsColSpatialType(to) {
|
||||
// We need to do this because some spatial type changes require a full table check, but not all.
|
||||
// TODO: This could be narrowed down to a smaller set of spatial type changes
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// columnMapping describes the mapping for a column being merged between the two sides of the merge as well as the ancestor.
|
||||
type columnMapping struct {
|
||||
anc *schema.Column
|
||||
|
||||
@@ -40,7 +40,7 @@ type testCommand struct {
|
||||
}
|
||||
|
||||
func (tc testCommand) exec(t *testing.T, ctx context.Context, dEnv *env.DoltEnv) int {
|
||||
return tc.cmd.Exec(ctx, tc.cmd.Name(), tc.args, dEnv, nil)
|
||||
return tc.cmd.Exec(ctx, tc.cmd.Name(), tc.args, dEnv, commands.BuildEmptyCliContext())
|
||||
}
|
||||
|
||||
type args []string
|
||||
|
||||
@@ -173,7 +173,7 @@ func setupMigrationTest(t *testing.T, ctx context.Context, test migrationTest) *
|
||||
|
||||
cmd := commands.SqlCmd{}
|
||||
for _, query := range test.setup {
|
||||
code := cmd.Exec(ctx, cmd.Name(), []string{"-q", query}, dEnv, nil)
|
||||
code := cmd.Exec(ctx, cmd.Name(), []string{"-q", query}, dEnv, commands.BuildEmptyCliContext())
|
||||
require.Equal(t, 0, code)
|
||||
}
|
||||
return dEnv
|
||||
|
||||
@@ -55,6 +55,8 @@ type testAssertion struct {
|
||||
rows []sql.Row
|
||||
}
|
||||
|
||||
var cliCtx = cmd.BuildEmptyCliContext()
|
||||
|
||||
var setupCommon = []testCommand{
|
||||
{cmd.SqlCmd{}, args{"-q",
|
||||
`create table test (
|
||||
@@ -203,7 +205,7 @@ func setupFilterBranchTests(t *testing.T) *env.DoltEnv {
|
||||
ctx := context.Background()
|
||||
dEnv := dtestutils.CreateTestEnv()
|
||||
for _, c := range setupCommon {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, nil)
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, cliCtx)
|
||||
require.Equal(t, 0, exitCode)
|
||||
}
|
||||
|
||||
@@ -215,13 +217,13 @@ func testFilterBranch(t *testing.T, test filterBranchTest) {
|
||||
dEnv := setupFilterBranchTests(t)
|
||||
defer dEnv.DoltDB.Close()
|
||||
for _, c := range test.setup {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, nil)
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, cliCtx)
|
||||
require.Equal(t, 0, exitCode)
|
||||
}
|
||||
|
||||
for _, a := range test.asserts {
|
||||
for _, c := range a.setup {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, nil)
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, cliCtx)
|
||||
require.Equal(t, 0, exitCode)
|
||||
}
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ func runTestSql(t *testing.T, ctx context.Context, setup []string) (*doltdb.Dolt
|
||||
dEnv := dtestutils.CreateTestEnv()
|
||||
cmd := commands.SqlCmd{}
|
||||
for _, query := range setup {
|
||||
code := cmd.Exec(ctx, cmd.Name(), []string{"-q", query}, dEnv, nil)
|
||||
code := cmd.Exec(ctx, cmd.Name(), []string{"-q", query}, dEnv, commands.BuildEmptyCliContext())
|
||||
require.Equal(t, 0, code)
|
||||
}
|
||||
root, err := dEnv.WorkingRoot(ctx)
|
||||
|
||||
@@ -24,9 +24,11 @@ import (
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/diff"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/merge"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
|
||||
noms "github.com/dolthub/dolt/go/store/types"
|
||||
)
|
||||
|
||||
var _ sql.Table = (*SchemaConflictsTable)(nil)
|
||||
@@ -148,8 +150,20 @@ type schemaConflict struct {
|
||||
description string
|
||||
}
|
||||
|
||||
func newSchemaConflict(ctx context.Context, table string, br *doltdb.RootValue, c doltdb.SchemaConflict) (schemaConflict, error) {
|
||||
base, err := getCreateTableStatementFromRoot(ctx, table, br)
|
||||
func newSchemaConflict(ctx context.Context, table string, baseRoot *doltdb.RootValue, c doltdb.SchemaConflict) (schemaConflict, error) {
|
||||
bs, err := baseRoot.GetAllSchemas(ctx)
|
||||
if err != nil {
|
||||
return schemaConflict{}, err
|
||||
}
|
||||
baseSch := bs[table]
|
||||
|
||||
fkc, err := baseRoot.GetForeignKeyCollection(ctx)
|
||||
if err != nil {
|
||||
return schemaConflict{}, err
|
||||
}
|
||||
baseFKs, _ := fkc.KeysForTable(table)
|
||||
|
||||
base, err := getCreateTableStatement(table, baseSch, baseFKs, bs)
|
||||
if err != nil {
|
||||
return schemaConflict{}, err
|
||||
}
|
||||
@@ -164,30 +178,20 @@ func newSchemaConflict(ctx context.Context, table string, br *doltdb.RootValue,
|
||||
return schemaConflict{}, err
|
||||
}
|
||||
|
||||
desc, err := getSchemaConflictDescription(ctx, table, baseSch, c.ToSch, c.FromSch)
|
||||
if err != nil {
|
||||
return schemaConflict{}, err
|
||||
}
|
||||
|
||||
return schemaConflict{
|
||||
table: table,
|
||||
baseSch: base,
|
||||
ourSch: ours,
|
||||
theirSch: theirs,
|
||||
table: table,
|
||||
baseSch: base,
|
||||
ourSch: ours,
|
||||
theirSch: theirs,
|
||||
description: desc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getCreateTableStatementFromRoot(ctx context.Context, table string, root *doltdb.RootValue) (string, error) {
|
||||
schemas, err := root.GetAllSchemas(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sch := schemas[table]
|
||||
|
||||
fkc, err := root.GetForeignKeyCollection(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fks, _ := fkc.KeysForTable(table)
|
||||
|
||||
return getCreateTableStatement(table, sch, fks, schemas)
|
||||
}
|
||||
|
||||
func getCreateTableStatement(table string, sch schema.Schema, fks []doltdb.ForeignKey, parents map[string]schema.Schema) (string, error) {
|
||||
pkSch, err := sqlutil.FromDoltSchema(table, sch)
|
||||
if err != nil {
|
||||
@@ -196,6 +200,15 @@ func getCreateTableStatement(table string, sch schema.Schema, fks []doltdb.Forei
|
||||
return diff.GenerateCreateTableStatement(table, sch, pkSch, fks, parents)
|
||||
}
|
||||
|
||||
func getSchemaConflictDescription(ctx context.Context, table string, base, ours, theirs schema.Schema) (string, error) {
|
||||
nbf := noms.Format_Default
|
||||
_, conflict, err := merge.SchemaMerge(ctx, nbf, ours, theirs, base, table)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return conflict.String(), nil
|
||||
}
|
||||
|
||||
type schemaConflictsIter struct {
|
||||
conflicts []schemaConflict
|
||||
baseSchemas map[string]schema.Schema
|
||||
|
||||
@@ -2906,7 +2906,7 @@ var SchemaConflictScripts = []queries.ScriptTest{
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `c0` varchar(20),\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `c0` datetime(6),\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `c0` int,\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
"",
|
||||
"different column definitions for our column c0 and their column c0",
|
||||
}},
|
||||
},
|
||||
{
|
||||
@@ -3721,6 +3721,8 @@ var DoltVerifyConstraintsTestScripts = []queries.ScriptTest{
|
||||
var errTmplNoAutomaticMerge = "table %s can't be automatically merged.\nTo merge this table, make the schema on the source and target branch equal."
|
||||
|
||||
var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
|
||||
|
||||
// Data conflicts during a merge with schema changes
|
||||
{
|
||||
Name: "data conflict",
|
||||
AncSetUpScript: []string{
|
||||
@@ -3760,51 +3762,8 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "unique constraint violation",
|
||||
AncSetUpScript: []string{
|
||||
"set autocommit = 0;",
|
||||
"CREATE table t (pk varchar(100) primary key, col1 int, col2 varchar(100), UNIQUE KEY unique1 (col2));",
|
||||
"INSERT into t values ('0', 0, '');",
|
||||
"alter table t add index idx1 (pk, col2);",
|
||||
},
|
||||
RightSetUpScript: []string{
|
||||
"alter table t drop column col1;",
|
||||
"INSERT into t (pk, col2) values ('10', 'same');",
|
||||
},
|
||||
LeftSetUpScript: []string{
|
||||
"INSERT into t values ('1', 10, 'same');",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "call dolt_merge('right');",
|
||||
Expected: []sql.Row{{0, 0x1}},
|
||||
},
|
||||
{
|
||||
Query: "select * from dolt_conflicts;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "select * from dolt_constraint_violations;",
|
||||
Expected: []sql.Row{{"t", uint(2)}},
|
||||
},
|
||||
{
|
||||
Query: "select violation_type, pk, col2, violation_info from dolt_constraint_violations_t;",
|
||||
Expected: []sql.Row{
|
||||
{uint(2), "1", "same", types.JSONDocument{Val: merge.UniqCVMeta{Columns: []string{"col2"}, Name: "unique1"}}},
|
||||
{uint(2), "10", "same", types.JSONDocument{Val: merge.UniqCVMeta{Columns: []string{"col2"}, Name: "unique1"}}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "select pk, col2 from t;",
|
||||
Expected: []sql.Row{
|
||||
{"0", ""},
|
||||
{"1", "same"},
|
||||
{"10", "same"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Basic column changes – adds/drops/renames/reorders
|
||||
{
|
||||
Name: "dropping columns",
|
||||
AncSetUpScript: []string{
|
||||
@@ -3877,8 +3836,6 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
|
||||
"alter table t add index idx4 (col1, col2);",
|
||||
"alter table t add index idx5 (col2, col1);",
|
||||
"alter table t add index idx6 (col2, pk, col1);",
|
||||
// TODO: This duplicate index causes a race condition in the merge code
|
||||
//"alter table t add index idx7 (col2);",
|
||||
},
|
||||
RightSetUpScript: []string{
|
||||
"alter table t rename column col1 to col11;",
|
||||
@@ -4034,6 +3991,8 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Constraint changes
|
||||
{
|
||||
Name: "removing a not-null constraint",
|
||||
AncSetUpScript: []string{
|
||||
@@ -4160,6 +4119,51 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "unique constraint violation",
|
||||
AncSetUpScript: []string{
|
||||
"set autocommit = 0;",
|
||||
"CREATE table t (pk varchar(100) primary key, col1 int, col2 varchar(100), UNIQUE KEY unique1 (col2));",
|
||||
"INSERT into t values ('0', 0, '');",
|
||||
"alter table t add index idx1 (pk, col2);",
|
||||
},
|
||||
RightSetUpScript: []string{
|
||||
"alter table t drop column col1;",
|
||||
"INSERT into t (pk, col2) values ('10', 'same');",
|
||||
},
|
||||
LeftSetUpScript: []string{
|
||||
"INSERT into t values ('1', 10, 'same');",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "call dolt_merge('right');",
|
||||
Expected: []sql.Row{{0, 0x1}},
|
||||
},
|
||||
{
|
||||
Query: "select * from dolt_conflicts;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "select * from dolt_constraint_violations;",
|
||||
Expected: []sql.Row{{"t", uint(2)}},
|
||||
},
|
||||
{
|
||||
Query: "select violation_type, pk, col2, violation_info from dolt_constraint_violations_t;",
|
||||
Expected: []sql.Row{
|
||||
{uint(2), "1", "same", types.JSONDocument{Val: merge.UniqCVMeta{Columns: []string{"col2"}, Name: "unique1"}}},
|
||||
{uint(2), "10", "same", types.JSONDocument{Val: merge.UniqCVMeta{Columns: []string{"col2"}, Name: "unique1"}}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "select pk, col2 from t;",
|
||||
Expected: []sql.Row{
|
||||
{"0", ""},
|
||||
{"1", "same"},
|
||||
{"10", "same"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "dropping a unique key",
|
||||
AncSetUpScript: []string{
|
||||
@@ -4187,7 +4191,85 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
|
||||
},
|
||||
},
|
||||
|
||||
// Schema conflict test cases
|
||||
// Schema conflicts
|
||||
{
|
||||
// Type widening - these changes move from smaller types to bigger types, so they are guaranteed to be safe.
|
||||
// TODO: We don't support automatically converting column types in merges yet, so currently these won't
|
||||
// automatically merge and instead return schema conflicts.
|
||||
Name: "type widening",
|
||||
AncSetUpScript: []string{
|
||||
"CREATE table t (pk int primary key, col1 enum('blue', 'green'), col2 float, col3 smallint, " +
|
||||
"col4 decimal(4,2), col5 varchar(10), col6 set('a', 'b'), col7 bit(1));",
|
||||
"INSERT into t values (1, 'blue', 1.0, 1, 0.1, 'one', 'a,b', 1);",
|
||||
"alter table t add index idx1 (col1);",
|
||||
},
|
||||
RightSetUpScript: []string{
|
||||
"alter table t modify column col1 enum('blue', 'green', 'red');",
|
||||
"alter table t modify column col2 double;",
|
||||
"alter table t modify column col3 bigint;",
|
||||
"alter table t modify column col4 decimal(8,4);",
|
||||
"alter table t modify column col5 varchar(20);",
|
||||
"alter table t modify column col6 set('a', 'b', 'c');",
|
||||
"alter table t modify column col7 bit(2);",
|
||||
"INSERT into t values (3, 'red', 3.0, 420, 0.001, 'three', 'a,b,c', 3);",
|
||||
},
|
||||
LeftSetUpScript: []string{
|
||||
"INSERT into t values (2, 'green', 2.0, 2, 0.2, 'two', 'a,b', 1);",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "call dolt_merge('right');",
|
||||
Expected: []sql.Row{{0, 0x1}},
|
||||
},
|
||||
{
|
||||
Query: "select table_name, our_schema, their_schema, base_schema from dolt_schema_conflicts;",
|
||||
Expected: []sql.Row{{"t",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` enum('blue','green'),\n `col2` float,\n `col3` smallint,\n `col4` decimal(4,2),\n `col5` varchar(10),\n `col6` set('a','b'),\n `col7` bit(1),\n PRIMARY KEY (`pk`),\n KEY `idx1` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` enum('blue','green','red'),\n `col2` double,\n `col3` bigint,\n `col4` decimal(8,4),\n `col5` varchar(20),\n `col6` set('a','b','c'),\n `col7` bit(2),\n PRIMARY KEY (`pk`),\n KEY `idx1` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` enum('blue','green'),\n `col2` float,\n `col3` smallint,\n `col4` decimal(4,2),\n `col5` varchar(10),\n `col6` set('a','b'),\n `col7` bit(1),\n PRIMARY KEY (`pk`),\n KEY `idx1` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// Type shortening – these changes move from a larger type to a smaller type and are not always safe.
|
||||
// For now, we automatically fail all of these with a schema conflict that the user must resolve, but in
|
||||
// theory, we could try to apply these changes and see if the data in the tables is compatible or not, but
|
||||
// that's an optimization left for the future. Until then, customers can manually alter their schema to
|
||||
// get merges to work, based on the schema conflict information.
|
||||
Name: "type shortening",
|
||||
AncSetUpScript: []string{
|
||||
"CREATE TABLE t (pk int primary key, col1 enum('blue','green','red'), col2 double, col3 bigint, col4 decimal(8,4), " +
|
||||
"col5 varchar(20), col6 set('a','b','c'), col7 bit(2));",
|
||||
"INSERT into t values (3, 'green', 3.0, 420, 0.001, 'three', 'a,b', 1);",
|
||||
"alter table t add index idx1 (col1);",
|
||||
},
|
||||
RightSetUpScript: []string{
|
||||
"alter table t modify column col1 enum('blue', 'green');",
|
||||
"alter table t modify column col2 float;",
|
||||
"alter table t modify column col3 smallint;",
|
||||
"alter table t modify column col4 decimal(4,2);",
|
||||
"alter table t modify column col5 varchar(10);",
|
||||
"alter table t modify column col6 set('a', 'b');",
|
||||
"alter table t modify column col7 bit(1);",
|
||||
"INSERT into t values (1, 'blue', 1.0, 1, 0.1, 'one', 'a,b', 1);",
|
||||
},
|
||||
LeftSetUpScript: []string{
|
||||
"INSERT into t values (2, 'green', 2.0, 2, 0.2, 'two', 'a,b', 1);",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "call dolt_merge('right');",
|
||||
Expected: []sql.Row{{0, 0x1}},
|
||||
},
|
||||
{
|
||||
Query: "select table_name, our_schema, their_schema, base_schema from dolt_schema_conflicts;",
|
||||
Expected: []sql.Row{{"t",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` enum('blue','green','red'),\n `col2` double,\n `col3` bigint,\n `col4` decimal(8,4),\n `col5` varchar(20),\n `col6` set('a','b','c'),\n `col7` bit(2),\n PRIMARY KEY (`pk`),\n KEY `idx1` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` enum('blue','green'),\n `col2` float,\n `col3` smallint,\n `col4` decimal(4,2),\n `col5` varchar(10),\n `col6` set('a','b'),\n `col7` bit(1),\n PRIMARY KEY (`pk`),\n KEY `idx1` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` enum('blue','green','red'),\n `col2` double,\n `col3` bigint,\n `col4` decimal(8,4),\n `col5` varchar(20),\n `col6` set('a','b','c'),\n `col7` bit(2),\n PRIMARY KEY (`pk`),\n KEY `idx1` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "index conflicts: both sides add an index with the same name, same columns, but different type",
|
||||
AncSetUpScript: []string{
|
||||
@@ -4216,7 +4298,6 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// https://github.com/dolthub/dolt/issues/2973
|
||||
Name: "modifying a column on one side of a merge, and deleting it on the other",
|
||||
@@ -4262,15 +4343,69 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Smarter merge conflict detection
|
||||
{
|
||||
// This merge tests reports a conflict on pk=1, because the tuple value is different on the left side, right
|
||||
Name: "changing the type of a column",
|
||||
AncSetUpScript: []string{
|
||||
"create table t (pk int primary key, col1 int);",
|
||||
"insert into t values (1, 10), (2, 20);",
|
||||
},
|
||||
RightSetUpScript: []string{
|
||||
"alter table t modify column col1 varchar(100)",
|
||||
"insert into t values (3, 'thirty'), (4, 'forty')",
|
||||
},
|
||||
LeftSetUpScript: []string{
|
||||
"insert into t values (5, 50), (6, 60);",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "call dolt_merge('right');",
|
||||
Expected: []sql.Row{{0, 1}},
|
||||
},
|
||||
{
|
||||
Query: "select table_name, our_schema, their_schema, base_schema from dolt_schema_conflicts;",
|
||||
Expected: []sql.Row{{"t",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` int,\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` varchar(100),\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` int,\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "changing the type of a column with an index",
|
||||
AncSetUpScript: []string{
|
||||
"create table t (pk int primary key, col1 int, INDEX col1_idx (col1));",
|
||||
"insert into t values (1, 100), (2, 20);",
|
||||
},
|
||||
RightSetUpScript: []string{
|
||||
"alter table t modify column col1 varchar(100);",
|
||||
"insert into t values (3, 'thirty'), (4, 'forty')",
|
||||
},
|
||||
LeftSetUpScript: []string{
|
||||
"insert into t values (5, 50), (6, 60);",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "call dolt_merge('right');",
|
||||
Expected: []sql.Row{{0, 1}},
|
||||
},
|
||||
{
|
||||
Query: "select table_name, our_schema, their_schema, base_schema from dolt_schema_conflicts;",
|
||||
Expected: []sql.Row{{"t",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` int,\n PRIMARY KEY (`pk`),\n KEY `col1_idx` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` varchar(100),\n PRIMARY KEY (`pk`),\n KEY `col1_idx` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` int,\n PRIMARY KEY (`pk`),\n KEY `col1_idx` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Unsupported automatic merge cases
|
||||
{
|
||||
// This merge test reports a conflict on pk=1, because the tuple value is different on the left side, right
|
||||
// side, and base. The value is the base is (10, '100'), on the right is nil, and on the left is ('100'),
|
||||
// because the data migration for the schema change happens before the diff iterator is invoked.
|
||||
// This should NOT be a conflict for a user – Dolt should not conflate the schema merge data migration with
|
||||
// a real data conflict created by a user. Allowing this is still better than completely blocking all schema
|
||||
// merges though, so we can live with this while we continue iterating and fine tuning schema merge logic.
|
||||
// merges though, so we can live with this while we continue iterating and fine-tuning schema merge logic.
|
||||
Name: "schema change combined with drop row",
|
||||
AncSetUpScript: []string{
|
||||
"SET autocommit = 0",
|
||||
@@ -4304,10 +4439,8 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Unsupported automatic merge cases
|
||||
{
|
||||
Name: "adding a non-null column to one side",
|
||||
Name: "adding a non-null column with a default value to one side",
|
||||
AncSetUpScript: []string{
|
||||
"create table t (pk int primary key, col1 int);",
|
||||
"insert into t values (1, 1);",
|
||||
@@ -4334,71 +4467,7 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "changing the type of a column",
|
||||
AncSetUpScript: []string{
|
||||
"create table t (pk int primary key, col1 int);",
|
||||
"insert into t values (1, 10), (2, 20);",
|
||||
},
|
||||
RightSetUpScript: []string{
|
||||
"alter table t modify column col1 varchar(100)",
|
||||
"insert into t values (3, 'thirty'), (4, 'forty')",
|
||||
},
|
||||
LeftSetUpScript: []string{
|
||||
"insert into t values (5, 50), (6, 60);",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "call dolt_merge('right');",
|
||||
ExpectedErrStr: fmt.Sprintf(errTmplNoAutomaticMerge, "t"),
|
||||
},
|
||||
{
|
||||
Query: "select pk, col1 from t;",
|
||||
Expected: []sql.Row{
|
||||
{1, "10"},
|
||||
{2, "20"},
|
||||
{3, "thirty"},
|
||||
{4, "forty"},
|
||||
{5, "50"},
|
||||
{6, "60"},
|
||||
},
|
||||
Skip: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "changing the type of a column with an index",
|
||||
AncSetUpScript: []string{
|
||||
"create table t (pk int primary key, col1 int, INDEX col1_idx (col1));",
|
||||
"insert into t values (1, 100), (2, 20);",
|
||||
},
|
||||
RightSetUpScript: []string{
|
||||
"alter table t modify column col1 varchar(100);",
|
||||
"insert into t values (3, 'thirty'), (4, 'forty')",
|
||||
},
|
||||
LeftSetUpScript: []string{
|
||||
"insert into t values (5, 50), (6, 60);",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "call dolt_merge('right');",
|
||||
ExpectedErrStr: fmt.Sprintf(errTmplNoAutomaticMerge, "t"),
|
||||
},
|
||||
{
|
||||
Skip: true,
|
||||
Query: "select pk, col1 from t order by col1;",
|
||||
Expected: []sql.Row{
|
||||
{1, "100"},
|
||||
{2, "20"},
|
||||
{3, "thirty"},
|
||||
{4, "forty"},
|
||||
{5, "50"},
|
||||
{6, "60"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "adding a not-null constraint with default to a column",
|
||||
Name: "adding a not-null constraint and default value to a column",
|
||||
AncSetUpScript: []string{
|
||||
"create table t (pk int primary key, col1 int);",
|
||||
"insert into t values (1, null), (2, null);",
|
||||
@@ -4451,9 +4520,9 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
|
||||
},
|
||||
},
|
||||
{
|
||||
// It would make sense if we table-scanned for check constraints during merge
|
||||
// and flagged failing constraints as violations in `dolt_constraint_violations`.
|
||||
Name: "adding a check-constraint should abort the merge.",
|
||||
// TODO: We should scan for check constraints during merge and flag failing
|
||||
// constraints as violations in `dolt_constraint_violations`.
|
||||
Name: "adding a check-constraint",
|
||||
AncSetUpScript: []string{
|
||||
"create table t (pk int primary key, col1 int);",
|
||||
"insert into t values (1, 1);",
|
||||
@@ -4467,14 +4536,18 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Skip: true,
|
||||
Query: "call dolt_merge('right');",
|
||||
ExpectedErrStr: "some schema error",
|
||||
// TODO: Dolt currently merges this without an error, but it shouldn't;
|
||||
// There is a constraint violation that should be reported.
|
||||
Skip: true,
|
||||
Query: "call dolt_merge('right');",
|
||||
Expected: []sql.Row{{0, 0x1}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "changing the collation of an indexed column is broken",
|
||||
// TODO: Changing a column's collation requires rewriting the table and any indexes containing that column.
|
||||
// For now, we just detect the schema incompatibility and return schema conflict metadata.
|
||||
Name: "changing the collation of an indexed column",
|
||||
AncSetUpScript: []string{
|
||||
"create table t (pk int primary key, col1 varchar(32) character set utf8mb4 collate utf8mb4_bin, index col1_idx (col1));",
|
||||
"insert into t values (1, 'ab'), (2, 'Ab');",
|
||||
@@ -4486,10 +4559,16 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
|
||||
"insert into t values (3, 'c');",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
// TODO: Fails secondary index validation. Changing the ordinal ordering of secondary indexes definitely breaks merge
|
||||
{
|
||||
Query: "call dolt_merge('right');",
|
||||
ExpectedErrStr: fmt.Sprintf(errTmplNoAutomaticMerge, "t"),
|
||||
Query: "call dolt_merge('right');",
|
||||
Expected: []sql.Row{{0, 1}},
|
||||
},
|
||||
{
|
||||
Query: "select table_name, our_schema, their_schema, base_schema from dolt_schema_conflicts;",
|
||||
Expected: []sql.Row{{"t",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` varchar(32) COLLATE utf8mb4_bin,\n PRIMARY KEY (`pk`),\n KEY `col1_idx` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` varchar(32) COLLATE utf8mb4_general_ci,\n PRIMARY KEY (`pk`),\n KEY `col1_idx` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` varchar(32) COLLATE utf8mb4_bin,\n PRIMARY KEY (`pk`),\n KEY `col1_idx` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -154,7 +154,7 @@ func TestDbRevision(t *testing.T) {
|
||||
|
||||
setup := append(setupCommon, test.setup...)
|
||||
for _, c := range setup {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, nil)
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, cmd.BuildEmptyCliContext())
|
||||
require.Equal(t, 0, exitCode)
|
||||
}
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ func setupHistoryTests(t *testing.T) *env.DoltEnv {
|
||||
ctx := context.Background()
|
||||
dEnv := dtestutils.CreateTestEnv()
|
||||
for _, c := range setupCommon {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, nil)
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, cmd.BuildEmptyCliContext())
|
||||
require.Equal(t, 0, exitCode)
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ func setupHistoryTests(t *testing.T) *env.DoltEnv {
|
||||
func testHistoryTable(t *testing.T, test historyTableTest, dEnv *env.DoltEnv) {
|
||||
ctx := context.Background()
|
||||
for _, c := range test.setup {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, nil)
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, cmd.BuildEmptyCliContext())
|
||||
require.Equal(t, 0, exitCode)
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ func testJsonValue(t *testing.T, test jsonValueTest, setupCommon []testCommand)
|
||||
|
||||
setup := append(setupCommon, test.setup...)
|
||||
for _, c := range setup {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, nil)
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv, cmd.BuildEmptyCliContext())
|
||||
require.Equal(t, 0, exitCode)
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ func populateRepo(dEnv *env.DoltEnv, insertData string) {
|
||||
execSql := func(dEnv *env.DoltEnv, q string) int {
|
||||
ctx := context.Background()
|
||||
args := []string{"-r", "null", "-q", q}
|
||||
return commands.SqlCmd{}.Exec(ctx, "sql", args, dEnv, nil)
|
||||
return commands.SqlCmd{}.Exec(ctx, "sql", args, dEnv, commands.BuildEmptyCliContext())
|
||||
}
|
||||
execSql(dEnv, createTable)
|
||||
execSql(dEnv, insertData)
|
||||
|
||||
@@ -160,7 +160,7 @@ func mustList(l types.List, err error) types.List {
|
||||
|
||||
func validate(ctx context.Context, nbf *types.NomsBinFormat, r types.Value) bool {
|
||||
rootType := mustType(types.MakeMapType(types.PrimitiveTypeMap[types.StringKind], mustType(types.MakeRefType(types.PrimitiveTypeMap[types.ValueKind]))))
|
||||
if isSub, err := types.IsValueSubtypeOf(ctx, nbf, r, rootType); err != nil {
|
||||
if isSub, err := types.IsValueSubtypeOf(nbf, r, rootType); err != nil {
|
||||
panic(err)
|
||||
} else if !isSub {
|
||||
fmt.Fprintf(os.Stderr, "Root of database must be %s, but you specified: %s\n", mustString(rootType.Describe(ctx)), mustString(mustType(types.TypeOf(r)).Describe(ctx)))
|
||||
|
||||
@@ -196,8 +196,8 @@ func outputEncodedValue(ctx context.Context, w io.Writer, value types.Value) err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(w, "\tPrimary Index (rows %d, depth %d) %s {",
|
||||
c, node.Level()+1, node.HashOf().String()[:8])
|
||||
fmt.Fprintf(w, "\tPrimary Index (rows %d, depth %d) #%s {",
|
||||
c, node.Level()+1, node.HashOf().String())
|
||||
tree.OutputProllyNode(w, node)
|
||||
fmt.Fprintf(w, "\t}\n")
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ const (
|
||||
)
|
||||
|
||||
var ErrCommitNotFound = errors.New("target commit not found")
|
||||
var ErrNotACommit = errors.New("value is not a commit")
|
||||
|
||||
type Commit struct {
|
||||
val types.Value
|
||||
@@ -303,7 +304,15 @@ func commitPtr(nbf *types.NomsBinFormat, v types.Value, r *types.Ref) (*Commit,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func commitFromValue(nbf *types.NomsBinFormat, v types.Value) (*Commit, error) {
|
||||
// CommitFromValue deserializes a types.Value into a Commit.
|
||||
func CommitFromValue(nbf *types.NomsBinFormat, v types.Value) (*Commit, error) {
|
||||
isCommit, err := IsCommit(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isCommit {
|
||||
return nil, ErrNotACommit
|
||||
}
|
||||
return commitPtr(nbf, v, nil)
|
||||
}
|
||||
|
||||
@@ -326,7 +335,7 @@ func LoadCommitAddr(ctx context.Context, vr types.ValueReader, addr hash.Hash) (
|
||||
if v == nil {
|
||||
return nil, errors.New("target commit not found")
|
||||
}
|
||||
return commitFromValue(vr.Format(), v)
|
||||
return CommitFromValue(vr.Format(), v)
|
||||
}
|
||||
|
||||
func findCommonAncestorUsingParentsList(ctx context.Context, c1, c2 *Commit, vr1, vr2 types.ValueReader, ns1, ns2 tree.NodeStore) (hash.Hash, bool, error) {
|
||||
@@ -695,9 +704,9 @@ func firstError(l, r error) error {
|
||||
return r
|
||||
}
|
||||
|
||||
func IsCommit(ctx context.Context, v types.Value) (bool, error) {
|
||||
func IsCommit(v types.Value) (bool, error) {
|
||||
if s, ok := v.(types.Struct); ok {
|
||||
return types.IsValueSubtypeOf(ctx, s.Format(), v, valueCommitType)
|
||||
return types.IsValueSubtypeOf(s.Format(), v, valueCommitType)
|
||||
} else if sm, ok := v.(types.SerialMessage); ok {
|
||||
data := []byte(sm)
|
||||
return serial.GetFileID(data) == serial.CommitFileID, nil
|
||||
|
||||
@@ -257,14 +257,14 @@ func TestCommitWithoutMetaField(t *testing.T) {
|
||||
"meta": types.EmptyStruct(db.Format()),
|
||||
})
|
||||
assert.NoError(err)
|
||||
assert.True(IsCommit(ctx, metaCommit))
|
||||
assert.True(IsCommit(metaCommit))
|
||||
|
||||
noMetaCommit, err := types.NewStruct(db.Format(), "Commit", types.StructData{
|
||||
"value": types.Float(9),
|
||||
"parents": mustSet(types.NewSet(ctx, db)),
|
||||
})
|
||||
assert.NoError(err)
|
||||
assert.False(IsCommit(ctx, noMetaCommit))
|
||||
assert.False(IsCommit(noMetaCommit))
|
||||
}
|
||||
|
||||
func mustCommitToTargetHashes(vrw types.ValueReadWriter, commits ...types.Value) []hash.Hash {
|
||||
@@ -638,9 +638,9 @@ func TestFindCommonAncestor(t *testing.T) {
|
||||
|
||||
assertCommonAncestor(t, a6, a9, ra9, db, rdb, "common third parent")
|
||||
|
||||
a9c, err := commitFromValue(db.Format(), a9)
|
||||
a9c, err := CommitFromValue(db.Format(), a9)
|
||||
require.NoError(t, err)
|
||||
ra9c, err := commitFromValue(rdb.Format(), ra9)
|
||||
ra9c, err := CommitFromValue(rdb.Format(), ra9)
|
||||
require.NoError(t, err)
|
||||
_, _, err = FindCommonAncestor(context.Background(), ra9c, a9c, db, rdb, db.ns, rdb.ns)
|
||||
assert.Error(err)
|
||||
|
||||
@@ -268,7 +268,7 @@ func (db *database) doSetHead(ctx context.Context, ds Dataset, addr hash.Hash) e
|
||||
headType := newHead.TypeName()
|
||||
switch headType {
|
||||
case commitName:
|
||||
iscommit, err := IsCommit(ctx, newVal)
|
||||
iscommit, err := IsCommit(newVal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -291,7 +291,7 @@ func (db *database) doSetHead(ctx context.Context, ds Dataset, addr hash.Hash) e
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iscommit, err := IsCommit(ctx, commitval)
|
||||
iscommit, err := IsCommit(commitval)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -376,7 +376,7 @@ func (db *database) doFastForward(ctx context.Context, ds Dataset, newHeadAddr h
|
||||
}
|
||||
|
||||
v := newHead.value()
|
||||
iscommit, err := IsCommit(ctx, v)
|
||||
iscommit, err := IsCommit(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -384,7 +384,7 @@ func (db *database) doFastForward(ctx context.Context, ds Dataset, newHeadAddr h
|
||||
return fmt.Errorf("FastForward: target value of new head address %v is not a commit.", newHeadAddr)
|
||||
}
|
||||
|
||||
newCommit, err := commitFromValue(db.Format(), v)
|
||||
newCommit, err := CommitFromValue(db.Format(), v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -392,7 +392,7 @@ func (db *database) doFastForward(ctx context.Context, ds Dataset, newHeadAddr h
|
||||
currentHeadAddr, ok := ds.MaybeHeadAddr()
|
||||
if ok {
|
||||
currentHeadValue, _ := ds.MaybeHead()
|
||||
currCommit, err := commitFromValue(db.Format(), currentHeadValue)
|
||||
currCommit, err := CommitFromValue(db.Format(), currentHeadValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -905,7 +905,7 @@ func (db *database) validateRefAsCommit(ctx context.Context, r types.Ref) (types
|
||||
var v types.Value
|
||||
v = rHead.(nomsHead).st
|
||||
|
||||
is, err := IsCommit(ctx, v)
|
||||
is, err := IsCommit(v)
|
||||
|
||||
if err != nil {
|
||||
return types.Struct{}, err
|
||||
|
||||
@@ -220,7 +220,7 @@ func (suite *DatabaseSuite) TestDatabaseCommit() {
|
||||
suite.NoError(err)
|
||||
suite.True(ok)
|
||||
suite.True(h.Equals(a))
|
||||
comm, err := commitFromValue(suite.db.Format(), mustHead(ds2))
|
||||
comm, err := CommitFromValue(suite.db.Format(), mustHead(ds2))
|
||||
suite.NoError(err)
|
||||
suite.Equal(uint64(1), comm.Height())
|
||||
|
||||
@@ -232,7 +232,7 @@ func (suite *DatabaseSuite) TestDatabaseCommit() {
|
||||
ds, err = CommitValue(context.Background(), suite.db, ds, b)
|
||||
suite.NoError(err)
|
||||
suite.True(mustHeadValue(ds).Equals(b))
|
||||
comm, err = commitFromValue(suite.db.Format(), mustHead(ds))
|
||||
comm, err = CommitFromValue(suite.db.Format(), mustHead(ds))
|
||||
suite.NoError(err)
|
||||
suite.Equal(uint64(2), comm.Height())
|
||||
|
||||
@@ -249,7 +249,7 @@ func (suite *DatabaseSuite) TestDatabaseCommit() {
|
||||
ds, err = CommitValue(context.Background(), suite.db, ds, d)
|
||||
suite.NoError(err)
|
||||
suite.True(mustHeadValue(ds).Equals(d))
|
||||
comm, err = commitFromValue(suite.db.Format(), mustHead(ds))
|
||||
comm, err = CommitFromValue(suite.db.Format(), mustHead(ds))
|
||||
suite.NoError(err)
|
||||
suite.Equal(uint64(3), comm.Height())
|
||||
|
||||
@@ -336,7 +336,7 @@ func assertMapOfStringToRefOfCommit(ctx context.Context, proposed, datasets type
|
||||
}
|
||||
if targetValue, err := ref.TargetValue(ctx, vr); err != nil {
|
||||
d.PanicIfError(err)
|
||||
} else if is, err := IsCommit(ctx, targetValue); err != nil {
|
||||
} else if is, err := IsCommit(targetValue); err != nil {
|
||||
d.PanicIfError(err)
|
||||
} else if !is {
|
||||
d.Panic("Root of a Database must be a Map<String, Ref<Commit>>, but the ref at key %s points to a %s", change.Key.(types.String), mustString(mustType(types.TypeOf(targetValue)).Describe(ctx)))
|
||||
|
||||
@@ -228,7 +228,7 @@ func (ms *MergeState) FromCommit(ctx context.Context, vr types.ValueReader) (*Co
|
||||
return nil, fmt.Errorf("corrupted MergeState struct")
|
||||
}
|
||||
|
||||
return commitFromValue(vr.Format(), commitV)
|
||||
return CommitFromValue(vr.Format(), commitV)
|
||||
}
|
||||
|
||||
func (ms *MergeState) FromCommitSpec(ctx context.Context, vr types.ValueReader) (string, error) {
|
||||
@@ -504,7 +504,7 @@ func newHead(ctx context.Context, head types.Value, addr hash.Hash) (dsHead, err
|
||||
}
|
||||
}
|
||||
|
||||
matched, err := IsCommit(ctx, head)
|
||||
matched, err := IsCommit(head)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ func NewStash(ctx context.Context, nbf *types.NomsBinFormat, vrw types.ValueRead
|
||||
return hash.Hash{}, types.Ref{}, err
|
||||
}
|
||||
|
||||
isCommit, err := IsCommit(ctx, headCommit)
|
||||
isCommit, err := IsCommit(headCommit)
|
||||
if err != nil {
|
||||
return hash.Hash{}, types.Ref{}, err
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ func newTag(ctx context.Context, db *database, commitAddr hash.Hash, meta *TagMe
|
||||
if err != nil {
|
||||
return hash.Hash{}, types.Ref{}, err
|
||||
}
|
||||
iscommit, err := IsCommit(ctx, commitSt)
|
||||
iscommit, err := IsCommit(commitSt)
|
||||
if err != nil {
|
||||
return hash.Hash{}, types.Ref{}, err
|
||||
}
|
||||
@@ -134,7 +134,7 @@ func tag_flatbuffer(commitAddr hash.Hash, meta *TagMeta) serial.Message {
|
||||
|
||||
func IsTag(ctx context.Context, v types.Value) (bool, error) {
|
||||
if s, ok := v.(types.Struct); ok {
|
||||
return types.IsValueSubtypeOf(ctx, s.Format(), v, valueTagType)
|
||||
return types.IsValueSubtypeOf(s.Format(), v, valueTagType)
|
||||
} else if sm, ok := v.(types.SerialMessage); ok {
|
||||
data := []byte(sm)
|
||||
return serial.GetFileID(data) == serial.TagFileID, nil
|
||||
|
||||
@@ -174,7 +174,7 @@ func (ml mapLeafSequence) walkRefs(nbf *NomsBinFormat, cb RefCallback) error {
|
||||
return walkRefs(w.buff[:w.offset], ml.format(), cb)
|
||||
}
|
||||
|
||||
func (ml mapLeafSequence) entries(ctx context.Context) (mapEntrySlice, error) {
|
||||
func (ml mapLeafSequence) entries() (mapEntrySlice, error) {
|
||||
dec, count := ml.decoderSkipToValues()
|
||||
entries := mapEntrySlice{
|
||||
make([]mapEntry, count),
|
||||
|
||||
@@ -491,7 +491,7 @@ func (ms metaSequence) getCompositeChildSequence(ctx context.Context, start uint
|
||||
var valueItems []mapEntry
|
||||
|
||||
for _, seq := range output {
|
||||
entries, err := seq.(mapLeafSequence).entries(ctx)
|
||||
entries, err := seq.(mapLeafSequence).entries()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -53,6 +53,15 @@ func (sm SerialMessage) Hash(nbf *NomsBinFormat) (hash.Hash, error) {
|
||||
}
|
||||
|
||||
func (sm SerialMessage) HumanReadableString() string {
|
||||
return sm.humanReadableStringAtIndentationLevel(0)
|
||||
}
|
||||
|
||||
func printWithIndendationLevel(level int, builder *strings.Builder, format string, a ...any) {
|
||||
fmt.Fprintf(builder, strings.Repeat("\t", level))
|
||||
fmt.Fprintf(builder, format, a...)
|
||||
}
|
||||
|
||||
func (sm SerialMessage) humanReadableStringAtIndentationLevel(level int) string {
|
||||
id := serial.GetFileID(sm)
|
||||
switch id {
|
||||
// NOTE: splunk uses a separate path for some printing
|
||||
@@ -60,101 +69,101 @@ func (sm SerialMessage) HumanReadableString() string {
|
||||
msg := serial.GetRootAsStoreRoot([]byte(sm), serial.MessagePrefixSz)
|
||||
ret := &strings.Builder{}
|
||||
mapbytes := msg.AddressMapBytes()
|
||||
fmt.Fprintf(ret, "StoreRoot{%s}", SerialMessage(mapbytes).HumanReadableString())
|
||||
printWithIndendationLevel(level, ret, "StoreRoot{%s}",
|
||||
SerialMessage(mapbytes).humanReadableStringAtIndentationLevel(level+1))
|
||||
return ret.String()
|
||||
case serial.StashListFileID:
|
||||
msg := serial.GetRootAsStashList([]byte(sm), serial.MessagePrefixSz)
|
||||
ret := &strings.Builder{}
|
||||
mapbytes := msg.AddressMapBytes()
|
||||
fmt.Fprintf(ret, "StashList{%s}", SerialMessage(mapbytes).HumanReadableString())
|
||||
printWithIndendationLevel(level, ret, "StashList{%s}",
|
||||
SerialMessage(mapbytes).humanReadableStringAtIndentationLevel(level+1))
|
||||
return ret.String()
|
||||
case serial.StashFileID:
|
||||
msg := serial.GetRootAsStash(sm, serial.MessagePrefixSz)
|
||||
ret := &strings.Builder{}
|
||||
fmt.Fprintf(ret, "{\n")
|
||||
fmt.Fprintf(ret, "\tBranchName: %s\n", msg.BranchName())
|
||||
fmt.Fprintf(ret, "\tDesc: %s\n", msg.Desc())
|
||||
fmt.Fprintf(ret, "\tStashRootAddr: #%s\n", hash.New(msg.StashRootAddrBytes()).String())
|
||||
fmt.Fprintf(ret, "\tHeadCommitAddr: #%s\n", hash.New(msg.HeadCommitAddrBytes()).String())
|
||||
fmt.Fprintf(ret, "}")
|
||||
printWithIndendationLevel(level, ret, "{\n")
|
||||
printWithIndendationLevel(level, ret, "\tBranchName: %s\n", msg.BranchName())
|
||||
printWithIndendationLevel(level, ret, "\tDesc: %s\n", msg.Desc())
|
||||
printWithIndendationLevel(level, ret, "\tStashRootAddr: #%s\n", hash.New(msg.StashRootAddrBytes()).String())
|
||||
printWithIndendationLevel(level, ret, "\tHeadCommitAddr: #%s\n", hash.New(msg.HeadCommitAddrBytes()).String())
|
||||
printWithIndendationLevel(level, ret, "}")
|
||||
return ret.String()
|
||||
case serial.TagFileID:
|
||||
return "Tag"
|
||||
case serial.WorkingSetFileID:
|
||||
msg := serial.GetRootAsWorkingSet(sm, serial.MessagePrefixSz)
|
||||
ret := &strings.Builder{}
|
||||
fmt.Fprintf(ret, "{\n")
|
||||
fmt.Fprintf(ret, "\tName: %s\n", msg.Name())
|
||||
fmt.Fprintf(ret, "\tDesc: %s\n", msg.Desc())
|
||||
fmt.Fprintf(ret, "\tEmail: %s\n", msg.Email())
|
||||
fmt.Fprintf(ret, "\tTime: %s\n", time.UnixMilli((int64)(msg.TimestampMillis())).String())
|
||||
fmt.Fprintf(ret, "\tWorkingRootAddr: #%s\n", hash.New(msg.WorkingRootAddrBytes()).String())
|
||||
fmt.Fprintf(ret, "\tStagedRootAddr: #%s\n", hash.New(msg.StagedRootAddrBytes()).String())
|
||||
fmt.Fprintf(ret, "}")
|
||||
printWithIndendationLevel(level, ret, "{\n")
|
||||
printWithIndendationLevel(level, ret, "\tName: %s\n", msg.Name())
|
||||
printWithIndendationLevel(level, ret, "\tDesc: %s\n", msg.Desc())
|
||||
printWithIndendationLevel(level, ret, "\tEmail: %s\n", msg.Email())
|
||||
printWithIndendationLevel(level, ret, "\tTime: %s\n", time.UnixMilli((int64)(msg.TimestampMillis())).String())
|
||||
printWithIndendationLevel(level, ret, "\tWorkingRootAddr: #%s\n", hash.New(msg.WorkingRootAddrBytes()).String())
|
||||
printWithIndendationLevel(level, ret, "\tStagedRootAddr: #%s\n", hash.New(msg.StagedRootAddrBytes()).String())
|
||||
printWithIndendationLevel(level, ret, "}")
|
||||
return ret.String()
|
||||
case serial.CommitFileID:
|
||||
msg := serial.GetRootAsCommit(sm, serial.MessagePrefixSz)
|
||||
ret := &strings.Builder{}
|
||||
fmt.Fprintf(ret, "{\n")
|
||||
fmt.Fprintf(ret, "\tName: %s\n", msg.Name())
|
||||
fmt.Fprintf(ret, "\tDesc: %s\n", msg.Description())
|
||||
fmt.Fprintf(ret, "\tEmail: %s\n", msg.Email())
|
||||
fmt.Fprintf(ret, "\tTime: %s\n", time.UnixMilli((int64)(msg.TimestampMillis())).String())
|
||||
fmt.Fprintf(ret, "\tHeight: %d\n", msg.Height())
|
||||
printWithIndendationLevel(level, ret, "{\n")
|
||||
printWithIndendationLevel(level, ret, "\tName: %s\n", msg.Name())
|
||||
printWithIndendationLevel(level, ret, "\tDesc: %s\n", msg.Description())
|
||||
printWithIndendationLevel(level, ret, "\tEmail: %s\n", msg.Email())
|
||||
printWithIndendationLevel(level, ret, "\tTime: %s\n", time.UnixMilli((int64)(msg.TimestampMillis())).String())
|
||||
printWithIndendationLevel(level, ret, "\tHeight: %d\n", msg.Height())
|
||||
|
||||
fmt.Fprintf(ret, "\tRootValue: {\n")
|
||||
printWithIndendationLevel(level, ret, "\tRootValue: {\n")
|
||||
hashes := msg.RootBytes()
|
||||
for i := 0; i < len(hashes)/hash.ByteLen; i++ {
|
||||
addr := hash.New(hashes[i*20 : (i+1)*20])
|
||||
fmt.Fprintf(ret, "\t\t#%s\n", addr.String())
|
||||
printWithIndendationLevel(level, ret, "\t\t#%s\n", addr.String())
|
||||
}
|
||||
fmt.Fprintf(ret, "\t}\n")
|
||||
printWithIndendationLevel(level, ret, "\t}\n")
|
||||
|
||||
fmt.Fprintf(ret, "\tParents: {\n")
|
||||
printWithIndendationLevel(level, ret, "\tParents: {\n")
|
||||
hashes = msg.ParentAddrsBytes()
|
||||
for i := 0; i < msg.ParentAddrsLength()/hash.ByteLen; i++ {
|
||||
addr := hash.New(hashes[i*20 : (i+1)*20])
|
||||
fmt.Fprintf(ret, "\t\t#%s\n", addr.String())
|
||||
printWithIndendationLevel(level, ret, "\t\t#%s\n", addr.String())
|
||||
}
|
||||
fmt.Fprintf(ret, "\t}\n")
|
||||
printWithIndendationLevel(level, ret, "\t}\n")
|
||||
|
||||
fmt.Fprintf(ret, "\tParentClosure: {\n")
|
||||
printWithIndendationLevel(level, ret, "\tParentClosure: {\n")
|
||||
hashes = msg.ParentClosureBytes()
|
||||
for i := 0; i < msg.ParentClosureLength()/hash.ByteLen; i++ {
|
||||
addr := hash.New(hashes[i*20 : (i+1)*20])
|
||||
fmt.Fprintf(ret, "\t\t#%s\n", addr.String())
|
||||
printWithIndendationLevel(level, ret, "\t\t#%s\n", addr.String())
|
||||
}
|
||||
fmt.Fprintf(ret, "\t}\n")
|
||||
printWithIndendationLevel(level, ret, "\t}\n")
|
||||
|
||||
fmt.Fprintf(ret, "}")
|
||||
printWithIndendationLevel(level, ret, "}")
|
||||
return ret.String()
|
||||
case serial.RootValueFileID:
|
||||
msg := serial.GetRootAsRootValue(sm, serial.MessagePrefixSz)
|
||||
ret := &strings.Builder{}
|
||||
fmt.Fprintf(ret, "{\n")
|
||||
fmt.Fprintf(ret, "\tFeatureVersion: %d\n", msg.FeatureVersion())
|
||||
fmt.Fprintf(ret, "\tForeignKeys: #%s\n", hash.New(msg.ForeignKeyAddrBytes()).String())
|
||||
fmt.Fprintf(ret, "\tTables: {\n\t%s", SerialMessage(msg.TablesBytes()).HumanReadableString())
|
||||
fmt.Fprintf(ret, "\t}\n")
|
||||
fmt.Fprintf(ret, "}")
|
||||
printWithIndendationLevel(level, ret, "{\n")
|
||||
printWithIndendationLevel(level, ret, "\tFeatureVersion: %d\n", msg.FeatureVersion())
|
||||
printWithIndendationLevel(level, ret, "\tForeignKeys: #%s\n", hash.New(msg.ForeignKeyAddrBytes()).String())
|
||||
printWithIndendationLevel(level, ret, "\tTables: %s\n",
|
||||
SerialMessage(msg.TablesBytes()).humanReadableStringAtIndentationLevel(level+1))
|
||||
printWithIndendationLevel(level, ret, "}")
|
||||
return ret.String()
|
||||
case serial.TableFileID:
|
||||
msg := serial.GetRootAsTable(sm, serial.MessagePrefixSz)
|
||||
ret := &strings.Builder{}
|
||||
|
||||
fmt.Fprintf(ret, "asdasdf {\n")
|
||||
fmt.Fprintf(ret, "\tSchema: #%s\n", hash.New(msg.SchemaBytes()).String())
|
||||
fmt.Fprintf(ret, "\tViolations: #%s\n", hash.New(msg.ViolationsBytes()).String())
|
||||
printWithIndendationLevel(level, ret, "{\n")
|
||||
printWithIndendationLevel(level, ret, "\tSchema: #%s\n", hash.New(msg.SchemaBytes()).String())
|
||||
printWithIndendationLevel(level, ret, "\tViolations: #%s\n", hash.New(msg.ViolationsBytes()).String())
|
||||
// TODO: merge conflicts, not stable yet
|
||||
|
||||
fmt.Fprintf(ret, "\tAutoinc: %d\n", msg.AutoIncrementValue())
|
||||
printWithIndendationLevel(level, ret, "\tAutoinc: %d\n", msg.AutoIncrementValue())
|
||||
|
||||
// TODO: can't use tree package to print here, creates a cycle
|
||||
fmt.Fprintf(ret, "\tPrimary index: prolly tree\n")
|
||||
|
||||
fmt.Fprintf(ret, "\tSecondary indexes: {\n\t%s\n", SerialMessage(msg.SecondaryIndexesBytes()).HumanReadableString())
|
||||
fmt.Fprintf(ret, "\t}\n")
|
||||
fmt.Fprintf(ret, "}")
|
||||
printWithIndendationLevel(level, ret, "\tPrimary index: #%s\n", hash.Of(msg.PrimaryIndexBytes()))
|
||||
printWithIndendationLevel(level, ret, "\tSecondary indexes: %s\n",
|
||||
SerialMessage(msg.SecondaryIndexesBytes()).humanReadableStringAtIndentationLevel(level+1))
|
||||
printWithIndendationLevel(level, ret, "}")
|
||||
return ret.String()
|
||||
case serial.AddressMapFileID:
|
||||
keys, values, _, cnt, err := message.UnpackFields(serial.Message(sm))
|
||||
@@ -162,16 +171,17 @@ func (sm SerialMessage) HumanReadableString() string {
|
||||
return fmt.Sprintf("error in HumanReadString(): %s", err)
|
||||
}
|
||||
var b strings.Builder
|
||||
b.Write([]byte("AddressMap{\n"))
|
||||
b.Write([]byte("AddressMap {\n"))
|
||||
for i := uint16(0); i < cnt; i++ {
|
||||
name := keys.GetItem(int(i), serial.Message(sm))
|
||||
addr := values.GetItem(int(i), serial.Message(sm))
|
||||
b.Write([]byte("\t"))
|
||||
b.Write([]byte(strings.Repeat("\t", level+1)))
|
||||
b.Write(name)
|
||||
b.Write([]byte(": #"))
|
||||
b.Write([]byte(hash.New(addr).String()))
|
||||
b.Write([]byte("\n"))
|
||||
}
|
||||
b.Write([]byte(strings.Repeat("\t", level)))
|
||||
b.Write([]byte("}"))
|
||||
return b.String()
|
||||
default:
|
||||
|
||||
+13
-15
@@ -21,8 +21,6 @@
|
||||
|
||||
package types
|
||||
|
||||
import "context"
|
||||
|
||||
// IsSubtype determines whether concreteType is a subtype of requiredType. For example, `Float` is a subtype of `Float | String`.
|
||||
func IsSubtype(nbf *NomsBinFormat, requiredType, concreteType *Type) bool {
|
||||
isSub, _ := isSubtypeTopLevel(nbf, requiredType, concreteType)
|
||||
@@ -177,8 +175,8 @@ func compoundSubtype(nbf *NomsBinFormat, requiredType, concreteType *Type, hasEx
|
||||
return isSubtypeDetails(nbf, requiredType, concreteType, hasExtra, parentStructTypes)
|
||||
}
|
||||
|
||||
func IsValueSubtypeOf(ctx context.Context, nbf *NomsBinFormat, v Value, t *Type) (bool, error) {
|
||||
isSub, _, err := isValueSubtypeOfDetails(ctx, nbf, v, t, false)
|
||||
func IsValueSubtypeOf(nbf *NomsBinFormat, v Value, t *Type) (bool, error) {
|
||||
isSub, _, err := isValueSubtypeOfDetails(nbf, v, t, false)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -211,11 +209,11 @@ func IsValueSubtypeOf(ctx context.Context, nbf *NomsBinFormat, v Value, t *Type)
|
||||
// }
|
||||
//
|
||||
// IsValueSubtypeOfDetails(v, type1) would return isSub == true, and hasExtra == true
|
||||
func IsValueSubtypeOfDetails(ctx context.Context, nbf *NomsBinFormat, v Value, t *Type) (bool, bool, error) {
|
||||
return isValueSubtypeOfDetails(ctx, nbf, v, t, false)
|
||||
func IsValueSubtypeOfDetails(nbf *NomsBinFormat, v Value, t *Type) (bool, bool, error) {
|
||||
return isValueSubtypeOfDetails(nbf, v, t, false)
|
||||
}
|
||||
|
||||
func isValueSubtypeOfDetails(ctx context.Context, nbf *NomsBinFormat, v Value, t *Type, hasExtra bool) (bool, bool, error) {
|
||||
func isValueSubtypeOfDetails(nbf *NomsBinFormat, v Value, t *Type, hasExtra bool) (bool, bool, error) {
|
||||
switch t.TargetKind() {
|
||||
case ValueKind:
|
||||
return true, hasExtra, nil
|
||||
@@ -241,7 +239,7 @@ func isValueSubtypeOfDetails(ctx context.Context, nbf *NomsBinFormat, v Value, t
|
||||
anonStruct = et
|
||||
continue
|
||||
}
|
||||
isSub, hasMore, err := isValueSubtypeOfDetails(ctx, nbf, v, et, hasExtra)
|
||||
isSub, hasMore, err := isValueSubtypeOfDetails(nbf, v, et, hasExtra)
|
||||
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
@@ -254,7 +252,7 @@ func isValueSubtypeOfDetails(ctx context.Context, nbf *NomsBinFormat, v Value, t
|
||||
}
|
||||
|
||||
if anonStruct != nil {
|
||||
isSub, hasMore, err := isValueSubtypeOfDetails(ctx, nbf, v, anonStruct, hasExtra)
|
||||
isSub, hasMore, err := isValueSubtypeOfDetails(nbf, v, anonStruct, hasExtra)
|
||||
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
@@ -300,7 +298,7 @@ func isValueSubtypeOfDetails(ctx context.Context, nbf *NomsBinFormat, v Value, t
|
||||
return false, hasExtra, nil
|
||||
}
|
||||
} else {
|
||||
isSub, hasMore, err := isValueSubtypeOfDetails(ctx, nbf, fv, f.Type, hasExtra)
|
||||
isSub, hasMore, err := isValueSubtypeOfDetails(nbf, fv, f.Type, hasExtra)
|
||||
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
@@ -332,14 +330,14 @@ func isValueSubtypeOfDetails(ctx context.Context, nbf *NomsBinFormat, v Value, t
|
||||
kt := desc.ElemTypes[0]
|
||||
vt := desc.ElemTypes[1]
|
||||
if seq, ok := v.orderedSequence.(mapLeafSequence); ok {
|
||||
meSl, err := seq.entries(ctx)
|
||||
meSl, err := seq.entries()
|
||||
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
for _, entry := range meSl.entries {
|
||||
isSub, hasMore, err := isValueSubtypeOfDetails(ctx, nbf, entry.key, kt, hasExtra)
|
||||
isSub, hasMore, err := isValueSubtypeOfDetails(nbf, entry.key, kt, hasExtra)
|
||||
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
@@ -350,7 +348,7 @@ func isValueSubtypeOfDetails(ctx context.Context, nbf *NomsBinFormat, v Value, t
|
||||
}
|
||||
|
||||
hasExtra = hasExtra || hasMore
|
||||
isSub, hasExtra, err = isValueSubtypeOfDetails(ctx, nbf, entry.value, vt, hasExtra)
|
||||
isSub, hasExtra, err = isValueSubtypeOfDetails(nbf, entry.value, vt, hasExtra)
|
||||
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
@@ -373,7 +371,7 @@ func isValueSubtypeOfDetails(ctx context.Context, nbf *NomsBinFormat, v Value, t
|
||||
return false, false, err
|
||||
}
|
||||
for _, v := range vals {
|
||||
isSub, hasMore, err := isValueSubtypeOfDetails(ctx, nbf, v, et, hasExtra)
|
||||
isSub, hasMore, err := isValueSubtypeOfDetails(nbf, v, et, hasExtra)
|
||||
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
@@ -397,7 +395,7 @@ func isValueSubtypeOfDetails(ctx context.Context, nbf *NomsBinFormat, v Value, t
|
||||
}
|
||||
|
||||
for _, v := range vals {
|
||||
isSub, hasMore, err := isValueSubtypeOfDetails(ctx, nbf, v, et, hasExtra)
|
||||
isSub, hasMore, err := isValueSubtypeOfDetails(nbf, v, et, hasExtra)
|
||||
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
|
||||
@@ -35,7 +35,7 @@ import (
|
||||
)
|
||||
|
||||
func assertSubtype(ctx context.Context, nbf *NomsBinFormat, t *Type, v Value) {
|
||||
is, err := IsValueSubtypeOf(ctx, nbf, v, t)
|
||||
is, err := IsValueSubtypeOf(nbf, v, t)
|
||||
d.PanicIfError(err)
|
||||
|
||||
if !is {
|
||||
@@ -652,11 +652,11 @@ func TestIsValueSubtypeOf(tt *testing.T) {
|
||||
vs := newTestValueStore()
|
||||
|
||||
assertTrue := func(v Value, t *Type) {
|
||||
assert.True(IsValueSubtypeOf(context.Background(), vs.Format(), v, t))
|
||||
assert.True(IsValueSubtypeOf(vs.Format(), v, t))
|
||||
}
|
||||
|
||||
assertFalse := func(v Value, t *Type) {
|
||||
assert.False(IsValueSubtypeOf(context.Background(), vs.Format(), v, t))
|
||||
assert.False(IsValueSubtypeOf(vs.Format(), v, t))
|
||||
}
|
||||
|
||||
allTypes := []struct {
|
||||
@@ -971,7 +971,7 @@ func TestIsValueSubtypeOfDetails(tt *testing.T) {
|
||||
require.NoError(tt, err)
|
||||
t, err := makeTestStructTypeFromFieldNames(tString)
|
||||
require.NoError(tt, err)
|
||||
isSub, hasExtra, err := IsValueSubtypeOfDetails(context.Background(), vs.Format(), v, t)
|
||||
isSub, hasExtra, err := IsValueSubtypeOfDetails(vs.Format(), v, t)
|
||||
require.NoError(tt, err)
|
||||
a.Equal(exp1, isSub, "expected %t for IsSub, received: %t", exp1, isSub)
|
||||
if isSub {
|
||||
|
||||
@@ -5,7 +5,7 @@ set -eo pipefail
|
||||
script_dir=$(dirname "$0")
|
||||
cd $script_dir/../..
|
||||
|
||||
paths=`find . -maxdepth 1 -mindepth 1 \( -name gen -prune -o -type d -print -o -type f -name '*.go' -print \)`
|
||||
paths=`find . -maxdepth 1 -mindepth 1 \( -type d -print -o -type f -name '*.go' -print \)`
|
||||
|
||||
goimports -w -local github.com/dolthub/dolt $paths
|
||||
|
||||
|
||||
@@ -320,7 +320,7 @@ NOT_VALID_REPO_ERROR="The current directory is not a valid dolt repository."
|
||||
@test "no-repo: don't panic if invalid HOME" {
|
||||
DOLT_ROOT_PATH=
|
||||
HOME=/this/is/garbage
|
||||
run dolt
|
||||
run dolt status
|
||||
[ "$status" -eq 1 ]
|
||||
[[ ! "$output" =~ "panic" ]]
|
||||
[[ "$output" =~ "Failed to load the HOME directory" ]]
|
||||
|
||||
@@ -10,6 +10,43 @@ teardown() {
|
||||
teardown_common
|
||||
}
|
||||
|
||||
extract_value() {
|
||||
key="$1"
|
||||
input="$2"
|
||||
echo "$input" | awk "
|
||||
BEGIN { in_value = 0 }
|
||||
/$key: {/ { in_value = 1; next }
|
||||
match("'$0'", /$key: /) { print substr("'$0'", RSTART+RLENGTH) }
|
||||
/}/ { if (in_value) { in_value = 0 } }
|
||||
in_value { gsub(/^[ \t]+/, \"\"); print }
|
||||
"
|
||||
}
|
||||
|
||||
assert_has_key() {
|
||||
key="$1"
|
||||
input="$2"
|
||||
extracted=$(extract_value "$key" "$input")
|
||||
if [[ -z $extracted ]]; then
|
||||
echo "Expected to find key $key"
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
assert_has_key_value() {
|
||||
key="$1"
|
||||
value="$2"
|
||||
input="$3"
|
||||
extracted=$(extract_value "$key" "$input")
|
||||
if [[ "$extracted" != "$value" ]]; then
|
||||
echo "Expected key $key to have value $value, instead found $extracted"
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
@test "show: on initialized repo" {
|
||||
run dolt show
|
||||
[ "$status" -eq "0" ]
|
||||
@@ -64,7 +101,7 @@ teardown() {
|
||||
dolt sql -q "create table testtable (pk int PRIMARY KEY)"
|
||||
dolt add .
|
||||
dolt commit -m "commit: add table"
|
||||
|
||||
|
||||
run dolt show
|
||||
[ $status -eq 0 ]
|
||||
[[ "$output" =~ "commit: add table" ]] || false
|
||||
@@ -73,14 +110,150 @@ teardown() {
|
||||
[[ "$output" =~ "+CREATE TABLE \`testtable\` (" ]] || false
|
||||
[[ "$output" =~ "+ \`pk\` int NOT NULL," ]] || false
|
||||
[[ "$output" =~ "+ PRIMARY KEY (\`pk\`)" ]] || false
|
||||
|
||||
|
||||
dolt sql -q 'insert into testtable values (4)'
|
||||
dolt add .
|
||||
dolt commit -m "commit: add values"
|
||||
|
||||
|
||||
run dolt show
|
||||
[ $status -eq 0 ]
|
||||
[[ "$output" =~ "commit: add values" ]] || false
|
||||
[[ "$output" =~ "| | pk |" ]] || false
|
||||
[[ "$output" =~ "| + | 4 |" ]] || false
|
||||
}
|
||||
|
||||
@test "show: --no-pretty" {
|
||||
skip_nbf_ld_1
|
||||
dolt commit --allow-empty -m "commit: initialize table1"
|
||||
run dolt show --no-pretty
|
||||
[ $status -eq 0 ]
|
||||
[[ "$output" =~ "SerialMessage" ]] || false
|
||||
assert_has_key "Name" "$output"
|
||||
assert_has_key_value "Name" "Bats Tests" "$output"
|
||||
assert_has_key_value "Desc" "commit: initialize table1" "$output"
|
||||
assert_has_key_value "Name" "Bats Tests" "$output"
|
||||
assert_has_key_value "Email" "bats@email.fake" "$output"
|
||||
assert_has_key "Time" "$output"
|
||||
assert_has_key_value "Height" "2" "$output"
|
||||
assert_has_key "RootValue" "$output"
|
||||
assert_has_key "Parents" "$output"
|
||||
assert_has_key "ParentClosure" "$output"
|
||||
}
|
||||
|
||||
@test "show: HEAD root" {
|
||||
skip_nbf_ld_1
|
||||
dolt sql -q "create table table1 (pk int PRIMARY KEY)"
|
||||
dolt sql -q "insert into table1 values (1), (2), (3)"
|
||||
dolt add .
|
||||
dolt commit -m "commit: initialize table1"
|
||||
dolt sql -q "create table table2 (pk int PRIMARY KEY)"
|
||||
dolt sql -q "insert into table1 values (4), (5), (6)"
|
||||
dolt add .
|
||||
dolt sql -q "create table table3 (pk int PRIMARY KEY)"
|
||||
dolt sql -q "insert into table1 values (7), (8), (9)"
|
||||
head=$(dolt show --no-pretty)
|
||||
rootValue=$(extract_value RootValue "$head")
|
||||
echo rootValue=$rootValue
|
||||
[[ ! -z $rootValue ]] || false
|
||||
|
||||
run dolt show $rootValue
|
||||
[ $status -eq 0 ]
|
||||
[[ "$output" =~ "table1" ]] || false
|
||||
[[ ! "$output" =~ "table2" ]] || false
|
||||
[[ ! "$output" =~ "table3" ]] || false
|
||||
}
|
||||
|
||||
@test "show: WORKING" {
|
||||
skip_nbf_ld_1
|
||||
dolt sql -q "create table table1 (pk int PRIMARY KEY)"
|
||||
dolt sql -q "insert into table1 values (1), (2), (3)"
|
||||
dolt add .
|
||||
dolt commit -m "commit: initialize table1"
|
||||
dolt sql -q "create table table2 (pk int PRIMARY KEY)"
|
||||
dolt sql -q "insert into table1 values (4), (5), (6)"
|
||||
dolt add .
|
||||
dolt sql -q "create table table3 (pk int PRIMARY KEY)"
|
||||
dolt sql -q "insert into table1 values (7), (8), (9)"
|
||||
run dolt show WORKING
|
||||
[ $status -eq 0 ]
|
||||
[[ "$output" =~ "table1" ]] || false
|
||||
[[ "$output" =~ "table2" ]] || false
|
||||
[[ "$output" =~ "table3" ]] || false
|
||||
}
|
||||
|
||||
@test "show: STAGED" {
|
||||
skip_nbf_ld_1
|
||||
dolt sql -q "create table table1 (pk int PRIMARY KEY)"
|
||||
dolt sql -q "insert into table1 values (1), (2), (3)"
|
||||
dolt add .
|
||||
dolt commit -m "commit: initialize table1"
|
||||
dolt sql -q "create table table2 (pk int PRIMARY KEY)"
|
||||
dolt sql -q "insert into table1 values (4), (5), (6)"
|
||||
dolt add .
|
||||
dolt sql -q "create table table3 (pk int PRIMARY KEY)"
|
||||
dolt sql -q "insert into table1 values (7), (8), (9)"
|
||||
run dolt show STAGED
|
||||
[ $status -eq 0 ]
|
||||
[[ "$output" =~ "table1" ]] || false
|
||||
[[ "$output" =~ "table2" ]] || false
|
||||
[[ ! "$output" =~ "table3" ]] || false
|
||||
}
|
||||
|
||||
@test "show: table" {
|
||||
skip_nbf_ld_1
|
||||
dolt sql -q "create table table1 (pk int PRIMARY KEY)"
|
||||
dolt sql -q "insert into table1 values (1), (2), (3)"
|
||||
dolt add .
|
||||
dolt commit -m "commit: initialize table1"
|
||||
dolt sql -q "create table table2 (pk int PRIMARY KEY)"
|
||||
dolt sql -q "insert into table1 values (4), (5), (6)"
|
||||
dolt add .
|
||||
dolt sql -q "create table table3 (pk int PRIMARY KEY)"
|
||||
dolt sql -q "insert into table1 values (7), (8), (9)"
|
||||
workingRoot=$(dolt show WORKING)
|
||||
tableAddress=$(extract_value table1 "$workingRoot")
|
||||
|
||||
run dolt show $tableAddress
|
||||
assert_has_key Schema "$output"
|
||||
assert_has_key Violations "$output"
|
||||
assert_has_key Autoinc "$output"
|
||||
assert_has_key "Primary index" "$output"
|
||||
assert_has_key "Secondary indexes" "$output"
|
||||
|
||||
}
|
||||
|
||||
@test "show: pretty commit from hash" {
|
||||
skip_nbf_ld_1
|
||||
dolt tag v0
|
||||
dolt commit --allow-empty -m "commit1"
|
||||
|
||||
head=$(dolt show --no-pretty)
|
||||
parentHash=$(extract_value Parents "$head")
|
||||
|
||||
run dolt show "$parentHash"
|
||||
[[ "$output" =~ "tag: v0" ]] || false
|
||||
}
|
||||
|
||||
@test "show: --no-pretty with old format" {
|
||||
skip_nbf_dolt
|
||||
|
||||
run dolt show --no-pretty
|
||||
[ $status -eq 1 ]
|
||||
[[ "$output" =~ "dolt show --no-pretty is not supported when using old LD_1 storage format." ]] || false
|
||||
}
|
||||
|
||||
@test "show: non-commit object with old format" {
|
||||
skip_nbf_dolt
|
||||
|
||||
run dolt show WORKING
|
||||
[ $status -eq 1 ]
|
||||
[[ "$output" =~ "dolt show cannot show non-commit objects when using the old LD_1 storage format: WORKING is not a commit" ]] || false
|
||||
}
|
||||
|
||||
@test "show: non-existent branch" {
|
||||
skip_nbf_dolt
|
||||
|
||||
run dolt show branch1
|
||||
[ $status -eq 1 ]
|
||||
[[ "$output" =~ "branch not found: branch1" ]] || false
|
||||
}
|
||||
@@ -204,6 +204,38 @@ teardown() {
|
||||
rm -rf db_dir
|
||||
}
|
||||
|
||||
@test "sql: check --data-dir can be used as argument before subcommand" {
|
||||
# remove config files
|
||||
rm -rf .doltcfg
|
||||
rm -rf db_dir
|
||||
|
||||
# create data dir
|
||||
mkdir db_dir
|
||||
cd db_dir
|
||||
DATADIR=$(pwd)
|
||||
|
||||
# create databases
|
||||
mkdir dba
|
||||
cd dba
|
||||
dolt init
|
||||
cd ..
|
||||
|
||||
mkdir dbb
|
||||
cd dbb
|
||||
dolt init
|
||||
|
||||
# Ensure --data-dir flag is really used.
|
||||
cd /tmp
|
||||
|
||||
# show databases, expect all
|
||||
run dolt --data-dir="$DATADIR" sql -q "show databases;"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "dba" ]] || false
|
||||
[[ "$output" =~ "dbb" ]] || false
|
||||
|
||||
dolt --data-dir="$DATADIR" sql -q "use dba; create table tablea (id int);"
|
||||
}
|
||||
|
||||
@test "sql: check configurations specify doltcfg directory" {
|
||||
# remove any previous config directories
|
||||
rm -rf .doltcfg
|
||||
|
||||
Reference in New Issue
Block a user