github.com/dolthub/go-mysql-server@v0.18.0/sql/rowexec/alter_table_test.go (about) 1 // Copyright 2022 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 rowexec 16 17 import ( 18 "testing" 19 20 "github.com/dolthub/vitess/go/sqltypes" 21 "github.com/stretchr/testify/assert" 22 "github.com/stretchr/testify/require" 23 24 "github.com/dolthub/go-mysql-server/sql" 25 "github.com/dolthub/go-mysql-server/sql/expression" 26 "github.com/dolthub/go-mysql-server/sql/plan" 27 "github.com/dolthub/go-mysql-server/sql/types" 28 ) 29 30 func TestAddColumnToSchema(t *testing.T) { 31 myTable := sql.Schema{ 32 {Name: "i", Type: types.Int64, Source: "mytable", PrimaryKey: true}, 33 {Name: "s", Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 20), Source: "mytable", Comment: "column s"}, 34 } 35 36 type testCase struct { 37 name string 38 schema sql.Schema 39 newColumn *sql.Column 40 order *sql.ColumnOrder 41 newSchema sql.Schema 42 projections []sql.Expression 43 } 44 45 varchar20 := types.MustCreateStringWithDefaults(sqltypes.VarChar, 20) 46 testCases := []testCase{ 47 { 48 name: "add at end", 49 schema: myTable, 50 newColumn: &sql.Column{Name: "i2", Type: types.Int64, Source: "mytable"}, 51 newSchema: sql.Schema{ 52 {Name: "i", Type: types.Int64, Source: "mytable", PrimaryKey: true}, 53 {Name: "s", Type: varchar20, Source: "mytable", Comment: "column s"}, 54 {Name: "i2", Type: types.Int64, Source: "mytable"}, 55 }, 56 projections: []sql.Expression{ 57 expression.NewGetField(0, types.Int64, "i", false), 58 expression.NewGetField(1, varchar20, "s", false), 59 plan.ColDefaultExpression{&sql.Column{Name: "i2", Type: types.Int64, Source: "mytable"}}, 60 }, 61 }, 62 { 63 name: "add at end, with 'after'", 64 schema: myTable, 65 newColumn: &sql.Column{Name: "i2", Type: types.Int64, Source: "mytable"}, 66 order: &sql.ColumnOrder{AfterColumn: "s"}, 67 newSchema: sql.Schema{ 68 {Name: "i", Type: types.Int64, Source: "mytable", PrimaryKey: true}, 69 {Name: "s", Type: varchar20, Source: "mytable", Comment: "column s"}, 70 {Name: "i2", Type: types.Int64, Source: "mytable"}, 71 }, 72 projections: []sql.Expression{ 73 expression.NewGetField(0, types.Int64, "i", false), 74 expression.NewGetField(1, varchar20, "s", false), 75 plan.ColDefaultExpression{&sql.Column{Name: "i2", Type: types.Int64, Source: "mytable"}}, 76 }, 77 }, 78 { 79 name: "add at beginning", 80 schema: myTable, 81 newColumn: &sql.Column{Name: "i2", Type: types.Int64, Source: "mytable"}, 82 order: &sql.ColumnOrder{First: true}, 83 newSchema: sql.Schema{ 84 {Name: "i2", Type: types.Int64, Source: "mytable"}, 85 {Name: "i", Type: types.Int64, Source: "mytable", PrimaryKey: true}, 86 {Name: "s", Type: varchar20, Source: "mytable", Comment: "column s"}, 87 }, 88 projections: []sql.Expression{ 89 plan.ColDefaultExpression{&sql.Column{Name: "i2", Type: types.Int64, Source: "mytable"}}, 90 expression.NewGetField(0, types.Int64, "i", false), 91 expression.NewGetField(1, varchar20, "s", false), 92 }, 93 }, 94 { 95 name: "add at beginning with default", 96 schema: myTable, 97 newColumn: &sql.Column{ 98 Name: "i2", 99 Type: types.Int64, 100 Source: "mytable", 101 Default: mustDefault(expression.NewGetField(1, types.Int64, "i", false), types.Int64, false, true, true), 102 }, 103 order: &sql.ColumnOrder{First: true}, 104 newSchema: sql.Schema{ 105 {Name: "i2", Type: types.Int64, Source: "mytable", Default: mustDefault(expression.NewGetField(0, types.Int64, "i", false), types.Int64, false, true, true)}, 106 {Name: "i", Type: types.Int64, Source: "mytable", PrimaryKey: true}, 107 {Name: "s", Type: varchar20, Source: "mytable", Comment: "column s"}, 108 }, 109 projections: []sql.Expression{ 110 plan.ColDefaultExpression{&sql.Column{ 111 Name: "i2", 112 Type: types.Int64, 113 Source: "mytable", 114 Default: mustDefault(expression.NewGetField(0, types.Int64, "i", false), types.Int64, false, true, true), 115 }}, 116 expression.NewGetField(0, types.Int64, "i", false), 117 expression.NewGetField(1, varchar20, "s", false), 118 }, 119 }, 120 { 121 name: "add in middle", 122 schema: myTable, 123 newColumn: &sql.Column{Name: "i2", Type: types.Int64, Source: "mytable"}, 124 order: &sql.ColumnOrder{AfterColumn: "i"}, 125 newSchema: sql.Schema{ 126 {Name: "i", Type: types.Int64, Source: "mytable", PrimaryKey: true}, 127 {Name: "i2", Type: types.Int64, Source: "mytable"}, 128 {Name: "s", Type: varchar20, Source: "mytable", Comment: "column s"}, 129 }, 130 projections: []sql.Expression{ 131 expression.NewGetField(0, types.Int64, "i", false), 132 plan.ColDefaultExpression{&sql.Column{Name: "i2", Type: types.Int64, Source: "mytable"}}, 133 expression.NewGetField(1, varchar20, "s", false), 134 }, 135 }, 136 { 137 name: "add in middle with default", 138 schema: myTable, 139 newColumn: &sql.Column{ 140 Name: "i2", 141 Type: types.Int64, 142 Source: "mytable", 143 Default: mustDefault(expression.NewGetField(2, types.Int64, "s", false), types.Int64, false, true, true), 144 }, 145 order: &sql.ColumnOrder{AfterColumn: "i"}, 146 newSchema: sql.Schema{ 147 {Name: "i", Type: types.Int64, Source: "mytable", PrimaryKey: true}, 148 {Name: "i2", Type: types.Int64, Source: "mytable", Default: mustDefault(expression.NewGetField(1, types.Int64, "s", false), types.Int64, false, true, true)}, 149 {Name: "s", Type: varchar20, Source: "mytable", Comment: "column s"}, 150 }, 151 projections: []sql.Expression{ 152 expression.NewGetField(0, types.Int64, "i", false), 153 plan.ColDefaultExpression{&sql.Column{ 154 Name: "i2", 155 Type: types.Int64, 156 Source: "mytable", 157 Default: mustDefault(expression.NewGetField(1, types.Int64, "s", false), types.Int64, false, true, true), 158 }}, 159 expression.NewGetField(1, varchar20, "s", false), 160 }, 161 }, 162 } 163 164 for _, tc := range testCases { 165 t.Run(tc.name, func(t *testing.T) { 166 schema, projections, err := addColumnToSchema(tc.schema, tc.newColumn, tc.order) 167 if err != nil { 168 return 169 } 170 require.NoError(t, err) 171 assert.Equal(t, tc.newSchema, schema) 172 assert.Equal(t, tc.projections, projections) 173 }) 174 } 175 } 176 177 func TestModifyColumnInSchema(t *testing.T) { 178 varchar20 := types.MustCreateStringWithDefaults(sqltypes.VarChar, 20) 179 180 myTable := sql.Schema{ 181 {Name: "i", Type: types.Int64, Source: "mytable", PrimaryKey: true}, 182 {Name: "f", Type: types.Float64, Source: "mytable"}, 183 {Name: "s", Type: varchar20, Source: "mytable", Comment: "column s"}, 184 } 185 186 type testCase struct { 187 name string 188 schema sql.Schema 189 colName string 190 newColumn *sql.Column 191 order *sql.ColumnOrder 192 newSchema sql.Schema 193 projections []sql.Expression 194 } 195 196 testCases := []testCase{ 197 { 198 name: "modify last in place", 199 schema: myTable, 200 colName: "s", 201 newColumn: &sql.Column{Name: "s2", Type: types.Int64, Source: "mytable"}, 202 newSchema: sql.Schema{ 203 {Name: "i", Type: types.Int64, Source: "mytable", PrimaryKey: true}, 204 {Name: "f", Type: types.Float64, Source: "mytable"}, 205 {Name: "s2", Type: types.Int64, Source: "mytable"}, 206 }, 207 projections: []sql.Expression{ 208 expression.NewGetField(0, types.Int64, "i", false), 209 expression.NewGetField(1, types.Float64, "f", false), 210 expression.NewGetField(2, varchar20, "s", false), 211 }, 212 }, 213 { 214 name: "modify first in place", 215 schema: myTable, 216 colName: "i", 217 newColumn: &sql.Column{Name: "i2", Type: types.Int64, Source: "mytable", Comment: "my comment", PrimaryKey: true}, 218 newSchema: sql.Schema{ 219 {Name: "i2", Type: types.Int64, Source: "mytable", Comment: "my comment", PrimaryKey: true}, 220 {Name: "f", Type: types.Float64, Source: "mytable"}, 221 {Name: "s", Type: varchar20, Source: "mytable", Comment: "column s"}, 222 }, 223 projections: []sql.Expression{ 224 expression.NewGetField(0, types.Int64, "i", false), 225 expression.NewGetField(1, types.Float64, "f", false), 226 expression.NewGetField(2, varchar20, "s", false), 227 }, 228 }, 229 { 230 name: "modify first, move to middle", 231 schema: myTable, 232 colName: "i", 233 order: &sql.ColumnOrder{AfterColumn: "F"}, 234 newColumn: &sql.Column{Name: "i2", Type: types.Int64, Source: "mytable", Comment: "my comment", PrimaryKey: true}, 235 newSchema: sql.Schema{ 236 {Name: "f", Type: types.Float64, Source: "mytable"}, 237 {Name: "i2", Type: types.Int64, Source: "mytable", Comment: "my comment", PrimaryKey: true}, 238 {Name: "s", Type: varchar20, Source: "mytable", Comment: "column s"}, 239 }, 240 projections: []sql.Expression{ 241 expression.NewGetField(1, types.Float64, "f", false), 242 expression.NewGetField(0, types.Int64, "i", false), 243 expression.NewGetField(2, varchar20, "s", false), 244 }, 245 }, 246 { 247 name: "modify first, move to end", 248 schema: myTable, 249 colName: "i", 250 order: &sql.ColumnOrder{AfterColumn: "s"}, 251 newColumn: &sql.Column{Name: "i2", Type: types.Int64, Source: "mytable", Comment: "my comment", PrimaryKey: true}, 252 newSchema: sql.Schema{ 253 {Name: "f", Type: types.Float64, Source: "mytable"}, 254 {Name: "s", Type: varchar20, Source: "mytable", Comment: "column s"}, 255 {Name: "i2", Type: types.Int64, Source: "mytable", Comment: "my comment", PrimaryKey: true}, 256 }, 257 projections: []sql.Expression{ 258 expression.NewGetField(1, types.Float64, "f", false), 259 expression.NewGetField(2, varchar20, "s", false), 260 expression.NewGetField(0, types.Int64, "i", false), 261 }, 262 }, 263 { 264 name: "modify last, move first", 265 schema: myTable, 266 colName: "s", 267 order: &sql.ColumnOrder{First: true}, 268 newColumn: &sql.Column{Name: "s2", Type: types.Int64, Source: "mytable", Comment: "my comment"}, 269 newSchema: sql.Schema{ 270 {Name: "s2", Type: types.Int64, Source: "mytable", Comment: "my comment"}, 271 {Name: "i", Type: types.Int64, Source: "mytable", PrimaryKey: true}, 272 {Name: "f", Type: types.Float64, Source: "mytable"}, 273 }, 274 projections: []sql.Expression{ 275 expression.NewGetField(2, varchar20, "s", false), 276 expression.NewGetField(0, types.Int64, "i", false), 277 expression.NewGetField(1, types.Float64, "f", false), 278 }, 279 }, 280 { 281 name: "modify middle, move first", 282 schema: myTable, 283 colName: "f", 284 order: &sql.ColumnOrder{First: true}, 285 newColumn: &sql.Column{Name: "f2", Type: types.Int64, Source: "mytable", Comment: "my comment"}, 286 newSchema: sql.Schema{ 287 {Name: "f2", Type: types.Int64, Source: "mytable", Comment: "my comment"}, 288 {Name: "i", Type: types.Int64, Source: "mytable", PrimaryKey: true}, 289 {Name: "s", Type: varchar20, Source: "mytable", Comment: "column s"}, 290 }, 291 projections: []sql.Expression{ 292 expression.NewGetField(1, types.Float64, "f", false), 293 expression.NewGetField(0, types.Int64, "i", false), 294 expression.NewGetField(2, varchar20, "s", false), 295 }, 296 }, 297 { 298 name: "modify middle, move to middle", 299 schema: myTable, 300 colName: "f", 301 order: &sql.ColumnOrder{AfterColumn: "I"}, 302 newColumn: &sql.Column{Name: "f2", Type: types.Int64, Source: "mytable", Comment: "my comment"}, 303 newSchema: sql.Schema{ 304 {Name: "i", Type: types.Int64, Source: "mytable", PrimaryKey: true}, 305 {Name: "f2", Type: types.Int64, Source: "mytable", Comment: "my comment"}, 306 {Name: "s", Type: varchar20, Source: "mytable", Comment: "column s"}, 307 }, 308 projections: []sql.Expression{ 309 expression.NewGetField(0, types.Int64, "i", false), 310 expression.NewGetField(1, types.Float64, "f", false), 311 expression.NewGetField(2, varchar20, "s", false), 312 }, 313 }, 314 { 315 name: "modify last, move to middle", 316 schema: myTable, 317 colName: "s", 318 order: &sql.ColumnOrder{AfterColumn: "I"}, 319 newColumn: &sql.Column{Name: "s2", Type: types.Int64, Source: "mytable", Comment: "my comment"}, 320 newSchema: sql.Schema{ 321 {Name: "i", Type: types.Int64, Source: "mytable", PrimaryKey: true}, 322 {Name: "s2", Type: types.Int64, Source: "mytable", Comment: "my comment"}, 323 {Name: "f", Type: types.Float64, Source: "mytable"}, 324 }, 325 projections: []sql.Expression{ 326 expression.NewGetField(0, types.Int64, "i", false), 327 expression.NewGetField(2, varchar20, "s", false), 328 expression.NewGetField(1, types.Float64, "f", false), 329 }, 330 }, 331 { 332 name: "modify middle, move first with defaults", 333 schema: sql.Schema{ 334 {Name: "one", Type: types.Int64, Source: "mytable", PrimaryKey: true}, 335 {Name: "two", Type: types.Int64, Source: "mytable"}, 336 {Name: "three", Type: types.Int64, Source: "mytable", Default: mustDefault( 337 expression.NewGetFieldWithTable(1, 1, types.Int64, "", "mytable", "two", false), 338 types.Int64, false, true, false), 339 }, 340 }, 341 colName: "two", 342 order: &sql.ColumnOrder{First: true}, 343 newColumn: &sql.Column{Name: "two", Type: types.Int64, Source: "mytable", Default: mustDefault( 344 expression.NewGetFieldWithTable(0, 1, types.Int64, "", "mytable", "one", false), 345 types.Int64, false, true, false), 346 }, 347 newSchema: sql.Schema{ 348 {Name: "two", Type: types.Int64, Source: "mytable", Default: mustDefault( 349 expression.NewGetFieldWithTable(1, 1, types.Int64, "", "mytable", "one", false), 350 types.Int64, false, true, false), 351 }, 352 {Name: "one", Type: types.Int64, Source: "mytable", PrimaryKey: true}, 353 {Name: "three", Type: types.Int64, Source: "mytable", Default: mustDefault( 354 expression.NewGetFieldWithTable(0, 1, types.Int64, "", "mytable", "two", false), 355 types.Int64, false, true, false), 356 }, 357 }, 358 projections: []sql.Expression{ 359 expression.NewGetFieldWithTable(1, 0, types.Int64, "", "", "two", false), 360 expression.NewGetFieldWithTable(0, 0, types.Int64, "", "", "one", false), 361 expression.NewGetFieldWithTable(2, 0, types.Int64, "", "", "three", false), 362 }, 363 }, 364 } 365 366 for _, tc := range testCases { 367 t.Run(tc.name, func(t *testing.T) { 368 schema, projections, err := modifyColumnInSchema(tc.schema, tc.colName, tc.newColumn, tc.order) 369 if err != nil { 370 return 371 } 372 require.NoError(t, err) 373 assert.Equal(t, tc.newSchema, schema) 374 assert.Equal(t, tc.projections, projections) 375 }) 376 } 377 } 378 379 // mustDefault enforces that no error occurred when constructing the column default value. 380 func mustDefault(expr sql.Expression, outType sql.Type, representsLiteral bool, parenthesized bool, mayReturnNil bool) *sql.ColumnDefaultValue { 381 colDef, err := sql.NewColumnDefaultValue(expr, outType, representsLiteral, parenthesized, mayReturnNil) 382 if err != nil { 383 panic(err) 384 } 385 return colDef 386 }