github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/row/fk_test.go (about) 1 // Copyright 2017 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package row 12 13 import ( 14 "bytes" 15 "context" 16 "fmt" 17 "reflect" 18 "sort" 19 "testing" 20 21 "github.com/cockroachdb/cockroach/pkg/base" 22 "github.com/cockroachdb/cockroach/pkg/sql/catalog" 23 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 24 "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" 25 "github.com/cockroachdb/cockroach/pkg/util/log" 26 "github.com/cockroachdb/errors" 27 "github.com/kr/pretty" 28 ) 29 30 type testTables struct { 31 nextID TableID 32 tablesByID map[TableID]*sqlbase.ImmutableTableDescriptor 33 tablesByName map[string]*sqlbase.ImmutableTableDescriptor 34 } 35 36 func (t *testTables) createTestTable(name string) TableID { 37 table := sqlbase.NewImmutableTableDescriptor(sqlbase.TableDescriptor{ 38 Name: name, 39 ID: t.nextID, 40 NextIndexID: sqlbase.IndexID(1), // This must be 1 to avoid clashing with a primary index. 41 }) 42 t.tablesByID[table.ID] = table 43 t.tablesByName[table.Name] = table 44 t.nextID++ 45 return table.ID 46 } 47 48 func (t *testTables) createForeignKeyReference( 49 referencingID TableID, 50 referencedID TableID, 51 onDelete sqlbase.ForeignKeyReference_Action, 52 onUpdate sqlbase.ForeignKeyReference_Action, 53 ) error { 54 // Get the tables 55 referencing, exists := t.tablesByID[referencingID] 56 if !exists { 57 return errors.Errorf("Can't find table with ID:%d", referencingID) 58 } 59 referenced, exists := t.tablesByID[referencedID] 60 if !exists { 61 return errors.Errorf("Can't find table with ID:%d", referencedID) 62 } 63 fk := sqlbase.ForeignKeyConstraint{ 64 ReferencedTableID: referencedID, 65 OriginTableID: referencingID, 66 OnDelete: onDelete, 67 OnUpdate: onUpdate, 68 } 69 referencing.OutboundFKs = append(referencing.OutboundFKs, fk) 70 referenced.InboundFKs = append(referenced.InboundFKs, fk) 71 return nil 72 } 73 74 // TestMakeFkMetadata creates an artificial set of tables to test the graph 75 // walking algorithm used in the function. 76 func TestMakeFkMetadata(t *testing.T) { 77 tables := testTables{ 78 nextID: TableID(1), 79 tablesByID: make(map[TableID]*sqlbase.ImmutableTableDescriptor), 80 tablesByName: make(map[string]*sqlbase.ImmutableTableDescriptor), 81 } 82 83 // First setup the table we will be testing against. 84 xID := tables.createTestTable("X") 85 86 expectedInsertIDs := []TableID{xID} 87 expectedUpdateIDs := []TableID{xID} 88 expectedDeleteIDs := []TableID{xID} 89 90 // For all possible combinations of relationships for foreign keys, create a 91 // table that X references, and one that references X. 92 for deleteNum, deleteName := range sqlbase.ForeignKeyReference_Action_name { 93 for updateNum, updateName := range sqlbase.ForeignKeyReference_Action_name { 94 subName := fmt.Sprintf("OnDelete%s OnUpdate%s", deleteName, updateName) 95 referencedByX := tables.createTestTable(fmt.Sprintf("X Referenced - %s", subName)) 96 if err := tables.createForeignKeyReference( 97 xID, referencedByX, sqlbase.ForeignKeyReference_Action(deleteNum), sqlbase.ForeignKeyReference_Action(updateNum), 98 ); err != nil { 99 t.Fatalf("could not add index: %s", err) 100 } 101 102 referencingX := tables.createTestTable(fmt.Sprintf("Referencing X - %s", subName)) 103 if err := tables.createForeignKeyReference( 104 referencingX, xID, sqlbase.ForeignKeyReference_Action(deleteNum), sqlbase.ForeignKeyReference_Action(updateNum), 105 ); err != nil { 106 t.Fatalf("could not add index: %s", err) 107 } 108 109 expectedInsertIDs = append(expectedInsertIDs, referencedByX) 110 expectedUpdateIDs = append(expectedUpdateIDs, referencedByX) 111 expectedUpdateIDs = append(expectedUpdateIDs, referencingX) 112 expectedDeleteIDs = append(expectedDeleteIDs, referencingX) 113 114 // To go even further, create another set of tables for all possible 115 // foreign key relationships that reference the table that is referencing 116 // X. This will ensure that we bound the tree walking algorithm correctly. 117 for deleteNum2, deleteName2 := range sqlbase.ForeignKeyReference_Action_name { 118 for updateNum2, updateName2 := range sqlbase.ForeignKeyReference_Action_name { 119 //if deleteNum2 != int32(ForeignKeyReference_CASCADE) || updateNum2 != int32(ForeignKeyReference_CASCADE) { 120 // continue 121 //} 122 subName2 := fmt.Sprintf("Referencing %d - OnDelete%s OnUpdated%s", referencingX, deleteName2, updateName2) 123 referencing2 := tables.createTestTable(subName2) 124 if err := tables.createForeignKeyReference( 125 referencing2, referencingX, sqlbase.ForeignKeyReference_Action(deleteNum2), sqlbase.ForeignKeyReference_Action(updateNum2), 126 ); err != nil { 127 t.Fatalf("could not add index: %s", err) 128 } 129 130 // Only fetch the next level of tables if a cascade can occur through 131 // the first level. 132 if deleteNum == int32(sqlbase.ForeignKeyReference_CASCADE) || 133 deleteNum == int32(sqlbase.ForeignKeyReference_SET_DEFAULT) || 134 deleteNum == int32(sqlbase.ForeignKeyReference_SET_NULL) { 135 expectedDeleteIDs = append(expectedDeleteIDs, referencing2) 136 } 137 if updateNum == int32(sqlbase.ForeignKeyReference_CASCADE) || 138 updateNum == int32(sqlbase.ForeignKeyReference_SET_DEFAULT) || 139 updateNum == int32(sqlbase.ForeignKeyReference_SET_NULL) { 140 expectedUpdateIDs = append(expectedUpdateIDs, referencing2) 141 } 142 } 143 } 144 } 145 } 146 147 sort.Slice(expectedInsertIDs, func(i, j int) bool { return expectedInsertIDs[i] < expectedInsertIDs[j] }) 148 sort.Slice(expectedUpdateIDs, func(i, j int) bool { return expectedUpdateIDs[i] < expectedUpdateIDs[j] }) 149 sort.Slice(expectedDeleteIDs, func(i, j int) bool { return expectedDeleteIDs[i] < expectedDeleteIDs[j] }) 150 151 xDesc, exists := tables.tablesByID[xID] 152 if !exists { 153 t.Fatalf("Could not find table:%d", xID) 154 } 155 156 lookup := func(ctx context.Context, tableID TableID) (catalog.TableEntry, error) { 157 table, exists := tables.tablesByID[tableID] 158 if !exists { 159 return catalog.TableEntry{}, errors.Errorf("Could not lookup table:%d", tableID) 160 } 161 return catalog.TableEntry{Desc: table}, nil 162 } 163 164 test := func(t *testing.T, usage FKCheckType, expectedIDs []TableID) { 165 tableLookups, err := MakeFkMetadata( 166 context.Background(), 167 xDesc, 168 usage, 169 lookup, 170 NoCheckPrivilege, 171 nil, /* analyzeExpr */ 172 nil, /* checkHelper */ 173 ) 174 if err != nil { 175 t.Fatal(err) 176 } 177 var actualIDs []TableID 178 for id := range tableLookups { 179 actualIDs = append(actualIDs, id) 180 } 181 sort.Slice(actualIDs, func(i, j int) bool { return actualIDs[i] < actualIDs[j] }) 182 if a, e := actualIDs, expectedIDs; !reflect.DeepEqual(a, e) { 183 t.Errorf("insert's expected table IDs did not match actual IDs diff:\n %v %v %v", pretty.Diff(e, a), e, a) 184 } 185 } 186 187 t.Run("Inserts", func(t *testing.T) { 188 test(t, CheckInserts, expectedInsertIDs) 189 }) 190 t.Run("Updates", func(t *testing.T) { 191 test(t, CheckUpdates, expectedUpdateIDs) 192 }) 193 t.Run("Deletes", func(t *testing.T) { 194 test(t, CheckDeletes, expectedDeleteIDs) 195 }) 196 } 197 198 // BenchmarkMultiRowFKCheckTypes performs several benchmarks that pertain to operations involving foreign keys and cascades. 199 func BenchmarkMultiRowFKCheckTypes(b *testing.B) { 200 if testing.Short() { 201 b.Skip("short flag") 202 } 203 defer log.Scope(b).Close(b) 204 205 // Throughout the course of testing there are four tables that are set up at the beginning of each sub-benchmark and 206 // torn down at the end of each sub-benchmark. 207 // `childFK` has a foreign key that references `parentFK`. 208 fkTables := map[string]string{ 209 `parentFK`: ` 210 CREATE TABLE IF NOT EXISTS parentFK( 211 foo INT PRIMARY KEY, 212 bar INT 213 )`, 214 `childFK`: ` 215 CREATE TABLE IF NOT EXISTS childFK( 216 baz INT, 217 foo INT, 218 FOREIGN KEY(foo) REFERENCES parentFK(foo) ON UPDATE CASCADE ON DELETE CASCADE 219 ) 220 `, 221 // `parentNoFK` and `childNoFK` are the same as `parentFK` and `childFK` but `childNoFK` has no foreign key reference 222 // to `parentNoFK` 223 `parentNoFK`: ` 224 CREATE TABLE IF NOT EXISTS parentNoFK( 225 foo INT PRIMARY KEY, 226 bar INT 227 ) 228 `, 229 `childNoFK`: ` 230 CREATE TABLE IF NOT EXISTS childNoFK( 231 baz INT, 232 foo INT 233 )`, 234 `parentInterleaved`: ` 235 CREATE TABLE IF NOT EXISTS parentInterleaved( 236 foo INT PRIMARY KEY, 237 bar int 238 )`, 239 `childInterleaved`: ` 240 CREATE TABLE IF NOT EXISTS childInterleaved( 241 baz INT, 242 foo INT, 243 PRIMARY KEY(foo, baz), 244 FOREIGN KEY(foo) REFERENCES parentInterleaved(foo) ON UPDATE CASCADE ON DELETE CASCADE 245 ) INTERLEAVE IN PARENT parentInterleaved (foo)`, 246 `siblingInterleaved`: ` 247 CREATE TABLE IF NOT EXISTS siblingInterleaved( 248 baz INT, 249 foo INT, 250 PRIMARY KEY(foo, baz), 251 FOREIGN KEY(foo) REFERENCES parentInterleaved(foo) ON UPDATE CASCADE ON DELETE CASCADE 252 ) INTERLEAVE IN PARENT parentInterleaved (foo)`, 253 `grandchildInterleaved`: ` 254 CREATE TABLE IF NOT EXISTS grandchildInterleaved( 255 bar INT, 256 foo INT, 257 baz INT, 258 PRIMARY KEY(foo, baz, bar), 259 FOREIGN KEY(foo, baz) REFERENCES childInterleaved(foo, baz) ON UPDATE CASCADE ON DELETE CASCADE 260 ) INTERLEAVE IN PARENT childInterleaved (foo, baz)`, 261 // `self_referential` has a foreign key reference to itself (parent-child relationship) with 262 // cascading updates and deletes 263 // `self_referential_noFK` has the same schema 264 // `self_referential_setnull` has an identical schema to `self_referential` except that instead of cascading 265 // on delete, it sets the reference field to null. 266 `self_referential`: ` 267 CREATE TABLE IF NOT EXISTS self_referential( 268 id INT PRIMARY KEY, 269 pid INT, 270 FOREIGN KEY(pid) REFERENCES self_referential(id) ON UPDATE CASCADE ON DELETE CASCADE 271 )`, 272 `self_referential_noFK`: ` 273 CREATE TABLE IF NOT EXISTS self_referential_noFK( 274 id INT PRIMARY KEY, 275 pid INT 276 )`, 277 `self_referential_setnull`: ` 278 CREATE TABLE IF NOT EXISTS self_referential_setnull( 279 id INT PRIMARY KEY, 280 pid INT, 281 FOREIGN KEY(pid) REFERENCES self_referential_setnull(id) ON UPDATE CASCADE ON DELETE SET NULL 282 )`, 283 } 284 _, db, _ := serverutils.StartServer(b, base.TestServerArgs{}) 285 286 // This function tears down all the tables and is meant to be called at the beginning and end of each sub-benchmark. 287 drop := func() { 288 // dropping has to be done in reverse so no drop causes a foreign key violation 289 for tableName := range fkTables { 290 if _, err := db.Exec(fmt.Sprintf(`DROP TABLE IF EXISTS %s CASCADE`, tableName)); err != nil { 291 b.Fatal(err) 292 } 293 } 294 } 295 296 // This function is to be called at the beginning of each sub-benchmark to set up the necessary tables. 297 setup := func(tablesNeeded []string) { 298 drop() 299 for _, t := range tablesNeeded { 300 if _, ok := fkTables[t]; !ok { 301 b.Fatal(errors.New("invalid table name for setup")) 302 } 303 if _, err := db.Exec(fkTables[t]); err != nil { 304 b.Fatal(err) 305 } 306 } 307 } 308 309 // The following sub-benchmarks are for the parentFK/childFK and parentNoFK/childNoFK tables. 310 // The insertRows and deleteRows sub-benchmarks of each kind measures insert performance and delete performance (respectively) 311 // of the following cases: 312 // * {insert,delete}Rows_IdenticalFK: All rows in child reference the same row in parent 313 // * {insert,delete}Rows_NoFK: Uses parentNoFK/childNoFK tables, no foreign key refs 314 // * {insert,delete}Rows_UniqueFKs: All rows in child reference a distinct row in parent 315 const numFKRows = 10000 316 b.Run("insertRows_IdenticalFK", func(b *testing.B) { 317 setup([]string{`parentFK`, `childFK`}) 318 if _, err := db.Exec(`INSERT INTO parentFK(foo) VALUES(1)`); err != nil { 319 b.Fatal(err) 320 } 321 defer drop() 322 b.ResetTimer() 323 var run bytes.Buffer 324 325 run.WriteString(`INSERT INTO childFK(baz, foo) VALUES `) 326 327 for i := 1; i <= numFKRows; i++ { 328 run.WriteString(fmt.Sprintf("(%d, 1)", i)) 329 if i != numFKRows { 330 run.WriteString(", ") 331 } 332 } 333 334 statement := run.String() 335 if _, err := db.Exec(statement); err != nil { 336 b.Fatal(err) 337 } 338 b.StopTimer() 339 }) 340 b.Run("deleteRows_IdenticalFK", func(b *testing.B) { 341 setup([]string{`parentFK`, `childFK`}) 342 if _, err := db.Exec(`INSERT INTO parentFK(foo) VALUES(1)`); err != nil { 343 b.Fatal(err) 344 } 345 defer drop() 346 var run bytes.Buffer 347 348 run.WriteString(`INSERT INTO childFK(baz, foo) VALUES `) 349 350 for i := 1; i <= numFKRows; i++ { 351 run.WriteString(fmt.Sprintf("(%d, 1)", i)) 352 if i != numFKRows { 353 run.WriteString(", ") 354 } 355 } 356 357 statement := run.String() 358 if _, err := db.Exec(statement); err != nil { 359 b.Fatal(err) 360 } 361 b.ResetTimer() 362 if _, err := db.Exec(`DELETE from childFK`); err != nil { 363 b.Fatal(err) 364 } 365 b.StopTimer() 366 }) 367 368 b.Run("insertRows_UniqueFKs", func(b *testing.B) { 369 setup([]string{`parentFK`, `childFK`}) 370 for i := 1; i <= numFKRows; i++ { 371 if _, err := db.Exec(fmt.Sprintf(`INSERT INTO parentFK(foo) VALUES(%d)`, i)); err != nil { 372 b.Fatal(err) 373 } 374 } 375 defer drop() 376 b.ResetTimer() 377 var run bytes.Buffer 378 379 run.WriteString(`INSERT INTO childFK(baz, foo) VALUES `) 380 381 for i := 1; i <= numFKRows; i++ { 382 run.WriteString(fmt.Sprintf("(%d, %d)", i, i)) 383 if i != numFKRows { 384 run.WriteString(", ") 385 } 386 } 387 388 b.ResetTimer() 389 statement := run.String() 390 if _, err := db.Exec(statement); err != nil { 391 b.Fatal(err) 392 } 393 b.StopTimer() 394 }) 395 396 b.Run("deleteRows_UniqueFKs", func(b *testing.B) { 397 setup([]string{`parentFK`, `childFK`}) 398 for i := 1; i <= numFKRows; i++ { 399 if _, err := db.Exec(fmt.Sprintf(`INSERT INTO parentFK(foo) VALUES(%d)`, i)); err != nil { 400 b.Fatal(err) 401 } 402 } 403 defer drop() 404 b.ResetTimer() 405 var run bytes.Buffer 406 407 run.WriteString(`INSERT INTO childFK(baz, foo) VALUES `) 408 409 for i := 1; i <= numFKRows; i++ { 410 run.WriteString(fmt.Sprintf("(%d, %d)", i, i)) 411 if i != numFKRows { 412 run.WriteString(", ") 413 } 414 } 415 statement := run.String() 416 if _, err := db.Exec(statement); err != nil { 417 b.Fatal(err) 418 } 419 b.ResetTimer() 420 if _, err := db.Exec(`DELETE FROM childFK`); err != nil { 421 b.Fatal(err) 422 } 423 b.StopTimer() 424 }) 425 426 b.Run("insertRows_NoFK", func(b *testing.B) { 427 setup([]string{`parentNoFK`, `childNoFK`}) 428 if _, err := db.Exec(`INSERT INTO parentNoFK(foo) VALUES(1)`); err != nil { 429 b.Fatal(err) 430 } 431 defer drop() 432 b.ResetTimer() 433 var run bytes.Buffer 434 run.WriteString(`INSERT INTO childNoFK(baz, foo) VALUES `) 435 436 for i := 1; i <= numFKRows; i++ { 437 run.WriteString(fmt.Sprintf("(%d, 1)", i)) 438 if i != numFKRows { 439 run.WriteString(", ") 440 } 441 } 442 statement := run.String() 443 if _, err := db.Exec(statement); err != nil { 444 b.Fatal(err) 445 } 446 447 b.StopTimer() 448 }) 449 b.Run("deleteRows_NoFK", func(b *testing.B) { 450 setup([]string{`parentNoFK`, `childNoFK`}) 451 if _, err := db.Exec(`INSERT INTO parentNoFK(foo) VALUES(1)`); err != nil { 452 b.Fatal(err) 453 } 454 defer drop() 455 var run bytes.Buffer 456 run.WriteString(`INSERT INTO childNoFK(baz, foo) VALUES `) 457 458 for i := 1; i <= numFKRows; i++ { 459 run.WriteString(fmt.Sprintf("(%d, 1)", i)) 460 if i != numFKRows { 461 run.WriteString(", ") 462 } 463 } 464 statement := run.String() 465 if _, err := db.Exec(statement); err != nil { 466 b.Fatal(err) 467 } 468 b.ResetTimer() 469 if _, err := db.Exec(`DELETE FROM childNoFK`); err != nil { 470 b.Fatal(err) 471 } 472 b.StopTimer() 473 }) 474 475 const numFKRowsMultipleRef = 1000 476 const refsPerRow = 10 477 b.Run("insertRows_multiple_refs", func(b *testing.B) { 478 setup([]string{`parentFK`, `childFK`}) 479 for i := 1; i <= numFKRowsMultipleRef; i++ { 480 if _, err := db.Exec(fmt.Sprintf(`INSERT INTO parentFK(foo) VALUES(%d)`, i)); err != nil { 481 b.Fatal(err) 482 } 483 } 484 defer drop() 485 var run bytes.Buffer 486 b.ResetTimer() 487 run.WriteString(`INSERT INTO childFK(baz, foo) VALUES `) 488 for i := 1; i <= numFKRowsMultipleRef; i++ { 489 for j := 1; j <= refsPerRow; j++ { 490 run.WriteString(fmt.Sprintf("(%d, %d)", j, i)) 491 if i != numFKRowsMultipleRef || j != refsPerRow { 492 run.WriteString(", ") 493 } 494 } 495 } 496 statement := run.String() 497 if _, err := db.Exec(statement); err != nil { 498 b.Fatal(err) 499 } 500 b.StopTimer() 501 }) 502 b.Run("deleteRows_multiple_refs", func(b *testing.B) { 503 setup([]string{`parentFK`, `childFK`}) 504 for i := 1; i <= numFKRowsMultipleRef; i++ { 505 if _, err := db.Exec(fmt.Sprintf(`INSERT INTO parentFK(foo) VALUES(%d)`, i)); err != nil { 506 b.Fatal(err) 507 } 508 } 509 defer drop() 510 var run bytes.Buffer 511 run.WriteString(`INSERT INTO childFK(baz, foo) VALUES `) 512 for i := 1; i <= numFKRowsMultipleRef; i++ { 513 for j := 1; j <= refsPerRow; j++ { 514 run.WriteString(fmt.Sprintf("(%d, %d)", j, i)) 515 if i != numFKRowsMultipleRef || j != refsPerRow { 516 run.WriteString(", ") 517 } 518 } 519 } 520 statement := run.String() 521 if _, err := db.Exec(statement); err != nil { 522 b.Fatal(err) 523 } 524 b.ResetTimer() 525 if _, err := db.Exec(`DELETE FROM childFK`); err != nil { 526 b.Fatal(err) 527 } 528 b.StopTimer() 529 }) 530 b.Run("insertRows_multiple_refs_noFK", func(b *testing.B) { 531 setup([]string{`parentNoFK`, `childNoFK`}) 532 for i := 1; i <= numFKRowsMultipleRef; i++ { 533 if _, err := db.Exec(fmt.Sprintf(`INSERT INTO parentNoFK(foo) VALUES(%d)`, i)); err != nil { 534 b.Fatal(err) 535 } 536 } 537 defer drop() 538 var run bytes.Buffer 539 b.ResetTimer() 540 run.WriteString(`INSERT INTO childNoFK(baz, foo) VALUES `) 541 for i := 1; i <= numFKRowsMultipleRef; i++ { 542 for j := 1; j <= refsPerRow; j++ { 543 run.WriteString(fmt.Sprintf("(%d, %d)", j, i)) 544 if i != numFKRowsMultipleRef || j != refsPerRow { 545 run.WriteString(", ") 546 } 547 } 548 } 549 statement := run.String() 550 if _, err := db.Exec(statement); err != nil { 551 b.Fatal(err) 552 } 553 b.StopTimer() 554 }) 555 556 b.Run("deleteRows_multiple_refs_No_FK", func(b *testing.B) { 557 setup([]string{`parentNoFK`, `childNoFK`}) 558 for i := 1; i <= numFKRowsMultipleRef; i++ { 559 if _, err := db.Exec(fmt.Sprintf(`INSERT INTO parentNoFK(foo) VALUES(%d)`, i)); err != nil { 560 b.Fatal(err) 561 } 562 } 563 defer drop() 564 var run bytes.Buffer 565 run.WriteString(`INSERT INTO childNoFK(baz, foo) VALUES `) 566 for i := 1; i <= numFKRowsMultipleRef; i++ { 567 for j := 1; j <= refsPerRow; j++ { 568 run.WriteString(fmt.Sprintf("(%d, %d)", j, i)) 569 if i != numFKRowsMultipleRef || j != refsPerRow { 570 run.WriteString(", ") 571 } 572 } 573 } 574 statement := run.String() 575 if _, err := db.Exec(statement); err != nil { 576 b.Fatal(err) 577 } 578 b.ResetTimer() 579 if _, err := db.Exec(`DELETE FROM childNoFK`); err != nil { 580 b.Fatal(err) 581 } 582 b.StopTimer() 583 }) 584 585 // Inserts and deletes are tested for interleaved tables 586 b.Run("insertRows_interleaved", func(b *testing.B) { 587 setup([]string{`parentInterleaved`, `childInterleaved`}) 588 for i := 1; i <= numFKRowsMultipleRef; i++ { 589 if _, err := db.Exec(fmt.Sprintf(`INSERT INTO parentInterleaved(foo) VALUES(%d)`, i)); err != nil { 590 b.Fatal(err) 591 } 592 } 593 defer drop() 594 var run bytes.Buffer 595 b.ResetTimer() 596 run.WriteString(`INSERT INTO childInterleaved(baz, foo) VALUES `) 597 for i := 1; i <= numFKRowsMultipleRef; i++ { 598 for j := 1; j <= refsPerRow; j++ { 599 run.WriteString(fmt.Sprintf("(%d, %d)", j, i)) 600 if i != numFKRowsMultipleRef || j != refsPerRow { 601 run.WriteString(", ") 602 } 603 } 604 } 605 statement := run.String() 606 if _, err := db.Exec(statement); err != nil { 607 b.Fatal(err) 608 } 609 b.StopTimer() 610 }) 611 612 b.Run("deleteRows_interleaved", func(b *testing.B) { 613 setup([]string{`parentInterleaved`, `childInterleaved`}) 614 for i := 1; i <= numFKRowsMultipleRef; i++ { 615 if _, err := db.Exec(fmt.Sprintf(`INSERT INTO parentInterleaved(foo) VALUES(%d)`, i)); err != nil { 616 b.Fatal(err) 617 } 618 } 619 defer drop() 620 var run bytes.Buffer 621 run.WriteString(`INSERT INTO childInterleaved(baz, foo) VALUES `) 622 for i := 1; i <= numFKRowsMultipleRef; i++ { 623 for j := 1; j <= refsPerRow; j++ { 624 run.WriteString(fmt.Sprintf("(%d, %d)", j, i)) 625 if i != numFKRowsMultipleRef || j != refsPerRow { 626 run.WriteString(", ") 627 } 628 } 629 } 630 statement := run.String() 631 if _, err := db.Exec(statement); err != nil { 632 b.Fatal(err) 633 } 634 b.ResetTimer() 635 if _, err := db.Exec(`DELETE FROM childInterleaved`); err != nil { 636 b.Fatal(err) 637 } 638 b.StopTimer() 639 }) 640 641 // This tests the performance of deleting rows from the parent table 642 b.Run("deleteRowsFromParent_interleaved", func(b *testing.B) { 643 setup([]string{`parentInterleaved`, `childInterleaved`}) 644 for i := 1; i <= numFKRowsMultipleRef; i++ { 645 if _, err := db.Exec(fmt.Sprintf(`INSERT INTO parentInterleaved(foo) VALUES(%d)`, i)); err != nil { 646 b.Fatal(err) 647 } 648 } 649 defer drop() 650 var run bytes.Buffer 651 run.WriteString(`INSERT INTO childInterleaved(baz, foo) VALUES `) 652 for i := 1; i <= numFKRowsMultipleRef; i++ { 653 for j := 1; j <= refsPerRow; j++ { 654 run.WriteString(fmt.Sprintf("(%d, %d)", j, i)) 655 if i != numFKRowsMultipleRef || j != refsPerRow { 656 run.WriteString(", ") 657 } 658 } 659 } 660 statement := run.String() 661 if _, err := db.Exec(statement); err != nil { 662 b.Fatal(err) 663 } 664 b.ResetTimer() 665 if _, err := db.Exec(`DELETE FROM parentInterleaved`); err != nil { 666 b.Fatal(err) 667 } 668 b.StopTimer() 669 }) 670 671 // This tests the performance of deleting rows from the parent table when there are two interleaved tables 672 b.Run("deleteRowsFromParent_interleaved_sibling", func(b *testing.B) { 673 setup([]string{`parentInterleaved`, `childInterleaved`, `siblingInterleaved`}) 674 for i := 1; i <= numFKRowsMultipleRef; i++ { 675 if _, err := db.Exec(fmt.Sprintf(`INSERT INTO parentInterleaved(foo) VALUES(%d)`, i)); err != nil { 676 b.Fatal(err) 677 } 678 } 679 defer drop() 680 var run, runSibling bytes.Buffer 681 run.WriteString(`INSERT INTO childInterleaved(baz, foo) VALUES `) 682 runSibling.WriteString(`INSERT INTO siblingInterleaved(baz, foo) VALUES `) 683 for i := 1; i <= numFKRowsMultipleRef; i++ { 684 for j := 1; j <= refsPerRow; j++ { 685 run.WriteString(fmt.Sprintf("(%d, %d)", j, i)) 686 runSibling.WriteString(fmt.Sprintf("(%d, %d)", j, i)) 687 if i != numFKRowsMultipleRef || j != refsPerRow { 688 run.WriteString(", ") 689 runSibling.WriteString(", ") 690 } 691 } 692 } 693 statement := run.String() 694 if _, err := db.Exec(statement); err != nil { 695 b.Fatal(err) 696 } 697 statementSibling := runSibling.String() 698 if _, err := db.Exec(statementSibling); err != nil { 699 b.Fatal(err) 700 } 701 b.ResetTimer() 702 if _, err := db.Exec(`DELETE FROM parentInterleaved`); err != nil { 703 b.Fatal(err) 704 } 705 b.StopTimer() 706 }) 707 708 // This tests the performance of deleting rows from the parent table when there is an interleaved table 709 // and another table further interleaved in it. 710 b.Run("deleteRowsFromParent_interleaved_grandchild", func(b *testing.B) { 711 setup([]string{`parentInterleaved`, `childInterleaved`, `grandchildInterleaved`}) 712 for i := 1; i <= numFKRowsMultipleRef; i++ { 713 if _, err := db.Exec(fmt.Sprintf(`INSERT INTO parentInterleaved(foo) VALUES(%d)`, i)); err != nil { 714 b.Fatal(err) 715 } 716 } 717 defer drop() 718 var run, runGrandchild bytes.Buffer 719 run.WriteString(`INSERT INTO childInterleaved(baz, foo) VALUES `) 720 runGrandchild.WriteString(`INSERT INTO grandChildInterleaved(foo, baz, bar) VALUES `) 721 for i := 1; i <= numFKRowsMultipleRef; i++ { 722 for j := 1; j <= refsPerRow; j++ { 723 run.WriteString(fmt.Sprintf("(%d, %d)", j, i)) 724 runGrandchild.WriteString(fmt.Sprintf("(%d, %d, 1)", i, j)) 725 if i != numFKRowsMultipleRef || j != refsPerRow { 726 run.WriteString(", ") 727 runGrandchild.WriteString(", ") 728 } 729 } 730 } 731 statement := run.String() 732 if _, err := db.Exec(statement); err != nil { 733 b.Fatal(err) 734 } 735 statementGrandchild := runGrandchild.String() 736 if _, err := db.Exec(statementGrandchild); err != nil { 737 b.Fatal(err) 738 } 739 b.ResetTimer() 740 if _, err := db.Exec(`DELETE FROM parentInterleaved`); err != nil { 741 b.Fatal(err) 742 } 743 b.StopTimer() 744 }) 745 746 // For the self-referential table benchmarks, `numSRRows` rows are inserted and there is again a contrast between 747 // rows with foreign key references and those without. 748 // There are several different cases: 749 // Cascade: casacading deletes 750 // No_FK: no foreign key references 751 // SetNull: ... ON DELETE SET NULL foreign key reference 752 // Within each of these three categories, there are two cases: 753 // Chain: row i references row i-1, chaining until row 1 754 // ManyChildren: row 2..numSRRows all reference row 1 755 const numSRRows = 10000 756 b.Run("SelfReferential_Cascade_FK_Chain_Delete", func(b *testing.B) { 757 setup([]string{`self_referential`}) 758 defer drop() 759 if _, err := db.Exec(`INSERT INTO self_referential(id) VALUES (1)`); err != nil { 760 b.Fatal(err) 761 } 762 763 for i := 2; i <= numSRRows; i++ { 764 insert := fmt.Sprintf(`INSERT INTO self_referential(id, pid) VALUES (%d, %d)`, i, i-1) 765 if _, err := db.Exec(insert); err != nil { 766 b.Fatal(err) 767 } 768 } 769 770 b.ResetTimer() 771 772 if _, err := db.Exec(`DELETE FROM self_referential`); err != nil { 773 b.Fatal(err) 774 } 775 b.StopTimer() 776 }) 777 778 b.Run("SelfReferential_Cascade_FK_ManyChildren_Delete", func(b *testing.B) { 779 setup([]string{`self_referential`}) 780 defer drop() 781 if _, err := db.Exec(`INSERT INTO self_referential(id) VALUES (1)`); err != nil { 782 b.Fatal(err) 783 } 784 785 for i := 2; i <= numSRRows; i++ { 786 insert := fmt.Sprintf(`INSERT INTO self_referential(id, pid) VALUES (%d, 1)`, i) 787 if _, err := db.Exec(insert); err != nil { 788 b.Fatal(err) 789 } 790 } 791 792 b.ResetTimer() 793 if _, err := db.Exec(`DELETE FROM self_referential`); err != nil { 794 b.Fatal(err) 795 } 796 b.StopTimer() 797 }) 798 799 b.Run("SelfReferential_No_FK_Chain_Delete", func(b *testing.B) { 800 setup([]string{`self_referential_noFK`}) 801 defer drop() 802 var insert bytes.Buffer 803 insert.WriteString(`INSERT INTO self_referential_noFK(id) VALUES `) 804 for i := 1; i <= numSRRows; i++ { 805 insert.WriteString(fmt.Sprintf(`(%d)`, i)) 806 if i != numSRRows { 807 insert.WriteString(`, `) 808 } 809 } 810 811 if _, err := db.Exec(insert.String()); err != nil { 812 b.Fatal(err) 813 } 814 815 b.ResetTimer() 816 817 if _, err := db.Exec(`DELETE FROM self_referential_noFK`); err != nil { 818 b.Fatal(err) 819 } 820 b.StopTimer() 821 }) 822 b.Run("SelfReferential_No_FK_ManyChildren_Delete", func(b *testing.B) { 823 setup([]string{`self_referential_noFK`}) 824 defer drop() 825 if _, err := db.Exec(`INSERT INTO self_referential_noFK(id) VALUES (1)`); err != nil { 826 b.Fatal(err) 827 } 828 829 for i := 2; i <= numSRRows; i++ { 830 insert := fmt.Sprintf(`INSERT INTO self_referential_noFK(id, pid) VALUES (%d, 1)`, i) 831 if _, err := db.Exec(insert); err != nil { 832 b.Fatal(err) 833 } 834 } 835 836 b.ResetTimer() 837 if _, err := db.Exec(`DELETE FROM self_referential_noFK`); err != nil { 838 b.Fatal(err) 839 } 840 b.StopTimer() 841 }) 842 b.Run("SelfReferential_SetNull_FK_Chain_Delete", func(b *testing.B) { 843 setup([]string{`self_referential_setnull`}) 844 defer drop() 845 run3 := `INSERT INTO self_referential_setnull(id) VALUES (1)` 846 if _, err := db.Exec(run3); err != nil { 847 b.Fatal(err) 848 } 849 850 for i := 2; i <= numSRRows; i++ { 851 insert := fmt.Sprintf(`INSERT INTO self_referential_setnull(id, pid) VALUES (%d, %d)`, i, i-1) 852 if _, err := db.Exec(insert); err != nil { 853 b.Fatal(err) 854 } 855 } 856 857 b.ResetTimer() 858 if _, err := db.Exec(`DELETE FROM self_referential_setnull`); err != nil { 859 b.Fatal(err) 860 } 861 b.StopTimer() 862 }) 863 864 b.Run("SelfReferential_SetNull_FK_ManyChildren", func(b *testing.B) { 865 setup([]string{`self_referential_setnull`}) 866 defer drop() 867 run3 := `INSERT INTO self_referential_setnull(id) VALUES (1)` 868 if _, err := db.Exec(run3); err != nil { 869 b.Fatal(err) 870 } 871 872 for i := 2; i <= numSRRows; i++ { 873 insert := fmt.Sprintf(`INSERT INTO self_referential_setnull(id, pid) VALUES (%d, 1)`, i) 874 if _, err := db.Exec(insert); err != nil { 875 b.Fatal(err) 876 } 877 } 878 879 b.ResetTimer() 880 881 if _, err := db.Exec(`DELETE FROM self_referential_setnull`); err != nil { 882 b.Fatal(err) 883 } 884 b.StopTimer() 885 }) 886 }