From d69df362a0c68f9e5923dd39721dc2d139f6e242 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Thu, 28 Nov 2024 17:00:22 +0100 Subject: [PATCH 01/11] feat: make last_insert_id with argument work for sharded plans Signed-off-by: Andres Taylor --- .../expressions/expressions.test | 4 + go/vt/vtgate/engine/fake_vcursor_test.go | 7 + go/vt/vtgate/engine/primitive.go | 2 +- go/vt/vtgate/engine/save_to_session.go | 125 ++++++++++++++++++ go/vt/vtgate/executorcontext/vcursor_impl.go | 4 + .../planbuilder/operator_transformers.go | 25 ++++ .../planbuilder/operators/query_planning.go | 27 +++- .../planbuilder/operators/save_to_session.go | 71 ++++++++++ .../planbuilder/testdata/select_cases.json | 80 +++++++++-- go/vt/vtgate/semantics/analyzer.go | 4 + go/vt/vtgate/semantics/semantic_table.go | 15 ++- 11 files changed, 345 insertions(+), 19 deletions(-) create mode 100644 go/vt/vtgate/engine/save_to_session.go create mode 100644 go/vt/vtgate/planbuilder/operators/save_to_session.go diff --git a/go/test/endtoend/vtgate/vitess_tester/expressions/expressions.test b/go/test/endtoend/vtgate/vitess_tester/expressions/expressions.test index 60c1e641463..9d8c1e55f67 100644 --- a/go/test/endtoend/vtgate/vitess_tester/expressions/expressions.test +++ b/go/test/endtoend/vtgate/vitess_tester/expressions/expressions.test @@ -28,3 +28,7 @@ SELECT (~ (1 || 0)) IS NULL; SELECT 1 WHERE (~ (1 || 0)) IS NULL; + +select last_insert_id(12); + +select last_insert_id(); \ No newline at end of file diff --git a/go/vt/vtgate/engine/fake_vcursor_test.go b/go/vt/vtgate/engine/fake_vcursor_test.go index 9ba4fdc6a6b..b2e9bbacc05 100644 --- a/go/vt/vtgate/engine/fake_vcursor_test.go +++ b/go/vt/vtgate/engine/fake_vcursor_test.go @@ -263,6 +263,10 @@ func (t *noopVCursor) SetFoundRows(u uint64) { panic("implement me") } +func (t *noopVCursor) SetLastInsertID(id uint64) { + panic("implement me") +} + func (t *noopVCursor) InTransactionAndIsDML() bool { panic("implement me") } @@ -500,6 +504,9 @@ func (f *loggingVCursor) GetSystemVariables(func(k string, v string)) { func (f *loggingVCursor) SetFoundRows(u uint64) { panic("implement me") } +func (f *loggingVCursor) SetLastInsertID(id uint64) { + panic("implement me") +} func (f *loggingVCursor) InTransactionAndIsDML() bool { return false diff --git a/go/vt/vtgate/engine/primitive.go b/go/vt/vtgate/engine/primitive.go index 4f3a388d04f..d79eaa13b05 100644 --- a/go/vt/vtgate/engine/primitive.go +++ b/go/vt/vtgate/engine/primitive.go @@ -182,7 +182,7 @@ type ( SetPriority(string) SetExecQueryTimeout(timeout *int) SetFoundRows(uint64) - + SetLastInsertID(uint64) SetDDLStrategy(string) GetDDLStrategy() string SetMigrationContext(string) diff --git a/go/vt/vtgate/engine/save_to_session.go b/go/vt/vtgate/engine/save_to_session.go new file mode 100644 index 00000000000..98852bf4c0d --- /dev/null +++ b/go/vt/vtgate/engine/save_to_session.go @@ -0,0 +1,125 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package engine + +import ( + "context" + + "vitess.io/vitess/go/sqltypes" + querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtgate/evalengine" +) + +type SaveToSession struct { + noTxNeeded + + Source Primitive + Offset evalengine.Expr +} + +var _ Primitive = (*SaveToSession)(nil) + +func (s *SaveToSession) RouteType() string { + return s.Source.RouteType() +} + +func (s *SaveToSession) GetKeyspaceName() string { + return s.Source.GetKeyspaceName() +} + +func (s *SaveToSession) GetTableName() string { + return s.Source.GetTableName() +} + +func (s *SaveToSession) GetFields(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { + return s.Source.GetFields(ctx, vcursor, bindVars) +} + +// TryExecute on SaveToSession will execute the Source and save the last row's value of Offset into the session. +func (s *SaveToSession) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { + result, err := s.Source.TryExecute(ctx, vcursor, bindVars, wantfields) + if err != nil { + return nil, err + } + + intEvalResult, ok, err := s.getUintFromOffset(ctx, vcursor, bindVars, result) + if err != nil { + return nil, err + } + if ok { + vcursor.Session().SetLastInsertID(intEvalResult) + } + return result, nil +} + +func (s *SaveToSession) getUintFromOffset(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, result *sqltypes.Result) (uint64, bool, error) { + if len(result.Rows) == 0 { + return 0, false, nil + } + + env := evalengine.NewExpressionEnv(ctx, bindVars, vcursor) + env.Row = result.Rows[len(result.Rows)-1] // last row + evalResult, err := env.Evaluate(s.Offset) + if err != nil { + return 0, false, err + } + value, err := evalResult.Value(vcursor.ConnCollation()).ToCastUint64() + if err != nil { + return 0, false, err + } + return value, true, nil +} + +// TryStreamExecute on SaveToSession will execute the Source and save the last row's value of Offset into the session. +func (s *SaveToSession) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { + var value *uint64 + + f := func(qr *sqltypes.Result) error { + v, ok, err := s.getUintFromOffset(ctx, vcursor, bindVars, qr) + if err != nil { + return err + } + if ok { + value = &v + } + return callback(qr) + } + + err := s.Source.TryStreamExecute(ctx, vcursor, bindVars, wantfields, f) + if err != nil { + return err + } + if value != nil { + vcursor.Session().SetLastInsertID(*value) + } + + return nil +} + +func (s *SaveToSession) Inputs() ([]Primitive, []map[string]any) { + return []Primitive{s.Source}, nil +} + +func (s *SaveToSession) description() PrimitiveDescription { + return PrimitiveDescription{ + OperatorType: "SaveToSession", + Other: map[string]interface{}{ + "Offset": sqlparser.String(s.Offset), + }, + } +} diff --git a/go/vt/vtgate/executorcontext/vcursor_impl.go b/go/vt/vtgate/executorcontext/vcursor_impl.go index c1f341b38cf..4a249059983 100644 --- a/go/vt/vtgate/executorcontext/vcursor_impl.go +++ b/go/vt/vtgate/executorcontext/vcursor_impl.go @@ -1130,6 +1130,10 @@ func (vc *VCursorImpl) SetFoundRows(foundRows uint64) { vc.SafeSession.SetFoundRows(foundRows) } +func (vc *vcursorImpl) SetLastInsertID(id uint64) { + vc.safeSession.LastInsertId = id +} + // SetDDLStrategy implements the SessionActions interface func (vc *VCursorImpl) SetDDLStrategy(strategy string) { vc.SafeSession.SetDDLStrategy(strategy) diff --git a/go/vt/vtgate/planbuilder/operator_transformers.go b/go/vt/vtgate/planbuilder/operator_transformers.go index df14745e6b2..0a1c7b68833 100644 --- a/go/vt/vtgate/planbuilder/operator_transformers.go +++ b/go/vt/vtgate/planbuilder/operator_transformers.go @@ -81,6 +81,8 @@ func transformToPrimitive(ctx *plancontext.PlanningContext, op operators.Operato return transformRecurseCTE(ctx, op) case *operators.PercentBasedMirror: return transformPercentBasedMirror(ctx, op) + case *operators.SaveToSession: + return transformSaveToSession(ctx, op) } return nil, vterrors.VT13001(fmt.Sprintf("unknown type encountered: %T (transformToPrimitive)", op)) @@ -481,6 +483,29 @@ func newSimpleProjection(cols []int, colNames []string, src engine.Primitive) en } } +func transformSaveToSession(ctx *plancontext.PlanningContext, op *operators.SaveToSession) (engine.Primitive, error) { + src, err := transformToPrimitive(ctx, op.Source) + if err != nil { + return nil, err + } + + cfg := &evalengine.Config{ + ResolveType: ctx.TypeForExpr, + Collation: ctx.SemTable.Collation, + Environment: ctx.VSchema.Environment(), + } + + offset, err := evalengine.Translate(sqlparser.NewOffset(op.Offset, nil), cfg) + if err != nil { + return nil, err + } + + return &engine.SaveToSession{ + Source: src, + Offset: offset, + }, nil +} + func transformFilter(ctx *plancontext.PlanningContext, op *operators.Filter) (engine.Primitive, error) { src, err := transformToPrimitive(ctx, op.Source) if err != nil { diff --git a/go/vt/vtgate/planbuilder/operators/query_planning.go b/go/vt/vtgate/planbuilder/operators/query_planning.go index 5fe0c7773c1..f47d6b6927c 100644 --- a/go/vt/vtgate/planbuilder/operators/query_planning.go +++ b/go/vt/vtgate/planbuilder/operators/query_planning.go @@ -829,11 +829,34 @@ func addTruncationOrProjectionToReturnOutput(ctx *plancontext.PlanningContext, s cols := output.GetSelectExprs(ctx) sizeCorrect := len(selExprs) == len(cols) || tryTruncateColumnsAt(output, len(selExprs)) - if sizeCorrect && colNamesAlign(selExprs, cols) { + if !sizeCorrect || !colNamesAlign(selExprs, cols) { + output = createSimpleProjection(ctx, selExprs, output) + } + + if !ctx.SemTable.QuerySignature.LastInsertIDArg { return output } - return createSimpleProjection(ctx, selExprs, output) + var offset int + for i, expr := range selExprs { + ae, ok := expr.(*sqlparser.AliasedExpr) + if !ok { + panic(vterrors.VT09015()) + } + fnc, ok := ae.Expr.(*sqlparser.FuncExpr) + if !ok || !fnc.Name.EqualString("last_insert_id") { + continue + } + offset = i + break + } + + return &SaveToSession{ + unaryOperator: unaryOperator{ + Source: output, + }, + Offset: offset, + } } func colNamesAlign(expected, actual sqlparser.SelectExprs) bool { diff --git a/go/vt/vtgate/planbuilder/operators/save_to_session.go b/go/vt/vtgate/planbuilder/operators/save_to_session.go new file mode 100644 index 00000000000..da00b7a4426 --- /dev/null +++ b/go/vt/vtgate/planbuilder/operators/save_to_session.go @@ -0,0 +1,71 @@ +/* +Copyright 2022 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package operators + +import ( + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" +) + +// SaveToSession is used to save a value to the session. +// At the moment it's only used for last_insert_id, but it could be used for other things in the future. +type SaveToSession struct { + unaryOperator + Offset int +} + +var _ Operator = (*SaveToSession)(nil) + +func (s *SaveToSession) Clone(inputs []Operator) Operator { + k := *s + k.Source = inputs[0] + return &k +} + +func (s *SaveToSession) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { + src := s.Source.AddPredicate(ctx, expr) + s.Source = src + return s +} + +func (s *SaveToSession) AddColumn(ctx *plancontext.PlanningContext, reuseExisting bool, addToGroupBy bool, expr *sqlparser.AliasedExpr) int { + return s.Source.AddColumn(ctx, reuseExisting, addToGroupBy, expr) +} + +func (s *SaveToSession) AddWSColumn(ctx *plancontext.PlanningContext, offset int, underRoute bool) int { + return s.Source.AddWSColumn(ctx, offset, underRoute) +} + +func (s *SaveToSession) FindCol(ctx *plancontext.PlanningContext, expr sqlparser.Expr, underRoute bool) int { + return s.Source.FindCol(ctx, expr, underRoute) +} + +func (s *SaveToSession) GetColumns(ctx *plancontext.PlanningContext) []*sqlparser.AliasedExpr { + return s.Source.GetColumns(ctx) +} + +func (s *SaveToSession) GetSelectExprs(ctx *plancontext.PlanningContext) sqlparser.SelectExprs { + return s.Source.GetSelectExprs(ctx) +} + +func (s *SaveToSession) ShortDescription() string { + return "" +} + +func (s *SaveToSession) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy { + return s.Source.GetOrdering(ctx) +} diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.json b/go/vt/vtgate/planbuilder/testdata/select_cases.json index eac13216380..1bb1e967ce3 100644 --- a/go/vt/vtgate/planbuilder/testdata/select_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/select_cases.json @@ -2160,6 +2160,62 @@ ] } }, + { + "comment": "save literal to session", + "query": "select last_insert_id(12)", + "plan": { + "QueryType": "SELECT", + "Original": "select last_insert_id(12)", + "Instructions": { + "OperatorType": "SaveToSession", + "Offset": "_vt_column_0", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Reference", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select last_insert_id(12) from dual where 1 != 1", + "Query": "select last_insert_id(12) from dual", + "Table": "dual" + } + ] + }, + "TablesUsed": [ + "main.dual" + ] + } + }, + { + "comment": "save column value to session - sharded", + "query": "select bar, 12, last_insert_id(foo) from user", + "plan": { + "QueryType": "SELECT", + "Original": "select bar, 12, last_insert_id(foo) from user", + "Instructions": { + "OperatorType": "SaveToSession", + "Offset": "_vt_column_2", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select bar, 12, last_insert_id(foo) from `user` where 1 != 1", + "Query": "select bar, 12, last_insert_id(foo) from `user`", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, { "comment": "sql_calc_found_rows with SelectEqualUnique plans", "query": "select sql_calc_found_rows * from music where user_id = 1 limit 2", @@ -5593,15 +5649,21 @@ "QueryType": "SELECT", "Original": "select last_insert_id(id) from user", "Instructions": { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select last_insert_id(id) from `user` where 1 != 1", - "Query": "select last_insert_id(id) from `user`", - "Table": "`user`" + "OperatorType": "SaveToSession", + "Offset": "_vt_column_0", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select last_insert_id(id) from `user` where 1 != 1", + "Query": "select last_insert_id(id) from `user`", + "Table": "`user`" + } + ] }, "TablesUsed": [ "user.user" diff --git a/go/vt/vtgate/semantics/analyzer.go b/go/vt/vtgate/semantics/analyzer.go index 0a9d2480d9b..fe48405aa45 100644 --- a/go/vt/vtgate/semantics/analyzer.go +++ b/go/vt/vtgate/semantics/analyzer.go @@ -463,6 +463,10 @@ func (a *analyzer) noteQuerySignature(node sqlparser.SQLNode) { a.sig.Aggregation = true case *sqlparser.Delete, *sqlparser.Update, *sqlparser.Insert: a.sig.DML = true + case *sqlparser.FuncExpr: + if len(node.Exprs) == 1 && node.Name.EqualString("last_insert_id") { + a.sig.LastInsertIDArg = true + } } } diff --git a/go/vt/vtgate/semantics/semantic_table.go b/go/vt/vtgate/semantics/semantic_table.go index f9856a901a6..77c8f6db835 100644 --- a/go/vt/vtgate/semantics/semantic_table.go +++ b/go/vt/vtgate/semantics/semantic_table.go @@ -80,13 +80,14 @@ type ( // QuerySignature is used to identify shortcuts in the planning process QuerySignature struct { - Aggregation bool - DML bool - Distinct bool - HashJoin bool - SubQueries bool - Union bool - RecursiveCTE bool + Aggregation bool + DML bool + Distinct bool + HashJoin bool + SubQueries bool + Union bool + RecursiveCTE bool + LastInsertIDArg bool // LastInsertIDArg is true if the query has a LAST_INSERT_ID(x) with an argument } // MirrorInfo stores information used to produce mirror From ef0ce607f807d09376825e6e911fc52f7a7cec32 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Thu, 28 Nov 2024 17:32:45 +0100 Subject: [PATCH 02/11] feat: make it possible to use the last_insert_id() with argument on unsharded select queries Signed-off-by: Andres Taylor --- .../planbuilder/operator_transformers.go | 7 ++++- .../planbuilder/single_sharded_shortcut.go | 24 ++++++++++++++++ .../planbuilder/testdata/select_cases.json | 28 +++++++++++++++++++ go/vt/vtgate/semantics/analyzer.go | 6 +++- go/vt/vtgate/semantics/table_collector.go | 27 +++++++++++------- 5 files changed, 80 insertions(+), 12 deletions(-) diff --git a/go/vt/vtgate/planbuilder/operator_transformers.go b/go/vt/vtgate/planbuilder/operator_transformers.go index 0a1c7b68833..846b15a1b48 100644 --- a/go/vt/vtgate/planbuilder/operator_transformers.go +++ b/go/vt/vtgate/planbuilder/operator_transformers.go @@ -489,13 +489,18 @@ func transformSaveToSession(ctx *plancontext.PlanningContext, op *operators.Save return nil, err } + v := op.Offset + return createSaveToSessionOnOffset(ctx, v, src) +} + +func createSaveToSessionOnOffset(ctx *plancontext.PlanningContext, v int, src engine.Primitive) (engine.Primitive, error) { cfg := &evalengine.Config{ ResolveType: ctx.TypeForExpr, Collation: ctx.SemTable.Collation, Environment: ctx.VSchema.Environment(), } - offset, err := evalengine.Translate(sqlparser.NewOffset(op.Offset, nil), cfg) + offset, err := evalengine.Translate(sqlparser.NewOffset(v, nil), cfg) if err != nil { return nil, err } diff --git a/go/vt/vtgate/planbuilder/single_sharded_shortcut.go b/go/vt/vtgate/planbuilder/single_sharded_shortcut.go index 5d877cd341d..a6910e22179 100644 --- a/go/vt/vtgate/planbuilder/single_sharded_shortcut.go +++ b/go/vt/vtgate/planbuilder/single_sharded_shortcut.go @@ -21,6 +21,7 @@ import ( "strings" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/planbuilder/operators" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" @@ -57,6 +58,29 @@ func selectUnshardedShortcut(ctx *plancontext.PlanningContext, stmt sqlparser.Se if err != nil { return nil, nil, err } + + if ctx.SemTable.QuerySignature.LastInsertIDArg { + var offset *int + for i, expr := range stmt.GetColumns() { + ae, ok := expr.(*sqlparser.AliasedExpr) + if !ok { + panic(vterrors.VT09015()) + } + fnc, ok := ae.Expr.(*sqlparser.FuncExpr) + if !ok || !fnc.Name.EqualString("last_insert_id") { + continue + } + offset = &i + break + } + if offset == nil { + return nil, nil, vterrors.VT12001("couldn't find last_insert_id in select expressions") + } + prim, err = createSaveToSessionOnOffset(ctx, *offset, prim) + if err != nil { + return nil, nil, err + } + } return prim, operators.QualifiedTableNames(ks, tableNames), nil } diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.json b/go/vt/vtgate/planbuilder/testdata/select_cases.json index 1bb1e967ce3..9a2f596ff6c 100644 --- a/go/vt/vtgate/planbuilder/testdata/select_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/select_cases.json @@ -2216,6 +2216,34 @@ ] } }, + { + "comment": "save column value to session - unsharded", + "query": "select bar, 12, last_insert_id(foo) from unsharded", + "plan": { + "QueryType": "SELECT", + "Original": "select bar, 12, last_insert_id(foo) from unsharded", + "Instructions": { + "OperatorType": "SaveToSession", + "Offset": "_vt_column_2", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select bar, 12, last_insert_id(foo) from unsharded where 1 != 1", + "Query": "select bar, 12, last_insert_id(foo) from unsharded", + "Table": "unsharded" + } + ] + }, + "TablesUsed": [ + "main.unsharded" + ] + } + }, { "comment": "sql_calc_found_rows with SelectEqualUnique plans", "query": "select sql_calc_found_rows * from music where user_id = 1 limit 2", diff --git a/go/vt/vtgate/semantics/analyzer.go b/go/vt/vtgate/semantics/analyzer.go index fe48405aa45..b01cc3fa836 100644 --- a/go/vt/vtgate/semantics/analyzer.go +++ b/go/vt/vtgate/semantics/analyzer.go @@ -151,7 +151,7 @@ func (a *analyzer) newSemTable( ExpandedColumns: map[sqlparser.TableName][]*sqlparser.ColName{}, columns: map[*sqlparser.Union]sqlparser.SelectExprs{}, StatementIDs: a.scoper.statementIDs, - QuerySignature: QuerySignature{}, + QuerySignature: a.sig, childForeignKeysInvolved: map[TableSet][]vindexes.ChildFKInfo{}, parentForeignKeysInvolved: map[TableSet][]vindexes.ParentFKInfo{}, childFkToUpdExprs: map[string]sqlparser.UpdateExprs{}, @@ -362,6 +362,10 @@ func (a *analyzer) analyze(statement sqlparser.Statement) error { return a.err } + if a.earlyTables.lastInsertIdWithArgument { + a.sig.LastInsertIDArg = true + } + if a.canShortCut(statement) { return nil } diff --git a/go/vt/vtgate/semantics/table_collector.go b/go/vt/vtgate/semantics/table_collector.go index 191d9c3b38e..c999b74384d 100644 --- a/go/vt/vtgate/semantics/table_collector.go +++ b/go/vt/vtgate/semantics/table_collector.go @@ -46,6 +46,10 @@ type ( // cte is a map of CTE definitions that are used in the query cte map[string]*CTE + + // lastInsertIdWithArgument is used to signal to later stages that we + // need to do special handling of the engine primitive + lastInsertIdWithArgument bool } ) @@ -59,16 +63,19 @@ func newEarlyTableCollector(si SchemaInformation, currentDb string) *earlyTableC } func (etc *earlyTableCollector) down(cursor *sqlparser.Cursor) bool { - with, ok := cursor.Node().(*sqlparser.With) - if !ok { - return true - } - for _, cte := range with.CTEs { - etc.cte[cte.ID.String()] = &CTE{ - Name: cte.ID.String(), - Query: cte.Subquery, - Columns: cte.Columns, - Recursive: with.Recursive, + switch node := cursor.Node().(type) { + case *sqlparser.With: + for _, cte := range node.CTEs { + etc.cte[cte.ID.String()] = &CTE{ + Name: cte.ID.String(), + Query: cte.Subquery, + Columns: cte.Columns, + Recursive: node.Recursive, + } + } + case *sqlparser.FuncExpr: + if node.Name.EqualString("last_insert_id") && len(node.Exprs) == 1 { + etc.lastInsertIdWithArgument = true } } return true From ab2df6b9b89f0798c4c616b9ffd09348746c861e Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Thu, 28 Nov 2024 17:41:31 +0100 Subject: [PATCH 03/11] codegen Signed-off-by: Andres Taylor --- go/vt/vtgate/engine/cached_size.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/go/vt/vtgate/engine/cached_size.go b/go/vt/vtgate/engine/cached_size.go index c764a6aab08..4ee5b4c03c8 100644 --- a/go/vt/vtgate/engine/cached_size.go +++ b/go/vt/vtgate/engine/cached_size.go @@ -1083,6 +1083,24 @@ func (cached *SQLCalcFoundRows) CachedSize(alloc bool) int64 { } return size } +func (cached *SaveToSession) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(32) + } + // field Source vitess.io/vitess/go/vt/vtgate/engine.Primitive + if cc, ok := cached.Source.(cachedObject); ok { + size += cc.CachedSize(true) + } + // field Offset vitess.io/vitess/go/vt/vtgate/evalengine.Expr + if cc, ok := cached.Offset.(cachedObject); ok { + size += cc.CachedSize(true) + } + return size +} func (cached *ScalarAggregate) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) From 005f9df17cc4ecf2215d968c722193c59a880865 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Thu, 28 Nov 2024 17:50:33 +0100 Subject: [PATCH 04/11] added note to release summary Signed-off-by: Andres Taylor --- changelog/22.0/22.0.0/summary.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog/22.0/22.0.0/summary.md b/changelog/22.0/22.0.0/summary.md index ebc0c485fc1..fe78bf3341e 100644 --- a/changelog/22.0/22.0.0/summary.md +++ b/changelog/22.0/22.0.0/summary.md @@ -8,10 +8,12 @@ - **[RPC Changes](#rpc-changes)** - **[Prefer not promoting a replica that is currently taking a backup](#reparents-prefer-not-backing-up)** - **[VTOrc Config File Changes](#vtorc-config-file-changes)** + - **[Added support for LAST_INSERT_ID(expr)](#last_insert_id)** - **[Minor Changes](#minor-changes)** - **[VTTablet Flags](#flags-vttablet)** + ## Major Changes ### RPC Changes @@ -59,6 +61,9 @@ The following fields can be dynamically changed - To upgrade to the newer version of the configuration file, first switch to using the flags in your current deployment before upgrading. Then you can switch to using the configuration file in the newer release. +### Added support for LAST_INSERT_ID(expr) + +Added support for LAST_INSERT_ID(expr) to align with MySQL behavior, enabling session-level assignment of the last insert ID via query expressions. ## Minor Changes From 533b2584ca8e97cbfbaba13614b799ba4439ae32 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Thu, 28 Nov 2024 17:53:38 +0100 Subject: [PATCH 05/11] refactor: removed unneccessary code Signed-off-by: Andres Taylor --- go/vt/vtgate/semantics/analyzer.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/go/vt/vtgate/semantics/analyzer.go b/go/vt/vtgate/semantics/analyzer.go index b01cc3fa836..988932f4414 100644 --- a/go/vt/vtgate/semantics/analyzer.go +++ b/go/vt/vtgate/semantics/analyzer.go @@ -467,10 +467,6 @@ func (a *analyzer) noteQuerySignature(node sqlparser.SQLNode) { a.sig.Aggregation = true case *sqlparser.Delete, *sqlparser.Update, *sqlparser.Insert: a.sig.DML = true - case *sqlparser.FuncExpr: - if len(node.Exprs) == 1 && node.Name.EqualString("last_insert_id") { - a.sig.LastInsertIDArg = true - } } } From d9cb7fccd980d29c85ce6b2f13fb97bd6f00622b Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 10 Dec 2024 12:13:22 +0100 Subject: [PATCH 06/11] feat: handle last_insert_id with arguments in complex expressions Signed-off-by: Andres Taylor --- go/vt/sqlparser/cow.go | 26 +++++ go/vt/vtgate/executorcontext/vcursor_impl.go | 4 +- .../planbuilder/operators/query_planning.go | 100 +++++++++++++----- .../planbuilder/testdata/select_cases.json | 12 +-- 4 files changed, 109 insertions(+), 33 deletions(-) diff --git a/go/vt/sqlparser/cow.go b/go/vt/sqlparser/cow.go index e807fdeef63..be376f84403 100644 --- a/go/vt/sqlparser/cow.go +++ b/go/vt/sqlparser/cow.go @@ -53,6 +53,32 @@ func CopyOnRewrite( return out } +func CopyAndReplaceExpr(node SQLNode, replaceFn func(node Expr) (Expr, bool)) SQLNode { + var replace Expr + pre := func(node, _ SQLNode) bool { + expr, ok := node.(Expr) + if !ok { + return true + } + newExpr, ok := replaceFn(expr) + if !ok { + return true + } + replace = newExpr + return false + } + + post := func(cursor *CopyOnWriteCursor) { + if replace == nil { + return + } + cursor.Replace(replace) + replace = nil + } + + return CopyOnRewrite(node, pre, post, nil) +} + // StopTreeWalk aborts the current tree walking. No more nodes will be visited, and the rewriter will exit out early func (c *CopyOnWriteCursor) StopTreeWalk() { c.stop = true diff --git a/go/vt/vtgate/executorcontext/vcursor_impl.go b/go/vt/vtgate/executorcontext/vcursor_impl.go index 4a249059983..6b83953fe23 100644 --- a/go/vt/vtgate/executorcontext/vcursor_impl.go +++ b/go/vt/vtgate/executorcontext/vcursor_impl.go @@ -1130,8 +1130,8 @@ func (vc *VCursorImpl) SetFoundRows(foundRows uint64) { vc.SafeSession.SetFoundRows(foundRows) } -func (vc *vcursorImpl) SetLastInsertID(id uint64) { - vc.safeSession.LastInsertId = id +func (vc *VCursorImpl) SetLastInsertID(id uint64) { + vc.SafeSession.LastInsertId = id } // SetDDLStrategy implements the SessionActions interface diff --git a/go/vt/vtgate/planbuilder/operators/query_planning.go b/go/vt/vtgate/planbuilder/operators/query_planning.go index f47d6b6927c..81e3a099ebc 100644 --- a/go/vt/vtgate/planbuilder/operators/query_planning.go +++ b/go/vt/vtgate/planbuilder/operators/query_planning.go @@ -32,8 +32,7 @@ import ( func planQuery(ctx *plancontext.PlanningContext, root Operator) Operator { var selExpr sqlparser.SelectExprs if horizon, isHorizon := root.(*Horizon); isHorizon { - sel := sqlparser.GetFirstSelect(horizon.Query) - selExpr = sqlparser.Clone(sel.SelectExprs) + selExpr = extractSelectExpressions(horizon) } output := runPhases(ctx, root) @@ -821,36 +820,46 @@ func tryPushUnion(ctx *plancontext.PlanningContext, op *Union) (Operator, *Apply return newUnion(sources, selects, op.unionColumns, op.distinct), Rewrote("merge union inputs") } -// addTruncationOrProjectionToReturnOutput uses the original Horizon to make sure that the output columns line up with what the user asked for -func addTruncationOrProjectionToReturnOutput(ctx *plancontext.PlanningContext, selExprs sqlparser.SelectExprs, output Operator) Operator { - if len(selExprs) == 0 { - return output - } - - cols := output.GetSelectExprs(ctx) - sizeCorrect := len(selExprs) == len(cols) || tryTruncateColumnsAt(output, len(selExprs)) - if !sizeCorrect || !colNamesAlign(selExprs, cols) { - output = createSimpleProjection(ctx, selExprs, output) - } - - if !ctx.SemTable.QuerySignature.LastInsertIDArg { - return output - } - - var offset int - for i, expr := range selExprs { +func handleLastInsertIDColumns(ctx *plancontext.PlanningContext, output Operator) Operator { + offset := -1 + topLevel := false + var arg sqlparser.Expr + for i, expr := range output.GetSelectExprs(ctx) { ae, ok := expr.(*sqlparser.AliasedExpr) if !ok { panic(vterrors.VT09015()) } - fnc, ok := ae.Expr.(*sqlparser.FuncExpr) - if !ok || !fnc.Name.EqualString("last_insert_id") { - continue + + replaceFn := func(node sqlparser.Expr) (sqlparser.Expr, bool) { + fnc, ok := node.(*sqlparser.FuncExpr) + if !ok || !fnc.Name.EqualString("last_insert_id") { + return node, false + } + if offset != -1 { + panic(vterrors.VT12001("last_insert_id() found multiple times in select list")) + } + arg = fnc.Exprs[0] + if node == ae.Expr { + topLevel = true + } + offset = i + return arg, true } - offset = i - break + + newExpr := sqlparser.CopyAndReplaceExpr(ae.Expr, replaceFn) + ae.Expr = newExpr.(sqlparser.Expr) } + if topLevel { + return &SaveToSession{ + unaryOperator: unaryOperator{ + Source: output, + }, + Offset: offset, + } + } + + offset = output.AddColumn(ctx, false, false, aeWrap(arg)) return &SaveToSession{ unaryOperator: unaryOperator{ Source: output, @@ -859,6 +868,47 @@ func addTruncationOrProjectionToReturnOutput(ctx *plancontext.PlanningContext, s } } +// addTruncationOrProjectionToReturnOutput uses the original Horizon to make sure that the output columns line up with what the user asked for +func addTruncationOrProjectionToReturnOutput(ctx *plancontext.PlanningContext, selExprs sqlparser.SelectExprs, output Operator) Operator { + if len(selExprs) == 0 { + return output + } + + if ctx.SemTable.QuerySignature.LastInsertIDArg { + output = handleLastInsertIDColumns(ctx, output) + } + + cols := output.GetSelectExprs(ctx) + sizeCorrect := len(selExprs) == len(cols) || tryTruncateColumnsAt(output, len(selExprs)) + if sizeCorrect && colNamesAlign(selExprs, cols) { + return output + } + + return createSimpleProjection(ctx, selExprs, output) +} + +func extractSelectExpressions(horizon *Horizon) sqlparser.SelectExprs { + sel := sqlparser.GetFirstSelect(horizon.Query) + // we handle last_insert_id with arguments separately - no need to send this down to mysql + selExprs := sqlparser.CopyAndReplaceExpr(sel.SelectExprs, func(node sqlparser.Expr) (sqlparser.Expr, bool) { + switch node := node.(type) { + case *sqlparser.FuncExpr: + if node.Name.EqualString("last_insert_id") && len(node.Exprs) == 1 { + return node.Exprs[0], true + } + return node, true + case sqlparser.Expr: + // we do this to make sure we get a clone of the expression + // if planning changes the expression, we should not change the original + return node, true + default: + return nil, false + } + }) + + return selExprs.(sqlparser.SelectExprs) +} + func colNamesAlign(expected, actual sqlparser.SelectExprs) bool { if len(expected) > len(actual) { // if we expect more columns than we have, we can't align diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.json b/go/vt/vtgate/planbuilder/testdata/select_cases.json index 9a2f596ff6c..fb90c96ec52 100644 --- a/go/vt/vtgate/planbuilder/testdata/select_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/select_cases.json @@ -2177,8 +2177,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select last_insert_id(12) from dual where 1 != 1", - "Query": "select last_insert_id(12) from dual", + "FieldQuery": "select 12 from dual where 1 != 1", + "Query": "select 12 from dual", "Table": "dual" } ] @@ -2205,8 +2205,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select bar, 12, last_insert_id(foo) from `user` where 1 != 1", - "Query": "select bar, 12, last_insert_id(foo) from `user`", + "FieldQuery": "select bar, 12, foo from `user` where 1 != 1", + "Query": "select bar, 12, foo from `user`", "Table": "`user`" } ] @@ -5687,8 +5687,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select last_insert_id(id) from `user` where 1 != 1", - "Query": "select last_insert_id(id) from `user`", + "FieldQuery": "select id from `user` where 1 != 1", + "Query": "select id from `user`", "Table": "`user`" } ] From f8cdb7cb6dbc82daa44f7d3982be16bb22ee3334 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 10 Dec 2024 12:15:04 +0100 Subject: [PATCH 07/11] comment Signed-off-by: Andres Taylor --- go/vt/sqlparser/{cow.go => copy_on_write.go} | 3 +++ 1 file changed, 3 insertions(+) rename go/vt/sqlparser/{cow.go => copy_on_write.go} (94%) diff --git a/go/vt/sqlparser/cow.go b/go/vt/sqlparser/copy_on_write.go similarity index 94% rename from go/vt/sqlparser/cow.go rename to go/vt/sqlparser/copy_on_write.go index be376f84403..4a355c0c9b8 100644 --- a/go/vt/sqlparser/cow.go +++ b/go/vt/sqlparser/copy_on_write.go @@ -53,6 +53,9 @@ func CopyOnRewrite( return out } +// CopyAndReplaceExpr traverses a syntax tree recursively, starting with root, +// and replaceFn is called for each node that is an Expr. If replaceFn returns +// true, the node is replaced with the returned node. func CopyAndReplaceExpr(node SQLNode, replaceFn func(node Expr) (Expr, bool)) SQLNode { var replace Expr pre := func(node, _ SQLNode) bool { From 00cca4d1a4d39f2b6c5056e72117e15cda4708f3 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 10 Dec 2024 12:33:52 +0100 Subject: [PATCH 08/11] test: add tests using aggregation and last_insert_id Signed-off-by: Andres Taylor --- .../aggregation/aggregation.test | 5 +++- .../planbuilder/testdata/aggr_cases.json | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/go/test/endtoend/vtgate/vitess_tester/aggregation/aggregation.test b/go/test/endtoend/vtgate/vitess_tester/aggregation/aggregation.test index 8b0997eed1a..1f4cd2fe4f0 100644 --- a/go/test/endtoend/vtgate/vitess_tester/aggregation/aggregation.test +++ b/go/test/endtoend/vtgate/vitess_tester/aggregation/aggregation.test @@ -64,4 +64,7 @@ from (select id, count(*) as num_segments from t1 group by 1 order by 2 desc lim join t2 u on u.id = t.id; select name -from (select name from t1 group by name having count(t1.id) > 1) t1; \ No newline at end of file +from (select name from t1 group by name having count(t1.id) > 1) t1; + +# this query uses last_insert_id with a column argument to show that this works well +select id, last_insert_id(count(*)) as num_segments from t1 group by id; diff --git a/go/vt/vtgate/planbuilder/testdata/aggr_cases.json b/go/vt/vtgate/planbuilder/testdata/aggr_cases.json index 49a03a8f05a..55f2b29392a 100644 --- a/go/vt/vtgate/planbuilder/testdata/aggr_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/aggr_cases.json @@ -4134,6 +4134,34 @@ ] } }, + { + "comment": "Save the count in last_insert_id", + "query": "select id, last_insert_id(count(*)) as num_segments from user group by id", + "plan": { + "QueryType": "SELECT", + "Original": "select id, last_insert_id(count(*)) as num_segments from user group by id", + "Instructions": { + "OperatorType": "SaveToSession", + "Offset": "_vt_column_1", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id, last_insert_id(count(*)) as num_segments from `user` where 1 != 1 group by id", + "Query": "select id, last_insert_id(count(*)) as num_segments from `user` group by id", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, { "comment": "Aggregations from derived table used in arithmetic outside derived table", "query": "select A.a, A.b, (A.a / A.b) as d from (select sum(a) as a, sum(b) as b from user) A", From 70996addd2522cf522fed582a96604bf0f962143 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 10 Dec 2024 12:43:59 +0100 Subject: [PATCH 09/11] feat: fail if last_insert_id is used in other contexts that SELECT Signed-off-by: Andres Taylor --- go/vt/vtgate/planbuilder/operators/query_planning.go | 4 +++- go/vt/vtgate/planbuilder/testdata/unsupported_cases.json | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/go/vt/vtgate/planbuilder/operators/query_planning.go b/go/vt/vtgate/planbuilder/operators/query_planning.go index 81e3a099ebc..6d6616ad92f 100644 --- a/go/vt/vtgate/planbuilder/operators/query_planning.go +++ b/go/vt/vtgate/planbuilder/operators/query_planning.go @@ -849,7 +849,9 @@ func handleLastInsertIDColumns(ctx *plancontext.PlanningContext, output Operator newExpr := sqlparser.CopyAndReplaceExpr(ae.Expr, replaceFn) ae.Expr = newExpr.(sqlparser.Expr) } - + if offset == -1 { + panic(vterrors.VT12001("last_insert_id() only supported in the select list")) + } if topLevel { return &SaveToSession{ unaryOperator: unaryOperator{ diff --git a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json index 9241cec595c..b1a858a65b3 100644 --- a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json @@ -383,5 +383,10 @@ "comment": "SOME/ANY/ALL comparison operator not supported for unsharded queries", "query": "select 1 from user where foo = ALL (select 1 from user_extra where foo = 1)", "plan": "VT12001: unsupported: ANY/ALL/SOME comparison operator" + }, + { + "comment": "last_insert_id() only supported in the select list", + "query": "select id, foo from user where last_insert_id(bar) = 12", + "plan": "VT12001: unsupported: last_insert_id() only supported in the select list" } ] From 0ab3717f239e51412e32f8b29e275334f8cd840b Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 10 Dec 2024 12:59:29 +0100 Subject: [PATCH 10/11] feat: keep the last_insert_id function to get the correct types in the output Signed-off-by: Andres Taylor --- .../planbuilder/operators/query_planning.go | 36 +++++-------------- .../planbuilder/testdata/select_cases.json | 12 +++---- 2 files changed, 14 insertions(+), 34 deletions(-) diff --git a/go/vt/vtgate/planbuilder/operators/query_planning.go b/go/vt/vtgate/planbuilder/operators/query_planning.go index 6d6616ad92f..c97f1750cfd 100644 --- a/go/vt/vtgate/planbuilder/operators/query_planning.go +++ b/go/vt/vtgate/planbuilder/operators/query_planning.go @@ -823,31 +823,29 @@ func tryPushUnion(ctx *plancontext.PlanningContext, op *Union) (Operator, *Apply func handleLastInsertIDColumns(ctx *plancontext.PlanningContext, output Operator) Operator { offset := -1 topLevel := false - var arg sqlparser.Expr + var foundFunc *sqlparser.FuncExpr for i, expr := range output.GetSelectExprs(ctx) { ae, ok := expr.(*sqlparser.AliasedExpr) if !ok { panic(vterrors.VT09015()) } - replaceFn := func(node sqlparser.Expr) (sqlparser.Expr, bool) { + _ = sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) { fnc, ok := node.(*sqlparser.FuncExpr) if !ok || !fnc.Name.EqualString("last_insert_id") { - return node, false + return true, nil } if offset != -1 { panic(vterrors.VT12001("last_insert_id() found multiple times in select list")) } - arg = fnc.Exprs[0] + foundFunc = fnc if node == ae.Expr { topLevel = true } offset = i - return arg, true - } + return false, nil - newExpr := sqlparser.CopyAndReplaceExpr(ae.Expr, replaceFn) - ae.Expr = newExpr.(sqlparser.Expr) + }, ae.Expr) } if offset == -1 { panic(vterrors.VT12001("last_insert_id() only supported in the select list")) @@ -861,7 +859,7 @@ func handleLastInsertIDColumns(ctx *plancontext.PlanningContext, output Operator } } - offset = output.AddColumn(ctx, false, false, aeWrap(arg)) + offset = output.AddColumn(ctx, false, false, aeWrap(foundFunc)) return &SaveToSession{ unaryOperator: unaryOperator{ Source: output, @@ -890,25 +888,7 @@ func addTruncationOrProjectionToReturnOutput(ctx *plancontext.PlanningContext, s } func extractSelectExpressions(horizon *Horizon) sqlparser.SelectExprs { - sel := sqlparser.GetFirstSelect(horizon.Query) - // we handle last_insert_id with arguments separately - no need to send this down to mysql - selExprs := sqlparser.CopyAndReplaceExpr(sel.SelectExprs, func(node sqlparser.Expr) (sqlparser.Expr, bool) { - switch node := node.(type) { - case *sqlparser.FuncExpr: - if node.Name.EqualString("last_insert_id") && len(node.Exprs) == 1 { - return node.Exprs[0], true - } - return node, true - case sqlparser.Expr: - // we do this to make sure we get a clone of the expression - // if planning changes the expression, we should not change the original - return node, true - default: - return nil, false - } - }) - - return selExprs.(sqlparser.SelectExprs) + return sqlparser.CloneSelectExprs(sqlparser.GetFirstSelect(horizon.Query).SelectExprs) } func colNamesAlign(expected, actual sqlparser.SelectExprs) bool { diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.json b/go/vt/vtgate/planbuilder/testdata/select_cases.json index fb90c96ec52..9a2f596ff6c 100644 --- a/go/vt/vtgate/planbuilder/testdata/select_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/select_cases.json @@ -2177,8 +2177,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select 12 from dual where 1 != 1", - "Query": "select 12 from dual", + "FieldQuery": "select last_insert_id(12) from dual where 1 != 1", + "Query": "select last_insert_id(12) from dual", "Table": "dual" } ] @@ -2205,8 +2205,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select bar, 12, foo from `user` where 1 != 1", - "Query": "select bar, 12, foo from `user`", + "FieldQuery": "select bar, 12, last_insert_id(foo) from `user` where 1 != 1", + "Query": "select bar, 12, last_insert_id(foo) from `user`", "Table": "`user`" } ] @@ -5687,8 +5687,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select id from `user` where 1 != 1", - "Query": "select id from `user`", + "FieldQuery": "select last_insert_id(id) from `user` where 1 != 1", + "Query": "select last_insert_id(id) from `user`", "Table": "`user`" } ] From 31af9e0e8f2af8c71ce1dbac3e5d9639a02b8e65 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 10 Dec 2024 13:28:10 +0100 Subject: [PATCH 11/11] test: update query to use valid column names Signed-off-by: Andres Taylor --- .../vitess_tester/aggregation/aggregation.test | 3 +++ .../planbuilder/testdata/select_cases.json | 16 ++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/go/test/endtoend/vtgate/vitess_tester/aggregation/aggregation.test b/go/test/endtoend/vtgate/vitess_tester/aggregation/aggregation.test index 1f4cd2fe4f0..d675b341616 100644 --- a/go/test/endtoend/vtgate/vitess_tester/aggregation/aggregation.test +++ b/go/test/endtoend/vtgate/vitess_tester/aggregation/aggregation.test @@ -68,3 +68,6 @@ from (select name from t1 group by name having count(t1.id) > 1) t1; # this query uses last_insert_id with a column argument to show that this works well select id, last_insert_id(count(*)) as num_segments from t1 group by id; + +# checking that we stored the correct value in the last_insert_id +select last_insert_id(); \ No newline at end of file diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.json b/go/vt/vtgate/planbuilder/testdata/select_cases.json index 9a2f596ff6c..d10638630ab 100644 --- a/go/vt/vtgate/planbuilder/testdata/select_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/select_cases.json @@ -2190,10 +2190,10 @@ }, { "comment": "save column value to session - sharded", - "query": "select bar, 12, last_insert_id(foo) from user", + "query": "select col, 12, last_insert_id(intcol) from user", "plan": { "QueryType": "SELECT", - "Original": "select bar, 12, last_insert_id(foo) from user", + "Original": "select col, 12, last_insert_id(intcol) from user", "Instructions": { "OperatorType": "SaveToSession", "Offset": "_vt_column_2", @@ -2205,8 +2205,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select bar, 12, last_insert_id(foo) from `user` where 1 != 1", - "Query": "select bar, 12, last_insert_id(foo) from `user`", + "FieldQuery": "select col, 12, last_insert_id(intcol) from `user` where 1 != 1", + "Query": "select col, 12, last_insert_id(intcol) from `user`", "Table": "`user`" } ] @@ -2218,10 +2218,10 @@ }, { "comment": "save column value to session - unsharded", - "query": "select bar, 12, last_insert_id(foo) from unsharded", + "query": "select col1, 12, last_insert_id(id) from unsharded", "plan": { "QueryType": "SELECT", - "Original": "select bar, 12, last_insert_id(foo) from unsharded", + "Original": "select col1, 12, last_insert_id(id) from unsharded", "Instructions": { "OperatorType": "SaveToSession", "Offset": "_vt_column_2", @@ -2233,8 +2233,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select bar, 12, last_insert_id(foo) from unsharded where 1 != 1", - "Query": "select bar, 12, last_insert_id(foo) from unsharded", + "FieldQuery": "select col1, 12, last_insert_id(id) from unsharded where 1 != 1", + "Query": "select col1, 12, last_insert_id(id) from unsharded", "Table": "unsharded" } ]