vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletmanager/vreplication/replicator_plan_test.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package vreplication 18 19 import ( 20 "encoding/json" 21 "strings" 22 "testing" 23 24 "vitess.io/vitess/go/vt/binlog/binlogplayer" 25 26 "github.com/stretchr/testify/assert" 27 28 "vitess.io/vitess/go/sqltypes" 29 binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" 30 ) 31 32 type TestReplicatorPlan struct { 33 VStreamFilter *binlogdatapb.Filter 34 TargetTables []string 35 TablePlans map[string]*TestTablePlan 36 } 37 38 type TestTablePlan struct { 39 TargetName string 40 SendRule string 41 InsertFront string `json:",omitempty"` 42 InsertValues string `json:",omitempty"` 43 InsertOnDup string `json:",omitempty"` 44 Insert string `json:",omitempty"` 45 Update string `json:",omitempty"` 46 Delete string `json:",omitempty"` 47 PKReferences []string `json:",omitempty"` 48 } 49 50 func TestBuildPlayerPlan(t *testing.T) { 51 testcases := []struct { 52 input *binlogdatapb.Filter 53 plan *TestReplicatorPlan 54 planpk *TestReplicatorPlan 55 err string 56 }{{ 57 // Regular expression 58 input: &binlogdatapb.Filter{ 59 Rules: []*binlogdatapb.Rule{{ 60 Match: "/.*", 61 }}, 62 }, 63 plan: &TestReplicatorPlan{ 64 VStreamFilter: &binlogdatapb.Filter{ 65 Rules: []*binlogdatapb.Rule{{ 66 Match: "t1", 67 Filter: "select * from t1", 68 }}, 69 }, 70 TargetTables: []string{"t1"}, 71 TablePlans: map[string]*TestTablePlan{ 72 "t1": { 73 TargetName: "t1", 74 SendRule: "t1", 75 }, 76 }, 77 }, 78 planpk: &TestReplicatorPlan{ 79 VStreamFilter: &binlogdatapb.Filter{ 80 Rules: []*binlogdatapb.Rule{{ 81 Match: "t1", 82 Filter: "select * from t1", 83 }}, 84 }, 85 TargetTables: []string{"t1"}, 86 TablePlans: map[string]*TestTablePlan{ 87 "t1": { 88 TargetName: "t1", 89 SendRule: "t1", 90 }, 91 }, 92 }, 93 }, { 94 // Regular with keyrange 95 input: &binlogdatapb.Filter{ 96 Rules: []*binlogdatapb.Rule{{ 97 Match: "/.*", 98 Filter: "-80", 99 }}, 100 }, 101 plan: &TestReplicatorPlan{ 102 VStreamFilter: &binlogdatapb.Filter{ 103 Rules: []*binlogdatapb.Rule{{ 104 Match: "t1", 105 Filter: "select * from t1 where in_keyrange('-80')", 106 }}, 107 }, 108 TargetTables: []string{"t1"}, 109 TablePlans: map[string]*TestTablePlan{ 110 "t1": { 111 TargetName: "t1", 112 SendRule: "t1", 113 }, 114 }, 115 }, 116 planpk: &TestReplicatorPlan{ 117 VStreamFilter: &binlogdatapb.Filter{ 118 Rules: []*binlogdatapb.Rule{{ 119 Match: "t1", 120 Filter: "select * from t1 where in_keyrange('-80')", 121 }}, 122 }, 123 TargetTables: []string{"t1"}, 124 TablePlans: map[string]*TestTablePlan{ 125 "t1": { 126 TargetName: "t1", 127 SendRule: "t1", 128 }, 129 }, 130 }, 131 }, { 132 // '*' expression 133 input: &binlogdatapb.Filter{ 134 Rules: []*binlogdatapb.Rule{{ 135 Match: "t1", 136 Filter: "select * from t2", 137 }}, 138 }, 139 plan: &TestReplicatorPlan{ 140 VStreamFilter: &binlogdatapb.Filter{ 141 Rules: []*binlogdatapb.Rule{{ 142 Match: "t2", 143 Filter: "select * from t2", 144 }}, 145 }, 146 TargetTables: []string{"t1"}, 147 TablePlans: map[string]*TestTablePlan{ 148 "t2": { 149 TargetName: "t1", 150 SendRule: "t2", 151 }, 152 }, 153 }, 154 planpk: &TestReplicatorPlan{ 155 VStreamFilter: &binlogdatapb.Filter{ 156 Rules: []*binlogdatapb.Rule{{ 157 Match: "t2", 158 Filter: "select * from t2", 159 }}, 160 }, 161 TargetTables: []string{"t1"}, 162 TablePlans: map[string]*TestTablePlan{ 163 "t2": { 164 TargetName: "t1", 165 SendRule: "t2", 166 }, 167 }, 168 }, 169 }, { 170 // Explicit columns 171 input: &binlogdatapb.Filter{ 172 Rules: []*binlogdatapb.Rule{{ 173 Match: "t1", 174 Filter: "select c1, c2 from t2", 175 }}, 176 }, 177 plan: &TestReplicatorPlan{ 178 VStreamFilter: &binlogdatapb.Filter{ 179 Rules: []*binlogdatapb.Rule{{ 180 Match: "t2", 181 Filter: "select c1, c2 from t2", 182 }}, 183 }, 184 TargetTables: []string{"t1"}, 185 TablePlans: map[string]*TestTablePlan{ 186 "t2": { 187 TargetName: "t1", 188 SendRule: "t2", 189 PKReferences: []string{"c1"}, 190 InsertFront: "insert into t1(c1,c2)", 191 InsertValues: "(:a_c1,:a_c2)", 192 Insert: "insert into t1(c1,c2) values (:a_c1,:a_c2)", 193 Update: "update t1 set c2=:a_c2 where c1=:b_c1", 194 Delete: "delete from t1 where c1=:b_c1", 195 }, 196 }, 197 }, 198 planpk: &TestReplicatorPlan{ 199 VStreamFilter: &binlogdatapb.Filter{ 200 Rules: []*binlogdatapb.Rule{{ 201 Match: "t2", 202 Filter: "select c1, c2, pk1, pk2 from t2", 203 }}, 204 }, 205 TargetTables: []string{"t1"}, 206 TablePlans: map[string]*TestTablePlan{ 207 "t2": { 208 TargetName: "t1", 209 SendRule: "t2", 210 PKReferences: []string{"c1", "pk1", "pk2"}, 211 InsertFront: "insert into t1(c1,c2)", 212 InsertValues: "(:a_c1,:a_c2)", 213 Insert: "insert into t1(c1,c2) select :a_c1, :a_c2 from dual where (:a_pk1,:a_pk2) <= (1,'aaa')", 214 Update: "update t1 set c2=:a_c2 where c1=:b_c1 and (:b_pk1,:b_pk2) <= (1,'aaa')", 215 Delete: "delete from t1 where c1=:b_c1 and (:b_pk1,:b_pk2) <= (1,'aaa')", 216 }, 217 }, 218 }, 219 }, { 220 // partial group by 221 input: &binlogdatapb.Filter{ 222 Rules: []*binlogdatapb.Rule{{ 223 Match: "t1", 224 Filter: "select c1, c2, c3 from t2 group by c3, c1", 225 }}, 226 }, 227 plan: &TestReplicatorPlan{ 228 VStreamFilter: &binlogdatapb.Filter{ 229 Rules: []*binlogdatapb.Rule{{ 230 Match: "t2", 231 Filter: "select c1, c2, c3 from t2", 232 }}, 233 }, 234 TargetTables: []string{"t1"}, 235 TablePlans: map[string]*TestTablePlan{ 236 "t2": { 237 TargetName: "t1", 238 SendRule: "t2", 239 PKReferences: []string{"c1"}, 240 InsertFront: "insert into t1(c1,c2,c3)", 241 InsertValues: "(:a_c1,:a_c2,:a_c3)", 242 InsertOnDup: "on duplicate key update c2=values(c2)", 243 Insert: "insert into t1(c1,c2,c3) values (:a_c1,:a_c2,:a_c3) on duplicate key update c2=values(c2)", 244 Update: "update t1 set c2=:a_c2 where c1=:b_c1", 245 Delete: "update t1 set c2=null where c1=:b_c1", 246 }, 247 }, 248 }, 249 planpk: &TestReplicatorPlan{ 250 VStreamFilter: &binlogdatapb.Filter{ 251 Rules: []*binlogdatapb.Rule{{ 252 Match: "t2", 253 Filter: "select c1, c2, c3, pk1, pk2 from t2", 254 }}, 255 }, 256 TargetTables: []string{"t1"}, 257 TablePlans: map[string]*TestTablePlan{ 258 "t2": { 259 TargetName: "t1", 260 SendRule: "t2", 261 PKReferences: []string{"c1", "pk1", "pk2"}, 262 InsertFront: "insert into t1(c1,c2,c3)", 263 InsertValues: "(:a_c1,:a_c2,:a_c3)", 264 InsertOnDup: "on duplicate key update c2=values(c2)", 265 Insert: "insert into t1(c1,c2,c3) select :a_c1, :a_c2, :a_c3 from dual where (:a_pk1,:a_pk2) <= (1,'aaa') on duplicate key update c2=values(c2)", 266 Update: "update t1 set c2=:a_c2 where c1=:b_c1 and (:b_pk1,:b_pk2) <= (1,'aaa')", 267 Delete: "update t1 set c2=null where c1=:b_c1 and (:b_pk1,:b_pk2) <= (1,'aaa')", 268 }, 269 }, 270 }, 271 }, { 272 // full group by 273 input: &binlogdatapb.Filter{ 274 Rules: []*binlogdatapb.Rule{{ 275 Match: "t1", 276 Filter: "select c1, c2, c3 from t2 group by c3, c1, c2", 277 }}, 278 }, 279 plan: &TestReplicatorPlan{ 280 VStreamFilter: &binlogdatapb.Filter{ 281 Rules: []*binlogdatapb.Rule{{ 282 Match: "t2", 283 Filter: "select c1, c2, c3 from t2", 284 }}, 285 }, 286 TargetTables: []string{"t1"}, 287 TablePlans: map[string]*TestTablePlan{ 288 "t2": { 289 TargetName: "t1", 290 SendRule: "t2", 291 PKReferences: []string{"c1"}, 292 InsertFront: "insert ignore into t1(c1,c2,c3)", 293 InsertValues: "(:a_c1,:a_c2,:a_c3)", 294 Insert: "insert ignore into t1(c1,c2,c3) values (:a_c1,:a_c2,:a_c3)", 295 Update: "insert ignore into t1(c1,c2,c3) values (:a_c1,:a_c2,:a_c3)", 296 }, 297 }, 298 }, 299 planpk: &TestReplicatorPlan{ 300 VStreamFilter: &binlogdatapb.Filter{ 301 Rules: []*binlogdatapb.Rule{{ 302 Match: "t2", 303 Filter: "select c1, c2, c3, pk1, pk2 from t2", 304 }}, 305 }, 306 TargetTables: []string{"t1"}, 307 TablePlans: map[string]*TestTablePlan{ 308 "t2": { 309 TargetName: "t1", 310 SendRule: "t2", 311 PKReferences: []string{"c1", "pk1", "pk2"}, 312 InsertFront: "insert ignore into t1(c1,c2,c3)", 313 InsertValues: "(:a_c1,:a_c2,:a_c3)", 314 Insert: "insert ignore into t1(c1,c2,c3) select :a_c1, :a_c2, :a_c3 from dual where (:a_pk1,:a_pk2) <= (1,'aaa')", 315 Update: "insert ignore into t1(c1,c2,c3) select :a_c1, :a_c2, :a_c3 from dual where (:a_pk1,:a_pk2) <= (1,'aaa')", 316 }, 317 }, 318 }, 319 }, { 320 input: &binlogdatapb.Filter{ 321 Rules: []*binlogdatapb.Rule{{ 322 Match: "t1", 323 Filter: "select foo(a) as c1, foo(a, b) as c2, c c3 from t1", 324 }}, 325 }, 326 plan: &TestReplicatorPlan{ 327 VStreamFilter: &binlogdatapb.Filter{ 328 Rules: []*binlogdatapb.Rule{{ 329 Match: "t1", 330 Filter: "select a, a, b, c from t1", 331 }}, 332 }, 333 TargetTables: []string{"t1"}, 334 TablePlans: map[string]*TestTablePlan{ 335 "t1": { 336 TargetName: "t1", 337 SendRule: "t1", 338 PKReferences: []string{"a"}, 339 InsertFront: "insert into t1(c1,c2,c3)", 340 InsertValues: "(foo(:a_a),foo(:a_a, :a_b),:a_c)", 341 Insert: "insert into t1(c1,c2,c3) values (foo(:a_a),foo(:a_a, :a_b),:a_c)", 342 Update: "update t1 set c2=foo(:a_a, :a_b), c3=:a_c where c1=(foo(:b_a))", 343 Delete: "delete from t1 where c1=(foo(:b_a))", 344 }, 345 }, 346 }, 347 planpk: &TestReplicatorPlan{ 348 VStreamFilter: &binlogdatapb.Filter{ 349 Rules: []*binlogdatapb.Rule{{ 350 Match: "t1", 351 Filter: "select a, a, b, c, pk1, pk2 from t1", 352 }}, 353 }, 354 TargetTables: []string{"t1"}, 355 TablePlans: map[string]*TestTablePlan{ 356 "t1": { 357 TargetName: "t1", 358 SendRule: "t1", 359 PKReferences: []string{"a", "pk1", "pk2"}, 360 InsertFront: "insert into t1(c1,c2,c3)", 361 InsertValues: "(foo(:a_a),foo(:a_a, :a_b),:a_c)", 362 Insert: "insert into t1(c1,c2,c3) select foo(:a_a), foo(:a_a, :a_b), :a_c from dual where (:a_pk1,:a_pk2) <= (1,'aaa')", 363 Update: "update t1 set c2=foo(:a_a, :a_b), c3=:a_c where c1=(foo(:b_a)) and (:b_pk1,:b_pk2) <= (1,'aaa')", 364 Delete: "delete from t1 where c1=(foo(:b_a)) and (:b_pk1,:b_pk2) <= (1,'aaa')", 365 }, 366 }, 367 }, 368 }, { 369 input: &binlogdatapb.Filter{ 370 Rules: []*binlogdatapb.Rule{{ 371 Match: "t1", 372 Filter: "select a + b as c1, c as c2 from t1", 373 }}, 374 }, 375 plan: &TestReplicatorPlan{ 376 VStreamFilter: &binlogdatapb.Filter{ 377 Rules: []*binlogdatapb.Rule{{ 378 Match: "t1", 379 Filter: "select a, b, c from t1", 380 }}, 381 }, 382 TargetTables: []string{"t1"}, 383 TablePlans: map[string]*TestTablePlan{ 384 "t1": { 385 TargetName: "t1", 386 SendRule: "t1", 387 PKReferences: []string{"a", "b"}, 388 InsertFront: "insert into t1(c1,c2)", 389 InsertValues: "(:a_a + :a_b,:a_c)", 390 Insert: "insert into t1(c1,c2) values (:a_a + :a_b,:a_c)", 391 Update: "update t1 set c2=:a_c where c1=(:b_a + :b_b)", 392 Delete: "delete from t1 where c1=(:b_a + :b_b)", 393 }, 394 }, 395 }, 396 planpk: &TestReplicatorPlan{ 397 VStreamFilter: &binlogdatapb.Filter{ 398 Rules: []*binlogdatapb.Rule{{ 399 Match: "t1", 400 Filter: "select a, b, c, pk1, pk2 from t1", 401 }}, 402 }, 403 TargetTables: []string{"t1"}, 404 TablePlans: map[string]*TestTablePlan{ 405 "t1": { 406 TargetName: "t1", 407 SendRule: "t1", 408 PKReferences: []string{"a", "b", "pk1", "pk2"}, 409 InsertFront: "insert into t1(c1,c2)", 410 InsertValues: "(:a_a + :a_b,:a_c)", 411 Insert: "insert into t1(c1,c2) select :a_a + :a_b, :a_c from dual where (:a_pk1,:a_pk2) <= (1,'aaa')", 412 Update: "update t1 set c2=:a_c where c1=(:b_a + :b_b) and (:b_pk1,:b_pk2) <= (1,'aaa')", 413 Delete: "delete from t1 where c1=(:b_a + :b_b) and (:b_pk1,:b_pk2) <= (1,'aaa')", 414 }, 415 }, 416 }, 417 }, { 418 // Keywords as names. 419 input: &binlogdatapb.Filter{ 420 Rules: []*binlogdatapb.Rule{{ 421 Match: "t1", 422 Filter: "select c1, c2, `primary` from `primary`", 423 }}, 424 }, 425 plan: &TestReplicatorPlan{ 426 VStreamFilter: &binlogdatapb.Filter{ 427 Rules: []*binlogdatapb.Rule{{ 428 Match: "primary", 429 Filter: "select c1, c2, `primary` from `primary`", 430 }}, 431 }, 432 TargetTables: []string{"t1"}, 433 TablePlans: map[string]*TestTablePlan{ 434 "primary": { 435 TargetName: "t1", 436 SendRule: "primary", 437 PKReferences: []string{"c1"}, 438 InsertFront: "insert into t1(c1,c2,`primary`)", 439 InsertValues: "(:a_c1,:a_c2,:a_primary)", 440 Insert: "insert into t1(c1,c2,`primary`) values (:a_c1,:a_c2,:a_primary)", 441 Update: "update t1 set c2=:a_c2, `primary`=:a_primary where c1=:b_c1", 442 Delete: "delete from t1 where c1=:b_c1", 443 }, 444 }, 445 }, 446 planpk: &TestReplicatorPlan{ 447 VStreamFilter: &binlogdatapb.Filter{ 448 Rules: []*binlogdatapb.Rule{{ 449 Match: "primary", 450 Filter: "select c1, c2, `primary`, pk1, pk2 from `primary`", 451 }}, 452 }, 453 TargetTables: []string{"t1"}, 454 TablePlans: map[string]*TestTablePlan{ 455 "primary": { 456 TargetName: "t1", 457 SendRule: "primary", 458 PKReferences: []string{"c1", "pk1", "pk2"}, 459 InsertFront: "insert into t1(c1,c2,`primary`)", 460 InsertValues: "(:a_c1,:a_c2,:a_primary)", 461 Insert: "insert into t1(c1,c2,`primary`) select :a_c1, :a_c2, :a_primary from dual where (:a_pk1,:a_pk2) <= (1,'aaa')", 462 Update: "update t1 set c2=:a_c2, `primary`=:a_primary where c1=:b_c1 and (:b_pk1,:b_pk2) <= (1,'aaa')", 463 Delete: "delete from t1 where c1=:b_c1 and (:b_pk1,:b_pk2) <= (1,'aaa')", 464 }, 465 }, 466 }, 467 }, { 468 // keyspace_id 469 input: &binlogdatapb.Filter{ 470 Rules: []*binlogdatapb.Rule{{ 471 Match: "t1", 472 Filter: "select c1, c2, keyspace_id() ksid from t1", 473 }}, 474 }, 475 plan: &TestReplicatorPlan{ 476 VStreamFilter: &binlogdatapb.Filter{ 477 Rules: []*binlogdatapb.Rule{{ 478 Match: "t1", 479 Filter: "select c1, c2, keyspace_id() from t1", 480 }}, 481 }, 482 TargetTables: []string{"t1"}, 483 TablePlans: map[string]*TestTablePlan{ 484 "t1": { 485 TargetName: "t1", 486 SendRule: "t1", 487 PKReferences: []string{"c1"}, 488 InsertFront: "insert into t1(c1,c2,ksid)", 489 InsertValues: "(:a_c1,:a_c2,:a_keyspace_id)", 490 Insert: "insert into t1(c1,c2,ksid) values (:a_c1,:a_c2,:a_keyspace_id)", 491 Update: "update t1 set c2=:a_c2, ksid=:a_keyspace_id where c1=:b_c1", 492 Delete: "delete from t1 where c1=:b_c1", 493 }, 494 }, 495 }, 496 planpk: &TestReplicatorPlan{ 497 VStreamFilter: &binlogdatapb.Filter{ 498 Rules: []*binlogdatapb.Rule{{ 499 Match: "t1", 500 Filter: "select c1, c2, keyspace_id(), pk1, pk2 from t1", 501 }}, 502 }, 503 TargetTables: []string{"t1"}, 504 TablePlans: map[string]*TestTablePlan{ 505 "t1": { 506 TargetName: "t1", 507 SendRule: "t1", 508 PKReferences: []string{"c1", "pk1", "pk2"}, 509 InsertFront: "insert into t1(c1,c2,ksid)", 510 InsertValues: "(:a_c1,:a_c2,:a_keyspace_id)", 511 Insert: "insert into t1(c1,c2,ksid) select :a_c1, :a_c2, :a_keyspace_id from dual where (:a_pk1,:a_pk2) <= (1,'aaa')", 512 Update: "update t1 set c2=:a_c2, ksid=:a_keyspace_id where c1=:b_c1 and (:b_pk1,:b_pk2) <= (1,'aaa')", 513 Delete: "delete from t1 where c1=:b_c1 and (:b_pk1,:b_pk2) <= (1,'aaa')", 514 }, 515 }, 516 }, 517 }, { 518 // syntax error 519 input: &binlogdatapb.Filter{ 520 Rules: []*binlogdatapb.Rule{{ 521 Match: "t1", 522 Filter: "bad query", 523 }}, 524 }, 525 err: "syntax error at position 4 near 'bad'", 526 }, { 527 // not a select 528 input: &binlogdatapb.Filter{ 529 Rules: []*binlogdatapb.Rule{{ 530 Match: "t1", 531 Filter: "update t1 set val=1", 532 }}, 533 }, 534 err: "unexpected: update t1 set val = 1", 535 }, { 536 // no distinct 537 input: &binlogdatapb.Filter{ 538 Rules: []*binlogdatapb.Rule{{ 539 Match: "t1", 540 Filter: "select distinct c1 from t1", 541 }}, 542 }, 543 err: "unexpected: select distinct c1 from t1", 544 }, { 545 // no ',' join 546 input: &binlogdatapb.Filter{ 547 Rules: []*binlogdatapb.Rule{{ 548 Match: "t1", 549 Filter: "select * from t1, t2", 550 }}, 551 }, 552 err: "unexpected: select * from t1, t2", 553 }, { 554 // no join 555 input: &binlogdatapb.Filter{ 556 Rules: []*binlogdatapb.Rule{{ 557 Match: "t1", 558 Filter: "select * from t1 join t2", 559 }}, 560 }, 561 err: "unexpected: select * from t1 join t2", 562 }, { 563 // no subqueries 564 input: &binlogdatapb.Filter{ 565 Rules: []*binlogdatapb.Rule{{ 566 Match: "t1", 567 Filter: "select * from (select * from t2) as a", 568 }}, 569 }, 570 err: "unexpected: select * from (select * from t2) as a", 571 }, { 572 // cannot combine '*' with other 573 input: &binlogdatapb.Filter{ 574 Rules: []*binlogdatapb.Rule{{ 575 Match: "t1", 576 Filter: "select *, c1 from t1", 577 }}, 578 }, 579 err: "unexpected: select *, c1 from t1", 580 }, { 581 // cannot combine '*' with other (different code path) 582 input: &binlogdatapb.Filter{ 583 Rules: []*binlogdatapb.Rule{{ 584 Match: "t1", 585 Filter: "select c1, * from t1", 586 }}, 587 }, 588 err: "unexpected: *", 589 }, { 590 // no distinct in func 591 input: &binlogdatapb.Filter{ 592 Rules: []*binlogdatapb.Rule{{ 593 Match: "t1", 594 Filter: "select hour(distinct c1) as a from t1", 595 }}, 596 }, 597 err: "syntax error at position 21 near 'distinct'", 598 }, { 599 // funcs need alias 600 input: &binlogdatapb.Filter{ 601 Rules: []*binlogdatapb.Rule{{ 602 Match: "t1", 603 Filter: "select hour(c1) from t1", 604 }}, 605 }, 606 err: "expression needs an alias: hour(c1)", 607 }, { 608 // only count(*) 609 input: &binlogdatapb.Filter{ 610 Rules: []*binlogdatapb.Rule{{ 611 Match: "t1", 612 Filter: "select count(c1) as c from t1", 613 }}, 614 }, 615 err: "only count(*) is supported: count(c1)", 616 }, { 617 // no sum(*) 618 input: &binlogdatapb.Filter{ 619 Rules: []*binlogdatapb.Rule{{ 620 Match: "t1", 621 Filter: "select sum(*) as c from t1", 622 }}, 623 }, 624 err: "syntax error at position 13", 625 }, { 626 // sum should have only one argument 627 input: &binlogdatapb.Filter{ 628 Rules: []*binlogdatapb.Rule{{ 629 Match: "t1", 630 Filter: "select sum(a, b) as c from t1", 631 }}, 632 }, 633 err: "syntax error at position 14", 634 }, { 635 // no complex expr in sum 636 input: &binlogdatapb.Filter{ 637 Rules: []*binlogdatapb.Rule{{ 638 Match: "t1", 639 Filter: "select sum(a + b) as c from t1", 640 }}, 641 }, 642 err: "unexpected: sum(a + b)", 643 }, { 644 // no complex expr in group by 645 input: &binlogdatapb.Filter{ 646 Rules: []*binlogdatapb.Rule{{ 647 Match: "t1", 648 Filter: "select a from t1 group by a + 1", 649 }}, 650 }, 651 err: "unexpected: a + 1", 652 }, { 653 // group by does not reference alias 654 input: &binlogdatapb.Filter{ 655 Rules: []*binlogdatapb.Rule{{ 656 Match: "t1", 657 Filter: "select a as b from t1 group by a", 658 }}, 659 }, 660 err: "group by expression does not reference an alias in the select list: a", 661 }, { 662 // cannot group by aggr 663 input: &binlogdatapb.Filter{ 664 Rules: []*binlogdatapb.Rule{{ 665 Match: "t1", 666 Filter: "select count(*) as a from t1 group by a", 667 }}, 668 }, 669 err: "group by expression is not allowed to reference an aggregate expression: a", 670 }} 671 672 PrimaryKeyInfos := map[string][]*ColumnInfo{ 673 "t1": {&ColumnInfo{Name: "c1", IsPK: true}}, 674 } 675 676 copyState := map[string]*sqltypes.Result{ 677 "t1": sqltypes.MakeTestResult( 678 sqltypes.MakeTestFields( 679 "pk1|pk2", 680 "int64|varchar", 681 ), 682 "1|aaa", 683 ), 684 } 685 686 for _, tcase := range testcases { 687 plan, err := buildReplicatorPlan(getSource(tcase.input), PrimaryKeyInfos, nil, binlogplayer.NewStats()) 688 gotPlan, _ := json.Marshal(plan) 689 wantPlan, _ := json.Marshal(tcase.plan) 690 if string(gotPlan) != string(wantPlan) { 691 t.Errorf("Filter(%v):\n%s, want\n%s", tcase.input, gotPlan, wantPlan) 692 } 693 gotErr := "" 694 if err != nil { 695 gotErr = err.Error() 696 } 697 if gotErr != tcase.err { 698 t.Errorf("Filter err(%v): %s, want %v", tcase.input, gotErr, tcase.err) 699 } 700 701 plan, err = buildReplicatorPlan(getSource(tcase.input), PrimaryKeyInfos, copyState, binlogplayer.NewStats()) 702 if err != nil { 703 continue 704 } 705 gotPlan, _ = json.Marshal(plan) 706 wantPlan, _ = json.Marshal(tcase.planpk) 707 if string(gotPlan) != string(wantPlan) { 708 t.Errorf("Filter(%v,copyState):\n%s, want\n%s", tcase.input, gotPlan, wantPlan) 709 } 710 } 711 } 712 713 func getSource(filter *binlogdatapb.Filter) *binlogdatapb.BinlogSource { 714 return &binlogdatapb.BinlogSource{Filter: filter} 715 } 716 717 func TestBuildPlayerPlanNoDup(t *testing.T) { 718 PrimaryKeyInfos := map[string][]*ColumnInfo{ 719 "t1": {&ColumnInfo{Name: "c1"}}, 720 "t2": {&ColumnInfo{Name: "c2"}}, 721 } 722 input := &binlogdatapb.Filter{ 723 Rules: []*binlogdatapb.Rule{{ 724 Match: "t1", 725 Filter: "select * from t", 726 }, { 727 Match: "t2", 728 Filter: "select * from t", 729 }}, 730 } 731 _, err := buildReplicatorPlan(getSource(input), PrimaryKeyInfos, nil, binlogplayer.NewStats()) 732 want := "more than one target for source table t" 733 if err == nil || !strings.Contains(err.Error(), want) { 734 t.Errorf("buildReplicatorPlan err: %v, must contain: %v", err, want) 735 } 736 } 737 738 func TestBuildPlayerPlanExclude(t *testing.T) { 739 PrimaryKeyInfos := map[string][]*ColumnInfo{ 740 "t1": {&ColumnInfo{Name: "c1"}}, 741 "t2": {&ColumnInfo{Name: "c2"}}, 742 } 743 input := &binlogdatapb.Filter{ 744 Rules: []*binlogdatapb.Rule{{ 745 Match: "t2", 746 Filter: "exclude", 747 }, { 748 Match: "/.*", 749 Filter: "", 750 }}, 751 } 752 plan, err := buildReplicatorPlan(getSource(input), PrimaryKeyInfos, nil, binlogplayer.NewStats()) 753 assert.NoError(t, err) 754 755 want := &TestReplicatorPlan{ 756 VStreamFilter: &binlogdatapb.Filter{ 757 Rules: []*binlogdatapb.Rule{{ 758 Match: "t1", 759 Filter: "select * from t1", 760 }}, 761 }, 762 TargetTables: []string{"t1"}, 763 TablePlans: map[string]*TestTablePlan{ 764 "t1": { 765 TargetName: "t1", 766 SendRule: "t1", 767 }, 768 }, 769 } 770 771 gotPlan, _ := json.Marshal(plan) 772 wantPlan, _ := json.Marshal(want) 773 assert.Equal(t, string(gotPlan), string(wantPlan)) 774 }