diff --git a/go.mod b/go.mod index 6573ead136..c9d715b505 100644 --- a/go.mod +++ b/go.mod @@ -6,10 +6,10 @@ require ( github.com/PuerkitoBio/goquery v1.8.1 github.com/cockroachdb/apd/v2 v2.0.3-0.20200518165714-d020e156310a github.com/cockroachdb/errors v1.7.5 - github.com/dolthub/dolt/go v0.40.5-0.20251213003033-5aae073ad198 + github.com/dolthub/dolt/go v0.40.5-0.20251216020900-9c1c5ed2c8fd 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.20251216223848-500454bc6d5f 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 diff --git a/go.sum b/go.sum index 40707b3d09..d628fe0121 100644 --- a/go.sum +++ b/go.sum @@ -230,6 +230,8 @@ github.com/dolthub/dolt-mcp v0.2.2 h1:bpROmam74n95uU4EA3BpOIVlTDT0pzeFMBwe/YRq2m github.com/dolthub/dolt-mcp v0.2.2/go.mod h1:S++DJ4QWTAXq+0TNzFa7Oq3IhoT456DJHwAINFAHgDQ= github.com/dolthub/dolt/go v0.40.5-0.20251213003033-5aae073ad198 h1:2DdAh70y/xSC4Ej/TWyRWJsjvd4R3T7lF0Nb3pzJhpo= github.com/dolthub/dolt/go v0.40.5-0.20251213003033-5aae073ad198/go.mod h1:eBvkNDgUSm/z17brovrRzfL57dwlQXBoXfqeisMUQ9E= +github.com/dolthub/dolt/go v0.40.5-0.20251216020900-9c1c5ed2c8fd h1:GcS8p05zGvduMKRB2O2KeTwPrWC3a5M3VX/tkR5fAYU= +github.com/dolthub/dolt/go v0.40.5-0.20251216020900-9c1c5ed2c8fd/go.mod h1:9p1QMq5hTZAExGlwK0+VJqfsMnV+TPw6ANk0QaxTZh8= github.com/dolthub/eventsapi_schema v0.0.0-20250915094920-eadfd39051ca h1:BGFz/0OlKIuC6qHIZQbvPapFvdAJkeEyGXWVgL5clmE= github.com/dolthub/eventsapi_schema v0.0.0-20250915094920-eadfd39051ca/go.mod h1:CoDLfgPqHyBtth0Cp+fi/CmC4R81zJNX4wPjShdZ+Bw= github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 h1:u3PMzfF8RkKd3lB9pZ2bfn0qEG+1Gms9599cr0REMww= @@ -238,12 +240,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.20251216223848-500454bc6d5f h1:trWK6M/ouvxhmA+V6bsXUGIFLinUC+oAG7gnoa73+F0= +github.com/dolthub/go-mysql-server v0.20.1-0.20251216223848-500454bc6d5f/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= diff --git a/server/analyzer/split.go b/server/analyzer/split.go index 29237b017a..a0e22ce909 100644 --- a/server/analyzer/split.go +++ b/server/analyzer/split.go @@ -22,34 +22,6 @@ import ( 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 { @@ -75,28 +47,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 _ sql.ExpressionTreeFilter = &LogicTreeWalker{} + +// Next implements the sql.ExpressionTreeFilter 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 } diff --git a/servercfg/config.go b/servercfg/config.go index d4763f8453..3fc66089dd 100755 --- a/servercfg/config.go +++ b/servercfg/config.go @@ -21,6 +21,8 @@ import ( "github.com/dolthub/go-mysql-server/sql" "gopkg.in/yaml.v2" + "github.com/dolthub/doltgresql/server/analyzer" + pgsql "github.com/dolthub/doltgresql/postgres/parser/parser/sql" "github.com/dolthub/doltgresql/server/expression" "github.com/dolthub/doltgresql/servercfg/cfgdetails" @@ -43,7 +45,8 @@ func (*DoltgresConfig) Overrides() sql.EngineOverrides { ParseTableAsColumn: expression.NewTableToComposite, Parser: pgsql.NewPostgresParser(), }, - SchemaFormatter: pgsql.NewPostgresSchemaFormatter(), + SchemaFormatter: pgsql.NewPostgresSchemaFormatter(), + CostedIndexScanExpressionFilter: &analyzer.LogicTreeWalker{}, } } diff --git a/testing/go/index_test.go b/testing/go/index_test.go index 42315a115c..1783c82533 100644 --- a/testing/go/index_test.go +++ b/testing/go/index_test.go @@ -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{ @@ -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, @@ -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]"}, + }, + }, }, }, {