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;
 | 
						|
}
 |