Merge pull request #5583 from dolthub/zachmu/diff-schema-fixes

Bug fix for view and trigger diffs in SQL and JSON diff outputs
This commit is contained in:
Zach Musgrave
2023-03-15 16:55:10 -07:00
committed by GitHub
8 changed files with 635 additions and 89 deletions

View File

@@ -22,7 +22,6 @@ import (
"strconv"
"strings"
textdiff "github.com/andreyvit/diff"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/types"
@@ -43,7 +42,6 @@ import (
type diffOutput int
type diffPart int
type diffMode int
const (
SchemaOnlyDiff diffPart = 1 // 0b0001
@@ -63,7 +61,6 @@ const (
SummaryFlag = "summary"
whereParam = "where"
limitParam = "limit"
SQLFlag = "sql"
SkinnyFlag = "skinny"
MergeBase = "merge-base"
DiffMode = "diff-mode"
@@ -526,21 +523,27 @@ func diffUserTables(ctx context.Context, dEnv *env.DoltEnv, dArgs *diffArgs) err
return errhand.VerboseErrorFromError(err)
}
doltSchemasChanged := false
for _, td := range tableDeltas {
if !dArgs.tableSet.Contains(td.FromName) && !dArgs.tableSet.Contains(td.ToName) {
if !shouldPrintTableDelta(dArgs.tableSet, td) {
continue
}
if strings.ToLower(td.ToName) != doltdb.SchemasTableName {
if isDoltSchemasTable(td) {
// save dolt_schemas table diff for last in diff output
doltSchemasChanged = true
} else {
verr := diffUserTable(sqlCtx, td, sqlEng, dArgs, dw)
if verr != nil {
return verr
}
} else {
// dolt_schemas table is treated as a special case. diff the rows of the table, and print fragments as DDL
verr := diffDoltSchemasTable(sqlCtx, td, sqlEng, dArgs, dw)
if verr != nil {
return verr
}
}
}
if doltSchemasChanged {
verr := diffDoltSchemasTable(sqlCtx, sqlEng, dArgs, dw)
if verr != nil {
return verr
}
}
@@ -552,6 +555,15 @@ func diffUserTables(ctx context.Context, dEnv *env.DoltEnv, dArgs *diffArgs) err
return nil
}
func shouldPrintTableDelta(tablesToPrint *set.StrSet, td diff.TableDelta) bool {
// TODO: this should be case insensitive
return tablesToPrint.Contains(td.FromName) || tablesToPrint.Contains(td.ToName)
}
func isDoltSchemasTable(td diff.TableDelta) bool {
return td.FromName == doltdb.SchemasTableName || td.ToName == doltdb.SchemasTableName
}
func diffUserTable(
ctx *sql.Context,
td diff.TableDelta,
@@ -581,7 +593,7 @@ func diffUserTable(
}
if dArgs.diffParts&SchemaOnlyDiff != 0 {
err := dw.WriteSchemaDiff(ctx, dArgs.toRoot, td)
err := dw.WriteTableSchemaDiff(ctx, dArgs.toRoot, td)
if err != nil {
return errhand.VerboseErrorFromError(err)
}
@@ -603,17 +615,14 @@ func diffUserTable(
func diffDoltSchemasTable(
sqlCtx *sql.Context,
td diff.TableDelta,
sqlEng *engine.SqlEngine,
dArgs *diffArgs,
dw diffWriter,
) errhand.VerboseError {
err := dw.BeginTable(sqlCtx, td)
if err != nil {
return errhand.VerboseErrorFromError(err)
}
query := fmt.Sprintf("select from_fragment,to_fragment from dolt_diff('%s','%s','%s')", dArgs.fromRef, dArgs.toRef, doltdb.SchemasTableName)
query := fmt.Sprintf("select from_name,to_name,from_type,to_type,from_fragment,to_fragment "+
"from dolt_diff('%s','%s','%s') "+
"order by coalesce(from_type, to_type), coalesce(from_name, to_name)",
dArgs.fromRef, dArgs.toRef, doltdb.SchemasTableName)
_, rowIter, err := sqlEng.Query(sqlCtx, query)
if err != nil {
@@ -629,30 +638,52 @@ func diffDoltSchemasTable(
return errhand.VerboseErrorFromError(err)
}
from := ""
var fragmentName string
if row[0] != nil {
from = fmt.Sprintf("%v;", row[0])
fragmentName = row[0].(string)
} else {
fragmentName = row[1].(string)
}
to := ""
if row[1] != nil {
to = fmt.Sprintf("%v;", row[1])
var fragmentType string
if row[2] != nil {
fragmentType = row[2].(string)
} else {
fragmentType = row[3].(string)
}
if from != to {
cli.Println(textdiff.LineDiff(from, to))
var oldFragment string
var newFragment string
if row[4] != nil {
oldFragment = row[4].(string)
// Typically schema fragements have the semicolons stripped, so put it back on
if len(oldFragment) > 0 && oldFragment[len(oldFragment)-1] != ';' {
oldFragment += ";"
}
}
if row[5] != nil {
newFragment = row[5].(string)
// Typically schema fragements have the semicolons stripped, so put it back on
if len(newFragment) > 0 && newFragment[len(newFragment)-1] != ';' {
newFragment += ";"
}
}
}
return nil
}
func writeSqlSchemaDiff(ctx context.Context, td diff.TableDelta, toSchemas map[string]schema.Schema) errhand.VerboseError {
ddlStatements, err := diff.SqlSchemaDiff(ctx, td, toSchemas)
if err != nil {
return errhand.VerboseErrorFromError(err)
}
for _, stmt := range ddlStatements {
cli.Println(stmt)
switch fragmentType {
case "view":
err := dw.WriteViewDiff(sqlCtx, fragmentName, oldFragment, newFragment)
if err != nil {
return nil
}
case "trigger":
err := dw.WriteTriggerDiff(sqlCtx, fragmentName, oldFragment, newFragment)
if err != nil {
return nil
}
default:
cli.PrintErrf("Unrecognized schema element type: %s", fragmentType)
continue
}
}
return nil

View File

@@ -16,6 +16,7 @@ package commands
import (
"context"
ejson "encoding/json"
"fmt"
"io"
@@ -42,8 +43,12 @@ import (
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
// WriteSchemaDiff is called to write a schema diff for the table given (if requested by args)
WriteSchemaDiff(ctx context.Context, toRoot *doltdb.RootValue, td diff.TableDelta) error
// WriteTableSchemaDiff is called to write a schema diff for the table given (if requested by args)
WriteTableSchemaDiff(ctx context.Context, toRoot *doltdb.RootValue, td diff.TableDelta) error
// WriteTriggerDiff is called to write a trigger diff
WriteTriggerDiff(ctx context.Context, triggerName, oldDefn, newDefn string) error
// WriteViewDiff is called to write a view diff
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)
@@ -216,7 +221,7 @@ func (t tabularDiffWriter) BeginTable(ctx context.Context, td diff.TableDelta) e
return nil
}
func (t tabularDiffWriter) WriteSchemaDiff(ctx context.Context, toRoot *doltdb.RootValue, td diff.TableDelta) error {
func (t tabularDiffWriter) WriteTableSchemaDiff(ctx context.Context, toRoot *doltdb.RootValue, td diff.TableDelta) error {
fromSch, toSch, err := td.GetSchemas(ctx)
if err != nil {
return errhand.BuildDError("cannot retrieve schema for table %s", td.ToName).AddCause(err).Build()
@@ -266,6 +271,17 @@ func (t tabularDiffWriter) WriteSchemaDiff(ctx context.Context, toRoot *doltdb.R
return nil
}
func (t tabularDiffWriter) WriteTriggerDiff(ctx context.Context, triggerName, oldDefn, newDefn string) error {
// identical implementation
return t.WriteViewDiff(ctx, triggerName, oldDefn, newDefn)
}
func (t tabularDiffWriter) WriteViewDiff(ctx context.Context, viewName, oldDefn, newDefn string) error {
diffString := textdiff.LineDiff(oldDefn, newDefn)
cli.Println(diffString)
return nil
}
func (t tabularDiffWriter) RowWriter(ctx context.Context, td diff.TableDelta, unionSch sql.Schema) (diff.SqlRowDiffWriter, error) {
return tabular.NewFixedWidthDiffTableWriter(unionSch, iohelp.NopWrCloser(cli.CliOut), 100), nil
}
@@ -282,13 +298,50 @@ func (s sqlDiffWriter) BeginTable(ctx context.Context, td diff.TableDelta) error
return nil
}
func (s sqlDiffWriter) WriteSchemaDiff(ctx context.Context, toRoot *doltdb.RootValue, td diff.TableDelta) error {
func (s sqlDiffWriter) WriteTableSchemaDiff(ctx context.Context, 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()
}
return writeSqlSchemaDiff(ctx, td, toSchemas)
ddlStatements, err := diff.SqlSchemaDiff(ctx, td, toSchemas)
if err != nil {
return errhand.VerboseErrorFromError(err)
}
for _, stmt := range ddlStatements {
cli.Println(stmt)
}
return nil
}
func (s sqlDiffWriter) WriteTriggerDiff(ctx context.Context, triggerName, oldDefn, newDefn string) error {
// definitions will already be semicolon terminated, no need to add additional ones
if oldDefn == "" {
cli.Println(newDefn)
} else if newDefn == "" {
cli.Println(fmt.Sprintf("DROP TRIGGER %s;", sql.QuoteIdentifier(triggerName)))
} else {
cli.Println(fmt.Sprintf("DROP TRIGGER %s;", sql.QuoteIdentifier(triggerName)))
cli.Println(newDefn)
}
return nil
}
func (s sqlDiffWriter) WriteViewDiff(ctx context.Context, viewName, oldDefn, newDefn string) error {
// definitions will already be semicolon terminated, no need to add additional ones
if oldDefn == "" {
cli.Println(newDefn)
} else if newDefn == "" {
cli.Println(fmt.Sprintf("DROP VIEW %s;", sql.QuoteIdentifier(viewName)))
} else {
cli.Println(fmt.Sprintf("DROP VIEW %s;", sql.QuoteIdentifier(viewName)))
cli.Println(newDefn)
}
return nil
}
func (s sqlDiffWriter) RowWriter(ctx context.Context, td diff.TableDelta, unionSch sql.Schema) (diff.SqlRowDiffWriter, error) {
@@ -301,11 +354,12 @@ func (s sqlDiffWriter) RowWriter(ctx context.Context, td diff.TableDelta, unionS
}
type jsonDiffWriter struct {
wr io.WriteCloser
schemaDiffWriter diff.SchemaDiffWriter
rowDiffWriter diff.SqlRowDiffWriter
schemaDiffsWritten int
tablesWritten int
wr io.WriteCloser
schemaDiffWriter diff.SchemaDiffWriter
rowDiffWriter diff.SqlRowDiffWriter
tablesWritten int
triggersWritten int
viewsWritten int
}
var _ diffWriter = (*tabularDiffWriter)(nil)
@@ -316,12 +370,27 @@ func newJsonDiffWriter(wr io.WriteCloser) (*jsonDiffWriter, error) {
}, nil
}
const tablesHeader = `"tables":[`
const jsonDiffTableHeader = `{"name":"%s","schema_diff":`
const jsonDiffFooter = `}]}`
const jsonDiffDataDiffHeader = `],"data_diff":[`
const jsonDataDiffFooter = `}]`
func (j *jsonDiffWriter) beginDocumentIfNecessary() error {
if j.tablesWritten == 0 && j.triggersWritten == 0 && j.viewsWritten == 0 {
_, err := j.wr.Write([]byte("{"))
return err
}
return nil
}
func (j *jsonDiffWriter) BeginTable(ctx context.Context, td diff.TableDelta) error {
if j.schemaDiffWriter == nil {
err := iohelp.WriteAll(j.wr, []byte(`{"tables":[`))
err := j.beginDocumentIfNecessary()
if err != nil {
return err
}
if j.tablesWritten == 0 {
err := iohelp.WriteAll(j.wr, []byte(tablesHeader))
if err != nil {
return err
}
@@ -332,7 +401,12 @@ func (j *jsonDiffWriter) BeginTable(ctx context.Context, td diff.TableDelta) err
}
}
err := iohelp.WriteAll(j.wr, []byte(fmt.Sprintf(jsonDiffTableHeader, td.ToName)))
tableName := td.FromName
if len(tableName) == 0 {
tableName = td.ToName
}
err = iohelp.WriteAll(j.wr, []byte(fmt.Sprintf(jsonDiffTableHeader, tableName)))
if err != nil {
return err
}
@@ -343,7 +417,7 @@ func (j *jsonDiffWriter) BeginTable(ctx context.Context, td diff.TableDelta) err
return err
}
func (j *jsonDiffWriter) WriteSchemaDiff(ctx context.Context, toRoot *doltdb.RootValue, td diff.TableDelta) error {
func (j *jsonDiffWriter) WriteTableSchemaDiff(ctx context.Context, 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()
@@ -366,7 +440,7 @@ func (j *jsonDiffWriter) WriteSchemaDiff(ctx context.Context, toRoot *doltdb.Roo
func (j *jsonDiffWriter) RowWriter(ctx context.Context, td diff.TableDelta, unionSch sql.Schema) (diff.SqlRowDiffWriter, error) {
// close off the schema diff block, start the data block
err := iohelp.WriteAll(j.wr, []byte(`],"data_diff":[`))
err := iohelp.WriteAll(j.wr, []byte(jsonDiffDataDiffHeader))
if err != nil {
return nil, err
}
@@ -390,9 +464,133 @@ func (j *jsonDiffWriter) RowWriter(ctx context.Context, td diff.TableDelta, unio
return j.rowDiffWriter, err
}
func (j *jsonDiffWriter) WriteTriggerDiff(ctx context.Context, triggerName, oldDefn, newDefn string) error {
err := j.beginDocumentIfNecessary()
if err != nil {
return err
}
if j.triggersWritten == 0 {
// end the table if necessary
if j.tablesWritten > 0 {
_, err := j.wr.Write([]byte(jsonDataDiffFooter + ","))
if err != nil {
return err
}
}
_, err := j.wr.Write([]byte(`"triggers":[`))
if err != nil {
return err
}
} else {
_, err := j.wr.Write([]byte(","))
if err != nil {
return err
}
}
triggerNameBytes, err := ejson.Marshal(triggerName)
if err != nil {
return err
}
oldDefnBytes, err := ejson.Marshal(oldDefn)
if err != nil {
return err
}
newDefnBytes, err := ejson.Marshal(newDefn)
if err != nil {
return err
}
_, err = j.wr.Write([]byte(fmt.Sprintf(`{"name":%s,"from_definition":%s,"to_definition":%s}`,
triggerNameBytes, oldDefnBytes, newDefnBytes)))
if err != nil {
return err
}
j.triggersWritten++
return nil
}
func (j *jsonDiffWriter) WriteViewDiff(ctx context.Context, viewName, oldDefn, newDefn string) error {
err := j.beginDocumentIfNecessary()
if err != nil {
return err
}
if j.viewsWritten == 0 {
// end the previous block if necessary
if j.tablesWritten > 0 && j.triggersWritten == 0 {
_, err := j.wr.Write([]byte(jsonDataDiffFooter + ","))
if err != nil {
return err
}
} else if j.triggersWritten > 0 {
_, err := j.wr.Write([]byte("],"))
if err != nil {
return err
}
}
}
if j.viewsWritten == 0 {
_, err := j.wr.Write([]byte(`"views":[`))
if err != nil {
return err
}
} else {
_, err := j.wr.Write([]byte(","))
if err != nil {
return err
}
}
viewNameBytes, err := ejson.Marshal(viewName)
if err != nil {
return err
}
oldDefnBytes, err := ejson.Marshal(oldDefn)
if err != nil {
return err
}
newDefnBytes, err := ejson.Marshal(newDefn)
if err != nil {
return err
}
_, err = j.wr.Write([]byte(fmt.Sprintf(`{"name":%s,"from_definition":%s,"to_definition":%s}`,
viewNameBytes, oldDefnBytes, newDefnBytes)))
if err != nil {
return err
}
j.viewsWritten++
return nil
}
func (j *jsonDiffWriter) Close(ctx context.Context) error {
if j.tablesWritten > 0 {
err := iohelp.WriteLine(j.wr, jsonDiffFooter)
if j.tablesWritten > 0 || j.triggersWritten > 0 || j.viewsWritten > 0 {
// We only need to close off the "tables" array if we didn't also write a view / trigger
// (which also closes that array)
if j.triggersWritten == 0 && j.viewsWritten == 0 {
_, err := j.wr.Write([]byte(jsonDataDiffFooter))
if err != nil {
return err
}
} else {
// if we did write a trigger or view, we need to close off that array
_, err := j.wr.Write([]byte("]"))
if err != nil {
return err
}
}
err := iohelp.WriteLine(j.wr, "}")
if err != nil {
return err
}

View File

@@ -159,6 +159,14 @@ func GetTableDeltas(ctx context.Context, fromRoot, toRoot *doltdb.RootValue) (de
return nil, err
}
// Make sure we always return the same order of deltas
sort.Slice(deltas, func(i, j int) bool {
if deltas[i].FromName == deltas[j].FromName {
return deltas[i].ToName < deltas[j].ToName
}
return deltas[i].FromName < deltas[j].FromName
})
return deltas, nil
}
@@ -552,7 +560,6 @@ func fkSlicesAreEqual(from, to []doltdb.ForeignKey) bool {
// SqlSchemaDiff returns a slice of DDL statements that will transform the schema in the from delta to the schema in
// the to delta.
// TODO: this doesn't handle constraints or triggers
func SqlSchemaDiff(ctx context.Context, td TableDelta, toSchemas map[string]schema.Schema) ([]string, error) {
fromSch, toSch, err := td.GetSchemas(ctx)
if err != nil {

View File

@@ -93,11 +93,13 @@ SQL
run dolt ls --all
[ "$status" -eq 0 ]
[[ "$output" =~ "dolt_schemas" ]]
[[ "$output" =~ "dolt_schemas" ]] || false
dolt diff
run dolt diff
[ "$status" -eq 0 ]
[[ "$output" =~ "dolt_schemas" ]]
[[ "$output" =~ "-create view four as select 2+2 as res from dual" ]] || false
[[ ! "$output" =~ "dolt_schemas" ]] || false
dolt commit -Am "dropped a view"
dolt sql -q "drop view six"
@@ -105,11 +107,12 @@ SQL
# Dropping all views should result in the dolt_schemas table deleting itself
run dolt ls --all
[ "$status" -eq 0 ]
[[ ! "$output" =~ "dolt_schemas" ]]
[[ ! "$output" =~ "dolt_schemas" ]] || false
run dolt diff
[ "$status" -eq 0 ]
[[ "$output" =~ "deleted table" ]]
[[ "$output" =~ "-create view six as select 3+3 as res from dual" ]] || false
[[ ! "$output" =~ "deleted table" ]] || false
dolt commit -Am "no views left"

View File

@@ -0,0 +1 @@
{"tables":[{"name":"test","schema_diff":[],"data_diff":[{"from_row":{"c1":5,"c2":6,"pk":4},"to_row":{}},{"from_row":{},"to_row":{"c1":8,"c2":9,"pk":7}}]}],"triggers":[{"name":"tr1","from_definition":"","to_definition":"create trigger tr1 before insert on test for each row set new.c1 = new.c1 + 1;"}],"views":[{"name":"v1","from_definition":"","to_definition":"create view v1 as select \"hello\" from test;"}]}

View File

@@ -120,6 +120,161 @@ EOF
[[ "$output" =~ "$EXPECTED" ]] || false
}
@test "json-diff: views" {
dolt sql <<SQL
drop table test;
create table test (pk int primary key, c1 int, c2 int);
call dolt_add('.');
insert into test values (1,2,3);
insert into test values (4,5,6);
SQL
dolt commit -am "First commit"
dolt sql <<SQL
create view v1 as select * from test;
SQL
dolt commit -Am "Second commit"
dolt diff -r json HEAD HEAD~
run dolt diff -r json HEAD HEAD~
EXPECTED=$(cat <<'EOF'
{"views":[{"name":"v1","from_definition":"create view v1 as select * from test;","to_definition":""}]}
EOF
)
[ "$status" -eq 0 ]
[[ "$output" =~ "$EXPECTED" ]] || false
dolt diff -r json HEAD~ HEAD
run dolt diff -r json HEAD~ HEAD
EXPECTED=$(cat <<'EOF'
{"views":[{"name":"v1","from_definition":"","to_definition":"create view v1 as select * from test;"}]}
EOF
)
[ "$status" -eq 0 ]
[[ "$output" =~ "$EXPECTED" ]] || false
dolt sql <<SQL
drop view v1;
create view v1 as select "one" from dual;
SQL
dolt commit -Am "redefined view"
dolt diff -r json HEAD~ HEAD
run dolt diff -r json HEAD~ HEAD
EXPECTED=$(cat <<'EOF'
{"views":[{"name":"v1","from_definition":"create view v1 as select * from test;","to_definition":"create view v1 as select \"one\" from dual;"}]}
EOF
)
[ "$status" -eq 0 ]
[[ "$output" =~ "$EXPECTED" ]] || false
}
@test "json-diff: views, triggers, tables" {
dolt sql <<SQL
drop table test;
create table test (pk int primary key, c1 int, c2 int);
call dolt_add('.');
insert into test values (1,2,3);
insert into test values (4,5,6);
SQL
dolt commit -am "Table with rows"
dolt sql <<SQL
insert into test values (7,8,9);
delete from test where pk = 4;
SQL
dolt commit -Am "Table data diff"
dolt sql <<SQL
create view v1 as select "hello" from test;
SQL
dolt commit -Am "View"
dolt sql <<SQL
create trigger tr1 before insert on test for each row set new.c1 = new.c1 + 1;
SQL
dolt commit -Am "Trigger"
# Only table data diff
dolt diff -r json HEAD~3 HEAD~2
run dolt diff -r json HEAD~3 HEAD~2
EXPECTED=$(cat <<'EOF'
{"tables":[{"name":"test","schema_diff":[],"data_diff":[{"from_row":{"c1":5,"c2":6,"pk":4},"to_row":{}},{"from_row":{},"to_row":{"c1":8,"c2":9,"pk":7}}]}]}
EOF
)
[ "$status" -eq 0 ]
[[ "$output" =~ "$EXPECTED" ]] || false
# Only view diff
dolt diff -r json HEAD~2 HEAD~
run dolt diff -r json HEAD~2 HEAD~
EXPECTED=$(cat <<'EOF'
{"views":[{"name":"v1","from_definition":"","to_definition":"create view v1 as select \"hello\" from test;"}]}
EOF
)
[ "$status" -eq 0 ]
[[ "$output" =~ "$EXPECTED" ]] || false
# Only trigger diff
dolt diff -r json HEAD~ HEAD
run dolt diff -r json HEAD~ HEAD
EXPECTED=$(cat <<'EOF'
{"triggers":[{"name":"tr1","from_definition":"","to_definition":"create trigger tr1 before insert on test for each row set new.c1 = new.c1 + 1;"}]}
EOF
)
[ "$status" -eq 0 ]
[[ "$output" =~ "$EXPECTED" ]] || false
# View and trigger diff
dolt diff -r json HEAD~2 HEAD
run dolt diff -r json HEAD~2 HEAD
EXPECTED=$(cat <<'EOF'
{"triggers":[{"name":"tr1","from_definition":"","to_definition":"create trigger tr1 before insert on test for each row set new.c1 = new.c1 + 1;"}],"views":[{"name":"v1","from_definition":"","to_definition":"create view v1 as select \"hello\" from test;"}]}
EOF
)
[ "$status" -eq 0 ]
[[ "$output" =~ "$EXPECTED" ]] || false
# Table and view diff
dolt diff -r json HEAD~3 HEAD~
run dolt diff -r json HEAD~3 HEAD~
EXPECTED=$(cat <<'EOF'
{"tables":[{"name":"test","schema_diff":[],"data_diff":[{"from_row":{"c1":5,"c2":6,"pk":4},"to_row":{}},{"from_row":{},"to_row":{"c1":8,"c2":9,"pk":7}}]}],"views":[{"name":"v1","from_definition":"","to_definition":"create view v1 as select \"hello\" from test;"}]}
EOF
)
[ "$status" -eq 0 ]
[[ "$output" =~ "$EXPECTED" ]] || false
# All three kinds of diff
dolt diff -r json HEAD~3 HEAD
run dolt diff -r json HEAD~3 HEAD
EXPECTED=$(cat <<'EOF'
{"tables":[{"name":"test","schema_diff":[],"data_diff":[{"from_row":{"c1":5,"c2":6,"pk":4},"to_row":{}},{"from_row":{},"to_row":{"c1":8,"c2":9,"pk":7}}]}],"triggers":[{"name":"tr1","from_definition":"","to_definition":"create trigger tr1 before insert on test for each row set new.c1 = new.c1 + 1;"}],"views":[{"name":"v1","from_definition":"","to_definition":"create view v1 as select \"hello\" from test;"}]}
EOF
)
[ "$status" -eq 0 ]
[[ "$output" =~ "$EXPECTED" ]] || false
}
@test "json-diff: with table args" {
dolt sql -q 'create table other (pk int not null primary key)'
dolt add .

View File

@@ -713,3 +713,154 @@ SQL
[ "$status" -eq 0 ]
[[ "$output" =~ 'INSERT INTO `test` (`pk`,`c1`) VALUES (0,NULL)' ]] || false
}
@test "sql-diff: views, triggers, tables" {
dolt sql <<SQL
create table test (pk int primary key, c1 int, c2 int);
call dolt_add('.');
insert into test values (1,2,3);
insert into test values (4,5,6);
SQL
dolt commit -am "Table with rows"
dolt sql <<SQL
insert into test values (7,8,9);
delete from test where pk = 4;
SQL
dolt commit -Am "Table data diff"
dolt sql <<SQL
create view v1 as select "hello" from test;
SQL
dolt commit -Am "View"
dolt sql <<SQL
create trigger tr1 before insert on test for each row set new.c1 = new.c1 + 1;
SQL
dolt commit -Am "Trigger"
# Only table data diff
dolt diff -r sql HEAD~3 HEAD~2
run dolt diff -r sql HEAD~3 HEAD~2
[ "$status" -eq 0 ]
cat > expected <<'EOF'
DELETE FROM `test` WHERE `pk`=4;
INSERT INTO `test` (`pk`,`c1`,`c2`) VALUES (7,8,9);
EOF
# We can't do direct bash comparisons because of newlines, so use diff to compare
# Diff returns non-zero unless empty
cat > actual <<EOF
${output}
EOF
diff -w expected actual
# Only view diff
dolt diff -r sql HEAD~2 HEAD~
run dolt diff -r sql HEAD~2 HEAD~
[ "$status" -eq 0 ]
cat > expected <<'EOF'
create view v1 as select "hello" from test;
EOF
cat > actual <<EOF
${output}
EOF
diff -w expected actual
# Only trigger diff
dolt diff -r sql HEAD~ HEAD
run dolt diff -r sql HEAD~ HEAD
[ "$status" -eq 0 ]
cat > expected <<'EOF'
create trigger tr1 before insert on test for each row set new.c1 = new.c1 + 1;
EOF
cat > actual <<EOF
${output}
EOF
diff -w expected actual
# View and trigger diff
dolt diff -r sql HEAD~2 HEAD
run dolt diff -r sql HEAD~2 HEAD
[ "$status" -eq 0 ]
cat > expected <<EOF
create trigger tr1 before insert on test for each row set new.c1 = new.c1 + 1;
create view v1 as select "hello" from test;
EOF
cat > actual <<EOF
${output}
EOF
diff -w expected actual
# Table and view diff
dolt diff -r sql HEAD~3 HEAD~
run dolt diff -r sql HEAD~3 HEAD~
[ "$status" -eq 0 ]
cat > expected <<'EOF'
DELETE FROM `test` WHERE `pk`=4;
INSERT INTO `test` (`pk`,`c1`,`c2`) VALUES (7,8,9);
create view v1 as select "hello" from test;
EOF
cat > actual <<EOF
${output}
EOF
diff -w expected actual
# All three kinds of diff
dolt diff -r sql HEAD~3 HEAD
run dolt diff -r sql HEAD~3 HEAD
[ "$status" -eq 0 ]
cat > expected <<'EOF'
DELETE FROM `test` WHERE `pk`=4;
INSERT INTO `test` (`pk`,`c1`,`c2`) VALUES (7,8,9);
create trigger tr1 before insert on test for each row set new.c1 = new.c1 + 1;
create view v1 as select "hello" from test;
EOF
cat > actual <<EOF
${output}
EOF
diff -w expected actual
# check alterations of triggers and views
dolt sql <<SQL
drop trigger tr1;
drop view v1;
create trigger tr1 before insert on test for each row set new.c1 = new.c1 + 100;
create view v1 as select "goodbye" from test;
SQL
dolt commit -am "new view and trigger defs"
dolt diff -r sql HEAD~ HEAD
run dolt diff -r sql HEAD~ HEAD
[ "$status" -eq 0 ]
cat > expected <<'EOF'
DROP TRIGGER `tr1`;
create trigger tr1 before insert on test for each row set new.c1 = new.c1 + 100;
DROP VIEW `v1`;
create view v1 as select "goodbye" from test;
EOF
cat > actual <<EOF
${output}
EOF
diff -w expected actual
}

View File

@@ -68,6 +68,20 @@ export const diffTests = [
q: "SELECT * FROM dolt_diff_stat(:fromRefName, :toRefName)",
p: { fromRefName: "HEAD", toRefName: "WORKING" },
res: [
{
table_name: "dolt_schemas",
rows_unmodified: 0,
rows_added: 1,
rows_deleted: 0,
rows_modified: 0,
cells_added: 4,
cells_deleted: 0,
cells_modified: 0,
old_row_count: 0,
new_row_count: 1,
old_cell_count: 0,
new_cell_count: 4,
},
{
table_name: "test",
rows_unmodified: 2,
@@ -96,20 +110,6 @@ export const diffTests = [
old_cell_count: 3,
new_cell_count: 0,
},
{
table_name: "dolt_schemas",
rows_unmodified: 0,
rows_added: 1,
rows_deleted: 0,
rows_modified: 0,
cells_added: 4,
cells_deleted: 0,
cells_modified: 0,
old_row_count: 0,
new_row_count: 1,
old_cell_count: 0,
new_cell_count: 4,
},
],
},
{
@@ -306,6 +306,20 @@ export const diffTests = [
q: "SELECT * FROM dolt_diff_stat(:refRange)",
p: { refRange: "main...HEAD" },
res: [
{
table_name: "dolt_schemas",
rows_unmodified: 0,
rows_added: 1,
rows_deleted: 0,
rows_modified: 0,
cells_added: 4,
cells_deleted: 0,
cells_modified: 0,
old_row_count: 0,
new_row_count: 1,
old_cell_count: 0,
new_cell_count: 4,
},
{
table_name: "test",
rows_unmodified: 2,
@@ -334,20 +348,6 @@ export const diffTests = [
old_cell_count: 3,
new_cell_count: 0,
},
{
table_name: "dolt_schemas",
rows_unmodified: 0,
rows_added: 1,
rows_deleted: 0,
rows_modified: 0,
cells_added: 4,
cells_deleted: 0,
cells_modified: 0,
old_row_count: 0,
new_row_count: 1,
old_cell_count: 0,
new_cell_count: 4,
},
],
},
{