mirror of
https://github.com/dolthub/dolt.git
synced 2026-03-14 11:10:40 -05:00
Merge remote-tracking branch 'origin/main' into andy/cli-diff-fix
This commit is contained in:
@@ -312,12 +312,6 @@ func CreateLogArgParser() *argparser.ArgParser {
|
||||
return ap
|
||||
}
|
||||
|
||||
func CreatePatchArgParser() *argparser.ArgParser {
|
||||
ap := argparser.NewArgParser()
|
||||
ap.SupportsFlag(CachedFlag, "c", "Show only the staged data changes.")
|
||||
return ap
|
||||
}
|
||||
|
||||
func CreateGCArgParser() *argparser.ArgParser {
|
||||
ap := argparser.NewArgParser()
|
||||
ap.SupportsFlag(ShallowFlag, "s", "perform a fast, but incomplete garbage collection pass")
|
||||
|
||||
@@ -27,9 +27,12 @@ import (
|
||||
|
||||
var stashClearDocs = cli.CommandDocumentationContent{
|
||||
ShortDesc: "Remove all the stash entries.",
|
||||
LongDesc: `
|
||||
Removes all the stash entries from the current stash list.`,
|
||||
Synopsis: []string{},
|
||||
LongDesc: `Removes all the stash entries from the current stash list. This command cannot be reverted and stash entries may not be recoverable.
|
||||
|
||||
This command does not apply the stash on current working directory, use 'dolt stash pop' to apply a stash on current working directory.`,
|
||||
Synopsis: []string{
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
type StashClearCmd struct{}
|
||||
|
||||
@@ -30,8 +30,9 @@ import (
|
||||
|
||||
var stashDropDocs = cli.CommandDocumentationContent{
|
||||
ShortDesc: "Remove a single stash entry.",
|
||||
LongDesc: `
|
||||
Removes a single stash entry at given index from the list of stash entries.`,
|
||||
LongDesc: `Removes a single stash entry at given index from the list of stash entries (e.g. 'dolt stash drop stash@{1}' will drop the stash entry at index 1 in the stash list).
|
||||
|
||||
This command does not apply the stash on current working directory, use 'dolt stash pop' to apply a stash on current working directory.`,
|
||||
Synopsis: []string{
|
||||
"{{.LessThan}}stash{{.GreaterThan}}",
|
||||
},
|
||||
|
||||
@@ -30,7 +30,9 @@ var stashListDocs = cli.CommandDocumentationContent{
|
||||
ShortDesc: "List the stash entries that you currently have.",
|
||||
LongDesc: `Each stash entry is listed with its name (e.g. stash@{0} is the latest entry, stash@{1} is the one before, etc.), the name of the branch that was current when the entry was made, and a short description of the commit the entry was based on.
|
||||
`,
|
||||
Synopsis: []string{},
|
||||
Synopsis: []string{
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
type StashListCmd struct{}
|
||||
|
||||
@@ -33,9 +33,9 @@ import (
|
||||
|
||||
var stashPopDocs = cli.CommandDocumentationContent{
|
||||
ShortDesc: "Remove a single stash from the stash list and apply it on top of the current working set.",
|
||||
LongDesc: `Applying the state can fail with conflicts; in this case, it is not removed from the stash list.
|
||||
LongDesc: `Apply a single stash at given index and drop that stash entry from the stash list (e.g. 'dolt stash pop stash@{1}' will apply and drop the stash entry at index 1 in the stash list).
|
||||
|
||||
You need to resolve the conflicts by hand and call dolt stash drop manually afterwards.
|
||||
Applying the stash entry can fail with conflicts; in this case, the stash entry is not removed from the stash list. You need to resolve the conflicts by hand and call dolt stash drop manually afterwards.
|
||||
`,
|
||||
Synopsis: []string{
|
||||
"{{.LessThan}}stash{{.GreaterThan}}",
|
||||
|
||||
@@ -48,7 +48,7 @@ var stashDocs = cli.CommandDocumentationContent{
|
||||
ShortDesc: "Stash the changes in a dirty working directory away.",
|
||||
LongDesc: `Use dolt stash when you want to record the current state of the working directory and the index, but want to go back to a clean working directory.
|
||||
|
||||
The command saves your local modifications away and reverts the working directory to match the HEAD commit.
|
||||
The command saves your local modifications away and reverts the working directory to match the HEAD commit. The stash entries that are saved away can be listed with 'dolt stash list'.
|
||||
`,
|
||||
Synopsis: []string{
|
||||
"", // this is for `dolt stash` itself.
|
||||
|
||||
@@ -57,7 +57,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
Version = "0.75.0"
|
||||
Version = "0.75.1"
|
||||
)
|
||||
|
||||
var dumpDocsCommand = &commands.DumpDocsCmd{}
|
||||
|
||||
@@ -58,7 +58,7 @@ require (
|
||||
github.com/cenkalti/backoff/v4 v4.1.3
|
||||
github.com/cespare/xxhash v1.1.0
|
||||
github.com/creasty/defaults v1.6.0
|
||||
github.com/dolthub/go-mysql-server v0.14.1-0.20230309222831-79f7895fa81a
|
||||
github.com/dolthub/go-mysql-server v0.14.1-0.20230309205536-f029801c3ad5
|
||||
github.com/google/flatbuffers v2.0.6+incompatible
|
||||
github.com/jmoiron/sqlx v1.3.4
|
||||
github.com/kch42/buzhash v0.0.0-20160816060738-9bdec3dec7c6
|
||||
|
||||
@@ -166,8 +166,8 @@ github.com/dolthub/flatbuffers v1.13.0-dh.1 h1:OWJdaPep22N52O/0xsUevxJ6Qfw1M2txC
|
||||
github.com/dolthub/flatbuffers v1.13.0-dh.1/go.mod h1:CorYGaDmXjHz1Z7i50PYXG1Ricn31GcA2wNOTFIQAKE=
|
||||
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.14.1-0.20230309222831-79f7895fa81a h1:tKPTlwMU0IfFbGX5tErzE9GvtDWgPxO5gpAzoRxJUIA=
|
||||
github.com/dolthub/go-mysql-server v0.14.1-0.20230309222831-79f7895fa81a/go.mod h1:DoBsD6F/N7+CXZrK3jp8zpKsj0jnMflgmaht98EDFos=
|
||||
github.com/dolthub/go-mysql-server v0.14.1-0.20230309205536-f029801c3ad5 h1:N2mkUkaaM7z5c6ru2SgyvQsexDOoSpaC5hnShKv81yk=
|
||||
github.com/dolthub/go-mysql-server v0.14.1-0.20230309205536-f029801c3ad5/go.mod h1:DoBsD6F/N7+CXZrK3jp8zpKsj0jnMflgmaht98EDFos=
|
||||
github.com/dolthub/ishell v0.0.0-20221214210346-d7db0b066488 h1:0HHu0GWJH0N6a6keStrHhUAK5/o9LVfkh44pvsV4514=
|
||||
github.com/dolthub/ishell v0.0.0-20221214210346-d7db0b066488/go.mod h1:ehexgi1mPxRTk0Mok/pADALuHbvATulTh6gzr7NzZto=
|
||||
github.com/dolthub/jsonpath v0.0.0-20210609232853-d49537a30474 h1:xTrR+l5l+1Lfq0NvhiEsctylXinUMFhhsqaEcl414p8=
|
||||
|
||||
@@ -567,13 +567,13 @@ func SqlSchemaDiff(ctx context.Context, td TableDelta, toSchemas map[string]sche
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stmt, err := generateCreateTableStatement(td.ToName, td.ToSch, toPkSch, td.ToFks, td.ToFksParentSch)
|
||||
stmt, err := GenerateCreateTableStatement(td.ToName, td.ToSch, toPkSch, td.ToFks, td.ToFksParentSch)
|
||||
if err != nil {
|
||||
return nil, errhand.VerboseErrorFromError(err)
|
||||
}
|
||||
ddlStatements = append(ddlStatements, stmt)
|
||||
} else {
|
||||
stmts, err := getNonCreateNonDropTableSqlSchemaDiff(td, toSchemas, fromSch, toSch)
|
||||
stmts, err := GetNonCreateNonDropTableSqlSchemaDiff(td, toSchemas, fromSch, toSch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -583,7 +583,8 @@ func SqlSchemaDiff(ctx context.Context, td TableDelta, toSchemas map[string]sche
|
||||
return ddlStatements, nil
|
||||
}
|
||||
|
||||
func getNonCreateNonDropTableSqlSchemaDiff(td TableDelta, toSchemas map[string]schema.Schema, fromSch, toSch schema.Schema) ([]string, error) {
|
||||
// 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() {
|
||||
// use add and drop specific methods
|
||||
return nil, nil
|
||||
@@ -659,6 +660,7 @@ func getNonCreateNonDropTableSqlSchemaDiff(td TableDelta, toSchemas map[string]s
|
||||
return ddlStatements, nil
|
||||
}
|
||||
|
||||
// GetDataDiffStatement returns any data diff in SQL statements for given table including INSERT, UPDATE and DELETE row statements.
|
||||
func GetDataDiffStatement(tableName string, sch schema.Schema, row sql.Row, rowDiffType ChangeType, colDiffTypes []ChangeType) (string, error) {
|
||||
if len(row) != len(colDiffTypes) {
|
||||
return "", fmt.Errorf("expected the same size for columns and diff types, got %d and %d", len(row), len(colDiffTypes))
|
||||
@@ -685,12 +687,12 @@ func GetDataDiffStatement(tableName string, sch schema.Schema, row sql.Row, rowD
|
||||
}
|
||||
}
|
||||
|
||||
// generateCreateTableStatement returns CREATE TABLE statement for given table. This function was made to share the same
|
||||
// GenerateCreateTableStatement returns CREATE TABLE statement for given table. This function was made to share the same
|
||||
// 'create table' statement logic as GMS. We initially were running `SHOW CREATE TABLE` query to get the statement;
|
||||
// however, it cannot be done for cases that need this statement in sql shell mode. Dolt uses its own Schema and
|
||||
// Column and other object types which are not directly compatible with GMS, so we try to use as much shared logic
|
||||
// as possible with GMS to get 'create table' statement in Dolt.
|
||||
func generateCreateTableStatement(tblName string, sch schema.Schema, pkSchema sql.PrimaryKeySchema, fks []doltdb.ForeignKey, fksParentSch map[string]schema.Schema) (string, error) {
|
||||
func GenerateCreateTableStatement(tblName string, sch schema.Schema, pkSchema sql.PrimaryKeySchema, fks []doltdb.ForeignKey, fksParentSch map[string]schema.Schema) (string, error) {
|
||||
sqlSch := pkSchema.Schema
|
||||
colStmts := make([]string, len(sqlSch))
|
||||
|
||||
|
||||
13
go/libraries/doltcore/env/actions/checkout.go
vendored
13
go/libraries/doltcore/env/actions/checkout.go
vendored
@@ -49,14 +49,21 @@ func MoveTablesFromHeadToWorking(ctx context.Context, roots doltdb.Roots, tbls [
|
||||
var unknownTbls []string
|
||||
for _, tblName := range tbls {
|
||||
tbl, ok, err := roots.Staged.GetTable(ctx, tblName)
|
||||
|
||||
if err != nil {
|
||||
return doltdb.Roots{}, err
|
||||
}
|
||||
fkc, err := roots.Staged.GetForeignKeyCollection(ctx)
|
||||
if err != nil {
|
||||
return doltdb.Roots{}, err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
tbl, ok, err = roots.Head.GetTable(ctx, tblName)
|
||||
if err != nil {
|
||||
return doltdb.Roots{}, err
|
||||
}
|
||||
|
||||
fkc, err = roots.Head.GetForeignKeyCollection(ctx)
|
||||
if err != nil {
|
||||
return doltdb.Roots{}, err
|
||||
}
|
||||
@@ -68,7 +75,11 @@ func MoveTablesFromHeadToWorking(ctx context.Context, roots doltdb.Roots, tbls [
|
||||
}
|
||||
|
||||
roots.Working, err = roots.Working.PutTable(ctx, tblName, tbl)
|
||||
if err != nil {
|
||||
return doltdb.Roots{}, err
|
||||
}
|
||||
|
||||
roots.Working, err = roots.Working.PutForeignKeyCollection(ctx, fkc)
|
||||
if err != nil {
|
||||
return doltdb.Roots{}, err
|
||||
}
|
||||
|
||||
@@ -16,20 +16,10 @@ package schema
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// EnableSpatialIndex enables the creation and use of spatial indexes
|
||||
var EnableSpatialIndex = false
|
||||
|
||||
func init() {
|
||||
if v := os.Getenv("DOLT_ENABLE_SPATIAL_INDEX"); v != "" {
|
||||
EnableSpatialIndex = true
|
||||
}
|
||||
}
|
||||
|
||||
type IndexCollection interface {
|
||||
// AddIndex adds the given index, overwriting any current indexes with the same name or columns.
|
||||
// It does not perform any kind of checking, and is intended for schema modifications.
|
||||
@@ -184,9 +174,6 @@ func (ixc *indexCollectionImpl) AddIndexByColTags(indexName string, tags []uint6
|
||||
|
||||
// validateColumnIndexable returns an error if the column given cannot be used in an index
|
||||
func validateColumnIndexable(c Column) error {
|
||||
if !EnableSpatialIndex && IsColSpatialType(c) {
|
||||
return fmt.Errorf("cannot create an index over spatial type columns")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -536,7 +536,7 @@ func resolveAsOfTime(ctx *sql.Context, ddb *doltdb.DoltDB, head ref.DoltRef, asO
|
||||
func resolveAsOfCommitRef(ctx *sql.Context, ddb *doltdb.DoltDB, head ref.DoltRef, commitRef string) (*doltdb.Commit, *doltdb.RootValue, error) {
|
||||
if commitRef == doltdb.Working || commitRef == doltdb.Staged {
|
||||
sess := dsess.DSessFromSess(ctx.Session)
|
||||
root, _, err := sess.ResolveRootForRef(ctx, ctx.GetCurrentDatabase(), commitRef)
|
||||
root, _, _, err := sess.ResolveRootForRef(ctx, ctx.GetCurrentDatabase(), commitRef)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -951,6 +951,9 @@ func (p DoltDatabaseProvider) TableFunction(_ *sql.Context, name string) (sql.Ta
|
||||
case "dolt_log":
|
||||
dtf := &LogTableFunction{}
|
||||
return dtf, nil
|
||||
case "dolt_patch":
|
||||
dtf := &PatchTableFunction{}
|
||||
return dtf, nil
|
||||
}
|
||||
|
||||
return nil, sql.ErrTableFunctionNotFound.New(name)
|
||||
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/diff"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dtables"
|
||||
)
|
||||
|
||||
@@ -80,8 +79,9 @@ func (ds *DiffStatTableFunction) Database() sql.Database {
|
||||
|
||||
// WithDatabase implements the sql.Databaser interface
|
||||
func (ds *DiffStatTableFunction) WithDatabase(database sql.Database) (sql.Node, error) {
|
||||
ds.database = database
|
||||
return ds, nil
|
||||
nds := *ds
|
||||
nds.database = database
|
||||
return &nds, nil
|
||||
}
|
||||
|
||||
// Name implements the sql.TableFunction interface
|
||||
@@ -200,46 +200,47 @@ func (ds *DiffStatTableFunction) WithExpressions(expression ...sql.Expression) (
|
||||
}
|
||||
}
|
||||
|
||||
newDstf := *ds
|
||||
if strings.Contains(expression[0].String(), "..") {
|
||||
if len(expression) < 1 || len(expression) > 2 {
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(ds.Name(), "1 or 2", len(expression))
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(newDstf.Name(), "1 or 2", len(expression))
|
||||
}
|
||||
ds.dotCommitExpr = expression[0]
|
||||
newDstf.dotCommitExpr = expression[0]
|
||||
if len(expression) == 2 {
|
||||
ds.tableNameExpr = expression[1]
|
||||
newDstf.tableNameExpr = expression[1]
|
||||
}
|
||||
} else {
|
||||
if len(expression) < 2 || len(expression) > 3 {
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(ds.Name(), "2 or 3", len(expression))
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(newDstf.Name(), "2 or 3", len(expression))
|
||||
}
|
||||
ds.fromCommitExpr = expression[0]
|
||||
ds.toCommitExpr = expression[1]
|
||||
newDstf.fromCommitExpr = expression[0]
|
||||
newDstf.toCommitExpr = expression[1]
|
||||
if len(expression) == 3 {
|
||||
ds.tableNameExpr = expression[2]
|
||||
newDstf.tableNameExpr = expression[2]
|
||||
}
|
||||
}
|
||||
|
||||
// validate the expressions
|
||||
if ds.dotCommitExpr != nil {
|
||||
if !types.IsText(ds.dotCommitExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(ds.Name(), ds.dotCommitExpr.String())
|
||||
if newDstf.dotCommitExpr != nil {
|
||||
if !types.IsText(newDstf.dotCommitExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(newDstf.Name(), newDstf.dotCommitExpr.String())
|
||||
}
|
||||
} else {
|
||||
if !types.IsText(ds.fromCommitExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(ds.Name(), ds.fromCommitExpr.String())
|
||||
if !types.IsText(newDstf.fromCommitExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(newDstf.Name(), newDstf.fromCommitExpr.String())
|
||||
}
|
||||
if !types.IsText(ds.toCommitExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(ds.Name(), ds.toCommitExpr.String())
|
||||
if !types.IsText(newDstf.toCommitExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(newDstf.Name(), newDstf.toCommitExpr.String())
|
||||
}
|
||||
}
|
||||
|
||||
if ds.tableNameExpr != nil {
|
||||
if !types.IsText(ds.tableNameExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(ds.Name(), ds.tableNameExpr.String())
|
||||
if newDstf.tableNameExpr != nil {
|
||||
if !types.IsText(newDstf.tableNameExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(newDstf.Name(), newDstf.tableNameExpr.String())
|
||||
}
|
||||
}
|
||||
|
||||
return ds, nil
|
||||
return &newDstf, nil
|
||||
}
|
||||
|
||||
// RowIter implements the sql.Node interface
|
||||
@@ -254,23 +255,12 @@ func (ds *DiffStatTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.Row
|
||||
return nil, fmt.Errorf("unexpected database type: %T", ds.database)
|
||||
}
|
||||
|
||||
fromCommitStr, toCommitStr, err := loadCommitStrings(ctx, fromCommitVal, toCommitVal, dotCommitVal, sqledb)
|
||||
fromRefDetails, toRefDetails, err := loadDetailsForRefs(ctx, fromCommitVal, toCommitVal, dotCommitVal, sqledb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sess := dsess.DSessFromSess(ctx.Session)
|
||||
fromRoot, _, err := sess.ResolveRootForRef(ctx, sqledb.Name(), fromCommitStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
toRoot, _, err := sess.ResolveRootForRef(ctx, sqledb.Name(), toCommitStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deltas, err := diff.GetTableDeltas(ctx, fromRoot, toRoot)
|
||||
deltas, err := diff.GetTableDeltas(ctx, fromRefDetails.root, toRefDetails.root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -278,7 +268,7 @@ func (ds *DiffStatTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.Row
|
||||
// If tableNameExpr defined, return a single table diff stat result
|
||||
if ds.tableNameExpr != nil {
|
||||
delta := findMatchingDelta(deltas, tableName)
|
||||
diffStat, hasDiff, err := getDiffStatNodeFromDelta(ctx, delta, fromRoot, toRoot, tableName)
|
||||
diffStat, hasDiff, err := getDiffStatNodeFromDelta(ctx, delta, fromRefDetails.root, toRefDetails.root, tableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -294,7 +284,7 @@ func (ds *DiffStatTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.Row
|
||||
if tblName == "" {
|
||||
tblName = delta.FromName
|
||||
}
|
||||
diffStat, hasDiff, err := getDiffStatNodeFromDelta(ctx, delta, fromRoot, toRoot, tblName)
|
||||
diffStat, hasDiff, err := getDiffStatNodeFromDelta(ctx, delta, fromRefDetails.root, toRefDetails.root, tblName)
|
||||
if err != nil {
|
||||
if errors.Is(err, diff.ErrPrimaryKeySetChanged) {
|
||||
ctx.Warn(dtables.PrimaryKeyChangeWarningCode, fmt.Sprintf("stat for table %s cannot be determined. Primary key set changed.", tblName))
|
||||
|
||||
@@ -70,8 +70,9 @@ func (ds *DiffSummaryTableFunction) Database() sql.Database {
|
||||
|
||||
// WithDatabase implements the sql.Databaser interface
|
||||
func (ds *DiffSummaryTableFunction) WithDatabase(database sql.Database) (sql.Node, error) {
|
||||
ds.database = database
|
||||
return ds, nil
|
||||
nds := *ds
|
||||
nds.database = database
|
||||
return &nds, nil
|
||||
}
|
||||
|
||||
// Name implements the sql.TableFunction interface
|
||||
@@ -190,46 +191,47 @@ func (ds *DiffSummaryTableFunction) WithExpressions(expression ...sql.Expression
|
||||
}
|
||||
}
|
||||
|
||||
newDstf := *ds
|
||||
if strings.Contains(expression[0].String(), "..") {
|
||||
if len(expression) < 1 || len(expression) > 2 {
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(ds.Name(), "1 or 2", len(expression))
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(newDstf.Name(), "1 or 2", len(expression))
|
||||
}
|
||||
ds.dotCommitExpr = expression[0]
|
||||
newDstf.dotCommitExpr = expression[0]
|
||||
if len(expression) == 2 {
|
||||
ds.tableNameExpr = expression[1]
|
||||
newDstf.tableNameExpr = expression[1]
|
||||
}
|
||||
} else {
|
||||
if len(expression) < 2 || len(expression) > 3 {
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(ds.Name(), "2 or 3", len(expression))
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(newDstf.Name(), "2 or 3", len(expression))
|
||||
}
|
||||
ds.fromCommitExpr = expression[0]
|
||||
ds.toCommitExpr = expression[1]
|
||||
newDstf.fromCommitExpr = expression[0]
|
||||
newDstf.toCommitExpr = expression[1]
|
||||
if len(expression) == 3 {
|
||||
ds.tableNameExpr = expression[2]
|
||||
newDstf.tableNameExpr = expression[2]
|
||||
}
|
||||
}
|
||||
|
||||
// validate the expressions
|
||||
if ds.dotCommitExpr != nil {
|
||||
if !types.IsText(ds.dotCommitExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(ds.Name(), ds.dotCommitExpr.String())
|
||||
if newDstf.dotCommitExpr != nil {
|
||||
if !types.IsText(newDstf.dotCommitExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(newDstf.Name(), newDstf.dotCommitExpr.String())
|
||||
}
|
||||
} else {
|
||||
if !types.IsText(ds.fromCommitExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(ds.Name(), ds.fromCommitExpr.String())
|
||||
if !types.IsText(newDstf.fromCommitExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(newDstf.Name(), newDstf.fromCommitExpr.String())
|
||||
}
|
||||
if !types.IsText(ds.toCommitExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(ds.Name(), ds.toCommitExpr.String())
|
||||
if !types.IsText(newDstf.toCommitExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(newDstf.Name(), newDstf.toCommitExpr.String())
|
||||
}
|
||||
}
|
||||
|
||||
if ds.tableNameExpr != nil {
|
||||
if !types.IsText(ds.tableNameExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(ds.Name(), ds.tableNameExpr.String())
|
||||
if newDstf.tableNameExpr != nil {
|
||||
if !types.IsText(newDstf.tableNameExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(newDstf.Name(), newDstf.tableNameExpr.String())
|
||||
}
|
||||
}
|
||||
|
||||
return ds, nil
|
||||
return &newDstf, nil
|
||||
}
|
||||
|
||||
// RowIter implements the sql.Node interface
|
||||
|
||||
@@ -75,9 +75,9 @@ func (dtf *DiffTableFunction) Database() sql.Database {
|
||||
|
||||
// WithDatabase implements the sql.Databaser interface
|
||||
func (dtf *DiffTableFunction) WithDatabase(database sql.Database) (sql.Node, error) {
|
||||
dtf.database = database
|
||||
|
||||
return dtf, nil
|
||||
ndtf := *dtf
|
||||
ndtf.database = database
|
||||
return &ndtf, nil
|
||||
}
|
||||
|
||||
// Expressions implements the sql.Expressioner interface
|
||||
@@ -111,32 +111,33 @@ func (dtf *DiffTableFunction) WithExpressions(expression ...sql.Expression) (sql
|
||||
}
|
||||
}
|
||||
|
||||
newDtf := *dtf
|
||||
if strings.Contains(expression[0].String(), "..") {
|
||||
if len(expression) != 2 {
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(fmt.Sprintf("%v with .. or ...", dtf.Name()), 2, len(expression))
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(fmt.Sprintf("%v with .. or ...", newDtf.Name()), 2, len(expression))
|
||||
}
|
||||
dtf.dotCommitExpr = expression[0]
|
||||
dtf.tableNameExpr = expression[1]
|
||||
newDtf.dotCommitExpr = expression[0]
|
||||
newDtf.tableNameExpr = expression[1]
|
||||
} else {
|
||||
if len(expression) != 3 {
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(dtf.Name(), 3, len(expression))
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(newDtf.Name(), 3, len(expression))
|
||||
}
|
||||
dtf.fromCommitExpr = expression[0]
|
||||
dtf.toCommitExpr = expression[1]
|
||||
dtf.tableNameExpr = expression[2]
|
||||
newDtf.fromCommitExpr = expression[0]
|
||||
newDtf.toCommitExpr = expression[1]
|
||||
newDtf.tableNameExpr = expression[2]
|
||||
}
|
||||
|
||||
fromCommitVal, toCommitVal, dotCommitVal, tableName, err := dtf.evaluateArguments()
|
||||
fromCommitVal, toCommitVal, dotCommitVal, tableName, err := newDtf.evaluateArguments()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = dtf.generateSchema(dtf.ctx, fromCommitVal, toCommitVal, dotCommitVal, tableName)
|
||||
err = newDtf.generateSchema(newDtf.ctx, fromCommitVal, toCommitVal, dotCommitVal, tableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dtf, nil
|
||||
return &newDtf, nil
|
||||
}
|
||||
|
||||
// Children implements the sql.Node interface
|
||||
@@ -206,16 +207,19 @@ func loadDetailsForRefs(ctx *sql.Context, fromRef, toRef, dotRef interface{}, db
|
||||
}
|
||||
|
||||
sess := dsess.DSessFromSess(ctx.Session)
|
||||
dbName := db.Name()
|
||||
|
||||
fromDetails, err := resolveRoot(ctx, sess, db.Name(), fromCommitStr)
|
||||
fromRoot, fromCommitTime, fromHashStr, err := sess.ResolveRootForRef(ctx, dbName, fromCommitStr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
fromDetails := &refDetails{fromRoot, fromHashStr, fromCommitTime}
|
||||
|
||||
toDetails, err := resolveRoot(ctx, sess, db.Name(), toCommitStr)
|
||||
toRoot, toCommitTime, toHashStr, err := sess.ResolveRootForRef(ctx, dbName, toCommitStr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
toDetails := &refDetails{toRoot, toHashStr, toCommitTime}
|
||||
|
||||
return fromDetails, toDetails, nil
|
||||
}
|
||||
@@ -297,7 +301,7 @@ func interfaceToString(r interface{}) (string, error) {
|
||||
}
|
||||
|
||||
func resolveRoot(ctx *sql.Context, sess *dsess.DoltSession, dbName, hashStr string) (*refDetails, error) {
|
||||
root, commitTime, err := sess.ResolveRootForRef(ctx, dbName, hashStr)
|
||||
root, commitTime, _, err := sess.ResolveRootForRef(ctx, dbName, hashStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -76,8 +76,9 @@ func (ltf *LogTableFunction) Database() sql.Database {
|
||||
|
||||
// WithDatabase implements the sql.Databaser interface
|
||||
func (ltf *LogTableFunction) WithDatabase(database sql.Database) (sql.Node, error) {
|
||||
ltf.database = database
|
||||
return ltf, nil
|
||||
nltf := *ltf
|
||||
nltf.database = database
|
||||
return &nltf, nil
|
||||
}
|
||||
|
||||
// Name implements the sql.TableFunction interface
|
||||
@@ -259,7 +260,8 @@ func (ltf *LogTableFunction) WithExpressions(expression ...sql.Expression) (sql.
|
||||
}
|
||||
}
|
||||
|
||||
if err := ltf.addOptions(expression); err != nil {
|
||||
newLtf := *ltf
|
||||
if err := newLtf.addOptions(expression); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -272,22 +274,22 @@ func (ltf *LogTableFunction) WithExpressions(expression ...sql.Expression) (sql.
|
||||
}
|
||||
|
||||
if len(filteredExpressions) > 2 {
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(ltf.Name(), "0 to 2", len(filteredExpressions))
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(newLtf.Name(), "0 to 2", len(filteredExpressions))
|
||||
}
|
||||
|
||||
exLen := len(filteredExpressions)
|
||||
if exLen > 0 {
|
||||
ltf.revisionExpr = filteredExpressions[0]
|
||||
newLtf.revisionExpr = filteredExpressions[0]
|
||||
}
|
||||
if exLen == 2 {
|
||||
ltf.secondRevisionExpr = filteredExpressions[1]
|
||||
newLtf.secondRevisionExpr = filteredExpressions[1]
|
||||
}
|
||||
|
||||
if err := ltf.validateRevisionExpressions(); err != nil {
|
||||
if err := newLtf.validateRevisionExpressions(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ltf, nil
|
||||
return &newLtf, nil
|
||||
}
|
||||
|
||||
func (ltf *LogTableFunction) invalidArgDetailsErr(expr sql.Expression, reason string) *errors.Error {
|
||||
|
||||
679
go/libraries/doltcore/sqle/dolt_patch_table_function.go
Normal file
679
go/libraries/doltcore/sqle/dolt_patch_table_function.go
Normal file
@@ -0,0 +1,679 @@
|
||||
// Copyright 2023 Dolthub, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sqle
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/expression"
|
||||
"github.com/dolthub/go-mysql-server/sql/plan"
|
||||
sqltypes "github.com/dolthub/go-mysql-server/sql/types"
|
||||
"github.com/dolthub/vitess/go/mysql"
|
||||
|
||||
"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/env"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dtables"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlfmt"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
|
||||
)
|
||||
|
||||
var _ sql.TableFunction = (*PatchTableFunction)(nil)
|
||||
|
||||
type PatchTableFunction struct {
|
||||
ctx *sql.Context
|
||||
|
||||
fromCommitExpr sql.Expression
|
||||
toCommitExpr sql.Expression
|
||||
dotCommitExpr sql.Expression
|
||||
tableNameExpr sql.Expression
|
||||
database sql.Database
|
||||
}
|
||||
|
||||
var patchTableSchema = sql.Schema{
|
||||
&sql.Column{Name: "statement_order", Type: sqltypes.Uint64, PrimaryKey: true, Nullable: false},
|
||||
&sql.Column{Name: "from_commit_hash", Type: sqltypes.LongText, Nullable: false},
|
||||
&sql.Column{Name: "to_commit_hash", Type: sqltypes.LongText, Nullable: false},
|
||||
&sql.Column{Name: "table_name", Type: sqltypes.LongText, Nullable: false},
|
||||
&sql.Column{Name: "diff_type", Type: sqltypes.LongText, Nullable: false},
|
||||
&sql.Column{Name: "statement", Type: sqltypes.LongText, Nullable: false},
|
||||
}
|
||||
|
||||
// NewInstance creates a new instance of TableFunction interface
|
||||
func (p *PatchTableFunction) NewInstance(ctx *sql.Context, db sql.Database, exprs []sql.Expression) (sql.Node, error) {
|
||||
newInstance := &PatchTableFunction{
|
||||
ctx: ctx,
|
||||
database: db,
|
||||
}
|
||||
|
||||
node, err := newInstance.WithExpressions(exprs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return node, nil
|
||||
}
|
||||
|
||||
// Resolved implements the sql.Resolvable interface
|
||||
func (p *PatchTableFunction) Resolved() bool {
|
||||
if p.tableNameExpr != nil {
|
||||
return p.commitsResolved() && p.tableNameExpr.Resolved()
|
||||
}
|
||||
return p.commitsResolved()
|
||||
}
|
||||
|
||||
func (p *PatchTableFunction) commitsResolved() bool {
|
||||
if p.dotCommitExpr != nil {
|
||||
return p.dotCommitExpr.Resolved()
|
||||
}
|
||||
return p.fromCommitExpr.Resolved() && p.toCommitExpr.Resolved()
|
||||
}
|
||||
|
||||
// String implements the Stringer interface
|
||||
func (p *PatchTableFunction) String() string {
|
||||
if p.dotCommitExpr != nil {
|
||||
if p.tableNameExpr != nil {
|
||||
return fmt.Sprintf("DOLT_PATCH(%s, %s)", p.dotCommitExpr.String(), p.tableNameExpr.String())
|
||||
}
|
||||
return fmt.Sprintf("DOLT_PATCH(%s)", p.dotCommitExpr.String())
|
||||
}
|
||||
if p.tableNameExpr != nil {
|
||||
return fmt.Sprintf("DOLT_PATCH(%s, %s, %s)", p.fromCommitExpr.String(), p.toCommitExpr.String(), p.tableNameExpr.String())
|
||||
}
|
||||
return fmt.Sprintf("DOLT_PATCH(%s, %s)", p.fromCommitExpr.String(), p.toCommitExpr.String())
|
||||
}
|
||||
|
||||
// Schema implements the sql.Node interface.
|
||||
func (p *PatchTableFunction) Schema() sql.Schema {
|
||||
return patchTableSchema
|
||||
}
|
||||
|
||||
// Children implements the sql.Node interface.
|
||||
func (p *PatchTableFunction) Children() []sql.Node {
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithChildren implements the sql.Node interface.
|
||||
func (p *PatchTableFunction) WithChildren(children ...sql.Node) (sql.Node, error) {
|
||||
if len(children) != 0 {
|
||||
return nil, fmt.Errorf("unexpected children")
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// CheckPrivileges implements the interface sql.Node.
|
||||
func (p *PatchTableFunction) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool {
|
||||
if p.tableNameExpr != nil {
|
||||
if !sqltypes.IsText(p.tableNameExpr.Type()) {
|
||||
return false
|
||||
}
|
||||
|
||||
tableNameVal, err := p.tableNameExpr.Eval(p.ctx, nil)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
tableName, ok := tableNameVal.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return opChecker.UserHasPrivileges(ctx,
|
||||
sql.NewPrivilegedOperation(p.database.Name(), tableName, "", sql.PrivilegeType_Select))
|
||||
}
|
||||
|
||||
tblNames, err := p.database.GetTableNames(ctx)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
var operations []sql.PrivilegedOperation
|
||||
for _, tblName := range tblNames {
|
||||
operations = append(operations, sql.NewPrivilegedOperation(p.database.Name(), tblName, "", sql.PrivilegeType_Select))
|
||||
}
|
||||
|
||||
return opChecker.UserHasPrivileges(ctx, operations...)
|
||||
}
|
||||
|
||||
// Expressions implements the sql.Expressioner interface.
|
||||
func (p *PatchTableFunction) Expressions() []sql.Expression {
|
||||
exprs := []sql.Expression{}
|
||||
if p.dotCommitExpr != nil {
|
||||
exprs = append(exprs, p.dotCommitExpr)
|
||||
} else {
|
||||
exprs = append(exprs, p.fromCommitExpr, p.toCommitExpr)
|
||||
}
|
||||
if p.tableNameExpr != nil {
|
||||
exprs = append(exprs, p.tableNameExpr)
|
||||
}
|
||||
return exprs
|
||||
}
|
||||
|
||||
// WithExpressions implements the sql.Expressioner interface.
|
||||
func (p *PatchTableFunction) WithExpressions(expression ...sql.Expression) (sql.Node, error) {
|
||||
if len(expression) < 1 {
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(p.Name(), "1 to 3", len(expression))
|
||||
}
|
||||
|
||||
for _, expr := range expression {
|
||||
if !expr.Resolved() {
|
||||
return nil, ErrInvalidNonLiteralArgument.New(p.Name(), expr.String())
|
||||
}
|
||||
// prepared statements resolve functions beforehand, so above check fails
|
||||
if _, ok := expr.(sql.FunctionExpression); ok {
|
||||
return nil, ErrInvalidNonLiteralArgument.New(p.Name(), expr.String())
|
||||
}
|
||||
}
|
||||
|
||||
newPtf := *p
|
||||
if strings.Contains(expression[0].String(), "..") {
|
||||
if len(expression) < 1 || len(expression) > 2 {
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(newPtf.Name(), "1 or 2", len(expression))
|
||||
}
|
||||
newPtf.dotCommitExpr = expression[0]
|
||||
if len(expression) == 2 {
|
||||
newPtf.tableNameExpr = expression[1]
|
||||
}
|
||||
} else {
|
||||
if len(expression) < 2 || len(expression) > 3 {
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(newPtf.Name(), "2 or 3", len(expression))
|
||||
}
|
||||
newPtf.fromCommitExpr = expression[0]
|
||||
newPtf.toCommitExpr = expression[1]
|
||||
if len(expression) == 3 {
|
||||
newPtf.tableNameExpr = expression[2]
|
||||
}
|
||||
}
|
||||
|
||||
// validate the expressions
|
||||
if newPtf.dotCommitExpr != nil {
|
||||
if !sqltypes.IsText(newPtf.dotCommitExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(newPtf.Name(), newPtf.dotCommitExpr.String())
|
||||
}
|
||||
} else {
|
||||
if !sqltypes.IsText(newPtf.fromCommitExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(newPtf.Name(), newPtf.fromCommitExpr.String())
|
||||
}
|
||||
if !sqltypes.IsText(newPtf.toCommitExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(newPtf.Name(), newPtf.toCommitExpr.String())
|
||||
}
|
||||
}
|
||||
|
||||
if newPtf.tableNameExpr != nil {
|
||||
if !sqltypes.IsText(newPtf.tableNameExpr.Type()) {
|
||||
return nil, sql.ErrInvalidArgumentDetails.New(newPtf.Name(), newPtf.tableNameExpr.String())
|
||||
}
|
||||
}
|
||||
|
||||
return &newPtf, nil
|
||||
}
|
||||
|
||||
// Database implements the sql.Databaser interface
|
||||
func (p *PatchTableFunction) Database() sql.Database {
|
||||
return p.database
|
||||
}
|
||||
|
||||
// WithDatabase implements the sql.Databaser interface
|
||||
func (p *PatchTableFunction) WithDatabase(database sql.Database) (sql.Node, error) {
|
||||
np := *p
|
||||
np.database = database
|
||||
return &np, nil
|
||||
}
|
||||
|
||||
// Name implements the sql.TableFunction interface
|
||||
func (p *PatchTableFunction) Name() string {
|
||||
return "dolt_patch"
|
||||
}
|
||||
|
||||
// RowIter implements the sql.Node interface
|
||||
func (p *PatchTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.RowIter, error) {
|
||||
fromCommitVal, toCommitVal, dotCommitVal, tableName, err := p.evaluateArguments()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sqledb, ok := p.database.(SqlDatabase)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to get dolt database")
|
||||
}
|
||||
|
||||
fromRefDetails, toRefDetails, err := loadDetailsForRefs(ctx, fromCommitVal, toCommitVal, dotCommitVal, sqledb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tableDeltas, err := diff.GetTableDeltas(ctx, fromRefDetails.root, toRefDetails.root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Slice(tableDeltas, func(i, j int) bool {
|
||||
return strings.Compare(tableDeltas[i].ToName, tableDeltas[j].ToName) < 0
|
||||
})
|
||||
|
||||
// If tableNameExpr defined, return a single table patch result
|
||||
if p.tableNameExpr != nil {
|
||||
fromTblExists, err := fromRefDetails.root.HasTable(ctx, tableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toTblExists, err := toRefDetails.root.HasTable(ctx, tableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !fromTblExists && !toTblExists {
|
||||
return nil, sql.ErrTableNotFound.New(tableName)
|
||||
}
|
||||
|
||||
delta := findMatchingDelta(tableDeltas, tableName)
|
||||
tableDeltas = []diff.TableDelta{delta}
|
||||
}
|
||||
|
||||
patches, err := getPatchNodes(ctx, sqledb.DbData(), tableDeltas, fromRefDetails, toRefDetails)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newPatchTableFunctionRowIter(patches, fromRefDetails.hashStr, toRefDetails.hashStr), nil
|
||||
}
|
||||
|
||||
// evaluateArguments returns fromCommitVal, toCommitVal, dotCommitVal, and tableName.
|
||||
// It evaluates the argument expressions to turn them into values this PatchTableFunction
|
||||
// can use. Note that this method only evals the expressions, and doesn't validate the values.
|
||||
func (p *PatchTableFunction) evaluateArguments() (interface{}, interface{}, interface{}, string, error) {
|
||||
var tableName string
|
||||
if p.tableNameExpr != nil {
|
||||
tableNameVal, err := p.tableNameExpr.Eval(p.ctx, nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, "", err
|
||||
}
|
||||
tn, ok := tableNameVal.(string)
|
||||
if !ok {
|
||||
return nil, nil, nil, "", ErrInvalidTableName.New(p.tableNameExpr.String())
|
||||
}
|
||||
tableName = tn
|
||||
}
|
||||
|
||||
if p.dotCommitExpr != nil {
|
||||
dotCommitVal, err := p.dotCommitExpr.Eval(p.ctx, nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, "", err
|
||||
}
|
||||
|
||||
return nil, nil, dotCommitVal, tableName, nil
|
||||
}
|
||||
|
||||
fromCommitVal, err := p.fromCommitExpr.Eval(p.ctx, nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, "", err
|
||||
}
|
||||
|
||||
toCommitVal, err := p.toCommitExpr.Eval(p.ctx, nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, "", err
|
||||
}
|
||||
|
||||
return fromCommitVal, toCommitVal, nil, tableName, nil
|
||||
}
|
||||
|
||||
type patchNode struct {
|
||||
tblName string
|
||||
schemaPatchStmts []string
|
||||
dataPatchStmts []string
|
||||
}
|
||||
|
||||
func getPatchNodes(ctx *sql.Context, dbData env.DbData, tableDeltas []diff.TableDelta, fromRefDetails, toRefDetails *refDetails) ([]*patchNode, error) {
|
||||
var patches []*patchNode
|
||||
for _, td := range tableDeltas {
|
||||
// no diff
|
||||
if td.FromTable == nil && td.ToTable == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
tblName := td.ToName
|
||||
if td.IsDrop() {
|
||||
tblName = td.FromName
|
||||
}
|
||||
|
||||
// Get SCHEMA DIFF
|
||||
schemaStmts, err := getSchemaSqlPatch(ctx, toRefDetails.root, td)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get DATA DIFF
|
||||
var dataStmts []string
|
||||
if canGetDataDiff(ctx, td) {
|
||||
dataStmts, err = getUserTableDataSqlPatch(ctx, dbData, td, fromRefDetails, toRefDetails)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
patches = append(patches, &patchNode{tblName: tblName, schemaPatchStmts: schemaStmts, dataPatchStmts: dataStmts})
|
||||
}
|
||||
|
||||
return patches, nil
|
||||
}
|
||||
|
||||
func getSchemaSqlPatch(ctx *sql.Context, toRoot *doltdb.RootValue, td diff.TableDelta) ([]string, error) {
|
||||
toSchemas, err := toRoot.GetAllSchemas(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read schemas from toRoot, cause: %s", err.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())
|
||||
}
|
||||
|
||||
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 := diff.GenerateCreateTableStatement(td.ToName, td.ToSch, toPkSch, td.ToFks, td.ToFksParentSch)
|
||||
if err != nil {
|
||||
return nil, errhand.VerboseErrorFromError(err)
|
||||
}
|
||||
ddlStatements = append(ddlStatements, stmt)
|
||||
} else {
|
||||
stmts, err := diff.GetNonCreateNonDropTableSqlSchemaDiff(td, toSchemas, fromSch, toSch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ddlStatements = append(ddlStatements, stmts...)
|
||||
}
|
||||
|
||||
return ddlStatements, nil
|
||||
}
|
||||
|
||||
func canGetDataDiff(ctx *sql.Context, td diff.TableDelta) bool {
|
||||
if td.IsDrop() {
|
||||
return false // don't output DELETE FROM statements after DROP TABLE
|
||||
}
|
||||
|
||||
// not diffable
|
||||
if !schema.ArePrimaryKeySetsDiffable(td.Format(), td.FromSch, td.ToSch) {
|
||||
ctx.Session.Warn(&sql.Warning{
|
||||
Level: "Warning",
|
||||
Code: mysql.ERNotSupportedYet,
|
||||
Message: fmt.Sprintf("Primary key sets differ between revisions for table '%s', skipping data diff", td.ToName),
|
||||
})
|
||||
return false
|
||||
}
|
||||
// cannot sql diff
|
||||
if td.ToSch == nil || (td.FromSch != nil && !schema.SchemasAreEqual(td.FromSch, td.ToSch)) {
|
||||
// TODO(8/24/22 Zach): this is overly broad, we can absolutely do better
|
||||
ctx.Session.Warn(&sql.Warning{
|
||||
Level: "Warning",
|
||||
Code: mysql.ERNotSupportedYet,
|
||||
Message: fmt.Sprintf("Incompatible schema change, skipping data diff for table '%s'", td.ToName),
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func getUserTableDataSqlPatch(ctx *sql.Context, dbData env.DbData, td diff.TableDelta, fromRefDetails, toRefDetails *refDetails) ([]string, error) {
|
||||
// ToTable is used as target table as it cannot be nil at this point
|
||||
diffSch, projections, ri, err := getDiffQuery(ctx, dbData, td, fromRefDetails, toRefDetails)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
targetPkSch, err := sqlutil.FromDoltSchema(td.ToName, td.ToSch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return getDataSqlPatchResults(ctx, diffSch, targetPkSch.Schema, projections, ri, td.ToName, td.ToSch)
|
||||
}
|
||||
|
||||
func getDataSqlPatchResults(ctx *sql.Context, diffQuerySch, targetSch sql.Schema, projections []sql.Expression, iter sql.RowIter, tn string, tsch schema.Schema) ([]string, error) {
|
||||
ds, err := diff.NewDiffSplitter(diffQuerySch, targetSch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res []string
|
||||
for {
|
||||
r, err := iter.Next(ctx)
|
||||
if err == io.EOF {
|
||||
return res, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err = plan.ProjectRow(ctx, projections, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oldRow, newRow, err := ds.SplitDiffResultRow(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var stmt string
|
||||
if oldRow.Row != nil {
|
||||
stmt, err = diff.GetDataDiffStatement(tn, tsch, oldRow.Row, oldRow.RowDiff, oldRow.ColDiffs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if newRow.Row != nil {
|
||||
stmt, err = diff.GetDataDiffStatement(tn, tsch, newRow.Row, newRow.RowDiff, newRow.ColDiffs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if stmt != "" {
|
||||
res = append(res, stmt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
// on sql engine, which returns the schema and rowIter of the final data diff result.
|
||||
func getDiffQuery(ctx *sql.Context, dbData env.DbData, td diff.TableDelta, fromRefDetails, toRefDetails *refDetails) (sql.Schema, []sql.Expression, sql.RowIter, error) {
|
||||
diffTableSchema, j, err := dtables.GetDiffTableSchemaAndJoiner(td.ToTable.Format(), td.FromSch, td.ToSch)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
diffPKSch, err := sqlutil.FromDoltSchema("", diffTableSchema)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
columnsWithDiff := getColumnNamesWithDiff(td.FromSch, td.ToSch)
|
||||
diffQuerySqlSch, projections := getDiffQuerySqlSchemaAndProjections(diffPKSch.Schema, columnsWithDiff)
|
||||
|
||||
dp := dtables.NewDiffPartition(td.ToTable, td.FromTable, toRefDetails.hashStr, fromRefDetails.hashStr, toRefDetails.commitTime, fromRefDetails.commitTime, td.ToSch, td.FromSch)
|
||||
ri := dtables.NewDiffPartitionRowIter(*dp, dbData.Ddb, j)
|
||||
|
||||
return diffQuerySqlSch, projections, ri, nil
|
||||
}
|
||||
|
||||
func getColumnNamesWithDiff(fromSch, toSch schema.Schema) []string {
|
||||
var cols []string
|
||||
|
||||
if fromSch != nil {
|
||||
_ = fromSch.GetAllCols().Iter(func(tag uint64, col schema.Column) (stop bool, err error) {
|
||||
cols = append(cols, fmt.Sprintf("from_%s", col.Name))
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
if toSch != nil {
|
||||
_ = toSch.GetAllCols().Iter(func(tag uint64, col schema.Column) (stop bool, err error) {
|
||||
cols = append(cols, fmt.Sprintf("to_%s", col.Name))
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
return cols
|
||||
}
|
||||
|
||||
// getDiffQuerySqlSchemaAndProjections returns the schema of columns with data diff and "diff_type". This is used for diff splitter.
|
||||
// When extracting the diff schema, the ordering must follow the ordering of given columns
|
||||
func getDiffQuerySqlSchemaAndProjections(diffTableSch sql.Schema, columns []string) (sql.Schema, []sql.Expression) {
|
||||
type column struct {
|
||||
sqlCol *sql.Column
|
||||
idx int
|
||||
}
|
||||
|
||||
columns = append(columns, "diff_type")
|
||||
colMap := make(map[string]*column)
|
||||
for _, c := range columns {
|
||||
colMap[c] = nil
|
||||
}
|
||||
|
||||
var cols = make([]*sql.Column, len(columns))
|
||||
var getFieldCols = make([]sql.Expression, len(columns))
|
||||
|
||||
for i, c := range diffTableSch {
|
||||
if _, ok := colMap[c.Name]; ok {
|
||||
colMap[c.Name] = &column{c, i}
|
||||
}
|
||||
}
|
||||
|
||||
for i, c := range columns {
|
||||
col := colMap[c].sqlCol
|
||||
cols[i] = col
|
||||
getFieldCols[i] = expression.NewGetField(colMap[c].idx, col.Type, col.Name, col.Nullable)
|
||||
}
|
||||
|
||||
return cols, getFieldCols
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
// patchTableFunctionRowIter
|
||||
//------------------------------------
|
||||
|
||||
var _ sql.RowIter = (*patchTableFunctionRowIter)(nil)
|
||||
|
||||
type patchTableFunctionRowIter struct {
|
||||
patches []*patchNode
|
||||
patchIdx int
|
||||
statementIdx int
|
||||
fromRef string
|
||||
toRef string
|
||||
currentPatch *patchNode
|
||||
currentRowIter *sql.RowIter
|
||||
}
|
||||
|
||||
// newPatchTableFunctionRowIter iterates over each patch nodes given returning
|
||||
// each statement in each patch node as a single row including from_commit_hash,
|
||||
// to_commit_hash and table_name prepended to diff_type and statement for each patch statement.
|
||||
func newPatchTableFunctionRowIter(patchNodes []*patchNode, fromRef, toRef string) sql.RowIter {
|
||||
return &patchTableFunctionRowIter{
|
||||
patches: patchNodes,
|
||||
patchIdx: 0,
|
||||
statementIdx: 0,
|
||||
fromRef: fromRef,
|
||||
toRef: toRef,
|
||||
}
|
||||
}
|
||||
|
||||
func (itr *patchTableFunctionRowIter) Next(ctx *sql.Context) (sql.Row, error) {
|
||||
for {
|
||||
if itr.patchIdx >= len(itr.patches) {
|
||||
return nil, io.EOF
|
||||
}
|
||||
if itr.currentPatch == nil {
|
||||
itr.currentPatch = itr.patches[itr.patchIdx]
|
||||
}
|
||||
if itr.currentRowIter == nil {
|
||||
ri := newPatchStatementsRowIter(itr.currentPatch.schemaPatchStmts, itr.currentPatch.dataPatchStmts)
|
||||
itr.currentRowIter = &ri
|
||||
}
|
||||
|
||||
row, err := (*itr.currentRowIter).Next(ctx)
|
||||
if err == io.EOF {
|
||||
itr.currentPatch = nil
|
||||
itr.currentRowIter = nil
|
||||
itr.patchIdx++
|
||||
continue
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
itr.statementIdx++
|
||||
r := sql.Row{itr.statementIdx, itr.fromRef, itr.toRef, itr.currentPatch.tblName}
|
||||
return r.Append(row), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (itr *patchTableFunctionRowIter) Close(_ *sql.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
// patchStatementsRowIter
|
||||
//------------------------------------
|
||||
|
||||
var _ sql.RowIter = (*patchStatementsRowIter)(nil)
|
||||
|
||||
type patchStatementsRowIter struct {
|
||||
stmts []string
|
||||
ddlLen int
|
||||
idx int
|
||||
}
|
||||
|
||||
// newPatchStatementsRowIter iterates over each patch statements returning row of diff_type of either 'schema' or 'data' with the statement.
|
||||
func newPatchStatementsRowIter(ddlStmts, dataStmts []string) sql.RowIter {
|
||||
return &patchStatementsRowIter{
|
||||
stmts: append(ddlStmts, dataStmts...),
|
||||
ddlLen: len(ddlStmts),
|
||||
idx: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *patchStatementsRowIter) Next(ctx *sql.Context) (sql.Row, error) {
|
||||
defer func() {
|
||||
p.idx++
|
||||
}()
|
||||
|
||||
if p.idx >= len(p.stmts) {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
if p.stmts == nil {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
stmt := p.stmts[p.idx]
|
||||
diffType := "schema"
|
||||
if p.idx >= p.ddlLen {
|
||||
diffType = "data"
|
||||
}
|
||||
|
||||
return sql.Row{diffType, stmt}, nil
|
||||
}
|
||||
|
||||
func (p *patchStatementsRowIter) Close(_ *sql.Context) error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,413 +0,0 @@
|
||||
// Copyright 2023 Dolthub, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package dprocedures
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/expression"
|
||||
"github.com/dolthub/go-mysql-server/sql/plan"
|
||||
"github.com/dolthub/vitess/go/mysql"
|
||||
|
||||
"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/env"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dtables"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
|
||||
"github.com/dolthub/dolt/go/libraries/utils/argparser"
|
||||
"github.com/dolthub/dolt/go/libraries/utils/set"
|
||||
"github.com/dolthub/dolt/go/store/types"
|
||||
)
|
||||
|
||||
// doltPatch is the stored procedure version for the CLI command `dolt patch` (CLI command not implemented yet).
|
||||
func doltPatch(ctx *sql.Context, args ...string) (sql.RowIter, error) {
|
||||
res, err := doDoltPatch(ctx, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newPatchRowIter(res), nil
|
||||
}
|
||||
|
||||
func doDoltPatch(ctx *sql.Context, args []string) ([]string, error) {
|
||||
dbName := ctx.GetCurrentDatabase()
|
||||
if len(dbName) == 0 {
|
||||
return nil, fmt.Errorf("error: empty database name")
|
||||
}
|
||||
|
||||
apr, err := cli.CreatePatchArgParser().Parse(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dSess := dsess.DSessFromSess(ctx.Session)
|
||||
doltDB, ok := dSess.GetDoltDB(ctx, dbName)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to get DoltDB")
|
||||
}
|
||||
|
||||
dbData, ok := dSess.GetDbData(ctx, dbName)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to get dbData")
|
||||
}
|
||||
roots, ok := dSess.GetRoots(ctx, dbName)
|
||||
if !ok {
|
||||
return nil, sql.ErrDatabaseNotFound.New(dbName)
|
||||
}
|
||||
|
||||
fromRef, fromRoot, toRef, toRoot, tables := parseRevisionsAndTablesArgs(ctx, dbData, doltDB, roots, apr)
|
||||
tableSet, err := validateTablesAndGetTablesSet(ctx, fromRoot, toRoot, tables)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tableDeltas, err := diff.GetTableDeltas(ctx, fromRoot, toRoot)
|
||||
if err != nil {
|
||||
return nil, errhand.BuildDError("error: unable to diff tables").AddCause(err).Build()
|
||||
}
|
||||
|
||||
sort.Slice(tableDeltas, func(i, j int) bool {
|
||||
return strings.Compare(tableDeltas[i].ToName, tableDeltas[j].ToName) < 0
|
||||
})
|
||||
|
||||
var finalRes []string
|
||||
for _, td := range tableDeltas {
|
||||
if !tableSet.Contains(td.FromName) && !tableSet.Contains(td.ToName) {
|
||||
continue
|
||||
}
|
||||
if td.FromTable == nil && td.ToTable == nil {
|
||||
return nil, errhand.BuildDError("error: both tables in tableDelta are nil").Build()
|
||||
}
|
||||
|
||||
ddlStatements, err := getSchemaSqlPatch(ctx, toRoot, td)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalRes = append(finalRes, ddlStatements...)
|
||||
|
||||
if canGetDataDiff(ctx, td) {
|
||||
res, err := getUserTableSqlPatch(ctx, dbData, td, fromRef, toRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalRes = append(finalRes, res...)
|
||||
}
|
||||
}
|
||||
|
||||
return finalRes, nil
|
||||
}
|
||||
|
||||
func getSchemaSqlPatch(ctx *sql.Context, toRoot *doltdb.RootValue, td diff.TableDelta) ([]string, error) {
|
||||
toSchemas, err := toRoot.GetAllSchemas(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read schemas from toRoot, cause: %s", err.Error())
|
||||
}
|
||||
|
||||
return diff.SqlSchemaDiff(ctx, td, toSchemas)
|
||||
}
|
||||
|
||||
func canGetDataDiff(ctx *sql.Context, td diff.TableDelta) bool {
|
||||
if td.IsDrop() {
|
||||
return false // don't output DELETE FROM statements after DROP TABLE
|
||||
}
|
||||
// not diffable
|
||||
if !schema.ArePrimaryKeySetsDiffable(td.Format(), td.FromSch, td.ToSch) {
|
||||
ctx.Session.Warn(&sql.Warning{
|
||||
Level: "Warning",
|
||||
Code: mysql.ERNotSupportedYet,
|
||||
Message: fmt.Sprintf("Primary key sets differ between revisions for table '%s', skipping data diff", td.ToName),
|
||||
})
|
||||
return false
|
||||
}
|
||||
// cannot sql diff
|
||||
if td.ToSch == nil || (td.FromSch != nil && !schema.SchemasAreEqual(td.FromSch, td.ToSch)) {
|
||||
// TODO(8/24/22 Zach): this is overly broad, we can absolutely do better
|
||||
ctx.Session.Warn(&sql.Warning{
|
||||
Level: "Warning",
|
||||
Code: mysql.ERNotSupportedYet,
|
||||
Message: fmt.Sprintf("Incompatible schema change, skipping data diff for table '%s'", td.ToName),
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func getUserTableSqlPatch(ctx *sql.Context, dbData env.DbData, td diff.TableDelta, fromRef, toRef string) ([]string, error) {
|
||||
// ToTable is used as target table as cannot be nil at this point
|
||||
diffSch, projections, ri, err := getDiffQuery(ctx, dbData, td, fromRef, toRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
targetPkSch, err := sqlutil.FromDoltSchema(td.ToName, td.ToSch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return getDiffResults(ctx, diffSch, targetPkSch.Schema, projections, ri, td.ToName, td.ToSch)
|
||||
}
|
||||
|
||||
// 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)
|
||||
// on sql engine, which returns the schema and rowIter of the final data diff result.
|
||||
func getDiffQuery(ctx *sql.Context, dbData env.DbData, td diff.TableDelta, fromRef, toRef string) (sql.Schema, []sql.Expression, sql.RowIter, error) {
|
||||
diffTableSchema, j, err := dtables.GetDiffTableSchemaAndJoiner(td.ToTable.Format(), td.FromSch, td.ToSch)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
diffPKSch, err := sqlutil.FromDoltSchema("", diffTableSchema)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
columnsWithDiff := getColumnNamesWithDiff(td.FromSch, td.ToSch)
|
||||
diffSqlSch, projections := getDiffSqlSchema(diffPKSch.Schema, columnsWithDiff)
|
||||
|
||||
// using arbitrary time since we do not care about the commit time in the result
|
||||
now := time.Now()
|
||||
dp := dtables.NewDiffPartition(td.ToTable, td.FromTable, toRef, fromRef, (*types.Timestamp)(&now), (*types.Timestamp)(&now), td.ToSch, td.FromSch)
|
||||
ri := dtables.NewDiffPartitionRowIter(*dp, dbData.Ddb, j)
|
||||
|
||||
return diffSqlSch, projections, ri, nil
|
||||
}
|
||||
|
||||
func getColumnNamesWithDiff(fromSch, toSch schema.Schema) []string {
|
||||
var cols []string
|
||||
|
||||
if fromSch != nil {
|
||||
_ = fromSch.GetAllCols().Iter(func(tag uint64, col schema.Column) (stop bool, err error) {
|
||||
cols = append(cols, fmt.Sprintf("from_%s", col.Name))
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
if toSch != nil {
|
||||
_ = toSch.GetAllCols().Iter(func(tag uint64, col schema.Column) (stop bool, err error) {
|
||||
cols = append(cols, fmt.Sprintf("to_%s", col.Name))
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
return cols
|
||||
}
|
||||
|
||||
// getDiffSqlSchema returns the schema of columns with data diff and "diff_type". This is used for diff splitter.
|
||||
// When extracting the diff schema, the ordering must follow the ordering of given columns
|
||||
func getDiffSqlSchema(diffTableSch sql.Schema, columns []string) (sql.Schema, []sql.Expression) {
|
||||
type column struct {
|
||||
sqlCol *sql.Column
|
||||
idx int
|
||||
}
|
||||
|
||||
columns = append(columns, "diff_type")
|
||||
colMap := make(map[string]*column)
|
||||
for _, c := range columns {
|
||||
colMap[c] = nil
|
||||
}
|
||||
|
||||
var cols = make([]*sql.Column, len(columns))
|
||||
var getFieldCols = make([]sql.Expression, len(columns))
|
||||
|
||||
for i, c := range diffTableSch {
|
||||
if _, ok := colMap[c.Name]; ok {
|
||||
colMap[c.Name] = &column{c, i}
|
||||
}
|
||||
}
|
||||
|
||||
for i, c := range columns {
|
||||
col := colMap[c].sqlCol
|
||||
cols[i] = col
|
||||
getFieldCols[i] = expression.NewGetField(colMap[c].idx, col.Type, col.Name, col.Nullable)
|
||||
}
|
||||
|
||||
return cols, getFieldCols
|
||||
}
|
||||
|
||||
func getDiffResults(ctx *sql.Context, diffQuerySch, targetSch sql.Schema, projections []sql.Expression, iter sql.RowIter, tn string, tsch schema.Schema) ([]string, error) {
|
||||
ds, err := diff.NewDiffSplitter(diffQuerySch, targetSch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res []string
|
||||
for {
|
||||
r, err := iter.Next(ctx)
|
||||
if err == io.EOF {
|
||||
return res, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err = plan.ProjectRow(ctx, projections, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oldRow, newRow, err := ds.SplitDiffResultRow(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var stmt string
|
||||
if oldRow.Row != nil {
|
||||
stmt, err = diff.GetDataDiffStatement(tn, tsch, oldRow.Row, oldRow.RowDiff, oldRow.ColDiffs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if newRow.Row != nil {
|
||||
stmt, err = diff.GetDataDiffStatement(tn, tsch, newRow.Row, newRow.RowDiff, newRow.ColDiffs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if stmt != "" {
|
||||
res = append(res, stmt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parseRevisionsAndTablesArgs checks given arguments whether each refers to a revision or a table name.
|
||||
// It returns from revision name, from root values, to revision name, to root values and potential table names.
|
||||
func parseRevisionsAndTablesArgs(ctx *sql.Context, dbData env.DbData, doltDB *doltdb.DoltDB, roots doltdb.Roots, apr *argparser.ArgParseResults) (string, *doltdb.RootValue, string, *doltdb.RootValue, []string) {
|
||||
var fromRef, toRef string
|
||||
var fromRoot, toRoot *doltdb.RootValue
|
||||
|
||||
fromRoot = roots.Staged
|
||||
fromRef = "STAGED"
|
||||
toRoot = roots.Working
|
||||
toRef = "WORKING"
|
||||
if apr.Contains(cli.CachedFlag) {
|
||||
fromRoot = roots.Head
|
||||
fromRef = "HEAD"
|
||||
toRoot = roots.Staged
|
||||
toRef = "STAGED"
|
||||
}
|
||||
|
||||
// `dolt diff`
|
||||
if apr.NArg() == 0 {
|
||||
return fromRef, fromRoot, toRef, toRoot, apr.Args
|
||||
}
|
||||
|
||||
from, ok := diff.MaybeResolveRoot(ctx, dbData.Rsr, doltDB, apr.Args[0])
|
||||
if !ok {
|
||||
// `dolt diff [...tables]`
|
||||
return fromRef, fromRoot, toRef, toRoot, apr.Args
|
||||
}
|
||||
|
||||
fromRoot = from
|
||||
fromRef = apr.Args[0]
|
||||
|
||||
if apr.NArg() == 1 {
|
||||
// `dolt diff from_commit`
|
||||
return fromRef, fromRoot, toRef, toRoot, apr.Args[1:]
|
||||
}
|
||||
|
||||
to, ok := diff.MaybeResolveRoot(ctx, dbData.Rsr, doltDB, apr.Args[1])
|
||||
if !ok {
|
||||
// `dolt diff from_commit [...tables]`
|
||||
return fromRef, fromRoot, toRef, toRoot, apr.Args[1:]
|
||||
}
|
||||
|
||||
toRoot = to
|
||||
toRef = apr.Args[1]
|
||||
|
||||
// `dolt diff from_commit to_commit [...tables]`
|
||||
return fromRef, fromRoot, toRef, toRoot, apr.Args[2:]
|
||||
}
|
||||
|
||||
// validateTablesAndGetTablesSet takes array of table names or an empty array and returns the table names
|
||||
// in string set type. If the array is empty, it returns union of table names on from and to roots.
|
||||
func validateTablesAndGetTablesSet(ctx context.Context, fromRoot, toRoot *doltdb.RootValue, tables []string) (*set.StrSet, error) {
|
||||
tableSet := set.NewStrSet(nil)
|
||||
|
||||
// if no tables or docs were specified as args, diff all tables and docs
|
||||
if len(tables) == 0 {
|
||||
utn, err := doltdb.UnionTableNames(ctx, fromRoot, toRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tableSet.Add(utn...)
|
||||
} else {
|
||||
for _, tableName := range tables {
|
||||
// verify table args exist in at least one root
|
||||
_, ok, err := fromRoot.GetTable(ctx, tableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ok {
|
||||
tableSet.Add(tableName)
|
||||
continue
|
||||
}
|
||||
|
||||
_, ok, err = toRoot.GetTable(ctx, tableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("table %s does not exist in either revision", tableName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tableSet, nil
|
||||
}
|
||||
|
||||
var _ sql.RowIter = (*patchRowIter)(nil)
|
||||
|
||||
type patchRowIter struct {
|
||||
stmts []string
|
||||
idx int
|
||||
}
|
||||
|
||||
func newPatchRowIter(stmts []string) sql.RowIter {
|
||||
return &patchRowIter{
|
||||
stmts: stmts,
|
||||
idx: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *patchRowIter) Next(ctx *sql.Context) (sql.Row, error) {
|
||||
defer func() {
|
||||
p.idx++
|
||||
}()
|
||||
|
||||
if p.idx >= len(p.stmts) {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
if p.stmts == nil {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
stmt := p.stmts[p.idx]
|
||||
return sql.Row{stmt}, nil
|
||||
}
|
||||
|
||||
func (p *patchRowIter) Close(_ *sql.Context) error {
|
||||
p.stmts = nil
|
||||
p.idx = 0
|
||||
return nil
|
||||
}
|
||||
@@ -36,7 +36,6 @@ var DoltProcedures = []sql.ExternalStoredProcedureDetails{
|
||||
{Name: "dolt_gc", Schema: int64Schema("success"), Function: doltGC},
|
||||
|
||||
{Name: "dolt_merge", Schema: int64Schema("fast_forward", "conflicts"), Function: doltMerge},
|
||||
{Name: "dolt_patch", Schema: stringSchema("statement"), Function: doltPatch},
|
||||
{Name: "dolt_pull", Schema: int64Schema("fast_forward", "conflicts"), Function: doltPull},
|
||||
{Name: "dolt_push", Schema: int64Schema("success"), Function: doltPush},
|
||||
{Name: "dolt_remote", Schema: int64Schema("status"), Function: doltRemote},
|
||||
@@ -59,7 +58,6 @@ var DoltProcedures = []sql.ExternalStoredProcedureDetails{
|
||||
// {Name: "dgc", Schema: int64Schema("status"), Function: doltGC},
|
||||
|
||||
{Name: "dmerge", Schema: int64Schema("fast_forward", "conflicts"), Function: doltMerge},
|
||||
{Name: "dpatch", Schema: stringSchema("statement"), Function: doltPatch},
|
||||
{Name: "dpull", Schema: int64Schema("fast_forward", "conflicts"), Function: doltPull},
|
||||
{Name: "dpush", Schema: int64Schema("success"), Function: doltPush},
|
||||
{Name: "dremote", Schema: int64Schema("status"), Function: doltRemote},
|
||||
|
||||
@@ -756,56 +756,62 @@ func (d *DoltSession) GetRoots(ctx *sql.Context, dbName string) (doltdb.Roots, b
|
||||
|
||||
// ResolveRootForRef returns the root value for the ref given, which refers to either a commit spec or is one of the
|
||||
// special identifiers |WORKING| or |STAGED|
|
||||
// Returns the root value associated with the identifier given and its commit time
|
||||
func (d *DoltSession) ResolveRootForRef(ctx *sql.Context, dbName, hashStr string) (*doltdb.RootValue, *types.Timestamp, error) {
|
||||
if hashStr == doltdb.Working || hashStr == doltdb.Staged {
|
||||
// Returns the root value associated with the identifier given, its commit time and its hash string. The hash string
|
||||
// for special identifiers |WORKING| or |STAGED| would be itself, 'WORKING' or 'STAGED', respectively.
|
||||
func (d *DoltSession) ResolveRootForRef(ctx *sql.Context, dbName, refStr string) (*doltdb.RootValue, *types.Timestamp, string, error) {
|
||||
if refStr == doltdb.Working || refStr == doltdb.Staged {
|
||||
// TODO: get from working set / staged update time
|
||||
now := types.Timestamp(time.Now())
|
||||
// TODO: no current database
|
||||
roots, _ := d.GetRoots(ctx, ctx.GetCurrentDatabase())
|
||||
if hashStr == doltdb.Working {
|
||||
return roots.Working, &now, nil
|
||||
} else if hashStr == doltdb.Staged {
|
||||
return roots.Staged, &now, nil
|
||||
if refStr == doltdb.Working {
|
||||
return roots.Working, &now, refStr, nil
|
||||
} else if refStr == doltdb.Staged {
|
||||
return roots.Staged, &now, refStr, nil
|
||||
}
|
||||
}
|
||||
|
||||
var root *doltdb.RootValue
|
||||
var commitTime *types.Timestamp
|
||||
cs, err := doltdb.NewCommitSpec(hashStr)
|
||||
cs, err := doltdb.NewCommitSpec(refStr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, "", err
|
||||
}
|
||||
|
||||
dbData, ok := d.GetDbData(ctx, dbName)
|
||||
if !ok {
|
||||
return nil, nil, sql.ErrDatabaseNotFound.New(dbName)
|
||||
return nil, nil, "", sql.ErrDatabaseNotFound.New(dbName)
|
||||
}
|
||||
|
||||
headRef, err := d.CWBHeadRef(ctx, dbName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, "", err
|
||||
}
|
||||
|
||||
cm, err := dbData.Ddb.Resolve(ctx, cs, headRef)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, "", err
|
||||
}
|
||||
|
||||
root, err = cm.GetRootValue(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, "", err
|
||||
}
|
||||
|
||||
meta, err := cm.GetCommitMeta(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, "", err
|
||||
}
|
||||
|
||||
t := meta.Time()
|
||||
commitTime = (*types.Timestamp)(&t)
|
||||
|
||||
return root, commitTime, nil
|
||||
commitHash, err := cm.HashOf()
|
||||
if err != nil {
|
||||
return nil, nil, "", err
|
||||
}
|
||||
|
||||
return root, commitTime, commitHash.String(), nil
|
||||
}
|
||||
|
||||
// SetRoot sets a new root value for the session for the database named. This is the primary mechanism by which data
|
||||
|
||||
@@ -343,25 +343,21 @@ func TestSpatialScriptsPrepared(t *testing.T) {
|
||||
|
||||
func TestSpatialIndexScripts(t *testing.T) {
|
||||
skipOldFormat(t)
|
||||
schema.EnableSpatialIndex = true
|
||||
enginetest.TestSpatialIndexScripts(t, newDoltHarness(t))
|
||||
}
|
||||
|
||||
func TestSpatialIndexScriptsPrepared(t *testing.T) {
|
||||
skipOldFormat(t)
|
||||
schema.EnableSpatialIndex = true
|
||||
enginetest.TestSpatialIndexScriptsPrepared(t, newDoltHarness(t))
|
||||
}
|
||||
|
||||
func TestSpatialIndexPlans(t *testing.T) {
|
||||
skipOldFormat(t)
|
||||
schema.EnableSpatialIndex = true
|
||||
enginetest.TestSpatialIndexPlans(t, newDoltHarness(t))
|
||||
}
|
||||
|
||||
func TestSpatialIndexPlansPrepared(t *testing.T) {
|
||||
skipOldFormat(t)
|
||||
schema.EnableSpatialIndex = true
|
||||
enginetest.TestSpatialIndexPlansPrepared(t, newDoltHarness(t))
|
||||
}
|
||||
|
||||
@@ -1280,6 +1276,28 @@ func TestDiffSummaryTableFunctionPrepared(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchTableFunction(t *testing.T) {
|
||||
harness := newDoltHarness(t)
|
||||
harness.Setup(setup.MydbData)
|
||||
for _, test := range PatchTableFunctionScriptTests {
|
||||
harness.engine = nil
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
enginetest.TestScript(t, harness, test)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchTableFunctionPrepared(t *testing.T) {
|
||||
harness := newDoltHarness(t)
|
||||
harness.Setup(setup.MydbData)
|
||||
for _, test := range PatchTableFunctionScriptTests {
|
||||
harness.engine = nil
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
enginetest.TestScriptPrepared(t, harness, test)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogTableFunction(t *testing.T) {
|
||||
harness := newDoltHarness(t)
|
||||
harness.Setup(setup.MydbData)
|
||||
|
||||
@@ -142,7 +142,7 @@ var ShowCreateTableAsOfScriptTest = queries.ScriptTest{
|
||||
" `pk` int NOT NULL,\n" +
|
||||
" `c2` varchar(20),\n" +
|
||||
" PRIMARY KEY (`pk`),\n" +
|
||||
" UNIQUE KEY `c2` (`c2`)\n" +
|
||||
" UNIQUE KEY `unique_c2` (`c2`)\n" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin",
|
||||
},
|
||||
},
|
||||
@@ -154,7 +154,7 @@ var ShowCreateTableAsOfScriptTest = queries.ScriptTest{
|
||||
" `pk` int NOT NULL,\n" +
|
||||
" `c2` varchar(20),\n" +
|
||||
" PRIMARY KEY (`pk`),\n" +
|
||||
" UNIQUE KEY `c2` (`c2`)\n" +
|
||||
" UNIQUE KEY `unique_c2` (`c2`)\n" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin",
|
||||
},
|
||||
},
|
||||
@@ -571,7 +571,7 @@ var DoltScripts = []queries.ScriptTest{
|
||||
" `c` int NOT NULL,\n" +
|
||||
" `d` varchar(10),\n" +
|
||||
" PRIMARY KEY (`c`),\n" +
|
||||
" UNIQUE KEY `d_0` (`d`),\n" +
|
||||
" UNIQUE KEY `t2du` (`d`),\n" +
|
||||
" CONSTRAINT `fk1` FOREIGN KEY (`d`) REFERENCES `t1` (`b`)\n" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
|
||||
},
|
||||
@@ -823,53 +823,6 @@ var DoltScripts = []queries.ScriptTest{
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "simple tests on DOLT_PATCH() stored procedure",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE parent (id int PRIMARY KEY, id_ext int, v1 int, v2 text, INDEX v1 (v1));",
|
||||
"CREATE TABLE child (id int primary key, v1 int);",
|
||||
"CALL DOLT_COMMIT('-Am','added tables')",
|
||||
|
||||
"ALTER TABLE child ADD CONSTRAINT fk_named FOREIGN KEY (v1) REFERENCES parent(v1);",
|
||||
"insert into parent values (0, 1, 2, NULL);",
|
||||
"ALTER TABLE parent DROP PRIMARY KEY;",
|
||||
"ALTER TABLE parent ADD PRIMARY KEY(id, id_ext);",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "CALL DOLT_PATCH()",
|
||||
Expected: []sql.Row{
|
||||
{"ALTER TABLE `child` ADD INDEX `v1`(`v1`);"},
|
||||
{"ALTER TABLE `child` ADD CONSTRAINT `fk_named` FOREIGN KEY (`v1`) REFERENCES `parent` (`v1`);"},
|
||||
{"ALTER TABLE `parent` DROP PRIMARY KEY;"},
|
||||
{"ALTER TABLE `parent` ADD PRIMARY KEY (id,id_ext);"}},
|
||||
},
|
||||
{
|
||||
Query: "CALL DOLT_PATCH('HEAD~')",
|
||||
Expected: []sql.Row{
|
||||
{"CREATE TABLE `child` (\n `id` int NOT NULL,\n `v1` int,\n PRIMARY KEY (`id`),\n KEY `v1` (`v1`),\n CONSTRAINT `fk_named` FOREIGN KEY (`v1`) REFERENCES `parent` (`v1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"},
|
||||
{"CREATE TABLE `parent` (\n `id` int NOT NULL,\n `id_ext` int NOT NULL,\n `v1` int,\n `v2` text,\n PRIMARY KEY (`id`,`id_ext`),\n KEY `v1` (`v1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"},
|
||||
{"INSERT INTO `parent` (`id`,`id_ext`,`v1`,`v2`) VALUES (0,1,2,NULL);"}},
|
||||
},
|
||||
{
|
||||
Query: "CALL DOLT_PATCH('child')",
|
||||
Expected: []sql.Row{
|
||||
{"ALTER TABLE `child` ADD INDEX `v1`(`v1`);"},
|
||||
{"ALTER TABLE `child` ADD CONSTRAINT `fk_named` FOREIGN KEY (`v1`) REFERENCES `parent` (`v1`);"}},
|
||||
},
|
||||
{
|
||||
Query: "SHOW WARNINGS;",
|
||||
Expected: []sql.Row{
|
||||
{"Warning", 1235, "Incompatible schema change, skipping data diff for table 'child'"}},
|
||||
},
|
||||
{
|
||||
Query: "CALL DOLT_PATCH('HEAD','HEAD~','parent')",
|
||||
Expected: []sql.Row{
|
||||
{"DROP TABLE `parent`;"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func makeLargeInsert(sz int) string {
|
||||
@@ -938,6 +891,20 @@ var DoltUserPrivTests = []queries.UserPrivilegeTest{
|
||||
Query: "SELECT * FROM dolt_diff_summary('main~..main', 'test');",
|
||||
ExpectedErr: sql.ErrDatabaseAccessDeniedForUser,
|
||||
},
|
||||
{
|
||||
// Without access to the database, dolt_patch should fail with a database access error
|
||||
User: "tester",
|
||||
Host: "localhost",
|
||||
Query: "SELECT * FROM dolt_patch('main~', 'main', 'test');",
|
||||
ExpectedErr: sql.ErrDatabaseAccessDeniedForUser,
|
||||
},
|
||||
{
|
||||
// Without access to the database, dolt_patch with dots should fail with a database access error
|
||||
User: "tester",
|
||||
Host: "localhost",
|
||||
Query: "SELECT * FROM dolt_patch('main~..main', 'test');",
|
||||
ExpectedErr: sql.ErrDatabaseAccessDeniedForUser,
|
||||
},
|
||||
{
|
||||
// Without access to the database, dolt_log should fail with a database access error
|
||||
User: "tester",
|
||||
@@ -1036,6 +1003,34 @@ var DoltUserPrivTests = []queries.UserPrivilegeTest{
|
||||
Query: "SELECT * FROM dolt_diff_summary('main~...main');",
|
||||
ExpectedErr: sql.ErrPrivilegeCheckFailed,
|
||||
},
|
||||
{
|
||||
// With access to the db, but not the table, dolt_patch should fail
|
||||
User: "tester",
|
||||
Host: "localhost",
|
||||
Query: "SELECT * FROM dolt_patch('main~', 'main', 'test2');",
|
||||
ExpectedErr: sql.ErrPrivilegeCheckFailed,
|
||||
},
|
||||
{
|
||||
// With access to the db, but not the table, dolt_patch with dots should fail
|
||||
User: "tester",
|
||||
Host: "localhost",
|
||||
Query: "SELECT * FROM dolt_patch('main~...main', 'test2');",
|
||||
ExpectedErr: sql.ErrPrivilegeCheckFailed,
|
||||
},
|
||||
{
|
||||
// With access to the db, dolt_patch should fail for all tables if no access any of tables
|
||||
User: "tester",
|
||||
Host: "localhost",
|
||||
Query: "SELECT * FROM dolt_patch('main~', 'main');",
|
||||
ExpectedErr: sql.ErrPrivilegeCheckFailed,
|
||||
},
|
||||
{
|
||||
// With access to the db, dolt_patch with dots should fail for all tables if no access any of tables
|
||||
User: "tester",
|
||||
Host: "localhost",
|
||||
Query: "SELECT * FROM dolt_patch('main~...main');",
|
||||
ExpectedErr: sql.ErrPrivilegeCheckFailed,
|
||||
},
|
||||
{
|
||||
// Revoke select on mydb.test
|
||||
User: "root",
|
||||
@@ -1106,6 +1101,20 @@ var DoltUserPrivTests = []queries.UserPrivilegeTest{
|
||||
Query: "SELECT COUNT(*) FROM dolt_diff_summary('main~...main');",
|
||||
Expected: []sql.Row{{1}},
|
||||
},
|
||||
{
|
||||
// After granting access to the entire db, dolt_patch should work
|
||||
User: "tester",
|
||||
Host: "localhost",
|
||||
Query: "SELECT COUNT(*) FROM dolt_patch('main~', 'main');",
|
||||
Expected: []sql.Row{{1}},
|
||||
},
|
||||
{
|
||||
// After granting access to the entire db, dolt_patch with dots should work
|
||||
User: "tester",
|
||||
Host: "localhost",
|
||||
Query: "SELECT COUNT(*) FROM dolt_patch('main~...main');",
|
||||
Expected: []sql.Row{{1}},
|
||||
},
|
||||
{
|
||||
// After granting access to the entire db, dolt_log should work
|
||||
User: "tester",
|
||||
@@ -1148,6 +1157,13 @@ var DoltUserPrivTests = []queries.UserPrivilegeTest{
|
||||
Query: "SELECT * FROM dolt_diff_summary('main~', 'main', 'test');",
|
||||
ExpectedErr: sql.ErrDatabaseAccessDeniedForUser,
|
||||
},
|
||||
{
|
||||
// After revoking access, dolt_patch should fail
|
||||
User: "tester",
|
||||
Host: "localhost",
|
||||
Query: "SELECT * FROM dolt_patch('main~', 'main', 'test');",
|
||||
ExpectedErr: sql.ErrDatabaseAccessDeniedForUser,
|
||||
},
|
||||
{
|
||||
// After revoking access, dolt_log should fail
|
||||
User: "tester",
|
||||
|
||||
@@ -2803,6 +2803,559 @@ var DiffSummaryTableFunctionScriptTests = []queries.ScriptTest{
|
||||
},
|
||||
}
|
||||
|
||||
var PatchTableFunctionScriptTests = []queries.ScriptTest{
|
||||
{
|
||||
Name: "invalid arguments",
|
||||
SetUpScript: []string{
|
||||
"create table t (pk int primary key, c1 varchar(20), c2 varchar(20));",
|
||||
"call dolt_add('.')",
|
||||
"set @Commit1 = '';",
|
||||
"call dolt_commit_hash_out(@Commit1, '-am', 'creating table t');",
|
||||
|
||||
"insert into t values(1, 'one', 'two'), (2, 'two', 'three');",
|
||||
"set @Commit2 = '';",
|
||||
"call dolt_commit_hash_out(@Commit2, '-am', 'inserting into t');",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "SELECT * from dolt_patch();",
|
||||
ExpectedErr: sql.ErrInvalidArgumentNumber,
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch('t');",
|
||||
ExpectedErr: sql.ErrInvalidArgumentNumber,
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch('t', @Commit1, @Commit2, 'extra');",
|
||||
ExpectedErr: sql.ErrInvalidArgumentNumber,
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch(null, null, null);",
|
||||
ExpectedErr: sql.ErrInvalidArgumentDetails,
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch(123, @Commit1, @Commit2);",
|
||||
ExpectedErr: sql.ErrInvalidArgumentDetails,
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch('t', 123, @Commit2);",
|
||||
ExpectedErr: sql.ErrInvalidArgumentDetails,
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch('t', @Commit1, 123);",
|
||||
ExpectedErr: sql.ErrInvalidArgumentDetails,
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch('fake-branch', @Commit2, 't');",
|
||||
ExpectedErrStr: "branch not found: fake-branch",
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch('fake-branch..main', 't');",
|
||||
ExpectedErrStr: "branch not found: fake-branch",
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch(@Commit1, 'fake-branch', 't');",
|
||||
ExpectedErrStr: "branch not found: fake-branch",
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch('main..fake-branch', 't');",
|
||||
ExpectedErrStr: "branch not found: fake-branch",
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch(@Commit1, @Commit2, 'doesnotexist');",
|
||||
ExpectedErr: sql.ErrTableNotFound,
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch('main^..main', 'doesnotexist');",
|
||||
ExpectedErr: sql.ErrTableNotFound,
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch(@Commit1, concat('fake', '-', 'branch'), 't');",
|
||||
ExpectedErr: sqle.ErrInvalidNonLiteralArgument,
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch(hashof('main'), @Commit2, 't');",
|
||||
ExpectedErr: sqle.ErrInvalidNonLiteralArgument,
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch(@Commit1, @Commit2, LOWER('T'));",
|
||||
ExpectedErr: sqle.ErrInvalidNonLiteralArgument,
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch('main..main~', LOWER('T'));",
|
||||
ExpectedErr: sqle.ErrInvalidNonLiteralArgument,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "basic case with single table",
|
||||
SetUpScript: []string{
|
||||
"set @Commit0 = HashOf('HEAD');",
|
||||
"set @Commit1 = '';",
|
||||
"call dolt_commit_hash_out(@Commit1, '--allow-empty', '-m', 'creating table t');",
|
||||
|
||||
// create table t only
|
||||
"create table t (pk int primary key, c1 varchar(20), c2 varchar(20));",
|
||||
"call dolt_add('.')",
|
||||
"set @Commit2 = '';",
|
||||
"call dolt_commit_hash_out(@Commit2, '-am', 'creating table t');",
|
||||
|
||||
// insert 1 row into t
|
||||
"insert into t values(1, 'one', 'two');",
|
||||
"set @Commit3 = '';",
|
||||
"call dolt_commit_hash_out(@Commit3, '-am', 'inserting 1 into table t');",
|
||||
|
||||
// insert 2 rows into t and update two cells
|
||||
"insert into t values(2, 'two', 'three'), (3, 'three', 'four');",
|
||||
"update t set c1='uno', c2='dos' where pk=1;",
|
||||
"set @Commit4 = '';",
|
||||
"call dolt_commit_hash_out(@Commit4, '-am', 'inserting 2 into table t');",
|
||||
|
||||
// drop table t only
|
||||
"drop table t;",
|
||||
"set @Commit5 = '';",
|
||||
"call dolt_commit_hash_out(@Commit5, '-am', 'drop table t');",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
// table is added, no data diff, result is empty
|
||||
Query: "SELECT * from dolt_patch(@Commit1, @Commit2, 't') WHERE diff_type = 'data';",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, statement from dolt_patch(@Commit1, @Commit2, 't') WHERE diff_type = 'schema';",
|
||||
Expected: []sql.Row{{1, "t", "CREATE TABLE `t` (\n `pk` int NOT NULL,\n `c1` varchar(20),\n `c2` varchar(20),\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement from dolt_patch(@Commit2, @Commit3, 't');",
|
||||
Expected: []sql.Row{{1, "t", "data", "INSERT INTO `t` (`pk`,`c1`,`c2`) VALUES (1,'one','two');"}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement from dolt_patch(@Commit3, @Commit4, 't');",
|
||||
Expected: []sql.Row{
|
||||
{1, "t", "data", "UPDATE `t` SET `c1`='uno',`c2`='dos' WHERE `pk`=1;"},
|
||||
{2, "t", "data", "INSERT INTO `t` (`pk`,`c1`,`c2`) VALUES (2,'two','three');"},
|
||||
{3, "t", "data", "INSERT INTO `t` (`pk`,`c1`,`c2`) VALUES (3,'three','four');"},
|
||||
},
|
||||
},
|
||||
{
|
||||
// change from and to commits
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement from dolt_patch(@Commit4, @Commit3, 't');",
|
||||
Expected: []sql.Row{
|
||||
{1, "t", "data", "UPDATE `t` SET `c1`='one',`c2`='two' WHERE `pk`=1;"},
|
||||
{2, "t", "data", "DELETE FROM `t` WHERE `pk`=2;"},
|
||||
{3, "t", "data", "DELETE FROM `t` WHERE `pk`=3;"},
|
||||
},
|
||||
},
|
||||
{
|
||||
// table is dropped
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement from dolt_patch(@Commit4, @Commit5, 't');",
|
||||
Expected: []sql.Row{{1, "t", "schema", "DROP TABLE `t`;"}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement from dolt_patch(@Commit1, @Commit4, 't');",
|
||||
Expected: []sql.Row{
|
||||
{1, "t", "schema", "CREATE TABLE `t` (\n `pk` int NOT NULL,\n `c1` varchar(20),\n `c2` varchar(20),\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"},
|
||||
{2, "t", "data", "INSERT INTO `t` (`pk`,`c1`,`c2`) VALUES (1,'uno','dos');"},
|
||||
{3, "t", "data", "INSERT INTO `t` (`pk`,`c1`,`c2`) VALUES (2,'two','three');"},
|
||||
{4, "t", "data", "INSERT INTO `t` (`pk`,`c1`,`c2`) VALUES (3,'three','four');"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * from dolt_patch(@Commit1, @Commit5, 't');",
|
||||
ExpectedErr: sql.ErrTableNotFound,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "basic case with multiple tables",
|
||||
SetUpScript: []string{
|
||||
"set @Commit0 = HashOf('HEAD');",
|
||||
|
||||
// add table t with 1 row
|
||||
"create table t (pk int primary key, c1 varchar(20), c2 varchar(20));",
|
||||
"insert into t values(1, 'one', 'two');",
|
||||
"call dolt_add('.')",
|
||||
"set @Commit1 = '';",
|
||||
"call dolt_commit_hash_out(@Commit1, '-am', 'inserting into table t');",
|
||||
|
||||
// add table t2 with 1 row
|
||||
"create table t2 (pk int primary key, c1 varchar(20), c2 varchar(20));",
|
||||
"insert into t2 values(100, 'hundred', 'hundert');",
|
||||
"call dolt_add('.')",
|
||||
"set @Commit2 = '';",
|
||||
"call dolt_commit_hash_out(@Commit2, '-am', 'inserting into table t2');",
|
||||
|
||||
// changes on both tables
|
||||
"insert into t values(2, 'two', 'three'), (3, 'three', 'four'), (4, 'four', 'five');",
|
||||
"update t set c1='uno', c2='dos' where pk=1;",
|
||||
"insert into t2 values(101, 'hundred one', 'one');",
|
||||
"set @Commit3 = '';",
|
||||
"call dolt_commit_hash_out(@Commit3, '-am', 'inserting into table t');",
|
||||
|
||||
// changes on both tables
|
||||
"delete from t where c2 = 'four';",
|
||||
"update t2 set c2='zero' where pk=100;",
|
||||
"set @Commit4 = '';",
|
||||
"call dolt_commit_hash_out(@Commit4, '-am', 'inserting into table t');",
|
||||
|
||||
// create keyless table
|
||||
"create table keyless (id int);",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement from dolt_patch(@Commit0, @Commit1);",
|
||||
Expected: []sql.Row{
|
||||
{1, "t", "schema", "CREATE TABLE `t` (\n `pk` int NOT NULL,\n `c1` varchar(20),\n `c2` varchar(20),\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"},
|
||||
{2, "t", "data", "INSERT INTO `t` (`pk`,`c1`,`c2`) VALUES (1,'one','two');"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement from dolt_patch(@Commit1, @Commit2);",
|
||||
Expected: []sql.Row{
|
||||
{1, "t2", "schema", "CREATE TABLE `t2` (\n `pk` int NOT NULL,\n `c1` varchar(20),\n `c2` varchar(20),\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"},
|
||||
{2, "t2", "data", "INSERT INTO `t2` (`pk`,`c1`,`c2`) VALUES (100,'hundred','hundert');"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement from dolt_patch(@Commit2, @Commit3);",
|
||||
Expected: []sql.Row{
|
||||
{1, "t", "data", "UPDATE `t` SET `c1`='uno',`c2`='dos' WHERE `pk`=1;"},
|
||||
{2, "t", "data", "INSERT INTO `t` (`pk`,`c1`,`c2`) VALUES (2,'two','three');"},
|
||||
{3, "t", "data", "INSERT INTO `t` (`pk`,`c1`,`c2`) VALUES (3,'three','four');"},
|
||||
{4, "t", "data", "INSERT INTO `t` (`pk`,`c1`,`c2`) VALUES (4,'four','five');"},
|
||||
{5, "t2", "data", "INSERT INTO `t2` (`pk`,`c1`,`c2`) VALUES (101,'hundred one','one');"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement from dolt_patch(@Commit3, @Commit4);",
|
||||
Expected: []sql.Row{
|
||||
{1, "t", "data", "DELETE FROM `t` WHERE `pk`=3;"},
|
||||
{2, "t2", "data", "UPDATE `t2` SET `c2`='zero' WHERE `pk`=100;"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement from dolt_patch(@Commit4, @Commit2);",
|
||||
Expected: []sql.Row{
|
||||
{1, "t", "data", "UPDATE `t` SET `c1`='one',`c2`='two' WHERE `pk`=1;"},
|
||||
{2, "t", "data", "DELETE FROM `t` WHERE `pk`=2;"},
|
||||
{3, "t", "data", "DELETE FROM `t` WHERE `pk`=4;"},
|
||||
{4, "t2", "data", "UPDATE `t2` SET `c2`='hundert' WHERE `pk`=100;"},
|
||||
{5, "t2", "data", "DELETE FROM `t2` WHERE `pk`=101;"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement from dolt_patch(@Commit3, 'WORKING');",
|
||||
Expected: []sql.Row{
|
||||
{1, "keyless", "schema", "CREATE TABLE `keyless` (\n `id` int\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"},
|
||||
{2, "t", "data", "DELETE FROM `t` WHERE `pk`=3;"},
|
||||
{3, "t2", "data", "UPDATE `t2` SET `c2`='zero' WHERE `pk`=100;"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "using WORKING and STAGED refs on RENAME, DROP and ADD column",
|
||||
SetUpScript: []string{
|
||||
"set @Commit0 = HashOf('HEAD');",
|
||||
"create table t (pk int primary key, c1 int, c2 int, c3 int, c4 int, c5 int comment 'tag:5');",
|
||||
"call dolt_add('.')",
|
||||
"insert into t values (0,1,2,3,4,5), (1,1,2,3,4,5);",
|
||||
"set @Commit1 = '';",
|
||||
"call dolt_commit_hash_out(@Commit1, '-am', 'inserting two rows into table t');",
|
||||
"alter table t rename column c1 to c0",
|
||||
"alter table t drop column c4",
|
||||
"alter table t add c6 bigint",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch(@Commit1, 'WORKING', 't')",
|
||||
Expected: []sql.Row{
|
||||
{1, "t", "schema", "ALTER TABLE `t` RENAME COLUMN `c1` TO `c0`;"},
|
||||
{2, "t", "schema", "ALTER TABLE `t` DROP `c4`;"},
|
||||
{3, "t", "schema", "ALTER TABLE `t` ADD `c6` bigint;"},
|
||||
},
|
||||
ExpectedWarning: 1235,
|
||||
ExpectedWarningsCount: 1,
|
||||
},
|
||||
{
|
||||
Query: "SHOW WARNINGS;",
|
||||
Expected: []sql.Row{
|
||||
{"Warning", 1235, "Incompatible schema change, skipping data diff for table 't'"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM dolt_patch('STAGED', 'WORKING', 't')",
|
||||
Expected: []sql.Row{
|
||||
{1, "STAGED", "WORKING", "t", "schema", "ALTER TABLE `t` RENAME COLUMN `c1` TO `c0`;"},
|
||||
{2, "STAGED", "WORKING", "t", "schema", "ALTER TABLE `t` DROP `c4`;"},
|
||||
{3, "STAGED", "WORKING", "t", "schema", "ALTER TABLE `t` ADD `c6` bigint;"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM dolt_patch('STAGED..WORKING', 't')",
|
||||
Expected: []sql.Row{
|
||||
{1, "STAGED", "WORKING", "t", "schema", "ALTER TABLE `t` RENAME COLUMN `c1` TO `c0`;"},
|
||||
{2, "STAGED", "WORKING", "t", "schema", "ALTER TABLE `t` DROP `c4`;"},
|
||||
{3, "STAGED", "WORKING", "t", "schema", "ALTER TABLE `t` ADD `c6` bigint;"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM dolt_patch('WORKING', 'STAGED', 't')",
|
||||
Expected: []sql.Row{
|
||||
{1, "WORKING", "STAGED", "t", "schema", "ALTER TABLE `t` RENAME COLUMN `c0` TO `c1`;"},
|
||||
{2, "WORKING", "STAGED", "t", "schema", "ALTER TABLE `t` DROP `c6`;"},
|
||||
{3, "WORKING", "STAGED", "t", "schema", "ALTER TABLE `t` ADD `c4` int;"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('WORKING', 'WORKING', 't')",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('WORKING..WORKING', 't')",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('STAGED', 'STAGED', 't')",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "call dolt_add('.')",
|
||||
SkipResultsCheck: true,
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('WORKING', 'STAGED', 't')",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('HEAD', 'STAGED', 't')",
|
||||
Expected: []sql.Row{
|
||||
{1, "t", "schema", "ALTER TABLE `t` RENAME COLUMN `c1` TO `c0`;"},
|
||||
{2, "t", "schema", "ALTER TABLE `t` DROP `c4`;"},
|
||||
{3, "t", "schema", "ALTER TABLE `t` ADD `c6` bigint;"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "using branch refs different ways",
|
||||
SetUpScript: []string{
|
||||
"create table t (pk int primary key, c1 varchar(20), c2 varchar(20));",
|
||||
"call dolt_add('.')",
|
||||
"set @Commit1 = '';",
|
||||
"call dolt_commit_hash_out(@Commit1, '-am', 'creating table t');",
|
||||
|
||||
"insert into t values(1, 'one', 'two');",
|
||||
"set @Commit2 = '';",
|
||||
"call dolt_commit_hash_out(@Commit2, '-am', 'inserting row 1 into t in main');",
|
||||
|
||||
"CALL DOLT_checkout('-b', 'branch1');",
|
||||
"alter table t drop column c2;",
|
||||
"set @Commit3 = '';",
|
||||
"call dolt_commit_hash_out(@Commit3, '-am', 'dropping column c2 in branch1');",
|
||||
|
||||
"delete from t where pk=1;",
|
||||
"set @Commit4 = '';",
|
||||
"call dolt_commit_hash_out(@Commit4, '-am', 'deleting row 1 in branch1');",
|
||||
|
||||
"insert into t values (2, 'two');",
|
||||
"set @Commit5 = '';",
|
||||
"call dolt_commit_hash_out(@Commit5, '-am', 'inserting row 2 in branch1');",
|
||||
|
||||
"CALL DOLT_checkout('main');",
|
||||
"insert into t values (2, 'two', 'three');",
|
||||
"set @Commit6 = '';",
|
||||
"call dolt_commit_hash_out(@Commit6, '-am', 'inserting row 2 in main');",
|
||||
|
||||
"create table newtable (pk int primary key);",
|
||||
"insert into newtable values (1), (2);",
|
||||
"set @Commit7 = '';",
|
||||
"call dolt_commit_hash_out(@Commit7, '-Am', 'new table newtable');",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('main', 'branch1', 't');",
|
||||
Expected: []sql.Row{{1, "t", "schema", "ALTER TABLE `t` DROP `c2`;"}},
|
||||
},
|
||||
{
|
||||
Query: "SHOW WARNINGS",
|
||||
Expected: []sql.Row{{"Warning", 1235, "Incompatible schema change, skipping data diff for table 't'"}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('main..branch1', 't');",
|
||||
Expected: []sql.Row{{1, "t", "schema", "ALTER TABLE `t` DROP `c2`;"}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('main', 'branch1');",
|
||||
Expected: []sql.Row{
|
||||
{1, "newtable", "schema", "DROP TABLE `newtable`;"},
|
||||
{2, "t", "schema", "ALTER TABLE `t` DROP `c2`;"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('main..branch1');",
|
||||
Expected: []sql.Row{
|
||||
{1, "newtable", "schema", "DROP TABLE `newtable`;"},
|
||||
{2, "t", "schema", "ALTER TABLE `t` DROP `c2`;"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('branch1', 'main', 't');",
|
||||
Expected: []sql.Row{{1, "t", "schema", "ALTER TABLE `t` ADD `c2` varchar(20);"}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('branch1..main', 't');",
|
||||
Expected: []sql.Row{{1, "t", "schema", "ALTER TABLE `t` ADD `c2` varchar(20);"}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('main~2', 'branch1', 't');",
|
||||
Expected: []sql.Row{{1, "t", "schema", "ALTER TABLE `t` DROP `c2`;"}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('main~2..branch1', 't');",
|
||||
Expected: []sql.Row{{1, "t", "schema", "ALTER TABLE `t` DROP `c2`;"}},
|
||||
},
|
||||
// Three dot
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('main...branch1', 't');",
|
||||
Expected: []sql.Row{{1, "t", "schema", "ALTER TABLE `t` DROP `c2`;"}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('main...branch1');",
|
||||
Expected: []sql.Row{{1, "t", "schema", "ALTER TABLE `t` DROP `c2`;"}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('branch1...main', 't');",
|
||||
Expected: []sql.Row{{1, "t", "data", "INSERT INTO `t` (`pk`,`c1`,`c2`) VALUES (2,'two','three');"}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('branch1...main');",
|
||||
Expected: []sql.Row{
|
||||
{1, "newtable", "schema", "CREATE TABLE `newtable` (\n `pk` int NOT NULL,\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"},
|
||||
{2, "newtable", "data", "INSERT INTO `newtable` (`pk`) VALUES (1);"},
|
||||
{3, "newtable", "data", "INSERT INTO `newtable` (`pk`) VALUES (2);"},
|
||||
{4, "t", "data", "INSERT INTO `t` (`pk`,`c1`,`c2`) VALUES (2,'two','three');"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('branch1...main^');",
|
||||
Expected: []sql.Row{{1, "t", "data", "INSERT INTO `t` (`pk`,`c1`,`c2`) VALUES (2,'two','three');"}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('branch1...main', 'newtable');",
|
||||
Expected: []sql.Row{
|
||||
{1, "newtable", "schema", "CREATE TABLE `newtable` (\n `pk` int NOT NULL,\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"},
|
||||
{2, "newtable", "data", "INSERT INTO `newtable` (`pk`) VALUES (1);"},
|
||||
{3, "newtable", "data", "INSERT INTO `newtable` (`pk`) VALUES (2);"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('main...main', 'newtable');",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "renamed table",
|
||||
SetUpScript: []string{
|
||||
"create table t1 (a int primary key, b int)",
|
||||
"call dolt_add('.')",
|
||||
"insert into t1 values (1,2)",
|
||||
"call dolt_commit('-am', 'new table')",
|
||||
"alter table t1 rename to t2",
|
||||
"call dolt_add('.')",
|
||||
"insert into t2 values (3,4)",
|
||||
"call dolt_commit('-am', 'renamed table')",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "select statement_order, table_name, diff_type, statement FROM dolt_patch('HEAD~', 'HEAD', 't2')",
|
||||
Expected: []sql.Row{
|
||||
{1, "t2", "schema", "RENAME TABLE `t1` TO `t2`;"},
|
||||
{2, "t2", "data", "INSERT INTO `t2` (`a`,`b`) VALUES (3,4);"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "select statement_order, table_name, diff_type, statement FROM dolt_patch('HEAD~..HEAD', 't2')",
|
||||
Expected: []sql.Row{
|
||||
{1, "t2", "schema", "RENAME TABLE `t1` TO `t2`;"},
|
||||
{2, "t2", "data", "INSERT INTO `t2` (`a`,`b`) VALUES (3,4);"},
|
||||
},
|
||||
},
|
||||
{
|
||||
// Old table name can be matched as well
|
||||
Query: "select statement_order, table_name, diff_type, statement FROM dolt_patch('HEAD~', 'HEAD', 't1')",
|
||||
Expected: []sql.Row{
|
||||
{1, "t2", "schema", "RENAME TABLE `t1` TO `t2`;"},
|
||||
{2, "t2", "data", "INSERT INTO `t2` (`a`,`b`) VALUES (3,4);"},
|
||||
},
|
||||
},
|
||||
{
|
||||
// Old table name can be matched as well
|
||||
Query: "select statement_order, table_name, diff_type, statement FROM dolt_patch('HEAD~..HEAD', 't1')",
|
||||
Expected: []sql.Row{
|
||||
{1, "t2", "schema", "RENAME TABLE `t1` TO `t2`;"},
|
||||
{2, "t2", "data", "INSERT INTO `t2` (`a`,`b`) VALUES (3,4);"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "multi PRIMARY KEY and FOREIGN KEY",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE parent (id int PRIMARY KEY, id_ext int, v1 int, v2 text COMMENT 'tag:1', INDEX v1 (v1));",
|
||||
"CREATE TABLE child (id int primary key, v1 int);",
|
||||
"call dolt_add('.')",
|
||||
"call dolt_commit('-am', 'new tables')",
|
||||
"ALTER TABLE child ADD CONSTRAINT fk_named FOREIGN KEY (v1) REFERENCES parent(v1);",
|
||||
"insert into parent values (0, 1, 2, NULL);",
|
||||
"ALTER TABLE parent DROP PRIMARY KEY;",
|
||||
"ALTER TABLE parent ADD PRIMARY KEY(id, id_ext);",
|
||||
"call dolt_add('.')",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('HEAD~', 'WORKING')",
|
||||
Expected: []sql.Row{
|
||||
{1, "child", "schema", "CREATE TABLE `child` (\n `id` int NOT NULL,\n `v1` int,\n PRIMARY KEY (`id`),\n KEY `v1` (`v1`),\n CONSTRAINT `fk_named` FOREIGN KEY (`v1`) REFERENCES `parent` (`v1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"},
|
||||
{2, "parent", "schema", "CREATE TABLE `parent` (\n `id` int NOT NULL,\n `id_ext` int NOT NULL,\n `v1` int,\n `v2` text COMMENT 'tag:1',\n PRIMARY KEY (`id`,`id_ext`),\n KEY `v1` (`v1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"},
|
||||
{3, "parent", "data", "INSERT INTO `parent` (`id`,`id_ext`,`v1`,`v2`) VALUES (0,1,2,NULL);"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT statement_order, to_commit_hash, table_name, diff_type, statement FROM dolt_patch('HEAD', 'STAGED')",
|
||||
Expected: []sql.Row{
|
||||
{1, "STAGED", "child", "schema", "ALTER TABLE `child` ADD INDEX `v1`(`v1`);"},
|
||||
{2, "STAGED", "child", "schema", "ALTER TABLE `child` ADD CONSTRAINT `fk_named` FOREIGN KEY (`v1`) REFERENCES `parent` (`v1`);"},
|
||||
{3, "STAGED", "parent", "schema", "ALTER TABLE `parent` DROP PRIMARY KEY;"},
|
||||
{4, "STAGED", "parent", "schema", "ALTER TABLE `parent` ADD PRIMARY KEY (id,id_ext);"},
|
||||
},
|
||||
ExpectedWarningsCount: 2,
|
||||
},
|
||||
{
|
||||
Query: "SHOW WARNINGS;",
|
||||
Expected: []sql.Row{
|
||||
{"Warning", 1235, "Incompatible schema change, skipping data diff for table 'child'"},
|
||||
{"Warning", 1235, "Primary key sets differ between revisions for table 'parent', skipping data diff"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "CHECK CONSTRAINTS",
|
||||
SetUpScript: []string{
|
||||
"create table foo (pk int, c1 int, CHECK (c1 > 3), PRIMARY KEY (pk));",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "SELECT statement_order, table_name, diff_type, statement FROM dolt_patch('HEAD', 'WORKING')",
|
||||
Expected: []sql.Row{{1, "foo", "schema", "CREATE TABLE `foo` (\n `pk` int NOT NULL,\n `c1` int,\n PRIMARY KEY (`pk`),\n CONSTRAINT `chk_eq3jn5ra` CHECK ((c1 > 3))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var UnscopedDiffSystemTableScriptTests = []queries.ScriptTest{
|
||||
{
|
||||
Name: "working set changes",
|
||||
|
||||
@@ -1800,8 +1800,8 @@ func (t *AlterableDoltTable) CreateIndex(ctx *sql.Context, idx sql.IndexDef) err
|
||||
if err := branch_control.CheckAccess(ctx, branch_control.Permissions_Write); err != nil {
|
||||
return err
|
||||
}
|
||||
if !schema.EnableSpatialIndex && idx.Constraint != sql.IndexConstraint_None && idx.Constraint != sql.IndexConstraint_Unique {
|
||||
return fmt.Errorf("only the following types of index constraints are supported: none, unique")
|
||||
if idx.Constraint != sql.IndexConstraint_None && idx.Constraint != sql.IndexConstraint_Unique && idx.Constraint != sql.IndexConstraint_Spatial {
|
||||
return fmt.Errorf("only the following types of index constraints are supported: none, unique, spatial")
|
||||
}
|
||||
|
||||
columns := make([]string, len(idx.Columns))
|
||||
@@ -2176,8 +2176,8 @@ func (t *AlterableDoltTable) UpdateForeignKey(ctx *sql.Context, fkName string, s
|
||||
|
||||
// CreateIndexForForeignKey implements sql.ForeignKeyTable
|
||||
func (t *AlterableDoltTable) CreateIndexForForeignKey(ctx *sql.Context, idx sql.IndexDef) error {
|
||||
if !schema.EnableSpatialIndex && idx.Constraint != sql.IndexConstraint_None && idx.Constraint != sql.IndexConstraint_Unique {
|
||||
return fmt.Errorf("only the following types of index constraints are supported: none, unique")
|
||||
if idx.Constraint != sql.IndexConstraint_None && idx.Constraint != sql.IndexConstraint_Unique && idx.Constraint != sql.IndexConstraint_Spatial {
|
||||
return fmt.Errorf("only the following types of index constraints are supported: none, unique, spatial")
|
||||
}
|
||||
columns := make([]string, len(idx.Columns))
|
||||
for i, indexCol := range idx.Columns {
|
||||
|
||||
@@ -257,8 +257,8 @@ func (t *TempTable) IndexedAccess(_ sql.IndexLookup) sql.IndexedTable {
|
||||
}
|
||||
|
||||
func (t *TempTable) CreateIndex(ctx *sql.Context, idx sql.IndexDef) error {
|
||||
if !schema.EnableSpatialIndex && idx.Constraint != sql.IndexConstraint_None && idx.Constraint != sql.IndexConstraint_Unique {
|
||||
return fmt.Errorf("only the following types of index constraints are supported: none, unique")
|
||||
if idx.Constraint != sql.IndexConstraint_None && idx.Constraint != sql.IndexConstraint_Unique && idx.Constraint != sql.IndexConstraint_Spatial {
|
||||
return fmt.Errorf("only the following types of index constraints are supported: none, unique, spatial")
|
||||
}
|
||||
cols := make([]string, len(idx.Columns))
|
||||
for i, c := range idx.Columns {
|
||||
|
||||
@@ -160,7 +160,8 @@ func (k prollyKeylessWriter) uniqueKeyError(ctx context.Context, keyStr string,
|
||||
vd := k.valBld.Desc
|
||||
for from := range k.valMap {
|
||||
to := k.valMap.MapOrdinal(from)
|
||||
if existing[to], err = index.GetField(ctx, vd, from, value, k.mut.NodeStore()); err != nil {
|
||||
// offset from index for keyless rows, as first field is the count
|
||||
if existing[to], err = index.GetField(ctx, vd, from+1, value, k.mut.NodeStore()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,27 +133,27 @@ func getSecondaryKeylessProllyWriters(ctx context.Context, t *doltdb.Table, sqlS
|
||||
|
||||
// Insert implements TableWriter.
|
||||
func (w *prollyTableWriter) Insert(ctx *sql.Context, sqlRow sql.Row) (err error) {
|
||||
if err := w.primary.ValidateKeyViolations(ctx, sqlRow); err != nil {
|
||||
if err = w.primary.ValidateKeyViolations(ctx, sqlRow); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, wr := range w.secondary {
|
||||
if err := wr.ValidateKeyViolations(ctx, sqlRow); err != nil {
|
||||
if err = wr.ValidateKeyViolations(ctx, sqlRow); err != nil {
|
||||
if uke, ok := err.(secondaryUniqueKeyError); ok {
|
||||
return w.primary.(primaryIndexErrBuilder).errForSecondaryUniqueKeyError(ctx, uke)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := w.primary.Insert(ctx, sqlRow); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, wr := range w.secondary {
|
||||
if err := wr.Insert(ctx, sqlRow); err != nil {
|
||||
if err = wr.Insert(ctx, sqlRow); err != nil {
|
||||
if uke, ok := err.(secondaryUniqueKeyError); ok {
|
||||
return w.primary.(primaryIndexErrBuilder).errForSecondaryUniqueKeyError(ctx, uke)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = w.primary.Insert(ctx, sqlRow); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +104,53 @@ SQL
|
||||
[[ "$output" =~ "some error" ]] || false
|
||||
}
|
||||
|
||||
@test "checkout: dolt checkout table to restore working tree tables with add and drop foreign key" {
|
||||
dolt sql -q "create table t (c1 int primary key, c2 int, check(c2 > 0))"
|
||||
dolt sql -q "create table z (c1 int primary key, c2 int)"
|
||||
dolt commit -Am "create tables t and z"
|
||||
|
||||
dolt sql -q "ALTER TABLE z ADD CONSTRAINT foreign_key1 FOREIGN KEY (c1) references t(c1)"
|
||||
run dolt status
|
||||
[[ "$output" =~ "Changes not staged for commit:" ]] || false
|
||||
[[ "$output" =~ "modified: z" ]] || false
|
||||
|
||||
run dolt schema show z
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "foreign_key1" ]] || false
|
||||
|
||||
dolt checkout z
|
||||
|
||||
run dolt status
|
||||
[[ "$output" =~ "On branch main" ]] || false
|
||||
[[ "$output" =~ "nothing to commit, working tree clean" ]] || false
|
||||
|
||||
run dolt schema show z
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ! "$output" =~ "foreign_key1" ]] || false
|
||||
|
||||
dolt sql -q "ALTER TABLE z ADD CONSTRAINT foreign_key1 FOREIGN KEY (c1) references t(c1)"
|
||||
dolt commit -am "add fkey"
|
||||
|
||||
dolt sql -q "alter table z drop constraint foreign_key1"
|
||||
run dolt status
|
||||
[[ "$output" =~ "Changes not staged for commit:" ]] || false
|
||||
[[ "$output" =~ "modified: z" ]] || false
|
||||
|
||||
run dolt schema show z
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ! "$output" =~ "foreign_key1" ]] || false
|
||||
|
||||
dolt checkout z
|
||||
|
||||
run dolt status
|
||||
[[ "$output" =~ "On branch main" ]] || false
|
||||
[[ "$output" =~ "nothing to commit, working tree clean" ]] || false
|
||||
|
||||
run dolt schema show z
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "foreign_key1" ]] || false
|
||||
}
|
||||
|
||||
@test "checkout: with -f flag without conflict" {
|
||||
dolt sql -q 'create table test (id int primary key);'
|
||||
dolt sql -q 'insert into test (id) values (8);'
|
||||
|
||||
@@ -1224,7 +1224,7 @@ SQL
|
||||
[[ "${lines[0]}" = "count" ]] || false
|
||||
[[ "${lines[1]}" = "1" ]] || false
|
||||
|
||||
run dolt index cat mytable v1v2 -r csv
|
||||
run dolt index cat mytable ux -r csv
|
||||
[[ "${lines[0]}" = "v1,v2" ]] || false
|
||||
[[ "${lines[1]}" = "2,2" ]] || false
|
||||
[[ "${#lines[@]}" = "2" ]] || false
|
||||
@@ -1264,7 +1264,7 @@ SQL
|
||||
[[ "${lines[1]}" = "1,2,2" ]] || false
|
||||
[[ "${#lines[@]}" = "2" ]] || false
|
||||
|
||||
run dolt index cat mytable v1 -r csv
|
||||
run dolt index cat mytable ux -r csv
|
||||
[[ "${lines[0]}" = "v1" ]] || false
|
||||
[[ "${lines[1]}" = "2" ]] || false
|
||||
[[ "${lines[2]}" = "3" ]] || false
|
||||
@@ -1295,7 +1295,7 @@ SQL
|
||||
[[ "${lines[1]}" = "1,2,2" ]] || false
|
||||
[[ "${#lines[@]}" = "2" ]] || false
|
||||
|
||||
run dolt index cat mytable v1 -r csv
|
||||
run dolt index cat mytable ux -r csv
|
||||
[ $status -eq 0 ]
|
||||
[[ "${lines[0]}" = "v1" ]] || false
|
||||
[[ "${lines[1]}" = "2" ]] || false
|
||||
|
||||
@@ -10,23 +10,16 @@ teardown() {
|
||||
teardown_common
|
||||
}
|
||||
|
||||
@test "spatial-index: spatial indexes disabled" {
|
||||
skip_nbf_not_dolt
|
||||
run dolt sql -q "create table t (p point srid 0 not null, spatial index(p))"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" =~ "only the following types of index constraints are supported" ]] || false
|
||||
}
|
||||
|
||||
@test "spatial-index: spatial indexes enabled" {
|
||||
skip_nbf_not_dolt
|
||||
DOLT_ENABLE_SPATIAL_INDEX=1 run dolt sql -q "create table t (p point srid 0 not null, spatial index(p))"
|
||||
run dolt sql -q "create table t (p point srid 0 not null, spatial index(p))"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "spatial-index: not supported in old format" {
|
||||
rm -rf .dolt
|
||||
dolt init --old-format
|
||||
DOLT_ENABLE_SPATIAL_INDEX=1 run dolt sql -q "create table t (p point srid 0 not null, spatial index(p))"
|
||||
run dolt sql -q "create table t (p point srid 0 not null, spatial index(p))"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" =~ "spatial indexes are only supported in storage format" ]] || false
|
||||
}
|
||||
@@ -1,412 +0,0 @@
|
||||
#!/usr/bin/env bats
|
||||
load $BATS_TEST_DIRNAME/helper/common.bash
|
||||
|
||||
setup() {
|
||||
setup_common
|
||||
}
|
||||
|
||||
teardown() {
|
||||
assert_feature_version
|
||||
teardown_common
|
||||
}
|
||||
|
||||
@test "sql-patch: --cached flag shows staged changes" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE test (id INT PRIMARY KEY, col1 TEXT);
|
||||
SQL
|
||||
dolt add test
|
||||
|
||||
run dolt sql -q "CALL DOLT_PATCH('--cached')"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "${lines[0]}" = "+-------------------------------------------------------------------+" ]
|
||||
[ "${lines[1]}" = "| statement |" ]
|
||||
[ "${lines[2]}" = "+-------------------------------------------------------------------+" ]
|
||||
[ "${lines[3]}" = "| CREATE TABLE \`test\` ( |" ]
|
||||
[ "${lines[4]}" = "| \`id\` int NOT NULL, |" ]
|
||||
[ "${lines[5]}" = "| \`col1\` text, |" ]
|
||||
[ "${lines[6]}" = "| PRIMARY KEY (\`id\`) |" ]
|
||||
[ "${lines[7]}" = "| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin; |" ]
|
||||
[ "${lines[8]}" = "+-------------------------------------------------------------------+" ]
|
||||
}
|
||||
|
||||
@test "sql-patch: output reconciles INSERT query" {
|
||||
dolt checkout -b firstbranch
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE test (
|
||||
pk BIGINT NOT NULL COMMENT 'tag:0',
|
||||
c1 BIGINT COMMENT 'tag:1',
|
||||
c2 BIGINT COMMENT 'tag:2',
|
||||
c3 BIGINT COMMENT 'tag:3',
|
||||
c4 BIGINT COMMENT 'tag:4',
|
||||
c5 BIGINT COMMENT 'tag:5',
|
||||
PRIMARY KEY (pk)
|
||||
);
|
||||
SQL
|
||||
dolt table import -u test `batshelper 1pk5col-ints.csv`
|
||||
dolt add test
|
||||
dolt commit -m "Added two initial row"
|
||||
|
||||
dolt checkout -b newbranch
|
||||
dolt sql -q 'INSERT INTO test (pk, c1, c2, c3, c4, c5) VALUES (2, 11, 0, 0, 0, 0)'
|
||||
dolt add test
|
||||
dolt commit -m "Added a third row"
|
||||
|
||||
run dolt sql -q "CALL DOLT_PATCH('firstbranch','newbranch')"
|
||||
[ "$status" -eq 0 ]
|
||||
# 3 lines for tabular lines and 1 for header and 1 for output statement
|
||||
[ "${#lines[@]}" -eq 5 ]
|
||||
|
||||
match_diff_and_patch_results firstbranch newbranch
|
||||
}
|
||||
|
||||
@test "sql-patch: output reconciles UPDATE query" {
|
||||
dolt checkout -b firstbranch
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE test (
|
||||
pk BIGINT NOT NULL COMMENT 'tag:0',
|
||||
c1 BIGINT COMMENT 'tag:1',
|
||||
c2 BIGINT COMMENT 'tag:2',
|
||||
c3 BIGINT COMMENT 'tag:3',
|
||||
c4 BIGINT COMMENT 'tag:4',
|
||||
c5 BIGINT COMMENT 'tag:5',
|
||||
PRIMARY KEY (pk)
|
||||
);
|
||||
SQL
|
||||
dolt table import -u test `batshelper 1pk5col-ints.csv`
|
||||
dolt add test
|
||||
dolt commit -m "Added one initial row"
|
||||
|
||||
dolt checkout -b newbranch
|
||||
dolt sql -q 'UPDATE test SET c1=11, c5=6 WHERE pk=0'
|
||||
dolt add test
|
||||
dolt commit -m "modified first row"
|
||||
|
||||
run dolt sql -q "CALL DOLT_PATCH('firstbranch','newbranch')"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "${#lines[@]}" -eq 5 ]
|
||||
|
||||
match_diff_and_patch_results firstbranch newbranch
|
||||
}
|
||||
|
||||
@test "sql-patch: output reconciles DELETE query" {
|
||||
dolt checkout -b firstbranch
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE test (
|
||||
pk BIGINT NOT NULL COMMENT 'tag:0',
|
||||
c1 BIGINT COMMENT 'tag:1',
|
||||
c2 BIGINT COMMENT 'tag:2',
|
||||
c3 BIGINT COMMENT 'tag:3',
|
||||
c4 BIGINT COMMENT 'tag:4',
|
||||
c5 BIGINT COMMENT 'tag:5',
|
||||
PRIMARY KEY (pk)
|
||||
);
|
||||
SQL
|
||||
dolt table import -u test `batshelper 1pk5col-ints.csv`
|
||||
dolt add test
|
||||
dolt commit -m "Added one initial row"
|
||||
|
||||
dolt checkout -b newbranch
|
||||
dolt sql -q 'DELETE FROM test WHERE pk=0'
|
||||
dolt add test
|
||||
dolt commit -m "deleted first row"
|
||||
|
||||
run dolt sql -q "CALL DOLT_PATCH('firstbranch','newbranch')"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "${#lines[@]}" -eq 5 ]
|
||||
|
||||
match_diff_and_patch_results firstbranch newbranch
|
||||
}
|
||||
|
||||
@test "sql-patch: output reconciles change to PRIMARY KEY field in row " {
|
||||
dolt checkout -b firstbranch
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE test (
|
||||
pk BIGINT NOT NULL COMMENT 'tag:0',
|
||||
c1 BIGINT COMMENT 'tag:1',
|
||||
c2 BIGINT COMMENT 'tag:2',
|
||||
c3 BIGINT COMMENT 'tag:3',
|
||||
c4 BIGINT COMMENT 'tag:4',
|
||||
c5 BIGINT COMMENT 'tag:5',
|
||||
PRIMARY KEY (pk)
|
||||
);
|
||||
SQL
|
||||
dolt table import -u test `batshelper 1pk5col-ints.csv`
|
||||
dolt add test
|
||||
dolt commit -m "Added one initial row"
|
||||
|
||||
dolt checkout -b newbranch
|
||||
dolt sql -q 'UPDATE test SET pk=2 WHERE pk=1'
|
||||
dolt add test
|
||||
dolt commit -m "modified first row"
|
||||
|
||||
dolt sql -q "CALL DOLT_PATCH('firstbranch','newbranch')"
|
||||
run dolt sql -q "CALL DOLT_PATCH('firstbranch','newbranch')"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "${#lines[@]}" -eq 6 ]
|
||||
|
||||
match_diff_and_patch_results firstbranch newbranch
|
||||
}
|
||||
|
||||
@test "sql-patch: output reconciles RENAME, DROP and ADD column" {
|
||||
dolt checkout -b firstbranch
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE test (
|
||||
pk BIGINT NOT NULL COMMENT 'tag:0',
|
||||
c1 BIGINT COMMENT 'tag:1',
|
||||
c2 BIGINT COMMENT 'tag:2',
|
||||
c3 BIGINT COMMENT 'tag:3',
|
||||
c4 BIGINT COMMENT 'tag:4',
|
||||
c5 BIGINT COMMENT 'tag:5',
|
||||
PRIMARY KEY (pk)
|
||||
);
|
||||
SQL
|
||||
dolt sql -q 'insert into test values (1,1,1,1,1,1)'
|
||||
dolt add .
|
||||
dolt commit -m "added row"
|
||||
|
||||
dolt checkout -b newbranch
|
||||
dolt sql -q "alter table test rename column c1 to c0"
|
||||
dolt sql -q "alter table test drop column c4"
|
||||
dolt sql -q "alter table test add c6 bigint"
|
||||
dolt add .
|
||||
dolt commit -m "renamed column"
|
||||
|
||||
dolt sql -q "CALL DOLT_PATCH('firstbranch','newbranch')"
|
||||
run dolt sql -q "CALL DOLT_PATCH('firstbranch','newbranch')"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "${#lines[@]}" -eq 7 ]
|
||||
|
||||
match_diff_and_patch_results firstbranch newbranch
|
||||
}
|
||||
|
||||
@test "sql-patch: reconciles CREATE TABLE with row INSERTS" {
|
||||
dolt checkout -b firstbranch
|
||||
dolt checkout -b newbranch
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE test (
|
||||
pk BIGINT NOT NULL COMMENT 'tag:0',
|
||||
c1 BIGINT COMMENT 'tag:1',
|
||||
PRIMARY KEY (pk)
|
||||
);
|
||||
SQL
|
||||
dolt sql -q 'insert into test values (1,1)'
|
||||
dolt sql -q 'insert into test values (2,2)'
|
||||
dolt commit -Am "created new table"
|
||||
|
||||
dolt sql -q "CALL DOLT_PATCH('firstbranch','newbranch')"
|
||||
run dolt sql -q "CALL DOLT_PATCH('firstbranch','newbranch')"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "${#lines[@]}" -eq 11 ]
|
||||
|
||||
match_diff_and_patch_results firstbranch newbranch
|
||||
}
|
||||
|
||||
@test "sql-patch: reconciles DROP TABLE" {
|
||||
dolt checkout -b firstbranch
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE test (
|
||||
pk BIGINT NOT NULL COMMENT 'tag:0',
|
||||
c1 BIGINT COMMENT 'tag:1',
|
||||
c2 BIGINT COMMENT 'tag:2',
|
||||
PRIMARY KEY (pk)
|
||||
);
|
||||
SQL
|
||||
dolt sql -q 'insert into test values (1,1,1)'
|
||||
dolt add .
|
||||
dolt commit -m "setup table"
|
||||
|
||||
dolt checkout -b newbranch
|
||||
dolt sql -q 'drop table test'
|
||||
dolt add .
|
||||
dolt commit -m "removed table"
|
||||
|
||||
dolt sql -q "CALL DOLT_PATCH('firstbranch','newbranch')"
|
||||
run dolt sql -q "CALL DOLT_PATCH('firstbranch','newbranch')"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "${#lines[@]}" -eq 5 ]
|
||||
[[ ! "$output" =~ "DELETE FROM" ]] || false
|
||||
|
||||
match_diff_and_patch_results firstbranch newbranch
|
||||
}
|
||||
|
||||
@test "sql-patch: reconciles RENAME TABLE with schema changes" {
|
||||
dolt checkout -b firstbranch
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE test (
|
||||
pk BIGINT NOT NULL COMMENT 'tag:0',
|
||||
c1 BIGINT COMMENT 'tag:1',
|
||||
c2 BIGINT COMMENT 'tag:2',
|
||||
c3 BIGINT COMMENT 'tag:3',
|
||||
c4 BIGINT COMMENT 'tag:4',
|
||||
c5 BIGINT COMMENT 'tag:5',
|
||||
PRIMARY KEY (pk)
|
||||
);
|
||||
SQL
|
||||
dolt sql -q 'insert into test values (1,1,1,1,1,1)'
|
||||
dolt add .
|
||||
dolt commit -m "created table"
|
||||
|
||||
dolt checkout -b newbranch
|
||||
dolt sql -q 'ALTER TABLE test RENAME COLUMN c2 to col2'
|
||||
dolt sql -q 'ALTER TABLE test ADD COLUMN c6 int'
|
||||
dolt sql -q='RENAME TABLE test TO newname'
|
||||
dolt sql -q 'ALTER TABLE newname DROP COLUMN c3'
|
||||
dolt sql -q 'insert into newname values (2,1,1,1,1,1)'
|
||||
dolt add .
|
||||
dolt commit -m "renamed table and added data"
|
||||
|
||||
dolt sql -q "CALL DOLT_PATCH('firstbranch','newbranch')"
|
||||
run dolt sql -q "CALL DOLT_PATCH('firstbranch','newbranch')"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "${#lines[@]}" -eq 8 ]
|
||||
|
||||
match_diff_and_patch_results firstbranch newbranch
|
||||
}
|
||||
|
||||
@test "sql-patch: diff sql recreates tables with all types" {
|
||||
dolt checkout -b firstbranch
|
||||
dolt checkout -b newbranch
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE test (
|
||||
\`pk\` BIGINT NOT NULL COMMENT 'tag:0',
|
||||
\`int\` BIGINT COMMENT 'tag:1',
|
||||
\`string\` LONGTEXT COMMENT 'tag:2',
|
||||
\`boolean\` BOOLEAN COMMENT 'tag:3',
|
||||
\`float\` DOUBLE COMMENT 'tag:4',
|
||||
\`uint\` BIGINT UNSIGNED COMMENT 'tag:5',
|
||||
\`uuid\` CHAR(36) CHARACTER SET ascii COLLATE ascii_bin COMMENT 'tag:6',
|
||||
PRIMARY KEY (pk)
|
||||
);
|
||||
SQL
|
||||
# dolt table import -u test `batshelper 1pksupportedtypes.csv`
|
||||
dolt add .
|
||||
dolt commit -m "created new table"
|
||||
|
||||
dolt sql -q "CALL DOLT_PATCH('firstbranch','newbranch')"
|
||||
run dolt sql -q "CALL DOLT_PATCH('firstbranch','newbranch')"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "${#lines[@]}" -eq 14 ]
|
||||
|
||||
match_diff_and_patch_results firstbranch newbranch
|
||||
}
|
||||
|
||||
@test "sql-patch: reconciles multi PRIMARY KEY and FOREIGN KEY" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE parent (
|
||||
id int PRIMARY KEY,
|
||||
id_ext int,
|
||||
v1 int,
|
||||
v2 text COMMENT 'tag:1',
|
||||
INDEX v1 (v1)
|
||||
);
|
||||
CREATE TABLE child (
|
||||
id int primary key,
|
||||
v1 int
|
||||
);
|
||||
SQL
|
||||
dolt sql -q "ALTER TABLE child ADD CONSTRAINT fk_named FOREIGN KEY (v1) REFERENCES parent(v1);"
|
||||
dolt sql -q "insert into parent values (0, 1, 2, NULL);"
|
||||
dolt sql -q "ALTER TABLE parent DROP PRIMARY KEY;"
|
||||
dolt sql -q "ALTER TABLE parent ADD PRIMARY KEY(id, id_ext);"
|
||||
|
||||
dolt sql -q "CALL DOLT_PATCH()"
|
||||
run dolt sql -q "CALL DOLT_PATCH()"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "${#lines[@]}" -eq 20 ]
|
||||
|
||||
match_diff_and_patch_results
|
||||
}
|
||||
|
||||
@test "sql-patch: reconciles CHECK CONSTRAINTS" {
|
||||
dolt sql <<SQL
|
||||
create table foo (
|
||||
pk int,
|
||||
c1 int,
|
||||
CHECK (c1 > 3),
|
||||
PRIMARY KEY (pk)
|
||||
);
|
||||
SQL
|
||||
|
||||
dolt sql -q "CALL DOLT_PATCH()"
|
||||
run dolt sql -q "CALL DOLT_PATCH()"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "${#lines[@]}" -eq 10 ]
|
||||
|
||||
match_diff_and_patch_results
|
||||
}
|
||||
|
||||
@test "sql-patch: any error causing no data diff is shown as warnings." {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE parent (
|
||||
id int PRIMARY KEY,
|
||||
id_ext int,
|
||||
v1 int,
|
||||
v2 text COMMENT 'tag:1',
|
||||
INDEX v1 (v1)
|
||||
);
|
||||
CREATE TABLE child (
|
||||
id int primary key,
|
||||
v1 int
|
||||
);
|
||||
SQL
|
||||
dolt commit -Am "add tables"
|
||||
dolt sql -q "ALTER TABLE child ADD CONSTRAINT fk_named FOREIGN KEY (v1) REFERENCES parent(v1);"
|
||||
dolt sql -q "insert into parent values (0, 1, 2, NULL);"
|
||||
dolt sql -q "ALTER TABLE parent DROP PRIMARY KEY"
|
||||
dolt sql -q "ALTER TABLE parent ADD PRIMARY KEY(id, id_ext);"
|
||||
|
||||
run dolt diff -r sql child
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Incompatible schema change, skipping data diff for table 'child'" ]] || false
|
||||
diff_output_0=${lines[0]}
|
||||
diff_output_1=${lines[1]}
|
||||
|
||||
run dolt sql -q "CALL DOLT_PATCH('child'); SHOW WARNINGS;"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${lines[1]}" =~ "statement" ]] || false
|
||||
[[ "${lines[3]}" =~ "$diff_output_0" ]] || false
|
||||
[[ "${lines[4]}" =~ "$diff_output_1" ]] || false
|
||||
[[ "$output" =~ "Incompatible schema change, skipping data diff for table 'child'" ]] || false
|
||||
|
||||
run dolt diff -r sql parent
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Primary key sets differ between revisions for table 'parent', skipping data diff" ]] || false
|
||||
diff_output_0=${lines[0]}
|
||||
diff_output_1=${lines[1]}
|
||||
|
||||
run dolt sql -q "CALL DOLT_PATCH('parent'); SHOW WARNINGS;"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${lines[1]}" =~ "statement" ]] || false
|
||||
[[ "${lines[3]}" =~ "$diff_output_0" ]] || false
|
||||
[[ "${lines[4]}" =~ "$diff_output_1" ]] || false
|
||||
[[ "$output" =~ "Primary key sets differ between revisions for table 'parent', skipping data diff" ]] || false
|
||||
}
|
||||
|
||||
# either no arguments or give two commit revisions
|
||||
match_diff_and_patch_results() {
|
||||
if [ -z "$1" ] && [ -z "$2" ]; then
|
||||
run dolt diff -r sql
|
||||
else
|
||||
run dolt diff -r sql $1 $2
|
||||
fi
|
||||
[ "$status" -eq 0 ]
|
||||
diff_array=( "${lines[@]}" )
|
||||
|
||||
if [ -z "$1" ] && [ -z "$2" ]; then
|
||||
run dolt sql -q "CALL DOLT_PATCH()"
|
||||
else
|
||||
run dolt sql -q "CALL DOLT_PATCH('$1','$2')"
|
||||
fi
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${lines[1]}" =~ "statement" ]] || false
|
||||
patch_array=( "${lines[@]:3}" )
|
||||
# to iterate over only the row values; remove one additional line for the last tabular closure line
|
||||
idx=$(( ${#patch_array[@]} - 2 ))
|
||||
|
||||
# do not include the last element of patch_array, which will be the closing line for tabular output
|
||||
# this also removes the last element of diff_array, which can be an error message.
|
||||
for i in $(seq 0 $idx); do
|
||||
# printf "%s ---- %s\n" "${patch_array[i]}" "${diff_array[i]}"
|
||||
[[ "${patch_array[i]}" =~ "${diff_array[i]}" ]] || false
|
||||
done
|
||||
}
|
||||
@@ -156,34 +156,6 @@ teardown() {
|
||||
[[ "$output" =~ "can't use Spatial Types as Primary Key" ]] || false
|
||||
}
|
||||
|
||||
@test "sql-spatial-types: prevent creating index on point type" {
|
||||
dolt sql -q "create table point_tbl (p point)"
|
||||
run dolt sql -q "create index idx on point_tbl (p)"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" =~ "cannot create an index over spatial type columns" ]] || false
|
||||
}
|
||||
|
||||
@test "sql-spatial-types: prevent creating index on linestring types" {
|
||||
dolt sql -q "create table line_tbl (l linestring)"
|
||||
run dolt sql -q "create index idx on line_tbl (l)"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" =~ "cannot create an index over spatial type columns" ]] || false
|
||||
}
|
||||
|
||||
@test "sql-spatial-types: prevent creating index on polygon types" {
|
||||
dolt sql -q "create table poly_tbl (p polygon)"
|
||||
run dolt sql -q "create index idx on poly_tbl (p)"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" =~ "cannot create an index over spatial type columns" ]] || false
|
||||
}
|
||||
|
||||
@test "sql-spatial-types: prevent creating index on geometry types" {
|
||||
dolt sql -q "create table geom_tbl (g geometry)"
|
||||
run dolt sql -q "create index idx on geom_tbl (g)"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" =~ "cannot create an index over spatial type columns" ]] || false
|
||||
}
|
||||
|
||||
@test "sql-spatial-types: allow index on non-spatial columns of spatial table" {
|
||||
dolt sql -q "create table poly_tbl (a int, p polygon)"
|
||||
dolt sql -q "create index idx on poly_tbl (a)"
|
||||
|
||||
@@ -596,6 +596,70 @@ teardown() {
|
||||
[[ "$output" =~ "Dropped refs/stash@{0}" ]] || false
|
||||
}
|
||||
|
||||
@test "stash: popping stash with deleted table that the same table exists on current head" {
|
||||
skip_nbf_ld_1
|
||||
dolt branch branch1
|
||||
dolt branch branch2
|
||||
|
||||
dolt checkout branch1
|
||||
run dolt ls
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "test" ]] || false
|
||||
|
||||
dolt sql -q "DROP TABLE test;"
|
||||
run dolt stash
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Saved working directory and index state" ]] || false
|
||||
|
||||
dolt checkout branch2
|
||||
run dolt ls
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "test" ]] || false
|
||||
|
||||
run dolt stash list
|
||||
[ "$status" -eq 0 ]
|
||||
[ "${#lines[@]}" -eq 1 ]
|
||||
[[ "$output" =~ "stash@{0}" ]] || false
|
||||
|
||||
# if the table is the same, it's dropped
|
||||
run dolt stash pop
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Changes not staged for commit:" ]] || false
|
||||
[[ "$output" =~ "deleted: test" ]] || false
|
||||
[[ "$output" =~ "Dropped refs/stash@{0}" ]] || false
|
||||
}
|
||||
|
||||
@test "stash: popping stash with deleted table that different table with same name on current head gives conflict" {
|
||||
skip_nbf_ld_1
|
||||
dolt branch branch1
|
||||
dolt branch branch2
|
||||
|
||||
dolt checkout branch1
|
||||
run dolt ls
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "test" ]] || false
|
||||
|
||||
dolt sql -q "DROP TABLE test;"
|
||||
run dolt stash
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Saved working directory and index state" ]] || false
|
||||
|
||||
dolt checkout branch2
|
||||
dolt sql -q "DROP TABLE test;"
|
||||
dolt sql -q "CREATE TABLE test (id BIGINT PRIMARY KEY);"
|
||||
|
||||
run dolt stash list
|
||||
[ "$status" -eq 0 ]
|
||||
[ "${#lines[@]}" -eq 1 ]
|
||||
[[ "$output" =~ "stash@{0}" ]] || false
|
||||
|
||||
# if the table is different with the same name, it gives conflict
|
||||
run dolt stash pop
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" =~ "The stash entry is kept in case you need it again." ]] || false
|
||||
[[ "$output" =~ "conflict: table with same name deleted and modified " ]] || false
|
||||
}
|
||||
|
||||
@test "stash: popping stash with added table with PK on current head with the exact same table is added already" {
|
||||
skip_nbf_ld_1
|
||||
dolt branch branch1
|
||||
|
||||
Reference in New Issue
Block a user