github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/sqlddl_test.go (about) 1 // Copyright 2019 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 sqle 16 17 import ( 18 "context" 19 "fmt" 20 "strings" 21 "testing" 22 23 gms "github.com/dolthub/go-mysql-server" 24 "github.com/dolthub/go-mysql-server/sql" 25 "github.com/dolthub/go-mysql-server/sql/analyzer" 26 gmstypes "github.com/dolthub/go-mysql-server/sql/types" 27 "github.com/dolthub/vitess/go/sqltypes" 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 31 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 32 "github.com/dolthub/dolt/go/libraries/doltcore/dtestutils" 33 "github.com/dolthub/dolt/go/libraries/doltcore/env" 34 "github.com/dolthub/dolt/go/libraries/doltcore/row" 35 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 36 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess" 37 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dtables" 38 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil" 39 "github.com/dolthub/dolt/go/store/types" 40 ) 41 42 func TestCreateTable(t *testing.T) { 43 tests := []struct { 44 name string 45 query string 46 expectedTable string 47 expectedSchema schema.Schema 48 expectedErr string 49 }{ 50 { 51 name: "Test create single column schema", 52 query: "create table testTable (id int primary key)", 53 expectedTable: "testTable", 54 expectedSchema: dtestutils.CreateSchema( 55 schemaNewColumn(t, "id", 4817, gmstypes.Int32, true, schema.NotNullConstraint{})), 56 }, 57 { 58 name: "Test create two column schema", 59 query: "create table testTable (id int primary key, age int)", 60 expectedTable: "testTable", 61 expectedSchema: dtestutils.CreateSchema( 62 schemaNewColumn(t, "id", 4817, gmstypes.Int32, true, schema.NotNullConstraint{}), 63 schemaNewColumn(t, "age", 7208, gmstypes.Int32, false)), 64 }, 65 { 66 name: "Test create two column keyless schema", 67 query: "create table testTable (id int, age int)", 68 expectedTable: "testTable", 69 expectedSchema: dtestutils.CreateSchema( 70 schemaNewColumn(t, "id", 4817, gmstypes.Int32, false), 71 schemaNewColumn(t, "age", 7208, gmstypes.Int32, false)), 72 }, 73 { 74 name: "Test syntax error", 75 query: "create table testTable id int, age int", 76 expectedTable: "testTable", 77 expectedErr: "syntax error", 78 }, 79 { 80 name: "Test bad table name", 81 query: "create table -testTable (id int primary key, age int)", 82 expectedErr: "syntax error", 83 }, 84 { 85 name: "Test reserved table name", 86 query: "create table dolt_table (id int primary key, age int)", 87 expectedErr: "Invalid table name", 88 }, 89 { 90 name: "Test in use table name", 91 query: "create table people (id int primary key, age int)", 92 expectedErr: "table with name people already exists", 93 }, 94 { 95 name: "Test in use table name with if not exists", 96 query: "create table if not exists people (id int primary key, age int)", 97 expectedTable: "people", 98 expectedSchema: PeopleTestSchema, 99 }, 100 { 101 name: "Test types", 102 query: `create table testTable ( 103 id int primary key, 104 age int, 105 first_name varchar(255), 106 is_married boolean) `, 107 expectedTable: "testTable", 108 expectedSchema: dtestutils.CreateSchema( 109 schemaNewColumn(t, "id", 4817, gmstypes.Int32, true, schema.NotNullConstraint{}), 110 schemaNewColumn(t, "age", 7208, gmstypes.Int32, false), 111 schemaNewColumn(t, "first_name", 3264, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 255), false), 112 schemaNewColumn(t, "is_married", 14626, gmstypes.Boolean, false)), 113 }, 114 { 115 name: "Test all supported types", 116 expectedTable: "testTable", 117 query: `create table testTable ( 118 c0 int primary key, 119 c1 tinyint, 120 c2 smallint, 121 c3 mediumint, 122 c4 integer, 123 c5 bigint, 124 c6 bool, 125 c7 boolean, 126 c8 bit(10), 127 c9 text, 128 c10 tinytext, 129 c11 mediumtext, 130 c12 longtext, 131 c16 char(5), 132 c17 varchar(255), 133 c18 varchar(80), 134 c19 float, 135 c20 double, 136 c22 int unsigned, 137 c23 tinyint unsigned, 138 c24 smallint unsigned, 139 c25 mediumint unsigned, 140 c26 bigint unsigned, 141 c27 tinyint(1))`, 142 expectedSchema: dtestutils.CreateSchema( 143 schemaNewColumn(t, "c0", 594, gmstypes.Int32, true, schema.NotNullConstraint{}), 144 schemaNewColumn(t, "c1", 601, gmstypes.Int8, false), 145 schemaNewColumn(t, "c2", 14542, gmstypes.Int16, false), 146 schemaNewColumn(t, "c3", 13309, gmstypes.Int24, false), 147 schemaNewColumn(t, "c4", 15884, gmstypes.Int32, false), 148 schemaNewColumn(t, "c5", 14619, gmstypes.Int64, false), 149 schemaNewColumn(t, "c6", 13192, gmstypes.Boolean, false), 150 schemaNewColumn(t, "c7", 5981, gmstypes.Boolean, false), 151 schemaNewColumn(t, "c8", 14871, gmstypes.MustCreateBitType(10), false), 152 schemaNewColumn(t, "c9", 4167, gmstypes.Text, false), 153 schemaNewColumn(t, "c10", 1965, gmstypes.TinyText, false), 154 schemaNewColumn(t, "c11", 12860, gmstypes.MediumText, false), 155 schemaNewColumn(t, "c12", 7155, gmstypes.LongText, false), 156 //schemaNewColumn(t, "c13", 113, sql.TinyBlob, false), 157 //schemaNewColumn(t, "c14", 114, sql.Blob, false), 158 //schemaNewColumn(t, "c15", 115, sql.LongBlob, false), 159 schemaNewColumn(t, "c16", 15859, gmstypes.MustCreateStringWithDefaults(sqltypes.Char, 5), false), 160 schemaNewColumn(t, "c17", 11710, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 255), false), 161 schemaNewColumn(t, "c18", 6838, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false), 162 schemaNewColumn(t, "c19", 9377, gmstypes.Float32, false), 163 schemaNewColumn(t, "c20", 15979, gmstypes.Float64, false), 164 //schemaNewColumn(t, "c21", 121, sql.MustCreateDecimalType(10, 5), false), 165 schemaNewColumn(t, "c22", 2910, gmstypes.Uint32, false), 166 schemaNewColumn(t, "c23", 8740, gmstypes.Uint8, false), 167 schemaNewColumn(t, "c24", 8689, gmstypes.Uint16, false), 168 schemaNewColumn(t, "c25", 5243, gmstypes.Uint24, false), 169 schemaNewColumn(t, "c26", 9338, gmstypes.Uint64, false), 170 schemaNewColumn(t, "c27", 5981, gmstypes.Boolean, false), 171 ), 172 }, 173 { 174 name: "Test primary keys", 175 query: `create table testTable ( 176 id int, 177 age int, 178 first_name varchar(80), 179 is_married bool, 180 primary key (id, age))`, 181 expectedTable: "testTable", 182 expectedSchema: dtestutils.CreateSchema( 183 schemaNewColumn(t, "id", 4817, gmstypes.Int32, true, schema.NotNullConstraint{}), 184 schemaNewColumn(t, "age", 7208, gmstypes.Int32, true, schema.NotNullConstraint{}), 185 schemaNewColumn(t, "first_name", 3264, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false), 186 schemaNewColumn(t, "is_married", 14626, gmstypes.Boolean, false)), 187 }, 188 { 189 name: "Test not null constraints", 190 query: `create table testTable ( 191 id int, 192 age int, 193 first_name varchar(80) not null, 194 is_married bool, 195 primary key (id, age))`, 196 expectedTable: "testTable", 197 expectedSchema: dtestutils.CreateSchema( 198 schemaNewColumn(t, "id", 4817, gmstypes.Int32, true, schema.NotNullConstraint{}), 199 schemaNewColumn(t, "age", 7208, gmstypes.Int32, true, schema.NotNullConstraint{}), 200 schemaNewColumn(t, "first_name", 3264, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false, schema.NotNullConstraint{}), 201 schemaNewColumn(t, "is_married", 14626, gmstypes.Boolean, false)), 202 }, 203 { 204 name: "Test quoted columns", 205 query: "create table testTable (" + 206 "`id` int, " + 207 "`age` int, " + 208 "`timestamp` varchar(80), " + 209 "`is married` bool, " + 210 "primary key (`id`, `age`))", 211 expectedTable: "testTable", 212 expectedSchema: dtestutils.CreateSchema( 213 schemaNewColumn(t, "id", 4817, gmstypes.Int32, true, schema.NotNullConstraint{}), 214 schemaNewColumn(t, "age", 7208, gmstypes.Int32, true, schema.NotNullConstraint{}), 215 schemaNewColumn(t, "timestamp", 10168, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false), 216 schemaNewColumn(t, "is married", 14626, gmstypes.Boolean, false)), 217 }, 218 { 219 name: "Test tag comments", 220 query: `create table testTable ( 221 id int primary key, age int)`, 222 expectedTable: "testTable", 223 expectedSchema: dtestutils.CreateSchema( 224 schemaNewColumn(t, "id", 4817, gmstypes.Int32, true, schema.NotNullConstraint{}), 225 schemaNewColumn(t, "age", 7208, gmstypes.Int32, false)), 226 }, 227 // Real world examples for regression testing 228 { 229 name: "Test ip2nation", 230 query: `CREATE TABLE ip2nation ( 231 ip int(11) unsigned NOT NULL default 0, 232 country char(2) NOT NULL default '', 233 PRIMARY KEY (ip));`, 234 expectedTable: "ip2nation", 235 expectedSchema: dtestutils.CreateSchema( 236 schemaNewColumnWDefVal(t, "ip", 7265, gmstypes.Uint32, true, "0", schema.NotNullConstraint{}), 237 schemaNewColumnWDefVal(t, "country", 6630, gmstypes.MustCreateStringWithDefaults(sqltypes.Char, 2), false, `''`, schema.NotNullConstraint{})), 238 }, 239 { 240 name: "Test ip2nationCountries", 241 expectedTable: "ip2nationCountries", 242 query: `CREATE TABLE ip2nationCountries ( 243 code varchar(4) NOT NULL default '', 244 iso_code_2 varchar(2) NOT NULL default '', 245 iso_code_3 varchar(3) default '', 246 iso_country varchar(255) NOT NULL default '', 247 country varchar(255) NOT NULL default '', 248 lat float NOT NULL default 0.0, 249 lon float NOT NULL default 0.0, 250 PRIMARY KEY (code));`, 251 expectedSchema: dtestutils.CreateSchema( 252 schemaNewColumnWDefVal(t, "code", 7802, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 4), true, `''`, schema.NotNullConstraint{}), 253 schemaNewColumnWDefVal(t, "iso_code_2", 9266, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 2), false, `''`, schema.NotNullConstraint{}), 254 schemaNewColumnWDefVal(t, "iso_code_3", 8427, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 3), false, `''`), 255 schemaNewColumnWDefVal(t, "iso_country", 7151, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 255), false, `''`, schema.NotNullConstraint{}), 256 schemaNewColumnWDefVal(t, "country", 879, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 255), false, `''`, schema.NotNullConstraint{}), 257 schemaNewColumnWDefVal(t, "lat", 3502, gmstypes.Float32, false, "0.0", schema.NotNullConstraint{}), 258 schemaNewColumnWDefVal(t, "lon", 9907, gmstypes.Float32, false, "0.0", schema.NotNullConstraint{})), 259 }, 260 } 261 262 for _, tt := range tests { 263 t.Run(tt.name, func(t *testing.T) { 264 ctx := context.Background() 265 dEnv, err := CreateEmptyTestDatabase() 266 require.NoError(t, err) 267 defer dEnv.DoltDB.Close() 268 269 root, err := dEnv.WorkingRoot(ctx) 270 require.NoError(t, err) 271 272 updatedRoot, err := ExecuteSql(dEnv, root, tt.query) 273 if tt.expectedErr == "" { 274 require.NoError(t, err) 275 } else { 276 require.Error(t, err) 277 assert.Contains(t, err.Error(), tt.expectedErr) 278 return 279 } 280 281 require.NotNil(t, updatedRoot) 282 283 table, ok, err := updatedRoot.GetTable(ctx, doltdb.TableName{Name: tt.expectedTable}) 284 require.True(t, ok) 285 require.NoError(t, err) 286 287 sch, err := table.GetSchema(ctx) 288 require.NoError(t, err) 289 equalSchemas(t, tt.expectedSchema, sch) 290 }) 291 } 292 } 293 294 func TestDropTable(t *testing.T) { 295 tests := []struct { 296 name string 297 query string 298 tableNames []string 299 expectedErr string 300 }{ 301 { 302 name: "drop table", 303 query: "drop table people", 304 tableNames: []string{"people"}, 305 }, 306 { 307 name: "drop table case insensitive", 308 query: "drop table PEOPLE", 309 tableNames: []string{"people"}, 310 }, 311 { 312 name: "drop table if exists", 313 query: "drop table if exists people", 314 tableNames: []string{"people"}, 315 }, 316 { 317 name: "drop non existent", 318 query: "drop table notfound", 319 expectedErr: "table not found: notfound", 320 }, 321 { 322 name: "drop non existent if exists", 323 query: "drop table if exists notFound", 324 tableNames: []string{"notFound"}, 325 }, 326 { 327 name: "drop many tables", 328 query: "drop table people, appearances, episodes", 329 tableNames: []string{"people", "appearances", "episodes"}, 330 }, 331 { 332 name: "drop many tables, some don't exist", 333 query: "drop table if exists people, not_real, appearances, episodes", 334 tableNames: []string{"people", "appearances", "not_real", "episodes"}, 335 }, 336 } 337 338 for _, tt := range tests { 339 t.Run(tt.name, func(t *testing.T) { 340 ctx := context.Background() 341 dEnv, err := CreateTestDatabase() 342 require.NoError(t, err) 343 defer dEnv.DoltDB.Close() 344 345 root, err := dEnv.WorkingRoot(ctx) 346 require.NoError(t, err) 347 348 updatedRoot, err := ExecuteSql(dEnv, root, tt.query) 349 350 if tt.expectedErr == "" { 351 require.NoError(t, err) 352 } else { 353 require.Error(t, err) 354 assert.Contains(t, err.Error(), tt.expectedErr) 355 return 356 } 357 358 require.NotNil(t, updatedRoot) 359 for _, tableName := range tt.tableNames { 360 has, err := updatedRoot.HasTable(ctx, tableName) 361 assert.NoError(t, err) 362 assert.False(t, has) 363 } 364 }) 365 } 366 } 367 368 func TestAddColumn(t *testing.T) { 369 tests := []struct { 370 name string 371 query string 372 expectedSchema schema.Schema 373 expectedRows []row.Row 374 expectedErr string 375 }{ 376 { 377 name: "alter add string column no default", 378 query: "alter table people add (newColumn varchar(80))", 379 expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema, 380 schemaNewColumn(t, "newColumn", 4208, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false)), 381 expectedRows: addColToRows(t, AllPeopleRows, 4208, nil), 382 }, 383 { 384 name: "alter add float column without default", 385 query: "alter table people add (newColumn float)", 386 expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema, 387 schemaNewColumn(t, "newColumn", 4208, gmstypes.Float32, false)), 388 expectedRows: addColToRows(t, AllPeopleRows, 4208, nil), 389 }, 390 { 391 name: "alter add uint column without default", 392 query: "alter table people add (newColumn bigint unsigned)", 393 expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema, 394 schemaNewColumn(t, "newColumn", 4208, gmstypes.Uint64, false)), 395 expectedRows: addColToRows(t, AllPeopleRows, 4208, nil), 396 }, 397 { 398 name: "alter add int column default", 399 query: "alter table people add (newColumn int default 2)", 400 expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema, 401 schemaNewColumnWDefVal(t, "newColumn", 2803, gmstypes.Int32, false, "2")), 402 expectedRows: addColToRows(t, AllPeopleRows, 2803, types.Int(int32(2))), 403 }, 404 { 405 name: "alter add uint column default", 406 query: "alter table people add (newColumn bigint unsigned default 20)", 407 expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema, 408 schemaNewColumnWDefVal(t, "newColumn", 517, gmstypes.Uint64, false, "20")), 409 expectedRows: addColToRows(t, AllPeopleRows, 517, types.Uint(uint64(20))), 410 }, 411 { 412 name: "alter add string column with default", 413 query: "alter table people add (newColumn varchar(80) default 'hi')", 414 expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema, 415 schemaNewColumnWDefVal(t, "newColumn", 13690, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false, `'hi'`)), 416 expectedRows: addColToRows(t, AllPeopleRows, 13690, types.String("hi")), 417 }, 418 { 419 name: "alter add column first", 420 query: "alter table people add newColumn varchar(80) first", 421 expectedSchema: dtestutils.CreateSchema( 422 schemaNewColumn(t, "newColumn", 4208, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false), 423 schema.NewColumn("id", IdTag, types.IntKind, true, schema.NotNullConstraint{}), 424 schema.NewColumn("first_name", FirstNameTag, types.StringKind, false, schema.NotNullConstraint{}), 425 schema.NewColumn("last_name", LastNameTag, types.StringKind, false, schema.NotNullConstraint{}), 426 schema.NewColumn("is_married", IsMarriedTag, types.IntKind, false), 427 schema.NewColumn("age", AgeTag, types.IntKind, false), 428 schema.NewColumn("rating", RatingTag, types.FloatKind, false), 429 schema.NewColumn("uuid", UuidTag, types.StringKind, false), 430 schema.NewColumn("num_episodes", NumEpisodesTag, types.UintKind, false), 431 ), 432 expectedRows: addColToRows(t, AllPeopleRows, 4208, nil), 433 }, 434 { 435 name: "alter add column middle", 436 query: "alter table people add newColumn varchar(80) after last_name", 437 expectedSchema: dtestutils.CreateSchema( 438 schema.NewColumn("id", IdTag, types.IntKind, true, schema.NotNullConstraint{}), 439 schema.NewColumn("first_name", FirstNameTag, types.StringKind, false, schema.NotNullConstraint{}), 440 schema.NewColumn("last_name", LastNameTag, types.StringKind, false, schema.NotNullConstraint{}), 441 schemaNewColumn(t, "newColumn", 4208, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false), 442 schema.NewColumn("is_married", IsMarriedTag, types.IntKind, false), 443 schema.NewColumn("age", AgeTag, types.IntKind, false), 444 schema.NewColumn("rating", RatingTag, types.FloatKind, false), 445 schema.NewColumn("uuid", UuidTag, types.StringKind, false), 446 schema.NewColumn("num_episodes", NumEpisodesTag, types.UintKind, false), 447 ), 448 expectedRows: addColToRows(t, AllPeopleRows, 4208, nil), 449 }, 450 { 451 name: "alter add column not null", 452 query: "alter table people add (newColumn varchar(80) not null default 'default')", 453 expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema, 454 schemaNewColumnWDefVal(t, "newColumn", 13690, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false, `'default'`, schema.NotNullConstraint{})), 455 expectedRows: addColToRows(t, AllPeopleRows, 13690, types.String("default")), 456 }, 457 { 458 name: "alter add column not null with expression default", 459 query: "alter table people add (newColumn int not null default (2+2/2))", 460 expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema, 461 schemaNewColumnWDefVal(t, "newColumn", 2803, gmstypes.Int32, false, "((2 + (2 / 2)))", schema.NotNullConstraint{})), 462 expectedRows: addColToRows(t, AllPeopleRows, 2803, types.Int(3)), 463 }, 464 { 465 name: "alter add column not null with negative expression", 466 query: "alter table people add (newColumn float not null default -1.1)", 467 expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema, 468 schemaNewColumnWDefVal(t, "newColumn", 12469, gmstypes.Float32, false, "-1.1", schema.NotNullConstraint{})), 469 expectedRows: addColToRows(t, AllPeopleRows, 12469, types.Float(float32(-1.1))), 470 }, 471 { 472 name: "alter add column not null with type mismatch in default", 473 query: "alter table people add (newColumn float not null default 'not a number')", 474 expectedErr: "incompatible type", 475 }, 476 { 477 name: "alter add column column not found", 478 query: "alter table people add column newColumn float after notFound", 479 expectedErr: `table "people" does not have column "notFound"`, 480 }, 481 { 482 name: "alter add column table not found", 483 query: "alter table notFound add column newColumn float", 484 expectedErr: "table not found: notFound", 485 }, 486 { 487 name: "alter add column not null without default", 488 query: "alter table people add (newColumn varchar(80) not null)", 489 expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema, 490 schemaNewColumnWDefVal(t, "newColumn", 13690, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false, "", schema.NotNullConstraint{})), 491 expectedRows: addColToRows(t, AllPeopleRows, 13690, types.String("")), 492 }, 493 { 494 name: "alter add column nullable", 495 query: "alter table people add (newColumn bigint)", 496 expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema, 497 schemaNewColumn(t, "newColumn", 4435, gmstypes.Int64, false)), 498 expectedRows: AllPeopleRows, 499 }, 500 { 501 name: "alter add column with optional column keyword", 502 query: "alter table people add column (newColumn varchar(80))", 503 expectedSchema: dtestutils.AddColumnToSchema(PeopleTestSchema, 504 schemaNewColumn(t, "newColumn", 4208, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false)), 505 expectedRows: AllPeopleRows, 506 }, 507 { 508 name: "alter table add column name clash", 509 query: "alter table people add column(age int)", 510 expectedErr: `Column "age" already exists`, 511 }, 512 } 513 514 for _, tt := range tests { 515 t.Run(tt.name, func(t *testing.T) { 516 dEnv, err := CreateTestDatabase() 517 require.NoError(t, err) 518 defer dEnv.DoltDB.Close() 519 520 ctx := context.Background() 521 root, err := dEnv.WorkingRoot(ctx) 522 require.NoError(t, err) 523 524 updatedRoot, err := ExecuteSql(dEnv, root, tt.query) 525 526 if tt.expectedErr == "" { 527 require.NoError(t, err) 528 } else { 529 require.Error(t, err) 530 assert.Contains(t, err.Error(), tt.expectedErr) 531 return 532 } 533 534 assert.NotNil(t, updatedRoot) 535 table, _, err := updatedRoot.GetTable(ctx, doltdb.TableName{Name: PeopleTableName}) 536 537 assert.NoError(t, err) 538 sch, err := table.GetSchema(ctx) 539 assert.NoError(t, err) 540 equalSchemas(t, tt.expectedSchema, sch) 541 542 if types.Format_Default != types.Format_LD_1 { 543 return // todo: convert these to enginetests 544 } 545 546 updatedTable, ok, err := updatedRoot.GetTable(ctx, doltdb.TableName{Name: "people"}) 547 assert.NoError(t, err) 548 require.True(t, ok) 549 550 rowData, err := updatedTable.GetNomsRowData(ctx) 551 assert.NoError(t, err) 552 var foundRows []row.Row 553 err = rowData.Iter(ctx, func(key, value types.Value) (stop bool, err error) { 554 r, err := row.FromNoms(tt.expectedSchema, key.(types.Tuple), value.(types.Tuple)) 555 assert.NoError(t, err) 556 foundRows = append(foundRows, r) 557 return false, nil 558 }) 559 560 assert.NoError(t, err) 561 assert.Equal(t, tt.expectedRows, foundRows) 562 }) 563 } 564 } 565 566 func TestRenameColumn(t *testing.T) { 567 tests := []struct { 568 name string 569 query string 570 expectedSchema schema.Schema 571 expectedRows []row.Row 572 expectedErr string 573 }{ 574 { 575 name: "alter rename column with column and as keywords", 576 query: "alter table people rename column rating as newRating", 577 expectedSchema: dtestutils.CreateSchema( 578 schema.NewColumn("id", IdTag, types.IntKind, true, schema.NotNullConstraint{}), 579 schema.NewColumn("first_name", FirstNameTag, types.StringKind, false, schema.NotNullConstraint{}), 580 schema.NewColumn("last_name", LastNameTag, types.StringKind, false, schema.NotNullConstraint{}), 581 schema.NewColumn("is_married", IsMarriedTag, types.IntKind, false), 582 schema.NewColumn("age", AgeTag, types.IntKind, false), 583 schema.NewColumn("newRating", RatingTag, types.FloatKind, false), 584 schema.NewColumn("uuid", UuidTag, types.StringKind, false), 585 schema.NewColumn("num_episodes", NumEpisodesTag, types.UintKind, false), 586 ), 587 expectedRows: AllPeopleRows, 588 }, 589 { 590 name: "alter rename column with column and to keyword", 591 query: "alter table people rename column rating to newRating", 592 expectedSchema: dtestutils.CreateSchema( 593 schema.NewColumn("id", IdTag, types.IntKind, true, schema.NotNullConstraint{}), 594 schema.NewColumn("first_name", FirstNameTag, types.StringKind, false, schema.NotNullConstraint{}), 595 schema.NewColumn("last_name", LastNameTag, types.StringKind, false, schema.NotNullConstraint{}), 596 schema.NewColumn("is_married", IsMarriedTag, types.IntKind, false), 597 schema.NewColumn("age", AgeTag, types.IntKind, false), 598 schema.NewColumn("newRating", RatingTag, types.FloatKind, false), 599 schema.NewColumn("uuid", UuidTag, types.StringKind, false), 600 schema.NewColumn("num_episodes", NumEpisodesTag, types.UintKind, false), 601 ), 602 expectedRows: AllPeopleRows, 603 }, 604 { 605 name: "alter rename primary key column", 606 query: "alter table people rename column id to newId", 607 expectedSchema: dtestutils.CreateSchema( 608 schema.NewColumn("newId", IdTag, types.IntKind, true, schema.NotNullConstraint{}), 609 schema.NewColumn("first_name", FirstNameTag, types.StringKind, false, schema.NotNullConstraint{}), 610 schema.NewColumn("last_name", LastNameTag, types.StringKind, false, schema.NotNullConstraint{}), 611 schema.NewColumn("is_married", IsMarriedTag, types.IntKind, false), 612 schema.NewColumn("age", AgeTag, types.IntKind, false), 613 schema.NewColumn("rating", RatingTag, types.FloatKind, false), 614 schema.NewColumn("uuid", UuidTag, types.StringKind, false), 615 schema.NewColumn("num_episodes", NumEpisodesTag, types.UintKind, false), 616 ), 617 expectedRows: AllPeopleRows, 618 }, 619 { 620 name: "table not found", 621 query: "alter table notFound rename column id to newId", 622 expectedErr: "table not found: notFound", 623 }, 624 { 625 name: "column not found", 626 query: "alter table people rename column notFound to newNotFound", 627 expectedErr: `table "people" does not have column "notFound"`, 628 }, 629 { 630 name: "column name collision", 631 query: "alter table people rename column id to AGE", 632 expectedErr: "Column \"AGE\" already exists", 633 }, 634 } 635 636 for _, tt := range tests { 637 t.Run(tt.name, func(t *testing.T) { 638 dEnv, err := CreateTestDatabase() 639 require.NoError(t, err) 640 defer dEnv.DoltDB.Close() 641 642 ctx := context.Background() 643 root, _ := dEnv.WorkingRoot(ctx) 644 645 updatedRoot, err := ExecuteSql(dEnv, root, tt.query) 646 647 if tt.expectedErr == "" { 648 require.NoError(t, err) 649 } else { 650 require.Error(t, err) 651 assert.Contains(t, err.Error(), tt.expectedErr) 652 return 653 } 654 655 require.NotNil(t, updatedRoot) 656 table, _, err := updatedRoot.GetTable(ctx, doltdb.TableName{Name: PeopleTableName}) 657 assert.NoError(t, err) 658 sch, err := table.GetSchema(ctx) 659 require.NoError(t, err) 660 assert.Equal(t, tt.expectedSchema, sch) 661 662 if types.Format_Default != types.Format_LD_1 { 663 return // todo: convert these to enginetests 664 } 665 666 updatedTable, ok, err := updatedRoot.GetTable(ctx, doltdb.TableName{Name: "people"}) 667 assert.NoError(t, err) 668 require.True(t, ok) 669 670 rowData, err := updatedTable.GetNomsRowData(ctx) 671 assert.NoError(t, err) 672 var foundRows []row.Row 673 err = rowData.Iter(ctx, func(key, value types.Value) (stop bool, err error) { 674 updatedSch, err := updatedTable.GetSchema(ctx) 675 assert.NoError(t, err) 676 r, err := row.FromNoms(updatedSch, key.(types.Tuple), value.(types.Tuple)) 677 assert.NoError(t, err) 678 foundRows = append(foundRows, r) 679 return false, nil 680 }) 681 682 assert.NoError(t, err) 683 assert.Equal(t, tt.expectedRows, foundRows) 684 }) 685 } 686 } 687 688 func TestRenameTableStatements(t *testing.T) { 689 tests := []struct { 690 name string 691 query string 692 oldTableName string 693 newTableName string 694 expectedSchema schema.Schema 695 expectedRows []row.Row 696 expectedErr string 697 }{ 698 { 699 name: "alter rename table", 700 query: "rename table people to newPeople", 701 oldTableName: "people", 702 newTableName: "newPeople", 703 expectedSchema: PeopleTestSchema, 704 expectedRows: AllPeopleRows, 705 }, 706 { 707 name: "alter rename table with alter syntax", 708 query: "alter table people rename to newPeople", 709 oldTableName: "people", 710 newTableName: "newPeople", 711 expectedSchema: PeopleTestSchema, 712 expectedRows: AllPeopleRows, 713 }, 714 { 715 name: "rename multiple tables", 716 query: "rename table people to newPeople, appearances to newAppearances", 717 oldTableName: "appearances", 718 newTableName: "newAppearances", 719 expectedSchema: AppearancesTestSchema, 720 expectedRows: AllAppsRows, 721 }, 722 { 723 name: "alter rename table with alter syntax", 724 query: "alter table people rename to 123People", 725 oldTableName: "people", 726 newTableName: "123People", 727 expectedSchema: PeopleTestSchema, 728 expectedRows: AllPeopleRows, 729 }, 730 { 731 name: "table not found", 732 query: "rename table notFound to newNowFound", 733 expectedErr: "table not found: notFound", 734 }, 735 { 736 name: "invalid table name", 737 query: "rename table people to `a!trailing^space*is%the(worst) `", 738 expectedErr: "Invalid table name", 739 }, 740 { 741 name: "reserved table name", 742 query: "rename table people to dolt_table", 743 expectedErr: "Invalid table name", 744 }, 745 { 746 name: "table name in use", 747 query: "rename table people to appearances", 748 expectedErr: "already exists", 749 }, 750 } 751 752 for _, tt := range tests { 753 t.Run(tt.name, func(t *testing.T) { 754 dEnv, err := CreateTestDatabase() 755 require.NoError(t, err) 756 defer dEnv.DoltDB.Close() 757 758 ctx := context.Background() 759 root, err := dEnv.WorkingRoot(ctx) 760 require.NoError(t, err) 761 762 updatedRoot, err := ExecuteSql(dEnv, root, tt.query) 763 if len(tt.expectedErr) > 0 { 764 require.Error(t, err) 765 assert.Contains(t, err.Error(), tt.expectedErr) 766 return 767 } else { 768 require.NoError(t, err) 769 } 770 require.NotNil(t, updatedRoot) 771 772 has, err := updatedRoot.HasTable(ctx, tt.oldTableName) 773 require.NoError(t, err) 774 assert.False(t, has) 775 776 newTable, ok, err := updatedRoot.GetTable(ctx, doltdb.TableName{Name: tt.newTableName}) 777 require.NoError(t, err) 778 require.True(t, ok) 779 780 sch, err := newTable.GetSchema(ctx) 781 require.NoError(t, err) 782 require.Equal(t, tt.expectedSchema, sch) 783 784 if types.Format_Default != types.Format_LD_1 { 785 return // todo: convert these to enginetests 786 } 787 788 rowData, err := newTable.GetNomsRowData(ctx) 789 require.NoError(t, err) 790 var foundRows []row.Row 791 err = rowData.Iter(ctx, func(key, value types.Value) (stop bool, err error) { 792 r, err := row.FromNoms(tt.expectedSchema, key.(types.Tuple), value.(types.Tuple)) 793 require.NoError(t, err) 794 foundRows = append(foundRows, r) 795 return false, nil 796 }) 797 798 require.NoError(t, err) 799 800 // Some test cases deal with rows declared in a different order than noms returns them, so use an order- 801 // insensitive comparison here. 802 assert.ElementsMatch(t, tt.expectedRows, foundRows) 803 }) 804 } 805 } 806 807 func TestAlterSystemTables(t *testing.T) { 808 systemTableNames := []string{"dolt_log", "dolt_history_people", "dolt_diff_people", "dolt_commit_diff_people", "dolt_schemas"} 809 reservedTableNames := []string{"dolt_query_catalog", "dolt_docs", "dolt_procedures", "dolt_ignore"} 810 811 var dEnv *env.DoltEnv 812 var err error 813 setup := func() { 814 dEnv, err = CreateTestDatabase() 815 require.NoError(t, err) 816 817 CreateTestTable(t, dEnv, "dolt_docs", doltdb.DocsSchema, 818 "INSERT INTO dolt_docs VALUES ('LICENSE.md','A license')") 819 CreateTestTable(t, dEnv, doltdb.DoltQueryCatalogTableName, dtables.DoltQueryCatalogSchema, 820 "INSERT INTO dolt_query_catalog VALUES ('abc123', 1, 'example', 'select 2+2 from dual', 'description')") 821 CreateTestTable(t, dEnv, doltdb.SchemasTableName, schemaTableSchema, 822 "INSERT INTO dolt_schemas (type, name, fragment) VALUES ('view', 'name', 'create view name as select 2+2 from dual')") 823 ExecuteSetupSQL(context.Background(), ` 824 CREATE PROCEDURE simple_proc2() SELECT 1+1; 825 INSERT INTO dolt_ignore VALUES ('test', 1);`)(t, dEnv) 826 } 827 828 t.Run("Create", func(t *testing.T) { 829 setup() 830 defer dEnv.DoltDB.Close() 831 for _, tableName := range append(systemTableNames, reservedTableNames...) { 832 assertFails(t, dEnv, fmt.Sprintf("create table %s (a int primary key not null)", tableName), "reserved") 833 } 834 }) 835 836 t.Run("Drop", func(t *testing.T) { 837 setup() 838 defer dEnv.DoltDB.Close() 839 for _, tableName := range systemTableNames { 840 expectedErr := "system table" 841 if strings.HasPrefix(tableName, "dolt_diff") || strings.HasPrefix(tableName, "dolt_history") { 842 expectedErr = "system tables cannot be dropped or altered" 843 } 844 assertFails(t, dEnv, fmt.Sprintf("drop table %s", tableName), expectedErr) 845 } 846 for _, tableName := range reservedTableNames { 847 assertSucceeds(t, dEnv, fmt.Sprintf("drop table %s", tableName)) 848 } 849 }) 850 851 t.Run("Rename", func(t *testing.T) { 852 setup() 853 defer dEnv.DoltDB.Close() 854 for _, tableName := range systemTableNames { 855 expectedErr := "system table" 856 if strings.HasPrefix(tableName, "dolt_diff") || strings.HasPrefix(tableName, "dolt_history") { 857 expectedErr = "system tables cannot be dropped or altered" 858 } 859 assertFails(t, dEnv, fmt.Sprintf("rename table %s to newname", tableName), expectedErr) 860 } 861 for i, tableName := range reservedTableNames { 862 assertSucceeds(t, dEnv, fmt.Sprintf("rename table %s to newname%d", tableName, i)) 863 } 864 }) 865 866 t.Run("Alter", func(t *testing.T) { 867 setup() 868 defer dEnv.DoltDB.Close() 869 for _, tableName := range append(systemTableNames, reservedTableNames...) { 870 expectedErr := "cannot be altered" 871 if strings.HasPrefix(tableName, "dolt_diff") || strings.HasPrefix(tableName, "dolt_history") { 872 expectedErr = " cannot be altered" 873 } 874 assertFails(t, dEnv, fmt.Sprintf("alter table %s add column a int", tableName), expectedErr) 875 } 876 }) 877 } 878 879 func TestParseCreateTableStatement(t *testing.T) { 880 tests := []struct { 881 name string 882 query string 883 expectedTable string 884 expectedSchema schema.Schema 885 expectedErr string 886 }{ 887 { 888 name: "Test create single column schema", 889 query: "create table testTable (id int primary key)", 890 expectedTable: "testTable", 891 expectedSchema: dtestutils.CreateSchema( 892 schemaNewColumn(t, "id", 4817, gmstypes.Int32, true, schema.NotNullConstraint{})), 893 }, 894 { 895 name: "Test create table starting with number", 896 query: "create table 123table (id int primary key)", 897 expectedTable: "123table", 898 expectedSchema: dtestutils.CreateSchema( 899 schemaNewColumn(t, "id", 4817, gmstypes.Int32, true, schema.NotNullConstraint{})), 900 }, 901 { 902 name: "Test create two column schema", 903 query: "create table testTable (id int primary key, age int)", 904 expectedTable: "testTable", 905 expectedSchema: dtestutils.CreateSchema( 906 schemaNewColumn(t, "id", 4817, gmstypes.Int32, true, schema.NotNullConstraint{}), 907 schemaNewColumn(t, "age", 7208, gmstypes.Int32, false)), 908 }, 909 { 910 name: "Test syntax error", 911 query: "create table testTable id int, age int", 912 expectedTable: "testTable", 913 expectedErr: "syntax error", 914 }, 915 { 916 name: "Test types", 917 query: `create table testTable ( 918 id int primary key, 919 age int, 920 first_name varchar(255), 921 is_married boolean) `, 922 expectedTable: "testTable", 923 expectedSchema: dtestutils.CreateSchema( 924 schemaNewColumn(t, "id", 4817, gmstypes.Int32, true, schema.NotNullConstraint{}), 925 schemaNewColumn(t, "age", 7208, gmstypes.Int32, false), 926 schemaNewColumn(t, "first_name", 3264, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 255), false), 927 schemaNewColumn(t, "is_married", 14626, gmstypes.Boolean, false)), 928 }, 929 { 930 name: "Test all supported types", 931 expectedTable: "testTable", 932 query: `create table testTable ( 933 c0 int primary key, 934 c1 tinyint, 935 c2 smallint, 936 c3 mediumint, 937 c4 integer, 938 c5 bigint, 939 c6 bool, 940 c7 boolean, 941 c8 bit(10), 942 c9 text, 943 c10 tinytext, 944 c11 mediumtext, 945 c12 longtext, 946 c16 char(5), 947 c17 varchar(255), 948 c18 varchar(80), 949 c19 float, 950 c20 double, 951 c22 int unsigned, 952 c23 tinyint unsigned, 953 c24 smallint unsigned, 954 c25 mediumint unsigned, 955 c26 bigint unsigned)`, 956 expectedSchema: dtestutils.CreateSchema( 957 schemaNewColumn(t, "c0", 594, gmstypes.Int32, true, schema.NotNullConstraint{}), 958 schemaNewColumn(t, "c1", 601, gmstypes.Int8, false), 959 schemaNewColumn(t, "c2", 14542, gmstypes.Int16, false), 960 schemaNewColumn(t, "c3", 13309, gmstypes.Int24, false), 961 schemaNewColumn(t, "c4", 15884, gmstypes.Int32, false), 962 schemaNewColumn(t, "c5", 14619, gmstypes.Int64, false), 963 schemaNewColumn(t, "c6", 13192, gmstypes.Boolean, false), 964 schemaNewColumn(t, "c7", 5981, gmstypes.Boolean, false), 965 schemaNewColumn(t, "c8", 14871, gmstypes.MustCreateBitType(10), false), 966 schemaNewColumn(t, "c9", 4167, gmstypes.Text, false), 967 schemaNewColumn(t, "c10", 1965, gmstypes.TinyText, false), 968 schemaNewColumn(t, "c11", 12860, gmstypes.MediumText, false), 969 schemaNewColumn(t, "c12", 7155, gmstypes.LongText, false), 970 //schemaNewColumn(t, "c13", 113, sql.TinyBlob, false), 971 //schemaNewColumn(t, "c14", 114, sql.Blob, false), 972 //schemaNewColumn(t, "c15", 115, sql.LongBlob, false), 973 schemaNewColumn(t, "c16", 15859, gmstypes.MustCreateStringWithDefaults(sqltypes.Char, 5), false), 974 schemaNewColumn(t, "c17", 11710, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 255), false), 975 schemaNewColumn(t, "c18", 6838, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false), 976 schemaNewColumn(t, "c19", 9377, gmstypes.Float32, false), 977 schemaNewColumn(t, "c20", 15979, gmstypes.Float64, false), 978 //schemaNewColumn(t, "c21", 121, sql.MustCreateDecimalType(10, 5), false), 979 schemaNewColumn(t, "c22", 2910, gmstypes.Uint32, false), 980 schemaNewColumn(t, "c23", 8740, gmstypes.Uint8, false), 981 schemaNewColumn(t, "c24", 8689, gmstypes.Uint16, false), 982 schemaNewColumn(t, "c25", 5243, gmstypes.Uint24, false), 983 schemaNewColumn(t, "c26", 9338, gmstypes.Uint64, false), 984 ), 985 }, 986 { 987 name: "Test primary keys", 988 query: `create table testTable ( 989 id int, 990 age int, 991 first_name varchar(80), 992 is_married bool, 993 primary key (id, age))`, 994 expectedTable: "testTable", 995 expectedSchema: dtestutils.CreateSchema( 996 schemaNewColumn(t, "id", 4817, gmstypes.Int32, true, schema.NotNullConstraint{}), 997 schemaNewColumn(t, "age", 7208, gmstypes.Int32, true, schema.NotNullConstraint{}), 998 schemaNewColumn(t, "first_name", 3264, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false), 999 schemaNewColumn(t, "is_married", 14626, gmstypes.Boolean, false)), 1000 }, 1001 { 1002 name: "Test not null constraints", 1003 query: `create table testTable ( 1004 id int, 1005 age int, 1006 first_name varchar(80) not null, 1007 is_married bool, 1008 primary key (id, age))`, 1009 expectedTable: "testTable", 1010 expectedSchema: dtestutils.CreateSchema( 1011 schemaNewColumn(t, "id", 4817, gmstypes.Int32, true, schema.NotNullConstraint{}), 1012 schemaNewColumn(t, "age", 7208, gmstypes.Int32, true, schema.NotNullConstraint{}), 1013 schemaNewColumn(t, "first_name", 3264, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false, schema.NotNullConstraint{}), 1014 schemaNewColumn(t, "is_married", 14626, gmstypes.Boolean, false)), 1015 }, 1016 { 1017 name: "Test quoted columns", 1018 query: "create table testTable (" + 1019 "`id` int, " + 1020 "`age` int, " + 1021 "`timestamp` varchar(80), " + 1022 "`is married` bool, " + 1023 "primary key (`id`, `age`))", 1024 expectedTable: "testTable", 1025 expectedSchema: dtestutils.CreateSchema( 1026 schemaNewColumn(t, "id", 4817, gmstypes.Int32, true, schema.NotNullConstraint{}), 1027 schemaNewColumn(t, "age", 7208, gmstypes.Int32, true, schema.NotNullConstraint{}), 1028 schemaNewColumn(t, "timestamp", 10168, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false), 1029 schemaNewColumn(t, "is married", 14626, gmstypes.Boolean, false)), 1030 }, 1031 { 1032 name: "Test tag comments", 1033 query: `create table testTable ( 1034 id int primary key, age int)`, 1035 expectedTable: "testTable", 1036 expectedSchema: dtestutils.CreateSchema( 1037 schemaNewColumn(t, "id", 4817, gmstypes.Int32, true, schema.NotNullConstraint{}), 1038 schemaNewColumn(t, "age", 7208, gmstypes.Int32, false)), 1039 }, 1040 // Real world examples for regression testing 1041 { 1042 name: "Test ip2nation", 1043 query: `CREATE TABLE ip2nation ( 1044 ip int(11) unsigned NOT NULL default 0, 1045 country char(2) NOT NULL default '', 1046 PRIMARY KEY (ip));`, 1047 expectedTable: "ip2nation", 1048 expectedSchema: dtestutils.CreateSchema( 1049 schemaNewColumnWDefVal(t, "ip", 7265, gmstypes.Uint32, true, "0", schema.NotNullConstraint{}), 1050 schemaNewColumnWDefVal(t, "country", 6630, gmstypes.MustCreateStringWithDefaults(sqltypes.Char, 2), false, `''`, schema.NotNullConstraint{})), 1051 }, 1052 { 1053 name: "Test ip2nationCountries", 1054 expectedTable: "ip2nationCountries", 1055 query: `CREATE TABLE ip2nationCountries ( 1056 code varchar(4) NOT NULL default '', 1057 iso_code_2 varchar(2) NOT NULL default '', 1058 iso_code_3 varchar(3) default '', 1059 iso_country varchar(255) NOT NULL default '', 1060 country varchar(255) NOT NULL default '', 1061 lat float NOT NULL default 0.0, 1062 lon float NOT NULL default 0.0, 1063 PRIMARY KEY (code));`, 1064 expectedSchema: dtestutils.CreateSchema( 1065 schemaNewColumnWDefVal(t, "code", 7802, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 4), true, `''`, schema.NotNullConstraint{}), 1066 schemaNewColumnWDefVal(t, "iso_code_2", 9266, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 2), false, `''`, schema.NotNullConstraint{}), 1067 schemaNewColumnWDefVal(t, "iso_code_3", 8427, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 3), false, `''`), 1068 schemaNewColumnWDefVal(t, "iso_country", 7151, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 255), false, `''`, schema.NotNullConstraint{}), 1069 schemaNewColumnWDefVal(t, "country", 879, gmstypes.MustCreateStringWithDefaults(sqltypes.VarChar, 255), false, `''`, schema.NotNullConstraint{}), 1070 schemaNewColumnWDefVal(t, "lat", 3502, gmstypes.Float32, false, "0.0", schema.NotNullConstraint{}), 1071 schemaNewColumnWDefVal(t, "lon", 9907, gmstypes.Float32, false, "0.0", schema.NotNullConstraint{})), 1072 }, 1073 } 1074 1075 for _, tt := range tests { 1076 t.Run(tt.name, func(t *testing.T) { 1077 dEnv := dtestutils.CreateTestEnv() 1078 defer dEnv.DoltDB.Close() 1079 ctx := context.Background() 1080 root, _ := dEnv.WorkingRoot(ctx) 1081 //eng, dbName, _ := engine.NewSqlEngineForEnv(ctx, dEnv) 1082 eng, sqlCtx := newTestEngine(ctx, dEnv) 1083 1084 _, iter, err := eng.Query(sqlCtx, "create database test") 1085 if err != nil { 1086 panic(err) 1087 } 1088 _, _ = sql.RowIterToRows(sqlCtx, iter) 1089 sqlCtx.SetCurrentDatabase("test") 1090 1091 tblName, sch, err := sqlutil.ParseCreateTableStatement(sqlCtx, root, eng, tt.query) 1092 1093 if tt.expectedErr != "" { 1094 require.Error(t, err) 1095 assert.Contains(t, err.Error(), tt.expectedErr) 1096 } else { 1097 require.NoError(t, err) 1098 equalSchemas(t, tt.expectedSchema, sch) 1099 assert.Equal(t, tt.expectedTable, tblName) 1100 } 1101 }) 1102 } 1103 } 1104 1105 func newTestEngine(ctx context.Context, dEnv *env.DoltEnv) (*gms.Engine, *sql.Context) { 1106 pro, err := NewDoltDatabaseProviderWithDatabases("main", dEnv.FS, nil, nil) 1107 if err != nil { 1108 panic(err) 1109 } 1110 1111 mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv) 1112 if err != nil { 1113 panic(err) 1114 } 1115 1116 doltSession, err := dsess.NewDoltSession(sql.NewBaseSession(), pro, dEnv.Config.WriteableConfig(), nil, nil) 1117 if err != nil { 1118 panic(err) 1119 } 1120 1121 sqlCtx := sql.NewContext(ctx, sql.WithSession(doltSession)) 1122 sqlCtx.SetCurrentDatabase(mrEnv.GetFirstDatabase()) 1123 1124 return gms.New(analyzer.NewBuilder(pro).WithParallelism(1).Build(), &gms.Config{ 1125 IsReadOnly: false, 1126 IsServerLocked: false, 1127 }), sqlCtx 1128 } 1129 func TestIndexOverwrite(t *testing.T) { 1130 ctx := context.Background() 1131 dEnv := dtestutils.CreateTestEnv() 1132 defer dEnv.DoltDB.Close() 1133 root, err := dEnv.WorkingRoot(ctx) 1134 if err != nil { 1135 panic(err) 1136 } 1137 root, err = ExecuteSql(dEnv, root, ` 1138 CREATE TABLE parent ( 1139 pk bigint PRIMARY KEY, 1140 v1 bigint, 1141 INDEX (v1) 1142 ); 1143 CREATE TABLE child ( 1144 pk varchar(10) PRIMARY KEY, 1145 parent_value bigint, 1146 CONSTRAINT fk_child FOREIGN KEY (parent_value) 1147 REFERENCES parent(v1) 1148 ); 1149 CREATE TABLE child_idx ( 1150 pk varchar(10) PRIMARY KEY, 1151 parent_value bigint, 1152 INDEX (parent_value), 1153 CONSTRAINT fk_child_idx FOREIGN KEY (parent_value) 1154 REFERENCES parent(v1) 1155 ); 1156 CREATE TABLE child_unq ( 1157 pk varchar(10) PRIMARY KEY, 1158 parent_value bigint, 1159 CONSTRAINT fk_child_unq FOREIGN KEY (parent_value) 1160 REFERENCES parent(v1) 1161 ); 1162 CREATE TABLE child_non_unq ( 1163 pk varchar(10) PRIMARY KEY, 1164 parent_value bigint, 1165 CONSTRAINT fk_child_non_unq FOREIGN KEY (parent_value) 1166 REFERENCES parent(v1) 1167 ); 1168 INSERT INTO parent VALUES (1, 1), (2, 2), (3, 3), (4, NULL), (5, 5), (6, 6), (7, 7); 1169 INSERT INTO child VALUES ('1', 1), ('2', NULL), ('3', 3), ('4', 3), ('5', 5); 1170 INSERT INTO child_idx VALUES ('1', 1), ('2', NULL), ('3', 3), ('4', 3), ('5', 5); 1171 INSERT INTO child_unq VALUES ('1', 1), ('2', NULL), ('3', 3), ('4', NULL), ('5', 5); 1172 INSERT INTO child_non_unq VALUES ('1', 1), ('2', NULL), ('3', 3), ('4', 3), ('5', 5); 1173 `) 1174 // test index creation 1175 require.NoError(t, err) 1176 root, err = ExecuteSql(dEnv, root, "CREATE INDEX abc ON child (parent_value);") 1177 require.NoError(t, err) 1178 _, err = ExecuteSql(dEnv, root, "CREATE INDEX abc_idx ON child_idx (parent_value);") 1179 require.NoError(t, err) 1180 root, err = ExecuteSql(dEnv, root, "CREATE UNIQUE INDEX abc_unq ON child_unq (parent_value);") 1181 require.NoError(t, err) 1182 _, err = ExecuteSql(dEnv, root, "CREATE UNIQUE INDEX abc_non_unq ON child_non_unq (parent_value);") 1183 if assert.Error(t, err) { 1184 assert.Contains(t, err.Error(), "duplicate unique key given") 1185 } 1186 1187 // check foreign keys for updated index (or verify they weren't updated) 1188 fkc, err := root.GetForeignKeyCollection(ctx) 1189 require.NoError(t, err) 1190 fkChild, ok := fkc.GetByNameCaseInsensitive("fk_child") 1191 require.True(t, ok) 1192 require.Equal(t, "abc", fkChild.TableIndex) 1193 fkChildIdx, ok := fkc.GetByNameCaseInsensitive("fk_child_idx") 1194 require.True(t, ok) 1195 require.Equal(t, "abc_idx", fkChildIdx.TableIndex) 1196 fkChildUnq, ok := fkc.GetByNameCaseInsensitive("fk_child_unq") 1197 require.True(t, ok) 1198 require.Equal(t, "abc_unq", fkChildUnq.TableIndex) 1199 fkChildNonUnq, ok := fkc.GetByNameCaseInsensitive("fk_child_non_unq") 1200 require.True(t, ok) 1201 require.Equal(t, "parent_value", fkChildNonUnq.TableIndex) 1202 1203 // insert tests against index 1204 root, err = ExecuteSql(dEnv, root, "INSERT INTO child VALUES ('6', 5)") 1205 require.NoError(t, err) 1206 root, err = ExecuteSql(dEnv, root, "INSERT INTO child_idx VALUES ('6', 5)") 1207 require.NoError(t, err) 1208 _, err = ExecuteSql(dEnv, root, "INSERT INTO child_unq VALUES ('6', 5)") 1209 if assert.Error(t, err) { 1210 assert.True(t, sql.ErrUniqueKeyViolation.Is(err.(sql.WrappedInsertError).Cause)) 1211 } 1212 root, err = ExecuteSql(dEnv, root, "INSERT INTO child_non_unq VALUES ('6', 5)") 1213 require.NoError(t, err) 1214 1215 // insert tests against foreign key 1216 _, err = ExecuteSql(dEnv, root, "INSERT INTO child VALUES ('9', 9)") 1217 if assert.Error(t, err) { 1218 assert.Contains(t, err.Error(), "Foreign key violation") 1219 } 1220 _, err = ExecuteSql(dEnv, root, "INSERT INTO child_idx VALUES ('9', 9)") 1221 if assert.Error(t, err) { 1222 assert.Contains(t, err.Error(), "Foreign key violation") 1223 } 1224 _, err = ExecuteSql(dEnv, root, "INSERT INTO child_unq VALUES ('9', 9)") 1225 if assert.Error(t, err) { 1226 assert.Contains(t, err.Error(), "Foreign key violation") 1227 } 1228 _, err = ExecuteSql(dEnv, root, "INSERT INTO child_non_unq VALUES ('9', 9)") 1229 if assert.Error(t, err) { 1230 assert.Contains(t, err.Error(), "Foreign key violation") 1231 } 1232 } 1233 1234 func TestDropPrimaryKey(t *testing.T) { 1235 ctx := context.Background() 1236 dEnv := dtestutils.CreateTestEnv() 1237 defer dEnv.DoltDB.Close() 1238 root, err := dEnv.WorkingRoot(ctx) 1239 if err != nil { 1240 panic(err) 1241 } 1242 1243 t.Run("drop primary key", func(t *testing.T) { 1244 // setup 1245 root, err = ExecuteSql(dEnv, root, "create table parent (i int, j int, k int, index i (i), index ij (i, j), index ijk (i, j, k), index j (j), index kji (k, j, i));") 1246 require.NoError(t, err) 1247 root, err = ExecuteSql(dEnv, root, "create table child (x int, y int, constraint fk_child foreign key (x, y) references parent (i, j));") 1248 require.NoError(t, err) 1249 1250 // check foreign keys for updated index 1251 fkc, err := root.GetForeignKeyCollection(ctx) 1252 require.NoError(t, err) 1253 fkChild, ok := fkc.GetByNameCaseInsensitive("fk_child") 1254 require.True(t, ok) 1255 require.Equal(t, "xy", fkChild.TableIndex) 1256 require.Equal(t, "ij", fkChild.ReferencedTableIndex) 1257 1258 // add primary key 1259 root, err = ExecuteSql(dEnv, root, "alter table parent add primary key (i, j);") 1260 require.NoError(t, err) 1261 1262 // dropping secondary index ij, should choose ijk 1263 root, err = ExecuteSql(dEnv, root, "alter table parent drop index ij;") 1264 require.NoError(t, err) 1265 1266 // check foreign keys for updated index 1267 fkc, err = root.GetForeignKeyCollection(ctx) 1268 require.NoError(t, err) 1269 fkChild, ok = fkc.GetByNameCaseInsensitive("fk_child") 1270 require.True(t, ok) 1271 require.Equal(t, "xy", fkChild.TableIndex) 1272 require.Equal(t, "ijk", fkChild.ReferencedTableIndex) 1273 1274 // dropping secondary index ijk, should switch to primary key 1275 root, err = ExecuteSql(dEnv, root, "alter table parent drop index ijk;") 1276 require.NoError(t, err) 1277 1278 // check foreign keys for updated index 1279 fkc, err = root.GetForeignKeyCollection(ctx) 1280 require.NoError(t, err) 1281 fkChild, ok = fkc.GetByNameCaseInsensitive("fk_child") 1282 require.True(t, ok) 1283 require.Equal(t, "xy", fkChild.TableIndex) 1284 require.Equal(t, "", fkChild.ReferencedTableIndex) 1285 1286 // no viable secondary indexes left, should be unable to drop primary key 1287 _, err = ExecuteSql(dEnv, root, "alter table parent drop primary key;") 1288 require.Error(t, err) 1289 }) 1290 } 1291 1292 func TestDropIndex(t *testing.T) { 1293 ctx := context.Background() 1294 dEnv := dtestutils.CreateTestEnv() 1295 defer dEnv.DoltDB.Close() 1296 root, err := dEnv.WorkingRoot(ctx) 1297 if err != nil { 1298 panic(err) 1299 } 1300 t.Run("drop secondary indexes", func(t *testing.T) { 1301 // setup 1302 root, err = ExecuteSql(dEnv, root, "create table parent (i int);") 1303 require.NoError(t, err) 1304 root, err = ExecuteSql(dEnv, root, "alter table parent add index idx1 (i);") 1305 require.NoError(t, err) 1306 root, err = ExecuteSql(dEnv, root, "alter table parent add index idx2 (i);") 1307 require.NoError(t, err) 1308 root, err = ExecuteSql(dEnv, root, "alter table parent add index idx3 (i);") 1309 require.NoError(t, err) 1310 root, err = ExecuteSql(dEnv, root, "create table child (j int, constraint fk_child foreign key (j) references parent (i));") 1311 require.NoError(t, err) 1312 1313 // drop and check next index 1314 fkc, err := root.GetForeignKeyCollection(ctx) 1315 require.NoError(t, err) 1316 fkChild, ok := fkc.GetByNameCaseInsensitive("fk_child") 1317 require.True(t, ok) 1318 require.Equal(t, "j", fkChild.TableIndex) 1319 require.Equal(t, "idx1", fkChild.ReferencedTableIndex) 1320 1321 // dropping secondary index, should switch to existing index 1322 root, err = ExecuteSql(dEnv, root, "alter table parent drop index idx1;") 1323 require.NoError(t, err) 1324 fkc, err = root.GetForeignKeyCollection(ctx) 1325 require.NoError(t, err) 1326 fkChild, ok = fkc.GetByNameCaseInsensitive("fk_child") 1327 require.True(t, ok) 1328 require.Equal(t, "j", fkChild.TableIndex) 1329 require.Equal(t, "idx2", fkChild.ReferencedTableIndex) 1330 1331 // dropping secondary index, should switch to existing index 1332 root, err = ExecuteSql(dEnv, root, "alter table parent drop index idx2;") 1333 require.NoError(t, err) 1334 fkc, err = root.GetForeignKeyCollection(ctx) 1335 require.NoError(t, err) 1336 fkChild, ok = fkc.GetByNameCaseInsensitive("fk_child") 1337 require.True(t, ok) 1338 require.Equal(t, "j", fkChild.TableIndex) 1339 require.Equal(t, "idx3", fkChild.ReferencedTableIndex) 1340 1341 // dropping secondary index, should fail since there are no indexes to replace it 1342 _, err = ExecuteSql(dEnv, root, "alter table parent drop index idx3;") 1343 require.Error(t, err) 1344 }) 1345 } 1346 1347 func TestCreateIndexUnique(t *testing.T) { 1348 dEnv := dtestutils.CreateTestEnv() 1349 defer dEnv.DoltDB.Close() 1350 root, err := dEnv.WorkingRoot(context.Background()) 1351 if err != nil { 1352 panic(err) 1353 } 1354 root, err = ExecuteSql(dEnv, root, ` 1355 CREATE TABLE pass_unique ( 1356 pk1 BIGINT PRIMARY KEY, 1357 v1 BIGINT, 1358 v2 BIGINT 1359 ); 1360 CREATE TABLE fail_unique ( 1361 pk1 BIGINT PRIMARY KEY, 1362 v1 BIGINT, 1363 v2 BIGINT 1364 ); 1365 INSERT INTO pass_unique VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3); 1366 INSERT INTO fail_unique VALUES (1, 1, 1), (2, 2, 2), (3, 2, 3); 1367 `) 1368 require.NoError(t, err) 1369 root, err = ExecuteSql(dEnv, root, "CREATE UNIQUE INDEX idx_v1 ON pass_unique(v1)") 1370 assert.NoError(t, err) 1371 root, err = ExecuteSql(dEnv, root, "CREATE UNIQUE INDEX idx_v1 ON fail_unique(v1)") 1372 if assert.Error(t, err) { 1373 assert.Contains(t, strings.ToLower(err.Error()), "unique") 1374 } 1375 } 1376 1377 func assertFails(t *testing.T, dEnv *env.DoltEnv, query, expectedErr string) { 1378 ctx := context.Background() 1379 root, _ := dEnv.WorkingRoot(ctx) 1380 _, err := ExecuteSql(dEnv, root, query) 1381 require.Error(t, err, query) 1382 assert.Contains(t, err.Error(), expectedErr) 1383 } 1384 1385 func assertSucceeds(t *testing.T, dEnv *env.DoltEnv, query string) { 1386 ctx := context.Background() 1387 root, _ := dEnv.WorkingRoot(ctx) 1388 _, err := ExecuteSql(dEnv, root, query) 1389 assert.NoError(t, err, query) 1390 }