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  }