mirror of
https://github.com/dolthub/dolt.git
synced 2026-02-14 18:18:55 -06:00
Merge branch 'main' into jennifersp-9517b259
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
package durable
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -96,6 +97,9 @@ type Table interface {
|
||||
GetAutoIncrement(ctx context.Context) (uint64, error)
|
||||
// SetAutoIncrement sets the AUTO_INCREMENT sequence value for this table.
|
||||
SetAutoIncrement(ctx context.Context, val uint64) (Table, error)
|
||||
|
||||
// DebugString returns the table contents for debugging purposes
|
||||
DebugString(ctx context.Context) string
|
||||
}
|
||||
|
||||
type nomsTable struct {
|
||||
@@ -103,6 +107,26 @@ type nomsTable struct {
|
||||
tableStruct types.Struct
|
||||
}
|
||||
|
||||
func (t nomsTable) DebugString(ctx context.Context) string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
buf.WriteString("\tStruct:\n")
|
||||
types.WriteEncodedValue(ctx, &buf, t.tableStruct)
|
||||
|
||||
buf.WriteString("\n\tRows:\n")
|
||||
data, err := t.GetTableRows(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = types.WriteEncodedValue(ctx, &buf, NomsMapFromIndex(data))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
var _ Table = nomsTable{}
|
||||
|
||||
var sharePool = pool.NewBuffPool()
|
||||
@@ -570,6 +594,10 @@ type doltDevTable struct {
|
||||
msg *serial.Table
|
||||
}
|
||||
|
||||
func (t doltDevTable) DebugString(ctx context.Context) string {
|
||||
return "doltDevTable has no DebugString" // TODO: fill in
|
||||
}
|
||||
|
||||
var _ Table = doltDevTable{}
|
||||
|
||||
type serialTableFields struct {
|
||||
|
||||
@@ -1317,20 +1317,10 @@ func (root *RootValue) DebugString(ctx context.Context, transitive bool) string
|
||||
if transitive {
|
||||
buf.WriteString("\nTables:")
|
||||
root.IterTables(ctx, func(name string, table *Table, sch schema.Schema) (stop bool, err error) {
|
||||
buf.WriteString("\nName:")
|
||||
buf.WriteString("\nTable ")
|
||||
buf.WriteString(name)
|
||||
buf.WriteString("\n")
|
||||
|
||||
buf.WriteString("Data:\n")
|
||||
data, err := table.GetNomsRowData(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = types.WriteEncodedValue(ctx, &buf, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
buf.WriteString(": \n")
|
||||
buf.WriteString(table.table.DebugString(ctx))
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -287,6 +287,8 @@ func (db Database) GetTableInsensitiveWithRoot(ctx *sql.Context, root *doltdb.Ro
|
||||
sess := dsess.DSessFromSess(ctx.Session)
|
||||
|
||||
// NOTE: system tables are not suitable for caching
|
||||
// TODO: these tables that cache a root value at construction time should not, they need to get it from the session
|
||||
// at runtime
|
||||
switch {
|
||||
case strings.HasPrefix(lwrName, doltdb.DoltDiffTablePrefix):
|
||||
suffix := tblName[len(doltdb.DoltDiffTablePrefix):]
|
||||
@@ -350,7 +352,7 @@ func (db Database) GetTableInsensitiveWithRoot(ctx *sql.Context, root *doltdb.Ro
|
||||
}
|
||||
dt, found = dtables.NewUnscopedDiffTable(ctx, db.ddb, head), true
|
||||
case doltdb.TableOfTablesInConflictName:
|
||||
dt, found = dtables.NewTableOfTablesInConflict(ctx, db.ddb, root), true
|
||||
dt, found = dtables.NewTableOfTablesInConflict(ctx, db.name, db.ddb), true
|
||||
case doltdb.TableOfTablesWithViolationsName:
|
||||
dt, found = dtables.NewTableOfTablesConstraintViolations(ctx, root), true
|
||||
case doltdb.BranchesTableName:
|
||||
|
||||
@@ -422,7 +422,6 @@ func checkForUncommittedChanges(ctx *sql.Context, root *doltdb.RootValue, headRo
|
||||
}
|
||||
|
||||
if rh != hrh {
|
||||
fmt.Printf("root: %s\nheadRoot: %s\n", root.DebugString(ctx, true), headRoot.DebugString(ctx, true))
|
||||
return ErrUncommittedChanges.New()
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -211,7 +211,14 @@ func (sess *Session) StartTransaction(ctx *sql.Context, dbName string, tCharacte
|
||||
// SetWorkingSet always sets the dirty bit, but by definition we are clean at transaction start
|
||||
sessionState.dirty = false
|
||||
|
||||
return NewDoltTransaction(ws, wsRef, sessionState.dbData, sessionState.WriteSession.GetOptions(), tCharacteristic), nil
|
||||
return NewDoltTransaction(
|
||||
dbName,
|
||||
ws,
|
||||
wsRef,
|
||||
sessionState.dbData,
|
||||
sessionState.WriteSession.GetOptions(),
|
||||
tCharacteristic,
|
||||
), nil
|
||||
}
|
||||
|
||||
func (sess *Session) newWorkingSetForHead(ctx *sql.Context, wsRef ref.WorkingSetRef, dbName string) (*doltdb.WorkingSet, error) {
|
||||
@@ -321,10 +328,20 @@ func (sess *Session) DoltCommit(
|
||||
commit *doltdb.PendingCommit,
|
||||
) (*doltdb.Commit, error) {
|
||||
commitFunc := func(ctx *sql.Context, dtx *DoltTransaction, workingSet *doltdb.WorkingSet) (*doltdb.WorkingSet, *doltdb.Commit, error) {
|
||||
return dtx.DoltCommit(
|
||||
ws, commit, err := dtx.DoltCommit(
|
||||
ctx,
|
||||
workingSet.WithWorkingRoot(commit.Roots.Working).WithStagedRoot(commit.Roots.Staged),
|
||||
commit)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Unlike normal COMMIT statements, CALL DOLT_COMMIT() doesn't get the current transaction cleared out by the query
|
||||
// engine, so we do it here.
|
||||
// TODO: the engine needs to manage this
|
||||
ctx.SetTransaction(nil)
|
||||
|
||||
return ws, commit, err
|
||||
}
|
||||
|
||||
return sess.doCommit(ctx, dbName, tx, commitFunc)
|
||||
@@ -406,7 +423,7 @@ func (sess *Session) NewPendingCommit(ctx *sql.Context, dbName string, roots dol
|
||||
|
||||
// RollbackTransaction rolls the given transaction back
|
||||
func (sess *Session) RollbackTransaction(ctx *sql.Context, dbName string, tx sql.Transaction) error {
|
||||
if !TransactionsDisabled(ctx) || dbName == "" {
|
||||
if TransactionsDisabled(ctx) || dbName == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -424,6 +441,9 @@ func (sess *Session) RollbackTransaction(ctx *sql.Context, dbName string, tx sql
|
||||
return fmt.Errorf("expected a DoltTransaction")
|
||||
}
|
||||
|
||||
// This operation usually doesn't matter, because the engine will process a `rollback` statement by first calling
|
||||
// this logic, then discarding any current transaction. So the next statement will get a fresh transaction regardless,
|
||||
// and this is throwaway work. It only matters if this method is used outside a standalone `rollback` statement.
|
||||
err = sess.SetRoot(ctx, dbName, dtx.startState.WorkingRoot())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -717,7 +737,14 @@ func (sess *Session) SwitchWorkingSet(
|
||||
tCharacteristic = sql.ReadOnly
|
||||
}
|
||||
}
|
||||
ctx.SetTransaction(NewDoltTransaction(ws, wsRef, sessionState.dbData, sessionState.WriteSession.GetOptions(), tCharacteristic))
|
||||
ctx.SetTransaction(NewDoltTransaction(
|
||||
dbName,
|
||||
ws,
|
||||
wsRef,
|
||||
sessionState.dbData,
|
||||
sessionState.WriteSession.GetOptions(),
|
||||
tCharacteristic,
|
||||
))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package dsess
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -36,6 +37,8 @@ const (
|
||||
maxTxCommitRetries = 5
|
||||
)
|
||||
|
||||
var ErrRetryTransaction = errors.New("this transaction conflicts with a committed transaction from another client, please retry")
|
||||
|
||||
func TransactionsDisabled(ctx *sql.Context) bool {
|
||||
enabled, err := ctx.GetSessionVariable(ctx, TransactionsDisabledSysVar)
|
||||
if err != nil {
|
||||
@@ -64,6 +67,7 @@ func (d DisabledTransaction) IsReadOnly() bool {
|
||||
}
|
||||
|
||||
type DoltTransaction struct {
|
||||
sourceDbName string
|
||||
startState *doltdb.WorkingSet
|
||||
workingSetRef ref.WorkingSetRef
|
||||
dbData env.DbData
|
||||
@@ -78,6 +82,7 @@ type savepoint struct {
|
||||
}
|
||||
|
||||
func NewDoltTransaction(
|
||||
dbName string,
|
||||
startState *doltdb.WorkingSet,
|
||||
workingSet ref.WorkingSetRef,
|
||||
dbData env.DbData,
|
||||
@@ -85,6 +90,7 @@ func NewDoltTransaction(
|
||||
tCharacteristic sql.TransactionCharacteristic,
|
||||
) *DoltTransaction {
|
||||
return &DoltTransaction{
|
||||
sourceDbName: dbName,
|
||||
startState: startState,
|
||||
workingSetRef: workingSet,
|
||||
dbData: dbData,
|
||||
@@ -164,10 +170,6 @@ func (tx *DoltTransaction) doCommit(
|
||||
commit *doltdb.PendingCommit,
|
||||
writeFn transactionWrite,
|
||||
) (*doltdb.WorkingSet, *doltdb.Commit, error) {
|
||||
err := checkForConflictsAndConstraintViolations(ctx, workingSet)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < maxTxCommitRetries; i++ {
|
||||
updatedWs, newCommit, err := func() (*doltdb.WorkingSet, *doltdb.Commit, error) {
|
||||
@@ -177,24 +179,28 @@ func (tx *DoltTransaction) doCommit(
|
||||
|
||||
newWorkingSet := false
|
||||
|
||||
ws, err := tx.dbData.Ddb.ResolveWorkingSet(ctx, tx.workingSetRef)
|
||||
existingWs, err := tx.dbData.Ddb.ResolveWorkingSet(ctx, tx.workingSetRef)
|
||||
if err == doltdb.ErrWorkingSetNotFound {
|
||||
// This is to handle the case where an existing DB pre working sets is committing to this HEAD for the
|
||||
// first time. Can be removed and called an error post 1.0
|
||||
ws = doltdb.EmptyWorkingSet(tx.workingSetRef)
|
||||
existingWs = doltdb.EmptyWorkingSet(tx.workingSetRef)
|
||||
newWorkingSet = true
|
||||
} else if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
wsHash, err := ws.HashOf()
|
||||
wsHash, err := existingWs.HashOf()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
existingWorkingRoot := ws.WorkingRoot()
|
||||
if newWorkingSet || rootsEqual(existingWorkingRoot, tx.startState.WorkingRoot()) {
|
||||
if newWorkingSet || rootsEqual(existingWs.WorkingRoot(), tx.startState.WorkingRoot()) {
|
||||
// ff merge
|
||||
err = tx.validateWorkingSetForCommit(ctx, workingSet, isFfMerge)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var newCommit *doltdb.Commit
|
||||
workingSet, newCommit, err = writeFn(ctx, tx, commit, workingSet, wsHash)
|
||||
if err == datas.ErrOptimisticLockFailed {
|
||||
@@ -207,34 +213,24 @@ func (tx *DoltTransaction) doCommit(
|
||||
return workingSet, newCommit, nil
|
||||
}
|
||||
|
||||
// otherwise (not a ff), merge the working sets together
|
||||
start := time.Now()
|
||||
mergedRoot, stats, err := merge.MergeRoots(ctx, existingWorkingRoot, workingSet.WorkingRoot(), tx.startState.WorkingRoot(), tx.mergeEditOpts)
|
||||
// TODO: this loses track of merge conflicts in the working set, clearing them out and replacing them with any
|
||||
// new merge conflicts produced by this merge operation. We want to preserve merge conflicts in the working set
|
||||
// given and permit them to be committed as long as a) no new ones are introduced, and b) any merge conflicts in
|
||||
// the shared working set match the merge conflicts in this one. Longer term, we will implement transaction
|
||||
// commit without a merge, making this point moot.
|
||||
mergedWorkingSet, err := tx.mergeRoots(ctx, existingWs, workingSet)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
logrus.Tracef("merge took %s", time.Since(start))
|
||||
|
||||
var tablesWithConflicts []string
|
||||
for table, mergeStats := range stats {
|
||||
if mergeStats.Conflicts > 0 {
|
||||
if transactionMergeStomp {
|
||||
tablesWithConflicts = append(tablesWithConflicts, table)
|
||||
} else {
|
||||
// TODO: surface duplicate key errors as appropriate
|
||||
return nil, nil, fmt.Errorf("conflict in table %s", table)
|
||||
}
|
||||
}
|
||||
err = tx.validateWorkingSetForCommit(ctx, mergedWorkingSet, notFfMerge)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Only resolve conflicts automatically if the stomp environment key is set
|
||||
if len(tablesWithConflicts) > 0 {
|
||||
mergedRoot, err = tx.stompConflicts(ctx, mergedRoot, tablesWithConflicts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
mergedWorkingSet := workingSet.WithWorkingRoot(mergedRoot)
|
||||
var newCommit *doltdb.Commit
|
||||
mergedWorkingSet, newCommit, err = writeFn(ctx, tx, commit, mergedWorkingSet, wsHash)
|
||||
if err == datas.ErrOptimisticLockFailed {
|
||||
@@ -258,9 +254,73 @@ func (tx *DoltTransaction) doCommit(
|
||||
return nil, nil, datas.ErrOptimisticLockFailed
|
||||
}
|
||||
|
||||
// checkForConflictsAndConstraintViolations determines which conflicts and constraint violations are ok to commit
|
||||
// given the state of certain system variables.
|
||||
func checkForConflictsAndConstraintViolations(ctx *sql.Context, workingSet *doltdb.WorkingSet) error {
|
||||
// mergeRoots merges the roots in the existing working set with the one being committed and returns the resulting
|
||||
// working set. Conflicts are automatically resolved with "accept ours" if the session settings dictate it.
|
||||
func (tx *DoltTransaction) mergeRoots(
|
||||
ctx *sql.Context,
|
||||
existingWorkingRoot *doltdb.WorkingSet,
|
||||
workingSet *doltdb.WorkingSet,
|
||||
) (*doltdb.WorkingSet, error) {
|
||||
|
||||
mergedRoot, mergeStats, err := merge.MergeRoots(
|
||||
ctx,
|
||||
existingWorkingRoot.WorkingRoot(),
|
||||
workingSet.WorkingRoot(),
|
||||
tx.startState.WorkingRoot(),
|
||||
tx.mergeEditOpts,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the conflict stomp env variable is set, resolve conflicts automatically (using the "accept ours" strategy)
|
||||
if transactionMergeStomp {
|
||||
var tablesWithConflicts []string
|
||||
for table, stat := range mergeStats {
|
||||
if stat.Conflicts > 0 {
|
||||
tablesWithConflicts = append(tablesWithConflicts, table)
|
||||
}
|
||||
}
|
||||
|
||||
if len(tablesWithConflicts) > 0 {
|
||||
mergedRoot, err = tx.stompConflicts(ctx, mergedRoot, tablesWithConflicts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return workingSet.WithWorkingRoot(mergedRoot), nil
|
||||
}
|
||||
|
||||
// rollback attempts a transaction rollback
|
||||
func (tx *DoltTransaction) rollback(ctx *sql.Context) error {
|
||||
sess := DSessFromSess(ctx.Session)
|
||||
rollbackErr := sess.RollbackTransaction(ctx, tx.sourceDbName, tx)
|
||||
if rollbackErr != nil {
|
||||
return rollbackErr
|
||||
}
|
||||
|
||||
// We also need to cancel out the transaction here so that a new one will begin on the next statement
|
||||
// TODO: it would be better for the engine to handle these details probably, this code is duplicated from the
|
||||
// rollback statement implementation in the engine.
|
||||
ctx.SetTransaction(nil)
|
||||
ctx.SetIgnoreAutoCommit(false)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ffMerge bool
|
||||
|
||||
const (
|
||||
isFfMerge = ffMerge(true)
|
||||
notFfMerge = ffMerge(false)
|
||||
)
|
||||
|
||||
// validateWorkingSetForCommit validates that the working set given is legal to commit according to the session
|
||||
// settings. Returns an error if the given working set has conflicts or constraint violations and the session settings
|
||||
// do not allow them.
|
||||
func (tx *DoltTransaction) validateWorkingSetForCommit(ctx *sql.Context, workingSet *doltdb.WorkingSet, isFf ffMerge) error {
|
||||
forceTransactionCommit, err := ctx.GetSessionVariable(ctx, ForceTransactionCommit)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -272,13 +332,31 @@ func checkForConflictsAndConstraintViolations(ctx *sql.Context, workingSet *dolt
|
||||
}
|
||||
|
||||
workingRoot := workingSet.WorkingRoot()
|
||||
hasConflicts, err := workingRoot.HasConflicts(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !(allowCommitConflicts.(int8) == 1 || forceTransactionCommit.(int8) == 1) {
|
||||
hasConflicts, err := workingRoot.HasConflicts(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
if hasConflicts {
|
||||
// Conflicts are never acceptable when they resulted from a merge with the existing working set -- it's equivalent
|
||||
// to hitting a write lock (which we didn't take). Always roll back and return an error in this case.
|
||||
if !isFf {
|
||||
rollbackErr := tx.rollback(ctx)
|
||||
if rollbackErr != nil {
|
||||
return rollbackErr
|
||||
}
|
||||
|
||||
return ErrRetryTransaction
|
||||
}
|
||||
if hasConflicts {
|
||||
|
||||
// If there were conflicts before merge with the persisted working set, whether we allow it to be committed is a
|
||||
// session setting
|
||||
if !(allowCommitConflicts.(int8) == 1 || forceTransactionCommit.(int8) == 1) {
|
||||
rollbackErr := tx.rollback(ctx)
|
||||
if rollbackErr != nil {
|
||||
return rollbackErr
|
||||
}
|
||||
|
||||
return doltdb.ErrUnresolvedConflicts
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ func init() {
|
||||
Dynamic: true,
|
||||
SetVarHintApplies: false,
|
||||
Type: sql.NewSystemBoolType(AllowCommitConflicts),
|
||||
Default: int8(1),
|
||||
Default: int8(0),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -21,19 +21,20 @@ import (
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/conflict"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
|
||||
)
|
||||
|
||||
var _ sql.Table = (*TableOfTablesInConflict)(nil)
|
||||
|
||||
// TableOfTablesInConflict is a sql.Table implementation that implements a system table which shows the current conflicts
|
||||
type TableOfTablesInConflict struct {
|
||||
ddb *doltdb.DoltDB
|
||||
root *doltdb.RootValue
|
||||
dbName string
|
||||
ddb *doltdb.DoltDB
|
||||
}
|
||||
|
||||
// NewTableOfTablesInConflict creates a TableOfTablesInConflict
|
||||
func NewTableOfTablesInConflict(_ *sql.Context, ddb *doltdb.DoltDB, root *doltdb.RootValue) sql.Table {
|
||||
return &TableOfTablesInConflict{ddb: ddb, root: root}
|
||||
func NewTableOfTablesInConflict(_ *sql.Context, dbName string, ddb *doltdb.DoltDB) sql.Table {
|
||||
return &TableOfTablesInConflict{dbName: dbName, ddb: ddb}
|
||||
}
|
||||
|
||||
// Name is a sql.Table interface function which returns the name of the table which is defined by the constant
|
||||
@@ -61,7 +62,6 @@ type tableInConflict struct {
|
||||
size uint64
|
||||
done bool
|
||||
schemas conflict.ConflictSchema
|
||||
//cnfItr types.MapIterator
|
||||
}
|
||||
|
||||
// Key returns a unique key for the partition
|
||||
@@ -111,15 +111,21 @@ func (p *tablesInConflict) Close(*sql.Context) error {
|
||||
|
||||
// Partitions is a sql.Table interface function that returns a partition of the data. Conflict data is partitioned by table.
|
||||
func (dt *TableOfTablesInConflict) Partitions(ctx *sql.Context) (sql.PartitionIter, error) {
|
||||
tblNames, err := dt.root.TablesInConflict(ctx)
|
||||
sess := dsess.DSessFromSess(ctx.Session)
|
||||
ws, err := sess.WorkingSet(ctx, dt.dbName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
root := ws.WorkingRoot()
|
||||
tblNames, err := root.TablesInConflict(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var partitions []*tableInConflict
|
||||
for _, tblName := range tblNames {
|
||||
tbl, ok, err := dt.root.GetTable(ctx, tblName)
|
||||
tbl, ok, err := root.GetTable(ctx, tblName)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -552,6 +552,7 @@ func TestStoredProcedures(t *testing.T) {
|
||||
func TestTransactions(t *testing.T) {
|
||||
skipNewFormat(t)
|
||||
enginetest.TestTransactionScripts(t, newDoltHarness(t))
|
||||
|
||||
for _, script := range DoltTransactionTests {
|
||||
enginetest.TestTransactionScript(t, newDoltHarness(t), script)
|
||||
}
|
||||
@@ -559,6 +560,10 @@ func TestTransactions(t *testing.T) {
|
||||
for _, script := range DoltSqlFuncTransactionTests {
|
||||
enginetest.TestTransactionScript(t, newDoltHarness(t), script)
|
||||
}
|
||||
|
||||
for _, script := range DoltConflictHandlingTests {
|
||||
enginetest.TestTransactionScript(t, newDoltHarness(t), script)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConcurrentTransactions(t *testing.T) {
|
||||
@@ -607,79 +612,90 @@ func TestSingleTransactionScript(t *testing.T) {
|
||||
t.Skip()
|
||||
|
||||
script := enginetest.TransactionTest{
|
||||
Name: "rollback",
|
||||
Name: "allow commit conflicts on, conflict on dolt_merge",
|
||||
SetUpScript: []string{
|
||||
"create table t (x int primary key, y int)",
|
||||
"insert into t values (1, 1)",
|
||||
"CREATE TABLE test (pk int primary key, val int)",
|
||||
"INSERT INTO test VALUES (0, 0)",
|
||||
"SELECT DOLT_COMMIT('-a', '-m', 'initial table');",
|
||||
},
|
||||
Assertions: []enginetest.ScriptTestAssertion{
|
||||
{
|
||||
Query: "/* client a */ set autocommit = off",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ set autocommit = off",
|
||||
Query: "/* client a */ set autocommit = off, dolt_allow_commit_conflicts = on",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ start transaction",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ set autocommit = off, dolt_allow_commit_conflicts = on",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ start transaction",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ insert into t values (2, 2)",
|
||||
Query: "/* client a */ insert into test values (1, 1)",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ insert into t values (3, 3)",
|
||||
Query: "/* client b */ call dolt_checkout('-b', 'new-branch')",
|
||||
SkipResultsCheck: true,
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ call dolt_commit('-am', 'commit on main')",
|
||||
SkipResultsCheck: true,
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ insert into test values (1, 2)",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ select * from t order by x",
|
||||
Expected: []sql.Row{{1, 1}, {2, 2}},
|
||||
Query: "/* client b */ call dolt_commit('-am', 'commit on new-branch')",
|
||||
SkipResultsCheck: true,
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ call dolt_merge('main')",
|
||||
Expected: []sql.Row{{0}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select count(*) from dolt_conflicts",
|
||||
Expected: []sql.Row{{1}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select * from test order by 1",
|
||||
Expected: []sql.Row{{0, 0}, {1, 2}},
|
||||
},
|
||||
{ // no error because of our session settings
|
||||
Query: "/* client b */ commit",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{ // TODO: it should be possible to do this without specifying a literal in the subselect, but it's not working
|
||||
Query: "/* client b */ update test t set val = (select their_val from dolt_conflicts_test where our_pk = 1) where pk = 1",
|
||||
Expected: []sql.Row{{sql.OkResult{
|
||||
RowsAffected: 1,
|
||||
Info: plan.UpdateInfo{
|
||||
Matched: 1,
|
||||
Updated: 1,
|
||||
},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ delete from dolt_conflicts_test",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ commit",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ select * from t order by x",
|
||||
Expected: []sql.Row{{1, 1}, {2, 2}},
|
||||
Query: "/* client b */ select * from test order by 1",
|
||||
Expected: []sql.Row{{0, 0}, {1, 1}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ rollback",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ select * from t order by x",
|
||||
Expected: []sql.Row{{1, 1}, {3, 3}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ insert into t values (2, 2)",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select * from t order by x",
|
||||
Expected: []sql.Row{{1, 1}, {3, 3}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ commit",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select * from t order by x",
|
||||
Expected: []sql.Row{{1, 1}, {3, 3}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ rollback",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select * from t order by x",
|
||||
Expected: []sql.Row{{1, 1}, {2, 2}, {3, 3}},
|
||||
Query: "/* client b */ select count(*) from dolt_conflicts",
|
||||
Expected: []sql.Row{{0}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -844,7 +844,7 @@ var DoltMerge = []enginetest.ScriptTest{
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "DOLT_MERGE with conflict is queryable and commitable until dolt_allow_commit_conflicts is turned off",
|
||||
Name: "DOLT_MERGE with conflict is queryable and committable with dolt_allow_commit_conflicts on",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk int primary key, val int)",
|
||||
"INSERT INTO test VALUES (0, 0)",
|
||||
@@ -856,6 +856,7 @@ var DoltMerge = []enginetest.ScriptTest{
|
||||
"SELECT DOLT_CHECKOUT('main');",
|
||||
"UPDATE test SET val=1001 WHERE pk=0;",
|
||||
"SELECT DOLT_COMMIT('-a', '-m', 'update a value');",
|
||||
"set dolt_allow_commit_conflicts = on",
|
||||
},
|
||||
Assertions: []enginetest.ScriptTestAssertion{
|
||||
{
|
||||
@@ -891,8 +892,8 @@ var DoltMerge = []enginetest.ScriptTest{
|
||||
ExpectedErrStr: doltdb.ErrUnresolvedConflicts.Error(),
|
||||
},
|
||||
{
|
||||
Query: "SELECT count(*) from dolt_conflicts_test", // Commit allows queries when flags are set.
|
||||
ExpectedErrStr: doltdb.ErrUnresolvedConflicts.Error(),
|
||||
Query: "SELECT count(*) from dolt_conflicts_test", // transaction has been rolled back, 0 results
|
||||
Expected: []sql.Row{{0}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -19,6 +19,8 @@ import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/plan"
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
)
|
||||
|
||||
@@ -110,17 +112,15 @@ var DoltTransactionTests = []enginetest.TransactionTest{
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ commit",
|
||||
ExpectedErrStr: "conflict in table t",
|
||||
ExpectedErrStr: dsess.ErrRetryTransaction.Error(),
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ select * from t order by x",
|
||||
Expected: []sql.Row{{1, 1}, {2, 2}},
|
||||
},
|
||||
// TODO: behavior right now is to leave the session state dirty on an unsuccessful commit, letting the
|
||||
// client choose whether to rollback or not. Not clear if this is the right behavior for a failed commit.
|
||||
{
|
||||
{ // client b gets a rollback after failed commit, so gets a new tx
|
||||
Query: "/* client b */ select * from t order by x",
|
||||
Expected: []sql.Row{{1, 1}, {2, 3}},
|
||||
Expected: []sql.Row{{1, 1}, {2, 2}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ rollback",
|
||||
@@ -263,15 +263,15 @@ var DoltTransactionTests = []enginetest.TransactionTest{
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ commit",
|
||||
ExpectedErrStr: "conflict in table t",
|
||||
ExpectedErrStr: dsess.ErrRetryTransaction.Error(),
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ select * from t order by x",
|
||||
Expected: []sql.Row{{1, 3}, {2, 3}},
|
||||
},
|
||||
{
|
||||
{ // client b got rolled back when its commit failed, so it sees the same values as client a
|
||||
Query: "/* client b */ select * from t order by x",
|
||||
Expected: []sql.Row{{1, 4}, {2, 4}},
|
||||
Expected: []sql.Row{{1, 3}, {2, 3}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ rollback",
|
||||
@@ -283,73 +283,6 @@ var DoltTransactionTests = []enginetest.TransactionTest{
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "conflicting updates, resolved with more updates",
|
||||
SetUpScript: []string{
|
||||
"create table t (x int primary key, y int)",
|
||||
"insert into t values (1, 1), (2, 2)",
|
||||
},
|
||||
Assertions: []enginetest.ScriptTestAssertion{
|
||||
{
|
||||
Query: "/* client a */ start transaction",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ start transaction",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ update t set y = 3",
|
||||
Expected: []sql.Row{{sql.OkResult{
|
||||
RowsAffected: uint64(2),
|
||||
Info: plan.UpdateInfo{
|
||||
Matched: 2,
|
||||
Updated: 2,
|
||||
},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ update t set y = 4",
|
||||
Expected: []sql.Row{{sql.OkResult{
|
||||
RowsAffected: uint64(2),
|
||||
Info: plan.UpdateInfo{
|
||||
Matched: 2,
|
||||
Updated: 2,
|
||||
},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ commit",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ commit",
|
||||
ExpectedErrStr: "conflict in table t",
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ update t set y = 3",
|
||||
Expected: []sql.Row{{sql.OkResult{
|
||||
RowsAffected: uint64(2),
|
||||
Info: plan.UpdateInfo{
|
||||
Matched: 2,
|
||||
Updated: 2,
|
||||
},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ commit",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ select * from t order by x",
|
||||
Expected: []sql.Row{{1, 3}, {2, 3}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select * from t order by x",
|
||||
Expected: []sql.Row{{1, 3}, {2, 3}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "non overlapping updates (diff rows)",
|
||||
SetUpScript: []string{
|
||||
@@ -598,7 +531,7 @@ var DoltTransactionTests = []enginetest.TransactionTest{
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ commit",
|
||||
ExpectedErrStr: "conflict in table t",
|
||||
ExpectedErrStr: dsess.ErrRetryTransaction.Error(),
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ rollback",
|
||||
@@ -741,7 +674,7 @@ var DoltTransactionTests = []enginetest.TransactionTest{
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Edits from different clients to table with out of order primary key set",
|
||||
Name: "edits from different clients to table with out of order primary key set",
|
||||
SetUpScript: []string{
|
||||
"create table test (x int, y int, z int, primary key(z, y))",
|
||||
"insert into test values (1, 1, 1), (2, 2, 2)",
|
||||
@@ -795,9 +728,402 @@ var DoltTransactionTests = []enginetest.TransactionTest{
|
||||
},
|
||||
}
|
||||
|
||||
var DoltConflictHandlingTests = []enginetest.TransactionTest{
|
||||
{
|
||||
Name: "default behavior (rollback on commit conflict)",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk int primary key, val int)",
|
||||
"INSERT INTO test VALUES (0, 0)",
|
||||
"SELECT DOLT_COMMIT('-a', '-m', 'initial table');",
|
||||
},
|
||||
Assertions: []enginetest.ScriptTestAssertion{
|
||||
{
|
||||
Query: "/* client a */ set autocommit = off",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ start transaction",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ set autocommit = off",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ start transaction",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ insert into test values (1, 1)",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ insert into test values (1, 2)",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ commit",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ commit",
|
||||
ExpectedErrStr: dsess.ErrRetryTransaction.Error(),
|
||||
},
|
||||
{ // no conflicts, transaction got rolled back
|
||||
Query: "/* client b */ select count(*) from dolt_conflicts",
|
||||
Expected: []sql.Row{{0}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select * from test order by 1",
|
||||
Expected: []sql.Row{{0, 0}, {1, 1}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "allow commit conflicts on, conflict on transaction commit",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk int primary key, val int)",
|
||||
"INSERT INTO test VALUES (0, 0)",
|
||||
"SELECT DOLT_COMMIT('-a', '-m', 'initial table');",
|
||||
},
|
||||
Assertions: []enginetest.ScriptTestAssertion{
|
||||
{
|
||||
Query: "/* client a */ set autocommit = off, dolt_allow_commit_conflicts = on",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ start transaction",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ set autocommit = off, dolt_allow_commit_conflicts = on",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ start transaction",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ insert into test values (1, 1)",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ insert into test values (1, 2)",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ commit",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ commit",
|
||||
ExpectedErrStr: dsess.ErrRetryTransaction.Error(),
|
||||
},
|
||||
{ // We see the merge value from a's commit here because we were rolled back and a new transaction begun
|
||||
Query: "/* client b */ select * from test order by 1",
|
||||
Expected: []sql.Row{{0, 0}, {1, 1}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "force commit on, conflict on transaction commit (same as dolt_allow_commit_conflicts)",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk int primary key, val int)",
|
||||
"INSERT INTO test VALUES (0, 0)",
|
||||
"SELECT DOLT_COMMIT('-a', '-m', 'initial table');",
|
||||
},
|
||||
Assertions: []enginetest.ScriptTestAssertion{
|
||||
{
|
||||
Query: "/* client a */ set autocommit = off, dolt_force_transaction_commit = on",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ start transaction",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ set autocommit = off, dolt_force_transaction_commit = on",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ start transaction",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ insert into test values (1, 1)",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ insert into test values (1, 2)",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ commit",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ commit",
|
||||
ExpectedErrStr: dsess.ErrRetryTransaction.Error(),
|
||||
},
|
||||
{ // We see the merge value from a's commit here because we were rolled back and a new transaction begun
|
||||
Query: "/* client b */ select * from test order by 1",
|
||||
Expected: []sql.Row{{0, 0}, {1, 1}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "allow commit conflicts on, conflict on dolt_merge",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk int primary key, val int)",
|
||||
"INSERT INTO test VALUES (0, 0)",
|
||||
"SELECT DOLT_COMMIT('-a', '-m', 'initial table');",
|
||||
},
|
||||
Assertions: []enginetest.ScriptTestAssertion{
|
||||
{
|
||||
Query: "/* client a */ set autocommit = off, dolt_allow_commit_conflicts = on",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ start transaction",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ set autocommit = off, dolt_allow_commit_conflicts = on",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ start transaction",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ insert into test values (1, 1)",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ call dolt_checkout('-b', 'new-branch')",
|
||||
SkipResultsCheck: true,
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ call dolt_commit('-am', 'commit on main')",
|
||||
SkipResultsCheck: true,
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ insert into test values (1, 2)",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ call dolt_commit('-am', 'commit on new-branch')",
|
||||
SkipResultsCheck: true,
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ call dolt_merge('main')",
|
||||
Expected: []sql.Row{{0}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select count(*) from dolt_conflicts",
|
||||
Expected: []sql.Row{{1}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select * from test order by 1",
|
||||
Expected: []sql.Row{{0, 0}, {1, 2}},
|
||||
},
|
||||
{ // no error because of our session settings
|
||||
// TODO: we should also be able to commit this if the other client made a compatible change
|
||||
// (has the same merge conflicts we do), but that's an error right now
|
||||
Query: "/* client b */ commit",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{ // TODO: it should be possible to do this without specifying a literal in the subselect, but it's not working
|
||||
Query: "/* client b */ update test t set val = (select their_val from dolt_conflicts_test where our_pk = 1) where pk = 1",
|
||||
Expected: []sql.Row{{sql.OkResult{
|
||||
RowsAffected: 1,
|
||||
Info: plan.UpdateInfo{
|
||||
Matched: 1,
|
||||
Updated: 1,
|
||||
},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ delete from dolt_conflicts_test",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ commit",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select * from test order by 1",
|
||||
Expected: []sql.Row{{0, 0}, {1, 1}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select count(*) from dolt_conflicts",
|
||||
Expected: []sql.Row{{0}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "force commit on, conflict on dolt_merge (same as dolt_allow_commit_conflicts)",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk int primary key, val int)",
|
||||
"INSERT INTO test VALUES (0, 0)",
|
||||
"SELECT DOLT_COMMIT('-a', '-m', 'initial table');",
|
||||
},
|
||||
Assertions: []enginetest.ScriptTestAssertion{
|
||||
{
|
||||
Query: "/* client a */ set autocommit = off, dolt_force_transaction_commit = on",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ start transaction",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ set autocommit = off, dolt_force_transaction_commit = on",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ start transaction",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ insert into test values (1, 1)",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ call dolt_checkout('-b', 'new-branch')",
|
||||
SkipResultsCheck: true,
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ call dolt_commit('-am', 'commit on main')",
|
||||
SkipResultsCheck: true,
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ insert into test values (1, 2)",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ call dolt_commit('-am', 'commit on new-branch')",
|
||||
SkipResultsCheck: true,
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ call dolt_merge('main')",
|
||||
Expected: []sql.Row{{0}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select count(*) from dolt_conflicts",
|
||||
Expected: []sql.Row{{1}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select * from test order by 1",
|
||||
Expected: []sql.Row{{0, 0}, {1, 2}},
|
||||
},
|
||||
{ // no error because of our session settings
|
||||
Query: "/* client b */ commit",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{ // TODO: it should be possible to do this without specifying a literal in the subselect, but it's not working
|
||||
Query: "/* client b */ update test t set val = (select their_val from dolt_conflicts_test where our_pk = 1) where pk = 1",
|
||||
Expected: []sql.Row{{sql.OkResult{
|
||||
RowsAffected: 1,
|
||||
Info: plan.UpdateInfo{
|
||||
Matched: 1,
|
||||
Updated: 1,
|
||||
},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ delete from dolt_conflicts_test",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ commit",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select * from test order by 1",
|
||||
Expected: []sql.Row{{0, 0}, {1, 1}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select count(*) from dolt_conflicts",
|
||||
Expected: []sql.Row{{0}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "allow commit conflicts off, conflict on dolt_merge",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk int primary key, val int)",
|
||||
"INSERT INTO test VALUES (0, 0)",
|
||||
"SELECT DOLT_COMMIT('-a', '-m', 'initial table');",
|
||||
},
|
||||
Assertions: []enginetest.ScriptTestAssertion{
|
||||
{
|
||||
Query: "/* client a */ set autocommit = off",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ start transaction",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ set autocommit = off",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ start transaction",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ insert into test values (1, 1)",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ call dolt_checkout('-b', 'new-branch')",
|
||||
SkipResultsCheck: true,
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ call dolt_commit('-am', 'commit on main')",
|
||||
SkipResultsCheck: true,
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ insert into test values (1, 2)",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ call dolt_commit('-am', 'commit on new-branch')",
|
||||
SkipResultsCheck: true,
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ call dolt_merge('main')",
|
||||
Expected: []sql.Row{{0}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select count(*) from dolt_conflicts",
|
||||
Expected: []sql.Row{{1}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ select * from test order by 1",
|
||||
Expected: []sql.Row{{0, 0}, {1, 2}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ insert into test values (2, 2)",
|
||||
Expected: []sql.Row{{sql.NewOkResult(1)}},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ commit",
|
||||
ExpectedErrStr: doltdb.ErrUnresolvedConflicts.Error(),
|
||||
},
|
||||
{ // our transaction got rolled back, so we lose the above insert
|
||||
Query: "/* client b */ select * from test order by 1",
|
||||
Expected: []sql.Row{{0, 0}, {1, 2}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var DoltSqlFuncTransactionTests = []enginetest.TransactionTest{
|
||||
{
|
||||
Name: "Committed conflicts are seen by other sessions",
|
||||
Name: "committed conflicts are seen by other sessions",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk int primary key, val int)",
|
||||
"INSERT INTO test VALUES (0, 0)",
|
||||
@@ -831,6 +1157,10 @@ var DoltSqlFuncTransactionTests = []enginetest.TransactionTest{
|
||||
Query: "/* client b */ SELECT count(*) from dolt_conflicts_test",
|
||||
Expected: []sql.Row{{0}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ set dolt_allow_commit_conflicts = 1",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ commit",
|
||||
Expected: []sql.Row{},
|
||||
@@ -867,13 +1197,13 @@ var DoltSqlFuncTransactionTests = []enginetest.TransactionTest{
|
||||
Query: "/* client a */ SELECT DOLT_MERGE('feature-branch')",
|
||||
ExpectedErrStr: doltdb.ErrUnresolvedConflicts.Error(),
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ SELECT count(*) from dolt_conflicts_test",
|
||||
ExpectedErrStr: doltdb.ErrUnresolvedConflicts.Error(),
|
||||
{ // client rolled back on merge with conflicts
|
||||
Query: "/* client a */ SELECT count(*) from dolt_conflicts_test",
|
||||
Expected: []sql.Row{{0}},
|
||||
},
|
||||
{
|
||||
Query: "/* client a */ commit",
|
||||
ExpectedErrStr: doltdb.ErrUnresolvedConflicts.Error(),
|
||||
Query: "/* client a */ commit",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "/* client b */ SELECT count(*) from dolt_conflicts_test",
|
||||
|
||||
@@ -63,8 +63,9 @@ func ProceduresTableSchema() schema.Schema {
|
||||
return schema.MustSchemaFromCols(colColl)
|
||||
}
|
||||
|
||||
// DoltProceduresGetTable returns the `dolt_procedures` table from the given db, creating it if it does not already exist.
|
||||
func DoltProceduresGetTable(ctx *sql.Context, db Database) (*WritableDoltTable, error) {
|
||||
// DoltProceduresGetOrCreateTable returns the `dolt_procedures` table from the given db, creating it in the db's
|
||||
// current root if it doesn't exist
|
||||
func DoltProceduresGetOrCreateTable(ctx *sql.Context, db Database) (*WritableDoltTable, error) {
|
||||
root, err := db.GetRoot(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -96,10 +97,29 @@ func DoltProceduresGetTable(ctx *sql.Context, db Database) (*WritableDoltTable,
|
||||
return tbl.(*WritableDoltTable), nil
|
||||
}
|
||||
|
||||
// DoltProceduresGetTable returns the `dolt_procedures` table from the given db, or nil if the table doesn't exist
|
||||
func DoltProceduresGetTable(ctx *sql.Context, db Database) (*WritableDoltTable, error) {
|
||||
root, err := db.GetRoot(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tbl, found, err := db.GetTableInsensitiveWithRoot(ctx, root, doltdb.ProceduresTableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if found {
|
||||
return tbl.(*WritableDoltTable), nil
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func DoltProceduresGetAll(ctx *sql.Context, db Database) ([]sql.StoredProcedureDetails, error) {
|
||||
tbl, err := DoltProceduresGetTable(ctx, db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if tbl == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
indexes, err := tbl.GetIndexes(ctx)
|
||||
@@ -167,7 +187,7 @@ func DoltProceduresGetAll(ctx *sql.Context, db Database) ([]sql.StoredProcedureD
|
||||
// DoltProceduresAddProcedure adds the stored procedure to the `dolt_procedures` table in the given db, creating it if
|
||||
// it does not exist.
|
||||
func DoltProceduresAddProcedure(ctx *sql.Context, db Database, spd sql.StoredProcedureDetails) (retErr error) {
|
||||
tbl, err := DoltProceduresGetTable(ctx, db)
|
||||
tbl, err := DoltProceduresGetOrCreateTable(ctx, db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -193,13 +213,17 @@ func DoltProceduresAddProcedure(ctx *sql.Context, db Database, spd sql.StoredPro
|
||||
})
|
||||
}
|
||||
|
||||
// DoltProceduresDropProcedure removes the stored procedure from the `dolt_procedures` table.
|
||||
// DoltProceduresDropProcedure removes the stored procedure from the `dolt_procedures` table. The procedure named must
|
||||
// exist.
|
||||
func DoltProceduresDropProcedure(ctx *sql.Context, db Database, name string) (retErr error) {
|
||||
strings.ToLower(name)
|
||||
tbl, err := DoltProceduresGetTable(ctx, db)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if tbl == nil {
|
||||
return sql.ErrStoredProcedureDoesNotExist.New(name)
|
||||
}
|
||||
|
||||
_, ok, err := DoltProceduresGetDetails(ctx, tbl, name)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -487,12 +487,12 @@ SQL
|
||||
dolt checkout main
|
||||
|
||||
run dolt sql <<"SQL"
|
||||
SET dolt_allow_commit_conflicts = 0;
|
||||
SELECT DOLT_MERGE('other');
|
||||
SQL
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ "conflicts" ]] || false
|
||||
run dolt sql <<"SQL"
|
||||
SET dolt_allow_commit_conflicts = 1;
|
||||
SELECT DOLT_MERGE('other');
|
||||
SQL
|
||||
[ "$status" -eq "0" ]
|
||||
|
||||
255
integration-tests/bats/import-mysqldump.bats
Normal file
255
integration-tests/bats/import-mysqldump.bats
Normal file
@@ -0,0 +1,255 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
setup() {
|
||||
REPO_NAME="dolt_repo_$$"
|
||||
mkdir $REPO_NAME
|
||||
cd $REPO_NAME
|
||||
|
||||
dolt init
|
||||
}
|
||||
|
||||
teardown() {
|
||||
cd ..
|
||||
rm -rf $REPO_NAME
|
||||
}
|
||||
|
||||
@test "import-mysqldump: database with view" {
|
||||
run dolt sql <<SQL
|
||||
DROP TABLE IF EXISTS mytable;
|
||||
CREATE TABLE mytable (
|
||||
id bigint NOT NULL,
|
||||
col2 bigint DEFAULT '999',
|
||||
col3 datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
LOCK TABLES mytable WRITE;
|
||||
/*!40000 ALTER TABLE mytable DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE mytable ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Temporary view structure for view myview
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS myview;
|
||||
/*!50001 DROP VIEW IF EXISTS myview*/;
|
||||
/*!50001 CREATE VIEW myview AS SELECT
|
||||
1 AS id,
|
||||
1 AS col2,
|
||||
1 AS col3*/;
|
||||
|
||||
--
|
||||
-- Final view structure for view myview
|
||||
--
|
||||
|
||||
/*!50001 DROP VIEW IF EXISTS myview*/;
|
||||
/*!50001 CREATE ALGORITHM=UNDEFINED */
|
||||
/*!50013 DEFINER=\`root\`@\`localhost\` SQL SECURITY DEFINER */
|
||||
/*!50001 VIEW \`myview\` AS select \`mytable\`.\`id\` AS \`id\`,\`mytable\`.\`col2\` AS \`col2\`,\`mytable\`.\`col3\` AS \`col3\` from \`mytable\` */;
|
||||
SQL
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
dolt sql -q "INSERT INTO mytable (id, col3) VALUES (1, TIMESTAMP('2003-12-31'));"
|
||||
run dolt sql -q "SELECT * FROM myview;" -r csv
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "1,999,2003-12-31 00:00:00 +0000 UTC" ]] || false
|
||||
|
||||
run dolt sql -q "SHOW CREATE VIEW myview;" -r csv
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "CREATE VIEW \`myview\` AS select \`mytable\`.\`id\` AS \`id\`,\`mytable\`.\`col2\` AS \`col2\`,\`mytable\`.\`col3\` AS \`col3\` from \`mytable\`" ]] || false
|
||||
}
|
||||
|
||||
@test "import-mysqldump: database with trigger" {
|
||||
run dolt sql <<SQL
|
||||
DROP TABLE IF EXISTS mytable;
|
||||
CREATE TABLE mytable (
|
||||
pk bigint NOT NULL,
|
||||
v1 bigint DEFAULT NULL,
|
||||
PRIMARY KEY (pk)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
LOCK TABLES mytable WRITE;
|
||||
/*!40000 ALTER TABLE mytable DISABLE KEYS */;
|
||||
INSERT INTO mytable VALUES (0,2),(1,3),(2,44);
|
||||
/*!40000 ALTER TABLE mytable ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
|
||||
/*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ;
|
||||
DELIMITER ;;
|
||||
/*!50003 CREATE*/ /*!50017 DEFINER=\`root\`@\`localhost\`*/ /*!50003 TRIGGER tt BEFORE INSERT ON mytable FOR EACH ROW SET NEW.v1 = NEW.v1 * 11 */;;
|
||||
DELIMITER ;
|
||||
/*!50003 SET sql_mode = @saved_sql_mode */ ;
|
||||
SQL
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
dolt sql -q "INSERT INTO mytable VALUES (6,8)"
|
||||
run dolt sql -q "SELECT * FROM mytable" -r csv
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "6,88" ]] || false
|
||||
|
||||
run dolt sql -q "SELECT trigger_name, event_object_table, action_statement, definer FROM information_schema.triggers" -r csv
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "tt,mytable,SET NEW.v1 = NEW.v1 * 11,\`root\`@\`localhost\`" ]] || false
|
||||
}
|
||||
|
||||
@test "import-mysqldump: database with procedure dumped with --routines flag" {
|
||||
run dolt sql <<SQL
|
||||
/*!50003 DROP PROCEDURE IF EXISTS new_proc */;
|
||||
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
|
||||
/*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ;
|
||||
DELIMITER ;;
|
||||
CREATE DEFINER=\`root\`@\`localhost\` PROCEDURE new_proc(x DOUBLE, y DOUBLE)
|
||||
SELECT x*y ;;
|
||||
DELIMITER ;
|
||||
/*!50003 SET sql_mode = @saved_sql_mode */ ;
|
||||
SQL
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run dolt sql -q "CALL new_proc(2, 3);" -r csv
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "6" ]] || false
|
||||
|
||||
run dolt sql -q "SHOW PROCEDURE STATUS" -r csv
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "new_proc,PROCEDURE,\`root\`@\`localhost\`" ]] || false
|
||||
}
|
||||
|
||||
@test "import-mysqldump: a table with all types with DEFAULT NULL dump" {
|
||||
run dolt sql <<SQL
|
||||
CREATE TABLE all_types (
|
||||
pk int NOT NULL,
|
||||
v1 binary(1) DEFAULT NULL,
|
||||
v2 bigint DEFAULT NULL,
|
||||
v3 bit(1) DEFAULT NULL,
|
||||
v4 blob,
|
||||
v5 char(1) DEFAULT NULL,
|
||||
v6 date DEFAULT NULL,
|
||||
v7 datetime DEFAULT NULL,
|
||||
v8 decimal(5,2) DEFAULT NULL,
|
||||
v9 double DEFAULT NULL,
|
||||
v10 enum('s','m','l') DEFAULT NULL,
|
||||
v11 float DEFAULT NULL,
|
||||
v12 geometry DEFAULT NULL,
|
||||
v13 int DEFAULT NULL,
|
||||
v14 json DEFAULT NULL,
|
||||
v15 linestring DEFAULT NULL,
|
||||
v16 longblob,
|
||||
v17 longtext,
|
||||
v18 mediumblob,
|
||||
v19 mediumint DEFAULT NULL,
|
||||
v20 mediumtext,
|
||||
v21 point DEFAULT NULL,
|
||||
v22 polygon DEFAULT NULL,
|
||||
v23 set('one','two') DEFAULT NULL,
|
||||
v24 smallint DEFAULT NULL,
|
||||
v25 text,
|
||||
v26 time(6) DEFAULT NULL,
|
||||
v27 timestamp NULL DEFAULT NULL,
|
||||
v28 tinyblob,
|
||||
v29 tinyint DEFAULT NULL,
|
||||
v30 tinytext,
|
||||
v31 varchar(255) DEFAULT NULL,
|
||||
v32 varbinary(255) DEFAULT NULL,
|
||||
v33 year DEFAULT NULL,
|
||||
PRIMARY KEY (pk)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
INSERT INTO all_types VALUES (2,0x01,1,0x01,0x616263,'i','2022-02-22','2022-02-22 22:22:22',999.99,1.1,'s',1.1,0x000000000101000000000000000000F03F0000000000000040,1,'{\"a\": 1}',0x0000000001020000000200000000000000000000000000000000000000000000000000F03F0000000000000040,0x616263,'abc',0x616263,1,'abc',0x000000000101000000000000000000F03F0000000000000040,0x00000000010300000001000000050000000000000000000000000000000000000000000000000020400000000000000000000000000000284000000000000022400000000000000000000000000000224000000000000000000000000000000000,'one',1,'abc','11:59:59.000000','2021-01-19 11:14:07',0x616263,1,'abc','varchar value',0x3131313131,2018);
|
||||
SQL
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
dolt sql -q "INSERT INTO all_types (pk) VALUES (1);"
|
||||
run dolt sql -q "SELECT st_aswkt(v15) from all_types;"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "LINESTRING(0 0,1 2)" ]] || false
|
||||
[[ "$output" =~ "NULL" ]] || false
|
||||
}
|
||||
|
||||
@test "import-mysqldump: a table with all types with DEFAULT not-null VALUE dump" {
|
||||
run dolt sql <<SQL
|
||||
CREATE TABLE types_default (
|
||||
pk int NOT NULL,
|
||||
v1 binary(1) DEFAULT '1',
|
||||
v2 bigint DEFAULT '1',
|
||||
v3 bit(2) DEFAULT b'10',
|
||||
v4 blob DEFAULT (_utf8mb4'abc'),
|
||||
v5 char(1) DEFAULT 'i',
|
||||
v6 date DEFAULT '2022-02-22',
|
||||
v7 datetime DEFAULT '2022-02-22 22:22:22',
|
||||
v8 decimal(5,2) DEFAULT '999.99',
|
||||
v9 double DEFAULT '1.1',
|
||||
v10 enum('s','m','l') DEFAULT 's',
|
||||
v11 float DEFAULT '1.1',
|
||||
v12 geometry DEFAULT (point(1.3,3)),
|
||||
v13 int DEFAULT '1',
|
||||
v14 json DEFAULT (json_object(_utf8mb4'a',1)),
|
||||
v15 linestring DEFAULT (linestring(point(0,0),point(1,2))),
|
||||
v16 longblob DEFAULT (_utf8mb4'abc'),
|
||||
v17 longtext DEFAULT (_utf8mb4'abc'),
|
||||
v18 mediumblob DEFAULT (_utf8mb4'abc'),
|
||||
v19 mediumint DEFAULT '1',
|
||||
v20 mediumtext DEFAULT (_utf8mb4'abc'),
|
||||
v21 point DEFAULT (point(1,2)),
|
||||
v22 polygon DEFAULT (polygon(linestring(point(0,0),point(8,0),point(12,9),point(0,9),point(0,0)))),
|
||||
v23 set('one','two') DEFAULT 'one',
|
||||
v24 smallint DEFAULT '1',
|
||||
v25 text DEFAULT (_utf8mb4'abc'),
|
||||
v26 time(6) DEFAULT '11:59:59.000000',
|
||||
v27 timestamp NULL DEFAULT '2021-01-19 11:14:07',
|
||||
v28 tinyblob DEFAULT (_utf8mb4'abc'),
|
||||
v29 tinyint DEFAULT '1',
|
||||
v30 tinytext DEFAULT (_utf8mb4'abc'),
|
||||
v31 varchar(255) DEFAULT 'varchar value',
|
||||
v32 varbinary(255) DEFAULT '11111',
|
||||
v33 year DEFAULT '2018',
|
||||
PRIMARY KEY (pk)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
SQL
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
dolt sql -q "INSERT INTO types_default (pk) VALUES (1);"
|
||||
run dolt sql -q "SELECT hex(st_aswkb(v12)) from types_default;"
|
||||
[ "$status" -eq 0 ]
|
||||
# should be "000000000101000000CDCCCCCCCCCCF43F0000000000000840"
|
||||
[[ "$output" =~ "0101000000CDCCCCCCCCCCF43F0000000000000840" ]] || false
|
||||
}
|
||||
|
||||
@test "import-mysqldump: a table with string literal representation in column definition" {
|
||||
run dolt sql <<SQL
|
||||
CREATE TABLE mytable (
|
||||
pk int NOT NULL,
|
||||
col2 int DEFAULT (date_format(now(),_utf8mb4'%Y')),
|
||||
col3 varchar(20) NOT NULL DEFAULT 'sometext',
|
||||
PRIMARY KEY (pk),
|
||||
CONSTRAINT status CHECK ((col3 like _utf8mb4'%sometext%'))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
SQL
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
dolt sql -q "INSERT INTO mytable VALUES (1, 2003, 'first_sometext');"
|
||||
run dolt sql -q "SELECT * FROM mytable;" -r csv
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "1,2003,first_sometext" ]] || false
|
||||
}
|
||||
|
||||
@test "import-mysqldump: charset introducer in tables from mysql db" {
|
||||
run dolt sql <<SQL
|
||||
CREATE TABLE engine_cost (
|
||||
engine_name varchar(64) NOT NULL,
|
||||
device_type int NOT NULL,
|
||||
cost_name varchar(64) NOT NULL,
|
||||
cost_value float DEFAULT NULL,
|
||||
last_update timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
comment varchar(1024) DEFAULT NULL,
|
||||
default_value float GENERATED ALWAYS AS ((case cost_name when _utf8mb3'io_block_read_cost' then 1.0 when _utf8mb3'memory_block_read_cost' then 0.25 else NULL end)) VIRTUAL,
|
||||
PRIMARY KEY (cost_name,engine_name,device_type)
|
||||
) /*!50100 TABLESPACE mysql */ ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC;
|
||||
SQL
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
skip "generated always as functionality is not supported"
|
||||
run dolt sql -q "SHOW CREATE TABLE engine_cost"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "GENERATED ALWAYS AS" ]] || false
|
||||
}
|
||||
@@ -42,7 +42,7 @@ teardown() {
|
||||
}
|
||||
|
||||
@test "sql-checkout: CALL DOLT_CHECKOUT just works" {
|
||||
run dolt sql -q "SELECT DOLT_CHECKOUT('-b', 'feature-branch')"
|
||||
run dolt sql -q "CALL DOLT_CHECKOUT('-b', 'feature-branch')"
|
||||
[ $status -eq 0 ]
|
||||
|
||||
# dolt sql -q "select dolt_checkout() should not change the branch
|
||||
@@ -62,6 +62,13 @@ teardown() {
|
||||
run dolt status
|
||||
[ $status -eq 0 ]
|
||||
[[ "$output" =~ "main" ]] || false
|
||||
|
||||
# Should also work in a transaction
|
||||
dolt sql <<SQL
|
||||
START TRANSACTION;
|
||||
CALL DOLT_CHECKOUT('-b', 'new-branch');
|
||||
SQL
|
||||
|
||||
}
|
||||
|
||||
@test "sql-checkout: DOLT_CHECKOUT -b throws error on branches that already exist" {
|
||||
|
||||
@@ -887,8 +887,9 @@ SQL
|
||||
[[ "${lines[1]}" =~ "nothing to commit, working tree clean" ]] || false
|
||||
}
|
||||
|
||||
@test "sql-merge: DOLT_MERGE can correctly commit unresolved conflicts" {
|
||||
@test "sql-merge: DOLT_MERGE can commit unresolved conflicts with dolt_allow_commit_conflicts set" {
|
||||
dolt sql << SQL
|
||||
set dolt_allow_commit_conflicts = on;
|
||||
CREATE TABLE one_pk (
|
||||
pk1 BIGINT NOT NULL,
|
||||
c1 BIGINT,
|
||||
@@ -915,8 +916,9 @@ SQL
|
||||
[[ $output =~ "merge has unresolved conflicts" ]] || false
|
||||
}
|
||||
|
||||
@test "sql-merge: CALL DOLT_MERGE can correctly commit unresolved conflicts" {
|
||||
@test "sql-merge: CALL DOLT_MERGE can commit unresolved conflicts with dolt_allow_commit_conflicts on" {
|
||||
dolt sql << SQL
|
||||
set dolt_allow_commit_conflicts = on;
|
||||
CREATE TABLE one_pk (
|
||||
pk1 BIGINT NOT NULL,
|
||||
c1 BIGINT,
|
||||
|
||||
Reference in New Issue
Block a user