github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/sqle/sqlupdate_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 "testing" 20 21 "github.com/dolthub/go-mysql-server/sql" 22 "github.com/google/uuid" 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 26 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 27 "github.com/dolthub/dolt/go/libraries/doltcore/doltdocs" 28 "github.com/dolthub/dolt/go/libraries/doltcore/dtestutils" 29 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 30 . "github.com/dolthub/dolt/go/libraries/doltcore/sql/sqltestutil" 31 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dtables" 32 "github.com/dolthub/dolt/go/store/types" 33 ) 34 35 // Set to the name of a single test to run just that test, useful for debugging 36 const singleUpdateQueryTest = "" //"Natural join with join clause" 37 38 // Set to false to run tests known to be broken 39 const skipBrokenUpdate = true 40 41 // Structure for a test of an update query 42 type UpdateTest struct { 43 // The name of this test. Names should be unique and descriptive. 44 Name string 45 // The update query to run 46 UpdateQuery string 47 // The select query to run to verify the results 48 SelectQuery string 49 // The schema of the result of the query, nil if an error is expected 50 ExpectedSchema schema.Schema 51 // The rows this query should return, nil if an error is expected 52 ExpectedRows []sql.Row 53 // An expected error string 54 ExpectedErr string 55 // Setup logic to run before executing this test, after initial tables have been created and populated 56 AdditionalSetup SetupFn 57 } 58 59 // BasicUpdateTests cover basic update statement features and error handling 60 var BasicUpdateTests = []UpdateTest{ 61 { 62 Name: "update one row, one col, primary key where clause", 63 UpdateQuery: `update people set first_name = "Domer" where id = 0`, 64 SelectQuery: `select * from people where id = 0`, 65 ExpectedRows: ToSqlRows(PeopleTestSchema, MutateRow(PeopleTestSchema, Homer, FirstNameTag, "Domer")), 66 ExpectedSchema: CompressSchema(PeopleTestSchema), 67 }, 68 { 69 Name: "update one row, one col, non-primary key where clause", 70 UpdateQuery: `update people set first_name = "Domer" where first_name = "Homer"`, 71 SelectQuery: `select * from people where first_name = "Domer"`, 72 ExpectedRows: ToSqlRows(PeopleTestSchema, MutateRow(PeopleTestSchema, Homer, FirstNameTag, "Domer")), 73 ExpectedSchema: CompressSchema(PeopleTestSchema), 74 }, 75 { 76 Name: "update one row, two cols, primary key where clause", 77 UpdateQuery: `update people set first_name = "Ned", last_name = "Flanders" where id = 0`, 78 SelectQuery: `select * from people where id = 0`, 79 ExpectedRows: ToSqlRows(PeopleTestSchema, MutateRow(PeopleTestSchema, Homer, FirstNameTag, "Ned", LastNameTag, "Flanders")), 80 ExpectedSchema: CompressSchema(PeopleTestSchema), 81 }, 82 { 83 Name: "update one row, all cols, non-primary key where clause", 84 UpdateQuery: `update people set first_name = "Ned", last_name = "Flanders", is_married = false, rating = 10, 85 age = 45, num_episodes = 150, uuid = '00000000-0000-0000-0000-000000000050' 86 where age = 38`, 87 SelectQuery: `select * from people where uuid = '00000000-0000-0000-0000-000000000050'`, 88 ExpectedRows: ToSqlRows(PeopleTestSchema, 89 MutateRow(PeopleTestSchema, Marge, FirstNameTag, "Ned", LastNameTag, "Flanders", IsMarriedTag, false, 90 RatingTag, 10.0, AgeTag, 45, NumEpisodesTag, uint64(150), 91 UuidTag, uuid.MustParse("00000000-0000-0000-0000-000000000050"))), 92 ExpectedSchema: CompressSchema(PeopleTestSchema), 93 }, 94 { 95 Name: "update one row, set columns to existing values", 96 UpdateQuery: `update people set first_name = "Homer", last_name = "Simpson", is_married = true, rating = 8.5, age = 40, 97 num_episodes = null, uuid = null 98 where id = 0`, 99 SelectQuery: `select * from people where id = 0`, 100 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer), 101 ExpectedSchema: CompressSchema(PeopleTestSchema), 102 }, 103 { 104 Name: "update one row, null out existing values", 105 UpdateQuery: `update people set first_name = "Homer", last_name = "Simpson", is_married = null, rating = null, age = null, 106 num_episodes = null, uuid = null 107 where first_name = "Homer"`, 108 SelectQuery: `select * from people where first_name = "Homer"`, 109 ExpectedRows: ToSqlRows(PeopleTestSchema, MutateRow(PeopleTestSchema, Homer, IsMarriedTag, nil, RatingTag, nil, AgeTag, nil)), 110 ExpectedSchema: CompressSchema(PeopleTestSchema), 111 }, 112 { 113 Name: "update multiple rows, set two columns", 114 UpdateQuery: `update people set first_name = "Changed", rating = 0.0 115 where last_name = "Simpson"`, 116 SelectQuery: `select * from people where last_name = "Simpson"`, 117 ExpectedRows: ToSqlRows(PeopleTestSchema, 118 MutateRow(PeopleTestSchema, Homer, FirstNameTag, "Changed", RatingTag, 0.0), 119 MutateRow(PeopleTestSchema, Marge, FirstNameTag, "Changed", RatingTag, 0.0), 120 MutateRow(PeopleTestSchema, Bart, FirstNameTag, "Changed", RatingTag, 0.0), 121 MutateRow(PeopleTestSchema, Lisa, FirstNameTag, "Changed", RatingTag, 0.0), 122 ), 123 ExpectedSchema: CompressSchema(PeopleTestSchema), 124 }, 125 { 126 Name: "update no matching rows", 127 UpdateQuery: `update people set first_name = "Changed", rating = 0.0 where last_name = "Flanders"`, 128 SelectQuery: `select * from people`, 129 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Marge, Bart, Lisa, Moe, Barney), 130 ExpectedSchema: CompressSchema(PeopleTestSchema), 131 }, 132 { 133 Name: "update without where clause", 134 UpdateQuery: `update people set first_name = "Changed", rating = 0.0`, 135 SelectQuery: `select * from people`, 136 ExpectedRows: ToSqlRows(PeopleTestSchema, 137 MutateRow(PeopleTestSchema, Homer, FirstNameTag, "Changed", RatingTag, 0.0), 138 MutateRow(PeopleTestSchema, Marge, FirstNameTag, "Changed", RatingTag, 0.0), 139 MutateRow(PeopleTestSchema, Bart, FirstNameTag, "Changed", RatingTag, 0.0), 140 MutateRow(PeopleTestSchema, Lisa, FirstNameTag, "Changed", RatingTag, 0.0), 141 MutateRow(PeopleTestSchema, Moe, FirstNameTag, "Changed", RatingTag, 0.0), 142 MutateRow(PeopleTestSchema, Barney, FirstNameTag, "Changed", RatingTag, 0.0), 143 ), 144 ExpectedSchema: CompressSchema(PeopleTestSchema), 145 }, 146 { 147 Name: "update set first_name = last_name", 148 UpdateQuery: `update people set first_name = last_name`, 149 SelectQuery: `select * from people`, 150 ExpectedRows: ToSqlRows(PeopleTestSchema, 151 MutateRow(PeopleTestSchema, Homer, FirstNameTag, "Simpson"), 152 MutateRow(PeopleTestSchema, Marge, FirstNameTag, "Simpson"), 153 MutateRow(PeopleTestSchema, Bart, FirstNameTag, "Simpson"), 154 MutateRow(PeopleTestSchema, Lisa, FirstNameTag, "Simpson"), 155 MutateRow(PeopleTestSchema, Moe, FirstNameTag, "Szyslak"), 156 MutateRow(PeopleTestSchema, Barney, FirstNameTag, "Gumble"), 157 ), 158 ExpectedSchema: CompressSchema(PeopleTestSchema), 159 }, 160 { 161 Name: "update increment age", 162 UpdateQuery: `update people set age = age + 1`, 163 SelectQuery: `select * from people`, 164 ExpectedRows: ToSqlRows(PeopleTestSchema, 165 MutateRow(PeopleTestSchema, Homer, AgeTag, 41), 166 MutateRow(PeopleTestSchema, Marge, AgeTag, 39), 167 MutateRow(PeopleTestSchema, Bart, AgeTag, 11), 168 MutateRow(PeopleTestSchema, Lisa, AgeTag, 9), 169 MutateRow(PeopleTestSchema, Moe, AgeTag, 49), 170 MutateRow(PeopleTestSchema, Barney, AgeTag, 41), 171 ), 172 ExpectedSchema: CompressSchema(PeopleTestSchema), 173 }, 174 { 175 Name: "update reverse rating", 176 UpdateQuery: `update people set rating = -rating`, 177 SelectQuery: `select * from people`, 178 ExpectedRows: ToSqlRows(PeopleTestSchema, 179 MutateRow(PeopleTestSchema, Homer, RatingTag, -8.5), 180 MutateRow(PeopleTestSchema, Marge, RatingTag, -8.0), 181 MutateRow(PeopleTestSchema, Bart, RatingTag, -9.0), 182 MutateRow(PeopleTestSchema, Lisa, RatingTag, -10.0), 183 MutateRow(PeopleTestSchema, Moe, RatingTag, -6.5), 184 MutateRow(PeopleTestSchema, Barney, RatingTag, -4.0), 185 ), 186 ExpectedSchema: CompressSchema(PeopleTestSchema), 187 }, 188 { 189 Name: "update datetime field", 190 UpdateQuery: `update episodes set air_date = "1993-03-24 20:00:00" where id = 1`, 191 SelectQuery: `select * from episodes where id = 1`, 192 ExpectedRows: ToSqlRows(EpisodesTestSchema, 193 MutateRow(EpisodesTestSchema, Ep1, EpAirDateTag, DatetimeStrToTimestamp("1993-03-24 20:00:00")), 194 ), 195 ExpectedSchema: CompressSchema(EpisodesTestSchema), 196 }, 197 { 198 Name: "update datetime field", 199 UpdateQuery: `update episodes set name = "fake_name" where id = 1;`, 200 SelectQuery: `select * from episodes where id = 1;`, 201 ExpectedRows: ToSqlRows(EpisodesTestSchema, 202 MutateRow(EpisodesTestSchema, Ep1, EpNameTag, "fake_name"), 203 ), 204 ExpectedSchema: CompressSchema(EpisodesTestSchema), 205 }, 206 { 207 Name: "update multiple rows, =", 208 UpdateQuery: `update people set first_name = "Homer" where last_name = "Simpson"`, 209 SelectQuery: `select * from people where last_name = "Simpson"`, 210 ExpectedRows: ToSqlRows(PeopleTestSchema, 211 Homer, 212 MutateRow(PeopleTestSchema, Marge, FirstNameTag, "Homer"), 213 MutateRow(PeopleTestSchema, Bart, FirstNameTag, "Homer"), 214 MutateRow(PeopleTestSchema, Lisa, FirstNameTag, "Homer"), 215 ), 216 ExpectedSchema: CompressSchema(PeopleTestSchema), 217 }, 218 { 219 Name: "update multiple rows, <>", 220 UpdateQuery: `update people set last_name = "Simpson" where last_name <> "Simpson"`, 221 SelectQuery: `select * from people`, 222 ExpectedRows: ToSqlRows(PeopleTestSchema, 223 Homer, 224 Marge, 225 Bart, 226 Lisa, 227 MutateRow(PeopleTestSchema, Moe, LastNameTag, "Simpson"), 228 MutateRow(PeopleTestSchema, Barney, LastNameTag, "Simpson"), 229 ), 230 ExpectedSchema: CompressSchema(PeopleTestSchema), 231 }, 232 { 233 Name: "update multiple rows, >", 234 UpdateQuery: `update people set first_name = "Homer" where age > 10`, 235 SelectQuery: `select * from people where age > 10`, 236 ExpectedRows: ToSqlRows(PeopleTestSchema, 237 Homer, 238 MutateRow(PeopleTestSchema, Marge, FirstNameTag, "Homer"), 239 MutateRow(PeopleTestSchema, Moe, FirstNameTag, "Homer"), 240 MutateRow(PeopleTestSchema, Barney, FirstNameTag, "Homer"), 241 ), 242 ExpectedSchema: CompressSchema(PeopleTestSchema), 243 }, 244 { 245 Name: "update multiple rows, >=", 246 UpdateQuery: `update people set first_name = "Homer" where age >= 10`, 247 SelectQuery: `select * from people where age >= 10`, 248 ExpectedRows: ToSqlRows(PeopleTestSchema, 249 Homer, 250 MutateRow(PeopleTestSchema, Marge, FirstNameTag, "Homer"), 251 MutateRow(PeopleTestSchema, Bart, FirstNameTag, "Homer"), 252 MutateRow(PeopleTestSchema, Moe, FirstNameTag, "Homer"), 253 MutateRow(PeopleTestSchema, Barney, FirstNameTag, "Homer"), 254 ), 255 ExpectedSchema: CompressSchema(PeopleTestSchema), 256 }, 257 { 258 Name: "update multiple rows, <", 259 UpdateQuery: `update people set first_name = "Bart" where age < 40`, 260 SelectQuery: `select * from people where age < 40`, 261 ExpectedRows: ToSqlRows(PeopleTestSchema, 262 MutateRow(PeopleTestSchema, Marge, FirstNameTag, "Bart"), 263 Bart, 264 MutateRow(PeopleTestSchema, Lisa, FirstNameTag, "Bart"), 265 ), 266 ExpectedSchema: CompressSchema(PeopleTestSchema), 267 }, 268 { 269 Name: "update multiple rows, <=", 270 UpdateQuery: `update people set first_name = "Homer" where age <= 40`, 271 SelectQuery: `select * from people where age <= 40`, 272 ExpectedRows: ToSqlRows(PeopleTestSchema, 273 Homer, 274 MutateRow(PeopleTestSchema, Marge, FirstNameTag, "Homer"), 275 MutateRow(PeopleTestSchema, Bart, FirstNameTag, "Homer"), 276 MutateRow(PeopleTestSchema, Lisa, FirstNameTag, "Homer"), 277 MutateRow(PeopleTestSchema, Barney, FirstNameTag, "Homer"), 278 ), 279 ExpectedSchema: CompressSchema(PeopleTestSchema), 280 }, 281 { 282 Name: "update multiple rows pk increment order by desc", 283 UpdateQuery: `update people set id = id + 1 order by id desc`, 284 SelectQuery: `select * from people`, 285 ExpectedRows: ToSqlRows(PeopleTestSchema, 286 MutateRow(PeopleTestSchema, Homer, IdTag, HomerId+1), 287 MutateRow(PeopleTestSchema, Marge, IdTag, MargeId+1), 288 MutateRow(PeopleTestSchema, Bart, IdTag, BartId+1), 289 MutateRow(PeopleTestSchema, Lisa, IdTag, LisaId+1), 290 MutateRow(PeopleTestSchema, Moe, IdTag, MoeId+1), 291 MutateRow(PeopleTestSchema, Barney, IdTag, BarneyId+1), 292 ), 293 ExpectedSchema: CompressSchema(PeopleTestSchema), 294 }, 295 { 296 Name: "update multiple rows pk increment order by desc", 297 UpdateQuery: `update people set id = id + 1 order by id desc`, 298 SelectQuery: `select * from people order by id`, 299 ExpectedRows: ToSqlRows(PeopleTestSchema, 300 MutateRow(PeopleTestSchema, Homer, IdTag, HomerId+1), 301 MutateRow(PeopleTestSchema, Marge, IdTag, MargeId+1), 302 MutateRow(PeopleTestSchema, Bart, IdTag, BartId+1), 303 MutateRow(PeopleTestSchema, Lisa, IdTag, LisaId+1), 304 MutateRow(PeopleTestSchema, Moe, IdTag, MoeId+1), 305 MutateRow(PeopleTestSchema, Barney, IdTag, BarneyId+1), 306 ), 307 ExpectedSchema: CompressSchema(PeopleTestSchema), 308 }, 309 { 310 Name: "update multiple rows pk increment order by asc", 311 UpdateQuery: `update people set id = id + 1 order by id asc`, 312 ExpectedErr: "duplicate primary key", 313 }, 314 { 315 Name: "update primary key col", 316 UpdateQuery: `update people set id = 0 where first_name = "Marge"`, 317 ExpectedErr: "duplicate primary key", 318 }, 319 { 320 Name: "null constraint failure", 321 UpdateQuery: `update people set first_name = null where id = 0`, 322 ExpectedErr: "Constraint failed for column 'first_name': Not null", 323 }, 324 { 325 Name: "type mismatch list -> string", 326 UpdateQuery: `update people set first_name = ("one", "two") where id = 0`, 327 ExpectedErr: "Type mismatch", 328 }, 329 { 330 Name: "type mismatch int -> uuid", 331 UpdateQuery: `update people set uuid = 0 where id = 0`, 332 ExpectedErr: "Type mismatch", 333 }, 334 { 335 Name: "type mismatch string -> int", 336 UpdateQuery: `update people set age = "pretty old" where id = 0`, 337 ExpectedErr: "Type mismatch", 338 }, 339 { 340 Name: "type mismatch string -> float", 341 UpdateQuery: `update people set rating = "great" where id = 0`, 342 ExpectedErr: "Type mismatch", 343 }, 344 { 345 Name: "type mismatch string -> uint", 346 UpdateQuery: `update people set num_episodes = "all of them" where id = 0`, 347 ExpectedErr: "Type mismatch", 348 }, 349 { 350 Name: "type mismatch string -> uuid", 351 UpdateQuery: `update people set uuid = "not a uuid string" where id = 0`, 352 ExpectedErr: "Type mismatch", 353 }, 354 { 355 Name: "type mismatch bool -> uuid", 356 UpdateQuery: `update people set uuid = false where id = 0`, 357 ExpectedErr: "Type mismatch", 358 }, 359 } 360 361 func TestExecuteUpdate(t *testing.T) { 362 for _, test := range BasicUpdateTests { 363 t.Run(test.Name, func(t *testing.T) { 364 testUpdateQuery(t, test) 365 }) 366 } 367 } 368 369 func TestExecuteUpdateSystemTables(t *testing.T) { 370 for _, test := range systemTableUpdateTests { 371 t.Run(test.Name, func(t *testing.T) { 372 testUpdateQuery(t, test) 373 }) 374 } 375 } 376 377 var systemTableUpdateTests = []UpdateTest{ 378 { 379 Name: "update dolt_docs", 380 AdditionalSetup: CreateTableFn("dolt_docs", 381 doltdocs.Schema, 382 NewRow(types.String("LICENSE.md"), types.String("A license"))), 383 UpdateQuery: "update dolt_docs set doc_text = 'Some text')", 384 ExpectedErr: "cannot insert into table", 385 }, 386 { 387 Name: "update dolt_query_catalog", 388 AdditionalSetup: CreateTableFn(doltdb.DoltQueryCatalogTableName, 389 dtables.DoltQueryCatalogSchema, 390 NewRowWithSchema(dtables.DoltQueryCatalogSchema, 391 types.String("abc123"), 392 types.Uint(1), 393 types.String("example"), 394 types.String("select 2+2 from dual"), 395 types.String("description"), 396 )), 397 UpdateQuery: "update dolt_query_catalog set display_order = display_order + 1", 398 SelectQuery: "select * from dolt_query_catalog", 399 ExpectedRows: ToSqlRows(CompressSchema(dtables.DoltQueryCatalogSchema), 400 NewRow(types.String("abc123"), types.Uint(2), types.String("example"), types.String("select 2+2 from dual"), types.String("description"))), 401 ExpectedSchema: CompressSchema(dtables.DoltQueryCatalogSchema), 402 }, 403 { 404 Name: "update dolt_schemas", 405 AdditionalSetup: CreateTableFn(doltdb.SchemasTableName, 406 schemasTableDoltSchema(), 407 NewRowWithSchema(schemasTableDoltSchema(), 408 types.String("view"), 409 types.String("name"), 410 types.String("select 2+2 from dual"), 411 types.Int(1), 412 )), 413 UpdateQuery: "update dolt_schemas set type = 'not a view'", 414 SelectQuery: "select * from dolt_schemas", 415 ExpectedRows: ToSqlRows(CompressSchema(schemasTableDoltSchema()), 416 NewRow(types.String("not a view"), types.String("name"), types.String("select 2+2 from dual"), types.Int(1)), 417 ), 418 ExpectedSchema: CompressSchema(schemasTableDoltSchema()), 419 }, 420 } 421 422 // Tests the given query on a freshly created dataset, asserting that the result has the given schema and rows. If 423 // expectedErr is set, asserts instead that the execution returns an error that matches. 424 func testUpdateQuery(t *testing.T, test UpdateTest) { 425 if (test.ExpectedRows == nil) != (test.ExpectedSchema == nil) { 426 require.Fail(t, "Incorrect test setup: schema and rows must both be provided if one is") 427 } 428 429 if len(singleUpdateQueryTest) > 0 && test.Name != singleUpdateQueryTest { 430 t.Skip("Skipping tests until " + singleUpdateQueryTest) 431 } 432 433 dEnv := dtestutils.CreateTestEnv() 434 CreateTestDatabase(dEnv, t) 435 436 if test.AdditionalSetup != nil { 437 test.AdditionalSetup(t, dEnv) 438 } 439 440 var err error 441 root, _ := dEnv.WorkingRoot(context.Background()) 442 root, err = executeModify(context.Background(), dEnv, root, test.UpdateQuery) 443 if len(test.ExpectedErr) > 0 { 444 require.Error(t, err) 445 return 446 } else { 447 require.NoError(t, err) 448 } 449 450 actualRows, sch, err := executeSelect(context.Background(), dEnv, root, test.SelectQuery) 451 require.NoError(t, err) 452 453 assert.Equal(t, test.ExpectedRows, actualRows) 454 sqlSchema := mustSqlSchema(test.ExpectedSchema) 455 assertSchemasEqual(t, sqlSchema, sch) 456 }