1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/hanchuanchuan-goInception

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
physical_plan_test.go 47 КБ
Копировать Редактировать Исходные данные Просмотреть построчно История
hanchuanchuan Отправлено 4 лет назад c64eddf
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319
// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package core_test
import (
"github.com/hanchuanchuan/goInception/infoschema"
"github.com/hanchuanchuan/goInception/kv"
"github.com/hanchuanchuan/goInception/model"
"github.com/hanchuanchuan/goInception/parser"
"github.com/hanchuanchuan/goInception/planner"
"github.com/hanchuanchuan/goInception/planner/core"
"github.com/hanchuanchuan/goInception/session"
"github.com/hanchuanchuan/goInception/sessionctx"
"github.com/hanchuanchuan/goInception/util/testleak"
. "github.com/pingcap/check"
"golang.org/x/net/context"
)
var _ = Suite(&testPlanSuite{})
type testPlanSuite struct {
*parser.Parser
is infoschema.InfoSchema
}
func (s *testPlanSuite) SetUpSuite(c *C) {
s.is = infoschema.MockInfoSchema([]*model.TableInfo{core.MockTable()})
s.Parser = parser.New()
}
func (s *testPlanSuite) TestDAGPlanBuilderSimpleCase(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
defer func() {
dom.Close()
store.Close()
}()
se, err := session.CreateSession4Test(store)
c.Assert(err, IsNil)
_, err = se.Execute(context.Background(), "use test")
c.Assert(err, IsNil)
tests := []struct {
sql string
best string
}{
// Test index hint.
{
sql: "select * from t t1 use index(c_d_e)",
best: "IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))",
},
// Test ts + Sort vs. DoubleRead + filter.
{
sql: "select a from t where a between 1 and 2 order by c",
best: "TableReader(Table(t))->Sort->Projection",
},
// Test DNF condition + Double Read.
{
sql: "select * from t where (t.c > 0 and t.c < 2) or (t.c > 4 and t.c < 6) or (t.c > 8 and t.c < 10) or (t.c > 12 and t.c < 14) or (t.c > 16 and t.c < 18)",
best: "IndexLookUp(Index(t.c_d_e)[(0 +inf,2 NULL) (4 +inf,6 NULL) (8 +inf,10 NULL) (12 +inf,14 NULL) (16 +inf,18 NULL)], Table(t))",
},
{
sql: "select * from t where (t.c > 0 and t.c < 1) or (t.c > 2 and t.c < 3) or (t.c > 4 and t.c < 5) or (t.c > 6 and t.c < 7) or (t.c > 9 and t.c < 10)",
best: "Dual",
},
// Test TopN to table branch in double read.
{
sql: "select * from t where t.c = 1 and t.e = 1 order by t.b limit 1",
best: "IndexLookUp(Index(t.c_d_e)[[1,1]]->Sel([eq(test.t.e, 1)]), Table(t))->TopN([test.t.b],0,1)",
},
// Test Null Range
{
sql: "select * from t where t.e_str is null",
best: "IndexLookUp(Index(t.e_d_c_str_prefix)[[NULL,NULL]], Table(t))",
},
// Test Null Range but the column has not null flag.
{
sql: "select * from t where t.c is null",
best: "Dual",
},
// Test TopN to index branch in double read.
{
sql: "select * from t where t.c = 1 and t.e = 1 order by t.e limit 1",
best: "IndexLookUp(Index(t.c_d_e)[[1,1]]->Sel([eq(test.t.e, 1)]), Table(t))->TopN([test.t.e],0,1)",
},
// Test TopN to Limit in double read.
{
sql: "select * from t where t.c = 1 and t.e = 1 order by t.d limit 1",
best: "IndexLookUp(Index(t.c_d_e)[[1,1]]->Sel([eq(test.t.e, 1)])->Limit, Table(t))->Limit",
},
// Test TopN to Limit in index single read.
{
sql: "select c from t where t.c = 1 and t.e = 1 order by t.d limit 1",
best: "IndexReader(Index(t.c_d_e)[[1,1]]->Sel([eq(test.t.e, 1)])->Limit)->Limit->Projection",
},
// Test TopN to Limit in table single read.
{
sql: "select c from t order by t.a limit 1",
best: "TableReader(Table(t)->Limit)->Limit->Projection",
},
// Test TopN push down in table single read.
{
sql: "select c from t order by t.a + t.b limit 1",
best: "TableReader(Table(t)->TopN([plus(test.t.a, test.t.b)],0,1))->TopN([plus(test.t.a, test.t.b)],0,1)->Projection",
},
// Test Limit push down in table single read.
{
sql: "select c from t limit 1",
best: "TableReader(Table(t)->Limit)->Limit",
},
// Test Limit push down in index single read.
{
sql: "select c from t where c = 1 limit 1",
best: "IndexReader(Index(t.c_d_e)[[1,1]]->Limit)->Limit",
},
// Test index single read and Selection.
{
sql: "select c from t where c = 1",
best: "IndexReader(Index(t.c_d_e)[[1,1]])",
},
// Test index single read and Sort.
{
sql: "select c from t order by c",
best: "IndexReader(Index(t.c_d_e)[[NULL,+inf]])",
},
// Test index single read and Sort.
{
sql: "select c from t where c = 1 order by e",
best: "IndexReader(Index(t.c_d_e)[[1,1]])->Sort->Projection",
},
// Test Limit push down in double single read.
{
sql: "select c, b from t where c = 1 limit 1",
best: "IndexLookUp(Index(t.c_d_e)[[1,1]]->Limit, Table(t))->Limit->Projection",
},
// Test Selection + Limit push down in double single read.
{
sql: "select c, b from t where c = 1 and e = 1 and b = 1 limit 1",
best: "IndexLookUp(Index(t.c_d_e)[[1,1]]->Sel([eq(test.t.e, 1)]), Table(t)->Sel([eq(test.t.b, 1)])->Limit)->Limit->Projection",
},
// Test Order by multi columns.
{
sql: "select c from t where c = 1 order by d, c",
best: "IndexReader(Index(t.c_d_e)[[1,1]])->Sort->Projection",
},
// Test for index with length.
{
sql: "select c_str from t where e_str = '1' order by d_str, c_str",
best: `IndexLookUp(Index(t.e_d_c_str_prefix)[["1","1"]], Table(t))->Sort->Projection`,
},
// Test PK in index single read.
{
sql: "select c from t where t.c = 1 and t.a > 1 order by t.d limit 1",
best: "IndexReader(Index(t.c_d_e)[[1,1]]->Sel([gt(test.t.a, 1)])->Limit)->Limit->Projection",
},
// Test composed index.
// FIXME: The TopN didn't be pushed.
{
sql: "select c from t where t.c = 1 and t.d = 1 order by t.a limit 1",
best: "IndexReader(Index(t.c_d_e)[[1 1,1 1]])->TopN([test.t.a],0,1)->Projection",
},
// Test PK in index double read.
{
sql: "select * from t where t.c = 1 and t.a > 1 order by t.d limit 1",
best: "IndexLookUp(Index(t.c_d_e)[[1,1]]->Sel([gt(test.t.a, 1)])->Limit, Table(t))->Limit",
},
// Test index filter condition push down.
{
sql: "select * from t use index(e_d_c_str_prefix) where t.c_str = 'abcdefghijk' and t.d_str = 'd' and t.e_str = 'e'",
best: "IndexLookUp(Index(t.e_d_c_str_prefix)[[\"e\" \"d\" \"abcdefghij\",\"e\" \"d\" \"abcdefghij\"]], Table(t)->Sel([eq(test.t.c_str, abcdefghijk)]))",
},
{
sql: "select * from t use index(e_d_c_str_prefix) where t.e_str = b'1110000'",
best: "IndexLookUp(Index(t.e_d_c_str_prefix)[[\"p\",\"p\"]], Table(t))",
},
{
sql: "select * from (select * from t use index() order by b) t left join t t1 on t.a=t1.a limit 10",
best: "IndexJoin{TableReader(Table(t)->TopN([test.t.b],0,10))->TopN([test.t.b],0,10)->TableReader(Table(t))}(test.t.a,t1.a)->Limit",
},
}
for i, tt := range tests {
comment := Commentf("case:%v sql:%s", i, tt.sql)
stmt, err := s.ParseOneStmt(tt.sql, "", "")
c.Assert(err, IsNil, comment)
err = se.NewTxn()
c.Assert(err, IsNil)
p, err := planner.Optimize(se, stmt, s.is)
c.Assert(err, IsNil)
c.Assert(core.ToString(p), Equals, tt.best, comment)
}
}
func (s *testPlanSuite) TestDAGPlanBuilderJoin(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
defer func() {
dom.Close()
store.Close()
}()
se, err := session.CreateSession4Test(store)
c.Assert(err, IsNil)
_, err = se.Execute(context.Background(), "use test")
c.Assert(err, IsNil)
tests := []struct {
sql string
best string
}{
{
sql: "select * from t t1 join t t2 on t1.a = t2.c_str",
best: "LeftHashJoin{TableReader(Table(t))->Projection->TableReader(Table(t))->Projection}(cast(t1.a),cast(t2.c_str))->Projection",
},
{
sql: "select * from t t1 join t t2 on t1.b = t2.a",
best: "LeftHashJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.b,t2.a)",
},
{
sql: "select * from t t1 join t t2 on t1.a = t2.a join t t3 on t1.a = t3.a",
best: "MergeInnerJoin{MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)->TableReader(Table(t))}(t1.a,t3.a)",
},
{
sql: "select * from t t1 join t t2 on t1.a = t2.a join t t3 on t1.b = t3.a",
best: "LeftHashJoin{MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)->TableReader(Table(t))}(t1.b,t3.a)",
},
{
sql: "select * from t t1 join t t2 on t1.b = t2.a order by t1.a",
best: "LeftHashJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.b,t2.a)->Sort",
},
{
sql: "select * from t t1 join t t2 on t1.b = t2.a order by t1.a limit 1",
best: "IndexJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.b,t2.a)->Limit",
},
// Test hash join's hint.
{
sql: "select /*+ TIDB_HJ(t1, t2) */ * from t t1 join t t2 on t1.b = t2.a order by t1.a limit 1",
best: "IndexJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.b,t2.a)->Limit",
},
{
sql: "select * from t t1 left join t t2 on t1.b = t2.a where 1 = 1 limit 1",
best: "IndexJoin{TableReader(Table(t)->Limit)->Limit->TableReader(Table(t))}(t1.b,t2.a)->Limit",
},
{
sql: "select * from t t1 join t t2 on t1.b = t2.a and t1.c = 1 and t1.d = 1 and t1.e = 1 order by t1.a limit 1",
best: "IndexJoin{IndexLookUp(Index(t.c_d_e)[[1 1 1,1 1 1]], Table(t))->TableReader(Table(t))}(t1.b,t2.a)->TopN([t1.a],0,1)",
},
{
sql: "select * from t t1 join t t2 on t1.b = t2.b join t t3 on t1.b = t3.b",
best: "LeftHashJoin{LeftHashJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.b,t2.b)->TableReader(Table(t))}(t1.b,t3.b)",
},
{
sql: "select * from t t1 join t t2 on t1.a = t2.a order by t1.a",
best: "MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)",
},
{
sql: "select * from t t1 left outer join t t2 on t1.a = t2.a right outer join t t3 on t1.a = t3.a",
best: "MergeRightOuterJoin{MergeLeftOuterJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)->TableReader(Table(t))}(t1.a,t3.a)",
},
{
sql: "select * from t t1 join t t2 on t1.a = t2.a join t t3 on t1.a = t3.a and t1.b = 1 and t3.c = 1",
best: "LeftHashJoin{IndexJoin{TableReader(Table(t)->Sel([eq(t1.b, 1)]))->TableReader(Table(t))}(t1.a,t2.a)->IndexLookUp(Index(t.c_d_e)[[1,1]], Table(t))}(t1.a,t3.a)",
},
{
sql: "select * from t where t.c in (select b from t s where s.a = t.a)",
best: "MergeSemiJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,s.a)",
},
{
sql: "select t.c in (select b from t s where s.a = t.a) from t",
best: "MergeLeftOuterSemiJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,s.a)->Projection",
},
// Test Single Merge Join.
// Merge Join now enforce a sort.
{
sql: "select /*+ TIDB_SMJ(t1,t2)*/ * from t t1, t t2 where t1.a = t2.b",
best: "LeftHashJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.b)",
},
{
sql: "select /*+ TIDB_SMJ(t1,t2)*/ * from t t1, t t2 where t1.a = t2.a",
best: "MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)",
},
// Test Single Merge Join + Sort.
{
sql: "select /*+ TIDB_SMJ(t1,t2)*/ * from t t1, t t2 where t1.a = t2.a order by t2.a",
best: "MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)",
},
{
sql: "select /*+ TIDB_SMJ(t1,t2)*/ * from t t1, t t2 where t1.b = t2.b order by t2.a",
best: "LeftHashJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.b,t2.b)->Sort",
},
// Test Single Merge Join + Sort + desc.
{
sql: "select /*+ TIDB_SMJ(t1,t2)*/ * from t t1, t t2 where t1.a = t2.a order by t2.a desc",
best: "MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)->Sort",
},
{
sql: "select /*+ TIDB_SMJ(t1,t2)*/ * from t t1, t t2 where t1.b = t2.b order by t2.b desc",
best: "LeftHashJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.b,t2.b)->Sort",
},
// Test Multi Merge Join.
{
sql: "select /*+ TIDB_SMJ(t1,t2,t3)*/ * from t t1, t t2, t t3 where t1.a = t2.a and t2.a = t3.a",
best: "MergeInnerJoin{MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)->TableReader(Table(t))}(t2.a,t3.a)",
},
{
sql: "select /*+ TIDB_SMJ(t1,t2,t3)*/ * from t t1, t t2, t t3 where t1.a = t2.b and t2.a = t3.b",
best: "LeftHashJoin{LeftHashJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.b)->TableReader(Table(t))}(t2.a,t3.b)",
},
// Test Multi Merge Join with multi keys.
// TODO: More tests should be added.
{
sql: "select /*+ TIDB_SMJ(t1,t2,t3)*/ * from t t1, t t2, t t3 where t1.c = t2.c and t1.d = t2.d and t3.c = t1.c and t3.d = t1.d",
best: "MergeInnerJoin{MergeInnerJoin{IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))->IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))}(t1.c,t2.c)(t1.d,t2.d)->IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))}(t1.c,t3.c)(t1.d,t3.d)",
},
{
sql: "select /*+ TIDB_SMJ(t1,t2,t3)*/ * from t t1, t t2, t t3 where t1.c = t2.c and t1.d = t2.d and t3.c = t1.c and t3.d = t1.d order by t1.c",
best: "MergeInnerJoin{MergeInnerJoin{IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))->IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))}(t1.c,t2.c)(t1.d,t2.d)->IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))}(t1.c,t3.c)(t1.d,t3.d)",
},
// Test Multi Merge Join + Outer Join.
{
sql: "select /*+ TIDB_SMJ(t1,t2,t3)*/ * from t t1 left outer join t t2 on t1.a = t2.a left outer join t t3 on t2.a = t3.a",
best: "LeftHashJoin{MergeLeftOuterJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)->TableReader(Table(t))}(t2.a,t3.a)",
},
{
sql: "select /*+ TIDB_SMJ(t1,t2,t3)*/ * from t t1 left outer join t t2 on t1.a = t2.a left outer join t t3 on t1.a = t3.a",
best: "MergeLeftOuterJoin{MergeLeftOuterJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)->TableReader(Table(t))}(t1.a,t3.a)",
},
// Test Index Join + TableScan.
{
sql: "select /*+ TIDB_INLJ(t1, t2) */ * from t t1, t t2 where t1.a = t2.a",
best: "MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)",
},
// Test Index Join + DoubleRead.
{
sql: "select /*+ TIDB_INLJ(t1, t2) */ * from t t1, t t2 where t1.a = t2.c",
best: "MergeInnerJoin{TableReader(Table(t))->IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))}(t1.a,t2.c)",
},
// // Test Index Join + SingleRead.
// {
// sql: "select /*+ TIDB_INLJ(t1, t2) */ t1.a , t2.a from t t1, t t2 where t1.a = t2.c",
// best: "IndexJoin{TableReader(Table(t))->IndexReader(Index(t.c_d_e)[[NULL,+inf]])}(t1.a,t2.c)->Projection",
// },
// // Test Index Join + Order by.
// {
// sql: "select /*+ TIDB_INLJ(t1, t2) */ t1.a, t2.a from t t1, t t2 where t1.a = t2.a order by t1.c",
// best: "IndexJoin{IndexReader(Index(t.c_d_e)[[NULL,+inf]])->TableReader(Table(t))}(t1.a,t2.a)->Projection",
// },
// // Test Index Join + Order by.
// {
// sql: "select /*+ TIDB_INLJ(t1, t2) */ t1.a, t2.a from t t1, t t2 where t1.a = t2.a order by t2.c",
// best: "IndexJoin{TableReader(Table(t))->IndexReader(Index(t.c_d_e)[[NULL,+inf]])}(t2.a,t1.a)->Projection",
// },
// // Test Index Join + TableScan + Rotate.
// {
// sql: "select /*+ TIDB_INLJ(t2) */ t1.a , t2.a from t t1, t t2 where t1.a = t2.c",
// best: "IndexJoin{TableReader(Table(t))->TableReader(Table(t))}(t2.c,t1.a)->Projection",
// },
// // Test Index Join + OuterJoin + TableScan.
// {
// sql: "select /*+ TIDB_INLJ(t1, t2) */ * from t t1 left outer join t t2 on t1.a = t2.a and t2.b < 1",
// best: "IndexJoin{TableReader(Table(t))->TableReader(Table(t)->Sel([lt(t2.b, 1)]))}(t1.a,t2.a)",
// },
// {
// sql: "select /*+ TIDB_INLJ(t1, t2) */ * from t t1 join t t2 on t1.d=t2.d and t2.c = 1",
// best: "IndexJoin{TableReader(Table(t))->IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))}(t1.d,t2.d)",
// },
// // Test Index Join failed.
// {
// sql: "select /*+ TIDB_INLJ(t1, t2) */ * from t t1 left outer join t t2 on t1.a = t2.b",
// best: "LeftHashJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.b)",
// },
// // Test Index Join failed.
// {
// sql: "select /*+ TIDB_INLJ(t1) */ * from t t1 right outer join t t2 on t1.a = t2.b",
// best: "RightHashJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.b)",
// },
// // Test Semi Join hint success.
// {
// sql: "select /*+ TIDB_INLJ(t1) */ * from t t1 where t1.a in (select a from t t2)",
// best: "IndexJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)",
// },
// // Test Semi Join hint fail.
// {
// sql: "select /*+ TIDB_INLJ(t2) */ * from t t1 where t1.a in (select a from t t2)",
// best: "MergeSemiJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)",
// },
// {
// sql: "select /*+ TIDB_INLJ(t1) */ * from t t1 join t t2 where t1.c=t2.c and t1.f=t2.f",
// best: "IndexJoin{TableReader(Table(t))->IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))}(t1.c,t2.c)",
// },
// {
// sql: "select /*+ TIDB_INLJ(t1) */ * from t t1 join t t2 where t1.a = t2.a and t1.f=t2.f",
// best: "IndexJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)",
// },
// {
// sql: "select /*+ TIDB_INLJ(t1) */ * from t t1 join t t2 where t1.f=t2.f and t1.a=t2.a",
// best: "IndexJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)",
// },
// {
// sql: "select /*+ TIDB_INLJ(t1) */ * from t t1 join t t2 where t1.a=t2.a and t2.a in (1, 2)",
// best: "IndexJoin{TableReader(Table(t))->TableReader(Table(t)->Sel([in(t2.a, 1, 2)]))}(t1.a,t2.a)",
// },
}
for i, tt := range tests {
comment := Commentf("case:%v sql:%s", i, tt.sql)
stmt, err := s.ParseOneStmt(tt.sql, "", "")
c.Assert(err, IsNil, comment)
p, err := planner.Optimize(se, stmt, s.is)
c.Assert(err, IsNil)
c.Assert(core.ToString(p), Equals, tt.best, comment)
}
}
func (s *testPlanSuite) TestDAGPlanBuilderSubquery(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
defer func() {
dom.Close()
store.Close()
}()
se, err := session.CreateSession4Test(store)
c.Assert(err, IsNil)
_, err = se.Execute(context.Background(), "use test")
c.Assert(err, IsNil)
tests := []struct {
sql string
best string
}{
// Test join key with cast.
{
sql: "select * from t where exists (select s.a from t s having sum(s.a) = t.a )",
best: "LeftHashJoin{TableReader(Table(t))->Projection->TableReader(Table(t)->StreamAgg)->StreamAgg}(cast(test.t.a),sel_agg_1)->Projection",
},
{
sql: "select * from t where exists (select s.a from t s having sum(s.a) = t.a ) order by t.a",
best: "LeftHashJoin{TableReader(Table(t))->Projection->TableReader(Table(t)->StreamAgg)->StreamAgg}(cast(test.t.a),sel_agg_1)->Projection->Sort",
},
// FIXME: Report error by resolver.
//{
// sql: "select * from t where exists (select s.a from t s having s.a = t.a ) order by t.a",
// best: "SemiJoin{TableReader(Table(t))->Projection->TableReader(Table(t)->HashAgg)->HashAgg}(cast(test.t.a),sel_agg_1)->Projection->Sort",
//},
{
sql: "select * from t where a in (select s.a from t s) order by t.a",
best: "MergeSemiJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,s.a)",
},
// Test Nested sub query.
{
sql: "select * from t where exists (select s.a from t s where s.c in (select c from t as k where k.d = s.d) having sum(s.a) = t.a )",
best: "LeftHashJoin{TableReader(Table(t))->Projection->MergeSemiJoin{IndexReader(Index(t.c_d_e)[[NULL,+inf]])->IndexReader(Index(t.c_d_e)[[NULL,+inf]])}(s.c,k.c)(s.d,k.d)->StreamAgg}(cast(test.t.a),sel_agg_1)->Projection",
},
// Test Semi Join + Order by.
{
sql: "select * from t where a in (select a from t) order by b",
best: "MergeSemiJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,test.t.a)->Sort",
},
// Test Apply.
{
sql: "select t.c in (select count(*) from t s , t t1 where s.a = t.a and s.a = t1.a) from t",
best: "Apply{TableReader(Table(t))->IndexJoin{TableReader(Table(t))->TableReader(Table(t)->Sel([eq(t1.a, test.t.a)]))}(s.a,t1.a)->StreamAgg}->Projection",
},
{
sql: "select (select count(*) from t s , t t1 where s.a = t.a and s.a = t1.a) from t",
best: "LeftHashJoin{TableReader(Table(t))->MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(s.a,t1.a)->StreamAgg}(test.t.a,s.a)->Projection->Projection",
},
{
sql: "select (select count(*) from t s , t t1 where s.a = t.a and s.a = t1.a) from t order by t.a",
best: "LeftHashJoin{TableReader(Table(t))->MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(s.a,t1.a)->StreamAgg}(test.t.a,s.a)->Projection->Sort->Projection",
},
}
for _, tt := range tests {
comment := Commentf("for %s", tt.sql)
stmt, err := s.ParseOneStmt(tt.sql, "", "")
c.Assert(err, IsNil, comment)
p, err := planner.Optimize(se, stmt, s.is)
c.Assert(err, IsNil)
c.Assert(core.ToString(p), Equals, tt.best, Commentf("for %s", tt.sql))
}
}
func (s *testPlanSuite) TestDAGPlanTopN(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
defer func() {
dom.Close()
store.Close()
}()
se, err := session.CreateSession4Test(store)
c.Assert(err, IsNil)
_, err = se.Execute(context.Background(), "use test")
c.Assert(err, IsNil)
tests := []struct {
sql string
best string
}{
{
sql: "select * from t t1 left join t t2 on t1.b = t2.b left join t t3 on t2.b = t3.b order by t1.a limit 1",
best: "LeftHashJoin{LeftHashJoin{TableReader(Table(t)->Limit)->Limit->TableReader(Table(t))}(t1.b,t2.b)->TopN([t1.a],0,1)->TableReader(Table(t))}(t2.b,t3.b)->TopN([t1.a],0,1)",
},
{
sql: "select * from t t1 left join t t2 on t1.b = t2.b left join t t3 on t2.b = t3.b order by t1.b limit 1",
best: "LeftHashJoin{LeftHashJoin{TableReader(Table(t)->TopN([t1.b],0,1))->TopN([t1.b],0,1)->TableReader(Table(t))}(t1.b,t2.b)->TopN([t1.b],0,1)->TableReader(Table(t))}(t2.b,t3.b)->TopN([t1.b],0,1)",
},
{
sql: "select * from t t1 left join t t2 on t1.b = t2.b left join t t3 on t2.b = t3.b limit 1",
best: "LeftHashJoin{LeftHashJoin{TableReader(Table(t)->Limit)->Limit->TableReader(Table(t))}(t1.b,t2.b)->Limit->TableReader(Table(t))}(t2.b,t3.b)->Limit",
},
{
sql: "select * from t where b = 1 and c = 1 order by c limit 1",
best: "IndexLookUp(Index(t.c_d_e)[[1,1]], Table(t)->Sel([eq(test.t.b, 1)]))->Limit",
},
{
sql: "select * from t where c = 1 order by c limit 1",
best: "IndexLookUp(Index(t.c_d_e)[[1,1]]->Limit, Table(t))->Limit",
},
{
sql: "select * from t order by a limit 1",
best: "TableReader(Table(t)->Limit)->Limit",
},
{
sql: "select c from t order by c limit 1",
best: "IndexReader(Index(t.c_d_e)[[NULL,+inf]]->Limit)->Limit",
},
}
for i, tt := range tests {
comment := Commentf("case:%v sql:%s", i, tt.sql)
stmt, err := s.ParseOneStmt(tt.sql, "", "")
c.Assert(err, IsNil, comment)
p, err := planner.Optimize(se, stmt, s.is)
c.Assert(err, IsNil)
c.Assert(core.ToString(p), Equals, tt.best, comment)
}
}
func (s *testPlanSuite) TestDAGPlanBuilderBasePhysicalPlan(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
defer func() {
dom.Close()
store.Close()
}()
se, err := session.CreateSession4Test(store)
c.Assert(err, IsNil)
_, err = se.Execute(context.Background(), "use test")
c.Assert(err, IsNil)
tests := []struct {
sql string
best string
}{
// Test for update.
{
sql: "select * from t order by b limit 1 for update",
// TODO: This is not reasonable. Mysql do like this because the limit of InnoDB, should TiDB keep consistency with MySQL?
best: "TableReader(Table(t))->Lock->TopN([test.t.b],0,1)",
},
// Test complex update.
{
sql: "update t set a = 5 where b < 1 order by d limit 1",
best: "TableReader(Table(t)->Sel([lt(test.t.b, 1)])->TopN([test.t.d],0,1))->TopN([test.t.d],0,1)->Update",
},
// Test simple update.
{
sql: "update t set a = 5",
best: "TableReader(Table(t))->Update",
},
// TODO: Test delete/update with join.
// Test join hint for delete and update
{
sql: "delete /*+ TIDB_INLJ(t1, t2) */ t1 from t t1, t t2 where t1.c=t2.c",
best: "LeftHashJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.c,t2.c)->Delete",
},
{
sql: "delete /*+ TIDB_SMJ(t1, t2) */ from t1 using t t1, t t2 where t1.c=t2.c",
best: "LeftHashJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.c,t2.c)->Delete",
},
{
sql: "update /*+ TIDB_SMJ(t1, t2) */ t t1, t t2 set t1.a=1, t2.a=1 where t1.a=t2.a",
best: "MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)->Update",
},
{
sql: "update /*+ TIDB_HJ(t1, t2) */ t t1, t t2 set t1.a=1, t2.a=1 where t1.a=t2.a",
best: "MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)->Update",
},
// Test complex delete.
{
sql: "delete from t where b < 1 order by d limit 1",
best: "TableReader(Table(t)->Sel([lt(test.t.b, 1)])->TopN([test.t.d],0,1))->TopN([test.t.d],0,1)->Delete",
},
// Test simple delete.
{
sql: "delete from t",
best: "TableReader(Table(t))->Delete",
},
// Test "USE INDEX" hint in delete statement from single table
{
sql: "delete from t use index(c_d_e) where b = 1",
best: "IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t)->Sel([eq(test.t.b, 1)]))->Delete",
},
// Test complex insert.
{
sql: "insert into t select * from t where b < 1 order by d limit 1",
best: "TableReader(Table(t)->Sel([lt(test.t.b, 1)])->TopN([test.t.d],0,1))->TopN([test.t.d],0,1)->Insert",
},
// Test simple insert.
{
sql: "insert into t (a, b, c, e, f, g) values(0,0,0,0,0,0)",
best: "Insert",
},
// Test dual.
{
sql: "select 1",
best: "Dual->Projection",
},
{
sql: "select * from t where false",
best: "Dual",
},
// Test show.
{
sql: "show tables",
best: "Show",
},
}
for _, tt := range tests {
comment := Commentf("for %s", tt.sql)
stmt, err := s.ParseOneStmt(tt.sql, "", "")
c.Assert(err, IsNil, comment)
core.Preprocess(se, stmt, s.is, false)
p, err := planner.Optimize(se, stmt, s.is)
c.Assert(err, IsNil)
c.Assert(core.ToString(p), Equals, tt.best, Commentf("for %s", tt.sql))
}
}
func (s *testPlanSuite) TestDAGPlanBuilderUnion(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
defer func() {
dom.Close()
store.Close()
}()
se, err := session.CreateSession4Test(store)
c.Assert(err, IsNil)
_, err = se.Execute(context.Background(), "use test")
c.Assert(err, IsNil)
tests := []struct {
sql string
best string
}{
// Test simple union.
{
sql: "select * from t union all select * from t",
best: "UnionAll{TableReader(Table(t))->TableReader(Table(t))}",
},
// Test Order by + Union.
{
sql: "select * from t union all (select * from t) order by a ",
best: "UnionAll{TableReader(Table(t))->TableReader(Table(t))}->Sort",
},
// Test Limit + Union.
{
sql: "select * from t union all (select * from t) limit 1",
best: "UnionAll{TableReader(Table(t)->Limit)->Limit->TableReader(Table(t)->Limit)->Limit}->Limit",
},
// Test TopN + Union.
{
sql: "select a from t union all (select c from t) order by a limit 1",
best: "UnionAll{TableReader(Table(t)->Limit)->Limit->IndexReader(Index(t.c_d_e)[[NULL,+inf]]->Limit)->Limit}->TopN([t.a],0,1)",
},
}
for i, tt := range tests {
comment := Commentf("case:%v sql:%s", i, tt.sql)
stmt, err := s.ParseOneStmt(tt.sql, "", "")
c.Assert(err, IsNil, comment)
p, err := planner.Optimize(se, stmt, s.is)
c.Assert(err, IsNil)
c.Assert(core.ToString(p), Equals, tt.best, comment)
}
}
func (s *testPlanSuite) TestDAGPlanBuilderUnionScan(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
defer func() {
dom.Close()
store.Close()
}()
se, err := session.CreateSession4Test(store)
c.Assert(err, IsNil)
_, err = se.Execute(context.Background(), "use test")
c.Assert(err, IsNil)
tests := []struct {
sql string
best string
}{
// Read table.
{
sql: "select * from t",
best: "TableReader(Table(t))->UnionScan([])",
},
{
sql: "select * from t where b = 1",
best: "TableReader(Table(t)->Sel([eq(test.t.b, 1)]))->UnionScan([eq(test.t.b, 1)])",
},
{
sql: "select * from t where a = 1",
best: "TableReader(Table(t))->UnionScan([eq(test.t.a, 1)])",
},
{
sql: "select * from t where a = 1 order by a",
best: "TableReader(Table(t))->UnionScan([eq(test.t.a, 1)])",
},
{
sql: "select * from t where a = 1 order by b",
best: "TableReader(Table(t))->UnionScan([eq(test.t.a, 1)])->Sort",
},
{
sql: "select * from t where a = 1 limit 1",
best: "TableReader(Table(t))->UnionScan([eq(test.t.a, 1)])->Limit",
},
{
sql: "select * from t where c = 1",
best: "IndexLookUp(Index(t.c_d_e)[[1,1]], Table(t))->UnionScan([eq(test.t.c, 1)])",
},
{
sql: "select c from t where c = 1",
best: "IndexReader(Index(t.c_d_e)[[1,1]])->UnionScan([eq(test.t.c, 1)])->Projection",
},
}
for _, tt := range tests {
comment := Commentf("for %s", tt.sql)
stmt, err := s.ParseOneStmt(tt.sql, "", "")
c.Assert(err, IsNil, comment)
err = se.NewTxn()
c.Assert(err, IsNil)
// Make txn not read only.
se.Txn().Set(kv.Key("AAA"), []byte("BBB"))
se.StmtCommit()
p, err := planner.Optimize(se, stmt, s.is)
c.Assert(err, IsNil)
c.Assert(core.ToString(p), Equals, tt.best, Commentf("for %s", tt.sql))
}
}
func (s *testPlanSuite) TestDAGPlanBuilderAgg(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
defer func() {
dom.Close()
store.Close()
}()
se, err := session.CreateSession4Test(store)
c.Assert(err, IsNil)
se.Execute(context.Background(), "use test")
c.Assert(err, IsNil)
tests := []struct {
sql string
best string
}{
// Test distinct.
{
sql: "select distinct b from t",
best: "TableReader(Table(t)->HashAgg)->HashAgg",
},
{
sql: "select count(*) from (select * from t order by b) t group by b",
best: "TableReader(Table(t))->Sort->StreamAgg",
},
{
sql: "select count(*), x from (select b as bbb, a + 1 as x from (select * from t order by b) t) t group by bbb",
best: "TableReader(Table(t))->Sort->Projection->StreamAgg",
},
// Test agg + table.
{
sql: "select sum(a), avg(b + c) from t group by d",
best: "TableReader(Table(t)->HashAgg)->HashAgg",
},
{
sql: "select sum(distinct a), avg(b + c) from t group by d",
best: "TableReader(Table(t))->HashAgg",
},
// Test group by (c + d)
{
sql: "select sum(e), avg(e + c) from t where c = 1 group by (c + d)",
best: "IndexReader(Index(t.c_d_e)[[1,1]]->HashAgg)->HashAgg",
},
// Test stream agg + index single.
{
sql: "select sum(e), avg(e + c) from t where c = 1 group by c",
best: "IndexReader(Index(t.c_d_e)[[1,1]]->StreamAgg)->StreamAgg",
},
// Test hash agg + index single.
{
sql: "select sum(e), avg(e + c) from t where c = 1 group by d",
best: "IndexReader(Index(t.c_d_e)[[1,1]]->HashAgg)->HashAgg",
},
// Test hash agg + index double.
{
sql: "select sum(e), avg(b + c) from t where c = 1 and e = 1 group by d",
best: "IndexLookUp(Index(t.c_d_e)[[1,1]]->Sel([eq(test.t.e, 1)]), Table(t))->HashAgg",
},
// Test stream agg + index double.
{
sql: "select sum(e), avg(b + c) from t where c = 1 and b = 1 group by c",
best: "IndexLookUp(Index(t.c_d_e)[[1,1]], Table(t)->Sel([eq(test.t.b, 1)]))->StreamAgg",
},
// Test hash agg + order.
{
sql: "select sum(e) as k, avg(b + c) from t where c = 1 and b = 1 and e = 1 group by d order by k",
best: "IndexLookUp(Index(t.c_d_e)[[1,1]]->Sel([eq(test.t.e, 1)]), Table(t)->Sel([eq(test.t.b, 1)]))->HashAgg->Sort",
},
// Test stream agg + order.
{
sql: "select sum(e) as k, avg(b + c) from t where c = 1 and b = 1 and e = 1 group by c order by k",
best: "IndexLookUp(Index(t.c_d_e)[[1,1]]->Sel([eq(test.t.e, 1)]), Table(t)->Sel([eq(test.t.b, 1)]))->StreamAgg->Sort",
},
// Test agg can't push down.
{
sql: "select sum(to_base64(e)) from t where c = 1",
best: "IndexReader(Index(t.c_d_e)[[1,1]])->StreamAgg",
},
{
sql: "select (select count(1) k from t s where s.a = t.a having k != 0) from t",
best: "MergeLeftOuterJoin{TableReader(Table(t))->TableReader(Table(t))->Projection}(test.t.a,s.a)->Projection->Projection",
},
// Test stream agg with multi group by columns.
{
sql: "select sum(to_base64(e)) from t group by e,d,c order by c",
best: "IndexReader(Index(t.c_d_e)[[NULL,+inf]])->StreamAgg->Projection",
},
{
sql: "select sum(e+1) from t group by e,d,c order by c",
best: "IndexReader(Index(t.c_d_e)[[NULL,+inf]]->StreamAgg)->StreamAgg->Projection",
},
{
sql: "select sum(to_base64(e)) from t group by e,d,c order by c,e",
best: "IndexReader(Index(t.c_d_e)[[NULL,+inf]])->StreamAgg->Sort->Projection",
},
{
sql: "select sum(e+1) from t group by e,d,c order by c,e",
best: "IndexReader(Index(t.c_d_e)[[NULL,+inf]]->StreamAgg)->StreamAgg->Sort->Projection",
},
// Test stream agg + limit or sort
{
sql: "select count(*) from t group by g order by g limit 10",
best: "IndexReader(Index(t.g)[[NULL,+inf]]->StreamAgg)->StreamAgg->Limit->Projection",
},
{
sql: "select count(*) from t group by g limit 10",
best: "IndexReader(Index(t.g)[[NULL,+inf]]->StreamAgg)->StreamAgg->Limit",
},
{
sql: "select count(*) from t group by g order by g",
best: "IndexReader(Index(t.g)[[NULL,+inf]]->StreamAgg)->StreamAgg->Projection",
},
{
sql: "select count(*) from t group by g order by g desc limit 1",
best: "IndexReader(Index(t.g)[[NULL,+inf]]->StreamAgg)->StreamAgg->Limit->Projection",
},
// Test hash agg + limit or sort
{
sql: "select count(*) from t group by b order by b limit 10",
best: "TableReader(Table(t)->HashAgg)->HashAgg->TopN([test.t.b],0,10)->Projection",
},
{
sql: "select count(*) from t group by b order by b",
best: "TableReader(Table(t)->HashAgg)->HashAgg->Sort->Projection",
},
{
sql: "select count(*) from t group by b limit 10",
best: "TableReader(Table(t)->HashAgg)->HashAgg->Limit",
},
// Test merge join + stream agg
{
sql: "select sum(a.g), sum(b.g) from t a join t b on a.g = b.g group by a.g",
best: "MergeInnerJoin{IndexReader(Index(t.g)[[NULL,+inf]])->IndexReader(Index(t.g)[[NULL,+inf]])}(a.g,b.g)->StreamAgg",
},
// Test index join + stream agg
{
sql: "select /*+ tidb_inlj(a,b) */ sum(a.g), sum(b.g) from t a join t b on a.g = b.g and a.g > 60 group by a.g order by a.g limit 1",
best: "IndexJoin{IndexReader(Index(t.g)[(60,+inf]])->IndexReader(Index(t.g)[[NULL,+inf]]->Sel([gt(b.g, 60)]))}(a.g,b.g)->StreamAgg->Limit->Projection",
},
{
sql: "select sum(a.g), sum(b.g) from t a join t b on a.g = b.g and a.a>5 group by a.g order by a.g limit 1",
best: "IndexJoin{IndexReader(Index(t.g)[[NULL,+inf]]->Sel([gt(a.a, 5)]))->IndexReader(Index(t.g)[[NULL,+inf]])}(a.g,b.g)->StreamAgg->Limit->Projection",
},
{
sql: "select sum(d) from t",
best: "TableReader(Table(t)->StreamAgg)->StreamAgg",
},
}
for _, tt := range tests {
comment := Commentf("for %s", tt.sql)
stmt, err := s.ParseOneStmt(tt.sql, "", "")
c.Assert(err, IsNil, comment)
p, err := planner.Optimize(se, stmt, s.is)
c.Assert(err, IsNil)
c.Assert(core.ToString(p), Equals, tt.best, Commentf("for %s", tt.sql))
}
}
func (s *testPlanSuite) TestRefine(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
defer func() {
dom.Close()
store.Close()
}()
se, err := session.CreateSession4Test(store)
c.Assert(err, IsNil)
_, err = se.Execute(context.Background(), "use test")
c.Assert(err, IsNil)
tests := []struct {
sql string
best string
}{
{
sql: "select a from t where c is not null",
best: "IndexReader(Index(t.c_d_e)[[-inf,+inf]])->Projection",
},
{
sql: "select a from t where c >= 4",
best: "IndexReader(Index(t.c_d_e)[[4,+inf]])->Projection",
},
{
sql: "select a from t where c <= 4",
best: "IndexReader(Index(t.c_d_e)[[-inf,4]])->Projection",
},
{
sql: "select a from t where c = 4 and d = 5 and e = 6",
best: "IndexReader(Index(t.c_d_e)[[4 5 6,4 5 6]])->Projection",
},
{
sql: "select a from t where d = 4 and c = 5",
best: "IndexReader(Index(t.c_d_e)[[5 4,5 4]])->Projection",
},
{
sql: "select a from t where c = 4 and e < 5",
best: "IndexReader(Index(t.c_d_e)[[4,4]]->Sel([lt(test.t.e, 5)]))->Projection",
},
{
sql: "select a from t where c = 4 and d <= 5 and d > 3",
best: "IndexReader(Index(t.c_d_e)[(4 3,4 5]])->Projection",
},
{
sql: "select a from t where d <= 5 and d > 3",
best: "TableReader(Table(t)->Sel([le(test.t.d, 5) gt(test.t.d, 3)]))->Projection",
},
{
sql: "select a from t where c between 1 and 2",
best: "IndexReader(Index(t.c_d_e)[[1,2]])->Projection",
},
{
sql: "select a from t where c not between 1 and 2",
best: "IndexReader(Index(t.c_d_e)[[-inf,1) (2,+inf]])->Projection",
},
{
sql: "select a from t where c <= 5 and c >= 3 and d = 1",
best: "IndexReader(Index(t.c_d_e)[[3,5]]->Sel([eq(test.t.d, 1)]))->Projection",
},
{
sql: "select a from t where c = 1 or c = 2 or c = 3",
best: "IndexReader(Index(t.c_d_e)[[1,3]])->Projection",
},
{
sql: "select b from t where c = 1 or c = 2 or c = 3 or c = 4 or c = 5",
best: "IndexLookUp(Index(t.c_d_e)[[1,5]], Table(t))->Projection",
},
{
sql: "select a from t where c = 5",
best: "IndexReader(Index(t.c_d_e)[[5,5]])->Projection",
},
{
sql: "select a from t where c = 5 and b = 1",
best: "IndexLookUp(Index(t.c_d_e)[[5,5]], Table(t)->Sel([eq(test.t.b, 1)]))->Projection",
},
{
sql: "select a from t where not a",
best: "TableReader(Table(t)->Sel([not(test.t.a)]))",
},
{
sql: "select a from t where c in (1)",
best: "IndexReader(Index(t.c_d_e)[[1,1]])->Projection",
},
{
sql: "select a from t where c in ('1')",
best: "IndexReader(Index(t.c_d_e)[[1,1]])->Projection",
},
{
sql: "select a from t where c = 1.0",
best: "IndexReader(Index(t.c_d_e)[[1,1]])->Projection",
},
{
sql: "select a from t where c in (1) and d > 3",
best: "IndexReader(Index(t.c_d_e)[(1 3,1 +inf]])->Projection",
},
{
sql: "select a from t where c in (1, 2, 3) and (d > 3 and d < 4 or d > 5 and d < 6)",
best: "Dual->Projection",
},
{
sql: "select a from t where c in (1, 2, 3) and (d > 2 and d < 4 or d > 5 and d < 7)",
best: "IndexReader(Index(t.c_d_e)[(1 2,1 4) (1 5,1 7) (2 2,2 4) (2 5,2 7) (3 2,3 4) (3 5,3 7)])->Projection",
},
{
sql: "select a from t where c in (1, 2, 3)",
best: "IndexReader(Index(t.c_d_e)[[1,1] [2,2] [3,3]])->Projection",
},
{
sql: "select a from t where c in (1, 2, 3) and d in (1,2) and e = 1",
best: "IndexReader(Index(t.c_d_e)[[1 1 1,1 1 1] [1 2 1,1 2 1] [2 1 1,2 1 1] [2 2 1,2 2 1] [3 1 1,3 1 1] [3 2 1,3 2 1]])->Projection",
},
{
sql: "select a from t where d in (1, 2, 3)",
best: "TableReader(Table(t)->Sel([in(test.t.d, 1, 2, 3)]))->Projection",
},
{
sql: "select a from t where c not in (1)",
best: "IndexReader(Index(t.c_d_e)[(NULL,1) (1,+inf]])->Projection",
},
// test like
{
sql: "select a from t use index(c_d_e) where c != 1",
best: "IndexReader(Index(t.c_d_e)[[-inf,1) (1,+inf]])->Projection",
},
{
sql: "select a from t where c_str like ''",
best: `IndexReader(Index(t.c_d_e_str)[["",""]])->Projection`,
},
{
sql: "select a from t where c_str like 'abc'",
best: `IndexReader(Index(t.c_d_e_str)[["abc","abc"]])->Projection`,
},
{
sql: "select a from t where c_str not like 'abc'",
best: "TableReader(Table(t)->Sel([not(like(test.t.c_str, abc, 92))]))->Projection",
},
{
sql: "select a from t where not (c_str like 'abc' or c_str like 'abd')",
best: `TableReader(Table(t)->Sel([and(not(like(test.t.c_str, abc, 92)), not(like(test.t.c_str, abd, 92)))]))->Projection`,
},
{
sql: "select a from t where c_str like '_abc'",
best: "TableReader(Table(t)->Sel([like(test.t.c_str, _abc, 92)]))->Projection",
},
{
sql: `select a from t where c_str like 'abc%'`,
best: `IndexReader(Index(t.c_d_e_str)[["abc","abd")])->Projection`,
},
{
sql: "select a from t where c_str like 'abc_'",
best: `IndexReader(Index(t.c_d_e_str)[("abc","abd")]->Sel([like(test.t.c_str, abc_, 92)]))->Projection`,
},
{
sql: "select a from t where c_str like 'abc%af'",
best: `IndexReader(Index(t.c_d_e_str)[["abc","abd")]->Sel([like(test.t.c_str, abc%af, 92)]))->Projection`,
},
{
sql: `select a from t where c_str like 'abc\\_' escape ''`,
best: `IndexReader(Index(t.c_d_e_str)[["abc_","abc_"]])->Projection`,
},
{
sql: `select a from t where c_str like 'abc\\_'`,
best: `IndexReader(Index(t.c_d_e_str)[["abc_","abc_"]])->Projection`,
},
{
sql: `select a from t where c_str like 'abc\\\\_'`,
best: "IndexReader(Index(t.c_d_e_str)[(\"abc\\\",\"abc]\")]->Sel([like(test.t.c_str, abc\\\\_, 92)]))->Projection",
},
{
sql: `select a from t where c_str like 'abc\\_%'`,
best: "IndexReader(Index(t.c_d_e_str)[[\"abc_\",\"abc`\")])->Projection",
},
{
sql: `select a from t where c_str like 'abc=_%' escape '='`,
best: "IndexReader(Index(t.c_d_e_str)[[\"abc_\",\"abc`\")])->Projection",
},
{
sql: `select a from t where c_str like 'abc\\__'`,
best: "IndexReader(Index(t.c_d_e_str)[(\"abc_\",\"abc`\")]->Sel([like(test.t.c_str, abc\\__, 92)]))->Projection",
},
{
// Check that 123 is converted to string '123'. index can be used.
sql: `select a from t where c_str like 123`,
best: "IndexReader(Index(t.c_d_e_str)[[\"123\",\"123\"]])->Projection",
},
// c is type int which will be added cast to specified type when building function signature, no index can be used.
{
sql: `select a from t where c like '1'`,
best: "TableReader(Table(t))->Sel([like(cast(test.t.c), 1, 92)])->Projection",
},
{
sql: `select a from t where c = 1.9 and d > 3`,
best: "Dual",
},
{
sql: `select a from t where c < 1.1`,
best: "IndexReader(Index(t.c_d_e)[[-inf,2)])->Projection",
},
{
sql: `select a from t where c <= 1.9`,
best: "IndexReader(Index(t.c_d_e)[[-inf,1]])->Projection",
},
{
sql: `select a from t where c >= 1.1`,
best: "IndexReader(Index(t.c_d_e)[[2,+inf]])->Projection",
},
{
sql: `select a from t where c > 1.9`,
best: "IndexReader(Index(t.c_d_e)[(1,+inf]])->Projection",
},
{
sql: `select a from t where c = 123456789098765432101234`,
best: "Dual",
},
{
sql: `select a from t where c = 'hanfei'`,
best: "TableReader(Table(t))->Sel([eq(cast(test.t.c), cast(hanfei))])->Projection",
},
}
for _, tt := range tests {
comment := Commentf("for %s", tt.sql)
stmt, err := s.ParseOneStmt(tt.sql, "", "")
c.Assert(err, IsNil, comment)
sc := se.(sessionctx.Context).GetSessionVars().StmtCtx
sc.IgnoreTruncate = false
p, err := planner.Optimize(se, stmt, s.is)
c.Assert(err, IsNil)
c.Assert(core.ToString(p), Equals, tt.best, Commentf("for %s", tt.sql))
}
}
func (s *testPlanSuite) TestAggEliminater(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
defer func() {
dom.Close()
store.Close()
}()
se, err := session.CreateSession4Test(store)
c.Assert(err, IsNil)
_, err = se.Execute(context.Background(), "use test")
c.Assert(err, IsNil)
tests := []struct {
sql string
best string
}{
// Max to Limit + Sort-Desc.
{
sql: "select max(a) from t;",
best: "TableReader(Table(t)->Limit)->Limit->StreamAgg",
},
// Min to Limit + Sort.
{
sql: "select min(a) from t;",
best: "TableReader(Table(t)->Limit)->Limit->StreamAgg",
},
// Min to Limit + Sort, and isnull() should be added.
{
sql: "select min(c_str) from t;",
best: "IndexReader(Index(t.c_d_e_str)[[-inf,+inf]]->Limit)->Limit->StreamAgg",
},
// Do nothing to max + firstrow.
{
sql: "select max(a), b from t;",
best: "TableReader(Table(t)->StreamAgg)->StreamAgg",
},
// If max/min contains scalar function, we can still do transformation.
{
sql: "select max(a+1) from t;",
best: "TableReader(Table(t)->Sel([not(isnull(plus(test.t.a, 1)))])->TopN([plus(test.t.a, 1) true],0,1))->TopN([plus(test.t.a, 1) true],0,1)->StreamAgg",
},
// Do nothing to max+min.
{
sql: "select max(a), min(a) from t;",
best: "TableReader(Table(t)->StreamAgg)->StreamAgg",
},
// Do nothing to max with groupby.
{
sql: "select max(a) from t group by b;",
best: "TableReader(Table(t)->HashAgg)->HashAgg",
},
// If inner is not a data source, we can still do transformation.
{
sql: "select max(a) from (select t1.a from t t1 join t t2 on t1.a=t2.a) t",
best: "IndexJoin{TableReader(Table(t))->TableReader(Table(t))}(t1.a,t2.a)->Limit->StreamAgg",
},
}
for _, tt := range tests {
comment := Commentf("for %s", tt.sql)
stmt, err := s.ParseOneStmt(tt.sql, "", "")
c.Assert(err, IsNil, comment)
sc := se.(sessionctx.Context).GetSessionVars().StmtCtx
sc.IgnoreTruncate = false
p, err := planner.Optimize(se, stmt, s.is)
c.Assert(err, IsNil)
c.Assert(core.ToString(p), Equals, tt.best, Commentf("for %s", tt.sql))
}
}
type overrideStore struct{ kv.Storage }
func (store overrideStore) GetClient() kv.Client {
cli := store.Storage.GetClient()
return overrideClient{cli}
}
type overrideClient struct{ kv.Client }
func (cli overrideClient) IsRequestTypeSupported(reqType, subType int64) bool {
return false
}
func (s *testPlanSuite) TestRequestTypeSupportedOff(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
defer func() {
dom.Close()
store.Close()
}()
se, err := session.CreateSession4Test(overrideStore{store})
c.Assert(err, IsNil)
_, err = se.Execute(context.Background(), "use test")
c.Assert(err, IsNil)
sql := "select * from t where a in (1, 10, 20)"
expect := "TableReader(Table(t))->Sel([in(test.t.a, 1, 10, 20)])"
stmt, err := s.ParseOneStmt(sql, "", "")
c.Assert(err, IsNil)
p, err := planner.Optimize(se, stmt, s.is)
c.Assert(err, IsNil)
c.Assert(core.ToString(p), Equals, expect, Commentf("for %s", sql))
}
func (s *testPlanSuite) TestIndexJoinUnionScan(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
defer func() {
dom.Close()
store.Close()
}()
se, err := session.CreateSession4Test(store)
c.Assert(err, IsNil)
_, err = se.Execute(context.Background(), "use test")
c.Assert(err, IsNil)
tests := []struct {
sql string
best string
}{
// Test Index Join + UnionScan + TableScan.
{
sql: "select /*+ TIDB_INLJ(t1, t2) */ * from t t1, t t2 where t1.a = t2.a",
best: "LeftHashJoin{TableReader(Table(t))->UnionScan([])->TableReader(Table(t))->UnionScan([])}(t1.a,t2.a)",
},
// Test Index Join + UnionScan + DoubleRead.
{
sql: "select /*+ TIDB_INLJ(t1, t2) */ * from t t1, t t2 where t1.a = t2.c",
best: "LeftHashJoin{TableReader(Table(t))->UnionScan([])->TableReader(Table(t))->UnionScan([])}(t1.a,t2.c)",
},
// Test Index Join + UnionScan + IndexScan.
{
sql: "select /*+ TIDB_INLJ(t1, t2) */ t1.a , t2.c from t t1, t t2 where t1.a = t2.c",
best: "LeftHashJoin{TableReader(Table(t))->UnionScan([])->TableReader(Table(t))->UnionScan([])}(t1.a,t2.c)->Projection",
},
}
for i, tt := range tests {
comment := Commentf("case:%v sql:%s", i, tt.sql)
stmt, err := s.ParseOneStmt(tt.sql, "", "")
c.Assert(err, IsNil, comment)
err = se.NewTxn()
c.Assert(err, IsNil)
// Make txn not read only.
se.Txn().Set(kv.Key("AAA"), []byte("BBB"))
se.StmtCommit()
p, err := planner.Optimize(se, stmt, s.is)
c.Assert(err, IsNil)
c.Assert(core.ToString(p), Equals, tt.best, comment)
}
}

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://gitlife.ru/oschina-mirror/hanchuanchuan-goInception.git
git@gitlife.ru:oschina-mirror/hanchuanchuan-goInception.git
oschina-mirror
hanchuanchuan-goInception
hanchuanchuan-goInception
v1.3.0