@@ -26,6 +26,8 @@ import (
26
26
"github.com/pingcap/errors"
27
27
"github.com/pingcap/tidb/pkg/expression"
28
28
"github.com/pingcap/tidb/pkg/expression/aggregation"
29
+ exprctx "github.com/pingcap/tidb/pkg/expression/context"
30
+ "github.com/pingcap/tidb/pkg/expression/contextopt"
29
31
"github.com/pingcap/tidb/pkg/infoschema"
30
32
infoschemactx "github.com/pingcap/tidb/pkg/infoschema/context"
31
33
"github.com/pingcap/tidb/pkg/parser/ast"
@@ -505,17 +507,20 @@ func (er *expressionRewriter) buildSubquery(ctx context.Context, planCtx *exprRe
505
507
return np , hintFlags , nil
506
508
}
507
509
508
- func (er * expressionRewriter ) requirePlanCtx (inNode ast.Node ) (ctx * exprRewriterPlanCtx , err error ) {
510
+ func (er * expressionRewriter ) requirePlanCtx (inNode ast.Node , detail string ) (ctx * exprRewriterPlanCtx , err error ) {
509
511
if ctx = er .planCtx ; ctx == nil {
510
- err = errors .Errorf ("node '%T' is not allowed when building an expression without planner" , inNode )
512
+ if detail != "" {
513
+ detail = ", " + detail
514
+ }
515
+ err = errors .Errorf ("planCtx is required when rewriting node: '%T'%s" , inNode , detail )
511
516
}
512
517
return
513
518
}
514
519
515
520
// Enter implements Visitor interface.
516
521
func (er * expressionRewriter ) Enter (inNode ast.Node ) (ast.Node , bool ) {
517
522
enterWithPlanCtx := func (fn func (* exprRewriterPlanCtx ) (ast.Node , bool )) (ast.Node , bool ) {
518
- planCtx , err := er .requirePlanCtx (inNode )
523
+ planCtx , err := er .requirePlanCtx (inNode , "" )
519
524
if err != nil {
520
525
er .err = err
521
526
return inNode , true
@@ -1416,8 +1421,8 @@ func (er *expressionRewriter) Leave(originInNode ast.Node) (retNode ast.Node, ok
1416
1421
inNode = er .preprocess (inNode )
1417
1422
}
1418
1423
1419
- withPlanCtx := func (fn func (* exprRewriterPlanCtx )) {
1420
- planCtx , err := er .requirePlanCtx (inNode )
1424
+ withPlanCtx := func (fn func (* exprRewriterPlanCtx ), detail string ) {
1425
+ planCtx , err := er .requirePlanCtx (inNode , detail )
1421
1426
if err != nil {
1422
1427
er .err = err
1423
1428
return
@@ -1448,15 +1453,19 @@ func (er *expressionRewriter) Leave(originInNode ast.Node) (retNode ast.Node, ok
1448
1453
case * driver.ParamMarkerExpr :
1449
1454
er .toParamMarker (v )
1450
1455
case * ast.VariableExpr :
1451
- withPlanCtx (func (planCtx * exprRewriterPlanCtx ) {
1452
- er .rewriteVariable (planCtx , v )
1453
- })
1456
+ if v .IsSystem {
1457
+ withPlanCtx (func (planCtx * exprRewriterPlanCtx ) {
1458
+ er .rewriteSystemVariable (planCtx , v )
1459
+ }, "accessing system variable requires plan context" )
1460
+ } else {
1461
+ er .rewriteUserVariable (v )
1462
+ }
1454
1463
case * ast.FuncCallExpr :
1455
1464
switch v .FnName .L {
1456
1465
case ast .Grouping :
1457
1466
withPlanCtx (func (planCtx * exprRewriterPlanCtx ) {
1458
1467
er .funcCallToExpressionWithPlanCtx (planCtx , v )
1459
- })
1468
+ }, "grouping function requires plan context" )
1460
1469
default :
1461
1470
if _ , ok := expression .TryFoldFunctions [v .FnName .L ]; ok {
1462
1471
er .tryFoldCounter --
@@ -1536,7 +1545,7 @@ func (er *expressionRewriter) Leave(originInNode ast.Node) (retNode ast.Node, ok
1536
1545
case * ast.PositionExpr :
1537
1546
withPlanCtx (func (planCtx * exprRewriterPlanCtx ) {
1538
1547
er .positionToScalarFunc (planCtx , v )
1539
- })
1548
+ }, "" )
1540
1549
case * ast.IsNullExpr :
1541
1550
er .isNullToExpression (v )
1542
1551
case * ast.IsTruthExpr :
@@ -1651,37 +1660,52 @@ func (er *expressionRewriter) useCache() bool {
1651
1660
return er .sctx .IsUseCache ()
1652
1661
}
1653
1662
1654
- func (er * expressionRewriter ) rewriteVariable ( planCtx * exprRewriterPlanCtx , v * ast.VariableExpr ) {
1663
+ func (er * expressionRewriter ) rewriteUserVariable ( v * ast.VariableExpr ) {
1655
1664
stkLen := len (er .ctxStack )
1656
1665
name := strings .ToLower (v .Name )
1657
- sessionVars := planCtx .builder .ctx .GetSessionVars ()
1658
- if ! v .IsSystem {
1659
- if v .Value != nil {
1660
- tp := er .ctxStack [stkLen - 1 ].GetType (er .sctx .GetEvalCtx ())
1661
- er .ctxStack [stkLen - 1 ], er .err = er .newFunction (ast .SetVar , tp ,
1662
- expression .DatumToConstant (types .NewDatum (name ), mysql .TypeString , 0 ),
1663
- er .ctxStack [stkLen - 1 ])
1664
- er .ctxNameStk [stkLen - 1 ] = types .EmptyName
1665
- // Store the field type of the variable into SessionVars.UserVarTypes.
1666
- // Normally we can infer the type from SessionVars.User, but we need SessionVars.UserVarTypes when
1667
- // GetVar has not been executed to fill the SessionVars.Users.
1668
- sessionVars .SetUserVarType (name , tp )
1669
- return
1670
- }
1671
- tp , ok := sessionVars .GetUserVarType (name )
1672
- if ! ok {
1673
- tp = types .NewFieldType (mysql .TypeVarString )
1674
- tp .SetFlen (mysql .MaxFieldVarCharLength )
1675
- }
1676
- f , err := er .newFunction (ast .GetVar , tp , expression .DatumToConstant (types .NewStringDatum (name ), mysql .TypeString , 0 ))
1677
- if err != nil {
1678
- er .err = err
1679
- return
1680
- }
1681
- f .SetCoercibility (expression .CoercibilityImplicit )
1682
- er .ctxStackAppend (f , types .EmptyName )
1666
+ evalCtx := er .sctx .GetEvalCtx ()
1667
+ if ! evalCtx .GetOptionalPropSet ().Contains (exprctx .OptPropSessionVars ) {
1668
+ er .err = errors .Errorf ("rewriting user variable requires '%s' in evalCtx" , exprctx .OptPropSessionVars .String ())
1669
+ return
1670
+ }
1671
+
1672
+ sessionVars , err := contextopt.SessionVarsPropReader {}.GetSessionVars (evalCtx )
1673
+ if err != nil {
1674
+ er .err = err
1675
+ return
1676
+ }
1677
+
1678
+ intest .Assert (er .planCtx == nil || sessionVars == er .planCtx .builder .ctx .GetSessionVars ())
1679
+
1680
+ if v .Value != nil {
1681
+ tp := er .ctxStack [stkLen - 1 ].GetType (er .sctx .GetEvalCtx ())
1682
+ er .ctxStack [stkLen - 1 ], er .err = er .newFunction (ast .SetVar , tp ,
1683
+ expression .DatumToConstant (types .NewDatum (name ), mysql .TypeString , 0 ),
1684
+ er .ctxStack [stkLen - 1 ])
1685
+ er .ctxNameStk [stkLen - 1 ] = types .EmptyName
1686
+ // Store the field type of the variable into SessionVars.UserVarTypes.
1687
+ // Normally we can infer the type from SessionVars.User, but we need SessionVars.UserVarTypes when
1688
+ // GetVar has not been executed to fill the SessionVars.Users.
1689
+ sessionVars .SetUserVarType (name , tp )
1690
+ return
1691
+ }
1692
+ tp , ok := sessionVars .GetUserVarType (name )
1693
+ if ! ok {
1694
+ tp = types .NewFieldType (mysql .TypeVarString )
1695
+ tp .SetFlen (mysql .MaxFieldVarCharLength )
1696
+ }
1697
+ f , err := er .newFunction (ast .GetVar , tp , expression .DatumToConstant (types .NewStringDatum (name ), mysql .TypeString , 0 ))
1698
+ if err != nil {
1699
+ er .err = err
1683
1700
return
1684
1701
}
1702
+ f .SetCoercibility (expression .CoercibilityImplicit )
1703
+ er .ctxStackAppend (f , types .EmptyName )
1704
+ }
1705
+
1706
+ func (er * expressionRewriter ) rewriteSystemVariable (planCtx * exprRewriterPlanCtx , v * ast.VariableExpr ) {
1707
+ name := strings .ToLower (v .Name )
1708
+ sessionVars := planCtx .builder .ctx .GetSessionVars ()
1685
1709
sysVar := variable .GetSysVar (name )
1686
1710
if sysVar == nil {
1687
1711
er .err = variable .ErrUnknownSystemVar .FastGenByArgs (name )
0 commit comments