github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/merge/schema_merge_test.go (about)

     1  // Copyright 2023 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package merge_test
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  
    21  	"github.com/dolthub/go-mysql-server/sql"
    22  	"github.com/shopspring/decimal"
    23  	"github.com/stretchr/testify/assert"
    24  	"github.com/stretchr/testify/require"
    25  
    26  	"github.com/dolthub/dolt/go/cmd/dolt/commands/engine"
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
    29  	"github.com/dolthub/dolt/go/libraries/doltcore/dtestutils"
    30  	"github.com/dolthub/dolt/go/libraries/doltcore/merge"
    31  	"github.com/dolthub/dolt/go/libraries/doltcore/ref"
    32  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    33  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
    34  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
    35  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/writer"
    36  	"github.com/dolthub/dolt/go/libraries/doltcore/table/editor"
    37  	"github.com/dolthub/dolt/go/store/hash"
    38  	"github.com/dolthub/dolt/go/store/types"
    39  )
    40  
    41  type schemaMergeTest struct {
    42  	name                string
    43  	ancestor            table
    44  	left, right         *table
    45  	merged              table
    46  	conflict            bool
    47  	skipNewFmt          bool
    48  	skipOldFmt          bool
    49  	skipFlipOnNewFormat bool
    50  	skipFlipOnOldFormat bool
    51  	dataTests           []dataTest
    52  }
    53  
    54  type dataTest struct {
    55  	name                 string
    56  	ancestor             []sql.Row
    57  	left, right          []sql.Row
    58  	merged               []sql.Row
    59  	constraintViolations []constraintViolation
    60  	dataConflict         bool
    61  	skip                 bool
    62  	skipFlip             bool
    63  }
    64  
    65  type table struct {
    66  	ns   namedSchema
    67  	rows []sql.Row
    68  }
    69  
    70  type namedSchema struct {
    71  	name   string
    72  	sch    schema.Schema
    73  	create string
    74  }
    75  
    76  // TestSchemaMerge are schema merge integration tests from 2023
    77  func TestSchemaMerge(t *testing.T) {
    78  	t.Run("column add/drop tests", func(t *testing.T) {
    79  		testSchemaMerge(t, columnAddDropTests)
    80  	})
    81  	t.Run("column default tests", func(t *testing.T) {
    82  		testSchemaMerge(t, columnDefaultTests)
    83  	})
    84  	t.Run("collation tests", func(t *testing.T) {
    85  		testSchemaMerge(t, collationTests)
    86  	})
    87  	t.Run("nullability tests", func(t *testing.T) {
    88  		testSchemaMerge(t, nullabilityTests)
    89  	})
    90  	t.Run("column type change tests", func(t *testing.T) {
    91  		testSchemaMerge(t, typeChangeTests)
    92  	})
    93  	t.Run("column reordering tests", func(t *testing.T) {
    94  		testSchemaMerge(t, columnReorderingTests)
    95  	})
    96  	t.Run("primary key change tests", func(t *testing.T) {
    97  		testSchemaMerge(t, keyChangeTests)
    98  	})
    99  	t.Run("secondary index tests", func(t *testing.T) {
   100  		testSchemaMerge(t, secondaryIndexTests)
   101  	})
   102  	t.Run("simple conflict tests", func(t *testing.T) {
   103  		testSchemaMerge(t, simpleConflictTests)
   104  	})
   105  	t.Run("json merge tests", func(t *testing.T) {
   106  		testSchemaMerge(t, jsonMergeTests)
   107  	})
   108  }
   109  
   110  var columnAddDropTests = []schemaMergeTest{
   111  	{
   112  		name:     "no schema changes",
   113  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)"), row(1)),
   114  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY)"), row(1)),
   115  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY)"), row(1)),
   116  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)"), row(1)),
   117  	},
   118  	// one side changes columns
   119  	{
   120  		name:     "left side column add",
   121  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)       ")),
   122  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)")),
   123  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY)       ")),
   124  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)")),
   125  		dataTests: []dataTest{
   126  			{
   127  				name:     "left side adds column and assigns non-null value",
   128  				ancestor: singleRow(1),
   129  				left:     singleRow(1, 2),
   130  				right:    singleRow(1),
   131  				merged:   singleRow(1, 2),
   132  			},
   133  			{
   134  				name:     "left side adds column and assigns null value",
   135  				ancestor: singleRow(1),
   136  				left:     singleRow(1, nil),
   137  				right:    singleRow(1),
   138  				merged:   singleRow(1, nil),
   139  			},
   140  		},
   141  	},
   142  	{
   143  		name:     "left side column add with additional column after",
   144  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int)       ")),
   145  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)")),
   146  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int)       ")),
   147  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)")),
   148  		dataTests: []dataTest{
   149  			{
   150  				name:     "left side adds column and assigns non-null value, extra column is non-NULL",
   151  				ancestor: singleRow(1, 3),
   152  				left:     singleRow(1, 2, 3),
   153  				right:    singleRow(1, 3),
   154  				merged:   singleRow(1, 2, 3),
   155  			},
   156  			{
   157  				name:     "left side adds column and assigns null value, extra column is non-NULL",
   158  				ancestor: singleRow(1, 3),
   159  				left:     singleRow(1, nil, 3),
   160  				right:    singleRow(1, 3),
   161  				merged:   singleRow(1, nil, 3),
   162  			},
   163  			{
   164  				// Skipped because of (https://github.com/dolthub/dolt/issues/6745)
   165  				name:     "left side adds column and assigns non-null value, extra column has data change on right",
   166  				ancestor: singleRow(1, 3),
   167  				left:     singleRow(1, 2, 3),
   168  				right:    singleRow(1, 4),
   169  				merged:   singleRow(1, 2, 4),
   170  				skipFlip: true,
   171  			},
   172  			{
   173  				// Skipped because of (https://github.com/dolthub/dolt/issues/6745)
   174  				name:     "left side adds column and assigns non-null value, extra column has data change on right to NULL",
   175  				ancestor: singleRow(1, 3),
   176  				left:     singleRow(1, 2, 3),
   177  				right:    singleRow(1, nil),
   178  				merged:   singleRow(1, 2, nil),
   179  				skipFlip: true,
   180  			},
   181  			{
   182  				// Skipped because of (https://github.com/dolthub/dolt/issues/6745)
   183  				name:     "left side adds column and assigns non-null value, extra column has data change on right to non-NULL",
   184  				ancestor: singleRow(1, nil),
   185  				left:     singleRow(1, 2, nil),
   186  				right:    singleRow(1, 3),
   187  				merged:   singleRow(1, 2, 3),
   188  				skipFlip: true,
   189  			},
   190  			{
   191  				name:     "left side adds column and assigns non-null value, extra column is NULL",
   192  				ancestor: singleRow(1, nil),
   193  				left:     singleRow(1, 2, nil),
   194  				right:    singleRow(1, nil),
   195  				merged:   singleRow(1, 2, nil),
   196  			},
   197  			{
   198  				name:     "left side adds column and assigns null value, extra column is NULL",
   199  				ancestor: singleRow(1, nil),
   200  				left:     singleRow(1, nil, nil),
   201  				right:    singleRow(1, nil),
   202  				merged:   singleRow(1, nil, nil),
   203  			},
   204  			{
   205  				// Skipped because of (https://github.com/dolthub/dolt/issues/6745)
   206  				name:     "left side adds column and assigns null value, extra column has data change on right",
   207  				ancestor: singleRow(1, 3),
   208  				left:     singleRow(1, nil, 3),
   209  				right:    singleRow(1, 4),
   210  				merged:   singleRow(1, nil, 4),
   211  				skipFlip: true,
   212  			},
   213  			{
   214  				// Skipped because of (https://github.com/dolthub/dolt/issues/6745)
   215  				name:     "left side adds column and assigns null value, extra column has data change on right to NULL",
   216  				ancestor: singleRow(1, 3),
   217  				left:     singleRow(1, nil, 3),
   218  				right:    singleRow(1, nil),
   219  				merged:   singleRow(1, nil, nil),
   220  				skipFlip: true,
   221  			},
   222  			{
   223  				// Skipped because of (https://github.com/dolthub/dolt/issues/6745)
   224  				name:     "left side adds column and assigns null value, extra column has data change on right to non-NULL",
   225  				ancestor: singleRow(1, nil),
   226  				left:     singleRow(1, nil, nil),
   227  				right:    singleRow(1, 3),
   228  				merged:   singleRow(1, nil, 3),
   229  				skipFlip: true,
   230  			},
   231  		},
   232  	},
   233  	{
   234  		name:     "left side column drop",
   235  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, a int)")),
   236  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int)       ")),
   237  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, a int)")),
   238  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int)       ")),
   239  		dataTests: []dataTest{
   240  			{
   241  				name:     "no data change",
   242  				ancestor: singleRow(1, 2, 3),
   243  				left:     singleRow(1, 2),
   244  				right:    singleRow(1, 2, 3),
   245  				merged:   singleRow(1, 2),
   246  			},
   247  			{
   248  				name:         "one side sets to NULL, other drops non-NULL",
   249  				ancestor:     singleRow(1, 2, 3),
   250  				left:         singleRow(1, 2),
   251  				right:        singleRow(1, 2, nil),
   252  				dataConflict: true,
   253  				skip:         true,
   254  			},
   255  			{
   256  				name:         "one side sets to NULL, other drops non-NULL, plus data change",
   257  				ancestor:     singleRow(1, 2, 3),
   258  				left:         singleRow(1, 2),
   259  				right:        singleRow(1, 3, nil),
   260  				dataConflict: true,
   261  			},
   262  			{
   263  				name:         "one side sets to non-NULL, other drops NULL",
   264  				ancestor:     singleRow(1, 2, nil),
   265  				left:         singleRow(1, 2),
   266  				right:        singleRow(1, 2, 3),
   267  				dataConflict: true,
   268  			},
   269  			{
   270  				name:         "one side sets to non-NULL, other drops NULL, plus data change",
   271  				ancestor:     singleRow(1, 2, nil),
   272  				left:         singleRow(1, 3),
   273  				right:        singleRow(1, 2, 3),
   274  				dataConflict: true,
   275  			},
   276  			{
   277  				name:         "one side sets to non-NULL, other drops non-NULL",
   278  				ancestor:     singleRow(1, 2, 3),
   279  				left:         singleRow(1, 2),
   280  				right:        singleRow(1, 2, 4),
   281  				dataConflict: true,
   282  			},
   283  			{
   284  				name:     "one side drops column, other deletes row",
   285  				ancestor: []sql.Row{row(1, 2, 3), row(4, 5, 6)},
   286  				left:     []sql.Row{row(1, 2), row(4, 5)},
   287  				right:    []sql.Row{row(1, 2, 3)},
   288  				merged:   []sql.Row{row(1, 2)},
   289  			},
   290  		},
   291  	},
   292  	{
   293  		name:     "left side column drop with additional column after",
   294  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)")),
   295  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int)       ")),
   296  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)")),
   297  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int)       ")),
   298  		dataTests: []dataTest{
   299  			{
   300  				name:     "no data change",
   301  				ancestor: singleRow(1, 2, 3),
   302  				left:     singleRow(1, 3),
   303  				right:    singleRow(1, 2, 3),
   304  				merged:   singleRow(1, 3),
   305  			},
   306  			{
   307  				name:         "one side sets to NULL, other drops non-NULL",
   308  				ancestor:     singleRow(1, 2, 3),
   309  				left:         singleRow(1, 3),
   310  				right:        singleRow(1, nil, 3),
   311  				dataConflict: true,
   312  			},
   313  			{
   314  				name:         "one side sets to NULL, other drops non-NULL, plus data change",
   315  				ancestor:     singleRow(1, 2, 4),
   316  				left:         singleRow(1, 3),
   317  				right:        singleRow(1, nil, 4),
   318  				dataConflict: true,
   319  			},
   320  			{
   321  				name:         "one side sets to non-NULL, other drops NULL, plus data change",
   322  				ancestor:     singleRow(1, nil, 3),
   323  				left:         singleRow(1, 3),
   324  				right:        singleRow(1, 2, 3),
   325  				dataConflict: true,
   326  			},
   327  			{
   328  				name:         "one side sets to non-NULL, other drops NULL, plus data change",
   329  				ancestor:     singleRow(1, nil, 3),
   330  				left:         singleRow(1, 4),
   331  				right:        singleRow(1, 2, 3),
   332  				dataConflict: true,
   333  			},
   334  			{
   335  				name:         "one side sets to non-NULL, other drops non-NULL",
   336  				ancestor:     singleRow(1, 2, 3),
   337  				left:         singleRow(1, 3),
   338  				right:        singleRow(1, 4, 3),
   339  				dataConflict: true,
   340  			},
   341  		},
   342  	},
   343  	// both sides change columns
   344  	{
   345  		name:       "independent column adds",
   346  		ancestor:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)              "), row(1)),
   347  		left:       tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)       "), row(1, 2)),
   348  		right:      tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int)       "), row(1, 3)),
   349  		merged:     *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)"), row(1, 2, 3)),
   350  		skipNewFmt: true,
   351  		skipOldFmt: true,
   352  	},
   353  	{
   354  		name:     "independent column drops",
   355  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)"), row(1, 2, 3)),
   356  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)       "), row(1, 2)),
   357  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int)       "), row(1, 3)),
   358  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)              "), row(1)),
   359  	},
   360  	{
   361  		name:     "convergent column adds",
   362  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int)       ")),
   363  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, a int)")),
   364  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, a int)")),
   365  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, a int)")),
   366  		dataTests: []dataTest{
   367  			{
   368  				name:     "convergent adds assigning null",
   369  				ancestor: singleRow(1, 2),
   370  				left:     singleRow(1, 2, nil),
   371  				right:    singleRow(1, 2, nil),
   372  				merged:   singleRow(1, 2, nil),
   373  			},
   374  			{
   375  				name:         "convergent adds with differing nullness",
   376  				ancestor:     singleRow(1, 2),
   377  				left:         singleRow(1, 2, nil),
   378  				right:        singleRow(1, 2, 3),
   379  				dataConflict: true,
   380  			},
   381  			{
   382  				name:         "convergent adds with differing nullness, plus convergent data change",
   383  				ancestor:     singleRow(1, 2),
   384  				left:         singleRow(1, 3, nil),
   385  				right:        singleRow(1, 3, 4),
   386  				dataConflict: true,
   387  			},
   388  		},
   389  	},
   390  	{
   391  		name:     "convergent column add in middle of schema",
   392  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)       ")),
   393  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, a int)")),
   394  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, a int)")),
   395  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, a int)")),
   396  		dataTests: []dataTest{
   397  			{
   398  				name:     "convergent adds assigning null",
   399  				ancestor: singleRow(1, 2),
   400  				left:     singleRow(1, nil, 2),
   401  				right:    singleRow(1, nil, 2),
   402  				merged:   singleRow(1, nil, 2),
   403  			},
   404  			{
   405  				name:         "convergent adds with differing nullness",
   406  				ancestor:     singleRow(1, 2),
   407  				left:         singleRow(1, nil, 2),
   408  				right:        singleRow(1, 3, 2),
   409  				dataConflict: true,
   410  			},
   411  			{
   412  				name:         "convergent adds with differing nullness, plus convergent data change",
   413  				ancestor:     singleRow(1, 2),
   414  				left:         singleRow(1, nil, 3),
   415  				right:        singleRow(1, 4, 3),
   416  				dataConflict: true,
   417  			},
   418  		},
   419  	},
   420  	{
   421  		name:     "convergent column drops",
   422  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)")),
   423  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY)       ")),
   424  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY)       ")),
   425  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)       ")),
   426  		dataTests: []dataTest{
   427  			{
   428  				name:     "no data change",
   429  				ancestor: singleRow(1, 2),
   430  				left:     singleRow(1),
   431  				right:    singleRow(1),
   432  				merged:   singleRow(1),
   433  			},
   434  			{
   435  				name:     "convergent drops on new row",
   436  				ancestor: nil,
   437  				left:     singleRow(1),
   438  				right:    singleRow(1),
   439  				merged:   singleRow(1),
   440  			},
   441  		},
   442  	},
   443  	{
   444  		name:     "convergent column adds, independent drops",
   445  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)"), row(1, 2, 3)),
   446  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, c int)"), row(1, 3, 4)),
   447  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, c int)"), row(1, 2, 4)),
   448  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int)       "), row(1, 4)),
   449  	},
   450  	{
   451  		name:       "convergent column drops, independent adds",
   452  		ancestor:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)       "), row(1, 2)),
   453  		left:       tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int)       "), row(1, 3)),
   454  		right:      tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int)       "), row(1, 4)),
   455  		merged:     *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, c int)"), row(1, 3, 4)),
   456  		skipNewFmt: true,
   457  		skipOldFmt: true,
   458  	},
   459  	// one side changes columns, the other inserts rows
   460  	{
   461  		name:     "left side column add, right side insert row",
   462  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)       "), row(1)),
   463  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)"), row(1, 2)),
   464  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY)       "), row(1), row(11)),
   465  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)"), row(1, 2), row(11, nil)),
   466  	},
   467  	{
   468  		name:     "left side column drop, right side insert row",
   469  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)"), row(1, 2)),
   470  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY)       "), row(1)),
   471  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)"), row(1, 2), row(11, 22)),
   472  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)       "), row(1), row(11)),
   473  	},
   474  	// both sides change columns and insert rows
   475  	{
   476  		name:       "independent column adds, both sides insert independent rows",
   477  		ancestor:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)              "), row(1)),
   478  		left:       tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)       "), row(1, 2), row(12, 22)),
   479  		right:      tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int)       "), row(1, 3), row(13, 33)),
   480  		merged:     *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)"), row(1, 2, 3), row(12, 22, nil), row(13, nil, 33)),
   481  		skipNewFmt: true,
   482  		skipOldFmt: true,
   483  	},
   484  	{
   485  		name:     "independent column drops, both sides insert independent rows",
   486  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)"), row(1, 2, 3)),
   487  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)       "), row(1, 2), row(12, 22)),
   488  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int)       "), row(1, 3), row(13, 33)),
   489  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)              "), row(1), row(12), row(13)),
   490  	},
   491  	{
   492  		name:     "convergent column adds, both sides insert independent rows",
   493  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)       "), row(1)),
   494  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)"), row(1, nil), row(12, 22)),
   495  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)"), row(1, nil), row(13, 33)),
   496  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)"), row(1, nil), row(12, 22), row(13, 33)),
   497  	},
   498  	{
   499  		name:     "convergent column drops, both sides insert independent rows",
   500  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)"), row(1, 2)),
   501  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY)       "), row(1), row(12)),
   502  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY)       "), row(1), row(13)),
   503  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)       "), row(1), row(12), row(13)),
   504  	},
   505  	{
   506  		name:       "independent column adds, both sides insert same row",
   507  		ancestor:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)              "), row(1)),
   508  		left:       tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)       "), row(1, 2), row(12, 22)),
   509  		right:      tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int)       "), row(1, 3), row(12, 33)),
   510  		merged:     *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)"), row(1, 2, 3), row(12, 22, 33)),
   511  		skipNewFmt: true,
   512  		skipOldFmt: true,
   513  	},
   514  	{
   515  		name:     "independent column drops, both sides insert same row",
   516  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)"), row(1, 2, 3)),
   517  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)       "), row(1, 2), row(12, 22)),
   518  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int)       "), row(1, 3), row(12, 33)),
   519  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)              "), row(1), row(12)),
   520  	},
   521  	{
   522  		name:     "right side drops and adds column of same type",
   523  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int, a int)")),
   524  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int, a int)")),
   525  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int, b int)")),
   526  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int, b int)")),
   527  		dataTests: []dataTest{
   528  			{
   529  				name:         "left side modifies dropped column",
   530  				ancestor:     singleRow(1, 1, 2),
   531  				left:         singleRow(1, 1, 3),
   532  				right:        singleRow(1, 2, 2),
   533  				dataConflict: true,
   534  			},
   535  		},
   536  	},
   537  	{
   538  		name:     "right side drops and adds column of different type",
   539  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int, a int)")),
   540  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int, a int)")),
   541  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int, b text)")),
   542  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int, b text)")),
   543  	},
   544  }
   545  
   546  type constraintViolation struct {
   547  	violationType merge.CvType
   548  	key, value    sql.Row
   549  }
   550  
   551  var collationTests = []schemaMergeTest{
   552  	{
   553  		name:     "left side changes collation",
   554  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(10) collate utf8mb4_0900_bin unique)")),
   555  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(10) collate utf8mb4_0900_ai_ci unique)")),
   556  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(10) collate utf8mb4_0900_bin unique)")),
   557  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(10) collate utf8mb4_0900_ai_ci unique)")),
   558  		dataTests: []dataTest{
   559  			{
   560  				name:     "no data change",
   561  				ancestor: singleRow(1, "hello"),
   562  				left:     singleRow(1, "hello"),
   563  				right:    singleRow(1, "hello"),
   564  				merged:   singleRow(1, "hello"),
   565  			},
   566  			{
   567  				name:     "right side insert",
   568  				ancestor: []sql.Row{{1, "hello"}},
   569  				left:     []sql.Row{{1, "hello"}},
   570  				right:    []sql.Row{{1, "hello"}, {2, "world"}},
   571  				merged:   []sql.Row{{1, "hello"}, {2, "world"}},
   572  			},
   573  			{
   574  				name:     "right side delete",
   575  				ancestor: []sql.Row{{1, "hello"}, {2, "world"}},
   576  				left:     []sql.Row{{1, "hello"}, {2, "world"}},
   577  				right:    []sql.Row{{1, "hello"}},
   578  				merged:   []sql.Row{{1, "hello"}},
   579  			},
   580  			{
   581  				name:     "right side insert causes unique violation",
   582  				ancestor: []sql.Row{{1, "hello"}},
   583  				left:     []sql.Row{{1, "hello"}},
   584  				right:    []sql.Row{{1, "hello"}, {2, "HELLO"}},
   585  				constraintViolations: []constraintViolation{
   586  					{merge.CvType_UniqueIndex, sql.Row{int32(1)}, sql.Row{"hello"}},
   587  					{merge.CvType_UniqueIndex, sql.Row{int32(2)}, sql.Row{"HELLO"}},
   588  				},
   589  			},
   590  		},
   591  	},
   592  	{
   593  		name:     "left side changes table collation",
   594  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(10) unique) collate utf8mb4_0900_bin")),
   595  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(10) unique) collate utf8mb4_0900_ai_ci")),
   596  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(10) unique) collate utf8mb4_0900_bin")),
   597  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(10) unique) collate utf8mb4_0900_ai_ci")),
   598  		dataTests: []dataTest{
   599  			{
   600  				name:     "no data change",
   601  				ancestor: singleRow(1, "hello"),
   602  				left:     singleRow(1, "hello"),
   603  				right:    singleRow(1, "hello"),
   604  				merged:   singleRow(1, "hello"),
   605  			},
   606  			{
   607  				name:     "right side insert",
   608  				ancestor: []sql.Row{{1, "hello"}},
   609  				left:     []sql.Row{{1, "hello"}},
   610  				right:    []sql.Row{{1, "hello"}, {2, "world"}},
   611  				merged:   []sql.Row{{1, "hello"}, {2, "world"}},
   612  			},
   613  			{
   614  				name:     "right side delete",
   615  				ancestor: []sql.Row{{1, "hello"}, {2, "world"}},
   616  				left:     []sql.Row{{1, "hello"}, {2, "world"}},
   617  				right:    []sql.Row{{1, "hello"}},
   618  				merged:   []sql.Row{{1, "hello"}},
   619  			},
   620  			{
   621  				name:     "right side insert causes unique violation",
   622  				ancestor: []sql.Row{{1, "hello"}},
   623  				left:     []sql.Row{{1, "hello"}},
   624  				right:    []sql.Row{{1, "hello"}, {2, "HELLO"}},
   625  				constraintViolations: []constraintViolation{
   626  					{merge.CvType_UniqueIndex, sql.Row{int32(1)}, sql.Row{"hello"}},
   627  					{merge.CvType_UniqueIndex, sql.Row{int32(2)}, sql.Row{"HELLO"}},
   628  				},
   629  			},
   630  		},
   631  	},
   632  	{
   633  		name:     "no collation changes",
   634  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int, c varchar(10) collate utf8mb4_0900_ai_ci unique, d decimal(5,3) unique)")),
   635  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int, c varchar(10) collate utf8mb4_0900_ai_ci unique, d decimal(5,3) unique)")),
   636  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int, c varchar(10) collate utf8mb4_0900_ai_ci unique, d decimal(5,3) unique)")),
   637  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int, c varchar(10) collate utf8mb4_0900_ai_ci unique, d decimal(5,3) unique)")),
   638  		dataTests: []dataTest{
   639  			{
   640  				name:     "no data change",
   641  				ancestor: singleRow(1, 1, 1, "foo", decimal.New(8, 0)),
   642  				left:     singleRow(1, 1, 2, "foo", decimal.New(8, 0)),
   643  				right:    singleRow(1, 2, 1, "foo", decimal.New(8, 0)),
   644  				merged:   singleRow(1, 2, 2, "foo", decimal.New(8, 0)),
   645  			},
   646  			{
   647  				name:     "replace varchar with equal replacement",
   648  				ancestor: singleRow(1, 1, 1, "foo", decimal.New(100, 0)),
   649  				left:     singleRow(1, 1, 2, "FOO", decimal.New(100, 0)),
   650  				right:    singleRow(1, 2, 1, "foo", decimal.New(100, 0)),
   651  				merged:   singleRow(1, 2, 2, "FOO", decimal.New(100, 0)),
   652  			},
   653  			{
   654  				name:         "conflict removal and replace varchar with equal replacement",
   655  				ancestor:     singleRow(1, 1, 1, "foo", decimal.New(100, 0)),
   656  				left:         singleRow(1, 1, 2, "FOO", decimal.New(100, 0)),
   657  				right:        nil,
   658  				dataConflict: true,
   659  			},
   660  			{
   661  				name:     "replace decimal with equal replacement",
   662  				ancestor: singleRow(1, 1, 1, "foo", decimal.New(100, 0)),
   663  				left:     singleRow(1, 1, 2, "foo", decimal.New(1, 2)),
   664  				right:    singleRow(1, 2, 1, "foo", decimal.New(100, 0)),
   665  				merged:   singleRow(1, 2, 2, "foo", decimal.New(1, 2)),
   666  			},
   667  			{
   668  				name:     "conflict removal and replace decimal with equal replacement",
   669  				ancestor: singleRow(1, 1, 1, "foo", decimal.New(100, 0)),
   670  				left:     singleRow(1, 1, 1, "foo", decimal.New(1, 2)),
   671  				right:    nil,
   672  				merged:   nil,
   673  			},
   674  		},
   675  	},
   676  }
   677  
   678  var columnDefaultTests = []schemaMergeTest{
   679  	{
   680  		name:     "left side add default",
   681  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)           ")),
   682  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)")),
   683  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)           ")),
   684  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)")),
   685  	},
   686  	{
   687  		name:     "left side drop default",
   688  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)")),
   689  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)           ")),
   690  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)")),
   691  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)           ")),
   692  	},
   693  	{
   694  		name:     "convergent add",
   695  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)           ")),
   696  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)")),
   697  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)")),
   698  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)")),
   699  	},
   700  	{
   701  		name:     "convergent drop",
   702  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)")),
   703  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)           ")),
   704  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)           ")),
   705  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)           ")),
   706  	},
   707  	// one side changes columns, the other inserts rows
   708  	{
   709  		name:     "left side column add, right side insert row",
   710  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)                  "), row(1)),
   711  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)"), row(1, 42)),
   712  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY)                  "), row(1), row(12)),
   713  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)"), row(1, 42), row(12, 42)),
   714  	},
   715  	// both sides change columns and insert rows
   716  	{
   717  		name:       "independent column adds, both sides insert independent rows",
   718  		ancestor:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)                                    "), row(1)),
   719  		left:       tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 19)                  "), row(1, 2), row(12, 19)),
   720  		right:      tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int DEFAULT 17)                  "), row(1, 3), row(13, 17)),
   721  		merged:     *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 19, b int DEFAULT 17)"), row(1, 2, 3), row(12, 22, 17), row(13, 19, 33)),
   722  		skipNewFmt: true,
   723  		skipOldFmt: true,
   724  	},
   725  	{
   726  		name:     "convergent column adds, both sides insert independent rows",
   727  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)                  "), row(1)),
   728  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 19)"), row(1, 19)),
   729  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 19)"), row(1, 19)),
   730  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 19)"), row(1, 19)),
   731  	},
   732  }
   733  
   734  var nullabilityTests = []schemaMergeTest{
   735  	{
   736  		name:                "add not null column to empty table",
   737  		ancestor:            *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)                ")),
   738  		left:                tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL)")),
   739  		right:               tbl(sch("CREATE TABLE t (id int PRIMARY KEY)                ")),
   740  		merged:              *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL)")),
   741  		skipOldFmt:          true,
   742  		skipFlipOnOldFormat: true,
   743  	},
   744  	{
   745  		name:                "add not null constraint to existing column",
   746  		ancestor:            *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)         "), row(1, 1)),
   747  		left:                tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL)"), row(1, 1)),
   748  		right:               tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)         "), row(1, 1), row(2, 2)),
   749  		merged:              *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL)"), row(1, 1), row(2, 2)),
   750  		skipOldFmt:          true,
   751  		skipFlipOnOldFormat: true,
   752  	},
   753  	{
   754  		name:                "add not null column to non-empty table",
   755  		ancestor:            *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)                              "), row(1)),
   756  		left:                tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL DEFAULT  '19')"), row(1, 19)),
   757  		right:               tbl(sch("CREATE TABLE t (id int PRIMARY KEY)                              "), row(1), row(2)),
   758  		merged:              *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL DEFAULT  '19')"), row(1, 19), row(2, 19)),
   759  		skipOldFmt:          true,
   760  		skipFlipOnOldFormat: true,
   761  	},
   762  	{
   763  		name:                "table delete plus add not null column to empty table",
   764  		ancestor:            *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)                              ")),
   765  		left:                tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL DEFAULT  '19')")),
   766  		right:               nil,
   767  		conflict:            true,
   768  		skipOldFmt:          true,
   769  		skipFlipOnOldFormat: true,
   770  	},
   771  	{
   772  		name:                "table delete plus add not null column to non-empty table",
   773  		ancestor:            *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)                              "), row(1)),
   774  		left:                tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL DEFAULT  '19')"), row(1, 19)),
   775  		right:               nil,
   776  		conflict:            true,
   777  		skipOldFmt:          true,
   778  		skipFlipOnOldFormat: true,
   779  	},
   780  }
   781  
   782  var columnReorderingTests = []schemaMergeTest{}
   783  
   784  var typeChangeTests = []schemaMergeTest{
   785  	{
   786  		name:     "modify column type on the left side",
   787  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)     "), row(1, 2, 3)),
   788  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b int)"), row(1, "2", 3)),
   789  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)     "), row(1, 2, 3)),
   790  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b int)"), row(1, "2", 3)),
   791  	},
   792  	{
   793  		name:       "independently modify column type on the both sides",
   794  		ancestor:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)          "), row(1, 2, 3)),
   795  		left:       tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b int)     "), row(1, "2", 3)),
   796  		right:      tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b char(20))     "), row(1, 2, "3")),
   797  		merged:     *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b char(20))"), row(1, "2", "3")),
   798  		skipNewFmt: true,
   799  		skipOldFmt: true,
   800  	},
   801  	{
   802  		name:     "convergently modify column type on the both sides",
   803  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)     "), row(1, 2, 3)),
   804  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b int)"), row(1, "2", 3)),
   805  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b int)"), row(1, "2", 3)),
   806  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b int)"), row(1, "2", 3)),
   807  	},
   808  	// column changes one side, data changes other side
   809  	{
   810  		name:     "modify column type on the left side between compatible string types",
   811  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(20), b int, c varchar(20))")),
   812  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a text, b int, c text)")),
   813  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(20), b int, c varchar(20))")),
   814  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a text, b int, c text)")),
   815  		dataTests: []dataTest{
   816  			{
   817  				name:     "schema change, no data change",
   818  				ancestor: singleRow(1, "test", 1, "test"),
   819  				left:     singleRow(1, "test", 1, "test"),
   820  				right:    singleRow(1, "test", 1, "test"),
   821  				merged:   singleRow(1, "test", 1, "test"),
   822  			},
   823  			{
   824  				name:     "insert and schema change on left, no change on right",
   825  				ancestor: nil,
   826  				left:     singleRow(1, "test", 1, "test"),
   827  				right:    nil,
   828  				merged:   singleRow(1, "test", 1, "test"),
   829  			},
   830  			{
   831  				name:     "insert on right, schema change on left",
   832  				ancestor: nil,
   833  				left:     nil,
   834  				right:    singleRow(1, "test", 1, "test"),
   835  				merged:   singleRow(1, "test", 1, "test"),
   836  			},
   837  			{
   838  				name:     "data and schema change on left, no change on right",
   839  				ancestor: singleRow(1, "test", 1, "test"),
   840  				left:     singleRow(1, "hello world", 1, "hello world"),
   841  				right:    singleRow(1, "test", 1, "test"),
   842  				merged:   singleRow(1, "hello world", 1, "hello world"),
   843  			},
   844  			{
   845  				name:     "data change on right, schema change on left",
   846  				ancestor: singleRow(1, "test", 1, "test"),
   847  				left:     singleRow(1, "test", 1, "test"),
   848  				right:    singleRow(1, "hello world", 1, "hello world"),
   849  				merged:   singleRow(1, "hello world", 1, "hello world"),
   850  			},
   851  			{
   852  				name:     "data set and schema change on left, no change on right",
   853  				ancestor: singleRow(1, nil, 1, nil),
   854  				left:     singleRow(1, "hello world", 1, "hello world"),
   855  				right:    singleRow(1, nil, 1, nil),
   856  				merged:   singleRow(1, "hello world", 1, "hello world"),
   857  			},
   858  			{
   859  				name:     "data set on right, schema change on left",
   860  				ancestor: singleRow(1, nil, 1, nil),
   861  				left:     singleRow(1, nil, 1, nil),
   862  				right:    singleRow(1, "hello world", 1, "hello world"),
   863  				merged:   singleRow(1, "hello world", 1, "hello world"),
   864  			},
   865  			{
   866  				name:     "convergent inserts",
   867  				ancestor: nil,
   868  				left:     singleRow(1, "test", 1, "test"),
   869  				right:    singleRow(1, "test", 1, "test"),
   870  				merged:   singleRow(1, "test", 1, "test"),
   871  			},
   872  			{
   873  				name:         "conflicting inserts",
   874  				ancestor:     nil,
   875  				left:         singleRow(1, "test", 1, "test"),
   876  				right:        singleRow(1, "hello world", 1, "hello world"),
   877  				dataConflict: true,
   878  			},
   879  			{
   880  				name:     "delete and schema change on left",
   881  				ancestor: singleRow(1, "test", 1, "test"),
   882  				left:     nil,
   883  				right:    singleRow(1, "test", 1, "test"),
   884  				merged:   nil,
   885  			},
   886  			{
   887  				name:     "schema change on left, delete on right",
   888  				ancestor: singleRow(1, "test", 1, "test"),
   889  				left:     singleRow(1, "test", 1, "test"),
   890  				right:    nil,
   891  				merged:   nil,
   892  			},
   893  			{
   894  				name:         "schema and value change on left, delete on right",
   895  				ancestor:     singleRow(1, "test", 1, "test"),
   896  				left:         singleRow(1, "hello", 1, "hello"),
   897  				right:        nil,
   898  				dataConflict: true,
   899  			},
   900  		},
   901  	},
   902  	{
   903  		name:     "modify column type on the left side between compatible string types with unique secondary index",
   904  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(20) unique, b int, c varchar(20) unique)")),
   905  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a text, b int, c text)")),
   906  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(20) unique, b int, c varchar(20) unique)")),
   907  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a text, b int, c text)")),
   908  		dataTests: []dataTest{
   909  			{
   910  				name:     "schema change, no data change",
   911  				ancestor: singleRow(1, "test", 1, "test"),
   912  				left:     singleRow(1, "test", 1, "test"),
   913  				right:    singleRow(1, "test", 1, "test"),
   914  				merged:   singleRow(1, "test", 1, "test"),
   915  			},
   916  			{
   917  				name:     "insert and schema change on left, no change on right",
   918  				ancestor: nil,
   919  				left:     singleRow(1, "test", 1, "test"),
   920  				right:    nil,
   921  				merged:   singleRow(1, "test", 1, "test"),
   922  			},
   923  			{
   924  				name:     "insert on right, schema change on left",
   925  				ancestor: nil,
   926  				left:     nil,
   927  				right:    singleRow(1, "test", 1, "test"),
   928  				merged:   singleRow(1, "test", 1, "test"),
   929  			},
   930  			{
   931  				name:     "data and schema change on left, no change on right",
   932  				ancestor: singleRow(1, "test", 1, "test"),
   933  				left:     singleRow(1, "hello world", 1, "hello world"),
   934  				right:    singleRow(1, "test", 1, "test"),
   935  				merged:   singleRow(1, "hello world", 1, "hello world"),
   936  			},
   937  			{
   938  				name:     "data change on right, schema change on left",
   939  				ancestor: singleRow(1, "test", 1, "test"),
   940  				left:     singleRow(1, "test", 1, "test"),
   941  				right:    singleRow(1, "hello world", 1, "hello world"),
   942  				merged:   singleRow(1, "hello world", 1, "hello world"),
   943  			},
   944  			{
   945  				name:     "data set and schema change on left, no change on right",
   946  				ancestor: singleRow(1, nil, 1, nil),
   947  				left:     singleRow(1, "hello world", 1, "hello world"),
   948  				right:    singleRow(1, nil, 1, nil),
   949  				merged:   singleRow(1, "hello world", 1, "hello world"),
   950  			},
   951  			{
   952  				name:     "data set on right, schema change on left",
   953  				ancestor: singleRow(1, nil, 1, nil),
   954  				left:     singleRow(1, nil, 1, nil),
   955  				right:    singleRow(1, "hello world", 1, "hello world"),
   956  				merged:   singleRow(1, "hello world", 1, "hello world"),
   957  			},
   958  			{
   959  				name:     "convergent inserts",
   960  				ancestor: nil,
   961  				left:     singleRow(1, "test", 1, "test"),
   962  				right:    singleRow(1, "test", 1, "test"),
   963  				merged:   singleRow(1, "test", 1, "test"),
   964  			},
   965  			{
   966  				name:         "conflicting inserts",
   967  				ancestor:     nil,
   968  				left:         singleRow(1, "test", 1, "test"),
   969  				right:        singleRow(1, "hello world", 1, "hello world"),
   970  				dataConflict: true,
   971  			},
   972  			{
   973  				name:     "delete and schema change on left",
   974  				ancestor: singleRow(1, "test", 1, "test"),
   975  				left:     nil,
   976  				right:    singleRow(1, "test", 1, "test"),
   977  				merged:   nil,
   978  			},
   979  			{
   980  				name:     "schema change on left, delete on right",
   981  				ancestor: singleRow(1, "test", 1, "test"),
   982  				left:     singleRow(1, "test", 1, "test"),
   983  				right:    nil,
   984  				merged:   nil,
   985  			},
   986  			{
   987  				name:         "schema and value change on left, delete on right",
   988  				ancestor:     singleRow(1, "test", 1, "test"),
   989  				left:         singleRow(1, "hello", 1, "hello"),
   990  				right:        nil,
   991  				dataConflict: true,
   992  			},
   993  		},
   994  	},
   995  }
   996  
   997  var keyChangeTests = []schemaMergeTest{
   998  	{
   999  		name:     "add a trailing primary key column on left side",
  1000  		ancestor: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))   "), row(1, "2", float32(3.0))),
  1001  		left:     tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a, b))"), row(1, "2", float32(3.0))),
  1002  		right:    tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))   "), row(1, "2", float32(3.0))),
  1003  		merged:   *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a, b))"), row(1, "2", float32(3.0))),
  1004  	},
  1005  	{
  1006  		name:     "add a leading primary key column on left side",
  1007  		ancestor: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))   "), row(1, "2", float32(3.0))),
  1008  		left:     tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (b, a))"), row(1, "2", float32(3.0))),
  1009  		right:    tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))   "), row(1, "2", float32(3.0))),
  1010  		merged:   *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (b, a))"), row(1, "2", float32(3.0))),
  1011  	},
  1012  	{
  1013  		name:                "remove a trailing primary key column on left side",
  1014  		ancestor:            *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a, b))"), row(1, "2", float32(3.0))),
  1015  		left:                tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))   "), row(1, "2", float32(3.0))),
  1016  		right:               tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a, b))"), row(1, "2", float32(3.0))),
  1017  		merged:              *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))   "), row(1, "2", float32(3.0))),
  1018  		skipFlipOnNewFormat: true,
  1019  	},
  1020  	{
  1021  		name:     "remove a trailing primary key column on both sides",
  1022  		ancestor: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a, b))"), row(1, "2", float32(3.0))),
  1023  		left:     tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))   "), row(1, "2", float32(3.0))),
  1024  		right:    tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))   "), row(1, "2", float32(3.0))),
  1025  		merged:   *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))   "), row(1, "2", float32(3.0))),
  1026  	},
  1027  	{
  1028  		name:                "remove a leading primary key column on left side",
  1029  		ancestor:            *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (b, a))"), row(1, "2", float32(3.0))),
  1030  		left:                tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))   "), row(1, "2", float32(3.0))),
  1031  		right:               tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (b, a))"), row(1, "2", float32(3.0))),
  1032  		merged:              *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))   "), row(1, "2", float32(3.0))),
  1033  		skipFlipOnNewFormat: true,
  1034  	},
  1035  	{
  1036  		name:     "remove a leading primary key column on both sides",
  1037  		ancestor: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (b, a))"), row(1, "2", float32(3.0))),
  1038  		left:     tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))   "), row(1, "2", float32(3.0))),
  1039  		right:    tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))   "), row(1, "2", float32(3.0))),
  1040  		merged:   *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))   "), row(1, "2", float32(3.0))),
  1041  	},
  1042  	{
  1043  		skipFlipOnNewFormat: true,
  1044  		skipFlipOnOldFormat: true,
  1045  		name:                "convert left side to a keyless table",
  1046  		ancestor:            *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))"), row(1, "2", float32(3.0))),
  1047  		left:                tbl(sch("CREATE TABLE t (a int, b char(20), c float)                 "), row(1, "2", float32(3.0))),
  1048  		right:               tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))"), row(1, "2", float32(3.0))),
  1049  		merged:              *tbl(sch("CREATE TABLE t (a int, b char(20), c float)                 "), row(1, "2", float32(3.0))),
  1050  	},
  1051  	{
  1052  		name:       "convert both sides to keyless tables",
  1053  		ancestor:   *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))"), row(1, "2", float32(3.0))),
  1054  		left:       tbl(sch("CREATE TABLE t (a int, b char(20), c float)                 "), row(1, "2", float32(3.0))),
  1055  		right:      tbl(sch("CREATE TABLE t (a int, b char(20), c float)                 "), row(1, "2", float32(3.0))),
  1056  		merged:     *tbl(sch("CREATE TABLE t (a int, b char(20), c float)                 "), row(1, "2", float32(3.0))),
  1057  		skipNewFmt: true,
  1058  		skipOldFmt: true,
  1059  	},
  1060  }
  1061  
  1062  var secondaryIndexTests = []schemaMergeTest{
  1063  	{
  1064  		name:     "independent index adds",
  1065  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float)                     "), row(1, "2", float32(3.0))),
  1066  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (a))          "), row(1, "2", float32(3.0))),
  1067  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (b))          "), row(1, "2", float32(3.0))),
  1068  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (a), INDEX(b))"), row(1, "2", float32(3.0))),
  1069  	},
  1070  	{
  1071  		name:     "independent composite index adds",
  1072  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float)                            "), row(1, "2", float32(3.0))),
  1073  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (a, b))              "), row(1, "2", float32(3.0))),
  1074  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (b, a))              "), row(1, "2", float32(3.0))),
  1075  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (a, b), INDEX (b, a))"), row(1, "2", float32(3.0))),
  1076  	},
  1077  	{
  1078  		name:                "independent index drops",
  1079  		ancestor:            *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (a), INDEX (b))"), row(1, "2", float32(3.0))),
  1080  		left:                tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (a))           "), row(1, "2", float32(3.0))),
  1081  		right:               tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (b))           "), row(1, "2", float32(3.0))),
  1082  		merged:              *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float)                      "), row(1, "2", float32(3.0))),
  1083  		skipOldFmt:          true,
  1084  		skipFlipOnOldFormat: true,
  1085  	},
  1086  }
  1087  
  1088  var simpleConflictTests = []schemaMergeTest{
  1089  	{
  1090  		name:     "conflicting column adds",
  1091  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)                "), row(1)),
  1092  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NULL)    "), row(1, 2)),
  1093  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL)"), row(1, 2)),
  1094  		conflict: true,
  1095  	},
  1096  	{
  1097  		name:     "column add and table drop",
  1098  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)                "), row(1)),
  1099  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NULL)    "), row(1, 2)),
  1100  		right:    nil,
  1101  		conflict: true,
  1102  	},
  1103  	{
  1104  		// TODO: This test case does NOT generate a conflict; the merge gets short circuited, because the table's
  1105  		//       right/left/anc hashes are all the same. This is an issue with the test framework, not with Dolt.
  1106  		//       The code we use in these tests to create a schema (sqlutil.ParseCreateTableStatement) silently
  1107  		//       drops index and check constraint definitions.
  1108  		skipNewFmt: true,
  1109  		skipOldFmt: true,
  1110  		name:       "conflicting index adds: same name and columns, different constraints",
  1111  		ancestor:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float)                      ")),
  1112  		left:       tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX idx (a))       ")),
  1113  		right:      tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, UNIQUE INDEX idx (a))")),
  1114  		conflict:   true,
  1115  	},
  1116  	{
  1117  		// TODO: This test case does NOT generate a conflict; the merge gets short circuited, because the table's
  1118  		//       right/left/anc hashes are all the same. This is an issue with the test framework, not with Dolt.
  1119  		//       The code we use in these tests to create a schema (sqlutil.ParseCreateTableStatement) silently
  1120  		//       drops index and check constraint definitions.
  1121  		skipNewFmt: true,
  1122  		skipOldFmt: true,
  1123  		// TODO: multiple indexes can exist for the same column set, so this shouldn't actually be a conflict;
  1124  		//       Dolt does report this as a schema conflict today, but we could merge the two indexes together.
  1125  		name:     "conflicting index adds: same column different names",
  1126  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float)                 ")),
  1127  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX a_idx (a))")),
  1128  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX key_a (a))")),
  1129  		conflict: true,
  1130  	},
  1131  	{
  1132  		// TODO: This test case does NOT generate a conflict; the merge gets short circuited, because the table's
  1133  		//       right/left/anc hashes are all the same. This is an issue with the test framework, not with Dolt.
  1134  		//       The code we use in these tests to create a schema (sqlutil.ParseCreateTableStatement) silently
  1135  		//       drops index and check constraint definitions.
  1136  		skipNewFmt: true,
  1137  		skipOldFmt: true,
  1138  		name:       "conflicting index adds: same name different definitions",
  1139  		ancestor:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float)               ")),
  1140  		left:       tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX idx (a))")),
  1141  		right:      tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX idx (b))")),
  1142  		conflict:   true,
  1143  	},
  1144  	{
  1145  		name:     "add primary key columns at different key positions on left and right sides",
  1146  		ancestor: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))   "), row(1, "2", float32(3.0))),
  1147  		left:     tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a, b))"), row(1, "2", float32(3.0))),
  1148  		right:    tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (b, a))"), row(1, "2", float32(3.0))),
  1149  		conflict: true,
  1150  	},
  1151  	{
  1152  		name:     "remove different primary key columns on left and right sides",
  1153  		ancestor: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a, b))"), row(1, "2", float32(3.0))),
  1154  		left:     tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))   "), row(1, "2", float32(3.0))),
  1155  		right:    tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (b))   "), row(1, "2", float32(3.0))),
  1156  		conflict: true,
  1157  	},
  1158  }
  1159  
  1160  var jsonMergeTests = []schemaMergeTest{
  1161  	{
  1162  		name:     "json merge",
  1163  		ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int, j json)")),
  1164  		left:     tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int, j json)")),
  1165  		right:    tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int, j json)")),
  1166  		merged:   *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int, j json)")),
  1167  		dataTests: []dataTest{
  1168  			{
  1169  				name:     "convergent insertion",
  1170  				ancestor: singleRow(1, 1, 1, `{}`),
  1171  				left:     singleRow(1, 2, 1, `{ "key1": "value1" }`),
  1172  				right:    singleRow(1, 1, 2, `{ "key1": "value1" }`),
  1173  				merged:   singleRow(1, 2, 2, `{ "key1": "value1" }`),
  1174  			},
  1175  			{
  1176  				name:     "convergent insertion with escaped quotes in keys",
  1177  				ancestor: singleRow(1, 1, 1, `{}`),
  1178  				left:     singleRow(1, 2, 1, `{ "\"key1\"": "\"value1\"" }`),
  1179  				right:    singleRow(1, 1, 2, `{ "\"key1\"": "\"value1\"" }`),
  1180  				merged:   singleRow(1, 2, 2, `{ "\"key1\"": "\"value1\"" }`),
  1181  			},
  1182  			{
  1183  				name:     `parallel insertion`,
  1184  				ancestor: singleRow(1, 1, 1, `{}`),
  1185  				left:     singleRow(1, 2, 1, `{ "key1": "value1" }`),
  1186  				right:    singleRow(1, 1, 2, `{ "key2": "value2" }`),
  1187  				merged:   singleRow(1, 2, 2, `{ "key1": "value1", "key2": "value2" }`),
  1188  			},
  1189  			{
  1190  				name:     `convergent modification`,
  1191  				ancestor: singleRow(1, 1, 1, `{ "key1": "value1" }`),
  1192  				left:     singleRow(1, 2, 1, `{ "key1": "value2" }`),
  1193  				right:    singleRow(1, 1, 2, `{ "key1": "value2" }`),
  1194  				merged:   singleRow(1, 2, 2, `{ "key1": "value2" }`),
  1195  			},
  1196  			{
  1197  				name:     `parallel modification`,
  1198  				ancestor: singleRow(1, 1, 1, `{ "key1": "value1", "key2": "value2" }`),
  1199  				left:     singleRow(1, 2, 1, `{ "key1": "value3", "key2": "value2" }`),
  1200  				right:    singleRow(1, 1, 2, `{ "key1": "value1", "key2": "value4" }`),
  1201  				merged:   singleRow(1, 2, 2, `{ "key1": "value3", "key2": "value4" }`),
  1202  			},
  1203  			{
  1204  				name:     `parallel deletion`,
  1205  				ancestor: singleRow(1, 1, 1, `{ "key1": "value1" }`),
  1206  				left:     singleRow(1, 2, 1, `{}`),
  1207  				right:    singleRow(1, 1, 2, `{}`),
  1208  				merged:   singleRow(1, 2, 2, `{}`),
  1209  			},
  1210  			{
  1211  				name:     `convergent deletion`,
  1212  				ancestor: singleRow(1, 1, 1, `{ "key1": "value1", "key2": "value2" }`),
  1213  				left:     singleRow(1, 2, 1, `{ "key2": "value2" }`),
  1214  				right:    singleRow(1, 1, 2, `{ "key1": "value1" }`),
  1215  				merged:   singleRow(1, 2, 2, `{}`),
  1216  			},
  1217  			{
  1218  				name:         `divergent insertion`,
  1219  				ancestor:     singleRow(1, 1, 1, `{}`),
  1220  				left:         singleRow(1, 2, 1, `{ "key1": "value1" }`),
  1221  				right:        singleRow(1, 1, 2, `{ "key1": "value2" }`),
  1222  				dataConflict: true,
  1223  			},
  1224  			{
  1225  				name:         `divergent modification`,
  1226  				ancestor:     singleRow(1, 1, 1, `{ "key1": "value1"}`),
  1227  				left:         singleRow(1, 2, 1, `{ "key1": "value2" }`),
  1228  				right:        singleRow(1, 1, 2, `{ "key1": "value3" }`),
  1229  				dataConflict: true,
  1230  			},
  1231  			{
  1232  				name:         `divergent modification and deletion`,
  1233  				ancestor:     singleRow(1, 1, 1, `{ "key1": "value1"}`),
  1234  				left:         singleRow(1, 2, 1, `{ "key1": "value2" }`),
  1235  				right:        singleRow(1, 1, 2, `{}`),
  1236  				dataConflict: true,
  1237  			},
  1238  			{
  1239  				name:     `nested insertion`,
  1240  				ancestor: singleRow(1, 1, 1, `{ "key1": {} }`),
  1241  				left:     singleRow(1, 2, 1, `{ "key1": { "key1a": "value1a" } }`),
  1242  				right:    singleRow(1, 1, 2, `{ "key1": { "key1b": "value1b" } }`),
  1243  				merged:   singleRow(1, 2, 2, `{ "key1": { "key1a": "value1a", "key1b": "value1b" } }`),
  1244  			},
  1245  			{
  1246  				name:     `nested insertion with escaped quotes in keys`,
  1247  				ancestor: singleRow(1, 1, 1, `{ "\"key1\"": {} }`),
  1248  				left:     singleRow(1, 2, 1, `{ "\"key1\"": { "\"key1a\"": "value1a" } }`),
  1249  				right:    singleRow(1, 1, 2, `{ "\"key1\"": { "\"key1b\"": "value1b" } }`),
  1250  				merged:   singleRow(1, 2, 2, `{ "\"key1\"": { "\"key1a\"": "value1a", "\"key1b\"": "value1b" } }`),
  1251  			},
  1252  			{
  1253  				name:     `nested modification`,
  1254  				ancestor: singleRow(1, 1, 1, `{ "key1": { "key1a": "value1a", "key1b": "value1b" } }`),
  1255  				left:     singleRow(1, 2, 1, `{ "key1": { "key1a": "value2a", "key1b": "value1b" } }`),
  1256  				right:    singleRow(1, 1, 2, `{ "key1": { "key1a": "value1a", "key1b": "value2b" } }`),
  1257  				merged:   singleRow(1, 2, 2, `{ "key1": { "key1a": "value2a", "key1b": "value2b" } }`),
  1258  			},
  1259  			{
  1260  				name:     `nested modification with escaped quotes in keys`,
  1261  				ancestor: singleRow(1, 1, 1, `{ "\"key1\"": { "\"key1a\"": "value1a", "\"key1b\"": "value1b" } }`),
  1262  				left:     singleRow(1, 2, 1, `{ "\"key1\"": { "\"key1a\"": "value2a", "\"key1b\"": "value1b" } }`),
  1263  				right:    singleRow(1, 1, 2, `{ "\"key1\"": { "\"key1a\"": "value1a", "\"key1b\"": "value2b" } }`),
  1264  				merged:   singleRow(1, 2, 2, `{ "\"key1\"": { "\"key1a\"": "value2a", "\"key1b\"": "value2b" } }`),
  1265  			},
  1266  			{
  1267  				name:     `nested deletion`,
  1268  				ancestor: singleRow(1, 1, 1, `{ "key1": { "key1a": "value1a", "key1b": "value1b" } }`),
  1269  				left:     singleRow(1, 2, 1, `{ "key1": { "key1a": "value1a" } }`),
  1270  				right:    singleRow(1, 1, 2, `{ "key1": { "key1b": "value1b" } }`),
  1271  				merged:   singleRow(1, 2, 2, `{ "key1": { } }`),
  1272  			},
  1273  			{
  1274  				name:     `nested deletion with escaped quotes in keys`,
  1275  				ancestor: singleRow(1, 1, 1, `{ "\"key1\"": { "\"key1a\"": "value1a", "\"key1b\"": "value1b" } }`),
  1276  				left:     singleRow(1, 2, 1, `{ "\"key1\"": { "\"key1a\"": "value1a" } }`),
  1277  				right:    singleRow(1, 1, 2, `{ "\"key1\"": { "\"key1b\"": "value1b" } }`),
  1278  				merged:   singleRow(1, 2, 2, `{ "\"key1\"": { } }`),
  1279  			},
  1280  			{
  1281  				name:     "complicated nested merge",
  1282  				ancestor: singleRow(1, 1, 1, `{ "removed": 1, "modified": 2, "nested": { "removed": 3, "modified": 4 } }`),
  1283  				left:     singleRow(1, 2, 1, `{ "added": 7, "modified": 2, "nested": { "removed": 3, "modified": 5 } }`),
  1284  				right:    singleRow(1, 1, 2, `{ "removed": 1, "modified": 6, "nested": { "added": 8, "modified": 4 } }`),
  1285  				merged:   singleRow(1, 2, 2, `{ "added": 7, "modified": 6, "nested": { "added": 8, "modified": 5 } }`),
  1286  			},
  1287  			{
  1288  				name:     "object with double quotes in keys",
  1289  				ancestor: singleRow(1, 1, 1, `{ "\"removed\"": 1, "\"modified\"": 2, "\"nested\"": { "\"removed\"": 3, "\"modified\"": 4 } }`),
  1290  				left:     singleRow(1, 2, 1, `{ "\"added\"": 7, "\"modified\"": 2, "\"nested\"": { "\"removed\"": 3, "\"modified\"": 5 } }`),
  1291  				right:    singleRow(1, 1, 2, `{ "\"removed\"": 1, "\"modified\"": 6, "\"nested\"": { "\"added\"": 8, "\"modified\"": 4 } }`),
  1292  				merged:   singleRow(1, 2, 2, `{ "\"added\"": 7, "\"modified\"": 6, "\"nested\"": { "\"added\"": 8, "\"modified\"": 5 } }`),
  1293  			},
  1294  			{
  1295  				name:     "changing types",
  1296  				ancestor: singleRow(1, 1, 1, `{ "key1": {}, "key2": 2 }`),
  1297  				left:     singleRow(1, 2, 1, `{ "key1": [], "key2": 2 }`),
  1298  				right:    singleRow(1, 1, 2, `{ "key1": {}, "key2": true }`),
  1299  				merged:   singleRow(1, 2, 2, `{ "key1": [], "key2": true }`),
  1300  			},
  1301  			{
  1302  				name:         "changing types conflict",
  1303  				ancestor:     singleRow(1, 1, 1, `{ "key1": {} }`),
  1304  				left:         singleRow(1, 2, 1, `{ "key1": [] }`),
  1305  				right:        singleRow(1, 1, 2, `{ "key1": 2 }`),
  1306  				dataConflict: true,
  1307  			},
  1308  			{
  1309  				name:         "object insert and modify conflict",
  1310  				ancestor:     singleRow(1, 1, 1, `{ "key1": {} }`),
  1311  				left:         singleRow(1, 2, 1, `{ "key1": { "key2": 2 } }`),
  1312  				right:        singleRow(1, 1, 2, `{ "key1": 2 }`),
  1313  				dataConflict: true,
  1314  			},
  1315  			{
  1316  				name:         "object insert and delete conflict",
  1317  				ancestor:     singleRow(1, 1, 1, `{ "key1": {} }`),
  1318  				left:         singleRow(1, 2, 1, `{ "key1": { "key2": 2 } }`),
  1319  				right:        singleRow(1, 1, 2, `{ }`),
  1320  				dataConflict: true,
  1321  			},
  1322  			{
  1323  				name:         "changing arrays conflict",
  1324  				ancestor:     singleRow(1, 1, 1, `{ "key1": [1] }`),
  1325  				left:         singleRow(1, 2, 1, `{ "key1": [1, 1] }`),
  1326  				right:        singleRow(1, 1, 2, `{ "key1": [] }`),
  1327  				dataConflict: true,
  1328  			},
  1329  			{
  1330  				// Which array element should go first?
  1331  				// We avoid making assumptions and flag this as a conflict.
  1332  				name:         "object inside array conflict",
  1333  				ancestor:     singleRow(1, 1, 1, `{ "key1": [ { } ] }`),
  1334  				left:         singleRow(1, 2, 1, `{ "key1": [ { "key2": "value2" } ] }`),
  1335  				right:        singleRow(1, 1, 2, `{ "key1": [ { "key3": "value3" } ] }`),
  1336  				dataConflict: true,
  1337  			},
  1338  			{
  1339  				// Did the left branch overwrite the first value in the array?
  1340  				// Or did it remove the last value and insert at the beginning?
  1341  				// Did the right branch overwrite the second value in the array?
  1342  				// Or did it remove the first value and insert at the end?
  1343  				// Diffs on arrays are ambiguous. We avoid making assumptions and flag this as a conflict.
  1344  				name:         "parallel array modification",
  1345  				ancestor:     singleRow(1, 1, 1, `{ "key1": [ 1, 1 ] }`),
  1346  				left:         singleRow(1, 2, 1, `{ "key1": [ 2, 1 ] }`),
  1347  				right:        singleRow(1, 1, 2, `{ "key1": [ 1, 2 ] }`),
  1348  				dataConflict: true,
  1349  			},
  1350  		},
  1351  	},
  1352  }
  1353  
  1354  func testSchemaMerge(t *testing.T, tests []schemaMergeTest) {
  1355  	t.Run("merge left to right", func(t *testing.T) {
  1356  		testSchemaMergeHelper(t, tests, false)
  1357  	})
  1358  	t.Run("merge right to left", func(t *testing.T) {
  1359  		testSchemaMergeHelper(t, tests, true)
  1360  	})
  1361  }
  1362  
  1363  func testSchemaMergeHelper(t *testing.T, tests []schemaMergeTest, flipSides bool) {
  1364  	for _, test := range tests {
  1365  		if flipSides {
  1366  			tmp := test.left
  1367  			test.left = test.right
  1368  			test.right = tmp
  1369  			for i, _ := range test.dataTests {
  1370  				tmp := test.dataTests[i].left
  1371  				test.dataTests[i].left = test.dataTests[i].right
  1372  				test.dataTests[i].right = tmp
  1373  			}
  1374  		}
  1375  
  1376  		t.Run(test.name, func(t *testing.T) {
  1377  			runTest := func(t *testing.T, test schemaMergeTest, expectDataConflict bool, expConstraintViolations []constraintViolation) {
  1378  				a, l, r, m := setupSchemaMergeTest(t, test)
  1379  
  1380  				ctx := context.Background()
  1381  				var mo merge.MergeOpts
  1382  				var eo editor.Options
  1383  				eo = eo.WithDeaf(editor.NewInMemDeaf(a.VRW()))
  1384  				// attempt merge before skipping to assert no panics
  1385  				result, err := merge.MergeRoots(sql.NewContext(ctx), l, r, a, rootish{r}, rootish{a}, eo, mo)
  1386  				maybeSkip(t, a.VRW().Format(), test, flipSides)
  1387  
  1388  				if test.conflict {
  1389  					// TODO: Test the conflict error message more deeply
  1390  					require.Error(t, err)
  1391  				} else {
  1392  					require.NoError(t, err)
  1393  					exp, err := doltdb.MapTableHashes(ctx, m)
  1394  					assert.NoError(t, err)
  1395  					act, err := doltdb.MapTableHashes(ctx, result.Root)
  1396  					assert.NoError(t, err)
  1397  
  1398  					assert.Equal(t, len(exp), len(act))
  1399  
  1400  					if expectDataConflict {
  1401  						foundDataConflict := false
  1402  						for name, _ := range exp {
  1403  							_, ok := act[name]
  1404  							assert.True(t, ok)
  1405  							actTbl, _, err := result.Root.GetTable(ctx, doltdb.TableName{Name: name})
  1406  							require.NoError(t, err)
  1407  							hasConflict, err := actTbl.HasConflicts(ctx)
  1408  							require.NoError(t, err)
  1409  							foundDataConflict = foundDataConflict || hasConflict
  1410  						}
  1411  						if !assert.True(t, foundDataConflict, "Expected data conflict, but didn't find one.") {
  1412  							for name, _ := range exp {
  1413  								table, _, err := result.Root.GetTable(ctx, doltdb.TableName{Name: name})
  1414  								require.NoError(t, err)
  1415  								t.Logf("table %s:", name)
  1416  								t.Log(table.DebugString(ctx, m.NodeStore()))
  1417  							}
  1418  
  1419  						}
  1420  					} else {
  1421  						for name, addr := range exp {
  1422  							a, ok := act[name]
  1423  							assert.True(t, ok)
  1424  
  1425  							actTbl, _, err := result.Root.GetTable(ctx, doltdb.TableName{Name: name})
  1426  							require.NoError(t, err)
  1427  							hasConflict, err := actTbl.HasConflicts(ctx)
  1428  							require.NoError(t, err)
  1429  							require.False(t, hasConflict, "Unexpected data conflict")
  1430  
  1431  							numConstraintViolations, err := actTbl.NumConstraintViolations(ctx)
  1432  							require.NoError(t, err)
  1433  							require.EqualValues(t, numConstraintViolations, len(expConstraintViolations))
  1434  
  1435  							sch, err := actTbl.GetSchema(ctx)
  1436  							require.NoError(t, err)
  1437  							kd, vd := sch.GetMapDescriptors()
  1438  
  1439  							if len(expConstraintViolations) > 0 {
  1440  								artifacts, err := actTbl.GetArtifacts(ctx)
  1441  								require.NoError(t, err)
  1442  								artifactMap := durable.ProllyMapFromArtifactIndex(artifacts)
  1443  								artifactIter, err := artifactMap.IterAllCVs(ctx)
  1444  								require.NoError(t, err)
  1445  
  1446  								// value tuples encoded in ConstraintViolationMeta may
  1447  								// violate the not null constraints assumed by fixed access
  1448  								kd = kd.WithoutFixedAccess()
  1449  								vd = vd.WithoutFixedAccess()
  1450  								for _, expectedViolation := range expConstraintViolations {
  1451  									violationType, key, value, err := merge.NextConstraintViolation(ctx, artifactIter, kd, vd, artifactMap.NodeStore())
  1452  									require.NoError(t, err)
  1453  									require.EqualValues(t, expectedViolation.violationType, violationType)
  1454  									require.EqualValues(t, expectedViolation.key, key)
  1455  									require.EqualValues(t, expectedViolation.value, value)
  1456  								}
  1457  							} else {
  1458  								if addr != a {
  1459  									expTbl, _, err := m.GetTable(ctx, doltdb.TableName{Name: name})
  1460  									require.NoError(t, err)
  1461  									expSchema, err := expTbl.GetSchema(ctx)
  1462  									require.NoError(t, err)
  1463  									expRowDataHash, err := expTbl.GetRowDataHash(ctx)
  1464  									require.NoError(t, err)
  1465  									actRowDataHash, err := actTbl.GetRowDataHash(ctx)
  1466  									require.NoError(t, err)
  1467  									if !expSchema.GetKeyDescriptor().Equals(kd) {
  1468  										t.Fatal("Primary key descriptors unequal")
  1469  									}
  1470  									if !expSchema.GetValueDescriptor().Equals(vd) {
  1471  										t.Fatal("Value descriptors unequal")
  1472  									}
  1473  									if expRowDataHash != actRowDataHash {
  1474  										t.Error("Rows unequal")
  1475  										t.Logf("expected rows: %s", expTbl.DebugString(ctx, m.NodeStore()))
  1476  										t.Logf("actual rows: %s", actTbl.DebugString(ctx, m.NodeStore()))
  1477  									}
  1478  									expIndexSet, err := expTbl.GetIndexSet(ctx)
  1479  									require.NoError(t, err)
  1480  									actIndexSet, err := actTbl.GetIndexSet(ctx)
  1481  									require.NoError(t, err)
  1482  									expSchema.Indexes().Iter(func(index schema.Index) (stop bool, err error) {
  1483  										expIndex, err := expIndexSet.GetIndex(ctx, expSchema, index.Name())
  1484  										require.NoError(t, err)
  1485  										actIndex, err := actIndexSet.GetIndex(ctx, expSchema, index.Name())
  1486  										require.NoError(t, err)
  1487  										expIndexHash, err := expIndex.HashOf()
  1488  										require.NoError(t, err)
  1489  										actIndexHash, err := actIndex.HashOf()
  1490  										require.NoError(t, err)
  1491  										if expIndexHash != actIndexHash {
  1492  											t.Errorf("Index %s unequal", index.Name())
  1493  											t.Logf("expected rows: %s", expIndex.DebugString(ctx, m.NodeStore(), expSchema))
  1494  											t.Logf("actual rows: %s", actIndex.DebugString(ctx, m.NodeStore(), expSchema))
  1495  										}
  1496  										return false, nil
  1497  									})
  1498  
  1499  								}
  1500  							}
  1501  						}
  1502  					}
  1503  				}
  1504  			}
  1505  			t.Run("test schema merge", func(t *testing.T) {
  1506  				runTest(t, test, false, nil)
  1507  			})
  1508  			for _, data := range test.dataTests {
  1509  				// Copy the test so that the values from one data test don't affect subsequent data tests.
  1510  				dataTest := test
  1511  				newLeft := *test.left
  1512  				newRight := *test.right
  1513  				dataTest.left = &newLeft
  1514  				dataTest.right = &newRight
  1515  				dataTest.ancestor.rows = data.ancestor
  1516  				dataTest.left.rows = data.left
  1517  				dataTest.right.rows = data.right
  1518  				dataTest.merged.rows = data.merged
  1519  				dataTest.skipNewFmt = dataTest.skipNewFmt || data.skip
  1520  				dataTest.skipFlipOnNewFormat = dataTest.skipFlipOnNewFormat || data.skipFlip
  1521  				t.Run(data.name, func(t *testing.T) {
  1522  					if data.skip {
  1523  						t.Skip()
  1524  					}
  1525  					runTest(t, dataTest, data.dataConflict, data.constraintViolations)
  1526  				})
  1527  			}
  1528  		})
  1529  	}
  1530  }
  1531  
  1532  func setupSchemaMergeTest(t *testing.T, test schemaMergeTest) (anc, left, right, merged doltdb.RootValue) {
  1533  	denv := dtestutils.CreateTestEnv()
  1534  	var eo editor.Options
  1535  	eo = eo.WithDeaf(editor.NewInMemDeaf(denv.DoltDB.ValueReadWriter()))
  1536  	anc = makeRootWithTable(t, denv.DoltDB, eo, test.ancestor)
  1537  	assert.NotNil(t, anc)
  1538  	if test.left != nil {
  1539  		left = makeRootWithTable(t, denv.DoltDB, eo, *test.left)
  1540  		assert.NotNil(t, left)
  1541  	} else {
  1542  		left = makeEmptyRoot(t, denv.DoltDB, eo)
  1543  	}
  1544  	if test.right != nil {
  1545  		right = makeRootWithTable(t, denv.DoltDB, eo, *test.right)
  1546  		assert.NotNil(t, right)
  1547  	} else {
  1548  		right = makeEmptyRoot(t, denv.DoltDB, eo)
  1549  	}
  1550  	if !test.conflict {
  1551  		merged = makeRootWithTable(t, denv.DoltDB, eo, test.merged)
  1552  		assert.NotNil(t, merged)
  1553  	}
  1554  	return
  1555  }
  1556  
  1557  func maybeSkip(t *testing.T, nbf *types.NomsBinFormat, test schemaMergeTest, flipSides bool) {
  1558  	if types.IsFormat_DOLT(nbf) {
  1559  		if test.skipNewFmt || flipSides && test.skipFlipOnNewFormat {
  1560  			t.Skip()
  1561  		}
  1562  	} else {
  1563  		if test.skipOldFmt || flipSides && test.skipFlipOnOldFormat {
  1564  			t.Skip()
  1565  		}
  1566  	}
  1567  }
  1568  
  1569  func tbl(ns namedSchema, rows ...sql.Row) *table {
  1570  	return &table{ns: ns, rows: rows}
  1571  }
  1572  
  1573  func sch(definition string) namedSchema {
  1574  	denv := dtestutils.CreateTestEnv()
  1575  	vrw := denv.DoltDB.ValueReadWriter()
  1576  	ns := denv.DoltDB.NodeStore()
  1577  	ctx := context.Background()
  1578  	root, _ := doltdb.EmptyRootValue(ctx, vrw, ns)
  1579  	eng, dbName, _ := engine.NewSqlEngineForEnv(ctx, denv)
  1580  	sqlCtx, _ := eng.NewDefaultContext(ctx)
  1581  	sqlCtx.SetCurrentDatabase(dbName)
  1582  	// TODO: ParseCreateTableStatement silently drops any indexes or check constraints in the definition
  1583  	name, s, err := sqlutil.ParseCreateTableStatement(sqlCtx, root, eng.GetUnderlyingEngine(), definition)
  1584  	if err != nil {
  1585  		panic(err)
  1586  	}
  1587  	return namedSchema{name: name, sch: s, create: definition}
  1588  }
  1589  
  1590  func row(values ...any) sql.Row {
  1591  	return sql.NewRow(values...)
  1592  }
  1593  
  1594  func singleRow(values ...any) []sql.Row {
  1595  	return []sql.Row{row(values...)}
  1596  }
  1597  func makeEmptyRoot(t *testing.T, ddb *doltdb.DoltDB, eo editor.Options) doltdb.RootValue {
  1598  	ctx := context.Background()
  1599  	wsr, err := ref.WorkingSetRefForHead(ref.NewBranchRef("main"))
  1600  	require.NoError(t, err)
  1601  	ws, err := ddb.ResolveWorkingSet(ctx, wsr)
  1602  	require.NoError(t, err)
  1603  
  1604  	gst, err := dsess.NewAutoIncrementTracker(ctx, "dolt", ws)
  1605  	require.NoError(t, err)
  1606  	sess := writer.NewWriteSession(ddb.Format(), ws, gst, eo)
  1607  
  1608  	ws, err = sess.Flush(sql.NewContext(ctx))
  1609  	require.NoError(t, err)
  1610  	return ws.WorkingRoot()
  1611  }
  1612  
  1613  func makeRootWithTable(t *testing.T, ddb *doltdb.DoltDB, eo editor.Options, tbl table) doltdb.RootValue {
  1614  	ctx := context.Background()
  1615  	wsr, err := ref.WorkingSetRefForHead(ref.NewBranchRef("main"))
  1616  	require.NoError(t, err)
  1617  	ws, err := ddb.ResolveWorkingSet(ctx, wsr)
  1618  	require.NoError(t, err)
  1619  	dt, err := doltdb.NewEmptyTable(ctx, ddb.ValueReadWriter(), ddb.NodeStore(), tbl.ns.sch)
  1620  	require.NoError(t, err)
  1621  	root, err := ws.WorkingRoot().PutTable(ctx, doltdb.TableName{Name: tbl.ns.name}, dt)
  1622  	require.NoError(t, err)
  1623  	ws = ws.WithWorkingRoot(root)
  1624  
  1625  	gst, err := dsess.NewAutoIncrementTracker(ctx, "dolt", ws)
  1626  	require.NoError(t, err)
  1627  	noop := func(ctx *sql.Context, dbName string, root doltdb.RootValue) (err error) { return }
  1628  	sess := writer.NewWriteSession(ddb.Format(), ws, gst, eo)
  1629  	wr, err := sess.GetTableWriter(sql.NewContext(ctx), doltdb.TableName{Name: tbl.ns.name}, "test", noop)
  1630  	require.NoError(t, err)
  1631  
  1632  	sctx := sql.NewEmptyContext()
  1633  	for _, r := range tbl.rows {
  1634  		err = wr.Insert(sctx, r)
  1635  		assert.NoError(t, err)
  1636  	}
  1637  	ws, err = sess.Flush(sql.NewContext(ctx))
  1638  	require.NoError(t, err)
  1639  	return ws.WorkingRoot()
  1640  }
  1641  
  1642  type rootish struct {
  1643  	rv doltdb.RootValue
  1644  }
  1645  
  1646  func (r rootish) ResolveRootValue(ctx context.Context) (doltdb.RootValue, error) {
  1647  	return r.rv, nil
  1648  }
  1649  
  1650  func (r rootish) HashOf() (hash.Hash, error) {
  1651  	return hash.Hash{}, nil
  1652  }