vitess.io/vitess@v0.16.2/go/test/endtoend/vtgate/reservedconn/sysvar_test.go (about) 1 /* 2 Copyright 2020 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package reservedconn 18 19 import ( 20 "context" 21 "fmt" 22 "testing" 23 "time" 24 25 "vitess.io/vitess/go/test/endtoend/utils" 26 27 "github.com/stretchr/testify/assert" 28 "github.com/stretchr/testify/require" 29 30 "vitess.io/vitess/go/mysql" 31 "vitess.io/vitess/go/test/endtoend/cluster" 32 ) 33 34 func TestSetSysVarSingle(t *testing.T) { 35 defer cluster.PanicHandler(t) 36 ctx := context.Background() 37 type queriesWithExpectations struct { 38 name, expr string 39 expected []string 40 } 41 42 queries := []queriesWithExpectations{{ 43 name: "default_storage_engine", // ignored 44 expr: "INNODB", 45 expected: []string{`[[VARCHAR("InnoDB")]]`}, 46 }, { 47 name: "character_set_client", // check and ignored 48 expr: "utf8mb4", 49 expected: []string{`[[VARCHAR("utf8mb4")]]`, `[[VARCHAR("utf8")]]`}, 50 }, { 51 name: "character_set_client", // ignored so will keep the actual value 52 expr: "@charvar", 53 expected: []string{`[[VARCHAR("utf8mb4")]]`, `[[VARCHAR("utf8")]]`}, 54 }, { 55 name: "sql_mode", // use reserved conn 56 expr: "''", 57 expected: []string{`[[VARCHAR("")]]`}, 58 }, { 59 name: "sql_mode", // use reserved conn 60 expr: `concat(@@sql_mode,"NO_ZERO_DATE")`, 61 expected: []string{`[[VARCHAR("NO_ZERO_DATE")]]`}, 62 }, { 63 name: "sql_mode", // use reserved conn 64 expr: "@@sql_mode", 65 expected: []string{`[[VARCHAR("NO_ZERO_DATE")]]`}, 66 }, { 67 name: "SQL_SAFE_UPDATES", // use reserved conn 68 expr: "1", 69 expected: []string{"[[INT64(1)]]"}, 70 }, { 71 name: "sql_auto_is_null", // ignored so will keep the actual value 72 expr: "on", 73 expected: []string{`[[INT64(0)]]`}, 74 }, { 75 name: "sql_notes", // use reserved conn 76 expr: "off", 77 expected: []string{"[[INT64(0)]]"}, 78 }} 79 80 conn, err := mysql.Connect(ctx, &vtParams) 81 82 require.NoError(t, err) 83 defer conn.Close() 84 85 for i, q := range queries { 86 query := fmt.Sprintf("set %s = %s", q.name, q.expr) 87 t.Run(fmt.Sprintf("%d-%s", i, query), func(t *testing.T) { 88 utils.Exec(t, conn, query) 89 utils.AssertMatchesAny(t, conn, fmt.Sprintf("select @@%s", q.name), q.expected...) 90 }) 91 } 92 } 93 94 func TestSetSystemVariable(t *testing.T) { 95 conn, err := mysql.Connect(context.Background(), &vtParams) 96 require.NoError(t, err) 97 defer conn.Close() 98 99 utils.Exec(t, conn, "set session sql_mode = 'NO_ZERO_DATE', session default_week_format = 1") 100 q := `select str_to_date('00/00/0000', '%m/%d/%Y'), WEEK('2008-02-20')` 101 utils.AssertMatches(t, conn, q, `[[NULL INT64(8)]]`) 102 103 utils.AssertMatches(t, conn, "select @@sql_mode", `[[VARCHAR("NO_ZERO_DATE")]]`) 104 utils.Exec(t, conn, "set @@sql_mode = '', session default_week_format = 0") 105 106 utils.AssertMatches(t, conn, q, `[[DATE("0000-00-00") INT64(7)]]`) 107 108 utils.Exec(t, conn, "SET @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483") 109 utils.AssertMatches(t, conn, "select @@sql_mode", `[[VARCHAR(",STRICT_ALL_TABLES,NO_AUTO_VALUE_ON_ZERO")]]`) 110 } 111 112 func TestSetSystemVarWithTxFailure(t *testing.T) { 113 114 conn, err := mysql.Connect(context.Background(), &vtParams) 115 require.NoError(t, err) 116 defer conn.Close() 117 118 utils.Exec(t, conn, "insert into test (id, val1) values (80, null)") 119 120 // before changing any settings, let's confirm sql_safe_updates value 121 utils.AssertMatches(t, conn, `select @@sql_safe_updates from test where id = 80`, `[[INT64(0)]]`) 122 123 utils.Exec(t, conn, "set sql_safe_updates = 1") 124 utils.Exec(t, conn, "begin") 125 126 qr := utils.Exec(t, conn, "select connection_id() from test where id = 80") 127 128 // kill the mysql connection shard which has transaction open. 129 vttablet1 := clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet() // 80- 130 vttablet1.VttabletProcess.QueryTablet(fmt.Sprintf("kill %s", qr.Rows[0][0].ToString()), keyspaceName, false) 131 132 // transaction fails on commit - we should no longer be in a transaction 133 _, err = conn.ExecuteFetch("commit", 1, true) 134 require.Error(t, err) 135 136 // we still want to have our system setting applied 137 utils.AssertMatches(t, conn, `select @@sql_safe_updates`, `[[INT64(1)]]`) 138 } 139 140 func TestSetSystemVarWithConnectionTimeout(t *testing.T) { 141 142 conn, err := mysql.Connect(context.Background(), &vtParams) 143 require.NoError(t, err) 144 defer conn.Close() 145 utils.Exec(t, conn, "delete from test") 146 147 utils.Exec(t, conn, "insert into test (id, val1) values (80, null)") 148 utils.Exec(t, conn, "set sql_safe_updates = 1") 149 utils.AssertMatches(t, conn, "select @@sql_safe_updates from test where id = 80", "[[INT64(1)]]") 150 151 // Connection timeout. 152 time.Sleep(10 * time.Second) 153 154 // connection has timed out, but vtgate will recreate the connection for us 155 utils.AssertMatches(t, conn, "select @@sql_safe_updates from test where id = 80", "[[INT64(1)]]") 156 } 157 158 func TestSetSystemVariableAndThenSuccessfulTx(t *testing.T) { 159 160 conn, err := mysql.Connect(context.Background(), &vtParams) 161 require.NoError(t, err) 162 defer conn.Close() 163 utils.Exec(t, conn, "delete from test") 164 165 utils.Exec(t, conn, "set sql_safe_updates = 1") 166 utils.Exec(t, conn, "begin") 167 utils.Exec(t, conn, "insert into test (id, val1) values (80, null)") 168 utils.Exec(t, conn, "commit") 169 utils.AssertMatches(t, conn, "select id, val1 from test", "[[INT64(80) NULL]]") 170 utils.AssertMatches(t, conn, "select @@sql_safe_updates", "[[INT64(1)]]") 171 } 172 173 func TestSetSystemVariableAndThenSuccessfulAutocommitDML(t *testing.T) { 174 175 conn, err := mysql.Connect(context.Background(), &vtParams) 176 require.NoError(t, err) 177 defer conn.Close() 178 utils.Exec(t, conn, `delete from test`) 179 180 utils.Exec(t, conn, `set sql_safe_updates = 1`) 181 182 utils.Exec(t, conn, `insert into test (id, val1) values (80, null)`) 183 utils.AssertMatches(t, conn, `select id, val1 from test`, `[[INT64(80) NULL]]`) 184 utils.AssertMatches(t, conn, `select @@sql_safe_updates`, `[[INT64(1)]]`) 185 186 utils.Exec(t, conn, `update test set val2 = 2 where val1 is null`) 187 utils.AssertMatches(t, conn, `select id, val1, val2 from test`, `[[INT64(80) NULL INT32(2)]]`) 188 utils.AssertMatches(t, conn, `select @@sql_safe_updates`, `[[INT64(1)]]`) 189 190 utils.Exec(t, conn, `update test set val1 = 'text' where val1 is null`) 191 utils.AssertMatches(t, conn, `select id, val1, val2 from test`, `[[INT64(80) VARCHAR("text") INT32(2)]]`) 192 utils.AssertMatches(t, conn, `select @@sql_safe_updates`, `[[INT64(1)]]`) 193 194 utils.Exec(t, conn, `delete from test where val1 = 'text'`) 195 utils.AssertMatches(t, conn, `select id, val1, val2 from test`, `[]`) 196 utils.AssertMatches(t, conn, `select @@sql_safe_updates`, `[[INT64(1)]]`) 197 } 198 199 func TestStartTxAndSetSystemVariableAndThenSuccessfulCommit(t *testing.T) { 200 201 conn, err := mysql.Connect(context.Background(), &vtParams) 202 require.NoError(t, err) 203 defer conn.Close() 204 utils.Exec(t, conn, "delete from test") 205 206 utils.Exec(t, conn, "begin") 207 utils.Exec(t, conn, "set sql_safe_updates = 1") 208 utils.Exec(t, conn, "insert into test (id, val1) values (54, null)") 209 utils.Exec(t, conn, "commit") 210 utils.AssertMatches(t, conn, "select id, val1 from test", "[[INT64(54) NULL]]") 211 utils.AssertMatches(t, conn, "select @@sql_safe_updates", "[[INT64(1)]]") 212 } 213 214 func TestSetSystemVarAutocommitWithConnError(t *testing.T) { 215 if clusterInstance.HasPartialKeyspaces { 216 t.Skip("For partial keyspaces, kill is called on the source keyspace but queries execute on the target, so this test will fail") 217 } 218 conn, err := mysql.Connect(context.Background(), &vtParams) 219 require.NoError(t, err) 220 defer conn.Close() 221 222 utils.Exec(t, conn, "delete from test") 223 utils.Exec(t, conn, "insert into test (id, val1) values (1, null), (4, null)") 224 225 utils.Exec(t, conn, "set sql_safe_updates = 1") // this should force us into a reserved connection 226 utils.AssertMatches(t, conn, "select id from test order by id", "[[INT64(1)] [INT64(4)]]") 227 qr := utils.Exec(t, conn, "select connection_id() from test where id = 1") 228 229 // kill the mysql connection shard which has transaction open. 230 vttablet1 := clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet() // -80 231 _, err = vttablet1.VttabletProcess.QueryTablet(fmt.Sprintf("kill %s", qr.Rows[0][0].ToString()), keyspaceName, false) 232 require.NoError(t, err) 233 234 // first query to 80- shard should pass 235 utils.AssertMatches(t, conn, "select id, val1 from test where id = 4", "[[INT64(4) NULL]]") 236 237 // first query to -80 shard will fail, but vtgate will auto-retry for us 238 utils.Exec(t, conn, "insert into test (id, val1) values (2, null)") 239 utils.AssertMatches(t, conn, "select id from test where id = 2", "[[INT64(2)]]") 240 utils.AssertMatches(t, conn, "select id, @@sql_safe_updates from test where id = 2", "[[INT64(2) INT64(1)]]") 241 } 242 243 func TestSetSystemVarInTxWithConnError(t *testing.T) { 244 245 conn, err := mysql.Connect(context.Background(), &vtParams) 246 require.NoError(t, err) 247 defer conn.Close() 248 249 utils.Exec(t, conn, "delete from test") 250 utils.Exec(t, conn, "insert into test (id, val1) values (1, null), (4, null)") 251 252 utils.Exec(t, conn, "set sql_safe_updates = 1") // this should force us into a reserved connection 253 qr := utils.Exec(t, conn, "select connection_id() from test where id = 4") 254 utils.Exec(t, conn, "begin") 255 utils.Exec(t, conn, "insert into test (id, val1) values (2, null)") 256 257 // kill the mysql connection shard which has transaction open. 258 vttablet1 := clusterInstance.Keyspaces[0].Shards[1].PrimaryTablet() // 80- 259 _, err = vttablet1.VttabletProcess.QueryTablet(fmt.Sprintf("kill %s", qr.Rows[0][0].ToString()), keyspaceName, false) 260 require.NoError(t, err) 261 262 // query to -80 shard should pass and remain in transaction. 263 utils.AssertMatches(t, conn, "select id, val1 from test where id = 2", "[[INT64(2) NULL]]") 264 utils.Exec(t, conn, "rollback") 265 utils.AssertMatches(t, conn, "select id, val1 from test where id = 2", "[]") 266 267 // first query to -80 shard will fail, but vtgate should retry once and succeed the second time 268 utils.Exec(t, conn, "select @@sql_safe_updates from test where id = 4") 269 270 // subsequent queries on 80- will pass 271 utils.AssertMatches(t, conn, "select id, @@sql_safe_updates from test where id = 4", "[[INT64(4) INT64(1)]]") 272 } 273 274 func BenchmarkReservedConnFieldQuery(b *testing.B) { 275 conn, err := mysql.Connect(context.Background(), &vtParams) 276 require.NoError(b, err) 277 defer conn.Close() 278 279 utils.Exec(b, conn, "delete from test") 280 utils.Exec(b, conn, "insert into test (id, val1) values (1, 'toto'), (4, 'tata')") 281 282 // set sql_mode to empty to force the use of reserved connection 283 utils.Exec(b, conn, "set sql_mode = ''") 284 utils.AssertMatches(b, conn, "select @@sql_mode", `[[VARCHAR("")]]`) 285 286 for i := 0; i < b.N; i++ { 287 utils.Exec(b, conn, "select id, val1 from test") 288 } 289 } 290 291 func TestEnableSystemSettings(t *testing.T) { 292 conn, err := mysql.Connect(context.Background(), &vtParams) 293 require.NoError(t, err) 294 defer conn.Close() 295 296 // test set @@enable_system_settings to false and true 297 utils.Exec(t, conn, "set enable_system_settings = false") 298 utils.AssertMatches(t, conn, `select @@enable_system_settings`, `[[INT64(0)]]`) 299 utils.Exec(t, conn, "set enable_system_settings = true") 300 utils.AssertMatches(t, conn, `select @@enable_system_settings`, `[[INT64(1)]]`) 301 302 // prepare the @@sql_mode variable 303 utils.Exec(t, conn, "set sql_mode = 'NO_ZERO_DATE'") 304 utils.AssertMatches(t, conn, "select @@sql_mode", `[[VARCHAR("NO_ZERO_DATE")]]`) 305 306 // check disabling @@enable_system_settings 307 utils.Exec(t, conn, "set enable_system_settings = false") 308 utils.Exec(t, conn, "set sql_mode = ''") // attempting to set @@sql_mode to an empty string 309 utils.AssertMatches(t, conn, "select @@sql_mode", `[[VARCHAR("NO_ZERO_DATE")]]`) // @@sql_mode did not change 310 311 // check enabling @@enable_system_settings 312 utils.Exec(t, conn, "set enable_system_settings = true") 313 utils.Exec(t, conn, "set sql_mode = ''") // changing @@sql_mode to empty string 314 utils.AssertMatches(t, conn, "select @@sql_mode", `[[VARCHAR("")]]`) // @@sql_mode did change 315 } 316 317 // Tests type consitency through multiple queries 318 func TestSystemVariableType(t *testing.T) { 319 conn, err := mysql.Connect(context.Background(), &vtParams) 320 require.NoError(t, err) 321 defer conn.Close() 322 323 utils.Exec(t, conn, "delete from test") 324 utils.Exec(t, conn, "insert into test (id, val1, val2, val3) values (1, null, 0, 0)") 325 326 // regardless of the "from", the select @@autocommit should return the same type 327 query1 := "select @@autocommit" 328 query2 := "select @@autocommit from test" 329 330 utils.Exec(t, conn, "set autocommit = false") 331 assertResponseMatch(t, conn, query1, query2) 332 333 utils.Exec(t, conn, "set autocommit = true") 334 assertResponseMatch(t, conn, query1, query2) 335 } 336 337 func TestSysvarSocket(t *testing.T) { 338 conn, err := mysql.Connect(context.Background(), &vtParams) 339 require.NoError(t, err) 340 defer conn.Close() 341 342 qr := utils.Exec(t, conn, "select @@socket") 343 assert.Contains(t, fmt.Sprintf("%v", qr.Rows), "mysql.sock") 344 345 _, err = utils.ExecAllowError(t, conn, "set socket = '/any/path'") 346 require.Error(t, err) 347 sqlErr, ok := err.(*mysql.SQLError) 348 require.True(t, ok, "not a mysql error: %T", err) 349 assert.Equal(t, mysql.ERIncorrectGlobalLocalVar, sqlErr.Number()) 350 assert.Equal(t, mysql.SSUnknownSQLState, sqlErr.SQLState()) 351 assert.Equal(t, "VT03010: variable 'socket' is a read only variable (errno 1238) (sqlstate HY000) during query: set socket = '/any/path'", sqlErr.Error()) 352 } 353 354 func TestReservedConnInStreaming(t *testing.T) { 355 356 conn, err := mysql.Connect(context.Background(), &vtParams) 357 require.NoError(t, err) 358 defer conn.Close() 359 utils.Exec(t, conn, "delete from test") 360 361 utils.Exec(t, conn, "set workload = olap") 362 utils.Exec(t, conn, "set sql_safe_updates = 1") 363 utils.Exec(t, conn, "begin") 364 utils.Exec(t, conn, "insert into test (id, val1) values (80, null)") 365 utils.Exec(t, conn, "commit") 366 utils.AssertMatches(t, conn, "select id, val1 from test", "[[INT64(80) NULL]]") 367 utils.AssertMatches(t, conn, "select @@sql_safe_updates", "[[INT64(1)]]") 368 } 369 370 func TestUnifiedOlapAndOltp(t *testing.T) { 371 372 conn, err := mysql.Connect(context.Background(), &vtParams) 373 require.NoError(t, err) 374 defer conn.Close() 375 376 utils.Exec(t, conn, "delete from test") 377 checkOltpAndOlapInterchangingTx(t, conn) 378 379 // modify some system settings to active reserved connection use. 380 utils.Exec(t, conn, "set sql_safe_updates = 1") 381 382 utils.Exec(t, conn, "delete from test") 383 checkOltpAndOlapInterchangingTx(t, conn) 384 } 385 386 func checkOltpAndOlapInterchangingTx(t *testing.T, conn *mysql.Conn) { 387 // start transaction in execute 388 utils.Exec(t, conn, "begin") 389 utils.Exec(t, conn, "insert into test (id, val1) values (80, null)") 390 391 // move to streaming 392 utils.Exec(t, conn, "set workload = olap") 393 394 // checking data in streaming 395 utils.AssertMatches(t, conn, "select id, val1 from test where id = 80", "[[INT64(80) NULL]]") 396 397 // rollback the tx 398 utils.Exec(t, conn, "rollback") 399 utils.AssertMatches(t, conn, "select id, val1 from test where id = 80", "[]") 400 401 // move back to oltp 402 utils.Exec(t, conn, "set workload = oltp") 403 utils.AssertMatches(t, conn, "select id, val1 from test where id = 80", "[]") 404 405 // move to streaming and start transaction 406 utils.Exec(t, conn, "set workload = olap") 407 utils.Exec(t, conn, "begin") 408 utils.Exec(t, conn, "insert into test (id, val1) values (80, null)") 409 410 // checking data in streaming 411 utils.AssertMatches(t, conn, "select id, val1 from test where id = 80", "[[INT64(80) NULL]]") 412 413 // move back to oltp and commit the tx 414 utils.Exec(t, conn, "set workload = oltp") 415 utils.Exec(t, conn, "commit") 416 417 // check in oltp 418 utils.AssertMatches(t, conn, "select id, val1 from test where id = 80", "[[INT64(80) NULL]]") 419 420 // check in olap 421 utils.Exec(t, conn, "set workload = oltp") 422 utils.AssertMatches(t, conn, "select id, val1 from test where id = 80", "[[INT64(80) NULL]]") 423 } 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 }