Starting work on migrating dolt diff:

- temporarily disable `dolt show`
- introduce TableInfo struct to maintain SQL table state
- update diffWriter to use non-doltdb primitives
- update diff.go to use non-doltdb primitives and load most data from SQL
This commit is contained in:
Pavel Safronov
2023-06-23 10:14:32 -07:00
parent 869352f8e1
commit d97c456e09
9 changed files with 1131 additions and 747 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -28,24 +28,21 @@ import (
"github.com/dolthub/dolt/go/cmd/dolt/cli"
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
"github.com/dolthub/dolt/go/libraries/doltcore/diff"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
"github.com/dolthub/dolt/go/libraries/doltcore/table/editor"
"github.com/dolthub/dolt/go/libraries/doltcore/table/typed/json"
"github.com/dolthub/dolt/go/libraries/doltcore/table/untyped/sqlexport"
"github.com/dolthub/dolt/go/libraries/doltcore/table/untyped/tabular"
"github.com/dolthub/dolt/go/libraries/utils/iohelp"
"github.com/dolthub/dolt/go/store/atomicerr"
)
// diffWriter is an interface that lets us write diffs in a variety of output formats
type diffWriter interface {
// BeginTable is called when a new table is about to be written, before any schema or row diffs are written
BeginTable(ctx context.Context, td diff.TableDelta) error
BeginTable(fromTableName, toTableName string, isAdd, isDrop bool) error
// WriteTableSchemaDiff is called to write a schema diff for the table given (if requested by args)
WriteTableSchemaDiff(ctx context.Context, fromRoot *doltdb.RootValue, toRoot *doltdb.RootValue, td diff.TableDelta) error
WriteTableSchemaDiff(fromTableInfo, toTableInfo *diff.TableInfo, tds diff.TableDeltaSummary) error
// WriteEventDiff is called to write an event diff
WriteEventDiff(ctx context.Context, eventName, oldDefn, newDefn string) error
// WriteTriggerDiff is called to write a trigger diff
@@ -54,7 +51,7 @@ type diffWriter interface {
WriteViewDiff(ctx context.Context, viewName, oldDefn, newDefn string) error
// RowWriter returns a row writer for the table delta provided, which will have Close() called on it when rows are
// done being written.
RowWriter(ctx context.Context, td diff.TableDelta, unionSch sql.Schema) (diff.SqlRowDiffWriter, error)
RowWriter(fromTableInfo, toTableInfo *diff.TableInfo, tds diff.TableDeltaSummary, unionSch sql.Schema) (diff.SqlRowDiffWriter, error)
// Close finalizes the work of the writer
Close(ctx context.Context) error
}
@@ -73,34 +70,20 @@ func newDiffWriter(diffOutput diffOutput) (diffWriter, error) {
}
}
func printDiffStat(ctx context.Context, td diff.TableDelta, oldColLen, newColLen int) errhand.VerboseError {
// todo: use errgroup.Group
ae := atomicerr.New()
ch := make(chan diff.DiffStatProgress)
go func() {
defer close(ch)
err := diff.StatForTableDelta(ctx, ch, td)
ae.SetIfError(err)
}()
func printDiffStat(diffStats []diffStatistics, oldColLen, newColLen int, areTablesKeyless bool) errhand.VerboseError {
acc := diff.DiffStatProgress{}
var count int64
var pos int
eP := cli.NewEphemeralPrinter()
for p := range ch {
if ae.IsSet() {
break
}
acc.Adds += p.Adds
acc.Removes += p.Removes
acc.Changes += p.Changes
acc.CellChanges += p.CellChanges
acc.NewRowSize += p.NewRowSize
acc.OldRowSize += p.OldRowSize
acc.NewCellSize += p.NewCellSize
acc.OldCellSize += p.OldCellSize
for _, diffStat := range diffStats {
acc.Adds += diffStat.RowsAdded
acc.Removes += diffStat.RowsDeleted
acc.Changes += diffStat.RowsModified
acc.CellChanges += diffStat.CellsModified
acc.NewRowSize += diffStat.NewRowCount
acc.OldRowSize += diffStat.OldRowCount
acc.NewCellSize += diffStat.NewCellCount
acc.OldCellSize += diffStat.OldCellCount
if count%10000 == 0 {
eP.Printf("prev size: %d, new size: %d, adds: %d, deletes: %d, modifications: %d\n", acc.OldRowSize, acc.NewRowSize, acc.Adds, acc.Removes, acc.Changes)
@@ -112,21 +95,12 @@ func printDiffStat(ctx context.Context, td diff.TableDelta, oldColLen, newColLen
pos = cli.DeleteAndPrint(pos, "")
if err := ae.Get(); err != nil {
return errhand.BuildDError("").AddCause(err).Build()
}
keyless, err := td.IsKeyless(ctx)
if err != nil {
return errhand.BuildDError("").AddCause(err).Build()
}
if (acc.Adds+acc.Removes+acc.Changes) == 0 && (acc.OldCellSize-acc.NewCellSize) == 0 {
cli.Println("No data changes. See schema changes by using -s or --schema.")
return nil
}
if keyless {
if areTablesKeyless {
printKeylessStat(acc)
} else {
printStat(acc, oldColLen, newColLen)
@@ -195,56 +169,31 @@ func (t tabularDiffWriter) Close(ctx context.Context) error {
return nil
}
func (t tabularDiffWriter) BeginTable(ctx context.Context, td diff.TableDelta) error {
func (t tabularDiffWriter) BeginTable(fromTableName, toTableName string, isAdd, isDrop bool) error {
bold := color.New(color.Bold)
if td.IsDrop() {
_, _ = bold.Printf("diff --dolt a/%s b/%s\n", td.FromName, td.FromName)
if isDrop {
_, _ = bold.Printf("diff --dolt a/%s b/%s\n", fromTableName, fromTableName)
_, _ = bold.Println("deleted table")
} else if td.IsAdd() {
_, _ = bold.Printf("diff --dolt a/%s b/%s\n", td.ToName, td.ToName)
} else if isAdd {
_, _ = bold.Printf("diff --dolt a/%s b/%s\n", toTableName, toTableName)
_, _ = bold.Println("added table")
} else {
_, _ = bold.Printf("diff --dolt a/%s b/%s\n", td.FromName, td.ToName)
h1, err := td.FromTable.HashOf()
if err != nil {
panic(err)
}
_, _ = bold.Printf("--- a/%s @ %s\n", td.FromName, h1.String())
h2, err := td.ToTable.HashOf()
if err != nil {
panic(err)
}
_, _ = bold.Printf("+++ b/%s @ %s\n", td.ToName, h2.String())
_, _ = bold.Printf("diff --dolt a/%s b/%s\n", fromTableName, toTableName)
_, _ = bold.Printf("--- a/%s\n", fromTableName)
_, _ = bold.Printf("+++ b/%s\n", toTableName)
}
return nil
}
func (t tabularDiffWriter) WriteTableSchemaDiff(ctx context.Context, fromRoot *doltdb.RootValue, toRoot *doltdb.RootValue, td diff.TableDelta) error {
func (t tabularDiffWriter) WriteTableSchemaDiff(fromTableInfo, toTableInfo *diff.TableInfo, tds diff.TableDeltaSummary) error {
var fromCreateStmt = ""
if td.FromTable != nil {
sqlDb := sqle.NewUserSpaceDatabase(fromRoot, editor.Options{})
sqlCtx, engine, _ := sqle.PrepareCreateTableStmt(ctx, sqlDb)
var err error
fromCreateStmt, err = sqle.GetCreateTableStmt(sqlCtx, engine, td.FromName)
if err != nil {
return errhand.VerboseErrorFromError(err)
}
if fromTableInfo != nil {
fromCreateStmt = fromTableInfo.CreateStmt
}
var toCreateStmt = ""
if td.ToTable != nil {
sqlDb := sqle.NewUserSpaceDatabase(toRoot, editor.Options{})
sqlCtx, engine, _ := sqle.PrepareCreateTableStmt(ctx, sqlDb)
var err error
toCreateStmt, err = sqle.GetCreateTableStmt(sqlCtx, engine, td.ToName)
if err != nil {
return errhand.VerboseErrorFromError(err)
}
if toTableInfo != nil {
toCreateStmt = toTableInfo.CreateStmt
}
if fromCreateStmt != toCreateStmt {
@@ -252,18 +201,22 @@ func (t tabularDiffWriter) WriteTableSchemaDiff(ctx context.Context, fromRoot *d
}
resolvedFromFks := map[string]struct{}{}
for _, fk := range td.FromFks {
if len(fk.ReferencedTableColumns) > 0 {
resolvedFromFks[fk.Name] = struct{}{}
if fromTableInfo != nil {
for _, fk := range fromTableInfo.Fks {
if len(fk.ReferencedTableColumns) > 0 {
resolvedFromFks[fk.Name] = struct{}{}
}
}
}
for _, fk := range td.ToFks {
if _, ok := resolvedFromFks[fk.Name]; ok {
continue
}
if len(fk.ReferencedTableColumns) > 0 {
cli.Println(fmt.Sprintf("resolved foreign key `%s` on table `%s`", fk.Name, fk.TableName))
if toTableInfo != nil {
for _, fk := range toTableInfo.Fks {
if _, ok := resolvedFromFks[fk.Name]; ok {
continue
}
if len(fk.ReferencedTableColumns) > 0 {
cli.Println(fmt.Sprintf("resolved foreign key `%s` on table `%s`", fk.Name, fk.TableName))
}
}
}
@@ -286,7 +239,7 @@ func (t tabularDiffWriter) WriteViewDiff(ctx context.Context, viewName, oldDefn,
return nil
}
func (t tabularDiffWriter) RowWriter(ctx context.Context, td diff.TableDelta, unionSch sql.Schema) (diff.SqlRowDiffWriter, error) {
func (t tabularDiffWriter) RowWriter(fromTableInfo, toTableInfo *diff.TableInfo, tds diff.TableDeltaSummary, unionSch sql.Schema) (diff.SqlRowDiffWriter, error) {
return tabular.NewFixedWidthDiffTableWriter(unionSch, iohelp.NopWrCloser(cli.CliOut), 100), nil
}
@@ -298,17 +251,16 @@ func (s sqlDiffWriter) Close(ctx context.Context) error {
return nil
}
func (s sqlDiffWriter) BeginTable(ctx context.Context, td diff.TableDelta) error {
func (s sqlDiffWriter) BeginTable(fromTableName, toTableName string, isAdd, isDrop bool) error {
return nil
}
func (s sqlDiffWriter) WriteTableSchemaDiff(ctx context.Context, fromRoot *doltdb.RootValue, toRoot *doltdb.RootValue, td diff.TableDelta) error {
toSchemas, err := toRoot.GetAllSchemas(ctx)
if err != nil {
return errhand.BuildDError("could not read schemas from toRoot").AddCause(err).Build()
}
func (s sqlDiffWriter) WriteTableSchemaDiff(fromTableInfo, toTableInfo *diff.TableInfo, tds diff.TableDeltaSummary) error {
//for _, stmt := range tds.AlterStmts {
// cli.Println(stmt)
//}
ddlStatements, err := diff.SqlSchemaDiff(ctx, td, toSchemas)
ddlStatements, err := diff.SqlSchemaDiff(fromTableInfo, toTableInfo, tds)
if err != nil {
return errhand.VerboseErrorFromError(err)
}
@@ -362,13 +314,16 @@ func (s sqlDiffWriter) WriteViewDiff(ctx context.Context, viewName, oldDefn, new
return nil
}
func (s sqlDiffWriter) RowWriter(ctx context.Context, td diff.TableDelta, unionSch sql.Schema) (diff.SqlRowDiffWriter, error) {
targetSch := td.ToSch
func (s sqlDiffWriter) RowWriter(fromTableInfo, toTableInfo *diff.TableInfo, tds diff.TableDeltaSummary, unionSch sql.Schema) (diff.SqlRowDiffWriter, error) {
var targetSch schema.Schema
if toTableInfo != nil {
targetSch = toTableInfo.Sch
}
if targetSch == nil {
targetSch = td.FromSch
targetSch = fromTableInfo.Sch
}
return sqlexport.NewSqlDiffWriter(td.ToName, targetSch, iohelp.NopWrCloser(cli.CliOut)), nil
return sqlexport.NewSqlDiffWriter(tds.ToTableName, targetSch, iohelp.NopWrCloser(cli.CliOut)), nil
}
type jsonDiffWriter struct {
@@ -402,7 +357,7 @@ func (j *jsonDiffWriter) beginDocumentIfNecessary() error {
return nil
}
func (j *jsonDiffWriter) BeginTable(ctx context.Context, td diff.TableDelta) error {
func (j *jsonDiffWriter) BeginTable(fromTableName, toTableName string, isAdd, isDrop bool) error {
err := j.beginDocumentIfNecessary()
if err != nil {
return err
@@ -420,9 +375,9 @@ func (j *jsonDiffWriter) BeginTable(ctx context.Context, td diff.TableDelta) err
}
}
tableName := td.FromName
tableName := fromTableName
if len(tableName) == 0 {
tableName = td.ToName
tableName = toTableName
}
err = iohelp.WriteAll(j.wr, []byte(fmt.Sprintf(jsonDiffTableHeader, tableName)))
@@ -436,19 +391,21 @@ func (j *jsonDiffWriter) BeginTable(ctx context.Context, td diff.TableDelta) err
return err
}
func (j *jsonDiffWriter) WriteTableSchemaDiff(ctx context.Context, fromRoot *doltdb.RootValue, toRoot *doltdb.RootValue, td diff.TableDelta) error {
toSchemas, err := toRoot.GetAllSchemas(ctx)
if err != nil {
return errhand.BuildDError("could not read schemas from toRoot").AddCause(err).Build()
}
func (j *jsonDiffWriter) WriteTableSchemaDiff(fromTableInfo, toTableInfo *diff.TableInfo, tds diff.TableDeltaSummary) error {
//for _, stmt := range tds.AlterStmts {
// err := j.schemaDiffWriter.WriteSchemaDiff(stmt)
// if err != nil {
// return err
// }
//}
stmts, err := diff.SqlSchemaDiff(ctx, td, toSchemas)
stmts, err := diff.SqlSchemaDiff(fromTableInfo, toTableInfo, tds)
if err != nil {
return err
}
for _, stmt := range stmts {
err := j.schemaDiffWriter.WriteSchemaDiff(ctx, stmt)
err := j.schemaDiffWriter.WriteSchemaDiff(stmt)
if err != nil {
return err
}
@@ -457,7 +414,7 @@ func (j *jsonDiffWriter) WriteTableSchemaDiff(ctx context.Context, fromRoot *dol
return nil
}
func (j *jsonDiffWriter) RowWriter(ctx context.Context, td diff.TableDelta, unionSch sql.Schema) (diff.SqlRowDiffWriter, error) {
func (j *jsonDiffWriter) RowWriter(fromTableInfo, toTableInfo *diff.TableInfo, tds diff.TableDeltaSummary, unionSch sql.Schema) (diff.SqlRowDiffWriter, error) {
// close off the schema diff block, start the data block
err := iohelp.WriteAll(j.wr, []byte(jsonDiffDataDiffHeader))
if err != nil {

View File

@@ -14,353 +14,353 @@
package commands
import (
"context"
"fmt"
"regexp"
"strings"
"github.com/pkg/errors"
"github.com/dolthub/dolt/go/cmd/dolt/cli"
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
eventsapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi/v1alpha1"
"github.com/dolthub/dolt/go/libraries/doltcore/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
*diffDisplaySettings
}
var showDocs = cli.CommandDocumentationContent{
ShortDesc: `Show information about a specific commit`,
LongDesc: `Show information about a specific commit`,
Synopsis: []string{
`[{{.LessThan}}revision{{.GreaterThan}}]`,
},
}
type ShowCmd struct{}
// Name returns the name of the Dolt cli command. This is what is used on the command line to invoke the command
func (cmd ShowCmd) Name() string {
return "show"
}
// Description returns a description of the command
func (cmd ShowCmd) Description() string {
return "Show information about a specific commit."
}
// EventType returns the type of the event to log
func (cmd ShowCmd) EventType() eventsapi.ClientEventType {
return eventsapi.ClientEventType_SHOW
}
func (cmd ShowCmd) Docs() *cli.CommandDocumentation {
ap := cmd.ArgParser()
return cli.NewCommandDocumentation(showDocs, ap)
}
func (cmd ShowCmd) ArgParser() *argparser.ArgParser {
ap := argparser.NewArgParserWithVariableArgs(cmd.Name())
// 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).")
ap.SupportsFlag(SchemaFlag, "s", "Show only the schema changes, do not show the data changes (Both shown by default).")
ap.SupportsFlag(StatFlag, "", "Show stats of data changes")
ap.SupportsFlag(SummaryFlag, "", "Show summary of data and schema changes")
ap.SupportsString(FormatFlag, "r", "result output format", "How to format diff output. Valid values are tabular, sql, json. Defaults to tabular.")
ap.SupportsString(whereParam, "", "column", "filters columns based on values in the diff. See {{.EmphasisLeft}}dolt diff --help{{.EmphasisRight}} for details.")
ap.SupportsInt(limitParam, "", "record_count", "limits to the first N diffs.")
ap.SupportsFlag(cli.CachedFlag, "c", "Show only the staged data changes.")
ap.SupportsFlag(SkinnyFlag, "sk", "Shows only primary key columns and any columns with data changes.")
ap.SupportsFlag(MergeBase, "", "Uses merge base of the first commit and second commit (or HEAD if not supplied) as the first commit")
ap.SupportsString(DiffMode, "", "diff mode", "Determines how to display modified rows with tabular output. Valid values are row, line, in-place, context. Defaults to context.")
return ap
}
// Exec executes the command
func (cmd ShowCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv, cliCtx cli.CliContext) int {
ap := cmd.ArgParser()
help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, showDocs, ap))
apr := cli.ParseArgsOrDie(ap, args, help)
opts, err := parseShowArgs(ctx, dEnv, apr)
if err != nil {
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 = showObjects(ctx, dEnv, opts)
return handleErrAndExit(err)
}
func (cmd ShowCmd) validateArgs(apr *argparser.ArgParseResults) errhand.VerboseError {
if apr.Contains(StatFlag) || apr.Contains(SummaryFlag) {
if apr.Contains(SchemaFlag) || apr.Contains(DataFlag) {
return errhand.BuildDError("invalid Arguments: --stat and --summary cannot be combined with --schema or --data").Build()
}
}
f, _ := apr.GetValue(FormatFlag)
switch strings.ToLower(f) {
case "tabular", "sql", "json", "":
default:
return errhand.BuildDError("invalid output format: %s", f).Build()
}
return nil
}
func parseShowArgs(ctx context.Context, dEnv *env.DoltEnv, apr *argparser.ArgParseResults) (*showOpts, error) {
decorateOption := apr.GetValueOrDefault(cli.DecorateFlag, "auto")
switch decorateOption {
case "short", "full", "auto", "no":
default:
return nil, fmt.Errorf("fatal: invalid --decorate option: %s", decorateOption)
}
return &showOpts{
showParents: apr.Contains(cli.ParentsFlag),
pretty: !apr.Contains(cli.NoPrettyFlag),
decoration: decorateOption,
specRefs: apr.Args,
}, nil
}
func showObjects(ctx context.Context, dEnv *env.DoltEnv, opts *showOpts) error {
if len(opts.specRefs) == 0 {
headRef, err := dEnv.RepoStateReader().CWBHeadSpec()
if err != nil {
return err
}
return showCommitSpec(ctx, dEnv, opts, headRef)
}
for _, specRef := range opts.specRefs {
err := showSpecRef(ctx, dEnv, opts, specRef)
if err != nil {
return err
}
}
return nil
}
// 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
}
func showSpecRef(ctx context.Context, dEnv *env.DoltEnv, opts *showOpts, specRef string) error {
roots, err := dEnv.Roots(ctx)
if err != nil {
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 {
headRef, err := dEnv.RepoStateReader().CWBHeadRef()
if err != nil {
return err
}
commit, err := dEnv.DoltDB.Resolve(ctx, commitSpec, headRef)
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
}
meta, mErr := comm.GetCommitMeta(ctx)
if mErr != nil {
cli.PrintErrln("error: failed to get commit metadata")
return err
}
pHashes, pErr := comm.ParentHashes(ctx)
if pErr != nil {
cli.PrintErrln("error: failed to get parent hashes")
return err
}
cmHash, cErr := comm.HashOf()
if cErr != nil {
cli.PrintErrln("error: failed to get commit hash")
return err
}
headRef, err := dEnv.RepoStateReader().CWBHeadRef()
if err != nil {
return err
}
cwbHash, err := dEnv.DoltDB.GetHashForRefStr(ctx, headRef.String())
if err != nil {
return err
}
cli.ExecuteWithStdioRestored(func() {
pager := outputpager.Start()
defer pager.Stop()
PrintCommit(pager, 0, opts.showParents, opts.decoration, logNode{
commitMeta: meta,
commitHash: cmHash,
parentHashes: pHashes,
branchNames: cHashToRefs[cmHash],
isHead: cmHash == *cwbHash})
})
if comm.NumParents() == 0 {
return nil
}
if comm.NumParents() > 1 {
return fmt.Errorf("requested commit is a merge commit. 'dolt show' currently only supports viewing non-merge commits")
}
commitRoot, err := comm.GetRootValue(ctx)
if err != nil {
return err
}
parent, err := comm.GetParent(ctx, 0)
if err != nil {
return err
}
parentRoot, err := parent.GetRootValue(ctx)
if err != nil {
return err
}
parentHash, err := parent.HashOf()
if err != nil {
return err
}
datasets := &diffDatasets{
fromRoot: parentRoot,
toRoot: commitRoot,
fromRef: parentHash.String(),
toRef: cmHash.String(),
}
// An empty string will cause all tables to be printed.
var tableNames []string
tableSet, err := parseDiffTableSet(ctx, dEnv, datasets, tableNames)
if err != nil {
return err
}
dArgs := &diffArgs{
diffDisplaySettings: opts.diffDisplaySettings,
diffDatasets: datasets,
tableSet: tableSet,
}
return diffUserTables(ctx, dEnv, dArgs)
}
//import (
// "context"
// "fmt"
// "regexp"
// "strings"
//
// "github.com/pkg/errors"
//
// "github.com/dolthub/dolt/go/cmd/dolt/cli"
// "github.com/dolthub/dolt/go/cmd/dolt/errhand"
// eventsapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi/v1alpha1"
// "github.com/dolthub/dolt/go/libraries/doltcore/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
//
// *diffDisplaySettings
//}
//
//var showDocs = cli.CommandDocumentationContent{
// ShortDesc: `Show information about a specific commit`,
// LongDesc: `Show information about a specific commit`,
// Synopsis: []string{
// `[{{.LessThan}}revision{{.GreaterThan}}]`,
// },
//}
//
//type ShowCmd struct{}
//
//// Name returns the name of the Dolt cli command. This is what is used on the command line to invoke the command
//func (cmd ShowCmd) Name() string {
// return "show"
//}
//
//// Description returns a description of the command
//func (cmd ShowCmd) Description() string {
// return "Show information about a specific commit."
//}
//
//// EventType returns the type of the event to log
//func (cmd ShowCmd) EventType() eventsapi.ClientEventType {
// return eventsapi.ClientEventType_SHOW
//}
//
//func (cmd ShowCmd) Docs() *cli.CommandDocumentation {
// ap := cmd.ArgParser()
// return cli.NewCommandDocumentation(showDocs, ap)
//}
//
//func (cmd ShowCmd) ArgParser() *argparser.ArgParser {
// ap := argparser.NewArgParserWithVariableArgs(cmd.Name())
// // 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).")
// ap.SupportsFlag(SchemaFlag, "s", "Show only the schema changes, do not show the data changes (Both shown by default).")
// ap.SupportsFlag(StatFlag, "", "Show stats of data changes")
// ap.SupportsFlag(SummaryFlag, "", "Show summary of data and schema changes")
// ap.SupportsString(FormatFlag, "r", "result output format", "How to format diff output. Valid values are tabular, sql, json. Defaults to tabular.")
// ap.SupportsString(whereParam, "", "column", "filters columns based on values in the diff. See {{.EmphasisLeft}}dolt diff --help{{.EmphasisRight}} for details.")
// ap.SupportsInt(limitParam, "", "record_count", "limits to the first N diffs.")
// ap.SupportsFlag(cli.CachedFlag, "c", "Show only the staged data changes.")
// ap.SupportsFlag(SkinnyFlag, "sk", "Shows only primary key columns and any columns with data changes.")
// ap.SupportsFlag(MergeBase, "", "Uses merge base of the first commit and second commit (or HEAD if not supplied) as the first commit")
// ap.SupportsString(DiffMode, "", "diff mode", "Determines how to display modified rows with tabular output. Valid values are row, line, in-place, context. Defaults to context.")
// return ap
//}
//
//// Exec executes the command
//func (cmd ShowCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv, cliCtx cli.CliContext) int {
// ap := cmd.ArgParser()
// help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, showDocs, ap))
// apr := cli.ParseArgsOrDie(ap, args, help)
//
// opts, err := parseShowArgs(ctx, dEnv, apr)
// if err != nil {
// 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 = showObjects(ctx, dEnv, opts)
//
// return handleErrAndExit(err)
//}
//
//func (cmd ShowCmd) validateArgs(apr *argparser.ArgParseResults) errhand.VerboseError {
// if apr.Contains(StatFlag) || apr.Contains(SummaryFlag) {
// if apr.Contains(SchemaFlag) || apr.Contains(DataFlag) {
// return errhand.BuildDError("invalid Arguments: --stat and --summary cannot be combined with --schema or --data").Build()
// }
// }
//
// f, _ := apr.GetValue(FormatFlag)
// switch strings.ToLower(f) {
// case "tabular", "sql", "json", "":
// default:
// return errhand.BuildDError("invalid output format: %s", f).Build()
// }
//
// return nil
//}
//
//func parseShowArgs(ctx context.Context, dEnv *env.DoltEnv, apr *argparser.ArgParseResults) (*showOpts, error) {
//
// decorateOption := apr.GetValueOrDefault(cli.DecorateFlag, "auto")
// switch decorateOption {
// case "short", "full", "auto", "no":
// default:
// return nil, fmt.Errorf("fatal: invalid --decorate option: %s", decorateOption)
// }
//
// return &showOpts{
// showParents: apr.Contains(cli.ParentsFlag),
// pretty: !apr.Contains(cli.NoPrettyFlag),
// decoration: decorateOption,
// specRefs: apr.Args,
// }, nil
//}
//
//func showObjects(ctx context.Context, dEnv *env.DoltEnv, opts *showOpts) error {
// if len(opts.specRefs) == 0 {
// headRef, err := dEnv.RepoStateReader().CWBHeadSpec()
// if err != nil {
// return err
// }
// return showCommitSpec(ctx, dEnv, opts, headRef)
// }
//
// for _, specRef := range opts.specRefs {
// err := showSpecRef(ctx, dEnv, opts, specRef)
// if err != nil {
// return err
// }
// }
//
// return nil
//}
//
//// 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
//}
//
//func showSpecRef(ctx context.Context, dEnv *env.DoltEnv, opts *showOpts, specRef string) error {
// roots, err := dEnv.Roots(ctx)
// if err != nil {
// 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 {
//
// headRef, err := dEnv.RepoStateReader().CWBHeadRef()
// if err != nil {
// return err
// }
//
// commit, err := dEnv.DoltDB.Resolve(ctx, commitSpec, headRef)
// 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
// }
//
// meta, mErr := comm.GetCommitMeta(ctx)
// if mErr != nil {
// cli.PrintErrln("error: failed to get commit metadata")
// return err
// }
// pHashes, pErr := comm.ParentHashes(ctx)
// if pErr != nil {
// cli.PrintErrln("error: failed to get parent hashes")
// return err
// }
// cmHash, cErr := comm.HashOf()
// if cErr != nil {
// cli.PrintErrln("error: failed to get commit hash")
// return err
// }
//
// headRef, err := dEnv.RepoStateReader().CWBHeadRef()
// if err != nil {
// return err
// }
// cwbHash, err := dEnv.DoltDB.GetHashForRefStr(ctx, headRef.String())
// if err != nil {
// return err
// }
//
// cli.ExecuteWithStdioRestored(func() {
// pager := outputpager.Start()
// defer pager.Stop()
//
// PrintCommit(pager, 0, opts.showParents, opts.decoration, logNode{
// commitMeta: meta,
// commitHash: cmHash,
// parentHashes: pHashes,
// branchNames: cHashToRefs[cmHash],
// isHead: cmHash == *cwbHash})
// })
//
// if comm.NumParents() == 0 {
// return nil
// }
//
// if comm.NumParents() > 1 {
// return fmt.Errorf("requested commit is a merge commit. 'dolt show' currently only supports viewing non-merge commits")
// }
//
// commitRoot, err := comm.GetRootValue(ctx)
// if err != nil {
// return err
// }
//
// parent, err := comm.GetParent(ctx, 0)
// if err != nil {
// return err
// }
//
// parentRoot, err := parent.GetRootValue(ctx)
// if err != nil {
// return err
// }
//
// parentHash, err := parent.HashOf()
// if err != nil {
// return err
// }
//
// datasets := &diffDatasets{
// fromRoot: parentRoot,
// toRoot: commitRoot,
// fromRef: parentHash.String(),
// toRef: cmHash.String(),
// }
//
// // An empty string will cause all tables to be printed.
// var tableNames []string
//
// tableSet, err := parseDiffTableSet(ctx, dEnv, datasets, tableNames)
// if err != nil {
// return err
// }
//
// dArgs := &diffArgs{
// diffDisplaySettings: opts.diffDisplaySettings,
// diffDatasets: datasets,
// tableSet: tableSet,
// }
//
// return diffUserTables(ctx, dEnv, dArgs)
//}

View File

@@ -80,7 +80,7 @@ var doltSubCommands = []cli.Command{
sqlserver.SqlServerCmd{VersionStr: Version},
sqlserver.SqlClientCmd{VersionStr: Version},
commands.LogCmd{},
commands.ShowCmd{},
//commands.ShowCmd{},
commands.BranchCmd{},
commands.CheckoutCmd{},
commands.MergeCmd{},
@@ -121,14 +121,12 @@ var doltSubCommands = []cli.Command{
}
var commandsWithoutCliCtx = []cli.Command{
commands.DiffCmd{},
commands.ResetCmd{},
commands.CleanCmd{},
admin.Commands,
sqlserver.SqlServerCmd{VersionStr: Version},
sqlserver.SqlClientCmd{VersionStr: Version},
commands.LogCmd{},
commands.ShowCmd{},
commands.CheckoutCmd{},
cnfcmds.Commands,
commands.CloneCmd{},

View File

@@ -85,7 +85,7 @@ type SqlRowDiffWriter interface {
type SchemaDiffWriter interface {
// WriteSchemaDiff writes the schema diff given (a SQL statement) and returns any error. A single table may have
// many SQL statements for a single diff. WriteSchemaDiff will be called before any row diffs via |WriteRow|
WriteSchemaDiff(ctx context.Context, schemaDiffStatement string) error
WriteSchemaDiff(schemaDiffStatement string) error
// Close finalizes the work of this writer.
Close(ctx context.Context) error
}

View File

@@ -21,12 +21,10 @@ import (
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlfmt"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
"github.com/dolthub/dolt/go/libraries/utils/set"
"github.com/dolthub/dolt/go/store/prolly/tree"
"github.com/dolthub/dolt/go/store/types"
@@ -41,6 +39,11 @@ const (
RemovedTable
)
type TableInfo struct {
Name string
Sch schema.Schema
CreateStmt string
}
// TableDelta represents the change of a single table between two roots.
// FromFKs and ToFKs contain Foreign Keys that constrain columns in this table,
// they do not contain Foreign Keys that reference this table.
@@ -68,6 +71,24 @@ type TableDeltaSummary struct {
TableName string
FromTableName string
ToTableName string
AlterStmts []string
}
// IsAdd returns true if the table was added between the fromRoot and toRoot.
func (tds TableDeltaSummary) IsAdd() bool {
return tds.FromTableName == "" && tds.ToTableName != ""
}
// IsDrop returns true if the table was dropped between the fromRoot and toRoot.
func (tds TableDeltaSummary) IsDrop() bool {
return tds.FromTableName != "" && tds.ToTableName == ""
}
// IsRename return true if the table was renamed between the fromRoot and toRoot.
func (tds TableDeltaSummary) IsRename() bool {
if tds.IsAdd() || tds.IsDrop() {
return false
}
return tds.FromTableName != tds.ToTableName
}
// GetStagedUnstagedTableDeltas represents staged and unstaged changes as TableDelta slices.
@@ -562,27 +583,15 @@ func (td TableDelta) GetRowData(ctx context.Context) (from, to durable.Index, er
// SqlSchemaDiff returns a slice of DDL statements that will transform the schema in the from delta to the schema in
// the to delta.
func SqlSchemaDiff(ctx context.Context, td TableDelta, toSchemas map[string]schema.Schema) ([]string, error) {
fromSch, toSch, err := td.GetSchemas(ctx)
if err != nil {
return nil, fmt.Errorf("cannot retrieve schema for table %s, cause: %s", td.ToName, err.Error())
}
func SqlSchemaDiff(fromTableInfo, toTableInfo *TableInfo, tds TableDeltaSummary) ([]string, error) {
var ddlStatements []string
if td.IsDrop() {
ddlStatements = append(ddlStatements, sqlfmt.DropTableStmt(td.FromName))
} else if td.IsAdd() {
toPkSch, err := sqlutil.FromDoltSchema(td.ToName, td.ToSch)
if err != nil {
return nil, err
}
stmt, err := GenerateCreateTableStatement(td.ToName, td.ToSch, toPkSch, td.ToFks, td.ToFksParentSch)
if err != nil {
return nil, errhand.VerboseErrorFromError(err)
}
ddlStatements = append(ddlStatements, stmt)
if tds.IsDrop() {
ddlStatements = append(ddlStatements, sqlfmt.DropTableStmt(tds.FromTableName))
} else if tds.IsAdd() {
ddlStatements = append(ddlStatements, toTableInfo.CreateStmt)
} else {
stmts, err := GetNonCreateNonDropTableSqlSchemaDiff(td, toSchemas, fromSch, toSch)
stmts, err := GetNonCreateNonDropTableSqlSchemaDiff(tds, fromTableInfo, toTableInfo)
if err != nil {
return nil, err
}
@@ -593,47 +602,32 @@ func SqlSchemaDiff(ctx context.Context, td TableDelta, toSchemas map[string]sche
}
// GetNonCreateNonDropTableSqlSchemaDiff returns any schema diff in SQL statements that is NEITHER 'CREATE TABLE' NOR 'DROP TABLE' statements.
func GetNonCreateNonDropTableSqlSchemaDiff(td TableDelta, toSchemas map[string]schema.Schema, fromSch, toSch schema.Schema) ([]string, error) {
if td.IsAdd() || td.IsDrop() {
func GetNonCreateNonDropTableSqlSchemaDiff(tds TableDeltaSummary, fromTableInfo, toTableInfo *TableInfo) ([]string, error) {
if tds.IsAdd() || tds.IsDrop() {
// use add and drop specific methods
return nil, nil
}
var ddlStatements []string
if td.FromName != td.ToName {
ddlStatements = append(ddlStatements, sqlfmt.RenameTableStmt(td.FromName, td.ToName))
if tds.FromTableName != tds.ToTableName {
ddlStatements = append(ddlStatements, sqlfmt.RenameTableStmt(tds.FromTableName, tds.ToTableName))
}
fromSch := fromTableInfo.Sch
toSch := toTableInfo.Sch
eq := schema.SchemasAreEqual(fromSch, toSch)
if eq && !td.HasFKChanges() {
if eq && !hasFkChanges(fromTableInfo, toTableInfo) {
return ddlStatements, nil
}
colDiffs, unionTags := DiffSchColumns(fromSch, toSch)
for _, tag := range unionTags {
cd := colDiffs[tag]
switch cd.DiffType {
case SchDiffNone:
case SchDiffAdded:
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddColStmt(td.ToName, sqlfmt.GenerateCreateTableColumnDefinition(*cd.New)))
case SchDiffRemoved:
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropColStmt(td.ToName, cd.Old.Name))
case SchDiffModified:
// Ignore any primary key set changes here
if cd.Old.IsPartOfPK != cd.New.IsPartOfPK {
continue
}
if cd.Old.Name != cd.New.Name {
ddlStatements = append(ddlStatements, sqlfmt.AlterTableRenameColStmt(td.ToName, cd.Old.Name, cd.New.Name))
}
}
}
ddlStatements = append(ddlStatements, tds.AlterStmts...)
// Print changes between a primary key set change. It contains an ALTER TABLE DROP and an ALTER TABLE ADD
if !schema.ColCollsAreEqual(fromSch.GetPKCols(), toSch.GetPKCols()) {
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropPks(td.ToName))
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropPks(tds.ToTableName))
if toSch.GetPKCols().Size() > 0 {
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddPrimaryKeys(td.ToName, toSch.GetPKCols()))
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddPrimaryKeys(tds.ToTableName, toSch.GetPKCols().GetColumnNames()))
}
}
@@ -641,28 +635,27 @@ func GetNonCreateNonDropTableSqlSchemaDiff(td TableDelta, toSchemas map[string]s
switch idxDiff.DiffType {
case SchDiffNone:
case SchDiffAdded:
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddIndexStmt(td.ToName, idxDiff.To))
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddIndexStmt(tds.ToTableName, idxDiff.To))
case SchDiffRemoved:
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropIndexStmt(td.FromName, idxDiff.From))
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropIndexStmt(tds.FromTableName, idxDiff.From))
case SchDiffModified:
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropIndexStmt(td.FromName, idxDiff.From))
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddIndexStmt(td.ToName, idxDiff.To))
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropIndexStmt(tds.FromTableName, idxDiff.From))
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddIndexStmt(tds.ToTableName, idxDiff.To))
}
}
for _, fkDiff := range DiffForeignKeys(td.FromFks, td.ToFks) {
for _, fkDiff := range DiffForeignKeyInfos(fromTableInfo.Fks, toTableInfo.Fks) {
switch fkDiff.DiffType {
case SchDiffNone:
case SchDiffAdded:
parentSch := toSchemas[fkDiff.To.ReferencedTableName]
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddForeignKeyStmt(fkDiff.To, toSch, parentSch))
to := fkDiff.To
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddForeignKeyStmtSimple(to.TableName, to.Name, to.ReferencedTableName, to.TableColumns, to.ReferencedTableColumns))
case SchDiffRemoved:
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropForeignKeyStmt(fkDiff.From))
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropForeignKeyStmt(fkDiff.From.TableName, fkDiff.From.Name))
case SchDiffModified:
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropForeignKeyStmt(fkDiff.From))
parentSch := toSchemas[fkDiff.To.ReferencedTableName]
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddForeignKeyStmt(fkDiff.To, toSch, parentSch))
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropForeignKeyStmt(fkDiff.From.TableName, fkDiff.From.Name))
to := fkDiff.To
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddForeignKeyStmtSimple(to.TableName, to.Name, to.ReferencedTableName, to.TableColumns, to.ReferencedTableColumns))
}
}

View File

@@ -400,7 +400,7 @@ func getSchemaSqlPatch(ctx *sql.Context, toRoot *doltdb.RootValue, td diff.Table
}
ddlStatements = append(ddlStatements, stmt)
} else {
stmts, err := diff.GetNonCreateNonDropTableSqlSchemaDiff(td, toSchemas, fromSch, toSch)
stmts, err := GetNonCreateNonDropTableSqlSchemaDiff(td, toSchemas, fromSch, toSch)
if err != nil {
return nil, err
}
@@ -498,6 +498,85 @@ func getDataSqlPatchResults(ctx *sql.Context, diffQuerySch, targetSch sql.Schema
}
}
// GetNonCreateNonDropTableSqlSchemaDiff returns any schema diff in SQL statements that is NEITHER 'CREATE TABLE' NOR 'DROP TABLE' statements.
func GetNonCreateNonDropTableSqlSchemaDiff(td diff.TableDelta, toSchemas map[string]schema.Schema, fromSch, toSch schema.Schema) ([]string, error) {
if td.IsAdd() || td.IsDrop() {
// use add and drop specific methods
return nil, nil
}
var ddlStatements []string
if td.FromName != td.ToName {
ddlStatements = append(ddlStatements, sqlfmt.RenameTableStmt(td.FromName, td.ToName))
}
eq := schema.SchemasAreEqual(fromSch, toSch)
if eq && !td.HasFKChanges() {
return ddlStatements, nil
}
colDiffs, unionTags := diff.DiffSchColumns(fromSch, toSch)
for _, tag := range unionTags {
cd := colDiffs[tag]
switch cd.DiffType {
case diff.SchDiffNone:
case diff.SchDiffAdded:
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddColStmt(td.ToName, sqlfmt.GenerateCreateTableColumnDefinition(*cd.New)))
case diff.SchDiffRemoved:
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropColStmt(td.ToName, cd.Old.Name))
case diff.SchDiffModified:
// Ignore any primary key set changes here
if cd.Old.IsPartOfPK != cd.New.IsPartOfPK {
continue
}
if cd.Old.Name != cd.New.Name {
ddlStatements = append(ddlStatements, sqlfmt.AlterTableRenameColStmt(td.ToName, cd.Old.Name, cd.New.Name))
}
}
}
// Print changes between a primary key set change. It contains an ALTER TABLE DROP and an ALTER TABLE ADD
if !schema.ColCollsAreEqual(fromSch.GetPKCols(), toSch.GetPKCols()) {
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropPks(td.ToName))
if toSch.GetPKCols().Size() > 0 {
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddPrimaryKeys(td.ToName, toSch.GetPKCols().GetColumnNames()))
}
}
for _, idxDiff := range diff.DiffSchIndexes(fromSch, toSch) {
switch idxDiff.DiffType {
case diff.SchDiffNone:
case diff.SchDiffAdded:
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddIndexStmt(td.ToName, idxDiff.To))
case diff.SchDiffRemoved:
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropIndexStmt(td.FromName, idxDiff.From))
case diff.SchDiffModified:
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropIndexStmt(td.FromName, idxDiff.From))
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddIndexStmt(td.ToName, idxDiff.To))
}
}
for _, fkDiff := range diff.DiffForeignKeys(td.FromFks, td.ToFks) {
switch fkDiff.DiffType {
case diff.SchDiffNone:
case diff.SchDiffAdded:
parentSch := toSchemas[fkDiff.To.ReferencedTableName]
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddForeignKeyStmt(fkDiff.To, toSch, parentSch))
case diff.SchDiffRemoved:
from := fkDiff.From
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropForeignKeyStmt(from.TableName, from.Name))
case diff.SchDiffModified:
from := fkDiff.From
ddlStatements = append(ddlStatements, sqlfmt.AlterTableDropForeignKeyStmt(from.TableName, from.Name))
parentSch := toSchemas[fkDiff.To.ReferencedTableName]
ddlStatements = append(ddlStatements, sqlfmt.AlterTableAddForeignKeyStmt(fkDiff.To, toSch, parentSch))
}
}
return ddlStatements, nil
}
// getDiffQuery returns diff schema for specified columns and array of sql.Expression as projection to be used
// on diff table function row iter. This function attempts to imitate running a query
// fmt.Sprintf("select %s, %s from dolt_diff('%s', '%s', '%s')", columnsWithDiff, "diff_type", fromRef, toRef, tableName)

View File

@@ -148,17 +148,17 @@ func AlterTableDropPks(tableName string) string {
return b.String()
}
func AlterTableAddPrimaryKeys(tableName string, pks *schema.ColCollection) string {
func AlterTableAddPrimaryKeys(tableName string, pkColNames []string) string {
var b strings.Builder
b.WriteString("ALTER TABLE ")
b.WriteString(QuoteIdentifier(tableName))
b.WriteString(" ADD PRIMARY KEY (")
for i := 0; i < pks.Size(); i++ {
for i := 0; i < len(pkColNames); i++ {
if i == 0 {
b.WriteString(pks.GetByIndex(i).Name)
b.WriteString(pkColNames[i])
} else {
b.WriteString("," + pks.GetByIndex(i).Name)
b.WriteString("," + pkColNames[i])
}
}
b.WriteRune(')')
@@ -225,12 +225,26 @@ func AlterTableAddForeignKeyStmt(fk doltdb.ForeignKey, sch, parentSch schema.Sch
return b.String()
}
func AlterTableDropForeignKeyStmt(fk doltdb.ForeignKey) string {
func AlterTableAddForeignKeyStmtSimple(tableName, fkName, fkReferencedTableName string, fkTableColumns, fkReferencedTableColumns []string) string {
var b strings.Builder
b.WriteString("ALTER TABLE ")
b.WriteString(QuoteIdentifier(fk.TableName))
b.WriteString(QuoteIdentifier(tableName))
b.WriteString(" ADD CONSTRAINT ")
b.WriteString(QuoteIdentifier(fkName))
b.WriteString(" FOREIGN KEY ")
b.WriteString("(" + strings.Join(fkTableColumns, ",") + ")")
b.WriteString(" REFERENCES ")
b.WriteString(QuoteIdentifier(fkReferencedTableName))
b.WriteString(" (" + strings.Join(fkReferencedTableColumns, ",") + ");")
return b.String()
}
func AlterTableDropForeignKeyStmt(tableName, fkName string) string {
var b strings.Builder
b.WriteString("ALTER TABLE ")
b.WriteString(QuoteIdentifier(tableName))
b.WriteString(" DROP FOREIGN KEY ")
b.WriteString(QuoteIdentifier(fk.Name))
b.WriteString(QuoteIdentifier(fkName))
b.WriteRune(';')
return b.String()
}

View File

@@ -166,7 +166,7 @@ func NewSchemaDiffWriter(wr io.WriteCloser) (*SchemaDiffWriter, error) {
}, nil
}
func (j *SchemaDiffWriter) WriteSchemaDiff(ctx context.Context, schemaDiffStatement string) error {
func (j *SchemaDiffWriter) WriteSchemaDiff(schemaDiffStatement string) error {
if j.schemaStmtsWritten > 0 {
err := iohelp.WriteAll(j.wr, []byte(","))
if err != nil {