Skip to content

Commit a33a3da

Browse files
harshit-gangalkamil-gwozdz
authored andcommitted
allow innodb_lock_wait_timeout as system variable (vitessio#16574)
Signed-off-by: Harshit Gangal <[email protected]>
1 parent 6b8851d commit a33a3da

File tree

5 files changed

+235
-1
lines changed

5 files changed

+235
-1
lines changed

go/test/endtoend/vtgate/reservedconn/sysvar_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,3 +421,74 @@ func checkOltpAndOlapInterchangingTx(t *testing.T, conn *mysql.Conn) {
421421
utils.Exec(t, conn, "set workload = oltp")
422422
utils.AssertMatches(t, conn, "select id, val1 from test where id = 80", "[[INT64(80) NULL]]")
423423
}
424+
425+
func TestSysVarTxIsolation(t *testing.T) {
426+
conn, err := mysql.Connect(context.Background(), &vtParams)
427+
require.NoError(t, err)
428+
defer conn.Close()
429+
430+
// will run every check twice to see that the isolation level is set for all the queries in the session and
431+
432+
// default from mysql
433+
utils.AssertMatches(t, conn, "select @@transaction_isolation", `[[VARCHAR("REPEATABLE-READ")]]`)
434+
// ensuring it goes to mysql
435+
utils.AssertContains(t, conn, "select @@transaction_isolation, connection_id()", `REPEATABLE-READ`)
436+
// second run, ensuring it has the same value.
437+
utils.AssertContains(t, conn, "select @@transaction_isolation, connection_id()", `REPEATABLE-READ`)
438+
439+
// setting to different value.
440+
utils.Exec(t, conn, "set @@transaction_isolation = 'read-committed'")
441+
utils.AssertMatches(t, conn, "select @@transaction_isolation", `[[VARCHAR("READ-COMMITTED")]]`)
442+
// ensuring it goes to mysql
443+
utils.AssertContains(t, conn, "select @@transaction_isolation, connection_id()", `READ-COMMITTED`)
444+
// second run, to ensuring the setting is applied on the session and not just on next query after settings.
445+
utils.AssertContains(t, conn, "select @@transaction_isolation, connection_id()", `READ-COMMITTED`)
446+
447+
// changing setting to different value.
448+
utils.Exec(t, conn, "set session transaction isolation level read uncommitted")
449+
utils.AssertMatches(t, conn, "select @@transaction_isolation", `[[VARCHAR("READ-UNCOMMITTED")]]`)
450+
// ensuring it goes to mysql
451+
utils.AssertContains(t, conn, "select @@transaction_isolation, connection_id()", `READ-UNCOMMITTED`)
452+
// second run, to ensuring the setting is applied on the session and not just on next query after settings.
453+
utils.AssertContains(t, conn, "select @@transaction_isolation, connection_id()", `READ-UNCOMMITTED`)
454+
455+
// changing setting to different value.
456+
utils.Exec(t, conn, "set transaction isolation level serializable")
457+
utils.AssertMatches(t, conn, "select @@transaction_isolation", `[[VARCHAR("SERIALIZABLE")]]`)
458+
// ensuring it goes to mysql
459+
utils.AssertContains(t, conn, "select @@transaction_isolation, connection_id()", `SERIALIZABLE`)
460+
// second run, to ensuring the setting is applied on the session and not just on next query after settings.
461+
utils.AssertContains(t, conn, "select @@transaction_isolation, connection_id()", `SERIALIZABLE`)
462+
}
463+
464+
// TestSysVarInnodbWaitTimeout tests the innodb_lock_wait_timeout system variable
465+
func TestSysVarInnodbWaitTimeout(t *testing.T) {
466+
conn, err := mysql.Connect(context.Background(), &vtParams)
467+
require.NoError(t, err)
468+
defer conn.Close()
469+
470+
// default from mysql
471+
utils.AssertMatches(t, conn, "select @@innodb_lock_wait_timeout", `[[UINT64(20)]]`)
472+
utils.AssertMatches(t, conn, "select @@global.innodb_lock_wait_timeout", `[[UINT64(20)]]`)
473+
// ensuring it goes to mysql
474+
utils.AssertContains(t, conn, "select @@innodb_lock_wait_timeout", `UINT64(20)`)
475+
utils.AssertContains(t, conn, "select @@global.innodb_lock_wait_timeout", `UINT64(20)`)
476+
477+
// setting to different value.
478+
utils.Exec(t, conn, "set @@innodb_lock_wait_timeout = 120")
479+
utils.AssertMatches(t, conn, "select @@innodb_lock_wait_timeout", `[[INT64(120)]]`)
480+
// ensuring it goes to mysql
481+
utils.AssertContains(t, conn, "select @@global.innodb_lock_wait_timeout, connection_id()", `UINT64(20)`)
482+
utils.AssertContains(t, conn, "select @@innodb_lock_wait_timeout, connection_id()", `INT64(120)`)
483+
// second run, to ensuring the setting is applied on the session and not just on next query after settings.
484+
utils.AssertContains(t, conn, "select @@innodb_lock_wait_timeout, connection_id()", `INT64(120)`)
485+
486+
// changing setting to different value.
487+
utils.Exec(t, conn, "set @@innodb_lock_wait_timeout = 240")
488+
utils.AssertMatches(t, conn, "select @@innodb_lock_wait_timeout", `[[INT64(240)]]`)
489+
// ensuring it goes to mysql
490+
utils.AssertContains(t, conn, "select @@global.innodb_lock_wait_timeout, connection_id()", `UINT64(20)`)
491+
utils.AssertContains(t, conn, "select @@innodb_lock_wait_timeout, connection_id()", `INT64(240)`)
492+
// second run, to ensuring the setting is applied on the session and not just on next query after settings.
493+
utils.AssertContains(t, conn, "select @@innodb_lock_wait_timeout, connection_id()", `INT64(240)`)
494+
}

go/vt/sqlparser/ast_rewriting.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,76 @@ func (er *astRewriter) rewrite(cursor *Cursor) bool {
415415
return true
416416
}
417417

418+
func (er *astRewriter) rewriteNotExpr(cursor *Cursor, node *NotExpr) {
419+
switch inner := node.Expr.(type) {
420+
case *ComparisonExpr:
421+
// not col = 42 => col != 42
422+
// not col > 42 => col <= 42
423+
// etc
424+
canChange, inverse := inverseOp(inner.Operator)
425+
if canChange {
426+
inner.Operator = inverse
427+
cursor.Replace(inner)
428+
}
429+
case *NotExpr:
430+
// not not true => true
431+
cursor.Replace(inner.Expr)
432+
case BoolVal:
433+
// not true => false
434+
inner = !inner
435+
cursor.Replace(inner)
436+
}
437+
}
438+
439+
func (er *astRewriter) rewriteVariable(cursor *Cursor, node *Variable) {
440+
// Iff we are in SET, we want to change the scope of variables if a modifier has been set
441+
// and only on the lhs of the assignment:
442+
// set session sql_mode = @someElse
443+
// here we need to change the scope of `sql_mode` and not of `@someElse`
444+
if v, isSet := cursor.Parent().(*SetExpr); isSet && v.Var == node {
445+
return
446+
}
447+
// no rewriting for global scope variable.
448+
// this should be returned from the underlying database.
449+
switch node.Scope {
450+
case VariableScope:
451+
er.udvRewrite(cursor, node)
452+
case SessionScope, NextTxScope:
453+
er.sysVarRewrite(cursor, node)
454+
}
455+
}
456+
457+
func (er *astRewriter) visitSelect(node *Select) {
458+
for _, col := range node.SelectExprs {
459+
if _, hasStar := col.(*StarExpr); hasStar {
460+
er.hasStarInSelect = true
461+
continue
462+
}
463+
464+
aliasedExpr, ok := col.(*AliasedExpr)
465+
if !ok || aliasedExpr.As.NotEmpty() {
466+
continue
467+
}
468+
buf := NewTrackedBuffer(nil)
469+
aliasedExpr.Expr.Format(buf)
470+
// select last_insert_id() -> select :__lastInsertId as `last_insert_id()`
471+
innerBindVarNeeds, err := er.rewriteAliasedExpr(aliasedExpr)
472+
if err != nil {
473+
er.err = err
474+
return
475+
}
476+
if innerBindVarNeeds.HasRewrites() {
477+
aliasedExpr.As = NewIdentifierCI(buf.String())
478+
}
479+
er.bindVars.MergeWith(innerBindVarNeeds)
480+
481+
}
482+
// set select limit if explicitly not set when sql_select_limit is set on the connection.
483+
if er.selectLimit > 0 && node.Limit == nil {
484+
node.Limit = &Limit{Rowcount: NewIntLiteral(strconv.Itoa(er.selectLimit))}
485+
}
486+
}
487+
418488
func inverseOp(i ComparisonExprOperator) (bool, ComparisonExprOperator) {
419489
switch i {
420490
case EqualOp:

go/vt/sysvars/sysvars.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ var (
171171
{Name: "foreign_key_checks", IsBoolean: true, SupportSetVar: true},
172172
{Name: "group_concat_max_len", SupportSetVar: true},
173173
{Name: "information_schema_stats_expiry"},
174+
{Name: "innodb_lock_wait_timeout"},
174175
{Name: "max_heap_table_size", SupportSetVar: true},
175176
{Name: "max_seeks_for_key", SupportSetVar: true},
176177
{Name: "max_tmp_tables"},
@@ -226,7 +227,6 @@ var (
226227
{Name: "collation_server"},
227228
{Name: "completion_type"},
228229
{Name: "div_precision_increment", SupportSetVar: true},
229-
{Name: "innodb_lock_wait_timeout"},
230230
{Name: "interactive_timeout"},
231231
{Name: "lc_time_names"},
232232
{Name: "lock_wait_timeout", SupportSetVar: true},

go/vt/vtgate/executor_select_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3782,3 +3782,85 @@ func TestMain(m *testing.M) {
37823782
_flag.ParseFlagsForTest()
37833783
os.Exit(m.Run())
37843784
}
3785+
3786+
func TestStreamJoinQuery(t *testing.T) {
3787+
ctx := utils.LeakCheckContext(t)
3788+
3789+
// Special setup: Don't use createExecutorEnv.
3790+
cell := "aa"
3791+
hc := discovery.NewFakeHealthCheck(nil)
3792+
u := createSandbox(KsTestUnsharded)
3793+
s := createSandbox(KsTestSharded)
3794+
s.VSchema = executorVSchema
3795+
u.VSchema = unshardedVSchema
3796+
serv := newSandboxForCells(ctx, []string{cell})
3797+
resolver := newTestResolver(ctx, hc, serv, cell)
3798+
shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"}
3799+
for _, shard := range shards {
3800+
_ = hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil)
3801+
}
3802+
executor := createExecutor(ctx, serv, cell, resolver)
3803+
defer executor.Close()
3804+
3805+
sql := "select u.foo, u.apa, ue.bar, ue.apa from user u join user_extra ue on u.foo = ue.bar"
3806+
result, err := executorStream(ctx, executor, sql)
3807+
require.NoError(t, err)
3808+
wantResult := &sqltypes.Result{
3809+
Fields: append(sandboxconn.SingleRowResult.Fields, sandboxconn.SingleRowResult.Fields...),
3810+
}
3811+
wantRow := append(sandboxconn.StreamRowResult.Rows[0], sandboxconn.StreamRowResult.Rows[0]...)
3812+
for i := 0; i < 64; i++ {
3813+
wantResult.Rows = append(wantResult.Rows, wantRow)
3814+
}
3815+
require.Equal(t, len(wantResult.Rows), len(result.Rows))
3816+
for idx := 0; idx < 64; idx++ {
3817+
utils.MustMatch(t, wantResult.Rows[idx], result.Rows[idx], "mismatched on: ", strconv.Itoa(idx))
3818+
}
3819+
}
3820+
3821+
// TestSysVarGlobalAndSession tests that global and session variables are set correctly.
3822+
// It also tests that setting a global variable does not affect the session variable and vice versa.
3823+
// Also, test what happens on running select @@global and select @@session for a system variable.
3824+
func TestSysVarGlobalAndSession(t *testing.T) {
3825+
executor, sbc1, _, _, _ := createExecutorEnv(t)
3826+
executor.normalize = true
3827+
session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, SystemVariables: map[string]string{}})
3828+
3829+
sbc1.SetResults([]*sqltypes.Result{
3830+
sqltypes.MakeTestResult(sqltypes.MakeTestFields("innodb_lock_wait_timeout", "uint64"), "20"),
3831+
sqltypes.MakeTestResult(sqltypes.MakeTestFields("innodb_lock_wait_timeout", "uint64"), "20"),
3832+
sqltypes.MakeTestResult(sqltypes.MakeTestFields("1", "int64")),
3833+
sqltypes.MakeTestResult(sqltypes.MakeTestFields("new", "uint64"), "40"),
3834+
sqltypes.MakeTestResult(sqltypes.MakeTestFields("reserve_execute", "uint64")),
3835+
sqltypes.MakeTestResult(sqltypes.MakeTestFields("@@global.innodb_lock_wait_timeout", "uint64"), "20"),
3836+
})
3837+
qr, err := executor.Execute(context.Background(), nil, "TestSetStmt", session,
3838+
"select @@innodb_lock_wait_timeout", nil)
3839+
require.NoError(t, err)
3840+
require.Equal(t, `[[UINT64(20)]]`, fmt.Sprintf("%v", qr.Rows))
3841+
3842+
qr, err = executor.Execute(context.Background(), nil, "TestSetStmt", session,
3843+
"select @@global.innodb_lock_wait_timeout", nil)
3844+
require.NoError(t, err)
3845+
require.Equal(t, `[[UINT64(20)]]`, fmt.Sprintf("%v", qr.Rows))
3846+
3847+
_, err = executor.Execute(context.Background(), nil, "TestSetStmt", session,
3848+
"set @@global.innodb_lock_wait_timeout = 120", nil)
3849+
require.NoError(t, err)
3850+
require.Empty(t, session.SystemVariables["innodb_lock_wait_timeout"])
3851+
3852+
_, err = executor.Execute(context.Background(), nil, "TestSetStmt", session,
3853+
"set @@innodb_lock_wait_timeout = 40", nil)
3854+
require.NoError(t, err)
3855+
require.EqualValues(t, "40", session.SystemVariables["innodb_lock_wait_timeout"])
3856+
3857+
qr, err = executor.Execute(context.Background(), nil, "TestSetStmt", session,
3858+
"select @@innodb_lock_wait_timeout", nil)
3859+
require.NoError(t, err)
3860+
require.Equal(t, `[[INT64(40)]]`, fmt.Sprintf("%v", qr.Rows))
3861+
3862+
qr, err = executor.Execute(context.Background(), nil, "TestSetStmt", session,
3863+
"select @@global.innodb_lock_wait_timeout", nil)
3864+
require.NoError(t, err)
3865+
require.Equal(t, `[[UINT64(20)]]`, fmt.Sprintf("%v", qr.Rows))
3866+
}

go/vt/vtgate/executor_set_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,17 @@ func TestExecutorSetOp(t *testing.T) {
347347
}, {
348348
in: "set global client_found_rows = 1",
349349
result: returnNoResult("client_found_rows", "int64"),
350+
}, {
351+
in: "set tx_isolation = 'read-committed'",
352+
sysVars: map[string]string{"tx_isolation": "'read-committed'"},
353+
result: returnResult("tx_isolation", "varchar", "read-committed"),
354+
}, {
355+
in: "set @@innodb_lock_wait_timeout=120",
356+
sysVars: map[string]string{"innodb_lock_wait_timeout": "120"},
357+
result: returnResult("innodb_lock_wait_timeout", "int64", "120"),
358+
}, {
359+
in: "set @@global.innodb_lock_wait_timeout=120",
360+
result: returnResult("innodb_lock_wait_timeout", "int64", "120"),
350361
}}
351362
for _, tcase := range testcases {
352363
t.Run(tcase.in, func(t *testing.T) {

0 commit comments

Comments
 (0)