Skip to content

Commit bfb0776

Browse files
author
James Cor
committed
Merge branch 'main' into james/proc
2 parents 6bd4174 + d8430eb commit bfb0776

File tree

12 files changed

+168
-41
lines changed

12 files changed

+168
-41
lines changed

engine.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ type Engine struct {
150150
Parser sql.Parser
151151
}
152152

153+
var _ analyzer.StatementRunner = (*Engine)(nil)
154+
153155
type ColumnWithRawDefault struct {
154156
SqlColumn *sql.Column
155157
Default string
@@ -195,6 +197,7 @@ func New(a *analyzer.Analyzer, cfg *Config) *Engine {
195197
Parser: sql.GlobalParser,
196198
}
197199
ret.ReadOnly.Store(cfg.IsReadOnly)
200+
a.Runner = ret
198201
return ret
199202
}
200203

enginetest/initialization.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ func NewEngineWithProvider(_ *testing.T, harness Harness, provider sql.DatabaseP
9191
if idh, ok := harness.(IndexDriverHarness); ok {
9292
idh.InitializeIndexDriver(engine.Analyzer.Catalog.AllDatabases(NewContext(harness)))
9393
}
94+
analyzer.Runner = engine
9495

9596
return engine
9697
}

enginetest/join_op_tests.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,56 @@ var DefaultJoinOpTests = []joinOpTest{
229229
},
230230
},
231231
},
232+
{
233+
name: "left join null-filter",
234+
setup: [][]string{
235+
setup.MydbData[0],
236+
{
237+
"CREATE table xy (x int primary key, y int, z int, index y_idx(y));",
238+
"CREATE table ab (a int primary key, b int, c int);",
239+
"insert into xy values (1,0,0), (2,null,1), (3,2,2),(4,2,3);",
240+
"insert into ab values (0,1,0), (1,2,1), (2,3,2), (3,4,3);",
241+
},
242+
},
243+
tests: []JoinOpTests{
244+
{
245+
Query: "select /*+ JOIN_ORDER(ab,xy) */ x from xy left join ab on x = a and z = 5 where a is null order by x ",
246+
Expected: []sql.Row{{1}, {2}, {3}, {4}},
247+
},
248+
{
249+
Query: "select /*+ JOIN_ORDER(xy,ab) */ x from xy left join ab on x = a and z = 5 where a is null order by x ",
250+
Expected: []sql.Row{{1}, {2}, {3}, {4}},
251+
},
252+
// partial return
253+
{
254+
Query: "select /*+ JOIN_ORDER(ab,xy) */ x from xy left join ab on x = a and z = 1 where a is null order by x ",
255+
Expected: []sql.Row{{1}, {3}, {4}},
256+
},
257+
{
258+
Query: "select /*+ JOIN_ORDER(xy,ab) */ x from xy left join ab on x = a and z in (1,2) where a is null order by x ",
259+
Expected: []sql.Row{{1}, {4}},
260+
},
261+
},
262+
},
263+
{
264+
name: "type conversion panic bug",
265+
setup: [][]string{
266+
setup.MydbData[0],
267+
{
268+
"create table xy (x int primary key, y int, z varchar(10), key (y,z));",
269+
"insert into xy values (0,0,'0'), (1,1,'1');",
270+
"create table ab (a int primary key, b int);",
271+
"insert into ab values (0,0), (1,1);",
272+
},
273+
},
274+
tests: []JoinOpTests{
275+
{
276+
// the literal z should be internally cast to the appropriate string type
277+
Query: "select /*+ JOIN_ORDER(ab,xy) */ count(*) from xy join ab on y = a and z = 0",
278+
Expected: []sql.Row{{1}},
279+
},
280+
},
281+
},
232282
{
233283
name: "partial key null lookup join indexes",
234284
setup: [][]string{

sql/analyzer/analyzer.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,8 @@ type Analyzer struct {
286286
Coster memo.Coster
287287
// ExecBuilder converts a sql.Node tree into an executable iterator.
288288
ExecBuilder sql.NodeExecBuilder
289+
// Runner represents the engine, which is represented as a separate interface to work around circular dependencies
290+
Runner StatementRunner
289291
}
290292

291293
// NewDefault creates a default Analyzer instance with all default Rules and configuration.

sql/analyzer/interpreter.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2022 Dolthub, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package analyzer
16+
17+
import (
18+
"github.com/dolthub/vitess/go/vt/sqlparser"
19+
20+
"github.com/dolthub/go-mysql-server/sql/transform"
21+
22+
"github.com/dolthub/go-mysql-server/sql"
23+
"github.com/dolthub/go-mysql-server/sql/plan"
24+
)
25+
26+
// Interpreter is an interface that implements an interpreter. These are typically used for functions (which may be
27+
// implemented as a set of operations that are interpreted during runtime).
28+
type Interpreter interface {
29+
SetStatementRunner(ctx *sql.Context, runner StatementRunner) sql.Expression
30+
}
31+
32+
// StatementRunner is essentially an interface that the engine will implement. We cannot directly reference the engine
33+
// here as it will cause an import cycle, so this may be updated to suit any function changes that the engine
34+
// experiences.
35+
type StatementRunner interface {
36+
QueryWithBindings(ctx *sql.Context, query string, parsed sqlparser.Statement, bindings map[string]sqlparser.Expr, qFlags *sql.QueryFlags) (sql.Schema, sql.RowIter, *sql.QueryFlags, error)
37+
}
38+
39+
// interpreter hands the engine to any interpreter expressions.
40+
func interpreter(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scope, sel RuleSelector, qFlags *sql.QueryFlags) (sql.Node, transform.TreeIdentity, error) {
41+
return transform.NodeExprs(n, func(expr sql.Expression) (sql.Expression, transform.TreeIdentity, error) {
42+
if interp, ok := expr.(Interpreter); ok {
43+
return interp.SetStatementRunner(ctx, a.Runner), transform.NewTree, nil
44+
}
45+
return expr, transform.SameTree, nil
46+
})
47+
}

sql/analyzer/resolve_column_defaults.go

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,29 +23,8 @@ import (
2323
"github.com/dolthub/go-mysql-server/sql/types"
2424
)
2525

26-
// Resolving column defaults is a multi-phase process, with different analyzer rules for each phase.
27-
//
28-
// - parseColumnDefaults: Some integrators (dolt but not GMS) store their column defaults as strings, which we need to
29-
// parse into expressions before we can analyze them any further.
30-
// - resolveColumnDefaults: Once we have an expression for a default value, it may contain expressions that need
31-
// simplification before further phases of processing can take place.
32-
//
33-
// After this stage, expressions in column default values are handled by the normal analyzer machinery responsible for
34-
// resolving expressions, including things like columns and functions. Every node that needs to do this for its default
35-
// values implements `sql.Expressioner` to expose such expressions. There is custom logic in `resolveColumns` to help
36-
// identify the correct indexes for column references, which can vary based on the node type.
37-
//
38-
// Finally there are cleanup phases:
39-
// - validateColumnDefaults: ensures that newly created column defaults from a DDL statement are legal for the type of
40-
// column, various other business logic checks to match MySQL's logic.
41-
// - stripTableNamesFromDefault: column defaults headed for storage or serialization in a query result need the table
42-
// names in any GetField expressions stripped out so that they serialize to strings without such table names. Table
43-
// names in GetField expressions are expected in much of the rest of the analyzer, so we do this after the bulk of
44-
// analyzer work.
45-
//
46-
// The `information_schema.columns` table also needs access to the default values of every column in the database, and
47-
// because it's a table it can't implement `sql.Expressioner` like other node types. Instead it has special handling
48-
// here, as well as in the `resolve_functions` rule.
26+
// validateColumnDefaults ensures that newly created column defaults from a DDL statement are legal for the type of
27+
// column, various other business logic checks to match MySQL's logic.
4928
func validateColumnDefaults(ctx *sql.Context, _ *Analyzer, n sql.Node, _ *plan.Scope, _ RuleSelector, qFlags *sql.QueryFlags) (sql.Node, transform.TreeIdentity, error) {
5029
span, ctx := ctx.Span("validateColumnDefaults")
5130
defer span.End()

sql/analyzer/rule_ids.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ const (
6565
assignRoutinesId // assignRoutines
6666
modifyUpdateExprsForJoinId // modifyUpdateExprsForJoin
6767
applyForeignKeysId // applyForeignKeys
68+
interpreterId // interpreter
6869

6970
// validate
7071
validateResolvedId // validateResolved

sql/analyzer/ruleid_string.go

Lines changed: 17 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sql/analyzer/rules.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ var OnceAfterDefault = []Rule{
9292
{assignRoutinesId, assignRoutines},
9393
{modifyUpdateExprsForJoinId, modifyUpdateExprsForJoin},
9494
{applyForeignKeysId, applyForeignKeys},
95+
{interpreterId, interpreter},
9596
}
9697

9798
// DefaultValidationRules to apply while analyzing nodes.

sql/errors.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package sql
1616

1717
import (
1818
"fmt"
19+
"io"
1920
"strings"
2021

2122
"github.com/dolthub/vitess/go/mysql"
@@ -1068,6 +1069,15 @@ func (w WrappedInsertError) Error() string {
10681069
return w.Cause.Error()
10691070
}
10701071

1072+
// Format implements fmt.Formatter
1073+
func (w WrappedInsertError) Format(s fmt.State, verb rune) {
1074+
if fmtErr, ok := w.Cause.(fmt.Formatter); ok {
1075+
fmtErr.Format(s, verb)
1076+
return
1077+
}
1078+
_, _ = io.WriteString(s, w.Error())
1079+
}
1080+
10711081
// IgnorableError is used propagate information about an error that needs to be ignored and does not interfere with
10721082
// any update accumulators
10731083
type IgnorableError struct {

0 commit comments

Comments
 (0)