Skip to content

Commit feaa6d5

Browse files
fix: use related semtable for each operator to sql builder conversion
Signed-off-by: Harshit Gangal <harshit@planetscale.com>
1 parent c58abd9 commit feaa6d5

15 files changed

+172
-80
lines changed

go/vt/vtgate/planbuilder/operator_transformers.go

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,8 @@ func transformFkCascade(ctx *plancontext.PlanningContext, fkc *operators.FkCasca
7979
return nil, nil
8080
}
8181

82-
// Once we have the parent logical plan, we can create the selection logical plan and the primitives for the children operators.
83-
// For all of these, we don't need the semTable anymore. We set it to nil, to avoid using an incorrect one.
84-
ctx.SemTable = nil
82+
// Using the correct semantics.SemTable for the selection query created during planning.
83+
ctx.SemTable = fkc.SSemTable
8584
selLP, err := transformToLogicalPlan(ctx, fkc.Selection)
8685
if err != nil {
8786
return nil, err
@@ -90,6 +89,8 @@ func transformFkCascade(ctx *plancontext.PlanningContext, fkc *operators.FkCasca
9089
// Go over the children and convert them to Primitives too.
9190
var children []*engine.FkChild
9291
for _, child := range fkc.Children {
92+
// Using the correct semantics.SemTable for the child table cascade query created during planning.
93+
ctx.SemTable = child.ST
9394
childLP, err := transformToLogicalPlan(ctx, child.Op)
9495
if err != nil {
9596
return nil, err
@@ -109,6 +110,31 @@ func transformFkCascade(ctx *plancontext.PlanningContext, fkc *operators.FkCasca
109110
return newFkCascade(parentLP, selLP, children), nil
110111
}
111112

113+
// transformFkVerify transforms a FkVerify operator into a logical plan.
114+
func transformFkVerify(ctx *plancontext.PlanningContext, fkv *operators.FkVerify) (logicalPlan, error) {
115+
inputLP, err := transformToLogicalPlan(ctx, fkv.Input)
116+
if err != nil {
117+
return nil, err
118+
}
119+
120+
// Go over the children and convert them to Primitives too.
121+
var verify []*verifyLP
122+
for _, v := range fkv.Verify {
123+
// Using the correct semantics.SemTable for the parent table verify query created during planning.
124+
ctx.SemTable = v.ST
125+
lp, err := transformToLogicalPlan(ctx, v.Op)
126+
if err != nil {
127+
return nil, err
128+
}
129+
verify = append(verify, &verifyLP{
130+
verify: lp,
131+
typ: v.Typ,
132+
})
133+
}
134+
135+
return newFkVerify(inputLP, verify), nil
136+
}
137+
112138
func transformSubQuery(ctx *plancontext.PlanningContext, op *operators.SubQuery) (logicalPlan, error) {
113139
outer, err := transformToLogicalPlan(ctx, op.Outer)
114140
if err != nil {
@@ -136,33 +162,6 @@ func transformSubQuery(ctx *plancontext.PlanningContext, op *operators.SubQuery)
136162
return newSemiJoin(outer, inner, op.Vars, lhsCols), nil
137163
}
138164

139-
// transformFkVerify transforms a FkVerify operator into a logical plan.
140-
func transformFkVerify(ctx *plancontext.PlanningContext, fkv *operators.FkVerify) (logicalPlan, error) {
141-
inputLP, err := transformToLogicalPlan(ctx, fkv.Input)
142-
if err != nil {
143-
return nil, err
144-
}
145-
146-
// Once we have the input logical plan, we can create the primitives for the verification operators.
147-
// For all of these, we don't need the semTable anymore. We set it to nil, to avoid using an incorrect one.
148-
ctx.SemTable = nil
149-
150-
// Go over the children and convert them to Primitives too.
151-
var verify []*verifyLP
152-
for _, v := range fkv.Verify {
153-
lp, err := transformToLogicalPlan(ctx, v.Op)
154-
if err != nil {
155-
return nil, err
156-
}
157-
verify = append(verify, &verifyLP{
158-
verify: lp,
159-
typ: v.Typ,
160-
})
161-
}
162-
163-
return newFkVerify(inputLP, verify), nil
164-
}
165-
166165
func transformAggregator(ctx *plancontext.PlanningContext, op *operators.Aggregator) (logicalPlan, error) {
167166
plan, err := transformToLogicalPlan(ctx, op.Source)
168167
if err != nil {

go/vt/vtgate/planbuilder/operators/SQL_builder.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func (qb *queryBuilder) addTableExpr(
8888
}
8989

9090
func (qb *queryBuilder) addPredicate(expr sqlparser.Expr) {
91-
if _, toBeSkipped := qb.ctx.SkipPredicates[expr]; toBeSkipped {
91+
if qb.ctx.ShouldSkip(expr) {
9292
// This is a predicate that was added to the RHS of an ApplyJoin.
9393
// The original predicate will be added, so we don't have to add this here
9494
return
@@ -566,21 +566,16 @@ func buildProjection(op *Projection, qb *queryBuilder) error {
566566
func buildApplyJoin(op *ApplyJoin, qb *queryBuilder) error {
567567
predicates := slice.Map(op.JoinPredicates, func(jc JoinColumn) sqlparser.Expr {
568568
// since we are adding these join predicates, we need to mark to broken up version (RHSExpr) of it as done
569-
qb.ctx.SkipPredicates[jc.RHSExpr] = nil
570-
569+
_ = qb.ctx.SkipJoinPredicates(jc.Original.Expr)
571570
return jc.Original.Expr
572571
})
572+
573573
pred := sqlparser.AndExpressions(predicates...)
574574
err := buildQuery(op.LHS, qb)
575575
if err != nil {
576576
return err
577577
}
578-
// If we are going to add the predicate used in join here
579-
// We should not add the predicate's copy of when it was split into
580-
// two parts. To avoid this, we use the SkipPredicates map.
581-
for _, pred := range op.JoinPredicates {
582-
qb.ctx.SkipPredicates[pred.RHSExpr] = nil
583-
}
578+
584579
qbR := &queryBuilder{ctx: qb.ctx}
585580
err = buildQuery(op.RHS, qbR)
586581
if err != nil {

go/vt/vtgate/planbuilder/operators/ast_to_op.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -204,16 +204,23 @@ func createOperatorFromUnion(ctx *plancontext.PlanningContext, node *sqlparser.U
204204
// createOpFromStmt creates an operator from the given statement. It takes in two additional arguments—
205205
// 1. verifyAllFKs: For this given statement, do we need to verify validity of all the foreign keys on the vtgate level.
206206
// 2. fkToIgnore: The foreign key constraint to specifically ignore while planning the statement.
207-
func createOpFromStmt(ctx *plancontext.PlanningContext, stmt sqlparser.Statement, verifyAllFKs bool, fkToIgnore string) (ops.Operator, error) {
207+
func createOpFromStmt(ctx *plancontext.PlanningContext, stmt sqlparser.Statement, verifyAllFKs bool, fkToIgnore string) (*semantics.SemTable, ops.Operator, error) {
208208
newCtx, err := plancontext.CreatePlanningContext(stmt, ctx.ReservedVars, ctx.VSchema, ctx.PlannerVersion)
209209
if err != nil {
210-
return nil, err
210+
return nil, nil, err
211211
}
212212

213213
newCtx.VerifyAllFKs = verifyAllFKs
214214
newCtx.ParentFKToIgnore = fkToIgnore
215215

216-
return PlanQuery(newCtx, stmt)
216+
query, err := PlanQuery(newCtx, stmt)
217+
if err != nil {
218+
return nil, nil, err
219+
}
220+
221+
ctx.KeepPredicateInfo(newCtx)
222+
223+
return newCtx.SemTable, query, err
217224
}
218225

219226
func getOperatorFromTableExpr(ctx *plancontext.PlanningContext, tableExpr sqlparser.TableExpr, onlyTable bool) (ops.Operator, error) {
@@ -377,7 +384,7 @@ func createSelectionOp(
377384
where *sqlparser.Where,
378385
limit *sqlparser.Limit,
379386
lock sqlparser.Lock,
380-
) (ops.Operator, error) {
387+
) (*semantics.SemTable, ops.Operator, error) {
381388
selectionStmt := &sqlparser.Select{
382389
SelectExprs: selectExprs,
383390
From: tableExprs,

go/vt/vtgate/planbuilder/operators/delete.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ func createFkCascadeOpForDelete(ctx *plancontext.PlanningContext, parentOp ops.O
195195
}
196196
fkChildren = append(fkChildren, fkChild)
197197
}
198-
selectionOp, err := createSelectionOp(ctx, selectExprs, delStmt.TableExprs, delStmt.Where, nil, sqlparser.ForUpdateLock)
198+
st, selectionOp, err := createSelectionOp(ctx, selectExprs, delStmt.TableExprs, delStmt.Where, nil, sqlparser.ForUpdateLock)
199199
if err != nil {
200200
return nil, err
201201
}
@@ -204,6 +204,8 @@ func createFkCascadeOpForDelete(ctx *plancontext.PlanningContext, parentOp ops.O
204204
Selection: selectionOp,
205205
Children: fkChildren,
206206
Parent: parentOp,
207+
208+
SSemTable: st,
207209
}, nil
208210
}
209211

@@ -247,7 +249,7 @@ func createFkChildForDelete(ctx *plancontext.PlanningContext, fk vindexes.ChildF
247249
}
248250

249251
// For the child statement of a DELETE query, we don't need to verify all the FKs on VTgate or ignore any foreign key explicitly.
250-
childOp, err := createOpFromStmt(ctx, childStmt, false /* verifyAllFKs */, "" /* fkToIgnore */)
252+
cST, childOp, err := createOpFromStmt(ctx, childStmt, false /* verifyAllFKs */, "" /* fkToIgnore */)
251253
if err != nil {
252254
return nil, err
253255
}
@@ -256,5 +258,6 @@ func createFkChildForDelete(ctx *plancontext.PlanningContext, fk vindexes.ChildF
256258
BVName: bvName,
257259
Cols: cols,
258260
Op: childOp,
261+
ST: cST,
259262
}, nil
260263
}

go/vt/vtgate/planbuilder/operators/expressions.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,7 @@ func BreakExpressionInLHSandRHS(
5252
cursor.Replace(arg)
5353
}, nil).(sqlparser.Expr)
5454

55-
if err != nil {
56-
return JoinColumn{}, err
57-
}
58-
ctx.JoinPredicates[expr] = append(ctx.JoinPredicates[expr], rewrittenExpr)
55+
ctx.AddJoinPredicates(expr, rewrittenExpr)
5956
col.RHSExpr = rewrittenExpr
6057
return
6158
}

go/vt/vtgate/planbuilder/operators/fk_cascade.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ import (
2020
"slices"
2121

2222
"vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops"
23+
"vitess.io/vitess/go/vt/vtgate/semantics"
2324
)
2425

2526
// FkChild is used to represent a foreign key child table operation
2627
type FkChild struct {
2728
BVName string
2829
Cols []int // indexes
2930
Op ops.Operator
31+
ST *semantics.SemTable
3032

3133
noColumns
3234
noPredicates
@@ -37,6 +39,7 @@ type FkChild struct {
3739
// cascades (for example, ON DELETE CASCADE).
3840
type FkCascade struct {
3941
Selection ops.Operator
42+
SSemTable *semantics.SemTable
4043
Children []*FkChild
4144
Parent ops.Operator
4245

@@ -80,6 +83,7 @@ func (fkc *FkCascade) Clone(inputs []ops.Operator) ops.Operator {
8083
newFkc := &FkCascade{
8184
Parent: inputs[0],
8285
Selection: inputs[1],
86+
SSemTable: fkc.SSemTable,
8387
}
8488
for idx, operator := range inputs {
8589
if idx < 2 {
@@ -90,6 +94,7 @@ func (fkc *FkCascade) Clone(inputs []ops.Operator) ops.Operator {
9094
BVName: fkc.Children[idx-2].BVName,
9195
Cols: slices.Clone(fkc.Children[idx-2].Cols),
9296
Op: operator,
97+
ST: fkc.Children[idx-2].ST,
9398
})
9499
}
95100
return newFkc

go/vt/vtgate/planbuilder/operators/fk_verify.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@ package operators
1818

1919
import (
2020
"vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops"
21+
"vitess.io/vitess/go/vt/vtgate/semantics"
2122
)
2223

2324
// VerifyOp keeps the information about the foreign key verification operation.
2425
// It is a Parent verification or a Child verification.
2526
type VerifyOp struct {
26-
Op ops.Operator
27+
Op ops.Operator
28+
ST *semantics.SemTable
29+
2730
Typ string
2831
}
2932

go/vt/vtgate/planbuilder/operators/update.go

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -344,13 +344,14 @@ func createFKCascadeOp(ctx *plancontext.PlanningContext, parentOp ops.Operator,
344344
fkChildren = append(fkChildren, fkChild)
345345
}
346346

347-
selectionOp, err := createSelectionOp(ctx, selectExprs, updStmt.TableExprs, updStmt.Where, nil, sqlparser.ForUpdateLock)
347+
st, selectionOp, err := createSelectionOp(ctx, selectExprs, updStmt.TableExprs, updStmt.Where, nil, sqlparser.ForUpdateLock)
348348
if err != nil {
349349
return nil, err
350350
}
351351

352352
return &FkCascade{
353353
Selection: selectionOp,
354+
SSemTable: st,
354355
Children: fkChildren,
355356
Parent: parentOp,
356357
}, nil
@@ -370,13 +371,14 @@ func createFkChildForUpdate(ctx *plancontext.PlanningContext, fk vindexes.ChildF
370371
compExpr := sqlparser.NewComparisonExpr(sqlparser.InOp, valTuple, sqlparser.NewListArg(bvName), nil)
371372
var childWhereExpr sqlparser.Expr = compExpr
372373

374+
var st *semantics.SemTable
373375
var childOp ops.Operator
374376
var err error
375377
switch fk.OnUpdate {
376378
case sqlparser.Cascade:
377-
childOp, err = buildChildUpdOpForCascade(ctx, fk, updStmt, childWhereExpr, updatedTable)
379+
st, childOp, err = buildChildUpdOpForCascade(ctx, fk, updStmt, childWhereExpr, updatedTable)
378380
case sqlparser.SetNull:
379-
childOp, err = buildChildUpdOpForSetNull(ctx, fk, updStmt, childWhereExpr)
381+
st, childOp, err = buildChildUpdOpForSetNull(ctx, fk, updStmt, childWhereExpr)
380382
case sqlparser.SetDefault:
381383
return nil, vterrors.VT09016()
382384
}
@@ -388,14 +390,15 @@ func createFkChildForUpdate(ctx *plancontext.PlanningContext, fk vindexes.ChildF
388390
BVName: bvName,
389391
Cols: cols,
390392
Op: childOp,
393+
ST: st,
391394
}, nil
392395
}
393396

394397
// buildChildUpdOpForCascade builds the child update statement operator for the CASCADE type foreign key constraint.
395398
// The query looks like this -
396399
//
397400
// `UPDATE <child_table> SET <child_column_updated_using_update_exprs_from_parent_update_query> WHERE <child_columns_in_fk> IN (<bind variable for the output from SELECT>)`
398-
func buildChildUpdOpForCascade(ctx *plancontext.PlanningContext, fk vindexes.ChildFKInfo, updStmt *sqlparser.Update, childWhereExpr sqlparser.Expr, updatedTable *vindexes.Table) (ops.Operator, error) {
401+
func buildChildUpdOpForCascade(ctx *plancontext.PlanningContext, fk vindexes.ChildFKInfo, updStmt *sqlparser.Update, childWhereExpr sqlparser.Expr, updatedTable *vindexes.Table) (*semantics.SemTable, ops.Operator, error) {
399402
// The update expressions are the same as the update expressions in the parent update query
400403
// with the column names replaced with the child column names.
401404
var childUpdateExprs sqlparser.UpdateExprs
@@ -436,7 +439,7 @@ func buildChildUpdOpForCascade(ctx *plancontext.PlanningContext, fk vindexes.Chi
436439
// `UPDATE <child_table> SET <child_column_updated_using_update_exprs_from_parent_update_query>
437440
// WHERE <child_columns_in_fk> IN (<bind variable for the output from SELECT>)
438441
// [AND ({<bind variables in the SET clause of the original update> IS NULL OR}... <child_columns_in_fk> NOT IN (<bind variables in the SET clause of the original update>))]`
439-
func buildChildUpdOpForSetNull(ctx *plancontext.PlanningContext, fk vindexes.ChildFKInfo, updStmt *sqlparser.Update, childWhereExpr sqlparser.Expr) (ops.Operator, error) {
442+
func buildChildUpdOpForSetNull(ctx *plancontext.PlanningContext, fk vindexes.ChildFKInfo, updStmt *sqlparser.Update, childWhereExpr sqlparser.Expr) (*semantics.SemTable, ops.Operator, error) {
440443
// For the SET NULL type constraint, we need to set all the child columns to NULL.
441444
var childUpdateExprs sqlparser.UpdateExprs
442445
for _, column := range fk.ChildColumns {
@@ -479,23 +482,25 @@ func createFKVerifyOp(ctx *plancontext.PlanningContext, childOp ops.Operator, up
479482
var Verify []*VerifyOp
480483
// This validates that new values exists on the parent table.
481484
for _, fk := range parentFks {
482-
op, err := createFkVerifyOpForParentFKForUpdate(ctx, updStmt, fk)
485+
st, op, err := createFkVerifyOpForParentFKForUpdate(ctx, updStmt, fk)
483486
if err != nil {
484487
return nil, err
485488
}
486489
Verify = append(Verify, &VerifyOp{
487490
Op: op,
491+
ST: st,
488492
Typ: engine.ParentVerify,
489493
})
490494
}
491495
// This validates that the old values don't exist on the child table.
492496
for _, fk := range restrictChildFks {
493-
op, err := createFkVerifyOpForChildFKForUpdate(ctx, updStmt, fk)
497+
st, op, err := createFkVerifyOpForChildFKForUpdate(ctx, updStmt, fk)
494498
if err != nil {
495499
return nil, err
496500
}
497501
Verify = append(Verify, &VerifyOp{
498502
Op: op,
503+
ST: st,
499504
Typ: engine.ChildVerify,
500505
})
501506
}
@@ -517,11 +522,11 @@ func createFKVerifyOp(ctx *plancontext.PlanningContext, childOp ops.Operator, up
517522
// where Parent.p1 is null and Parent.p2 is null and Child.id = 1
518523
// and Child.c2 is not null
519524
// limit 1
520-
func createFkVerifyOpForParentFKForUpdate(ctx *plancontext.PlanningContext, updStmt *sqlparser.Update, pFK vindexes.ParentFKInfo) (ops.Operator, error) {
525+
func createFkVerifyOpForParentFKForUpdate(ctx *plancontext.PlanningContext, updStmt *sqlparser.Update, pFK vindexes.ParentFKInfo) (*semantics.SemTable, ops.Operator, error) {
521526
childTblExpr := updStmt.TableExprs[0].(*sqlparser.AliasedTableExpr)
522527
childTbl, err := childTblExpr.TableName()
523528
if err != nil {
524-
return nil, err
529+
return nil, nil, err
525530
}
526531
parentTbl := pFK.Table.GetTableName()
527532
var whereCond sqlparser.Expr
@@ -594,16 +599,16 @@ func createFkVerifyOpForParentFKForUpdate(ctx *plancontext.PlanningContext, updS
594599
// verify query:
595600
// select 1 from Child join Parent on Parent.p1 = Child.c1 and Parent.p2 = Child.c2
596601
// where Parent.id = 1 and (1 IS NULL OR (child.c1) NOT IN ((1))) limit 1
597-
func createFkVerifyOpForChildFKForUpdate(ctx *plancontext.PlanningContext, updStmt *sqlparser.Update, cFk vindexes.ChildFKInfo) (ops.Operator, error) {
602+
func createFkVerifyOpForChildFKForUpdate(ctx *plancontext.PlanningContext, updStmt *sqlparser.Update, cFk vindexes.ChildFKInfo) (*semantics.SemTable, ops.Operator, error) {
598603
// ON UPDATE RESTRICT foreign keys that require validation, should only be allowed in the case where we
599604
// are verifying all the FKs on vtgate level.
600605
if !ctx.VerifyAllFKs {
601-
return nil, vterrors.VT12002()
606+
return nil, nil, vterrors.VT12002()
602607
}
603608
parentTblExpr := updStmt.TableExprs[0].(*sqlparser.AliasedTableExpr)
604609
parentTbl, err := parentTblExpr.TableName()
605610
if err != nil {
606-
return nil, err
611+
return nil, nil, err
607612
}
608613
childTbl := cFk.Table.GetTableName()
609614
var joinCond sqlparser.Expr

0 commit comments

Comments
 (0)