github.com/dolthub/go-mysql-server@v0.18.0/enginetest/evaluation.go (about) 1 // Copyright 2022 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 enginetest 16 17 import ( 18 "fmt" 19 "strconv" 20 "strings" 21 "testing" 22 "time" 23 24 "github.com/dolthub/vitess/go/sqltypes" 25 querypb "github.com/dolthub/vitess/go/vt/proto/query" 26 "github.com/dolthub/vitess/go/vt/sqlparser" 27 "github.com/shopspring/decimal" 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 "gopkg.in/src-d/go-errors.v1" 31 32 sqle "github.com/dolthub/go-mysql-server" 33 "github.com/dolthub/go-mysql-server/enginetest/queries" 34 "github.com/dolthub/go-mysql-server/enginetest/scriptgen/setup" 35 "github.com/dolthub/go-mysql-server/sql" 36 "github.com/dolthub/go-mysql-server/sql/expression" 37 "github.com/dolthub/go-mysql-server/sql/plan" 38 "github.com/dolthub/go-mysql-server/sql/planbuilder" 39 "github.com/dolthub/go-mysql-server/sql/transform" 40 "github.com/dolthub/go-mysql-server/sql/types" 41 ) 42 43 // RunQueryWithContext runs the query given and asserts that it doesn't result in an error. 44 // If |ctx| is nil, this function creates new context using `NewContext()` method on given harness. 45 func RunQueryWithContext(t *testing.T, e QueryEngine, harness Harness, ctx *sql.Context, query string) { 46 if ctx == nil { 47 ctx = NewContext(harness) 48 } 49 ctx = ctx.WithQuery(query) 50 _, iter, err := e.Query(ctx, query) 51 require.NoError(t, err, "error running query %s: %v", query, err) 52 _, err = sql.RowIterToRows(ctx, iter) 53 require.NoError(t, err) 54 validateEngine(t, ctx, harness, e) 55 } 56 57 // TestScript runs the test script given, making any assertions given 58 func TestScript(t *testing.T, harness Harness, script queries.ScriptTest) { 59 e := mustNewEngine(t, harness) 60 defer e.Close() 61 TestScriptWithEngine(t, e, harness, script) 62 } 63 64 func IsServerEngine(e QueryEngine) bool { 65 _, ok := e.(*ServerQueryEngine) 66 return ok 67 } 68 69 // CreateNewConnectionForServerEngine creates a new connection in the server engine. 70 // If there was an existing one, it gets closed before the new gets created. 71 // This function should be called when needing to use new session for the server. 72 func CreateNewConnectionForServerEngine(ctx *sql.Context, e QueryEngine) error { 73 if IsServerEngine(e) { 74 return e.(*ServerQueryEngine).NewConnection(ctx) 75 } 76 return nil 77 } 78 79 // TestScriptWithEngine runs the test script given with the engine provided. 80 func TestScriptWithEngine(t *testing.T, e QueryEngine, harness Harness, script queries.ScriptTest) { 81 ctx := NewContext(harness) 82 err := CreateNewConnectionForServerEngine(ctx, e) 83 require.NoError(t, err, nil) 84 85 t.Run(script.Name, func(t *testing.T) { 86 for _, statement := range script.SetUpScript { 87 if sh, ok := harness.(SkippingHarness); ok { 88 if sh.SkipQueryTest(statement) { 89 t.Skip() 90 } 91 } 92 ctx = ctx.WithQuery(statement) 93 RunQueryWithContext(t, e, harness, ctx, statement) 94 } 95 96 assertions := script.Assertions 97 if len(assertions) == 0 { 98 assertions = []queries.ScriptTestAssertion{ 99 { 100 Query: script.Query, 101 Expected: script.Expected, 102 ExpectedErr: script.ExpectedErr, 103 ExpectedIndexes: script.ExpectedIndexes, 104 }, 105 } 106 } 107 108 for _, assertion := range assertions { 109 t.Run(assertion.Query, func(t *testing.T) { 110 if assertion.NewSession { 111 th, ok := harness.(TransactionHarness) 112 require.True(t, ok, "ScriptTestAssertion requested a NewSession, "+ 113 "but harness doesn't implement TransactionHarness") 114 ctx = th.NewSession() 115 } 116 117 if sh, ok := harness.(SkippingHarness); ok && sh.SkipQueryTest(assertion.Query) { 118 t.Skip() 119 } 120 if assertion.Skip { 121 t.Skip() 122 } 123 124 if assertion.ExpectedErr != nil { 125 AssertErr(t, e, harness, assertion.Query, assertion.ExpectedErr) 126 } else if assertion.ExpectedErrStr != "" { 127 AssertErrWithCtx(t, e, harness, ctx, assertion.Query, nil, assertion.ExpectedErrStr) 128 } else if assertion.ExpectedWarning != 0 { 129 AssertWarningAndTestQuery(t, e, nil, harness, assertion.Query, 130 assertion.Expected, nil, assertion.ExpectedWarning, assertion.ExpectedWarningsCount, 131 assertion.ExpectedWarningMessageSubstring, assertion.SkipResultsCheck) 132 } else if assertion.SkipResultsCheck { 133 RunQueryWithContext(t, e, harness, nil, assertion.Query) 134 } else if assertion.CheckIndexedAccess { 135 TestQueryWithIndexCheck(t, ctx, e, harness, assertion.Query, assertion.Expected, assertion.ExpectedColumns, assertion.Bindings) 136 } else { 137 var expected = assertion.Expected 138 if IsServerEngine(e) && assertion.SkipResultCheckOnServerEngine { 139 // TODO: remove this check in the future 140 expected = nil 141 } 142 TestQueryWithContext(t, ctx, e, harness, assertion.Query, expected, assertion.ExpectedColumns, assertion.Bindings) 143 } 144 if assertion.ExpectedIndexes != nil && !IsServerEngine(e) { 145 evalIndexTest(t, harness, e, assertion.Query, assertion.ExpectedIndexes, assertion.Skip) 146 } 147 if assertion.JoinTypes != nil && !IsServerEngine(e) { 148 evalJoinTypeTest(t, harness, e, assertion.Query, assertion.JoinTypes, assertion.Skip) 149 } 150 }) 151 } 152 }) 153 } 154 155 // TestScriptPrepared substitutes literals for bindvars, runs the test script given, 156 // and makes any assertions given 157 func TestScriptPrepared(t *testing.T, harness Harness, script queries.ScriptTest) bool { 158 return t.Run(script.Name, func(t *testing.T) { 159 if script.SkipPrepared { 160 t.Skip() 161 } 162 163 e := mustNewEngine(t, harness) 164 defer e.Close() 165 TestScriptWithEnginePrepared(t, e, harness, script) 166 }) 167 } 168 169 // TestScriptWithEnginePrepared runs the test script with bindvars substituted for literals 170 // using the engine provided. 171 func TestScriptWithEnginePrepared(t *testing.T, e QueryEngine, harness Harness, script queries.ScriptTest) { 172 ctx := NewContext(harness) 173 err := CreateNewConnectionForServerEngine(ctx, e) 174 require.NoError(t, err, nil) 175 176 for _, statement := range script.SetUpScript { 177 if sh, ok := harness.(SkippingHarness); ok { 178 if sh.SkipQueryTest(statement) { 179 t.Skip() 180 } 181 } 182 ctx = NewContext(harness).WithQuery(statement) 183 RunQueryWithContext(t, e, harness, ctx, statement) 184 validateEngine(t, ctx, harness, e) 185 } 186 187 assertions := script.Assertions 188 if len(assertions) == 0 { 189 assertions = []queries.ScriptTestAssertion{ 190 { 191 Query: script.Query, 192 Expected: script.Expected, 193 ExpectedErr: script.ExpectedErr, 194 ExpectedIndexes: script.ExpectedIndexes, 195 }, 196 } 197 } 198 199 for _, assertion := range assertions { 200 t.Run(assertion.Query, func(t *testing.T) { 201 202 if sh, ok := harness.(SkippingHarness); ok { 203 if sh.SkipQueryTest(assertion.Query) { 204 t.Skip() 205 } 206 } 207 if assertion.Skip { 208 t.Skip() 209 } 210 211 if assertion.NewSession { 212 th, ok := harness.(TransactionHarness) 213 require.True(t, ok, "ScriptTestAssertion requested a NewSession, "+ 214 "but harness doesn't implement TransactionHarness") 215 ctx = th.NewSession() 216 } 217 if assertion.ExpectedErr != nil { 218 AssertErrPreparedWithCtx(t, e, harness, ctx, assertion.Query, assertion.ExpectedErr) 219 } else if assertion.ExpectedErrStr != "" { 220 AssertErrPreparedWithCtx(t, e, harness, ctx, assertion.Query, nil, assertion.ExpectedErrStr) 221 } else if assertion.ExpectedWarning != 0 { 222 AssertWarningAndTestQuery(t, e, nil, harness, assertion.Query, 223 assertion.Expected, nil, assertion.ExpectedWarning, assertion.ExpectedWarningsCount, 224 assertion.ExpectedWarningMessageSubstring, assertion.SkipResultsCheck) 225 } else if assertion.SkipResultsCheck { 226 ctx = NewContext(harness).WithQuery(assertion.Query) 227 _, _, err := runQueryPreparedWithCtx(t, ctx, e, assertion.Query, assertion.Bindings, false) 228 require.NoError(t, err) 229 } else { 230 ctx = NewContext(harness).WithQuery(assertion.Query) 231 TestPreparedQueryWithContext(t, ctx, e, harness, assertion.Query, assertion.Expected, nil, assertion.Bindings, assertion.CheckIndexedAccess) 232 } 233 if assertion.ExpectedIndexes != nil { 234 evalIndexTest(t, harness, e, assertion.Query, assertion.ExpectedIndexes, assertion.Skip) 235 } 236 }) 237 } 238 } 239 240 // TestTransactionScript runs the test script given, making any assertions given 241 func TestTransactionScript(t *testing.T, harness Harness, script queries.TransactionTest) bool { 242 // todo(max): these use dolt_commit, need harness reset to reset back to original commit 243 return t.Run(script.Name, func(t *testing.T) { 244 harness.Setup(setup.MydbData) 245 e := mustNewEngine(t, harness) 246 defer e.Close() 247 TestTransactionScriptWithEngine(t, e, harness, script) 248 }) 249 } 250 251 // TestTransactionScriptWithEngine runs the transaction test script given with the engine provided. 252 func TestTransactionScriptWithEngine(t *testing.T, e QueryEngine, harness Harness, script queries.TransactionTest) { 253 setupSession := NewSession(harness) 254 for _, statement := range script.SetUpScript { 255 RunQueryWithContext(t, e, harness, setupSession, statement) 256 } 257 258 clientSessions := make(map[string]*sql.Context) 259 assertions := script.Assertions 260 261 for _, assertion := range assertions { 262 client := getClient(assertion.Query) 263 264 clientSession, ok := clientSessions[client] 265 if !ok { 266 clientSession = NewSession(harness) 267 clientSessions[client] = clientSession 268 } 269 270 t.Run(assertion.Query, func(t *testing.T) { 271 if assertion.ExpectedErr != nil { 272 AssertErrWithCtx(t, e, harness, clientSession, assertion.Query, assertion.ExpectedErr) 273 } else if assertion.ExpectedErrStr != "" { 274 AssertErrWithCtx(t, e, harness, clientSession, assertion.Query, nil, assertion.ExpectedErrStr) 275 } else if assertion.ExpectedWarning != 0 { 276 AssertWarningAndTestQuery(t, e, nil, harness, assertion.Query, assertion.Expected, 277 nil, assertion.ExpectedWarning, assertion.ExpectedWarningsCount, 278 assertion.ExpectedWarningMessageSubstring, false) 279 } else if assertion.SkipResultsCheck { 280 RunQueryWithContext(t, e, harness, clientSession, assertion.Query) 281 } else { 282 TestQueryWithContext(t, clientSession, e, harness, assertion.Query, assertion.Expected, nil, nil) 283 } 284 }) 285 } 286 } 287 288 // TestQuery runs a query on the engine given and asserts that results are as expected. 289 // TODO: this should take en engine 290 func TestQuery(t *testing.T, harness Harness, q string, expected []sql.Row, expectedCols []*sql.Column, bindings map[string]*querypb.BindVariable) { 291 t.Run(q, func(t *testing.T) { 292 if sh, ok := harness.(SkippingHarness); ok { 293 if sh.SkipQueryTest(q) { 294 t.Skipf("Skipping query %s", q) 295 } 296 } 297 298 e := mustNewEngine(t, harness) 299 defer e.Close() 300 ctx := NewContext(harness) 301 TestQueryWithContext(t, ctx, e, harness, q, expected, expectedCols, bindings) 302 }) 303 } 304 305 // TestQuery runs a query on the engine given and asserts that results are as expected. 306 func TestQuery2(t *testing.T, harness Harness, e QueryEngine, q string, expected []sql.Row, expectedCols []*sql.Column, bindings map[string]*querypb.BindVariable) { 307 t.Run(q, func(t *testing.T) { 308 if sh, ok := harness.(SkippingHarness); ok { 309 if sh.SkipQueryTest(q) { 310 t.Skipf("Skipping query %s", q) 311 } 312 } 313 314 ctx := NewContext(harness) 315 TestQueryWithContext(t, ctx, e, harness, q, expected, expectedCols, bindings) 316 }) 317 } 318 319 // TODO: collapse into TestQuery 320 func TestQueryWithEngine(t *testing.T, harness Harness, e QueryEngine, tt queries.QueryTest) { 321 t.Run(tt.Query, func(t *testing.T) { 322 if sh, ok := harness.(SkippingHarness); ok { 323 if sh.SkipQueryTest(tt.Query) { 324 t.Skipf("Skipping query %s", tt.Query) 325 } 326 } 327 328 ctx := NewContext(harness) 329 TestQueryWithContext(t, ctx, e, harness, tt.Query, tt.Expected, tt.ExpectedColumns, tt.Bindings) 330 }) 331 } 332 333 func TestQueryWithContext(t *testing.T, ctx *sql.Context, e QueryEngine, harness Harness, q string, expected []sql.Row, expectedCols []*sql.Column, bindings map[string]*querypb.BindVariable) { 334 ctx = ctx.WithQuery(q) 335 require := require.New(t) 336 if len(bindings) > 0 { 337 _, err := e.PrepareQuery(ctx, q) 338 require.NoError(err) 339 } 340 341 sch, iter, err := e.QueryWithBindings(ctx, q, nil, bindings) 342 require.NoError(err, "Unexpected error for query %s: %s", q, err) 343 344 rows, err := sql.RowIterToRows(ctx, iter) 345 require.NoError(err, "Unexpected error for query %s: %s", q, err) 346 347 if expected != nil { 348 checkResults(t, expected, expectedCols, sch, rows, q, e) 349 } 350 351 require.Equal( 352 0, ctx.Memory.NumCaches()) 353 validateEngine(t, ctx, harness, e) 354 } 355 356 func GetFilterIndex(n sql.Node) sql.IndexLookup { 357 var lookup sql.IndexLookup 358 transform.InspectUp(n, func(n sql.Node) bool { 359 switch n := n.(type) { 360 case *plan.IndexedTableAccess: 361 lookup = plan.GetIndexLookup(n) 362 return true 363 default: 364 return false 365 } 366 }) 367 return lookup 368 } 369 370 func TestQueryWithIndexCheck(t *testing.T, ctx *sql.Context, e QueryEngine, harness Harness, q string, expected []sql.Row, expectedCols []*sql.Column, bindings map[string]*querypb.BindVariable) { 371 ctx = ctx.WithQuery(q) 372 require := require.New(t) 373 if len(bindings) > 0 { 374 _, err := e.PrepareQuery(ctx, q) 375 require.NoError(err) 376 } 377 378 if !IsServerEngine(e) { 379 node, err := e.AnalyzeQuery(ctx, q) 380 require.NoError(err, "Unexpected error for query %s: %s", q, err) 381 require.True(CheckIndexedAccess(node), "expected plan to have index, but found: %s", sql.DebugString(node)) 382 } 383 384 sch, iter, err := e.QueryWithBindings(ctx, q, nil, bindings) 385 require.NoError(err, "Unexpected error for query %s: %s", q, err) 386 387 rows, err := sql.RowIterToRows(ctx, iter) 388 require.NoError(err, "Unexpected error for query %s: %s", q, err) 389 390 if expected != nil { 391 checkResults(t, expected, expectedCols, sch, rows, q, e) 392 } 393 394 require.Equal( 395 0, ctx.Memory.NumCaches()) 396 validateEngine(t, ctx, harness, e) 397 } 398 399 func CheckIndexedAccess(n sql.Node) bool { 400 var hasIndex bool 401 transform.Inspect(n, func(n sql.Node) bool { 402 if n == nil { 403 return false 404 } 405 if _, ok := n.(*plan.IndexedTableAccess); ok { 406 hasIndex = true 407 } 408 return true 409 }) 410 return hasIndex 411 } 412 413 // TestPreparedQuery runs a prepared query on the engine given and asserts that results are as expected. 414 func TestPreparedQuery(t *testing.T, harness Harness, q string, expected []sql.Row, expectedCols []*sql.Column) { 415 t.Run(q, func(t *testing.T) { 416 if sh, ok := harness.(SkippingHarness); ok { 417 if sh.SkipQueryTest(q) { 418 t.Skipf("Skipping query %s", q) 419 } 420 } 421 e := mustNewEngine(t, harness) 422 defer e.Close() 423 ctx := NewContext(harness) 424 TestPreparedQueryWithContext(t, ctx, e, harness, q, expected, expectedCols, nil, false) 425 }) 426 } 427 428 func TestPreparedQueryWithEngine(t *testing.T, harness Harness, e QueryEngine, tt queries.QueryTest) { 429 t.Run(tt.Query, func(t *testing.T) { 430 if sh, ok := harness.(SkippingHarness); ok { 431 if sh.SkipQueryTest(tt.Query) { 432 t.Skipf("Skipping query %s", tt.Query) 433 } 434 } 435 ctx := NewContext(harness) 436 TestPreparedQueryWithContext(t, ctx, e, harness, tt.Query, tt.Expected, tt.ExpectedColumns, nil, false) 437 }) 438 } 439 440 func TestPreparedQueryWithContext(t *testing.T, ctx *sql.Context, e QueryEngine, h Harness, q string, expected []sql.Row, expectedCols []*sql.Column, bindVars map[string]*querypb.BindVariable, checkIndexedAccess bool) { 441 require := require.New(t) 442 rows, sch, err := runQueryPreparedWithCtx(t, ctx, e, q, bindVars, false) 443 if err != nil { 444 print(q) 445 } 446 require.NoError(err, "Unexpected error for query %s", q) 447 448 if expected != nil { 449 // TODO fix expected cols for prepared? 450 checkResults(t, expected, nil, sch, rows, q, e) 451 } 452 453 require.Equal(0, ctx.Memory.NumCaches()) 454 validateEngine(t, ctx, h, e) 455 } 456 457 func injectBindVarsAndPrepare( 458 t *testing.T, 459 ctx *sql.Context, 460 e QueryEngine, 461 q string, 462 ) (string, map[string]*querypb.BindVariable, error) { 463 sqlMode := sql.LoadSqlMode(ctx) 464 parsed, err := sqlparser.ParseWithOptions(q, sqlMode.ParserOptions()) 465 if err != nil { 466 // cannot prepare empty statement, can query 467 if err.Error() == "empty statement" { 468 return q, nil, nil 469 } 470 return q, nil, sql.ErrSyntaxError.New(err) 471 } 472 473 switch p := parsed.(type) { 474 case *sqlparser.Load, *sqlparser.Prepare, *sqlparser.Execute: 475 // LOAD DATA query cannot be used as PREPARED STATEMENT 476 return q, nil, nil 477 case *sqlparser.Set: 478 // SET system variable query cannot be used as PREPARED STATEMENT 479 for _, setVar := range p.Exprs { 480 if setVar.Scope != sqlparser.SetScope_User { 481 return q, nil, nil 482 } 483 } 484 } 485 486 resPlan, err := planbuilder.ParseWithOptions(ctx, e.EngineAnalyzer().Catalog, q, sqlMode.ParserOptions()) 487 if err != nil { 488 return q, nil, err 489 } 490 491 b := planbuilder.New(ctx, sql.MapCatalog{}) 492 _, isInsert := resPlan.(*plan.InsertInto) 493 bindVars := make(map[string]*querypb.BindVariable) 494 var bindCnt int 495 var foundBindVar bool 496 var skipTypeConv bool 497 err = sqlparser.Walk(func(n sqlparser.SQLNode) (kontinue bool, err error) { 498 switch n := n.(type) { 499 case *sqlparser.SQLVal: 500 if n == nil { 501 return false, nil 502 } 503 switch n.Type { 504 case sqlparser.HexNum, sqlparser.HexVal: 505 return false, nil 506 } 507 expr := b.ConvertVal(n) 508 var val interface{} 509 if l, ok := expr.(*expression.Literal); ok { 510 val, _, err = expr.Type().Promote().Convert(l.Value()) 511 if err != nil { 512 skipTypeConv = true 513 return false, nil 514 } 515 } else { 516 // If the |expr| is not Literal, then |val| is nil 517 skipTypeConv = true 518 return false, nil 519 } 520 521 bindVar, err := sqltypes.BuildBindVariable(val) 522 if err != nil { 523 skipTypeConv = true 524 return false, nil 525 } 526 varName := fmt.Sprintf("v%d", bindCnt+1) 527 bindVars[varName] = bindVar 528 n.Type = sqlparser.ValArg 529 n.Val = []byte(fmt.Sprintf(":v%d", bindCnt+1)) 530 bindCnt++ 531 case *sqlparser.Insert: 532 isInsert = true 533 default: 534 } 535 return true, nil 536 }, parsed) 537 if err != nil { 538 return "", nil, err 539 } 540 if skipTypeConv { 541 return q, nil, nil 542 } 543 544 buf := sqlparser.NewTrackedBuffer(nil) 545 parsed.Format(buf) 546 e.EnginePreparedDataCache().CacheStmt(ctx.Session.ID(), buf.String(), parsed) 547 548 _, isDatabaser := resPlan.(sql.Databaser) 549 550 // *ast.MultiAlterDDL parses arbitrary nodes in a *plan.Block 551 if bl, ok := resPlan.(*plan.Block); ok { 552 for _, n := range bl.Children() { 553 if _, ok := n.(*plan.InsertInto); ok { 554 isInsert = true 555 } else if _, ok := n.(sql.Databaser); ok { 556 isDatabaser = true 557 } 558 559 } 560 } 561 if isDatabaser && !isInsert { 562 return q, nil, nil 563 } 564 565 if foundBindVar { 566 t.Skip() 567 } 568 569 return buf.String(), bindVars, nil 570 } 571 572 func runQueryPreparedWithCtx(t *testing.T, ctx *sql.Context, e QueryEngine, q string, bindVars map[string]*querypb.BindVariable, checkIndexedAccess bool) ([]sql.Row, sql.Schema, error) { 573 // If bindvars were not provided, try to inject some 574 if bindVars == nil || len(bindVars) == 0 { 575 var err error 576 q, bindVars, err = injectBindVarsAndPrepare(t, ctx, e, q) 577 if err != nil { 578 return nil, nil, err 579 } 580 } 581 582 if checkIndexedAccess { 583 n, err := e.AnalyzeQuery(ctx, q) 584 require.NoError(t, err) 585 require.True(t, CheckIndexedAccess(n), "expected plan to have index, but found: %s", sql.DebugString(n)) 586 } 587 588 sch, iter, err := e.QueryWithBindings(ctx, q, nil, bindVars) 589 if err != nil { 590 return nil, nil, err 591 } 592 593 rows, err := sql.RowIterToRows(ctx, iter) 594 return rows, sch, err 595 } 596 597 // CustomValueValidator is an interface for custom validation of values in the result set 598 type CustomValueValidator interface { 599 Validate(interface{}) (bool, error) 600 } 601 602 // toSQL converts the given expected value into appropriate type of given column. 603 // |isZeroTime| is true if the query is any `SHOW` statement, except for `SHOW EVENTS`. 604 // This is set earlier in `checkResult()` method. 605 func toSQL(c *sql.Column, expected any, isZeroTime bool) (any, error) { 606 _, isTime := expected.(time.Time) 607 _, isStr := expected.(string) 608 // cases where we don't want the result value to be converted 609 if expected == nil || types.IsDecimal(c.Type) || types.IsEnum(c.Type) || types.IsSet(c.Type) || 610 c.Type.Type() == sqltypes.Year || (isTime && isZeroTime) || (isStr && types.IsTextOnly(c.Type)) { 611 return expected, nil 612 } else { 613 val, _, err := c.Type.Convert(expected) 614 return val, err 615 } 616 } 617 618 func checkResults( 619 t *testing.T, 620 expected []sql.Row, 621 expectedCols []*sql.Column, 622 sch sql.Schema, 623 rows []sql.Row, 624 q string, 625 e QueryEngine, 626 ) { 627 widenedRows := WidenRows(sch, rows) 628 widenedExpected := WidenRows(sch, expected) 629 630 upperQuery := strings.ToUpper(q) 631 orderBy := strings.Contains(upperQuery, "ORDER BY ") 632 633 isServerEngine := IsServerEngine(e) 634 isNilOrEmptySchema := sch == nil || len(sch) == 0 635 // We replace all times for SHOW statements with the Unix epoch except for SHOW EVENTS 636 setZeroTime := strings.HasPrefix(upperQuery, "SHOW ") && !strings.Contains(upperQuery, "EVENTS") 637 638 for _, widenedRow := range widenedRows { 639 for i, val := range widenedRow { 640 switch v := val.(type) { 641 case time.Time: 642 if setZeroTime { 643 widenedRow[i] = time.Unix(0, 0).UTC() 644 } 645 case uint64: 646 // index value of enum, in uint16, and bit value of set, in uint64, are cast/widened to uint64. 647 if !isServerEngine && !isNilOrEmptySchema { 648 // index value for enum and bit value for set types returned 649 // from enginetests need conversion to its string type value. 650 if types.IsEnum(sch[i].Type) { 651 el, exists := sch[i].Type.(sql.EnumType).At(int(v)) 652 if !exists { 653 t.Errorf("Enum type element does not exist at index: %v", v) 654 } 655 widenedRow[i] = el 656 } else if types.IsSet(sch[i].Type) { 657 el, err := sch[i].Type.(sql.SetType).BitsToString(v) 658 require.NoError(t, err) 659 widenedRow[i] = el 660 } 661 } 662 } 663 } 664 } 665 666 // if the sch is nil or empty, over the wire result is no row whereas single empty row is expected. 667 // This happens for SET and SELECT INTO statements. 668 if isServerEngine && isNilOrEmptySchema && len(widenedRows) == 0 && len(widenedExpected) == 1 && len(widenedExpected[0]) == 0 { 669 widenedExpected = widenedRows 670 } 671 672 // The expected results that need conversion before checking against actual results. 673 for i, row := range widenedExpected { 674 for j, field := range row { 675 // Special case for custom values 676 if cvv, isCustom := field.(CustomValueValidator); isCustom { 677 if i >= len(widenedRows) { 678 continue 679 } 680 actual := widenedRows[i][j] // shouldn't panic, but fine if it does 681 ok, err := cvv.Validate(actual) 682 if err != nil { 683 t.Error(err.Error()) 684 } 685 if !ok { 686 t.Errorf("Custom value validation, got %v", actual) 687 } 688 widenedExpected[i][j] = actual // ensure it passes equality check later 689 } 690 691 if !isServerEngine || isNilOrEmptySchema { 692 continue 693 } 694 695 // The result received from go sql driver does not have 'Info' 696 // data returned, so we set it to 'nil' for server engine tests only. 697 if okRes, ok := widenedExpected[i][j].(types.OkResult); ok { 698 okResult := types.OkResult{ 699 RowsAffected: okRes.RowsAffected, 700 InsertID: okRes.InsertID, 701 Info: nil, 702 } 703 widenedExpected[i][j] = okResult 704 } else { 705 // this attempts to do what `rowToSQL()` method in `handler.go` on expected row 706 // because over the wire values gets converted to SQL values depending on the column types. 707 convertedExpected, err := toSQL(sch[j], widenedExpected[i][j], setZeroTime) 708 require.NoError(t, err) 709 widenedExpected[i][j] = convertedExpected 710 } 711 } 712 } 713 714 // .Equal gives better error messages than .ElementsMatch, so use it when possible 715 if orderBy || len(expected) <= 1 { 716 require.Equal(t, widenedExpected, widenedRows, "Unexpected result for query %s", q) 717 } else { 718 require.ElementsMatch(t, widenedExpected, widenedRows, "Unexpected result for query %s", q) 719 } 720 721 // If the expected schema was given, test it as well 722 if expectedCols != nil && !isServerEngine { 723 assert.Equal(t, simplifyResultSchema(expectedCols), simplifyResultSchema(sch)) 724 } 725 } 726 727 type resultSchemaCol struct { 728 Name string 729 Type querypb.Type 730 } 731 732 func simplifyResultSchema(s sql.Schema) []resultSchemaCol { 733 fields := make([]resultSchemaCol, len(s)) 734 for i, c := range s { 735 fields[i] = resultSchemaCol{ 736 Name: c.Name, 737 Type: c.Type.Type(), 738 } 739 } 740 return fields 741 } 742 743 // WidenRows returns a slice of rows with all values widened to their widest type. 744 // For a variety of reasons, the widths of various primitive types can vary when passed through different SQL queries 745 // (and different database implementations). We may eventually decide that this undefined behavior is a problem, but 746 // for now it's mostly just an issue when comparing results in tests. To get around this, we widen every type to its 747 // widest value in actual and expected results. 748 func WidenRows(sch sql.Schema, rows []sql.Row) []sql.Row { 749 widened := make([]sql.Row, len(rows)) 750 for i, row := range rows { 751 widened[i] = WidenRow(sch, row) 752 } 753 return widened 754 } 755 756 // WidenRow returns a row with all values widened to their widest type 757 func WidenRow(sch sql.Schema, row sql.Row) sql.Row { 758 widened := make(sql.Row, len(row)) 759 for i, v := range row { 760 761 var vw interface{} 762 if i < len(sch) && types.IsJSON(sch[i].Type) { 763 widened[i] = widenJSONValues(v) 764 continue 765 } 766 767 switch x := v.(type) { 768 case int: 769 vw = int64(x) 770 case int8: 771 vw = int64(x) 772 case int16: 773 vw = int64(x) 774 case int32: 775 vw = int64(x) 776 case uint: 777 vw = uint64(x) 778 case uint8: 779 vw = uint64(x) 780 case uint16: 781 vw = uint64(x) 782 case uint32: 783 vw = uint64(x) 784 case float32: 785 // casting it to float64 causes approximation, which doesn't work for server engine results. 786 vw, _ = strconv.ParseFloat(fmt.Sprintf("%v", v), 64) 787 case decimal.Decimal: 788 // The exact expected decimal type value cannot be defined in enginetests, 789 // so convert the result to string format, which is the value we get on sql shell. 790 vw = x.StringFixed(x.Exponent() * -1) 791 default: 792 vw = v 793 } 794 widened[i] = vw 795 } 796 return widened 797 } 798 799 func widenJSONValues(val interface{}) sql.JSONWrapper { 800 if val == nil { 801 return nil 802 } 803 804 js, ok := val.(sql.JSONWrapper) 805 if !ok { 806 str, ok := val.(string) 807 if !ok { 808 panic(fmt.Sprintf("%v is not json", val)) 809 } 810 js = types.MustJSON(str) 811 } 812 813 doc := js.ToInterface() 814 815 if _, ok := js.(sql.Statistic); ok { 816 // avoid comparing time values in statistics 817 delete(doc.(map[string]interface{})["statistic"].(map[string]interface{}), "created_at") 818 } 819 820 doc = widenJSON(doc) 821 return types.JSONDocument{Val: doc} 822 } 823 824 func widenJSON(val interface{}) interface{} { 825 switch x := val.(type) { 826 case int: 827 return float64(x) 828 case int8: 829 return float64(x) 830 case int16: 831 return float64(x) 832 case int32: 833 return float64(x) 834 case int64: 835 return float64(x) 836 case uint: 837 return float64(x) 838 case uint8: 839 return float64(x) 840 case uint16: 841 return float64(x) 842 case uint32: 843 return float64(x) 844 case uint64: 845 return float64(x) 846 case float32: 847 return float64(x) 848 case []interface{}: 849 return widenJSONArray(x) 850 case map[string]interface{}: 851 return widenJSONObject(x) 852 default: 853 return x 854 } 855 } 856 857 func widenJSONObject(narrow map[string]interface{}) (wide map[string]interface{}) { 858 wide = make(map[string]interface{}, len(narrow)) 859 for k, v := range narrow { 860 wide[k] = widenJSON(v) 861 } 862 return 863 } 864 865 func widenJSONArray(narrow []interface{}) (wide []interface{}) { 866 wide = make([]interface{}, len(narrow)) 867 for i, v := range narrow { 868 wide[i] = widenJSON(v) 869 } 870 return 871 } 872 873 // AssertErr asserts that the given query returns an error during its execution, optionally specifying a type of error. 874 func AssertErr(t *testing.T, e QueryEngine, harness Harness, query string, expectedErrKind *errors.Kind, errStrs ...string) { 875 AssertErrWithCtx(t, e, harness, NewContext(harness), query, expectedErrKind, errStrs...) 876 } 877 878 // AssertErrWithBindings asserts that the given query returns an error during its execution, optionally specifying a 879 // type of error. 880 func AssertErrWithBindings(t *testing.T, e QueryEngine, harness Harness, query string, bindings map[string]*querypb.BindVariable, expectedErrKind *errors.Kind, errStrs ...string) { 881 ctx := NewContext(harness) 882 _, iter, err := e.QueryWithBindings(ctx, query, nil, bindings) 883 if err == nil { 884 _, err = sql.RowIterToRows(ctx, iter) 885 } 886 require.Error(t, err) 887 if expectedErrKind != nil { 888 if !IsServerEngine(e) { 889 require.True(t, expectedErrKind.Is(err), "Expected error of type %s but got %s", expectedErrKind, err) 890 } 891 } else if len(errStrs) >= 1 { 892 require.Equal(t, errStrs[0], err.Error()) 893 } 894 validateEngine(t, ctx, harness, e) 895 } 896 897 // AssertErrWithCtx is the same as AssertErr, but uses the context given instead of creating one from a harness 898 func AssertErrWithCtx(t *testing.T, e QueryEngine, harness Harness, ctx *sql.Context, query string, expectedErrKind *errors.Kind, errStrs ...string) { 899 ctx = ctx.WithQuery(query) 900 _, iter, err := e.Query(ctx, query) 901 if err == nil { 902 _, err = sql.RowIterToRows(ctx, iter) 903 } 904 require.Error(t, err) 905 if expectedErrKind != nil { 906 err = sql.UnwrapError(err) 907 if !IsServerEngine(e) { 908 require.True(t, expectedErrKind.Is(err), "Expected error of type %s but got %s", expectedErrKind, err) 909 } 910 } 911 // If there are multiple error strings then we only match against the first 912 if len(errStrs) >= 1 { 913 require.Equal(t, errStrs[0], err.Error()) 914 } 915 validateEngine(t, ctx, harness, e) 916 } 917 918 // AssertErrPrepared asserts that the given query returns an error during its execution, optionally specifying a type of error. 919 func AssertErrPrepared(t *testing.T, e QueryEngine, harness Harness, query string, expectedErrKind *errors.Kind, errStrs ...string) { 920 AssertErrPreparedWithCtx(t, e, harness, NewContext(harness), query, expectedErrKind, errStrs...) 921 } 922 923 // AssertErrPreparedWithCtx is the same as AssertErr, but uses the context given instead of creating one from a harness 924 func AssertErrPreparedWithCtx(t *testing.T, e QueryEngine, harness Harness, ctx *sql.Context, query string, expectedErrKind *errors.Kind, errStrs ...string) { 925 ctx = ctx.WithQuery(query) 926 _, _, err := runQueryPreparedWithCtx(t, ctx, e, query, nil, false) 927 require.Error(t, err) 928 if expectedErrKind != nil { 929 err = sql.UnwrapError(err) 930 if !IsServerEngine(e) { 931 require.True(t, expectedErrKind.Is(err), "Expected error of type %s but got %s", expectedErrKind, err) 932 } 933 } 934 // If there are multiple error strings then we only match against the first 935 if len(errStrs) >= 1 { 936 require.Equal(t, errStrs[0], err.Error()) 937 } 938 validateEngine(t, ctx, harness, e) 939 } 940 941 // AssertWarningAndTestQuery tests the query and asserts an expected warning code. If |ctx| is provided, it will be 942 // used. Otherwise the harness will be used to create a fresh context. 943 func AssertWarningAndTestQuery( 944 t *testing.T, 945 e QueryEngine, 946 ctx *sql.Context, 947 harness Harness, 948 query string, 949 expected []sql.Row, 950 expectedCols []*sql.Column, 951 expectedCode int, 952 expectedWarningsCount int, 953 expectedWarningMessageSubstring string, 954 skipResultsCheck bool, 955 ) { 956 require := require.New(t) 957 if ctx == nil { 958 ctx = NewContext(harness) 959 } 960 ctx.ClearWarnings() 961 ctx = ctx.WithQuery(query) 962 963 sch, iter, err := e.Query(ctx, query) 964 require.NoError(err, "Unexpected error for query %s", query) 965 966 rows, err := sql.RowIterToRows(ctx, iter) 967 require.NoError(err, "Unexpected error for query %s", query) 968 969 if !IsServerEngine(e) { 970 // check warnings depend on context, which ServerEngine does not depend on 971 if expectedWarningsCount > 0 { 972 assert.Equal(t, expectedWarningsCount, len(ctx.Warnings())) 973 } 974 975 if expectedCode > 0 { 976 for _, warning := range ctx.Warnings() { 977 assert.Equal(t, expectedCode, warning.Code, "Unexpected warning code") 978 } 979 } 980 981 if len(expectedWarningMessageSubstring) > 0 { 982 for _, warning := range ctx.Warnings() { 983 assert.Contains(t, warning.Message, expectedWarningMessageSubstring, "Unexpected warning message") 984 } 985 } 986 } 987 988 if !skipResultsCheck { 989 checkResults(t, expected, expectedCols, sch, rows, query, e) 990 } 991 validateEngine(t, ctx, harness, e) 992 } 993 994 func assertSchemasEqualWithDefaults(t *testing.T, expected, actual sql.Schema) bool { 995 if len(expected) != len(actual) { 996 return assert.Equal(t, expected, actual) 997 } 998 999 ec, ac := make(sql.Schema, len(expected)), make(sql.Schema, len(actual)) 1000 for i := range expected { 1001 ecc := *expected[i] 1002 acc := *actual[i] 1003 1004 ecc.Default = nil 1005 acc.Default = nil 1006 1007 ac[i] = &acc 1008 ec[i] = &ecc 1009 1010 // For the default, compare just the string representations. This makes it possible for integrators who don't reify 1011 // default value expressions at schema load time (best practice) to run these tests. We also trim off any parens 1012 // for the same reason. 1013 eds, ads := "NULL", "NULL" 1014 if expected[i].Default != nil { 1015 eds = strings.Trim(expected[i].Default.String(), "()") 1016 } 1017 if actual[i].Default != nil { 1018 ads = strings.Trim(actual[i].Default.String(), "()") 1019 } 1020 1021 assert.Equal(t, eds, ads, "column default values differ") 1022 } 1023 1024 return assert.Equal(t, ec, ac) 1025 } 1026 1027 func ExtractQueryNode(node sql.Node) sql.Node { 1028 switch node := node.(type) { 1029 case *plan.QueryProcess: 1030 return ExtractQueryNode(node.Child()) 1031 case *plan.Releaser: 1032 return ExtractQueryNode(node.Child) 1033 default: 1034 return node 1035 } 1036 } 1037 1038 // RunWriteQueryTest runs the specified |tt| WriteQueryTest using the specified harness. 1039 func RunWriteQueryTest(t *testing.T, harness Harness, tt queries.WriteQueryTest) { 1040 e := mustNewEngine(t, harness) 1041 defer e.Close() 1042 RunWriteQueryTestWithEngine(t, harness, e, tt) 1043 } 1044 1045 // RunWriteQueryTestWithEngine runs the specified |tt| WriteQueryTest, using the specified harness and engine. Callers 1046 // are still responsible for closing the engine. 1047 func RunWriteQueryTestWithEngine(t *testing.T, harness Harness, e QueryEngine, tt queries.WriteQueryTest) { 1048 t.Run(tt.WriteQuery, func(t *testing.T) { 1049 if sh, ok := harness.(SkippingHarness); ok { 1050 if sh.SkipQueryTest(tt.WriteQuery) { 1051 t.Logf("Skipping query %s", tt.WriteQuery) 1052 return 1053 } 1054 if sh.SkipQueryTest(tt.SelectQuery) { 1055 t.Logf("Skipping query %s", tt.SelectQuery) 1056 return 1057 } 1058 } 1059 ctx := NewContext(harness) 1060 TestQueryWithContext(t, ctx, e, harness, tt.WriteQuery, tt.ExpectedWriteResult, nil, nil) 1061 expectedSelect := tt.ExpectedSelect 1062 if IsServerEngine(e) && tt.SkipServerEngine { 1063 expectedSelect = nil 1064 } 1065 TestQueryWithContext(t, ctx, e, harness, tt.SelectQuery, expectedSelect, nil, nil) 1066 }) 1067 } 1068 1069 func runWriteQueryTestPrepared(t *testing.T, harness Harness, tt queries.WriteQueryTest) { 1070 t.Run(tt.WriteQuery, func(t *testing.T) { 1071 if sh, ok := harness.(SkippingHarness); ok { 1072 if sh.SkipQueryTest(tt.WriteQuery) { 1073 t.Logf("Skipping query %s", tt.WriteQuery) 1074 return 1075 } 1076 if sh.SkipQueryTest(tt.SelectQuery) { 1077 t.Logf("Skipping query %s", tt.SelectQuery) 1078 return 1079 } 1080 } 1081 e := mustNewEngine(t, harness) 1082 defer e.Close() 1083 ctx := NewContext(harness) 1084 TestPreparedQueryWithContext(t, ctx, e, harness, tt.WriteQuery, tt.ExpectedWriteResult, nil, tt.Bindings, false) 1085 TestPreparedQueryWithContext(t, ctx, e, harness, tt.SelectQuery, tt.ExpectedSelect, nil, tt.Bindings, false) 1086 }) 1087 } 1088 1089 func runGenericErrorTest(t *testing.T, h Harness, tt queries.GenericErrorQueryTest) { 1090 t.Run(tt.Name, func(t *testing.T) { 1091 if sh, ok := h.(SkippingHarness); ok { 1092 if sh.SkipQueryTest(tt.Query) { 1093 t.Skipf("skipping query %s", tt.Query) 1094 } 1095 } 1096 e := mustNewEngine(t, h) 1097 defer e.Close() 1098 AssertErr(t, e, h, tt.Query, nil) 1099 }) 1100 } 1101 1102 func runQueryErrorTest(t *testing.T, h Harness, tt queries.QueryErrorTest) { 1103 t.Run(tt.Query, func(t *testing.T) { 1104 if sh, ok := h.(SkippingHarness); ok { 1105 if sh.SkipQueryTest(tt.Query) { 1106 t.Skipf("skipping query %s", tt.Query) 1107 } 1108 } 1109 e := mustNewEngine(t, h) 1110 defer e.Close() 1111 if tt.ExpectedErrStr == "" { 1112 AssertErr(t, e, h, tt.Query, tt.ExpectedErr) 1113 } else { 1114 AssertErr(t, e, h, tt.Query, tt.ExpectedErr, tt.ExpectedErrStr) 1115 } 1116 1117 }) 1118 } 1119 1120 func validateEngine(t *testing.T, ctx *sql.Context, harness Harness, e QueryEngine) { 1121 if harness == nil { 1122 assert.NotNil(t, harness) 1123 } 1124 require.NotNil(t, harness) 1125 if vh, ok := harness.(ValidatingHarness); ok { 1126 if sqlEng, ok := e.(*sqle.Engine); ok { 1127 assert.NoError(t, vh.ValidateEngine(ctx, sqlEng)) 1128 } 1129 } 1130 }