102 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			102 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /**
 | |
|  * @file bug694.cpp  - regression test for bug694 ("RWSplit: SELECT @a:=@a+1 as a, test.b FROM test breaks
 | |
|  * client session")
 | |
|  *
 | |
|  * - set use_sql_variables_in=all in MaxScale.cnf
 | |
|  * - connect to readwritesplit router and execute:
 | |
|  * @verbatim
 | |
|  *  CREATE TABLE test (b integer);
 | |
|  *  SELECT @a:=@a+1 as a, test.b FROM test;
 | |
|  *  USE test
 | |
|  *  @endverbatim
 | |
|  * - check if MaxScale alive
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  *
 | |
|  *  Description Vilho Raatikka 2015-01-14 08:09:45 UTC
 | |
|  *  Reproduce:
 | |
|  *  - set use_sql_variables_in=all in MaxScale.cnf
 | |
|  *  - connect to readwritesplit router and execute:
 | |
|  *  CREATE TABLE test (b integer);
 | |
|  *  SELECT @a:=@a+1 as a, test.b FROM test;
 | |
|  *  USE test
 | |
|  *
 | |
|  *  You'll get:
 | |
|  *  ERROR 2006 (HY000): MySQL server has gone away
 | |
|  *
 | |
|  *  It is a known limitation that SELÈCTs with SQL variable modifications are not supported. The issue is that
 | |
|  * they aren't detected and as a consequence hte client session is disconnected.
 | |
|  *
 | |
|  *  It is possible to detect this kind of query in query classifier, but set_query_type loses part of the
 | |
|  * information. If both SELECT and SQL variable update are detected they can be stored in query type and
 | |
|  * rwsplit could, for example, prevent from executing the query, execute it in master only (and log), force
 | |
|  * all SQL variable modifications from that point to master (and log), etc.
 | |
|  *  Comment 1 Vilho Raatikka 2015-01-15 13:19:18 UTC
 | |
|  *  query_classifier.cc: set_query_type lost previous query type if the new was more restrictive. Problem was
 | |
|  * that if query is both READ and SESSION_WRITE and configuration parameter use_sql_variables_in=all was set,
 | |
|  * routing target became ambiguous. Replaced call to set_query_type with simply adding new type to type (=bit
 | |
|  * field) and checking unsupported combinations in readwritesplit.c:get_route_target. If such a case is met, a
 | |
|  * detailed error is written to error log in readwritesplit.c. mysql_client.c sees the error code and sends an
 | |
|  * error to client. Then mysql_client.c calls router's handleError which ensures that there are enough backend
 | |
|  * servers so that the session can continue.
 | |
|  */
 | |
| 
 | |
| 
 | |
| #include <iostream>
 | |
| #include "testconnections.h"
 | |
| #include "mariadb_func.h"
 | |
| 
 | |
| int main(int argc, char* argv[])
 | |
| {
 | |
|     TestConnections* Test = new TestConnections(argc, argv);
 | |
| 
 | |
|     Test->set_timeout(120);
 | |
|     Test->maxscales->connect_maxscale(0);
 | |
| 
 | |
|     Test->try_query(Test->maxscales->conn_rwsplit[0], "USE test");
 | |
|     Test->try_query(Test->maxscales->conn_rwsplit[0], "DROP TABLE IF EXISTS test");
 | |
|     Test->try_query(Test->maxscales->conn_rwsplit[0], "CREATE TABLE test (b integer)");
 | |
| 
 | |
|     const int iter = Test->smoke ? 10 : 100;
 | |
|     Test->tprintf("Creating and inserting %d rows into a table\n", iter);
 | |
| 
 | |
|     for (int i = 0; i < iter; i++)
 | |
|     {
 | |
|         Test->set_timeout(30);
 | |
|         execute_query(Test->maxscales->conn_rwsplit[0], "insert into test value(2);");
 | |
|         Test->stop_timeout();
 | |
|     }
 | |
| 
 | |
|     Test->set_timeout(200);
 | |
| 
 | |
|     Test->tprintf("Trying SELECT @a:=@a+1 as a, test.b FROM test\n");
 | |
|     if (execute_query(Test->maxscales->conn_rwsplit[0], "SELECT @a:=@a+1 as a, test.b FROM test;") == 0)
 | |
|     {
 | |
|         Test->add_result(1, "Query succeded, but expected to fail.\n");
 | |
|     }
 | |
|     Test->tprintf("Trying USE test\n");
 | |
|     Test->try_query(Test->maxscales->conn_rwsplit[0], "USE test");
 | |
| 
 | |
|     Test->try_query(Test->maxscales->conn_rwsplit[0], "DROP TABLE IF EXISTS test");
 | |
| 
 | |
|     Test->tprintf("Checking if MaxScale alive\n");
 | |
|     Test->maxscales->close_maxscale_connections(0);
 | |
| 
 | |
|     Test->tprintf("Checking logs\n");
 | |
|     Test->check_log_err(0,
 | |
|                         (char*)
 | |
|                         "The query can't be routed to all backend servers because it includes SELECT and SQL variable modifications which is not supported",
 | |
|                         true);
 | |
|     Test->check_log_err(0,
 | |
|                         (char*)
 | |
|                         "SELECT with session data modification is not supported if configuration parameter use_sql_variables_in=all",
 | |
|                         true);
 | |
| 
 | |
|     Test->check_maxscale_alive(0);
 | |
| 
 | |
|     int rval = Test->global_result;
 | |
|     delete Test;
 | |
|     return rval;
 | |
| }
 | 
