github.com/dolthub/go-mysql-server@v0.18.0/enginetest/engine_only_test.go (about) 1 // Copyright 2020-2021 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_test 16 17 import ( 18 "context" 19 sql2 "database/sql" 20 "fmt" 21 "io" 22 "net" 23 "runtime" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/dolthub/vitess/go/sqltypes" 29 "github.com/pmezard/go-difflib/difflib" 30 "github.com/stretchr/testify/assert" 31 "github.com/stretchr/testify/require" 32 "go.opentelemetry.io/otel/trace" 33 "gopkg.in/src-d/go-errors.v1" 34 35 sqle "github.com/dolthub/go-mysql-server" 36 "github.com/dolthub/go-mysql-server/enginetest" 37 "github.com/dolthub/go-mysql-server/enginetest/queries" 38 "github.com/dolthub/go-mysql-server/enginetest/scriptgen/setup" 39 "github.com/dolthub/go-mysql-server/memory" 40 "github.com/dolthub/go-mysql-server/server" 41 "github.com/dolthub/go-mysql-server/sql" 42 "github.com/dolthub/go-mysql-server/sql/analyzer" 43 "github.com/dolthub/go-mysql-server/sql/expression" 44 "github.com/dolthub/go-mysql-server/sql/expression/function" 45 "github.com/dolthub/go-mysql-server/sql/plan" 46 "github.com/dolthub/go-mysql-server/sql/planbuilder" 47 "github.com/dolthub/go-mysql-server/sql/rowexec" 48 "github.com/dolthub/go-mysql-server/sql/types" 49 ) 50 51 // This file is for tests of the engine that we are very sure do not rely on a particular database implementation. They 52 // use the default in-memory implementation harness, but in principle they do not rely on it being correct (beyond 53 // the ability to create databases and tables without panicking) and don't test the implementation itself. Despite this, 54 // most test methods dispatch to exported Test functions in the enginetest package, so that integrators can run those 55 // tests against their own implementations if they choose. 56 // 57 // Tests that rely on a correct implementation of the in-memory database (memory package) should go in 58 // memory_engine_test.go 59 60 func TestSessionSelectLimit(t *testing.T) { 61 enginetest.TestSessionSelectLimit(t, enginetest.NewDefaultMemoryHarness()) 62 } 63 64 func TestVariables(t *testing.T) { 65 enginetest.TestVariables(t, enginetest.NewDefaultMemoryHarness()) 66 } 67 68 func TestVariableErrors(t *testing.T) { 69 enginetest.TestVariableErrors(t, enginetest.NewDefaultMemoryHarness()) 70 } 71 72 func TestWarnings(t *testing.T) { 73 harness := enginetest.NewDefaultMemoryHarness() 74 if harness.IsUsingServer() { 75 t.Skip("tracking issue: https://github.com/dolthub/dolt/issues/6921 and the one mentioned inside this issue as well") 76 } 77 t.Run("sequential", func(t *testing.T) { 78 enginetest.TestWarnings(t, harness) 79 }) 80 81 harness = enginetest.NewMemoryHarness("parallel", 2, testNumPartitions, false, nil) 82 t.Run("parallel", func(t *testing.T) { 83 enginetest.TestWarnings(t, harness) 84 }) 85 } 86 87 func TestClearWarnings(t *testing.T) { 88 harness := enginetest.NewDefaultMemoryHarness() 89 if harness.IsUsingServer() { 90 // TODO: needs more investigation on this test 91 t.Skip("tracking issue: https://github.com/dolthub/dolt/issues/6921 and the one mentioned inside this issue as well") 92 } 93 enginetest.TestClearWarnings(t, harness) 94 } 95 96 func TestUse(t *testing.T) { 97 enginetest.TestUse(t, enginetest.NewDefaultMemoryHarness()) 98 } 99 100 func TestNoDatabaseSelected(t *testing.T) { 101 enginetest.TestNoDatabaseSelected(t, enginetest.NewDefaultMemoryHarness()) 102 } 103 104 func TestTracing(t *testing.T) { 105 harness := enginetest.NewDefaultMemoryHarness() 106 if harness.IsUsingServer() { 107 t.Skip("this test depends on Context, which ServerEngine does not depend on or update the current context") 108 } 109 enginetest.TestTracing(t, harness) 110 } 111 112 func TestCurrentTimestamp(t *testing.T) { 113 enginetest.TestCurrentTimestamp(t, enginetest.NewDefaultMemoryHarness()) 114 } 115 116 // TODO: it's not currently possible to test this via harness, because the underlying table implementations are added to 117 // the database, rather than the wrapper tables. We need a better way of inspecting lock state to test this properly. 118 // Also, currently locks are entirely implementation dependent, so there isn't much to test except that lock and unlock 119 // are being called. 120 func TestLocks(t *testing.T) { 121 require := require.New(t) 122 123 harness := enginetest.NewDefaultMemoryHarness() 124 db := harness.NewDatabases("db")[0].(*memory.HistoryDatabase) 125 t1 := newLockableTable(memory.NewTable(db.BaseDatabase, "t1", sql.PrimaryKeySchema{}, db.GetForeignKeyCollection())) 126 t2 := newLockableTable(memory.NewTable(db.BaseDatabase, "t2", sql.PrimaryKeySchema{}, db.GetForeignKeyCollection())) 127 t3 := memory.NewTable(db.BaseDatabase, "t3", sql.PrimaryKeySchema{}, db.GetForeignKeyCollection()) 128 db.AddTable("t1", t1) 129 db.AddTable("t2", t2) 130 db.AddTable("t3", t3) 131 132 analyzer := analyzer.NewDefault(harness.Provider()) 133 engine := sqle.New(analyzer, new(sqle.Config)) 134 135 ctx := enginetest.NewContext(harness) 136 ctx.SetCurrentDatabase("db") 137 _, iter, err := engine.Query(ctx, "LOCK TABLES t1 READ, t2 WRITE, t3 READ") 138 require.NoError(err) 139 140 _, err = sql.RowIterToRows(ctx, iter) 141 require.NoError(err) 142 143 ctx = enginetest.NewContext(harness) 144 ctx.SetCurrentDatabase("db") 145 _, iter, err = engine.Query(ctx, "UNLOCK TABLES") 146 require.NoError(err) 147 148 _, err = sql.RowIterToRows(ctx, iter) 149 require.NoError(err) 150 151 require.Equal(1, t1.readLocks) 152 require.Equal(0, t1.writeLocks) 153 require.Equal(1, t1.unlocks) 154 require.Equal(0, t2.readLocks) 155 require.Equal(1, t2.writeLocks) 156 require.Equal(1, t2.unlocks) 157 } 158 159 type mockSpan struct { 160 trace.Span 161 finished bool 162 } 163 164 func (m *mockSpan) End(options ...trace.SpanEndOption) { 165 m.finished = true 166 m.Span.End(options...) 167 } 168 169 func newMockSpan(ctx context.Context) (context.Context, *mockSpan) { 170 ctx, span := trace.NewNoopTracerProvider().Tracer("").Start(ctx, "") 171 return ctx, &mockSpan{span, false} 172 } 173 174 func TestRootSpanFinish(t *testing.T) { 175 harness := enginetest.NewDefaultMemoryHarness() 176 if harness.IsUsingServer() { 177 t.Skip("this test depends on Context, which ServerEngine does not depend on or update the current context") 178 } 179 e, err := harness.NewEngine(t) 180 if err != nil { 181 panic(err) 182 } 183 sqlCtx := harness.NewContext() 184 ctx, fakeSpan := newMockSpan(sqlCtx) 185 sql.WithRootSpan(fakeSpan)(sqlCtx) 186 sqlCtx = sqlCtx.WithContext(ctx) 187 188 _, iter, err := e.Query(sqlCtx, "SELECT 1") 189 require.NoError(t, err) 190 191 _, err = sql.RowIterToRows(sqlCtx, iter) 192 require.NoError(t, err) 193 194 require.True(t, fakeSpan.finished) 195 } 196 197 type lockableTable struct { 198 sql.Table 199 readLocks int 200 writeLocks int 201 unlocks int 202 } 203 204 func (l *lockableTable) IgnoreSessionData() bool { 205 return true 206 } 207 208 func (l *lockableTable) UnderlyingTable() *memory.Table { 209 return l.Table.(*memory.Table) 210 } 211 212 func newLockableTable(t sql.Table) *lockableTable { 213 return &lockableTable{Table: t} 214 } 215 216 var _ sql.Lockable = (*lockableTable)(nil) 217 218 func (l *lockableTable) Lock(ctx *sql.Context, write bool) error { 219 if write { 220 l.writeLocks++ 221 } else { 222 l.readLocks++ 223 } 224 return nil 225 } 226 227 func (l *lockableTable) Unlock(ctx *sql.Context, id uint32) error { 228 l.unlocks++ 229 return nil 230 } 231 232 type analyzerTestCase struct { 233 name string 234 query string 235 planGenerator func(*testing.T, *sql.Context, enginetest.QueryEngine) sql.Node 236 err *errors.Kind 237 } 238 239 func TestShowProcessList(t *testing.T) { 240 require := require.New(t) 241 242 addr1 := "127.0.0.1:34567" 243 addr2 := "127.0.0.1:34568" 244 username := "foo" 245 246 p := sqle.NewProcessList() 247 p.AddConnection(1, addr1) 248 p.AddConnection(2, addr2) 249 sess := sql.NewBaseSessionWithClientServer("0.0.0.0:3306", sql.Client{Address: addr1, User: username}, 1) 250 p.ConnectionReady(sess) 251 ctx := sql.NewContext(context.Background(), sql.WithPid(1), sql.WithSession(sess), sql.WithProcessList(p)) 252 253 ctx, err := p.BeginQuery(ctx, "SELECT foo") 254 require.NoError(err) 255 256 p.AddTableProgress(ctx.Pid(), "a", 5) 257 p.AddTableProgress(ctx.Pid(), "b", 6) 258 259 sess = sql.NewBaseSessionWithClientServer("0.0.0.0:3306", sql.Client{Address: addr2, User: username}, 2) 260 p.ConnectionReady(sess) 261 ctx = sql.NewContext(context.Background(), sql.WithPid(2), sql.WithSession(sess), sql.WithProcessList(p)) 262 ctx, err = p.BeginQuery(ctx, "SELECT bar") 263 require.NoError(err) 264 265 p.AddTableProgress(ctx.Pid(), "foo", 2) 266 267 p.UpdateTableProgress(1, "a", 3) 268 p.UpdateTableProgress(1, "a", 1) 269 p.UpdatePartitionProgress(1, "a", "a-1", 7) 270 p.UpdatePartitionProgress(1, "a", "a-2", 9) 271 p.UpdateTableProgress(1, "b", 2) 272 p.UpdateTableProgress(2, "foo", 1) 273 274 n := plan.NewShowProcessList() 275 276 iter, err := rowexec.DefaultBuilder.Build(ctx, n, nil) 277 require.NoError(err) 278 rows, err := sql.RowIterToRows(ctx, iter) 279 require.NoError(err) 280 281 expected := []sql.Row{ 282 {int64(1), username, addr1, nil, "Query", int64(0), 283 ` 284 a (4/5 partitions) 285 ├─ a-1 (7/? rows) 286 └─ a-2 (9/? rows) 287 288 b (2/6 partitions) 289 `, "SELECT foo"}, 290 {int64(2), username, addr2, nil, "Query", int64(0), "\nfoo (1/2 partitions)\n", "SELECT bar"}, 291 } 292 293 require.ElementsMatch(expected, rows) 294 } 295 296 // TODO: this was an analyzer test, but we don't have a mock process list for it to use, so it has to be here 297 func TestTrackProcess(t *testing.T) { 298 require := require.New(t) 299 db := memory.NewDatabase("db") 300 provider := memory.NewDBProvider(db) 301 a := analyzer.NewDefault(provider) 302 sess := memory.NewSession(sql.NewBaseSession(), provider) 303 304 node := plan.NewInnerJoin( 305 plan.NewResolvedTable(&nonIndexableTable{memory.NewPartitionedTable(db.BaseDatabase, "foo", sql.PrimaryKeySchema{}, nil, 2)}, nil, nil), 306 plan.NewResolvedTable(memory.NewPartitionedTable(db.BaseDatabase, "bar", sql.PrimaryKeySchema{}, nil, 4), nil, nil), 307 expression.NewLiteral(int64(1), types.Int64), 308 ) 309 310 pl := sqle.NewProcessList() 311 312 ctx := sql.NewContext(context.Background(), sql.WithPid(1), sql.WithProcessList(pl), sql.WithSession(sess)) 313 pl.AddConnection(ctx.Session.ID(), "localhost") 314 pl.ConnectionReady(ctx.Session) 315 ctx, err := ctx.ProcessList.BeginQuery(ctx, "SELECT foo") 316 require.NoError(err) 317 318 rule := getRuleFrom(analyzer.OnceAfterAll, analyzer.TrackProcessId) 319 result, _, err := rule.Apply(ctx, a, node, nil, analyzer.DefaultRuleSelector) 320 require.NoError(err) 321 322 processes := ctx.ProcessList.Processes() 323 require.Len(processes, 1) 324 require.Equal("SELECT foo", processes[0].Query) 325 require.Equal( 326 map[string]sql.TableProgress{ 327 "foo": sql.TableProgress{ 328 Progress: sql.Progress{Name: "foo", Done: 0, Total: 2}, 329 PartitionsProgress: map[string]sql.PartitionProgress{}}, 330 "bar": sql.TableProgress{ 331 Progress: sql.Progress{Name: "bar", Done: 0, Total: 4}, 332 PartitionsProgress: map[string]sql.PartitionProgress{}}, 333 }, 334 processes[0].Progress) 335 336 proc, ok := result.(*plan.QueryProcess) 337 require.True(ok) 338 339 join, ok := proc.Child().(*plan.JoinNode) 340 require.True(ok) 341 require.Equal(join.JoinType(), plan.JoinTypeInner) 342 343 lhs, ok := join.Left().(*plan.ResolvedTable) 344 require.True(ok) 345 _, ok = lhs.Table.(*plan.ProcessTable) 346 require.True(ok) 347 348 rhs, ok := join.Right().(*plan.ResolvedTable) 349 require.True(ok) 350 _, ok = rhs.Table.(*plan.ProcessTable) 351 require.True(ok) 352 353 iter, err := rowexec.DefaultBuilder.Build(ctx, proc, nil) 354 require.NoError(err) 355 _, err = sql.RowIterToRows(ctx, iter) 356 require.NoError(err) 357 358 procs := ctx.ProcessList.Processes() 359 require.Len(procs, 1) 360 require.Equal(procs[0].Command, sql.ProcessCommandSleep) 361 require.Error(ctx.Err()) 362 } 363 364 func TestConcurrentProcessList(t *testing.T) { 365 enginetest.TestConcurrentProcessList(t, enginetest.NewDefaultMemoryHarness()) 366 } 367 368 func getRuleFrom(rules []analyzer.Rule, id analyzer.RuleId) *analyzer.Rule { 369 for _, rule := range rules { 370 if rule.Id == id { 371 return &rule 372 } 373 } 374 375 return nil 376 } 377 378 // wrapper around sql.Table to make it not indexable 379 type nonIndexableTable struct { 380 *memory.Table 381 } 382 383 var _ memory.MemTable = (*nonIndexableTable)(nil) 384 385 func (t *nonIndexableTable) IgnoreSessionData() bool { 386 return true 387 } 388 389 func TestLockTables(t *testing.T) { 390 require := require.New(t) 391 db := memory.NewDatabase("db") 392 393 t1 := newLockableTable(memory.NewTable(db.BaseDatabase, "foo", sql.PrimaryKeySchema{}, nil)) 394 t2 := newLockableTable(memory.NewTable(db.BaseDatabase, "bar", sql.PrimaryKeySchema{}, nil)) 395 node := plan.NewLockTables([]*plan.TableLock{ 396 {plan.NewResolvedTable(t1, nil, nil), true}, 397 {plan.NewResolvedTable(t2, nil, nil), false}, 398 }) 399 node.Catalog = analyzer.NewCatalog(sql.NewDatabaseProvider()) 400 401 _, err := rowexec.DefaultBuilder.Build(sql.NewEmptyContext(), node, nil) 402 403 require.NoError(err) 404 405 require.Equal(1, t1.writeLocks) 406 require.Equal(0, t1.readLocks) 407 require.Equal(1, t2.readLocks) 408 require.Equal(0, t2.writeLocks) 409 } 410 411 func TestUnlockTables(t *testing.T) { 412 require := require.New(t) 413 db := memory.NewDatabase("db") 414 415 t1 := newLockableTable(memory.NewTable(db.BaseDatabase, "foo", sql.PrimaryKeySchema{}, db.GetForeignKeyCollection())) 416 t2 := newLockableTable(memory.NewTable(db.BaseDatabase, "bar", sql.PrimaryKeySchema{}, db.GetForeignKeyCollection())) 417 t3 := newLockableTable(memory.NewTable(db.BaseDatabase, "baz", sql.PrimaryKeySchema{}, db.GetForeignKeyCollection())) 418 db.AddTable("foo", t1) 419 db.AddTable("bar", t2) 420 db.AddTable("baz", t3) 421 422 catalog := analyzer.NewCatalog(sql.NewDatabaseProvider(db)) 423 424 ctx := sql.NewContext(context.Background()) 425 ctx.SetCurrentDatabase("db") 426 catalog.LockTable(ctx, "foo") 427 catalog.LockTable(ctx, "bar") 428 429 node := plan.NewUnlockTables() 430 node.Catalog = catalog 431 432 _, err := node.RowIter(ctx, nil) 433 require.NoError(err) 434 435 require.Equal(1, t1.unlocks) 436 require.Equal(1, t2.unlocks) 437 require.Equal(0, t3.unlocks) 438 } 439 440 var _ sql.PartitionCounter = (*nonIndexableTable)(nil) 441 442 func (t *nonIndexableTable) PartitionCount(ctx *sql.Context) (int64, error) { 443 return t.Table.PartitionCount(ctx) 444 } 445 446 var analyzerTestCases = []analyzerTestCase{ 447 { 448 name: "show tables as of", 449 query: "SHOW TABLES AS OF 'abc123'", 450 planGenerator: func(t *testing.T, ctx *sql.Context, engine enginetest.QueryEngine) sql.Node { 451 db, err := engine.EngineAnalyzer().Catalog.Database(ctx, "mydb") 452 require.NoError(t, err) 453 return plan.NewShowTables(db, false, expression.NewLiteral("abc123", types.LongText)) 454 }, 455 }, 456 { 457 name: "show tables as of, from", 458 query: "SHOW TABLES FROM foo AS OF 'abc123'", 459 planGenerator: func(t *testing.T, ctx *sql.Context, engine enginetest.QueryEngine) sql.Node { 460 db, err := engine.EngineAnalyzer().Catalog.Database(ctx, "foo") 461 require.NoError(t, err) 462 return plan.NewShowTables(db, false, expression.NewLiteral("abc123", types.LongText)) 463 }, 464 }, 465 { 466 name: "show tables as of, function call", 467 query: "SHOW TABLES FROM foo AS OF GREATEST('abc123', 'cde456')", 468 planGenerator: func(t *testing.T, ctx *sql.Context, engine enginetest.QueryEngine) sql.Node { 469 db, err := engine.EngineAnalyzer().Catalog.Database(ctx, "foo") 470 require.NoError(t, err) 471 greatest, err := function.NewGreatest( 472 expression.NewLiteral("abc123", types.LongText), 473 expression.NewLiteral("cde456", types.LongText), 474 ) 475 require.NoError(t, err) 476 return plan.NewShowTables(db, false, greatest) 477 }, 478 }, 479 { 480 name: "show tables as of, timestamp", 481 query: "SHOW TABLES FROM foo AS OF TIMESTAMP('20200101:120000Z')", 482 planGenerator: func(t *testing.T, ctx *sql.Context, engine enginetest.QueryEngine) sql.Node { 483 db, err := engine.EngineAnalyzer().Catalog.Database(ctx, "foo") 484 require.NoError(t, err) 485 timestamp, err := function.NewTimestamp( 486 expression.NewLiteral("20200101:120000Z", types.LongText), 487 ) 488 require.NoError(t, err) 489 return plan.NewShowTables(db, false, timestamp) 490 }, 491 }, 492 { 493 name: "show tables as of, naked literal", 494 query: "SHOW TABLES AS OF abc123", 495 planGenerator: func(t *testing.T, ctx *sql.Context, engine enginetest.QueryEngine) sql.Node { 496 db, err := engine.EngineAnalyzer().Catalog.Database(ctx, "mydb") 497 require.NoError(t, err) 498 return plan.NewShowTables(db, false, expression.NewLiteral("abc123", types.LongText)) 499 }, 500 }, 501 } 502 503 // Grab bag tests for testing analysis of various nodes that are difficult to verify through other means 504 func TestAnalyzer_Exp(t *testing.T) { 505 harness := enginetest.NewDefaultMemoryHarness() 506 harness.Setup(setup.MydbData, setup.FooData) 507 for _, tt := range analyzerTestCases { 508 t.Run(tt.name, func(t *testing.T) { 509 e, err := harness.NewEngine(t) 510 require.NoError(t, err) 511 512 ctx := enginetest.NewContext(harness) 513 b := planbuilder.New(ctx, e.EngineAnalyzer().Catalog) 514 parsed, _, _, err := b.Parse(tt.query, false) 515 require.NoError(t, err) 516 517 analyzed, err := e.EngineAnalyzer().Analyze(ctx, parsed, nil) 518 analyzed = analyzer.StripPassthroughNodes(analyzed) 519 if tt.err != nil { 520 require.Error(t, err) 521 assert.True(t, tt.err.Is(err)) 522 } else { 523 assertNodesEqualWithDiff(t, tt.planGenerator(t, ctx, e), analyzed) 524 } 525 }) 526 } 527 } 528 529 func assertNodesEqualWithDiff(t *testing.T, expected, actual sql.Node) { 530 if x, ok := actual.(*plan.QueryProcess); ok { 531 actual = x.Child() 532 } 533 534 if !assert.Equal(t, expected, actual) { 535 expectedStr := sql.DebugString(expected) 536 actualStr := sql.DebugString(actual) 537 diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ 538 A: difflib.SplitLines(expectedStr), 539 B: difflib.SplitLines(actualStr), 540 FromFile: "expected", 541 FromDate: "", 542 ToFile: "actual", 543 ToDate: "", 544 Context: 1, 545 }) 546 require.NoError(t, err) 547 548 if len(diff) > 0 { 549 fmt.Println(diff) 550 } 551 } 552 } 553 554 func TestRecursiveViewDefinition(t *testing.T) { 555 enginetest.TestRecursiveViewDefinition(t, enginetest.NewDefaultMemoryHarness()) 556 } 557 558 func TestShowCharset(t *testing.T) { 559 iterForAllImplemented := func(t *testing.T) []sql.Row { 560 var rows []sql.Row 561 iter := sql.NewCharacterSetsIterator() 562 for charset, ok := iter.Next(); ok; charset, ok = iter.Next() { 563 if charset.Encoder != nil { 564 rows = append(rows, sql.Row{ 565 charset.Name, 566 charset.Description, 567 charset.DefaultCollation.String(), 568 uint64(charset.MaxLength), 569 }) 570 } 571 } 572 return rows 573 } 574 575 tests := []struct { 576 Query string 577 RowGen func(t *testing.T) []sql.Row 578 }{ 579 { 580 Query: "SHOW CHARACTER SET;", 581 RowGen: iterForAllImplemented, 582 }, 583 { 584 Query: "SHOW CHARSET;", 585 RowGen: iterForAllImplemented, 586 }, 587 { 588 Query: "SHOW CHARSET LIKE 'utf8%'", 589 RowGen: func(t *testing.T) []sql.Row { 590 var rows []sql.Row 591 iter := sql.NewCharacterSetsIterator() 592 for charset, ok := iter.Next(); ok; charset, ok = iter.Next() { 593 if charset.Encoder != nil && strings.HasPrefix(charset.Name, "utf8") { 594 rows = append(rows, sql.Row{ 595 charset.Name, 596 charset.Description, 597 charset.DefaultCollation.String(), 598 uint64(charset.MaxLength), 599 }) 600 } 601 } 602 return rows 603 }, 604 }, 605 { 606 Query: "SHOW CHARSET WHERE Charset='binary'", 607 RowGen: func(t *testing.T) []sql.Row { 608 var rows []sql.Row 609 iter := sql.NewCharacterSetsIterator() 610 for charset, ok := iter.Next(); ok; charset, ok = iter.Next() { 611 if charset.Encoder != nil && charset.Name == "binary" { 612 rows = append(rows, sql.Row{ 613 charset.Name, 614 charset.Description, 615 charset.DefaultCollation.String(), 616 uint64(charset.MaxLength), 617 }) 618 } 619 } 620 return rows 621 }, 622 }, 623 { 624 Query: `SHOW CHARSET WHERE Charset = 'foo'`, 625 RowGen: func(t *testing.T) []sql.Row { 626 var rows []sql.Row 627 iter := sql.NewCharacterSetsIterator() 628 for charset, ok := iter.Next(); ok; charset, ok = iter.Next() { 629 if charset.Encoder != nil && charset.Name == "foo" { 630 rows = append(rows, sql.Row{ 631 charset.Name, 632 charset.Description, 633 charset.DefaultCollation.String(), 634 uint64(charset.MaxLength), 635 }) 636 } 637 } 638 return rows 639 }, 640 }, 641 } 642 643 harness := enginetest.NewMemoryHarness("", 1, 1, false, nil) 644 for _, test := range tests { 645 enginetest.TestQuery(t, harness, test.Query, test.RowGen(t), nil, nil) 646 } 647 } 648 649 func TestEngineJoinOps(t *testing.T) { 650 enginetest.TestJoinOps(t, enginetest.NewDefaultMemoryHarness(), enginetest.EngineOnlyJoinOpTests) 651 } 652 653 func TestTableFunctions(t *testing.T) { 654 harness := enginetest.NewDefaultMemoryHarness() 655 harness.Setup(setup.MydbData) 656 657 databaseProvider := harness.NewDatabaseProvider() 658 testDatabaseProvider := enginetest.NewTestProvider( 659 &databaseProvider, 660 SimpleTableFunction{}, 661 memory.IntSequenceTable{}, 662 memory.PointLookupTable{}, 663 memory.TableFunc{}, 664 memory.ExponentialDistTable{}, 665 memory.NormalDistTable{}) 666 667 engine := enginetest.NewEngineWithProvider(t, harness, testDatabaseProvider) 668 harness = harness.WithProvider(engine.Analyzer.Catalog.DbProvider) 669 670 engine.EngineAnalyzer().ExecBuilder = rowexec.DefaultBuilder 671 672 engine, err := enginetest.RunSetupScripts(harness.NewContext(), engine, setup.MydbData, true) 673 require.NoError(t, err) 674 _ = harness.NewSession() 675 676 for _, test := range queries.TableFunctionScriptTests { 677 enginetest.TestScriptWithEngine(t, engine, harness, test) 678 } 679 } 680 681 func TestExternalProcedures(t *testing.T) { 682 harness := enginetest.NewDefaultMemoryHarness() 683 harness.Setup(setup.MydbData) 684 for _, script := range queries.ExternalProcedureTests { 685 func() { 686 e, err := harness.NewEngine(t) 687 require.NoError(t, err) 688 defer func() { 689 _ = e.Close() 690 }() 691 enginetest.TestScriptWithEngine(t, e, harness, script) 692 }() 693 } 694 } 695 696 func TestCallAsOf(t *testing.T) { 697 harness := enginetest.NewDefaultMemoryHarness() 698 enginetest.CreateVersionedTestData(t, harness) 699 for _, script := range queries.CallAsofScripts { 700 func() { 701 e, err := harness.NewEngine(t) 702 require.NoError(t, err) 703 defer func() { 704 _ = e.Close() 705 }() 706 enginetest.TestScriptWithEngine(t, e, harness, script) 707 }() 708 } 709 } 710 711 func TestTriggerViewWarning(t *testing.T) { 712 // Old versions of Dolt could create view triggers. 713 // Check that users in this state can still write to 714 // regular table. 715 harness := enginetest.NewDefaultMemoryHarness() 716 harness.Setup(setup.MydbData, setup.MytableData) 717 e, err := harness.NewEngine(t) 718 assert.NoError(t, err) 719 720 prov := e.EngineAnalyzer().Catalog.DbProvider.(*memory.DbProvider) 721 db, err := prov.Database(nil, "mydb") 722 assert.NoError(t, err) 723 724 baseDb := db.(*memory.HistoryDatabase).BaseDatabase 725 err = baseDb.CreateTrigger(nil, sql.TriggerDefinition{ 726 Name: "view_trig", 727 CreateStatement: "CREATE TRIGGER view_trig BEFORE INSERT ON myview FOR EACH ROW SET i=i+2", 728 }) 729 assert.NoError(t, err) 730 731 ctx := harness.NewContext() 732 enginetest.CreateNewConnectionForServerEngine(ctx, e) 733 734 enginetest.TestQueryWithContext(t, ctx, e, harness, "insert into mytable values (4, 'fourth row')", []sql.Row{{types.NewOkResult(1)}}, nil, nil) 735 enginetest.TestQueryWithContext(t, ctx, e, harness, "SHOW WARNINGS", []sql.Row{{"Warning", 0, "trigger on view is not supported; 'DROP TRIGGER view_trig' to fix"}}, nil, nil) 736 enginetest.AssertErrWithCtx(t, e, harness, ctx, "insert into myview values (5, 'fifth row')", nil, "expected insert destination to be resolved or unresolved table") 737 } 738 739 func TestCollationCoercion(t *testing.T) { 740 harness := enginetest.NewDefaultMemoryHarness() 741 if harness.IsUsingServer() { 742 t.Skip("TODO: need further investigation") 743 } 744 harness.Setup(setup.MydbData) 745 engine, err := harness.NewEngine(t) 746 require.NoError(t, err) 747 defer engine.Close() 748 749 ctx := harness.NewContext() 750 ctx.SetCurrentDatabase("mydb") 751 752 for _, statement := range queries.CollationCoercionSetup { 753 enginetest.RunQueryWithContext(t, engine, harness, ctx, statement) 754 } 755 756 for _, test := range queries.CollationCoercionTests { 757 coercibilityQuery := fmt.Sprintf(`SELECT COERCIBILITY(%s) FROM temp_tbl LIMIT 1;`, test.Parameters) 758 collationQuery := fmt.Sprintf(`SELECT COLLATION(%s) FROM temp_tbl LIMIT 1;`, test.Parameters) 759 for i, query := range []string{coercibilityQuery, collationQuery} { 760 t.Run(query, func(t *testing.T) { 761 _, iter, err := engine.Query(ctx, query) 762 if test.Error { 763 if err == nil { 764 _, err := sql.RowIterToRows(ctx, iter) 765 require.Error(t, err) 766 } else { 767 require.Error(t, err) 768 } 769 } else { 770 require.NoError(t, err) 771 rows, err := sql.RowIterToRows(ctx, iter) 772 require.NoError(t, err) 773 require.Equal(t, 1, len(rows)) 774 require.Equal(t, 1, len(rows[0])) 775 if i == 0 { 776 num, _, err := types.Int64.Convert(rows[0][0]) 777 require.NoError(t, err) 778 require.Equal(t, test.Coercibility, num.(int64)) 779 } else { 780 str, _, err := types.LongText.Convert(rows[0][0]) 781 require.NoError(t, err) 782 require.Equal(t, test.Collation.Name(), str.(string)) 783 } 784 } 785 }) 786 } 787 } 788 } 789 790 func TestRegex(t *testing.T) { 791 harness := enginetest.NewDefaultMemoryHarness() 792 harness.Setup(setup.SimpleSetup...) 793 engine, err := harness.NewEngine(t) 794 require.NoError(t, err) 795 defer engine.Close() 796 797 ctx := enginetest.NewContext(harness) 798 for _, tt := range queries.RegexTests { 799 t.Run(tt.Query, func(t *testing.T) { 800 if harness.SkipQueryTest(tt.Query) { 801 t.Skipf("Skipping query plan for %s", tt.Query) 802 } 803 if tt.ExpectedErr == nil { 804 enginetest.TestQueryWithContext(t, ctx, engine, harness, tt.Query, tt.Expected, nil, nil) 805 } else { 806 newCtx := ctx.WithQuery(tt.Query) 807 _, iter, err := engine.Query(newCtx, tt.Query) 808 if err == nil { 809 _, err = sql.RowIterToRows(newCtx, iter) 810 require.Error(t, err) 811 } 812 } 813 }) 814 } 815 // We force garbage collection twice as we have two levels of finalizers on our regex objects, and we want to make 816 // sure that neither of them panic. 817 runtime.GC() 818 runtime.GC() 819 } 820 821 var _ sql.TableFunction = (*SimpleTableFunction)(nil) 822 var _ sql.CollationCoercible = (*SimpleTableFunction)(nil) 823 var _ sql.ExecSourceRel = (*SimpleTableFunction)(nil) 824 825 // SimpleTableFunction an extremely simple implementation of TableFunction for testing. 826 // When evaluated, returns a single row: {"foo", 123} 827 type SimpleTableFunction struct { 828 returnedResults bool 829 id sql.TableId 830 cols sql.ColSet 831 } 832 833 func (s SimpleTableFunction) WithId(id sql.TableId) plan.TableIdNode { 834 s.id = id 835 return s 836 } 837 838 func (s SimpleTableFunction) Id() sql.TableId { 839 return s.id 840 } 841 842 func (s SimpleTableFunction) WithColumns(set sql.ColSet) plan.TableIdNode { 843 s.cols = set 844 return s 845 } 846 847 func (s SimpleTableFunction) Columns() sql.ColSet { 848 return s.cols 849 } 850 851 func (s SimpleTableFunction) NewInstance(_ *sql.Context, _ sql.Database, _ []sql.Expression) (sql.Node, error) { 852 return SimpleTableFunction{}, nil 853 } 854 855 func (s SimpleTableFunction) RowIter(ctx *sql.Context, r sql.Row) (sql.RowIter, error) { 856 if s.returnedResults == true { 857 return nil, io.EOF 858 } 859 s.returnedResults = true 860 return &SimpleTableFunctionRowIter{}, nil 861 } 862 863 func (s SimpleTableFunction) IsReadOnly() bool { 864 return true 865 } 866 867 func (s SimpleTableFunction) Resolved() bool { 868 return true 869 } 870 871 func (s SimpleTableFunction) String() string { 872 return "SimpleTableFunction" 873 } 874 875 func (s SimpleTableFunction) Schema() sql.Schema { 876 schema := []*sql.Column{ 877 &sql.Column{ 878 Name: "one", 879 Type: types.TinyText, 880 }, 881 &sql.Column{ 882 Name: "two", 883 Type: types.Int64, 884 }, 885 } 886 887 return schema 888 } 889 890 func (s SimpleTableFunction) Children() []sql.Node { 891 return []sql.Node{} 892 } 893 894 func (s SimpleTableFunction) WithChildren(_ ...sql.Node) (sql.Node, error) { 895 return s, nil 896 } 897 898 func (s SimpleTableFunction) CheckPrivileges(_ *sql.Context, _ sql.PrivilegedOperationChecker) bool { 899 return true 900 } 901 902 // CollationCoercibility implements the interface sql.CollationCoercible. 903 func (SimpleTableFunction) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 904 return sql.Collation_binary, 7 905 } 906 907 func (s SimpleTableFunction) Expressions() []sql.Expression { 908 return []sql.Expression{} 909 } 910 911 func (s SimpleTableFunction) WithExpressions(e ...sql.Expression) (sql.Node, error) { 912 return s, nil 913 } 914 915 func (s SimpleTableFunction) Database() sql.Database { 916 return nil 917 } 918 919 func (s SimpleTableFunction) WithDatabase(_ sql.Database) (sql.Node, error) { 920 return s, nil 921 } 922 923 func (s SimpleTableFunction) Name() string { 924 return "simple_table_function" 925 } 926 927 func (s SimpleTableFunction) Description() string { 928 return "SimpleTableFunction" 929 } 930 931 var _ sql.RowIter = (*SimpleTableFunctionRowIter)(nil) 932 933 type SimpleTableFunctionRowIter struct { 934 returnedResults bool 935 } 936 937 func (itr *SimpleTableFunctionRowIter) Next(_ *sql.Context) (sql.Row, error) { 938 if itr.returnedResults { 939 return nil, io.EOF 940 } 941 942 itr.returnedResults = true 943 return sql.Row{"foo", 123}, nil 944 } 945 946 func (itr *SimpleTableFunctionRowIter) Close(_ *sql.Context) error { 947 return nil 948 } 949 950 func TestTimestampBindingsCanBeConverted(t *testing.T) { 951 db, close := newDatabase() 952 defer close() 953 954 _, err := db.Exec("CREATE TABLE mytable (t TIMESTAMP)") 955 require.NoError(t, err) 956 957 // All we are doing in this test is ensuring that writing a timestamp to the 958 // database does not throw an error. 959 _, err = db.Exec("INSERT INTO mytable (t) VALUES (?)", time.Now()) 960 require.NoError(t, err) 961 } 962 963 func TestTimestampBindingsCanBeCompared(t *testing.T) { 964 db, close := newDatabase() 965 defer close() 966 967 _, err := db.Exec("CREATE TABLE mytable (t TIMESTAMP)") 968 require.NoError(t, err) 969 970 // We'll insert both of these timestamps and then try and filter them. 971 t0 := time.Date(2022, 01, 01, 0, 0, 0, 0, time.UTC) 972 t1 := t0.Add(1 * time.Minute) 973 974 _, err = db.Exec("INSERT INTO mytable (t) VALUES (?)", t0) 975 require.NoError(t, err) 976 _, err = db.Exec("INSERT INTO mytable (t) VALUES (?)", t1) 977 require.NoError(t, err) 978 979 var count int 980 err = db.QueryRow("SELECT COUNT(1) FROM mytable WHERE t > ?", t0).Scan(&count) 981 require.NoError(t, err) 982 require.Equal(t, 1, count) 983 } 984 985 // TestAlterTableWithBadSchema is a backwards compatibility test that 986 // ensures tables made with old versions of the engine can be altered. 987 func TestAlterTableWithBadSchema(t *testing.T) { 988 harness := enginetest.NewDefaultMemoryHarness() 989 pro := harness.Provider() 990 harness.NewDatabases("mydb") 991 ctx := harness.NewContext() 992 sqlDb, err := pro.Database(ctx, "mydb") 993 require.NoError(t, err) 994 db := sqlDb.(*memory.HistoryDatabase) 995 996 sch := sql.NewPrimaryKeySchema(sql.Schema{ 997 {Name: "a", Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 16383), Source: "mytable"}, 998 {Name: "b", Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 16383), Source: "mytable"}, 999 {Name: "c", Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 16383), Source: "mytable"}, 1000 }) 1001 1002 harness.NewTableAsOf(db, "mytable", sch, nil) 1003 1004 engine, err := harness.NewEngine(t) 1005 require.NoError(t, err) 1006 1007 tests := []struct { 1008 name string 1009 q string 1010 err bool 1011 }{ 1012 { 1013 name: "noop modify triggers validation", 1014 q: "alter table mytable rename column a to d", 1015 err: true, 1016 }, 1017 { 1018 name: "partial update with invalid final schema fails", 1019 q: "alter table mytable modify column a varchar(100), modify column b varchar(100)", 1020 err: true, 1021 }, 1022 { 1023 name: "update with valid final schema succeeds", 1024 q: "alter table mytable modify column a varchar(100), modify column b varchar(100), modify column c varchar(100)", 1025 err: false, 1026 }, 1027 { 1028 name: "mixed update add with invalid final schema fails", 1029 q: "alter table mytable modify column a varchar(100), modify column b varchar(100), modify column c varchar(100), add column d varchar(max)", 1030 err: true, 1031 }, 1032 } 1033 for _, tt := range tests { 1034 t.Run(tt.name, func(t *testing.T) { 1035 ctx := harness.NewContext() 1036 _, iter, err := engine.Query(ctx, tt.q) 1037 // errors should be analyze time, not execution time 1038 if tt.err { 1039 require.Error(t, err) 1040 } else { 1041 require.NoError(t, err) 1042 _, err = sql.RowIterToRows(ctx, iter) 1043 require.NoError(t, err) 1044 } 1045 }) 1046 } 1047 } 1048 1049 func newDatabase() (*sql2.DB, func()) { 1050 // Grab an empty port so that tests do not fail if a specific port is already in use 1051 listener, err := net.Listen("tcp", ":0") 1052 if err != nil { 1053 panic(err) 1054 } 1055 port := listener.Addr().(*net.TCPAddr).Port 1056 if err = listener.Close(); err != nil { 1057 panic(err) 1058 } 1059 1060 harness := enginetest.NewDefaultMemoryHarness() 1061 pro := harness.Provider() 1062 harness.NewDatabases("mydb") 1063 1064 engine := sqle.New(analyzer.NewDefault(pro), &sqle.Config{ 1065 IncludeRootAccount: true, 1066 }) 1067 cfg := server.Config{ 1068 Protocol: "tcp", 1069 Address: fmt.Sprintf("localhost:%d", port), 1070 } 1071 srv, err := server.NewServer(cfg, engine, harness.SessionBuilder(), nil) 1072 if err != nil { 1073 panic(err) 1074 } 1075 go srv.Start() 1076 1077 db, err := sql2.Open("mysql", fmt.Sprintf("root:@tcp(localhost:%d)/mydb", port)) 1078 if err != nil { 1079 panic(err) 1080 } 1081 return db, func() { srv.Close() } 1082 }