diff --git a/planner/optimizer.go b/planner/optimizer.go index 8de63fa96..e1a7e23ab 100644 --- a/planner/optimizer.go +++ b/planner/optimizer.go @@ -526,6 +526,11 @@ func getCandidateFromfilterNode(f *stream.FilterOperator, tableName string, info // we'll start with checking if the path is the primary key of the table if pk := info.GetPrimaryKey(); pk != nil && pk.Path.IsEqual(path) { + // if both types are different, don't select this scanner + if pk.Type != v.Type { + return nil, nil + } + cd.isPk = true cd.priority = 3 @@ -541,6 +546,11 @@ func getCandidateFromfilterNode(f *stream.FilterOperator, tableName string, info // if not, check if an index exists for that path if idx := indexes.GetIndexByPath(document.Path(path)); idx != nil { + // if both types are different, don't select this scanner + if !idx.Info.Type.IsZero() && idx.Info.Type != v.Type { + return nil, nil + } + cd.isIndex = true if idx.Info.Unique { cd.priority = 2 diff --git a/planner/optimizer_test.go b/planner/optimizer_test.go index fcb99be73..abb06d4c7 100644 --- a/planner/optimizer_test.go +++ b/planner/optimizer_test.go @@ -355,6 +355,27 @@ func TestUseIndexBasedOnSelectionNodeRule(t *testing.T) { Pipe(stream.Filter(parser.MustParseExpr("b = 2"))). Pipe(stream.Project(parser.MustParseExpr("a"))), }, + { + "SELECT a FROM foo WHERE c = 'hello' AND b = 2", + stream.New(stream.SeqScan("foo")). + Pipe(stream.Filter(parser.MustParseExpr("c = 'hello'"))). + Pipe(stream.Filter(parser.MustParseExpr("b = 2"))). + Pipe(stream.Project(parser.MustParseExpr("a"))), + stream.New(stream.IndexScan("idx_foo_b", st.Range{Min: document.NewIntegerValue(2), Exact: true})). + Pipe(stream.Filter(parser.MustParseExpr("c = 'hello'"))). + Pipe(stream.Project(parser.MustParseExpr("a"))), + }, + { + "SELECT a FROM foo WHERE c = 'hello' AND d = 2", + stream.New(stream.SeqScan("foo")). + Pipe(stream.Filter(parser.MustParseExpr("c = 'hello'"))). + Pipe(stream.Filter(parser.MustParseExpr("d = 2"))). + Pipe(stream.Project(parser.MustParseExpr("a"))), + stream.New(stream.SeqScan("foo")). + Pipe(stream.Filter(parser.MustParseExpr("c = 'hello'"))). + Pipe(stream.Filter(parser.MustParseExpr("d = 2"))). + Pipe(stream.Project(parser.MustParseExpr("a"))), + }, { "FROM foo WHERE a IN [1, 2]", stream.New(stream.SeqScan("foo")).Pipe(stream.Filter( @@ -404,6 +425,14 @@ func TestUseIndexBasedOnSelectionNodeRule(t *testing.T) { stream.New(stream.IndexScan("idx_foo_a", st.Range{Min: document.NewIntegerValue(1), Exact: true})). Pipe(stream.Filter(parser.MustParseExpr("k < 2"))), }, + { + "FROM foo WHERE a = 1 AND k = 'hello'", + stream.New(stream.SeqScan("foo")). + Pipe(stream.Filter(parser.MustParseExpr("a = 1"))). + Pipe(stream.Filter(parser.MustParseExpr("k = 'hello'"))), + stream.New(stream.IndexScan("idx_foo_a", st.Range{Min: document.NewIntegerValue(1), Exact: true})). + Pipe(stream.Filter(parser.MustParseExpr("k = 'hello'"))), + }, } for _, test := range tests { @@ -417,7 +446,7 @@ func TestUseIndexBasedOnSelectionNodeRule(t *testing.T) { defer tx.Rollback() err = tx.Exec(` - CREATE TABLE foo (k INT PRIMARY KEY); + CREATE TABLE foo (k INT PRIMARY KEY, c INT); CREATE INDEX idx_foo_a ON foo(a); CREATE INDEX idx_foo_b ON foo(b); CREATE UNIQUE INDEX idx_foo_c ON foo(c);