diff --git a/.github/workflows/cd-release.yaml b/.github/workflows/cd-release.yaml index bd85004848..7e14f7a0ae 100644 --- a/.github/workflows/cd-release.yaml +++ b/.github/workflows/cd-release.yaml @@ -31,7 +31,7 @@ jobs: run: | latest=$(git rev-parse HEAD) echo "::set-output name=commitish::$latest" - GO_BUILD_VERSION=1.15.11 go/utils/publishrelease/buildbinaries.sh + GO_BUILD_VERSION=1.17.1 go/utils/publishrelease/buildbinaries.sh - name: Create Release id: create_release uses: actions/create-release@v1 diff --git a/.github/workflows/ci-bats-unix.yaml b/.github/workflows/ci-bats-unix.yaml index b3d5f904d2..7457b8091b 100644 --- a/.github/workflows/ci-bats-unix.yaml +++ b/.github/workflows/ci-bats-unix.yaml @@ -39,7 +39,7 @@ jobs: - name: Setup Go 1.x uses: actions/setup-go@v2 with: - go-version: ^1.15 + go-version: ^1.17 id: go - name: Setup Python 3.x uses: actions/setup-python@v2 diff --git a/.github/workflows/ci-bats-windows.yaml b/.github/workflows/ci-bats-windows.yaml index 3c1596e431..36a10f4e4d 100644 --- a/.github/workflows/ci-bats-windows.yaml +++ b/.github/workflows/ci-bats-windows.yaml @@ -84,7 +84,7 @@ jobs: - name: Setup Go 1.x uses: actions/setup-go@v2 with: - go-version: ^1.15 + go-version: ^1.17 id: go - name: Setup Python 3.x uses: actions/setup-python@v2 diff --git a/.github/workflows/ci-check-repo.yaml b/.github/workflows/ci-check-repo.yaml index b09066d1d9..027c4f014d 100644 --- a/.github/workflows/ci-check-repo.yaml +++ b/.github/workflows/ci-check-repo.yaml @@ -12,7 +12,7 @@ jobs: - name: Setup Go 1.x uses: actions/setup-go@v2 with: - go-version: ^1.15 + go-version: ^1.17 - uses: actions/checkout@v2 - name: Check all working-directory: ./go diff --git a/.github/workflows/ci-compatibility-tests.yaml b/.github/workflows/ci-compatibility-tests.yaml index bd99788945..2b90812a56 100644 --- a/.github/workflows/ci-compatibility-tests.yaml +++ b/.github/workflows/ci-compatibility-tests.yaml @@ -16,7 +16,7 @@ jobs: - name: Setup Go 1.x uses: actions/setup-go@v2 with: - go-version: ^1.13 + go-version: ^1.17 id: go - uses: actions/checkout@v2 - uses: actions/setup-node@v1 diff --git a/.github/workflows/ci-format-repo.yaml b/.github/workflows/ci-format-repo.yaml index 036e1d6166..f0b319bdd2 100644 --- a/.github/workflows/ci-format-repo.yaml +++ b/.github/workflows/ci-format-repo.yaml @@ -12,7 +12,7 @@ jobs: - name: Setup Go 1.x uses: actions/setup-go@v2 with: - go-version: ^1.15 + go-version: ^1.17 - uses: actions/checkout@v2 with: token: ${{ secrets.REPO_ACCESS_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci-go-tests.yaml b/.github/workflows/ci-go-tests.yaml index 4e02246ac4..9e1f17df0f 100644 --- a/.github/workflows/ci-go-tests.yaml +++ b/.github/workflows/ci-go-tests.yaml @@ -22,7 +22,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v2 with: - go-version: ^1.15 + go-version: ^1.17 id: go - uses: actions/checkout@v2 - name: Test All @@ -69,7 +69,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v2 with: - go-version: ^1.15 + go-version: ^1.17 id: go - uses: actions/checkout@v2 - name: Test All diff --git a/go/cmd/dolt/cli/command.go b/go/cmd/dolt/cli/command.go index 61594c8d90..14c98157fa 100644 --- a/go/cmd/dolt/cli/command.go +++ b/go/cmd/dolt/cli/command.go @@ -16,7 +16,10 @@ package cli import ( "context" + "os" + "os/signal" "strings" + "syscall" "github.com/fatih/color" @@ -54,7 +57,7 @@ func hasHelpFlag(args []string) bool { // Command is the interface which defines a Dolt cli command type Command interface { - // Name is returns the name of the Dolt cli command. This is what is used on the command line to invoke the command + // Name returns the name of the Dolt cli command. This is what is used on the command line to invoke the command Name() string // Description returns a description of the command Description() string @@ -64,6 +67,15 @@ type Command interface { CreateMarkdown(fs filesys.Filesys, path, commandStr string) error } +// SignalCommand is an extension of Command that allows commands to install their own signal handlers, rather than use +// the global one (which cancels the global context). +type SignalCommand interface { + Command + + // InstallsSignalHandlers returns whether this command manages its own signal handlers for interruption / termination. + InstallsSignalHandlers() bool +} + // This type is to store the content of a documented command, elsewhere we can transform this struct into // other structs that are used to generate documentation at the command line and in markdown files. type CommandDocumentationContent struct { @@ -153,6 +165,7 @@ func (hc SubCommandHandler) Exec(ctx context.Context, commandStr string, args [] if len(args) > 0 { subCommandStr = strings.ToLower(strings.TrimSpace(args[0])) } + for _, cmd := range hc.Subcommands { lwrName := strings.ToLower(cmd.Name()) if lwrName == subCommandStr { @@ -190,6 +203,14 @@ func (hc SubCommandHandler) handleCommand(ctx context.Context, commandStr string ctx = events.NewContextForEvent(ctx, evt) } + // Certain commands cannot tolerate a top-level signal handler (which cancels the root context) but need to manage + // their own interrupt semantics. + if signalCmd, ok := cmd.(SignalCommand); !ok || !signalCmd.InstallsSignalHandlers() { + var stop context.CancelFunc + ctx, stop = signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) + defer stop() + } + ret := cmd.Exec(ctx, commandStr, args, dEnv) if evt != nil { diff --git a/go/cmd/dolt/commands/sql.go b/go/cmd/dolt/commands/sql.go index 8dee2bb159..8441d66024 100644 --- a/go/cmd/dolt/commands/sql.go +++ b/go/cmd/dolt/commands/sql.go @@ -19,10 +19,12 @@ import ( "fmt" "io" "os" + "os/signal" "path/filepath" "regexp" "runtime" "strings" + "syscall" "github.com/abiosoft/readline" sqle "github.com/dolthub/go-mysql-server" @@ -121,6 +123,13 @@ type SqlCmd struct { VersionStr string } +// The SQL shell installs its own signal handlers so that you can cancel a running query without and still run a new one. +func (cmd SqlCmd) InstallsSignalHandlers() bool { + return true +} + +var _ cli.SignalCommand = SqlCmd{} + // Name returns the name of the Dolt cli command. This is what is used on the command line to invoke the command func (cmd SqlCmd) Name() string { return "sql" @@ -368,12 +377,12 @@ func execShell( format resultFormat, ) errhand.VerboseError { dbs := CollectDBs(mrEnv) - se, sqlCtx, err := newSqlEngine(ctx, dEnv, mrEnv, roots, readOnly, format, dbs...) + se, err := newSqlEngine(ctx, dEnv, mrEnv, roots, readOnly, format, dbs...) if err != nil { return errhand.VerboseErrorFromError(err) } - err = runShell(sqlCtx, se, mrEnv, roots) + err = runShell(ctx, se, mrEnv, roots) if err != nil { return errhand.BuildDError(err.Error()).Build() } @@ -391,7 +400,12 @@ func execBatch( format resultFormat, ) errhand.VerboseError { dbs := CollectDBs(mrEnv) - se, sqlCtx, err := newSqlEngine(ctx, dEnv, mrEnv, roots, readOnly, format, dbs...) + se, err := newSqlEngine(ctx, dEnv, mrEnv, roots, readOnly, format, dbs...) + if err != nil { + return errhand.VerboseErrorFromError(err) + } + + sqlCtx, err := se.newContext(ctx) if err != nil { return errhand.VerboseErrorFromError(err) } @@ -428,7 +442,12 @@ func execMultiStatements( format resultFormat, ) errhand.VerboseError { dbs := CollectDBs(mrEnv) - se, sqlCtx, err := newSqlEngine(ctx, dEnv, mrEnv, roots, readOnly, format, dbs...) + se, err := newSqlEngine(ctx, dEnv, mrEnv, roots, readOnly, format, dbs...) + if err != nil { + return errhand.VerboseErrorFromError(err) + } + + sqlCtx, err := se.newContext(ctx) if err != nil { return errhand.VerboseErrorFromError(err) } @@ -459,7 +478,12 @@ func execQuery( format resultFormat, ) errhand.VerboseError { dbs := CollectDBs(mrEnv) - se, sqlCtx, err := newSqlEngine(ctx, dEnv, mrEnv, roots, readOnly, format, dbs...) + se, err := newSqlEngine(ctx, dEnv, mrEnv, roots, readOnly, format, dbs...) + if err != nil { + return errhand.VerboseErrorFromError(err) + } + + sqlCtx, err := se.newContext(ctx) if err != nil { return errhand.VerboseErrorFromError(err) } @@ -742,14 +766,19 @@ func runBatchMode(ctx *sql.Context, se *sqlEngine, input io.Reader, continueOnEr // runShell starts a SQL shell. Returns when the user exits the shell. The Root of the sqlEngine may // be updated by any queries which were processed. -func runShell(ctx *sql.Context, se *sqlEngine, mrEnv env.MultiRepoEnv, initialRoots map[string]*doltdb.RootValue) error { +func runShell(ctx context.Context, se *sqlEngine, mrEnv env.MultiRepoEnv, initialRoots map[string]*doltdb.RootValue) error { _ = iohelp.WriteLine(cli.CliOut, welcomeMsg) - currentDB := ctx.Session.GetCurrentDatabase() + + sqlCtx, err := se.newContext(ctx) + if err != nil { + return err + } + + currentDB := sqlCtx.Session.GetCurrentDatabase() currEnv := mrEnv[currentDB] - // start the doltsql shell historyFile := filepath.Join(".sqlhistory") // history file written to working dir - initialPrompt := fmt.Sprintf("%s> ", ctx.GetCurrentDatabase()) + initialPrompt := fmt.Sprintf("%s> ", sqlCtx.GetCurrentDatabase()) initialMultilinePrompt := fmt.Sprintf(fmt.Sprintf("%%%ds", len(initialPrompt)), "-> ") rlConf := readline.Config{ @@ -783,6 +812,8 @@ func runShell(ctx *sql.Context, se *sqlEngine, mrEnv env.MultiRepoEnv, initialRo c.Stop() }) + // The shell's interrupt handler handles an interrupt that occurs when it's accepting input. We also install our own + // that handles interrupts during query execution or result printing, see below. shell.Interrupt(func(c *ishell.Context, count int, input string) { if count > 1 { c.Stop() @@ -791,7 +822,6 @@ func runShell(ctx *sql.Context, se *sqlEngine, mrEnv env.MultiRepoEnv, initialRo } }) - var returnedVerr errhand.VerboseError = nil // Verr that cannot be just printed but needs to be returned. shell.Uninterpreted(func(c *ishell.Context) { query := c.Args[0] if len(strings.TrimSpace(query)) == 0 { @@ -809,44 +839,58 @@ func runShell(ctx *sql.Context, se *sqlEngine, mrEnv env.MultiRepoEnv, initialRo shell.Println(color.RedString(err.Error())) } - shouldProcessQuery := true //TODO: Handle comments and enforce the current line terminator if matches := delimiterRegex.FindStringSubmatch(query); len(matches) == 3 { // If we don't match from anything, then we just pass to the SQL engine and let it complain. shell.SetLineTerminator(matches[1]) - shouldProcessQuery = false + return } - if shouldProcessQuery { - var sqlSch sql.Schema - var rowIter sql.RowIter - var err error + var nextPrompt string + var sqlSch sql.Schema + var rowIter sql.RowIter - // The SQL parser does not understand any other terminator besides semicolon, so we remove it. - if shell.LineTerminator() != ";" && strings.HasSuffix(query, shell.LineTerminator()) { - query = query[:len(query)-len(shell.LineTerminator())] + // The SQL parser does not understand any other terminator besides semicolon, so we remove it. + if shell.LineTerminator() != ";" && strings.HasSuffix(query, shell.LineTerminator()) { + query = query[:len(query)-len(shell.LineTerminator())] + } + + cont := func() bool { + subCtx, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) + defer stop() + + sqlCtx, err = se.newContext(subCtx) + if err != nil { + shell.Println(color.RedString(err.Error())) + return false } - if sqlSch, rowIter, err = processQuery(ctx, query, se); err != nil { + if sqlSch, rowIter, err = processQuery(sqlCtx, query, se); err != nil { verr := formatQueryError("", err) shell.Println(verr.Verbose()) } else if rowIter != nil { - err = PrettyPrintResults(ctx, se.resultFormat, sqlSch, rowIter, HasTopLevelOrderByClause(query)) + err = PrettyPrintResults(sqlCtx, se.resultFormat, sqlSch, rowIter, HasTopLevelOrderByClause(query)) if err != nil { shell.Println(color.RedString(err.Error())) } } + + nextPrompt = fmt.Sprintf("%s> ", sqlCtx.GetCurrentDatabase()) + return true + }() + + if !cont { + return } - currPrompt := fmt.Sprintf("%s> ", ctx.GetCurrentDatabase()) - shell.SetPrompt(currPrompt) - shell.SetMultiPrompt(fmt.Sprintf(fmt.Sprintf("%%%ds", len(currPrompt)), "-> ")) + shell.SetPrompt(nextPrompt) + shell.SetMultiPrompt(fmt.Sprintf(fmt.Sprintf("%%%ds", len(nextPrompt)), "-> ")) }) shell.Run() _ = iohelp.WriteLine(cli.CliOut, "Bye") - return returnedVerr + return nil } // Returns a new auto completer with table names, column names, and SQL keywords. @@ -1349,10 +1393,12 @@ func mergeResultIntoStats(statement sqlparser.Statement, rowIter sql.RowIter, s } type sqlEngine struct { - dbs map[string]dsqle.Database - mrEnv env.MultiRepoEnv - engine *sqle.Engine - resultFormat resultFormat + dbs map[string]dsqle.Database + mrEnv env.MultiRepoEnv + sess *dsess.Session + contextFactory func(ctx context.Context) (*sql.Context, error) + engine *sqle.Engine + resultFormat resultFormat } var ErrDBNotFoundKind = errors.NewKind("database '%s' not found") @@ -1366,7 +1412,7 @@ func newSqlEngine( readOnly bool, format resultFormat, dbs ...dsqle.Database, -) (*sqlEngine, *sql.Context, error) { +) (*sqlEngine, error) { var au auth.Auth if readOnly { @@ -1381,7 +1427,7 @@ func newSqlEngine( err := cat.Register(dfunctions.DoltFunctions...) if err != nil { - return nil, nil, err + return nil, err } engine := sqle.New(cat, analyzer.NewBuilder(cat).WithParallelism(parallelism).Build(), &sqle.Config{Auth: au}) @@ -1405,7 +1451,7 @@ func newSqlEngine( // since it isn't a current HEAD. dbState, err := getDbState(ctx, db, mrEnv) if err != nil { - return nil, nil, err + return nil, err } dbStates = append(dbStates, dbState) @@ -1416,41 +1462,50 @@ func newSqlEngine( email := *dEnv.Config.GetStringOrDefault(env.UserEmailKey, "") sess, err := dsess.NewSession(sql.NewEmptyContext(), sql.NewBaseSession(), pro, username, email, dbStates...) - sqlCtx := sql.NewContext(ctx, - sql.WithSession(sess), - sql.WithIndexRegistry(sql.NewIndexRegistry()), - sql.WithViewRegistry(sql.NewViewRegistry()), - sql.WithTracer(tracing.Tracer(ctx))) - - for _, db := range dbsAsDSQLDBs(cat.AllDatabases()) { - root, err := db.GetRoot(sqlCtx) - if err != nil { - return nil, nil, err - } - - err = dsqle.RegisterSchemaFragments(sqlCtx, db, root) - if err != nil { - return nil, nil, err - } - } - - err = sqlCtx.SetSessionVariable(sqlCtx, sql.AutoCommitSessionVar, true) + // TODO: this should just be the session default like it is with MySQL + err = sess.SetSessionVariable(sql.NewContext(ctx), sql.AutoCommitSessionVar, true) if err != nil { - return nil, nil, err + return nil, err } - initialRoots, err := mrEnv.GetWorkingRoots(ctx) - if err != nil { - return nil, nil, err - } + return &sqlEngine{ + dbs: nameToDB, + mrEnv: mrEnv, + sess: sess, + contextFactory: newSqlContext(sess, cat), + engine: engine, + resultFormat: format, + }, nil +} - if len(initialRoots) == 1 { - for name := range initialRoots { - sqlCtx.SetCurrentDatabase(name) +func newSqlContext(sess *dsess.Session, cat *sql.Catalog) func(ctx context.Context) (*sql.Context, error) { + return func(ctx context.Context) (*sql.Context, error) { + sqlCtx := sql.NewContext(ctx, + sql.WithSession(sess), + sql.WithIndexRegistry(sql.NewIndexRegistry()), + sql.WithViewRegistry(sql.NewViewRegistry()), + sql.WithTracer(tracing.Tracer(ctx))) + + seenOne := false + for _, db := range dbsAsDSQLDBs(cat.AllDatabases()) { + root, err := db.GetRoot(sqlCtx) + if err != nil { + return nil, err + } + + err = dsqle.RegisterSchemaFragments(sqlCtx, db, root) + if err != nil { + return nil, err + } + + if !seenOne { + sqlCtx.SetCurrentDatabase(db.Name()) + seenOne = true + } } - } - return &sqlEngine{nameToDB, mrEnv, engine, format}, sqlCtx, nil + return sqlCtx, nil + } } func dbsAsDSQLDBs(dbs []sql.Database) []dsqle.Database { @@ -1546,6 +1601,10 @@ func (se *sqlEngine) getRoots(sqlCtx *sql.Context) (map[string]*doltdb.RootValue return newRoots, nil } +func (se *sqlEngine) newContext(ctx context.Context) (*sql.Context, error) { + return se.contextFactory(ctx) +} + // Execute a SQL statement and return values for printing. func (se *sqlEngine) query(ctx *sql.Context, query string) (sql.Schema, sql.RowIter, error) { return se.engine.Query(ctx, query) diff --git a/go/cmd/dolt/dolt.go b/go/cmd/dolt/dolt.go index 4c46d7547b..3f0e89b137 100644 --- a/go/cmd/dolt/dolt.go +++ b/go/cmd/dolt/dolt.go @@ -21,10 +21,8 @@ import ( _ "net/http/pprof" "os" "os/exec" - "os/signal" "strconv" "strings" - "syscall" "time" "github.com/fatih/color" @@ -226,14 +224,7 @@ func runMain() int { warnIfMaxFilesTooLow() - ctx, cancelF := context.WithCancel(context.Background()) - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt, syscall.SIGTERM) - go func() { - <-c - cancelF() - }() - + ctx := context.Background() dEnv := env.Load(ctx, env.GetCurrentUserHomeDir, filesys.LocalFS, doltdb.LocalDirDoltDB, Version) if dEnv.DBLoadError == nil && commandNeedsMigrationCheck(args) { diff --git a/go/go.mod b/go/go.mod index df263338dc..25a866dc1c 100644 --- a/go/go.mod +++ b/go/go.mod @@ -1,12 +1,14 @@ module github.com/dolthub/dolt/go require ( + cloud.google.com/go v0.66.0 // indirect cloud.google.com/go/storage v1.12.0 github.com/BurntSushi/toml v0.3.1 github.com/HdrHistogram/hdrhistogram-go v1.0.0 github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d + github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect @@ -15,7 +17,9 @@ require ( github.com/bcicen/jstream v1.0.0 github.com/boltdb/bolt v1.3.1 github.com/cenkalti/backoff v2.2.1+incompatible + github.com/cespare/xxhash v1.1.0 // indirect github.com/codahale/blake2 v0.0.0-20150924215134-8d10d0420cbf + github.com/davecgh/go-spew v1.1.1 // indirect github.com/denisbrodbeck/machineid v1.0.1 github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi v0.0.0-20201005193433-3ee972b1d078 github.com/dolthub/fslock v0.0.3 @@ -28,29 +32,39 @@ require ( github.com/fatih/color v1.9.0 github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 github.com/go-kit/kit v0.10.0 // indirect + github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-openapi/errors v0.19.6 // indirect github.com/go-openapi/strfmt v0.19.5 // indirect github.com/go-sql-driver/mysql v1.6.0 + github.com/go-stack/stack v1.8.0 // indirect github.com/gocraft/dbr/v2 v2.7.0 github.com/golang/glog v0.0.0-20210429001901-424d2337a529 // indirect + github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/protobuf v1.5.2 github.com/golang/snappy v0.0.1 github.com/google/go-cmp v0.5.5 github.com/google/uuid v1.2.0 + github.com/googleapis/gax-go/v2 v2.0.5 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jedib0t/go-pretty v4.3.1-0.20191104025401-85fe5d6a7c4d+incompatible + github.com/jmespath/go-jmespath v0.3.0 // indirect github.com/jpillora/backoff v1.0.0 + github.com/jstemmer/go-junit-report v0.9.1 // indirect github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d github.com/kch42/buzhash v0.0.0-20160816060738-9bdec3dec7c6 github.com/lestrrat-go/strftime v1.0.4 // indirect + github.com/mattn/go-colorable v0.1.7 // indirect github.com/mattn/go-isatty v0.0.12 github.com/mattn/go-runewidth v0.0.9 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b github.com/mitchellh/hashstructure v1.1.0 // indirect github.com/mitchellh/mapstructure v1.3.2 // indirect + github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 // indirect github.com/opentracing/opentracing-go v1.2.0 github.com/pkg/errors v0.9.1 github.com/pkg/profile v1.5.0 + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.1.0 github.com/sergi/go-diff v1.1.0 // indirect github.com/shirou/gopsutil v3.21.2+incompatible @@ -59,19 +73,32 @@ require ( github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cobra v1.0.0 + github.com/spf13/pflag v1.0.5 // indirect + github.com/src-d/go-oniguruma v1.1.0 // indirect github.com/stretchr/testify v1.7.0 github.com/tealeg/xlsx v1.0.5 github.com/tidwall/pretty v1.0.1 // indirect github.com/tklauser/go-sysconf v0.3.5 // indirect + github.com/tklauser/numcpus v0.2.2 // indirect github.com/uber/jaeger-client-go v2.25.0+incompatible github.com/uber/jaeger-lib v2.4.0+incompatible // indirect go.mongodb.org/mongo-driver v1.7.0 // indirect + go.opencensus.io v0.22.4 // indirect + go.uber.org/atomic v1.6.0 // indirect + go.uber.org/multierr v1.5.0 // indirect go.uber.org/zap v1.15.0 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 + golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect + golang.org/x/mod v0.3.0 // indirect golang.org/x/net v0.0.0-20210505214959-0714010a04ed + golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 // indirect golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 + golang.org/x/text v0.3.6 // indirect + golang.org/x/tools v0.1.0 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/api v0.32.0 + google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20210506142907-4a47615972c2 // indirect google.golang.org/grpc v1.37.0 google.golang.org/protobuf v1.26.0 @@ -83,4 +110,4 @@ require ( replace github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi => ./gen/proto/dolt/services/eventsapi -go 1.15 +go 1.17 diff --git a/go/go.sum b/go/go.sum index aa2e0e529d..2c51d05af3 100644 --- a/go/go.sum +++ b/go/go.sum @@ -144,8 +144,6 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 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.10.1-0.20210902175808-c05838e8d173 h1:gM1YsiCfQMVcaSQrj9ZZMvJStGi0/TZc7zwsf9KbweI= -github.com/dolthub/go-mysql-server v0.10.1-0.20210902175808-c05838e8d173/go.mod h1:cPg39xeFH8/+McnJxncb79SgUuREeIqR+eTvxE6OmXc= github.com/dolthub/go-mysql-server v0.10.1-0.20210903190613-4c25c32c3883 h1:IfpwAn8PtCr1roskOckNiNxj2Iqly0yTtWeWkgla0YM= github.com/dolthub/go-mysql-server v0.10.1-0.20210903190613-4c25c32c3883/go.mod h1:cPg39xeFH8/+McnJxncb79SgUuREeIqR+eTvxE6OmXc= github.com/dolthub/ishell v0.0.0-20210205014355-16a4ce758446 h1:0ol5pj+QlKUKAtqs1LiPM3ZJKs+rHPgLSsMXmhTrCAM= diff --git a/go/libraries/doltcore/row/row.go b/go/libraries/doltcore/row/row.go index df053e92b6..1ffac5d745 100644 --- a/go/libraries/doltcore/row/row.go +++ b/go/libraries/doltcore/row/row.go @@ -201,7 +201,7 @@ func findInvalidCol(r Row, sch schema.Schema) (*schema.Column, schema.ColConstra if !col.TypeInfo.IsValid(val) { badCol = &col - return true, fmt.Errorf(`"%v" is not valid for "%v"`, val, col.TypeInfo.String()) + return true, fmt.Errorf(`"%v" is not valid for column "%s" (type "%s")`, val, col.Name, col.TypeInfo.ToSqlType().String()) } if len(col.Constraints) > 0 { diff --git a/go/utils/remotesrv/main.go b/go/utils/remotesrv/main.go index 0127b5aae1..b60656e1ee 100644 --- a/go/utils/remotesrv/main.go +++ b/go/utils/remotesrv/main.go @@ -75,7 +75,6 @@ func main() { func waitForSignal() { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, os.Kill) - <-c } diff --git a/integration-tests/bats/import-update-tables.bats b/integration-tests/bats/import-update-tables.bats index bedbc767cb..9ee251e43b 100644 --- a/integration-tests/bats/import-update-tables.bats +++ b/integration-tests/bats/import-update-tables.bats @@ -14,6 +14,14 @@ CREATE TABLE test ( c5 BIGINT COMMENT 'tag:5', PRIMARY KEY (pk) ); +SQL + + cat < 1pk1col-char-sch.sql +CREATE TABLE test ( + pk BIGINT NOT NULL COMMENT 'tag:0', + c CHAR(5) COMMENT 'tag:1', + PRIMARY KEY (pk) +); SQL cat < 1pk5col-ints.csv @@ -186,6 +194,20 @@ SQL [[ "${lines[6]}" =~ "end date" ]] || false } +@test "import-update-tables: update table with incorrect length char throws bad row error" { + cat < 1pk1col-rpt-chars.csv +pk,c +1,"123456" +DELIM + + dolt sql < 1pk1col-char-sch.sql + run dolt table import -u test 1pk1col-rpt-chars.csv + [ "$status" -eq 1 ] + [[ "$output" =~ "A bad row was encountered while moving data" ]] || false + [[ "$output" =~ "Bad Row:" ]] || false + [[ "$output" =~ '"123456" is not valid for column "c" (type "CHAR(5)")' ]] || false +} + @test "import-update-tables: update table with repeat pk in csv throws error" { cat < 1pk5col-rpt-ints.csv pk,c1,c2,c3,c4,c5