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