diff --git a/engine.go b/engine.go index 126aad7877..a8a8988f26 100644 --- a/engine.go +++ b/engine.go @@ -210,7 +210,7 @@ func (e *Engine) AnalyzeQuery( query string, ) (sql.Node, error) { binder := planbuilder.New(ctx, e.Analyzer.Catalog, e.Parser) - parsed, _, _, qFlags, err := binder.Parse(query, false) + parsed, _, _, qFlags, err := binder.Parse(query, nil, false) if err != nil { return nil, err } @@ -586,7 +586,7 @@ func (e *Engine) bindQuery(ctx *sql.Context, query string, parsed sqlparser.Stat var bound sql.Node var err error if parsed == nil { - bound, _, _, qFlags, err = binder.Parse(query, false) + bound, _, _, qFlags, err = binder.Parse(query, qFlags, false) if err != nil { clearAutocommitErr := clearAutocommitTransaction(ctx) if clearAutocommitErr != nil { diff --git a/enginetest/engine_only_test.go b/enginetest/engine_only_test.go index 6fb9c344a7..92c16714f0 100644 --- a/enginetest/engine_only_test.go +++ b/enginetest/engine_only_test.go @@ -511,7 +511,7 @@ func TestAnalyzer_Exp(t *testing.T) { ctx := enginetest.NewContext(harness) b := planbuilder.New(ctx, e.EngineAnalyzer().Catalog, sql.NewMysqlParser()) - parsed, _, _, _, err := b.Parse(tt.query, false) + parsed, _, _, _, err := b.Parse(tt.query, nil, false) require.NoError(t, err) analyzed, err := e.EngineAnalyzer().Analyze(ctx, parsed, nil, nil) diff --git a/server/handler.go b/server/handler.go index 5aae08f5fe..f72336d276 100644 --- a/server/handler.go +++ b/server/handler.go @@ -425,6 +425,7 @@ func (h *Handler) doQuery( } }() + qFlags.Set(sql.QFlagDeferProjections) schema, rowIter, qFlags, err := queryExec(sqlCtx, query, parsed, analyzedPlan, bindings, qFlags) if err != nil { sqlCtx.GetLogger().WithError(err).Warn("error running query") @@ -520,7 +521,8 @@ func GetDeferredProjections(iter sql.RowIter) []sql.Expression { case *plan.TrackedRowIter: if commitIter, isCommitIter := i.GetIter().(*rowexec.TransactionCommittingIter); isCommitIter { if projIter, isProjIter := commitIter.GetIter().(*rowexec.ProjectIter); isProjIter { - if projIter.Deferred() { + if projIter.CanDefer() { + projIter.Defer() return projIter.GetProjections() } } diff --git a/sql/analyzer/stored_procedures.go b/sql/analyzer/stored_procedures.go index f8a2750983..d1b1575b80 100644 --- a/sql/analyzer/stored_procedures.go +++ b/sql/analyzer/stored_procedures.go @@ -56,7 +56,7 @@ func loadStoredProcedures(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan var parsedProcedure sql.Node b := planbuilder.New(ctx, a.Catalog, sql.NewMysqlParser()) b.SetParserOptions(sql.NewSqlModeFromString(procedure.SqlMode).ParserOptions()) - parsedProcedure, _, _, _, err = b.Parse(procedure.CreateStatement, false) + parsedProcedure, _, _, _, err = b.Parse(procedure.CreateStatement, nil, false) if err != nil { procToRegister = &plan.Procedure{ CreateProcedureString: procedure.CreateStatement, @@ -300,7 +300,7 @@ func applyProcedures(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scop b.ProcCtx().AsOf = asOf } b.ProcCtx().DbName = call.Database().Name() - parsedProcedure, _, _, _, err = b.Parse(procedure.CreateStatement, false) + parsedProcedure, _, _, _, err = b.Parse(procedure.CreateStatement, nil, false) if err != nil { return nil, transform.SameTree, err } diff --git a/sql/analyzer/triggers.go b/sql/analyzer/triggers.go index 9bb6a5da45..4fe8d89db1 100644 --- a/sql/analyzer/triggers.go +++ b/sql/analyzer/triggers.go @@ -204,7 +204,7 @@ func applyTriggers(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scope, var parsedTrigger sql.Node sqlMode := sql.NewSqlModeFromString(trigger.SqlMode) b.SetParserOptions(sqlMode.ParserOptions()) - parsedTrigger, _, _, _, err = b.Parse(trigger.CreateStatement, false) + parsedTrigger, _, _, _, err = b.Parse(trigger.CreateStatement, nil, false) b.Reset() if err != nil { return nil, transform.SameTree, err @@ -225,7 +225,7 @@ func applyTriggers(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scope, // first pass allows unresolved before we know whether trigger is relevant // TODO store destination table name with trigger, so we don't have to do parse twice b.TriggerCtx().Call = true - parsedTrigger, _, _, _, err = b.Parse(trigger.CreateStatement, false) + parsedTrigger, _, _, _, err = b.Parse(trigger.CreateStatement, nil, false) b.TriggerCtx().Call = false b.Reset() if err != nil { diff --git a/sql/plan/project.go b/sql/plan/project.go index a3cabe4bc9..814f63bd13 100644 --- a/sql/plan/project.go +++ b/sql/plan/project.go @@ -27,7 +27,7 @@ import ( type Project struct { UnaryNode Projections []sql.Expression - Deferred bool + CanDefer bool } var _ sql.Expressioner = (*Project)(nil) @@ -187,6 +187,6 @@ func (p *Project) WithExpressions(exprs ...sql.Expression) (sql.Node, error) { func (p *Project) WithCanDefer(canDefer bool) *Project { np := *p - np.Deferred = canDefer + np.CanDefer = canDefer return &np } diff --git a/sql/planbuilder/parse.go b/sql/planbuilder/parse.go index 94b7ab510c..58550301c5 100644 --- a/sql/planbuilder/parse.go +++ b/sql/planbuilder/parse.go @@ -41,11 +41,11 @@ func ParseWithOptions(ctx *sql.Context, cat sql.Catalog, query string, options a // TODO: need correct parser b := New(ctx, cat, sql.NewMysqlParser()) b.SetParserOptions(options) - node, _, _, qFlags, err := b.Parse(query, false) + node, _, _, qFlags, err := b.Parse(query, nil, false) return node, qFlags, err } -func (b *Builder) Parse(query string, multi bool) (ret sql.Node, parsed, remainder string, qProps *sql.QueryFlags, err error) { +func (b *Builder) Parse(query string, qFlags *sql.QueryFlags, multi bool) (ret sql.Node, parsed, remainder string, qProps *sql.QueryFlags, err error) { defer trace.StartRegion(b.ctx, "ParseOnly").End() b.nesting++ if b.nesting > maxAnalysisIterations { @@ -74,6 +74,10 @@ func (b *Builder) Parse(query string, multi bool) (ret sql.Node, parsed, remaind return nil, parsed, remainder, nil, sql.ErrSyntaxError.New(err.Error()) } + if qFlags != nil { + b.qFlags = qFlags + } + outScope := b.build(nil, stmt, parsed) return outScope.node, parsed, remainder, b.qFlags, err diff --git a/sql/planbuilder/project.go b/sql/planbuilder/project.go index d70bc8e042..95d98f5a33 100644 --- a/sql/planbuilder/project.go +++ b/sql/planbuilder/project.go @@ -221,7 +221,7 @@ func (b *Builder) buildProjection(inScope, outScope *scope) { return } } - proj.(*plan.Project).Deferred = true + proj.(*plan.Project).CanDefer = true } func selectExprNeedsAlias(e *ast.AliasedExpr, expr sql.Expression) bool { diff --git a/sql/planbuilder/show.go b/sql/planbuilder/show.go index d56fb30e1b..d5ee9c076e 100644 --- a/sql/planbuilder/show.go +++ b/sql/planbuilder/show.go @@ -343,7 +343,7 @@ func (b *Builder) buildShowProcedureStatus(inScope *scope, s *ast.Show) (outScop node, _, _, _, err := b.Parse("select routine_schema as `Db`, routine_name as `Name`, routine_type as `Type`,"+ "definer as `Definer`, last_altered as `Modified`, created as `Created`, security_type as `Security_type`,"+ "routine_comment as `Comment`, CHARACTER_SET_CLIENT as `character_set_client`, COLLATION_CONNECTION as `collation_connection`,"+ - "database_collation as `Database Collation` from information_schema.routines where routine_type = 'PROCEDURE'", false) + "database_collation as `Database Collation` from information_schema.routines where routine_type = 'PROCEDURE'", nil, false) if err != nil { b.handleErr(err) } @@ -379,7 +379,7 @@ func (b *Builder) buildShowFunctionStatus(inScope *scope, s *ast.Show) (outScope node, _, _, _, err := b.Parse("select routine_schema as `Db`, routine_name as `Name`, routine_type as `Type`,"+ "definer as `Definer`, last_altered as `Modified`, created as `Created`, security_type as `Security_type`,"+ "routine_comment as `Comment`, character_set_client, collation_connection,"+ - "database_collation as `Database Collation` from information_schema.routines where routine_type = 'FUNCTION'", false) + "database_collation as `Database Collation` from information_schema.routines where routine_type = 'FUNCTION'", nil, false) if err != nil { b.handleErr(err) } @@ -783,7 +783,7 @@ func (b *Builder) buildShowCollation(inScope *scope, s *ast.Show) (outScope *sco // information_schema, with slightly different syntax and with some columns aliased. // TODO: install information_schema automatically for all catalogs node, _, _, _, err := b.Parse("select collation_name as `collation`, character_set_name as charset, id,"+ - "is_default as `default`, is_compiled as compiled, sortlen, pad_attribute from information_schema.collations order by collation_name", false) + "is_default as `default`, is_compiled as compiled, sortlen, pad_attribute from information_schema.collations order by collation_name", nil, false) if err != nil { b.handleErr(err) } @@ -828,7 +828,7 @@ select XA as XA, SAVEPOINTS as Savepoints from information_schema.engines -`, false) +`, nil, false) if err != nil { b.handleErr(err) } @@ -839,7 +839,7 @@ from information_schema.engines func (b *Builder) buildShowPlugins(inScope *scope, s *ast.Show) (outScope *scope) { outScope = inScope.push() - infoSchemaSelect, _, _, _, err := b.Parse("select * from information_schema.plugins", false) + infoSchemaSelect, _, _, _, err := b.Parse("select * from information_schema.plugins", nil,false) if err != nil { b.handleErr(err) } diff --git a/sql/rowexec/rel.go b/sql/rowexec/rel.go index eb74a94e41..17bb1c7f37 100644 --- a/sql/rowexec/rel.go +++ b/sql/rowexec/rel.go @@ -313,7 +313,7 @@ func (b *BaseBuilder) buildProject(ctx *sql.Context, n *plan.Project, row sql.Ro return sql.NewSpanIter(span, &ProjectIter{ projs: n.Projections, - deferred: n.Deferred, + canDefer: n.CanDefer, childIter: i, }), nil } diff --git a/sql/rowexec/rel_iters.go b/sql/rowexec/rel_iters.go index 13e12a4ed1..7569306e19 100644 --- a/sql/rowexec/rel_iters.go +++ b/sql/rowexec/rel_iters.go @@ -126,6 +126,7 @@ var _ sql.RowIter = &iters.JsonTableRowIter{} type ProjectIter struct { projs []sql.Expression + canDefer bool deferred bool childIter sql.RowIter } @@ -149,8 +150,12 @@ func (i *ProjectIter) GetProjections() []sql.Expression { return i.projs } -func (i *ProjectIter) Deferred() bool { - return i.deferred +func (i *ProjectIter) CanDefer() bool { + return i.canDefer +} + +func (i *ProjectIter) Defer() { + i.deferred = true } // ProjectRow evaluates a set of projections.