Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/dolthub/dolt/go v0.40.5-0.20251213003033-5aae073ad198
github.com/dolthub/eventsapi_schema v0.0.0-20250915094920-eadfd39051ca
github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2
github.com/dolthub/go-mysql-server v0.20.1-0.20251215235453-b3613827cc44
github.com/dolthub/go-mysql-server v0.20.1-0.20251216005908-df5d1018a216
github.com/dolthub/pg_query_go/v6 v6.0.0-20251215122834-fb20be4254d1
github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216
github.com/dolthub/vitess v0.0.0-20251210200925-1d33d416d162
Expand Down
8 changes: 2 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,8 @@ github.com/dolthub/fslock v0.0.3 h1:iLMpUIvJKMKm92+N1fmHVdxJP5NdyDK5bK7z7Ba2s2U=
github.com/dolthub/fslock v0.0.3/go.mod h1:QWql+P17oAAMLnL4HGB5tiovtDuAjdDTPbuqx7bYfa0=
github.com/dolthub/go-icu-regex v0.0.0-20250916051405-78a38d478790 h1:zxMsH7RLiG+dlZ/y0LgJHTV26XoiSJcuWq+em6t6VVc=
github.com/dolthub/go-icu-regex v0.0.0-20250916051405-78a38d478790/go.mod h1:F3cnm+vMRK1HaU6+rNqQrOCyR03HHhR1GWG2gnPOqaE=
github.com/dolthub/go-mysql-server v0.20.1-0.20251212235309-4422a1ca9f43 h1:3OiTNnL3rFiSXGo7/xTt64FmJGtGU1igvyPishbsM4o=
github.com/dolthub/go-mysql-server v0.20.1-0.20251212235309-4422a1ca9f43/go.mod h1:NjewWKoa5bVSLdKwL7fg7eAfrcIxDybWUKoWEHWRTw4=
github.com/dolthub/go-mysql-server v0.20.1-0.20251215224112-7cb4535802a5 h1:gjUTlYUVTWnKoZBizqLQzWL/rxwT4ZC8IGt49pzpCQg=
github.com/dolthub/go-mysql-server v0.20.1-0.20251215224112-7cb4535802a5/go.mod h1:NjewWKoa5bVSLdKwL7fg7eAfrcIxDybWUKoWEHWRTw4=
github.com/dolthub/go-mysql-server v0.20.1-0.20251215235453-b3613827cc44 h1:/e1XKLp5D200IwFq7uKAFgza30NuNTmbpD7VgcrAqhI=
github.com/dolthub/go-mysql-server v0.20.1-0.20251215235453-b3613827cc44/go.mod h1:NjewWKoa5bVSLdKwL7fg7eAfrcIxDybWUKoWEHWRTw4=
github.com/dolthub/go-mysql-server v0.20.1-0.20251216005908-df5d1018a216 h1:Y5BkRx0prW70wqlhC5uYzEtGZ8CmYz/72G56cy4vcV4=
github.com/dolthub/go-mysql-server v0.20.1-0.20251216005908-df5d1018a216/go.mod h1:NjewWKoa5bVSLdKwL7fg7eAfrcIxDybWUKoWEHWRTw4=
github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63 h1:OAsXLAPL4du6tfbBgK0xXHZkOlos63RdKYS3Sgw/dfI=
github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63/go.mod h1:lV7lUeuDhH5thVGDCKXbatwKy2KW80L4rMT46n+Y2/Q=
github.com/dolthub/ishell v0.0.0-20240701202509-2b217167d718 h1:lT7hE5k+0nkBdj/1UOSFwjWpNxf+LCApbRHgnCA17XE=
Expand Down
1 change: 1 addition & 0 deletions server/analyzer/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ func initEngine() {
// There are a couple places during analysis where SplitConjunction in GMS cannot correctly split up
// Doltgres expressions, so we need to override the default function used.
analyzer.SplitConjunction = SplitConjunction
analyzer.CostedIndexScanExpressionWalker = &LogicTreeWalker{}
memo.SplitConjunction = SplitConjunction
}

Expand Down
64 changes: 14 additions & 50 deletions server/analyzer/split.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,13 @@ package analyzer

import (
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/analyzer"
"github.com/dolthub/go-mysql-server/sql/expression"

pgexprs "github.com/dolthub/doltgresql/server/expression"
pgtypes "github.com/dolthub/doltgresql/server/types"
)

// SplitDisjunction breaks OR expressions into their left and right parts, recursively. Also handles expressions that
// can be approximated as OR expressions, such as IN for tuples.
func SplitDisjunction(expr sql.Expression) []sql.Expression {
if expr == nil {
return nil
}
switch expr := expr.(type) {
case *expression.Or:
return append(
SplitDisjunction(expr.LeftChild),
SplitDisjunction(expr.RightChild)...,
)
case *pgexprs.GMSCast:
// We should check to see if we need to preserve the cast on each child individually
split := SplitDisjunction(expr.Child())
for i := range split {
if _, ok := split[i].Type().(*pgtypes.DoltgresType); !ok {
split[i] = pgexprs.NewGMSCast(split[i])
}
}
return split
case *pgexprs.InTuple:
return SplitDisjunction(expr.Decay())
default:
return []sql.Expression{expr}
}
}

// SplitConjunction breaks AND expressions into their left and right parts, recursively.
func SplitConjunction(expr sql.Expression) []sql.Expression {
if expr == nil {
Expand All @@ -75,28 +48,19 @@ func SplitConjunction(expr sql.Expression) []sql.Expression {
}
}

// SplitDisjunctions performs the same operation as SplitDisjunction, except that it applies to a slice.
func SplitDisjunctions(exprs []sql.Expression) []sql.Expression {
if len(exprs) == 0 {
return nil
}
// New slice will be at least the size of the incoming slice
newExprs := make([]sql.Expression, 0, len(exprs))
for _, expr := range exprs {
newExprs = append(newExprs, SplitDisjunction(expr)...)
}
return newExprs
}
// LogicTreeWalker is a walker that removes GMSCast and other Doltgres specific expression nodes from
// logic expression trees. This allows the analyzer logic to correctly reason about expressions in filters
// to apply indexes.
type LogicTreeWalker struct{}

// SplitConjunctions performs the same operation as SplitConjunction, except that it applies to a slice.
func SplitConjunctions(exprs []sql.Expression) []sql.Expression {
if len(exprs) == 0 {
return nil
}
// New slice will be at least the size of the incoming slice
newExprs := make([]sql.Expression, 0, len(exprs))
for _, expr := range exprs {
newExprs = append(newExprs, SplitConjunction(expr)...)
var _ analyzer.LogicTreeWalker = &LogicTreeWalker{}

// Next implements the analyzer.LogicTreeWalker interface.
func (l *LogicTreeWalker) Next(e sql.Expression) sql.Expression {
switch expr := e.(type) {
case *pgexprs.GMSCast:
return l.Next(expr.Child())
default:
return e
}
return newExprs
}
65 changes: 63 additions & 2 deletions testing/go/index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,39 @@ func TestBasicIndexing(t *testing.T) {
{" └─ columns: [pk v1]"},
},
},
{
Query: "SELECT * FROM test WHERE (v1 > 3 OR v1 < 2) AND v1 <> 5 ORDER BY pk;",
Expected: []sql.Row{
{11, 1},
{14, 4}},
},
{
Query: "explain SELECT * FROM test WHERE (v1 > 3 OR v1 < 2) AND v1 <> 5 ORDER BY pk;",
Expected: []sql.Row{
{"Sort(test.pk ASC)"},
{" └─ IndexedTableAccess(test)"},
{" ├─ index: [test.v1]"},
{" ├─ filters: [{(NULL, 2)}, {(3, 5)}, {(5, ∞)}]"},
{" └─ columns: [pk v1]"},
},
},
{
Query: "SELECT * FROM test WHERE v1 = 2 OR v1 = 4 ORDER BY pk;",
Expected: []sql.Row{
{12, 2},
{14, 4},
},
},
{
Query: "explain SELECT * FROM test WHERE v1 = 2 OR v1 = 4 ORDER BY pk;",
Expected: []sql.Row{
{"Sort(test.pk ASC)"},
{" └─ IndexedTableAccess(test)"},
{" ├─ index: [test.v1]"},
{" ├─ filters: [{[2, 2]}, {[4, 4]}]"},
{" └─ columns: [pk v1]"},
},
},
{
Query: "SELECT * FROM test WHERE v1 IN (2, 4) ORDER BY pk;",
Expected: []sql.Row{
Expand Down Expand Up @@ -151,6 +184,24 @@ func TestBasicIndexing(t *testing.T) {
{12, "twelve"},
},
},
{
Query: "SELECT * FROM test WHERE v1 > 't' OR v1 < 'f' ORDER BY pk;",
Expected: []sql.Row{
{11, "eleven"},
{12, "twelve"},
{13, "thirteen"},
},
},
{
Query: "explain SELECT * FROM test WHERE v1 > 't' OR v1 < 'f' ORDER BY pk;",
Expected: []sql.Row{
{"Sort(test.pk ASC)"},
{" └─ IndexedTableAccess(test)"},
{" ├─ index: [test.pk,test.v1]"},
{" ├─ filters: [{[NULL, ∞), (NULL, f)}, {[NULL, ∞), (t, ∞)}]"},
{" └─ columns: [pk v1]"},
},
},
{
Query: "DELETE FROM test WHERE v1 = 'twelve'",
SkipResultsCheck: true,
Expand Down Expand Up @@ -432,8 +483,8 @@ func TestBasicIndexing(t *testing.T) {
Assertions: []ScriptTestAssertion{
{
Query: "select /*+ lookup_join(sq, test) */ HINT * from test join " +
"(select * from jointable) sq " +
"on test.v1 = sq.v3 and test.v2 = sq.v4 order by 1",
"(select * from jointable) sq " +
"on test.v1 = sq.v3 and test.v2 = sq.v4 order by 1",
Expected: []sql.Row{
{11, 1, 21, 1, 21},
},
Expand Down Expand Up @@ -1261,6 +1312,16 @@ func TestBasicIndexing(t *testing.T) {
{5, 9},
},
},
{
Query: "explain SELECT * FROM test WHERE v1 BETWEEN 3 AND 5 OR v1 BETWEEN 7 AND 9 order by 1;",
Expected: []sql.Row{
{"Sort(test.pk ASC)"},
{" └─ IndexedTableAccess(test)"},
{" ├─ index: [test.v1]"},
{" ├─ filters: [{[3, 5]}, {[7, 9]}]"},
{" └─ columns: [pk v1]"},
},
},
},
},
{
Expand Down
Loading