diff --git a/go/test/endtoend/utils/cmp.go b/go/test/endtoend/utils/cmp.go index f01497d7bdf..32c90a27a5b 100644 --- a/go/test/endtoend/utils/cmp.go +++ b/go/test/endtoend/utils/cmp.go @@ -136,7 +136,7 @@ func (mcmp *MySQLCompare) AssertMatchesAnyNoCompare(query string, expected ...st // Both clients need to return an error. The error of Vitess must be matching the given expectation. func (mcmp *MySQLCompare) AssertContainsError(query, expected string) { mcmp.t.Helper() - _, err := mcmp.ExecAllowAndCompareError(query) + _, err := mcmp.ExecAllowAndCompareError(query, CompareOptions{}) require.Error(mcmp.t, err) assert.Contains(mcmp.t, err.Error(), expected, "actual error: %s", err.Error()) } @@ -211,7 +211,7 @@ func (mcmp *MySQLCompare) Exec(query string) *sqltypes.Result { mysqlQr, err := mcmp.MySQLConn.ExecuteFetch(query, 1000, true) require.NoError(mcmp.t, err, "[MySQL Error] for query: "+query) - compareVitessAndMySQLResults(mcmp.t, query, mcmp.VtConn, vtQr, mysqlQr, false) + compareVitessAndMySQLResults(mcmp.t, query, mcmp.VtConn, vtQr, mysqlQr, CompareOptions{}) return vtQr } @@ -238,7 +238,7 @@ func (mcmp *MySQLCompare) ExecWithColumnCompare(query string) *sqltypes.Result { mysqlQr, err := mcmp.MySQLConn.ExecuteFetch(query, 1000, true) require.NoError(mcmp.t, err, "[MySQL Error] for query: "+query) - compareVitessAndMySQLResults(mcmp.t, query, mcmp.VtConn, vtQr, mysqlQr, true) + compareVitessAndMySQLResults(mcmp.t, query, mcmp.VtConn, vtQr, mysqlQr, CompareOptions{CompareColumnNames: true}) return vtQr } @@ -250,7 +250,7 @@ func (mcmp *MySQLCompare) ExecWithColumnCompare(query string) *sqltypes.Result { // The result set and error produced by Vitess are returned to the caller. // If the Vitess and MySQL error are both nil, but the results do not match, // the mismatched results are instead returned as an error, as well as the Vitess result set -func (mcmp *MySQLCompare) ExecAllowAndCompareError(query string) (*sqltypes.Result, error) { +func (mcmp *MySQLCompare) ExecAllowAndCompareError(query string, opts CompareOptions) (*sqltypes.Result, error) { mcmp.t.Helper() vtQr, vtErr := mcmp.VtConn.ExecuteFetch(query, 1000, true) mysqlQr, mysqlErr := mcmp.MySQLConn.ExecuteFetch(query, 1000, true) @@ -259,7 +259,7 @@ func (mcmp *MySQLCompare) ExecAllowAndCompareError(query string) (*sqltypes.Resu // Since we allow errors, we don't want to compare results if one of the client failed. // Vitess and MySQL should always be agreeing whether the query returns an error or not. if vtErr == nil && mysqlErr == nil { - vtErr = compareVitessAndMySQLResults(mcmp.t, query, mcmp.VtConn, vtQr, mysqlQr, false) + vtErr = compareVitessAndMySQLResults(mcmp.t, query, mcmp.VtConn, vtQr, mysqlQr, opts) } return vtQr, vtErr } @@ -297,7 +297,7 @@ func (mcmp *MySQLCompare) ExecAllowError(query string) (*sqltypes.Result, error) // Since we allow errors, we don't want to compare results if one of the client failed. // Vitess and MySQL should always be agreeing whether the query returns an error or not. if mysqlErr == nil { - vtErr = compareVitessAndMySQLResults(mcmp.t, query, mcmp.VtConn, vtQr, mysqlQr, false) + vtErr = compareVitessAndMySQLResults(mcmp.t, query, mcmp.VtConn, vtQr, mysqlQr, CompareOptions{}) } return vtQr, vtErr } diff --git a/go/test/endtoend/utils/mysql.go b/go/test/endtoend/utils/mysql.go index e6eb693acab..53b50195036 100644 --- a/go/test/endtoend/utils/mysql.go +++ b/go/test/endtoend/utils/mysql.go @@ -170,7 +170,12 @@ func prepareMySQLWithSchema(params mysql.ConnParams, sql string) error { return nil } -func compareVitessAndMySQLResults(t TestingT, query string, vtConn *mysql.Conn, vtQr, mysqlQr *sqltypes.Result, compareColumnNames bool) error { +type CompareOptions struct { + CompareColumnNames bool + IgnoreRowsAffected bool +} + +func compareVitessAndMySQLResults(t TestingT, query string, vtConn *mysql.Conn, vtQr, mysqlQr *sqltypes.Result, opts CompareOptions) error { t.Helper() if vtQr == nil && mysqlQr == nil { @@ -203,7 +208,7 @@ func compareVitessAndMySQLResults(t TestingT, query string, vtConn *mysql.Conn, myCols = append(myCols, myField.Name) } - if compareColumnNames && !assert.Equal(t, myCols, vtCols, "column names do not match - the expected values are what mysql produced") { + if opts.CompareColumnNames && !assert.Equal(t, myCols, vtCols, "column names do not match - the expected values are what mysql produced") { t.Errorf("column names do not match - the expected values are what mysql produced\nNot equal: \nexpected: %v\nactual: %v\n", myCols, vtCols) } } @@ -218,6 +223,11 @@ func compareVitessAndMySQLResults(t TestingT, query string, vtConn *mysql.Conn, orderBy = selStmt.GetOrderBy() != nil } + if opts.IgnoreRowsAffected { + vtQr.RowsAffected = 0 + mysqlQr.RowsAffected = 0 + } + if (orderBy && sqltypes.ResultsEqual([]sqltypes.Result{*vtQr}, []sqltypes.Result{*mysqlQr})) || sqltypes.ResultsEqualUnordered([]sqltypes.Result{*vtQr}, []sqltypes.Result{*mysqlQr}) { return nil } diff --git a/go/test/endtoend/utils/utils.go b/go/test/endtoend/utils/utils.go index 8a36ed3d083..345f1e787b4 100644 --- a/go/test/endtoend/utils/utils.go +++ b/go/test/endtoend/utils/utils.go @@ -181,7 +181,7 @@ func ExecCompareMySQL(t *testing.T, vtConn, mysqlConn *mysql.Conn, query string) mysqlQr, err := mysqlConn.ExecuteFetch(query, 1000, true) require.NoError(t, err, "[MySQL Error] for query: "+query) - compareVitessAndMySQLResults(t, query, vtConn, vtQr, mysqlQr, false) + compareVitessAndMySQLResults(t, query, vtConn, vtQr, mysqlQr, CompareOptions{}) return vtQr } diff --git a/go/test/endtoend/vtgate/foreignkey/fk_fuzz_test.go b/go/test/endtoend/vtgate/foreignkey/fk_fuzz_test.go index 9d2f267a288..490c54f2299 100644 --- a/go/test/endtoend/vtgate/foreignkey/fk_fuzz_test.go +++ b/go/test/endtoend/vtgate/foreignkey/fk_fuzz_test.go @@ -333,7 +333,7 @@ func (fz *fuzzer) generateAndExecuteStatementQuery(t *testing.T, mcmp utils.MySQ for _, query := range queries { // When the concurrency is 1, then we run the query both on MySQL and Vitess. if fz.concurrency == 1 { - _, _ = mcmp.ExecAllowAndCompareError(query) + _, _ = mcmp.ExecAllowAndCompareError(query, utils.CompareOptions{IgnoreRowsAffected: true}) // If t is marked failed, we have encountered our first failure. // Let's collect the required information and finish execution. if t.Failed() { diff --git a/go/test/endtoend/vtgate/foreignkey/fk_test.go b/go/test/endtoend/vtgate/foreignkey/fk_test.go index 5a34a2b49c0..1972d0a6259 100644 --- a/go/test/endtoend/vtgate/foreignkey/fk_test.go +++ b/go/test/endtoend/vtgate/foreignkey/fk_test.go @@ -902,7 +902,7 @@ func TestFkScenarios(t *testing.T) { } // Run the DML query that needs to be tested and verify output with MySQL. - _, err := mcmp.ExecAllowAndCompareError(tt.dmlQuery) + _, err := mcmp.ExecAllowAndCompareError(tt.dmlQuery, utils.CompareOptions{}) if tt.dmlShouldErr { assert.Error(t, err) } else { @@ -948,7 +948,7 @@ func TestFkScenarios(t *testing.T) { mcmp.Exec("SELECT * FROM fk_t13 ORDER BY id") // Update that fails - _, err := mcmp.ExecAllowAndCompareError("UPDATE fk_t10 SET col = 15 WHERE id = 1") + _, err := mcmp.ExecAllowAndCompareError("UPDATE fk_t10 SET col = 15 WHERE id = 1", utils.CompareOptions{}) require.Error(t, err) // Verify the results @@ -1006,6 +1006,7 @@ func TestFkQueries(t *testing.T) { testcases := []struct { name string queries []string + opts utils.CompareOptions }{ { name: "Non-literal update", @@ -1135,6 +1136,17 @@ func TestFkQueries(t *testing.T) { "delete fk_t11 from fk_t11 join fk_t12 using (id) where fk_t11.id = 4", }, }, + { + name: "Multi table delete where MySQL and Vitess report different rows affected", + queries: []string{ + "insert /*+ SET_VAR(foreign_key_checks=0) */ into fk_t11 (id, col) values (4, '1'), (5, '3'), (7, '22'), (8, '5'), (9, NULL), (10, '3')", + "insert /*+ SET_VAR(foreign_key_checks=0) */ into fk_t12 (id, col) values (4, '1'), (5, '3'), (7, '22'), (8, '5'), (9, NULL), (10, '3')", + "delete fk_t11, fk_t12 from fk_t11 join fk_t12 using (id) where fk_t11.id = 5", + }, + opts: utils.CompareOptions{ + IgnoreRowsAffected: true, + }, + }, } for _, tt := range testcases { @@ -1153,7 +1165,7 @@ func TestFkQueries(t *testing.T) { ensureDatabaseState(t, mcmp.MySQLConn, true) for _, query := range tt.queries { - _, _ = mcmp.ExecAllowAndCompareError(query) + _, _ = mcmp.ExecAllowAndCompareError(query, tt.opts) if t.Failed() { break } @@ -1212,7 +1224,7 @@ func TestFkOneCase(t *testing.T) { log.Errorf("Query %v, Result - %v", query, res.Rows) continue } - _, _ = mcmp.ExecAllowAndCompareError(query) + _, _ = mcmp.ExecAllowAndCompareError(query, utils.CompareOptions{}) if t.Failed() { log.Errorf("Query failed - %v", query) break @@ -1490,5 +1502,5 @@ create table temp2(id bigint auto_increment primary key, col varchar(20) not nul mcmp.Exec(`set foreign_key_checks = on`) mcmp.Exec(`insert into temp2(col) values('a'), ('b'), ('c') `) mcmp.Exec(`insert into temp1(col) values('a') `) - mcmp.ExecAllowAndCompareError(`insert into temp1(col) values('d') `) + mcmp.ExecAllowAndCompareError(`insert into temp1(col) values('d') `, utils.CompareOptions{}) } diff --git a/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go b/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go index 67a6c5e3710..fd4088ba704 100644 --- a/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go +++ b/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go @@ -768,7 +768,7 @@ func TestHavingQueries(t *testing.T) { for _, query := range queries { mcmp.Run(query, func(mcmp *utils.MySQLCompare) { - mcmp.ExecAllowAndCompareError(query) + mcmp.ExecAllowAndCompareError(query, utils.CompareOptions{}) }) } } diff --git a/go/test/endtoend/vtgate/queries/dml/dml_test.go b/go/test/endtoend/vtgate/queries/dml/dml_test.go index 3841a05fcdb..8706b37d445 100644 --- a/go/test/endtoend/vtgate/queries/dml/dml_test.go +++ b/go/test/endtoend/vtgate/queries/dml/dml_test.go @@ -252,7 +252,7 @@ func TestDeleteWithSubquery(t *testing.T) { `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)]]`) // delete with subquery from same table (fails on mysql) - subquery get's merged so fails for vitess - _, err := mcmp.ExecAllowAndCompareError(`delete from s_tbl where id in (select id from s_tbl)`) + _, err := mcmp.ExecAllowAndCompareError(`delete from s_tbl where id in (select id from s_tbl)`, utils.CompareOptions{}) require.ErrorContains(t, err, "You can't specify target table 's_tbl' for update in FROM clause (errno 1093) (sqlstate HY000)") // delete with subquery from same table (fails on mysql) - subquery not merged so passes for vitess diff --git a/go/test/endtoend/vtgate/queries/misc/misc_test.go b/go/test/endtoend/vtgate/queries/misc/misc_test.go index d0c610084cd..07f0766e150 100644 --- a/go/test/endtoend/vtgate/queries/misc/misc_test.go +++ b/go/test/endtoend/vtgate/queries/misc/misc_test.go @@ -106,11 +106,11 @@ func TestInvalidDateTimeTimestampVals(t *testing.T) { mcmp, closer := start(t) defer closer() - _, err := mcmp.ExecAllowAndCompareError(`select date'2022'`) + _, err := mcmp.ExecAllowAndCompareError(`select date'2022'`, utils.CompareOptions{}) require.Error(t, err) - _, err = mcmp.ExecAllowAndCompareError(`select time'12:34:56:78'`) + _, err = mcmp.ExecAllowAndCompareError(`select time'12:34:56:78'`, utils.CompareOptions{}) require.Error(t, err) - _, err = mcmp.ExecAllowAndCompareError(`select timestamp'2022'`) + _, err = mcmp.ExecAllowAndCompareError(`select timestamp'2022'`, utils.CompareOptions{}) require.Error(t, err) } @@ -257,12 +257,12 @@ func TestPrepareStatements(t *testing.T) { mcmp.AssertMatchesNoOrder(`execute prep_in_pk using @id1, @id2`, `[[INT64(0) INT64(0)] [INT64(1) INT64(0)]]`) // Fail by providing wrong number of arguments - _, err := mcmp.ExecAllowAndCompareError(`execute prep_in_pk using @id1, @id1, @id`) + _, err := mcmp.ExecAllowAndCompareError(`execute prep_in_pk using @id1, @id1, @id`, utils.CompareOptions{}) incorrectCount := "VT03025: Incorrect arguments to EXECUTE" assert.ErrorContains(t, err, incorrectCount) - _, err = mcmp.ExecAllowAndCompareError(`execute prep_in_pk using @id1`) + _, err = mcmp.ExecAllowAndCompareError(`execute prep_in_pk using @id1`, utils.CompareOptions{}) assert.ErrorContains(t, err, incorrectCount) - _, err = mcmp.ExecAllowAndCompareError(`execute prep_in_pk`) + _, err = mcmp.ExecAllowAndCompareError(`execute prep_in_pk`, utils.CompareOptions{}) assert.ErrorContains(t, err, incorrectCount) mcmp.Exec(`prepare prep_art from 'select 1+?, 10/?'`) @@ -282,10 +282,10 @@ func TestPrepareStatements(t *testing.T) { mcmp.Exec(`select 1+9999999999999999999999999999, 10/9999999999999999999999999999 from t1 limit 1`) mcmp.Exec("deallocate prepare prep_art") - _, err = mcmp.ExecAllowAndCompareError(`execute prep_art using @id1, @id1`) + _, err = mcmp.ExecAllowAndCompareError(`execute prep_art using @id1, @id1`, utils.CompareOptions{}) assert.ErrorContains(t, err, "VT09011: Unknown prepared statement handler (prep_art) given to EXECUTE") - _, err = mcmp.ExecAllowAndCompareError("deallocate prepare prep_art") + _, err = mcmp.ExecAllowAndCompareError("deallocate prepare prep_art", utils.CompareOptions{}) assert.ErrorContains(t, err, "VT09011: Unknown prepared statement handler (prep_art) given to DEALLOCATE PREPARE") } diff --git a/go/test/endtoend/vtgate/queries/orderby/orderby_test.go b/go/test/endtoend/vtgate/queries/orderby/orderby_test.go index b63ecc1b004..e8d8d4bfef1 100644 --- a/go/test/endtoend/vtgate/queries/orderby/orderby_test.go +++ b/go/test/endtoend/vtgate/queries/orderby/orderby_test.go @@ -150,7 +150,7 @@ func TestOrderByComplex(t *testing.T) { for _, query := range queries { mcmp.Run(query, func(mcmp *utils.MySQLCompare) { - _, _ = mcmp.ExecAllowAndCompareError(query) + _, _ = mcmp.ExecAllowAndCompareError(query, utils.CompareOptions{}) }) } } diff --git a/go/test/endtoend/vtgate/queries/random/random_test.go b/go/test/endtoend/vtgate/queries/random/random_test.go index b3d6944cb56..2d210ee7f99 100644 --- a/go/test/endtoend/vtgate/queries/random/random_test.go +++ b/go/test/endtoend/vtgate/queries/random/random_test.go @@ -71,7 +71,7 @@ func helperTest(t *testing.T, query string) { mcmp, closer := start(t) defer closer() - result, err := mcmp.ExecAllowAndCompareError(query) + result, err := mcmp.ExecAllowAndCompareError(query, utils.CompareOptions{}) fmt.Println(result) fmt.Println(err) }) @@ -261,7 +261,7 @@ func TestRandom(t *testing.T) { qg := newQueryGenerator(genConfig, 2, 2, 2, schemaTables) qg.randomQuery() query := sqlparser.String(qg.stmt) - _, vtErr := mcmp.ExecAllowAndCompareError(query) + _, vtErr := mcmp.ExecAllowAndCompareError(query, utils.CompareOptions{}) // this assumes all queries are valid mysql queries if vtErr != nil { diff --git a/go/test/endtoend/vtgate/queries/random/simplifier_test.go b/go/test/endtoend/vtgate/queries/random/simplifier_test.go index 3f3f78017cd..c93c0e679c1 100644 --- a/go/test/endtoend/vtgate/queries/random/simplifier_test.go +++ b/go/test/endtoend/vtgate/queries/random/simplifier_test.go @@ -64,7 +64,7 @@ func TestSimplifyResultsMismatchedQuery(t *testing.T) { mcmp, closer := start(t) defer closer() - mcmp.ExecAllowAndCompareError(simplified) + mcmp.ExecAllowAndCompareError(simplified, utils.CompareOptions{}) }) fmt.Printf("final simplified query: %s\n", simplified) @@ -77,7 +77,7 @@ func simplifyResultsMismatchedQuery(t *testing.T, query string) string { mcmp, closer := start(t) defer closer() - _, err := mcmp.ExecAllowAndCompareError(query) + _, err := mcmp.ExecAllowAndCompareError(query, utils.CompareOptions{}) if err == nil { t.Fatalf("query (%s) does not error", query) } else if !strings.Contains(err.Error(), "mismatched") { @@ -105,7 +105,7 @@ func simplifyResultsMismatchedQuery(t *testing.T, query string) string { vSchemaWrapper, func(statement sqlparser.SelectStatement) bool { q := sqlparser.String(statement) - _, newErr := mcmp.ExecAllowAndCompareError(q) + _, newErr := mcmp.ExecAllowAndCompareError(q, utils.CompareOptions{}) if newErr == nil { return false } else {