Repository: vitessio/vitess
Pull request #8564 (2021-07-29 09:03:38)

Adds support for ordering by NULL in gen4
## Description
Adds support for ordering by NULL in gen4

## Related Issue(s)
<!-- List related issues and pull requests. If this PR fixes an issue, please add it using Fixes #????  -->

#7280 

## Checklist
- [ ] Should this PR be backported?
- [x] Tests were added or are not required
- [x] Documentation was added or is not required

## Deployment Notes
<!-- Notes regarding deployment of the contained body of work. These should note any db migrations, etc. -->


Patch:
diff --git a/go/vt/vtgate/engine/join.go b/go/vt/vtgate/engine/join.go
index cbdb8d5815..a909dddef3 100644
--- a/go/vt/vtgate/engine/join.go
+++ b/go/vt/vtgate/engine/join.go
@@ -262,6 +262,9 @@ func (jn *Join) description() PrimitiveDescription {
 		"TableName":         jn.GetTableName(),
 		"JoinColumnIndexes": strings.Trim(strings.Join(strings.Fields(fmt.Sprint(jn.Cols)), ","), "[]"),
 	}
+	if len(jn.Vars) > 0 {
+		other["JoinVars"] = jn.Vars
+	}
 	return PrimitiveDescription{
 		OperatorType: "Join",
 		Variant:      jn.Opcode.String(),
diff --git a/go/vt/vtgate/planbuilder/abstract/queryprojection.go b/go/vt/vtgate/planbuilder/abstract/queryprojection.go
index 9311645a12..1e6d7d052f 100644
--- a/go/vt/vtgate/planbuilder/abstract/queryprojection.go
+++ b/go/vt/vtgate/planbuilder/abstract/queryprojection.go
@@ -164,7 +164,8 @@ func (qp *QueryProjection) getNonAggrExprNotMatchingGroupByExprs() sqlparser.Exp
 		}
 	}
 	for _, order := range qp.OrderExprs {
-		if sqlparser.IsAggregation(order.WeightStrExpr) {
+		// ORDER BY NULL or Aggregation functions need not be present in group by
+		if sqlparser.IsNull(order.Inner.Expr) || sqlparser.IsAggregation(order.WeightStrExpr) {
 			continue
 		}
 		isGroupByOk := false
@@ -217,6 +218,10 @@ func (qp *QueryProjection) getSimplifiedExpr(e sqlparser.Expr, caller string) (e
 		}
 	}
 
+	if sqlparser.IsNull(e) {
+		return e, nil, nil
+	}
+
 	return e, e, nil
 }
 
diff --git a/go/vt/vtgate/planbuilder/horizon_planning.go b/go/vt/vtgate/planbuilder/horizon_planning.go
index d8dcd92e28..d60eda5566 100644
--- a/go/vt/vtgate/planbuilder/horizon_planning.go
+++ b/go/vt/vtgate/planbuilder/horizon_planning.go
@@ -347,6 +347,16 @@ func (hp *horizonPlanning) planOrderBy(orderExprs []abstract.OrderBy, plan logic
 
 		return newPlan, nil
 	case *orderedAggregate:
+		// remove ORDER BY NULL from the list of order by expressions since we will be doing the ordering on vtgate level so NULL is not useful
+		var orderExprsWithoutNils []abstract.OrderBy
+		for _, expr := range orderExprs {
+			if sqlparser.IsNull(expr.Inner.Expr) {
+				continue
+			}
+			orderExprsWithoutNils = append(orderExprsWithoutNils, expr)
+		}
+		orderExprs = orderExprsWithoutNils
+
 		for _, order := range orderExprs {
 			if sqlparser.ContainsAggregation(order.WeightStrExpr) {
 				ms, err := createMemorySortPlanOnAggregation(plan, orderExprs)
@@ -372,6 +382,10 @@ func (hp *horizonPlanning) planOrderBy(orderExprs []abstract.OrderBy, plan logic
 func planOrderByForRoute(orderExprs []abstract.OrderBy, plan *route, semTable *semantics.SemTable) (logicalPlan, bool, error) {
 	origColCount := plan.Select.GetColumnCount()
 	for _, order := range orderExprs {
+		plan.Select.AddOrder(order.Inner)
+		if sqlparser.IsNull(order.Inner.Expr) {
+			continue
+		}
 		offset, weightStringOffset, _, err := wrapAndPushExpr(order.Inner.Expr, order.WeightStrExpr, plan, semTable)
 		if err != nil {
 			return nil, false, err
@@ -382,7 +396,6 @@ func planOrderByForRoute(orderExprs []abstract.OrderBy, plan *route, semTable *s
 			WeightStringCol: weightStringOffset,
 			Desc:            order.Inner.Direction == sqlparser.DescOrder,
 		})
-		plan.Select.AddOrder(order.Inner)
 	}
 	return plan, origColCount != plan.Select.GetColumnCount(), nil
 }
diff --git a/go/vt/vtgate/planbuilder/jointree.go b/go/vt/vtgate/planbuilder/jointree.go
index ca58d507ec..8593d5cf5d 100644
--- a/go/vt/vtgate/planbuilder/jointree.go
+++ b/go/vt/vtgate/planbuilder/jointree.go
@@ -726,15 +726,11 @@ func (jp *joinTree) clone() queryTree {
 		lhs:   jp.lhs.clone(),
 		rhs:   jp.rhs.clone(),
 		outer: jp.outer,
+		vars:  jp.vars,
 	}
 	return result
 }
 
-/*
-
-select id, t2.b from t1 , (select b from t2) t2 where t.id = 1
-*/
-
 func (jp *joinTree) pushOutputColumns(columns []*sqlparser.ColName, semTable *semantics.SemTable) ([]int, error) {
 	var toTheLeft []bool
 	var lhs, rhs []*sqlparser.ColName
diff --git a/go/vt/vtgate/planbuilder/route_planning.go b/go/vt/vtgate/planbuilder/route_planning.go
index fb40d1064c..e6945b6047 100644
--- a/go/vt/vtgate/planbuilder/route_planning.go
+++ b/go/vt/vtgate/planbuilder/route_planning.go
@@ -382,16 +382,27 @@ func pushJoinPredicate(exprs []sqlparser.Expr, tree queryTree, semTable *semanti
 		// we break up the predicates so that colnames from the LHS are replaced by arguments
 		var rhsPreds []sqlparser.Expr
 		var lhsColumns []*sqlparser.ColName
+		var lhsVarsName []string
 		lhsSolves := node.lhs.tableID()
 		for _, expr := range exprs {
-			cols, predicate, err := breakPredicateInLHSandRHS(expr, semTable, lhsSolves)
+			bvName, cols, predicate, err := breakPredicateInLHSandRHS(expr, semTable, lhsSolves)
 			if err != nil {
 				return nil, err
 			}
 			lhsColumns = append(lhsColumns, cols...)
+			lhsVarsName = append(lhsVarsName, bvName...)
 			rhsPreds = append(rhsPreds, predicate)
 		}
-		node.pushOutputColumns(lhsColumns, semTable)
+		if lhsColumns != nil && lhsVarsName != nil {
+			idxs, err := node.pushOutputColumns(lhsColumns, semTable)
+			if err != nil {
+				return nil, err
+			}
+			for i, idx := range idxs {
+				node.vars[lhsVarsName[i]] = idx
+			}
+		}
+
 		rhsPlan, err := pushJoinPredicate(rhsPreds, node.rhs, semTable)
 		if err != nil {
 			return nil, err
@@ -401,6 +412,7 @@ func pushJoinPredicate(exprs []sqlparser.Expr, tree queryTree, semTable *semanti
 			lhs:   node.lhs,
 			rhs:   rhsPlan,
 			outer: node.outer,
+			vars:  node.vars,
 		}, nil
 	case *derivedTree:
 		plan := node.clone().(*derivedTree)
@@ -430,7 +442,7 @@ func pushJoinPredicate(exprs []sqlparser.Expr, tree queryTree, semTable *semanti
 	}
 }
 
-func breakPredicateInLHSandRHS(expr sqlparser.Expr, semTable *semantics.SemTable, lhs semantics.TableSet) (columns []*sqlparser.ColName, predicate sqlparser.Expr, err error) {
+func breakPredicateInLHSandRHS(expr sqlparser.Expr, semTable *semantics.SemTable, lhs semantics.TableSet) (bvNames []string, columns []*sqlparser.ColName, predicate sqlparser.Expr, err error) {
 	predicate = sqlparser.CloneExpr(expr)
 	_ = sqlparser.Rewrite(predicate, nil, func(cursor *sqlparser.Cursor) bool {
 		switch node := cursor.Node().(type) {
@@ -443,14 +455,16 @@ func breakPredicateInLHSandRHS(expr sqlparser.Expr, semTable *semantics.SemTable
 			if deps.IsSolvedBy(lhs) {
 				node.Qualifier.Qualifier = sqlparser.NewTableIdent("")
 				columns = append(columns, node)
-				arg := sqlparser.NewArgument(node.CompliantName())
+				bvName := node.CompliantName()
+				bvNames = append(bvNames, bvName)
+				arg := sqlparser.NewArgument(bvName)
 				cursor.Replace(arg)
 			}
 		}
 		return true
 	})
 	if err != nil {
-		return nil, nil, err
+		return nil, nil, nil, err
 	}
 	return
 }
@@ -465,7 +479,7 @@ func mergeOrJoin(lhs, rhs queryTree, joinPredicates []sqlparser.Expr, semTable *
 		return newPlan, nil
 	}
 
-	tree := &joinTree{lhs: lhs.clone(), rhs: rhs.clone(), outer: !inner}
+	tree := &joinTree{lhs: lhs.clone(), rhs: rhs.clone(), outer: !inner, vars: map[string]int{}}
 	return pushJoinPredicate(joinPredicates, tree, semTable)
 }
 


Test patch:
diff --git a/go/test/endtoend/vtgate/gen4/gen4_test.go b/go/test/endtoend/vtgate/gen4/gen4_test.go
index f795f8debd..3a7cbc20c6 100644
--- a/go/test/endtoend/vtgate/gen4/gen4_test.go
+++ b/go/test/endtoend/vtgate/gen4/gen4_test.go
@@ -86,6 +86,23 @@ func TestGroupBy(t *testing.T) {
 			`[INT64(2) VARCHAR("B") VARCHAR("C") VARCHAR("abc")]]`)
 }
 
+func TestJoinBindVars(t *testing.T) {
+	ctx := context.Background()
+	conn, err := mysql.Connect(ctx, &vtParams)
+	require.NoError(t, err)
+	defer conn.Close()
+
+	defer func() {
+		_, _ = exec(t, conn, `delete from t2`)
+		_, _ = exec(t, conn, `delete from t3`)
+	}()
+
+	checkedExec(t, conn, `insert into t2(id, tcol1, tcol2) values (1, 'A', 'A'),(2, 'B', 'C'),(3, 'A', 'C'),(4, 'C', 'A'),(5, 'A', 'A'),(6, 'B', 'C'),(7, 'B', 'A'),(8, 'C', 'B')`)
+	checkedExec(t, conn, `insert into t3(id, tcol1, tcol2) values (1, 'A', 'A'),(2, 'B', 'C'),(3, 'A', 'C'),(4, 'C', 'A'),(5, 'A', 'A'),(6, 'B', 'C'),(7, 'B', 'A'),(8, 'C', 'B')`)
+
+	assertMatches(t, conn, `select t2.tcol1 from t2 join t3 on t2.tcol2 = t3.tcol2 where t2.tcol1 = 'A'`, `[[VARCHAR("A")] [VARCHAR("A")] [VARCHAR("A")] [VARCHAR("A")] [VARCHAR("A")] [VARCHAR("A")] [VARCHAR("A")] [VARCHAR("A")] [VARCHAR("A")] [VARCHAR("A")] [VARCHAR("A")]]`)
+}
+
 func TestDistinctAggregationFunc(t *testing.T) {
 	ctx := context.Background()
 	conn, err := mysql.Connect(ctx, &vtParams)
diff --git a/go/test/endtoend/vtgate/gen4/main_test.go b/go/test/endtoend/vtgate/gen4/main_test.go
index 4969d5d8e5..01b46f8af4 100644
--- a/go/test/endtoend/vtgate/gen4/main_test.go
+++ b/go/test/endtoend/vtgate/gen4/main_test.go
@@ -42,6 +42,13 @@ create table t2(
 	tcol2 varchar(50),
 	primary key(id)
 ) Engine=InnoDB;
+
+create table t3(
+	id bigint,
+	tcol1 varchar(50),
+	tcol2 varchar(50),
+	primary key(id)
+) Engine=InnoDB;
 `
 
 	VSchema = `
@@ -74,6 +81,20 @@ create table t2(
           "type": "VARCHAR"
         }
       ]
+    },
+    "t3": {
+      "column_vindexes": [
+        {
+          "column": "id",
+          "name": "xxhash"
+        }
+      ],
+      "columns": [
+        {
+          "name": "tcol1",
+          "type": "VARCHAR"
+        }
+      ]
     }
   }
 }`
diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go
index e02c8870b5..77b7825217 100644
--- a/go/vt/vtgate/planbuilder/plan_test.go
+++ b/go/vt/vtgate/planbuilder/plan_test.go
@@ -534,7 +534,12 @@ func testFile(t *testing.T, filename, tempDir string, vschema *vschemaWrapper, c
 					if outFirstPlanner == out {
 						expected.WriteString(samePlanMarker)
 					} else {
-						expected.WriteString(fmt.Sprintf("%s\n", out))
+						if err != nil {
+							out = out[1 : len(out)-1] // remove the double quotes
+							expected.WriteString(fmt.Sprintf("Gen4 error: %s\n", out))
+						} else {
+							expected.WriteString(fmt.Sprintf("%s\n", out))
+						}
 					}
 				})
 			} else {
diff --git a/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt b/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt
index 5b00054dbc..34de59996c 100644
--- a/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt
@@ -1495,12 +1495,12 @@ Gen4 plan same as above
     ]
   }
 }
+Gen4 plan same as above
 
 # scatter aggregate with complex select list (can't build order by)
 "select distinct a+1 from user"
 "generating order by clause: cannot reference a complex expression"
 
-
 # scatter aggregate with numbered order by columns
 "select a, b, c, d, count(*) from user group by 1, 2, 3 order by 1, 2, 3"
 {
@@ -2309,3 +2309,93 @@ Gen4 plan same as above
     ]
   }
 }
+
+# aggregate query with order by aggregate column along with NULL
+"select col, count(*) k from user group by col order by null, k"
+"unsupported: in scatter query: complex order by expression: null"
+{
+  "QueryType": "SELECT",
+  "Original": "select col, count(*) k from user group by col order by null, k",
+  "Instructions": {
+    "OperatorType": "Sort",
+    "Variant": "Memory",
+    "OrderBy": "1 ASC",
+    "ResultColumns": 2,
+    "Inputs": [
+      {
+        "OperatorType": "Aggregate",
+        "Variant": "Ordered",
+        "Aggregates": "count(1)",
+        "GroupBy": "(0|2)",
+        "Inputs": [
+          {
+            "OperatorType": "Route",
+            "Variant": "SelectScatter",
+            "Keyspace": {
+              "Name": "user",
+              "Sharded": true
+            },
+            "FieldQuery": "select col, count(*) as k, weight_string(col) from `user` where 1 != 1 group by col",
+            "OrderBy": "(0|2) ASC",
+            "Query": "select col, count(*) as k, weight_string(col) from `user` group by col order by col asc",
+            "Table": "`user`"
+          }
+        ]
+      }
+    ]
+  }
+}
+
+# aggregate query with order by NULL
+"select col, count(*) k from user group by col order by null"
+{
+  "QueryType": "SELECT",
+  "Original": "select col, count(*) k from user group by col order by null",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "count(1)",
+    "GroupBy": "0",
+    "Inputs": [
+      {
+        "OperatorType": "Route",
+        "Variant": "SelectScatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select col, count(*) as k, weight_string(col) from `user` where 1 != 1 group by col",
+        "OrderBy": "(0|2) ASC",
+        "Query": "select col, count(*) as k, weight_string(col) from `user` group by col order by col asc",
+        "ResultColumns": 2,
+        "Table": "`user`"
+      }
+    ]
+  }
+}
+{
+  "QueryType": "SELECT",
+  "Original": "select col, count(*) k from user group by col order by null",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "count(1)",
+    "GroupBy": "(0|2)",
+    "ResultColumns": 2,
+    "Inputs": [
+      {
+        "OperatorType": "Route",
+        "Variant": "SelectScatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select col, count(*) as k, weight_string(col) from `user` where 1 != 1 group by col",
+        "OrderBy": "(0|2) ASC",
+        "Query": "select col, count(*) as k, weight_string(col) from `user` group by col order by col asc",
+        "Table": "`user`"
+      }
+    ]
+  }
+}
+
diff --git a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt
index 7ad39a0391..da63bd2338 100644
--- a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt
@@ -679,6 +679,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1",
+    "JoinVars": {
+      "user_col": 0
+    },
     "TableName": "`user`_user_extra",
     "Inputs": [
       {
@@ -721,6 +724,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1",
+    "JoinVars": {
+      "user_col": 0
+    },
     "TableName": "`user`_user_extra",
     "Inputs": [
       {
@@ -763,6 +769,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1",
+    "JoinVars": {
+      "user_col": 0
+    },
     "TableName": "`user`_user_extra",
     "Inputs": [
       {
@@ -808,6 +817,50 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1",
+    "JoinVars": {
+      "user_col": 0
+    },
+    "TableName": "`user`_user_extra",
+    "Inputs": [
+      {
+        "OperatorType": "Route",
+        "Variant": "SelectScatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select `user`.col from `user` where 1 != 1",
+        "Query": "select `user`.col from `user`",
+        "Table": "`user`"
+      },
+      {
+        "OperatorType": "Route",
+        "Variant": "SelectEqualUnique",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select user_extra.id from user_extra where 1 != 1",
+        "Query": "select user_extra.id from user_extra where user_extra.col = :user_col and user_extra.user_id = :user_col",
+        "Table": "user_extra",
+        "Values": [
+          ":user_col"
+        ],
+        "Vindex": "user_index"
+      }
+    ]
+  }
+}
+{
+  "QueryType": "SELECT",
+  "Original": "select user_extra.id from user join user_extra on user.col = user_extra.col where user_extra.user_id = user.col",
+  "Instructions": {
+    "OperatorType": "Join",
+    "Variant": "Join",
+    "JoinColumnIndexes": "1",
+    "JoinVars": {
+      "user_col": 1
+    },
     "TableName": "`user`_user_extra",
     "Inputs": [
       {
@@ -839,7 +892,6 @@ Gen4 plan same as above
     ]
   }
 }
-Gen4 plan same as above
 
 # Multi-route with non-route constraint, should use first route.
 "select user_extra.id from user join user_extra on user.col = user_extra.col where 1 = 1"
@@ -850,6 +902,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1",
+    "JoinVars": {
+      "user_col": 0
+    },
     "TableName": "`user`_user_extra",
     "Inputs": [
       {
@@ -884,6 +939,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1",
+    "JoinVars": {
+      "user_col": 0
+    },
     "TableName": "`user`_user_extra",
     "Inputs": [
       {
@@ -1083,6 +1141,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1",
+    "JoinVars": {
+      "user_id": 0
+    },
     "TableName": "`user`_unsharded",
     "Inputs": [
       {
@@ -1117,6 +1178,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "unsharded_id": 0
+    },
     "TableName": "unsharded_`user`",
     "Inputs": [
       {
@@ -1181,6 +1245,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1",
+    "JoinVars": {
+      "user_extra_col": 0
+    },
     "TableName": "user_extra_`user`",
     "Inputs": [
       {
@@ -1266,6 +1333,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1",
+    "JoinVars": {
+      "user_extra_col": 0
+    },
     "TableName": "user_extra_`user`",
     "Inputs": [
       {
diff --git a/go/vt/vtgate/planbuilder/testdata/from_cases.txt b/go/vt/vtgate/planbuilder/testdata/from_cases.txt
index 21ea09210f..1953d14ffe 100644
--- a/go/vt/vtgate/planbuilder/testdata/from_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/from_cases.txt
@@ -349,6 +349,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "LeftJoin",
     "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "u_a": 1
+    },
     "TableName": "`user`_unsharded",
     "Inputs": [
       {
@@ -383,6 +386,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "LeftJoin",
     "JoinColumnIndexes": "-2",
+    "JoinVars": {
+      "u_a": 0
+    },
     "TableName": "`user`_unsharded",
     "Inputs": [
       {
@@ -420,12 +426,18 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "LeftJoin",
     "JoinColumnIndexes": "-1,1",
+    "JoinVars": {
+      "m1_col": 1
+    },
     "TableName": "`user`_unsharded_unsharded",
     "Inputs": [
       {
         "OperatorType": "Join",
         "Variant": "LeftJoin",
         "JoinColumnIndexes": "-1,1",
+        "JoinVars": {
+          "user_col": 0
+        },
         "TableName": "`user`_unsharded",
         "Inputs": [
           {
@@ -473,12 +485,18 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "LeftJoin",
     "JoinColumnIndexes": "-2,1",
+    "JoinVars": {
+      "m1_col": 0
+    },
     "TableName": "`user`_unsharded_unsharded",
     "Inputs": [
       {
         "OperatorType": "Join",
         "Variant": "LeftJoin",
         "JoinColumnIndexes": "1,-1",
+        "JoinVars": {
+          "user_col": 0
+        },
         "TableName": "`user`_unsharded",
         "Inputs": [
           {
@@ -529,6 +547,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "LeftJoin",
     "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "user_col": 0
+    },
     "TableName": "`user`_user_extra_unsharded",
     "Inputs": [
       {
@@ -545,6 +566,9 @@ Gen4 plan same as above
       {
         "OperatorType": "Join",
         "Variant": "LeftJoin",
+        "JoinVars": {
+          "e_col": 0
+        },
         "TableName": "user_extra_unsharded",
         "Inputs": [
           {
@@ -581,6 +605,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "LeftJoin",
     "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "user_col": 0
+    },
     "TableName": "`user`_user_extra_unsharded",
     "Inputs": [
       {
@@ -597,6 +624,9 @@ Gen4 plan same as above
       {
         "OperatorType": "Join",
         "Variant": "LeftJoin",
+        "JoinVars": {
+          "e_col": 0
+        },
         "TableName": "user_extra_unsharded",
         "Inputs": [
           {
@@ -1167,6 +1197,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "user_id": 1
+    },
     "TableName": "`user`_user_extra",
     "Inputs": [
       {
@@ -1288,6 +1321,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "user_id": 1
+    },
     "TableName": "`user`_user_extra",
     "Inputs": [
       {
@@ -1322,6 +1358,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1",
+    "JoinVars": {
+      "user_extra_col": 0
+    },
     "TableName": "user_extra_`user`",
     "Inputs": [
       {
@@ -1363,6 +1402,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1",
+    "JoinVars": {
+      "user_extra_user_id": 0
+    },
     "TableName": "user_extra_`user`",
     "Inputs": [
       {
@@ -1401,6 +1443,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-2",
+    "JoinVars": {
+      "user_name": 0
+    },
     "TableName": "`user`_user_extra",
     "Inputs": [
       {
@@ -1734,6 +1779,9 @@ Gen4 error: Duplicate column name 'id'
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "t_id": 0
+    },
     "TableName": "`user`_user_extra",
     "Inputs": [
       {
@@ -2097,6 +2145,10 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "t_col1": 0,
+      "t_id": 1
+    },
     "TableName": "`user`_user_extra_unsharded",
     "Inputs": [
       {
@@ -2168,6 +2220,9 @@ Gen4 plan same as above
         "OperatorType": "Join",
         "Variant": "Join",
         "JoinColumnIndexes": "-1,-2",
+        "JoinVars": {
+          "user_col": 2
+        },
         "TableName": "`user`_user_extra",
         "Inputs": [
           {
@@ -2325,6 +2380,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "2",
+    "JoinVars": {
+      "ua_id": 0
+    },
     "TableName": "unsharded_a_`user`_user_extra",
     "Inputs": [
       {
@@ -2342,6 +2400,9 @@ Gen4 plan same as above
         "OperatorType": "Join",
         "Variant": "Join",
         "JoinColumnIndexes": "-1,-2",
+        "JoinVars": {
+          "user_id": 0
+        },
         "TableName": "`user`_user_extra",
         "Inputs": [
           {
@@ -2669,6 +2730,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1,1",
+    "JoinVars": {
+      "user_col2": 1
+    },
     "TableName": "`user`_unsharded",
     "Inputs": [
       {
@@ -2703,6 +2767,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-2,1",
+    "JoinVars": {
+      "user_col2": 0
+    },
     "TableName": "`user`_unsharded",
     "Inputs": [
       {
@@ -2931,6 +2998,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "user_id": 0
+    },
     "TableName": "`user`_user_extra",
     "Inputs": [
       {
@@ -2965,6 +3035,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1",
+    "JoinVars": {
+      "user_extra_id": 0
+    },
     "TableName": "user_extra_`user`",
     "Inputs": [
       {
@@ -2997,6 +3070,95 @@ Gen4 plan same as above
   }
 }
 
+# join with bindvariables
+"SELECT `user`.`id` FROM `user` INNER JOIN `user_extra` ON `user`.`id` = `user_extra`.`assembly_id` WHERE `user_extra`.`user_id` = 2"
+{
+  "QueryType": "SELECT",
+  "Original": "SELECT `user`.`id` FROM `user` INNER JOIN `user_extra` ON `user`.`id` = `user_extra`.`assembly_id` WHERE `user_extra`.`user_id` = 2",
+  "Instructions": {
+    "OperatorType": "Join",
+    "Variant": "Join",
+    "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "user_id": 0
+    },
+    "TableName": "`user`_user_extra",
+    "Inputs": [
+      {
+        "OperatorType": "Route",
+        "Variant": "SelectScatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select `user`.id from `user` where 1 != 1",
+        "Query": "select `user`.id from `user`",
+        "Table": "`user`"
+      },
+      {
+        "OperatorType": "Route",
+        "Variant": "SelectEqualUnique",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select 1 from user_extra where 1 != 1",
+        "Query": "select 1 from user_extra where user_extra.assembly_id = :user_id and user_extra.user_id = 2",
+        "Table": "user_extra",
+        "Values": [
+          2
+        ],
+        "Vindex": "user_index"
+      }
+    ]
+  }
+}
+{
+  "QueryType": "SELECT",
+  "Original": "SELECT `user`.`id` FROM `user` INNER JOIN `user_extra` ON `user`.`id` = `user_extra`.`assembly_id` WHERE `user_extra`.`user_id` = 2",
+  "Instructions": {
+    "OperatorType": "Join",
+    "Variant": "Join",
+    "JoinColumnIndexes": "1",
+    "JoinVars": {
+      "user_extra_assembly_id": 0
+    },
+    "TableName": "user_extra_`user`",
+    "Inputs": [
+      {
+        "OperatorType": "Route",
+        "Variant": "SelectEqualUnique",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select user_extra.assembly_id from user_extra where 1 != 1",
+        "Query": "select user_extra.assembly_id from user_extra where user_extra.user_id = 2",
+        "Table": "user_extra",
+        "Values": [
+          2
+        ],
+        "Vindex": "user_index"
+      },
+      {
+        "OperatorType": "Route",
+        "Variant": "SelectEqualUnique",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select `user`.id from `user` where 1 != 1",
+        "Query": "select `user`.id from `user` where `user`.id = :user_extra_assembly_id",
+        "Table": "`user`",
+        "Values": [
+          ":user_extra_assembly_id"
+        ],
+        "Vindex": "user_index"
+      }
+    ]
+  }
+}
+
 # verify ',' vs JOIN precedence
 "select u1.a from unsharded u1, unsharded u2 join unsharded u3 on u1.a = u2.a"
 "symbol u1.a not found"
diff --git a/go/vt/vtgate/planbuilder/testdata/large_cases.txt b/go/vt/vtgate/planbuilder/testdata/large_cases.txt
index 6db381e46c..1a3974a70a 100644
--- a/go/vt/vtgate/planbuilder/testdata/large_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/large_cases.txt
@@ -6,6 +6,9 @@
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "user_id": 0
+    },
     "TableName": "`user`_user_extra_user_metadata_music_unsharded_unsharded_a_unsharded_b_unsharded_auto_music_extra",
     "Inputs": [
       {
@@ -22,6 +25,9 @@
       {
         "OperatorType": "Join",
         "Variant": "Join",
+        "JoinVars": {
+          "user_extra_user_id": 0
+        },
         "TableName": "user_extra_user_metadata_music_unsharded_unsharded_a_unsharded_b_unsharded_auto_music_extra",
         "Inputs": [
           {
@@ -62,6 +68,9 @@
               {
                 "OperatorType": "Join",
                 "Variant": "Join",
+                "JoinVars": {
+                  "music_id": 0
+                },
                 "TableName": "music_unsharded_unsharded_a_unsharded_b_unsharded_auto_music_extra",
                 "Inputs": [
                   {
@@ -78,6 +87,9 @@
                   {
                     "OperatorType": "Join",
                     "Variant": "Join",
+                    "JoinVars": {
+                      "unsharded_x": 0
+                    },
                     "TableName": "unsharded_unsharded_a_unsharded_b_unsharded_auto_music_extra",
                     "Inputs": [
                       {
diff --git a/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.txt b/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.txt
index e11eba36c5..068829ac7b 100644
--- a/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.txt
@@ -329,6 +329,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
         "OperatorType": "Join",
         "Variant": "Join",
         "JoinColumnIndexes": "-1,-2,1,2",
+        "JoinVars": {
+          "user_id": 2
+        },
         "TableName": "`user`_music",
         "Inputs": [
           {
@@ -379,6 +382,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
         "OperatorType": "Join",
         "Variant": "Join",
         "JoinColumnIndexes": "-2,-3,1,2",
+        "JoinVars": {
+          "user_id": 0
+        },
         "TableName": "`user`_music",
         "Inputs": [
           {
@@ -432,6 +438,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
         "OperatorType": "Join",
         "Variant": "Join",
         "JoinColumnIndexes": "-1,-2,1,-3,2,-4",
+        "JoinVars": {
+          "user_id": 4
+        },
         "TableName": "`user`_music",
         "Inputs": [
           {
@@ -482,6 +491,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
         "OperatorType": "Join",
         "Variant": "Join",
         "JoinColumnIndexes": "-2,-3,1,-4,2,-5",
+        "JoinVars": {
+          "user_id": 0
+        },
         "TableName": "`user`_music",
         "Inputs": [
           {
@@ -673,6 +685,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "u_a": 0
+    },
     "TableName": "`user`_music",
     "Inputs": [
       {
diff --git a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt
index c9f459ad8a..6d7e40f2e6 100644
--- a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt
@@ -492,6 +492,7 @@ Gen4 plan same as above
     "Table": "`user`"
   }
 }
+Gen4 plan same as above
 
 # ORDER BY after pull-out subquery
 "select col from user where col in (select col2 from user) order by col"
@@ -539,6 +540,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1,-2,1",
+    "JoinVars": {
+      "user_id": 2
+    },
     "TableName": "`user`_music",
     "Inputs": [
       {
@@ -584,6 +588,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1,-2,1",
+    "JoinVars": {
+      "user_id": 2
+    },
     "TableName": "`user`_music",
     "Inputs": [
       {
@@ -626,6 +633,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-2,-3,1",
+    "JoinVars": {
+      "user_id": 0
+    },
     "TableName": "`user`_music",
     "Inputs": [
       {
@@ -672,6 +682,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1,-2,1",
+    "JoinVars": {
+      "user_id": 2
+    },
     "TableName": "`user`_music",
     "Inputs": [
       {
@@ -714,6 +727,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-2,-3,1",
+    "JoinVars": {
+      "user_id": 0
+    },
     "TableName": "`user`_music",
     "Inputs": [
       {
@@ -813,6 +829,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1,-2,1",
+    "JoinVars": {
+      "user_id": 2
+    },
     "TableName": "`user`_music",
     "Inputs": [
       {
@@ -962,6 +981,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1,1",
+    "JoinVars": {
+      "u_col": 1
+    },
     "TableName": "`user`_user_extra",
     "Inputs": [
       {
diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.txt b/go/vt/vtgate/planbuilder/testdata/select_cases.txt
index a3899c346e..f07fa6a26b 100644
--- a/go/vt/vtgate/planbuilder/testdata/select_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/select_cases.txt
@@ -858,6 +858,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1,1",
+    "JoinVars": {
+      "user_id": 0
+    },
     "TableName": "`user`_unsharded",
     "Inputs": [
       {
diff --git a/go/vt/vtgate/planbuilder/testdata/symtab_cases.txt b/go/vt/vtgate/planbuilder/testdata/symtab_cases.txt
index bb40820a79..f31ab8742c 100644
--- a/go/vt/vtgate/planbuilder/testdata/symtab_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/symtab_cases.txt
@@ -9,6 +9,9 @@
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1,1",
+    "JoinVars": {
+      "predef2": 0
+    },
     "TableName": "`user`_unsharded",
     "Inputs": [
       {
diff --git a/go/vt/vtgate/planbuilder/testdata/vindex_func_cases.txt b/go/vt/vtgate/planbuilder/testdata/vindex_func_cases.txt
index c81faa8c7c..f420659bea 100644
--- a/go/vt/vtgate/planbuilder/testdata/vindex_func_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/vindex_func_cases.txt
@@ -190,6 +190,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1,-2,1",
+    "JoinVars": {
+      "user_index_id": 0
+    },
     "TableName": "_unsharded",
     "Inputs": [
       {
@@ -230,6 +233,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1,1",
+    "JoinVars": {
+      "user_index_id": 1
+    },
     "TableName": "_unsharded",
     "Inputs": [
       {
@@ -270,6 +276,9 @@ Gen4 plan same as above
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1,1",
+    "JoinVars": {
+      "ui_id": 1
+    },
     "TableName": "_unsharded",
     "Inputs": [
       {
diff --git a/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt b/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt
index 0671c868a2..1706396ca6 100644
--- a/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt
@@ -7,6 +7,9 @@
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1,-1,2",
+    "JoinVars": {
+      "uid": 0
+    },
     "TableName": "`user`_user_extra",
     "Inputs": [
       {
@@ -44,6 +47,9 @@
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1,-1,2",
+    "JoinVars": {
+      "uid1": 0
+    },
     "TableName": "`user`_user_extra",
     "Inputs": [
       {
@@ -81,6 +87,9 @@
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "u1_col": 1
+    },
     "TableName": "`user`_`user`_`user`",
     "Inputs": [
       {
@@ -151,6 +160,9 @@
         "OperatorType": "Join",
         "Variant": "Join",
         "JoinColumnIndexes": "-2",
+        "JoinVars": {
+          "u1_col": 0
+        },
         "TableName": "`user`_`user`",
         "Inputs": [
           {
@@ -190,6 +202,9 @@
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "u2_col": 1
+    },
     "TableName": "`user`_`user`_`user`",
     "Inputs": [
       {
@@ -259,6 +274,9 @@
       {
         "OperatorType": "Join",
         "Variant": "Join",
+        "JoinVars": {
+          "u2_col": 0
+        },
         "TableName": "`user`_`user`",
         "Inputs": [
           {
@@ -298,12 +316,18 @@
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "u1_col": 1
+    },
     "TableName": "`user`_`user`_`user`",
     "Inputs": [
       {
         "OperatorType": "Join",
         "Variant": "Join",
         "JoinColumnIndexes": "-1,-2",
+        "JoinVars": {
+          "u1_col": 1
+        },
         "TableName": "`user`_`user`",
         "Inputs": [
           {
@@ -351,6 +375,9 @@
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1",
+    "JoinVars": {
+      "u3_col": 0
+    },
     "TableName": "`user`_`user`_`user`",
     "Inputs": [
       {
@@ -368,6 +395,9 @@
         "OperatorType": "Join",
         "Variant": "Join",
         "JoinColumnIndexes": "-2",
+        "JoinVars": {
+          "u1_col": 0
+        },
         "TableName": "`user`_`user`",
         "Inputs": [
           {
@@ -410,12 +440,18 @@
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "u1_col": 1
+    },
     "TableName": "`user`_`user`_`user`_`user`",
     "Inputs": [
       {
         "OperatorType": "Join",
         "Variant": "Join",
         "JoinColumnIndexes": "-1,-2",
+        "JoinVars": {
+          "u1_col": 1
+        },
         "TableName": "`user`_`user`_`user`",
         "Inputs": [
           {
@@ -503,6 +539,9 @@
         "OperatorType": "Join",
         "Variant": "Join",
         "JoinColumnIndexes": "1",
+        "JoinVars": {
+          "u4_col": 0
+        },
         "TableName": "`user`_`user`_`user`",
         "Inputs": [
           {
@@ -520,6 +559,9 @@
             "OperatorType": "Join",
             "Variant": "Join",
             "JoinColumnIndexes": "-2",
+            "JoinVars": {
+              "u1_col": 0
+            },
             "TableName": "`user`_`user`",
             "Inputs": [
               {
@@ -565,6 +607,9 @@
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1",
+    "JoinVars": {
+      "u1_col": 1
+    },
     "TableName": "`user`_`user`_`user`",
     "Inputs": [
       {
@@ -625,12 +670,18 @@
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-2",
+    "JoinVars": {
+      "u1_col": 0
+    },
     "TableName": "`user`_`user`_`user`",
     "Inputs": [
       {
         "OperatorType": "Join",
         "Variant": "Join",
         "JoinColumnIndexes": "-1,-2",
+        "JoinVars": {
+          "u1_col": 0
+        },
         "TableName": "`user`_`user`",
         "Inputs": [
           {
@@ -689,6 +740,9 @@
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-1,1",
+    "JoinVars": {
+      "weird_name_a_b_c": 1
+    },
     "TableName": "`weird``name`_unsharded",
     "Inputs": [
       {
@@ -723,6 +777,9 @@
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1,-2",
+    "JoinVars": {
+      "unsharded_id": 0
+    },
     "TableName": "unsharded_`weird``name`",
     "Inputs": [
       {
@@ -764,6 +821,9 @@
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "1",
+    "JoinVars": {
+      "weird_name_a_b_c": 0
+    },
     "TableName": "`weird``name`_unsharded",
     "Inputs": [
       {
@@ -798,6 +858,9 @@
     "OperatorType": "Join",
     "Variant": "Join",
     "JoinColumnIndexes": "-2",
+    "JoinVars": {
+      "unsharded_id": 0
+    },
     "TableName": "unsharded_`weird``name`",
     "Inputs": [
       {
@@ -843,6 +906,9 @@
         "OperatorType": "Join",
         "Variant": "Join",
         "JoinColumnIndexes": "-1,1",
+        "JoinVars": {
+          "u_col": 1
+        },
         "TableName": "`user`_user_extra",
         "Inputs": [
           {
@@ -883,6 +949,9 @@
         "OperatorType": "Join",
         "Variant": "Join",
         "JoinColumnIndexes": "-2,1",
+        "JoinVars": {
+          "u_col": 0
+        },
         "TableName": "`user`_user_extra",
         "Inputs": [
           {
@@ -912,6 +981,7 @@
     ]
   }
 }
+
 # Wire-up in subquery
 "select 1 from user where id in (select u.id, e.id from user u join user_extra e where e.id = u.col limit 10)"
 {
@@ -929,6 +999,9 @@
             "OperatorType": "Join",
             "Variant": "Join",
             "JoinColumnIndexes": "-1,1",
+            "JoinVars": {
+              "u_col": 1
+            },
             "TableName": "`user`_user_extra",
             "Inputs": [
               {
@@ -1004,6 +1077,9 @@
             "OperatorType": "Join",
             "Variant": "Join",
             "JoinColumnIndexes": "-1,1,-2",
+            "JoinVars": {
+              "u_col": 2
+            },
             "TableName": "`user`_user_extra",
             "Inputs": [
               {