diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index 784620e4f..608ea3d40 100644 --- a/include/MySQL_Session.h +++ b/include/MySQL_Session.h @@ -225,6 +225,7 @@ class MySQL_Session void Memory_Stats(); void create_new_session_and_reset_connection(MySQL_Data_Stream *_myds); bool handle_command_query_kill(PtrSize_t *); + void finishQuery(MySQL_Data_Stream *myds, MySQL_Connection *myconn, bool); }; #define KILL_QUERY 1 diff --git a/include/mysql_connection.h b/include/mysql_connection.h index 83244451e..fb3b4771d 100644 --- a/include/mysql_connection.h +++ b/include/mysql_connection.h @@ -14,6 +14,7 @@ #define STATUS_MYSQL_CONNECTION_NO_MULTIPLEX 0x00000080 #define STATUS_MYSQL_CONNECTION_SQL_LOG_BIN0 0x00000100 #define STATUS_MYSQL_CONNECTION_FOUND_ROWS 0x00000200 +#define STATUS_MYSQL_CONNECTION_NO_BACKSLASH_ESCAPES 0x00000400 class MySQL_Connection_userinfo { private: @@ -57,6 +58,7 @@ class MySQL_Connection { uint8_t sql_log_bin; int8_t last_set_autocommit; bool autocommit; + bool no_backslash_escapes; } options; struct { unsigned long length; @@ -103,6 +105,7 @@ class MySQL_Connection { MySQL_Connection(); ~MySQL_Connection(); bool set_autocommit(bool); + bool set_no_backslash_escapes(bool); uint8_t set_charset(uint8_t); void set_status_transaction(bool); @@ -110,6 +113,7 @@ class MySQL_Connection { void set_status_get_lock(bool); void set_status_lock_tables(bool); void set_status_temporary_table(bool); + void set_status_no_backslash_escapes(bool); void set_status_prepared_statement(bool); void set_status_user_variable(bool); void set_status_no_multiplex(bool); @@ -120,6 +124,7 @@ class MySQL_Connection { bool get_status_get_lock(); bool get_status_lock_tables(); bool get_status_temporary_table(); + bool get_status_no_backslash_escapes(); bool get_status_prepared_statement(); bool get_status_user_variable(); bool get_status_no_multiplex(); diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 1dfc4d812..cd3f5cac3 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -509,6 +509,11 @@ bool MySQL_Protocol::generate_pkt_EOF(bool send, void **ptr, unsigned int *len, break; } } + if (*myds && (*myds)->myconn) { + if ((*myds)->myconn->options.no_backslash_escapes) { + internal_status += SERVER_STATUS_NO_BACKSLASH_ESCAPES; + } + } memcpy(_ptr+l, &warnings, sizeof(uint16_t)); l+=sizeof(uint16_t); memcpy(_ptr+l, &internal_status, sizeof(uint16_t)); @@ -618,6 +623,11 @@ bool MySQL_Protocol::generate_pkt_OK(bool send, void **ptr, unsigned int *len, u break; } } + if (*myds && (*myds)->myconn) { + if ((*myds)->myconn->options.no_backslash_escapes) { + internal_status += SERVER_STATUS_NO_BACKSLASH_ESCAPES; + } + } memcpy(_ptr+l, &internal_status, sizeof(uint16_t)); l+=sizeof(uint16_t); memcpy(_ptr+l, &warnings, sizeof(uint16_t)); l+=sizeof(uint16_t); if (msg && strlen(msg)) { diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 045c113ff..518747494 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -1727,6 +1727,30 @@ bool MySQL_Session::handler_again___status_SETTING_SQL_MODE(int *_rc) { myds->revents|=POLLOUT; // we also set again POLLOUT to send a query immediately! st=previous_status.top(); previous_status.pop(); + bool nbe = (myconn->mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES); + if (client_myds) { + client_myds->myconn->set_no_backslash_escapes(nbe); + } + if (nbe) { + myconn->set_status_no_backslash_escapes(nbe); + } + if (st == PROCESSING_QUERY) { // only TEXT protocol, no prepared statements + if (client_myds && mirror==false) { + if (CurrentQuery.QueryParserArgs.digest_text) { + // this is not meant to match all the SET SQL_MODE, but just to + // reduce unnecessary SET SQL_MODE when possible + if (strncasecmp(CurrentQuery.QueryParserArgs.digest_text,(char *)"set sql_mode",12)==0) { + unsigned int nTrx=NumActiveTransactions(); + uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); + if (autocommit) setStatus += SERVER_STATUS_AUTOCOMMIT; + client_myds->myprot.generate_pkt_OK(true,NULL,NULL,1,0,0,setStatus,0,NULL); + RequestEnd(myds); + finishQuery(myds,myconn,false); + return ret; + } + } + } + } NEXT_IMMEDIATE_NEW(st); } else { if (rc==-1) { @@ -3129,52 +3153,7 @@ handler_again: } RequestEnd(myds); - myds->myconn->reduce_auto_increment_delay_token(); - if (mysql_thread___multiplexing && (myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { - if (mysql_thread___connection_delay_multiplex_ms && mirror==false) { - myds->wait_until=thread->curtime+mysql_thread___connection_delay_multiplex_ms*1000; - myconn->async_state_machine=ASYNC_IDLE; - myconn->multiplex_delayed=true; - myds->DSS=STATE_MARIADB_GENERIC; - } else if (prepared_stmt_with_no_params==true) { // see issue #1432 - myconn->async_state_machine=ASYNC_IDLE; - myds->DSS=STATE_MARIADB_GENERIC; - myds->wait_until=0; - myconn->multiplex_delayed=false; - } else { - myconn->multiplex_delayed=false; - myds->wait_until=0; - myds->DSS=STATE_NOT_INITIALIZED; - if (mysql_thread___autocommit_false_not_reusable && myds->myconn->IsAutoCommit()==false) { - if (mysql_thread___reset_connection_algorithm == 2) { - create_new_session_and_reset_connection(myds); - } else { - myds->destroy_MySQL_Connection_From_Pool(true); - } - } else { - myds->return_MySQL_Connection_To_Pool(); - } - } - if (transaction_persistent==true) { - transaction_persistent_hostgroup=-1; - } - } else { - myconn->multiplex_delayed=false; - myconn->compute_unknown_transaction_status(); - myconn->async_state_machine=ASYNC_IDLE; - myds->DSS=STATE_MARIADB_GENERIC; - if (transaction_persistent==true) { - if (transaction_persistent_hostgroup==-1) { // change only if not set already, do not allow to change it again - if (myds->myconn->IsActiveTransaction()==true) { // only active transaction is important here. Ignore other criterias - transaction_persistent_hostgroup=current_hostgroup; - } - } else { - if (myds->myconn->IsActiveTransaction()==false) { // a transaction just completed - transaction_persistent_hostgroup=-1; - } - } - } - } + finishQuery(myds,myconn,prepared_stmt_with_no_params); } else { if (rc==-1) { int myerr=mysql_errno(myconn->mysql); @@ -4302,6 +4281,9 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C } proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection SQL Mode to %s\n", value1.c_str()); client_myds->myconn->options.sql_mode=strdup(value1.c_str()); + if (strcasestr(value1.c_str(), (char *)"NO_BACKSLASH_ESCAPES")) { + goto __exit_set_destination_hostgroup; + } } } else if (var == "time_zone") { std::string value1 = *values; @@ -5191,3 +5173,52 @@ void MySQL_Session::add_ldap_comment_to_pkt(PtrSize_t *_pkt) { _pkt->size = _pkt->size + strlen(b); _pkt->ptr = _new_pkt.ptr; } + +void MySQL_Session::finishQuery(MySQL_Data_Stream *myds, MySQL_Connection *myconn, bool prepared_stmt_with_no_params) { + myds->myconn->reduce_auto_increment_delay_token(); + if (mysql_thread___multiplexing && (myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + if (mysql_thread___connection_delay_multiplex_ms && mirror==false) { + myds->wait_until=thread->curtime+mysql_thread___connection_delay_multiplex_ms*1000; + myconn->async_state_machine=ASYNC_IDLE; + myconn->multiplex_delayed=true; + myds->DSS=STATE_MARIADB_GENERIC; + } else if (prepared_stmt_with_no_params==true) { // see issue #1432 + myconn->async_state_machine=ASYNC_IDLE; + myds->DSS=STATE_MARIADB_GENERIC; + myds->wait_until=0; + myconn->multiplex_delayed=false; + } else { + myconn->multiplex_delayed=false; + myds->wait_until=0; + myds->DSS=STATE_NOT_INITIALIZED; + if (mysql_thread___autocommit_false_not_reusable && myds->myconn->IsAutoCommit()==false) { + if (mysql_thread___reset_connection_algorithm == 2) { + create_new_session_and_reset_connection(myds); + } else { + myds->destroy_MySQL_Connection_From_Pool(true); + } + } else { + myds->return_MySQL_Connection_To_Pool(); + } + } + if (transaction_persistent==true) { + transaction_persistent_hostgroup=-1; + } + } else { + myconn->multiplex_delayed=false; + myconn->compute_unknown_transaction_status(); + myconn->async_state_machine=ASYNC_IDLE; + myds->DSS=STATE_MARIADB_GENERIC; + if (transaction_persistent==true) { + if (transaction_persistent_hostgroup==-1) { // change only if not set already, do not allow to change it again + if (myds->myconn->IsActiveTransaction()==true) { // only active transaction is important here. Ignore other criterias + transaction_persistent_hostgroup=current_hostgroup; + } + } else { + if (myds->myconn->IsActiveTransaction()==false) { // a transaction just completed + transaction_persistent_hostgroup=-1; + } + } + } + } +} diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 0ed4ffdd3..02f2556db 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -202,6 +202,7 @@ MySQL_Connection::MySQL_Connection() { options.server_version=NULL; options.last_set_autocommit=-1; // -1 = never set options.autocommit=true; + options.no_backslash_escapes=false; options.init_connect=NULL; options.init_connect_sent=false; options.ldap_user_variable=NULL; @@ -279,6 +280,12 @@ bool MySQL_Connection::set_autocommit(bool _ac) { return _ac; } +bool MySQL_Connection::set_no_backslash_escapes(bool _ac) { + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "Setting no_backslash_escapes %d\n", _ac); + options.no_backslash_escapes=_ac; + return _ac; +} + uint8_t MySQL_Connection::set_charset(uint8_t _c) { proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "Setting charset %d\n", _c); options.charset=_c; @@ -339,6 +346,14 @@ void MySQL_Connection::set_status_temporary_table(bool v) { } } +void MySQL_Connection::set_status_no_backslash_escapes(bool v) { + if (v) { + status_flags |= STATUS_MYSQL_CONNECTION_NO_BACKSLASH_ESCAPES; + } else { + status_flags &= ~STATUS_MYSQL_CONNECTION_NO_BACKSLASH_ESCAPES; + } +} + void MySQL_Connection::set_status_user_variable(bool v) { if (v) { status_flags |= STATUS_MYSQL_CONNECTION_USER_VARIABLE; @@ -402,6 +417,10 @@ bool MySQL_Connection::get_status_temporary_table() { return status_flags & STATUS_MYSQL_CONNECTION_TEMPORARY_TABLE; } +bool MySQL_Connection::get_status_no_backslash_escapes() { + return status_flags & STATUS_MYSQL_CONNECTION_NO_BACKSLASH_ESCAPES; +} + bool MySQL_Connection::get_status_prepared_statement() { return status_flags & STATUS_MYSQL_CONNECTION_PREPARED_STATEMENT; } @@ -1631,7 +1650,7 @@ bool MySQL_Connection::MultiplexDisabled() { // status_flags stores information about the status of the connection // can be used to determine if multiplexing can be enabled or not bool ret=false; - if (status_flags & (STATUS_MYSQL_CONNECTION_TRANSACTION|STATUS_MYSQL_CONNECTION_USER_VARIABLE|STATUS_MYSQL_CONNECTION_PREPARED_STATEMENT|STATUS_MYSQL_CONNECTION_LOCK_TABLES|STATUS_MYSQL_CONNECTION_TEMPORARY_TABLE|STATUS_MYSQL_CONNECTION_GET_LOCK|STATUS_MYSQL_CONNECTION_NO_MULTIPLEX|STATUS_MYSQL_CONNECTION_SQL_LOG_BIN0|STATUS_MYSQL_CONNECTION_FOUND_ROWS) ) { + if (status_flags & (STATUS_MYSQL_CONNECTION_TRANSACTION|STATUS_MYSQL_CONNECTION_USER_VARIABLE|STATUS_MYSQL_CONNECTION_PREPARED_STATEMENT|STATUS_MYSQL_CONNECTION_LOCK_TABLES|STATUS_MYSQL_CONNECTION_TEMPORARY_TABLE|STATUS_MYSQL_CONNECTION_GET_LOCK|STATUS_MYSQL_CONNECTION_NO_MULTIPLEX|STATUS_MYSQL_CONNECTION_SQL_LOG_BIN0|STATUS_MYSQL_CONNECTION_FOUND_ROWS|STATUS_MYSQL_CONNECTION_NO_BACKSLASH_ESCAPES) ) { ret=true; } if (auto_increment_delay_token) return true;