diff --git a/Makefile b/Makefile index 35da72e50..546286f3e 100644 --- a/Makefile +++ b/Makefile @@ -146,7 +146,7 @@ build_tap_test: build_src cd test/tap && OPTZ="${O0} -ggdb -DDEBUG" CC=${CC} CXX=${CXX} ${MAKE} .PHONY: build_tap_test_debug -build_tap_test_debug: build_src +build_tap_test_debug: build_src_debug cd test/tap && OPTZ="${O0} -ggdb -DDEBUG" CC=${CC} CXX=${CXX} ${MAKE} debug .PHONY: build_src_debug diff --git a/include/Client_Session.h b/include/Client_Session.h index e1b6d4002..d1adf5229 100644 --- a/include/Client_Session.h +++ b/include/Client_Session.h @@ -74,14 +74,15 @@ class Query_Info { class Client_Session { friend class MySQL_Session; + friend class Admin_MySQL_Session; private: //int handler_ret; - void handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(PtrSize_t *, bool *); +// void handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(PtrSize_t *, bool *); - void handler___status_CHANGING_USER_CLIENT___STATE_CLIENT_HANDSHAKE(PtrSize_t *, bool *); +// void handler___status_CHANGING_USER_CLIENT___STATE_CLIENT_HANDSHAKE(PtrSize_t *, bool *); // void handler_WCDSS_MYSQL_COM_FIELD_LIST(PtrSize_t *); - void handler_WCDSS_MYSQL_COM_INIT_DB(PtrSize_t *); +// void handler_WCDSS_MYSQL_COM_INIT_DB(PtrSize_t *); /** * @brief Handles 'COM_QUERIES' holding 'USE DB' statements. * @@ -93,7 +94,7 @@ class Client_Session * But since it was change for handling 'USE' statements which are preceded by * comments, it's called after 'QueryProcessor' has processed the query. */ - void handler_WCDSS_MYSQL_COM_QUERY_USE_DB(PtrSize_t *pkt); +// void handler_WCDSS_MYSQL_COM_QUERY_USE_DB(PtrSize_t *pkt); // void handler_WCDSS_MYSQL_COM_PING(PtrSize_t *); // void handler_WCDSS_MYSQL_COM_CHANGE_USER(PtrSize_t *, bool *); @@ -113,47 +114,51 @@ class Client_Session */ // void handler_WCDSS_MYSQL_COM_RESET_CONNECTION(PtrSize_t *pkt); // void handler_WCDSS_MYSQL_COM_SET_OPTION(PtrSize_t *); - void handler_WCDSS_MYSQL_COM_STATISTICS(PtrSize_t *); +// void handler_WCDSS_MYSQL_COM_STATISTICS(PtrSize_t *); // void handler_WCDSS_MYSQL_COM_PROCESS_KILL(PtrSize_t *); - bool handler_WCDSS_MYSQL_COM_QUERY_qpo(PtrSize_t *, bool *lock_hostgroup, bool ps=false); +// bool handler_WCDSS_MYSQL_COM_QUERY_qpo(PtrSize_t *, bool *lock_hostgroup, bool ps=false); void handler___client_DSS_QUERY_SENT___server_DSS_NOT_INITIALIZED__get_mysql_connection(); void return_proxysql_internal(PtrSize_t *); +/* bool handler_special_queries(PtrSize_t *); bool handler_CommitRollback(PtrSize_t *); bool handler_SetAutocommit(PtrSize_t *); +*/ /** * @brief Performs the cleanup of current session state, and the required operations to the supplied * 'ProxySQL_Data_Stream' required for processing further queries. * @param The 'ProxySQL_Data_Stream' which executed the previous query and which status should be updated. */ - void RequestEnd_mysql(ProxySQL_Data_Stream *); +// void RequestEnd_mysql(ProxySQL_Data_Stream *); void LogQuery(ProxySQL_Data_Stream *); +/* void handler_WCDSS_MYSQL_COM_QUERY___create_mirror_session(); int handler_again___status_PINGING_SERVER(); int handler_again___status_RESETTING_CONNECTION(); +*/ void handler_again___new_thread_to_kill_connection(); - bool handler_again___verify_init_connect(); - bool handler_again___verify_ldap_user_variable(); - bool handler_again___verify_backend_autocommit(); - bool handler_again___verify_backend_session_track_gtids(); - bool handler_again___verify_backend_multi_statement(); - bool handler_again___verify_backend_user_schema(); - bool handler_again___status_SETTING_INIT_CONNECT(int *); - bool handler_again___status_SETTING_LDAP_USER_VARIABLE(int *); +// bool handler_again___verify_init_connect(); +// bool handler_again___verify_ldap_user_variable(); +// bool handler_again___verify_backend_autocommit(); +// bool handler_again___verify_backend_session_track_gtids(); +// bool handler_again___verify_backend_multi_statement(); +// bool handler_again___verify_backend_user_schema(); +// bool handler_again___status_SETTING_INIT_CONNECT(int *); +// bool handler_again___status_SETTING_LDAP_USER_VARIABLE(int *); bool handler_again___status_SETTING_SQL_MODE(int *); - bool handler_again___status_SETTING_SESSION_TRACK_GTIDS(int *); - bool handler_again___status_CHANGING_CHARSET(int *_rc); - bool handler_again___status_CHANGING_SCHEMA(int *); +// bool handler_again___status_SETTING_SESSION_TRACK_GTIDS(int *); +// bool handler_again___status_CHANGING_CHARSET(int *_rc); +// bool handler_again___status_CHANGING_SCHEMA(int *); bool handler_again___status_CONNECTING_SERVER(int *); bool handler_again___status_CHANGING_USER_SERVER(int *); - bool handler_again___status_CHANGING_AUTOCOMMIT(int *); - bool handler_again___status_SETTING_MULTI_STMT(int *_rc); - bool handler_again___multiple_statuses(int *rc); - void add_ldap_comment_to_pkt(PtrSize_t *); +// bool handler_again___status_CHANGING_AUTOCOMMIT(int *); +// bool handler_again___status_SETTING_MULTI_STMT(int *_rc); +// bool handler_again___multiple_statuses(int *rc); +// void add_ldap_comment_to_pkt(PtrSize_t *); int get_pkts_from_client(bool&, PtrSize_t&); /* @@ -170,29 +175,29 @@ class Client_Session /* bool handler_rc0_PROCESSING_STMT_PREPARE(enum session_status& st, ProxySQL_Data_Stream *myds, bool& prepared_stmt_with_no_params); void handler_rc0_PROCESSING_STMT_EXECUTE(ProxySQL_Data_Stream *myds); -*/ bool handler_minus1_ClientLibraryError(ProxySQL_Data_Stream *myds, int myerr, char **errmsg); void handler_minus1_LogErrorDuringQuery(MySQL_Connection *myconn, int myerr, char *errmsg); bool handler_minus1_HandleErrorCodes(ProxySQL_Data_Stream *myds, int myerr, char **errmsg, int& handler_ret); void handler_minus1_GenerateErrorMessage(ProxySQL_Data_Stream *myds, MySQL_Connection *myconn, bool& wrong_pass); void handler_minus1_HandleBackendConnection(ProxySQL_Data_Stream *myds, MySQL_Connection *myconn); +*/ int RunQuery_mysql(ProxySQL_Data_Stream *myds, MySQL_Connection *myconn); void handler___status_WAITING_CLIENT_DATA(); void handler_rc0_Process_GTID(MySQL_Connection *myconn); void handler_WCDSS_MYSQL_COM_INIT_DB_replace_CLICKHOUSE(PtrSize_t& pkt); void handler_WCDSS_MYSQL_COM_QUERY___not_mysql(PtrSize_t& pkt); - bool handler_WCDSS_MYSQL_COM_QUERY_detect_SQLi(); - bool handler_WCDSS_MULTI_PACKET(PtrSize_t& pkt); - bool handler_WCDSS_MYSQL_COM__various(PtrSize_t* pkt, bool* wrong_pass); - void handler___status_WAITING_CLIENT_DATA___default(); +// bool handler_WCDSS_MYSQL_COM_QUERY_detect_SQLi(); +// bool handler_WCDSS_MULTI_PACKET(PtrSize_t& pkt); +// bool handler_WCDSS_MYSQL_COM__various(PtrSize_t* pkt, bool* wrong_pass); +// void handler___status_WAITING_CLIENT_DATA___default(); void handler___status_NONE_or_default(PtrSize_t& pkt); void handler_WCDSS_MYSQL_COM_QUERY_inner1(PtrSize_t&); - void handler_WCD_SS_MCQ_qpo_QueryRewrite(PtrSize_t *pkt); - void handler_WCD_SS_MCQ_qpo_OK_msg(PtrSize_t *pkt); - void handler_WCD_SS_MCQ_qpo_error_msg(PtrSize_t *pkt); - void handler_WCD_SS_MCQ_qpo_LargePacket(PtrSize_t *pkt); +// void handler_WCD_SS_MCQ_qpo_QueryRewrite(PtrSize_t *pkt); +// void handler_WCD_SS_MCQ_qpo_OK_msg(PtrSize_t *pkt); +// void handler_WCD_SS_MCQ_qpo_error_msg(PtrSize_t *pkt); +// void handler_WCD_SS_MCQ_qpo_LargePacket(PtrSize_t *pkt); // int handler_WCD_SS_MCQ_qpo_Parse_SQL_LOG_BIN(PtrSize_t *pkt, bool *lock_hostgroup, unsigned int nTrx, string& nq); protected: @@ -200,8 +205,8 @@ class Client_Session void reset(); public: - bool handler_again___status_SETTING_GENERIC_VARIABLE(int *_rc, const char *var_name, const char *var_value, bool no_quote=false, bool set_transaction=false); - bool handler_again___status_SETTING_SQL_LOG_BIN(int *); +// bool handler_again___status_SETTING_GENERIC_VARIABLE(int *_rc, const char *var_name, const char *var_value, bool no_quote=false, bool set_transaction=false); +// bool handler_again___status_SETTING_SQL_LOG_BIN(int *); std::stack previous_status; void * operator new(size_t); void operator delete(void *); @@ -222,7 +227,7 @@ class Client_Session StatCounters *command_counters; MySQL_Backend *mybe; PtrArray *mybes; - ProxySQL_Data_Stream *client_myds; +// ProxySQL_Data_Stream *client_myds; char * default_schema; char * user_attributes; @@ -314,8 +319,8 @@ class Client_Session MySQL_Backend * find_or_create_mysql_backend(int, ProxySQL_Data_Stream *_myds=NULL); void SQLite3_to_MySQL(SQLite3_result *, char *, int , MySQL_Protocol *, bool in_transaction=false, bool deprecate_eof_active=false); - void MySQL_Result_to_MySQL_wire(MYSQL *mysql, MySQL_ResultSet *MyRS, ProxySQL_Data_Stream *_myds=NULL); - void MySQL_Stmt_Result_to_MySQL_wire(MYSQL_STMT *stmt, MySQL_Connection *myconn); +// void MySQL_Result_to_MySQL_wire(MYSQL *mysql, MySQL_ResultSet *MyRS, ProxySQL_Data_Stream *_myds=NULL); +// void MySQL_Stmt_Result_to_MySQL_wire(MYSQL_STMT *stmt, MySQL_Connection *myconn); unsigned int NumActiveTransactions(); bool HasOfflineBackends(); bool SetEventInOfflineBackends(); @@ -325,7 +330,7 @@ class Client_Session void reset_all_mysql_backends(); void writeout(); void Memory_Stats(); - void create_new_session_and_reset_mysql_connection(ProxySQL_Data_Stream *_myds); +// void create_new_session_and_reset_mysql_connection(ProxySQL_Data_Stream *_myds); bool handle_command_query_kill(PtrSize_t *); /** * @brief Performs the final operations after current query has finished to be executed. It updates the session diff --git a/include/MySQL_Data_Stream.h b/include/MySQL_Data_Stream.h new file mode 100644 index 000000000..66507b774 --- /dev/null +++ b/include/MySQL_Data_Stream.h @@ -0,0 +1,209 @@ +#ifndef __CLASS_MYSQL_DATA_STREAM_H +#define __CLASS_MYSQL_DATA_STREAM_H + +#include "proxysql.h" +#include "cpp.h" + +#include "MySQL_Protocol.h" + +/* +#define QUEUE_T_DEFAULT_SIZE 32768 +#define MY_SSL_BUFFER 8192 +*/ +// this class avoid copying data +class MyDS_real_query { + public: + PtrSize_t pkt; // packet coming from the client + char *QueryPtr; // pointer to beginning of the query + unsigned int QuerySize; // size of the query + void init(PtrSize_t *_pkt) { + pkt.ptr=_pkt->ptr; + pkt.size=_pkt->size; + QueryPtr=(char *)pkt.ptr+5; + QuerySize=pkt.size-5; + } + void end() { + l_free(pkt.size,pkt.ptr); + pkt.size=0; + QuerySize=0; + pkt.ptr=NULL; + QueryPtr=NULL; + } +}; + +/* +enum sslstatus { SSLSTATUS_OK, SSLSTATUS_WANT_IO, SSLSTATUS_FAIL}; +*/ +class MySQL_Data_Stream: public ProxySQL_Data_Stream +{ + private: + int array2buffer(); + int buffer2array(); + void generate_compressed_packet(); + enum sslstatus do_ssl_handshake(); + void queue_encrypted_bytes(const char *buf, size_t len); + + public: + +/* + queue_t queueIN; + uint64_t pkts_recv; // counter of received packets + queue_t queueOUT; + uint64_t pkts_sent; // counter of sent packets +*/ + struct { + PtrSize_t pkt; + unsigned int partial; + } CompPktIN; + struct { + PtrSize_t pkt; + unsigned int partial; + } CompPktOUT; + + MySQL_Protocol myprot; + MyDS_real_query mysql_real_query; +/* + bytes_stats_t bytes_info; // bytes statistics + + PtrSize_t multi_pkt; + + unsigned long long pause_until; + unsigned long long wait_until; + unsigned long long killed_at; + unsigned long long max_connect_time; + + struct { + unsigned long long questions; + unsigned long long myconnpoll_get; + unsigned long long myconnpoll_put; + } statuses; + + PtrSizeArray *PSarrayIN; + PtrSizeArray *PSarrayOUT; + //PtrSizeArray *PSarrayOUTpending; + PtrSizeArray *resultset; + unsigned int resultset_length; + + ProxySQL_Poll *mypolls; + //int listener; +*/ + MySQL_Connection *myconn; +/* + Client_Session *sess; // pointer to the session using this data stream +*/ + MySQL_Backend *mybe; // if this is a connection to a mysql server, this points to a backend structure +/* + char *x509_subject_alt_name; + SSL *ssl; + BIO *rbio_ssl; + BIO *wbio_ssl; + char *ssl_write_buf; + size_t ssl_write_len; + struct sockaddr *client_addr; + + struct { + char *addr; + int port; + } addr; + struct { + char *addr; + int port; + } proxy_addr; + + unsigned int connect_tries; + int query_retries_on_failure; + int connect_retries_on_failure; + enum data_stream_status DSS; + enum MySQL_DS_type myds_type; + + socklen_t client_addrlen; + + int fd; // file descriptor + int poll_fds_idx; + + + int active_transaction; // 1 if there is an active transaction + int active; // data stream is active. If not, shutdown+close needs to be called + int status; // status . FIXME: make it a ORable variable + + int switching_auth_stage; + int switching_auth_type; + unsigned int tmp_charset; + + short revents; + + char kill_type; + + bool encrypted; + bool net_failure; + + uint8_t pkt_sid; +*/ + + bool com_field_list; + char *com_field_wild; + MySQL_Data_Stream(); + ~MySQL_Data_Stream(); + int array2buffer_full(); + void init(); // initialize the data stream + void init(enum MySQL_DS_type, Client_Session *, int); // initialize with arguments +/* + void shut_soft(); + void shut_hard(); +*/ + int read_from_net(); + int write_to_net(); + int write_to_net_poll(); +/* + bool available_data_out(); + void remove_pollout(); +*/ + void set_pollout(); + void mysql_free(); +/* + void set_net_failure(); + void setDSS_STATE_QUERY_SENT_NET(); + + void setDSS(enum data_stream_status dss) { + DSS=dss; + } +*/ + int read_pkts(); + int write_pkts(); + + void unplug_backend(); + void check_data_flow(); + int assign_fd_from_mysql_conn(); + + unsigned char * resultset2buffer(bool); + void buffer2resultset(unsigned char *, unsigned int); + + // safe way to attach a MySQL Connection + void attach_connection(MySQL_Connection *mc) { + statuses.myconnpoll_get++; + myconn=mc; + myconn->statuses.myconnpoll_get++; + mc->myds=this; + } + + // safe way to detach a MySQL Connection + void detach_connection() { + assert(myconn); + myconn->statuses.myconnpoll_put++; + statuses.myconnpoll_put++; + myconn->myds=NULL; + myconn=NULL; + } + + void return_MySQL_Connection_To_Pool(); + + void destroy_MySQL_Connection_From_Pool(bool sq); + void free_mysql_real_query(); +/* + void reinit_queues(); + void destroy_queues(); + + bool data_in_rbio(); +*/ +}; +#endif /* __CLASS_MYSQL_DATA_STREAM_H */ diff --git a/include/MySQL_Protocol.h b/include/MySQL_Protocol.h index e580fa087..1792577cc 100644 --- a/include/MySQL_Protocol.h +++ b/include/MySQL_Protocol.h @@ -24,7 +24,7 @@ class MySQL_ResultSet { bool resultset_completed; //bool reset_pid; uint8_t sid; - ProxySQL_Data_Stream *myds; + MySQL_Data_Stream *myds; MySQL_Protocol *myprot; MYSQL *mysql; MYSQL_RES *result; @@ -53,7 +53,7 @@ class MySQL_ResultSet { unsigned int add_row2(MYSQL_ROWS *row, unsigned char *offset); void add_eof(); void remove_last_eof(); - void add_err(ProxySQL_Data_Stream *_myds); + void add_err(MySQL_Data_Stream *myds); bool get_resultset(PtrSizeArray *PSarrayFinal); //bool generate_COM_FIELD_LIST_response(PtrSizeArray *PSarrayFinal); unsigned char *buffer; @@ -96,17 +96,17 @@ class MySQL_Protocol { MySQL_Connection_userinfo *userinfo; Client_Session *sess; public: - ProxySQL_Data_Stream **myds; + MySQL_Data_Stream **myds; #ifdef DEBUG bool dump_pkt; #endif MySQL_Prepared_Stmt_info *current_PreStmt; uint16_t prot_status; - ProxySQL_Data_Stream *get_myds() { return *myds; } + MySQL_Data_Stream *get_myds() { return *myds; } MySQL_Protocol() { prot_status=0; } - void init(ProxySQL_Data_Stream **, MySQL_Connection_userinfo *, Client_Session *); + void init(MySQL_Data_Stream **, MySQL_Connection_userinfo *, Client_Session *); // members get as arguments: // - a data stream (optionally NULL for some) diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index a7ad31541..37e0dc58f 100644 --- a/include/MySQL_Session.h +++ b/include/MySQL_Session.h @@ -76,19 +76,16 @@ class Query_Info { class MySQL_Session: public Client_Session { friend class Client_Session; + friend class Admin_MySQL_Session; friend class Query_Info; friend class MySQL_Protocol; private: -/* //int handler_ret; void handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(PtrSize_t *, bool *); void handler___status_CHANGING_USER_CLIENT___STATE_CLIENT_HANDSHAKE(PtrSize_t *, bool *); -*/ void handler_WCDSS_MYSQL_COM_FIELD_LIST(PtrSize_t *); -/* void handler_WCDSS_MYSQL_COM_INIT_DB(PtrSize_t *); -*/ /** * @brief Handles 'COM_QUERIES' holding 'USE DB' statements. * @@ -100,9 +97,7 @@ class MySQL_Session: public Client_Session * But since it was change for handling 'USE' statements which are preceded by * comments, it's called after 'QueryProcessor' has processed the query. */ -/* void handler_WCDSS_MYSQL_COM_QUERY_USE_DB(PtrSize_t *pkt); -*/ void handler_WCDSS_MYSQL_COM_PING(PtrSize_t *); void handler_WCDSS_MYSQL_COM_CHANGE_USER(PtrSize_t *, bool *); /** @@ -121,33 +116,32 @@ class MySQL_Session: public Client_Session */ void handler_WCDSS_MYSQL_COM_RESET_CONNECTION(PtrSize_t *pkt); void handler_WCDSS_MYSQL_COM_SET_OPTION(PtrSize_t *); -/* void handler_WCDSS_MYSQL_COM_STATISTICS(PtrSize_t *); -*/ void handler_WCDSS_MYSQL_COM_PROCESS_KILL(PtrSize_t *); -/* bool handler_WCDSS_MYSQL_COM_QUERY_qpo(PtrSize_t *, bool *lock_hostgroup, bool ps=false); - +/* void handler___client_DSS_QUERY_SENT___server_DSS_NOT_INITIALIZED__get_mysql_connection(); void return_proxysql_internal(PtrSize_t *); +*/ bool handler_special_queries(PtrSize_t *); bool handler_CommitRollback(PtrSize_t *); bool handler_SetAutocommit(PtrSize_t *); -*/ /** * @brief Performs the cleanup of current session state, and the required operations to the supplied * 'ProxySQL_Data_Stream' required for processing further queries. * @param The 'ProxySQL_Data_Stream' which executed the previous query and which status should be updated. */ -/* void RequestEnd_mysql(ProxySQL_Data_Stream *); +/* void LogQuery(ProxySQL_Data_Stream *); - +*/ void handler_WCDSS_MYSQL_COM_QUERY___create_mirror_session(); int handler_again___status_PINGING_SERVER(); int handler_again___status_RESETTING_CONNECTION(); +/* void handler_again___new_thread_to_kill_connection(); +*/ bool handler_again___verify_init_connect(); bool handler_again___verify_ldap_user_variable(); @@ -157,21 +151,23 @@ class MySQL_Session: public Client_Session bool handler_again___verify_backend_user_schema(); bool handler_again___status_SETTING_INIT_CONNECT(int *); bool handler_again___status_SETTING_LDAP_USER_VARIABLE(int *); +/* bool handler_again___status_SETTING_SQL_MODE(int *); +*/ bool handler_again___status_SETTING_SESSION_TRACK_GTIDS(int *); bool handler_again___status_CHANGING_CHARSET(int *_rc); bool handler_again___status_CHANGING_SCHEMA(int *); +/* bool handler_again___status_CONNECTING_SERVER(int *); bool handler_again___status_CHANGING_USER_SERVER(int *); +*/ bool handler_again___status_CHANGING_AUTOCOMMIT(int *); bool handler_again___status_SETTING_MULTI_STMT(int *_rc); bool handler_again___multiple_statuses(int *rc); -*/ void mysql_session_init(); void mysql_session_reset(); -/* void add_ldap_comment_to_pkt(PtrSize_t *); - +/* int get_pkts_from_client(bool&, PtrSize_t&); */ void handler_WCDSS_MYSQL_COM_STMT_RESET(PtrSize_t&); @@ -186,34 +182,39 @@ class MySQL_Session: public Client_Session */ bool handler_rc0_PROCESSING_STMT_PREPARE(enum session_status& st, ProxySQL_Data_Stream *myds, bool& prepared_stmt_with_no_params); void handler_rc0_PROCESSING_STMT_EXECUTE(ProxySQL_Data_Stream *myds); -/* bool handler_minus1_ClientLibraryError(ProxySQL_Data_Stream *myds, int myerr, char **errmsg); void handler_minus1_LogErrorDuringQuery(MySQL_Connection *myconn, int myerr, char *errmsg); bool handler_minus1_HandleErrorCodes(ProxySQL_Data_Stream *myds, int myerr, char **errmsg, int& handler_ret); void handler_minus1_GenerateErrorMessage(ProxySQL_Data_Stream *myds, MySQL_Connection *myconn, bool& wrong_pass); void handler_minus1_HandleBackendConnection(ProxySQL_Data_Stream *myds, MySQL_Connection *myconn); +/* int RunQuery_mysql(ProxySQL_Data_Stream *myds, MySQL_Connection *myconn); +*/ void handler___status_WAITING_CLIENT_DATA(); +/* void handler_rc0_Process_GTID(MySQL_Connection *myconn); void handler_WCDSS_MYSQL_COM_INIT_DB_replace_CLICKHOUSE(PtrSize_t& pkt); void handler_WCDSS_MYSQL_COM_QUERY___not_mysql(PtrSize_t& pkt); +*/ bool handler_WCDSS_MYSQL_COM_QUERY_detect_SQLi(); bool handler_WCDSS_MULTI_PACKET(PtrSize_t& pkt); bool handler_WCDSS_MYSQL_COM__various(PtrSize_t* pkt, bool* wrong_pass); void handler___status_WAITING_CLIENT_DATA___default(); void handler___status_NONE_or_default(PtrSize_t& pkt); - +/* void handler_WCDSS_MYSQL_COM_QUERY_inner1(PtrSize_t&); - +*/ void handler_WCD_SS_MCQ_qpo_QueryRewrite(PtrSize_t *pkt); void handler_WCD_SS_MCQ_qpo_OK_msg(PtrSize_t *pkt); void handler_WCD_SS_MCQ_qpo_error_msg(PtrSize_t *pkt); void handler_WCD_SS_MCQ_qpo_LargePacket(PtrSize_t *pkt); // int handler_WCD_SS_MCQ_qpo_Parse_SQL_LOG_BIN(PtrSize_t *pkt, bool *lock_hostgroup, unsigned int nTrx, string& nq); - public: + MySQL_Session(); + ~MySQL_Session(); bool handler_again___status_SETTING_GENERIC_VARIABLE(int *_rc, const char *var_name, const char *var_value, bool no_quote=false, bool set_transaction=false); bool handler_again___status_SETTING_SQL_LOG_BIN(int *); +/* std::stack previous_status; void * operator new(size_t); void operator delete(void *); @@ -234,7 +235,9 @@ class MySQL_Session: public Client_Session StatCounters *command_counters; MySQL_Backend *mybe; PtrArray *mybes; - ProxySQL_Data_Stream *client_myds; +*/ + MySQL_Data_Stream *client_myds; +/* char * default_schema; char * user_attributes; @@ -330,8 +333,10 @@ class MySQL_Session: public Client_Session MySQL_Backend * find_or_create_mysql_backend(int, ProxySQL_Data_Stream *_myds=NULL); void SQLite3_to_MySQL(SQLite3_result *, char *, int , MySQL_Protocol *, bool in_transaction=false, bool deprecate_eof_active=false); +*/ void MySQL_Result_to_MySQL_wire(MYSQL *mysql, MySQL_ResultSet *MyRS, ProxySQL_Data_Stream *_myds=NULL); void MySQL_Stmt_Result_to_MySQL_wire(MYSQL_STMT *stmt, MySQL_Connection *myconn); +/* unsigned int NumActiveTransactions(); bool HasOfflineBackends(); bool SetEventInOfflineBackends(); @@ -339,9 +344,13 @@ class MySQL_Session: public Client_Session unsigned long long IdleTime(); void reset_all_mysql_backends(); +*/ void writeout(); +/* void Memory_Stats(); - void create_new_session_and_reset_mysql_connection(ProxySQL_Data_Stream *_myds); +*/ + void create_new_session_and_reset_mysql_connection(MySQL_Data_Stream *_myds); +/* bool handle_command_query_kill(PtrSize_t *); */ /** @@ -365,6 +374,13 @@ class MySQL_Session: public Client_Session void detected_broken_connection(const char *file, unsigned int line, const char *func, const char *action, MySQL_Connection *myconn, int myerr, const char *message, bool verbose=false); void generate_status_one_hostgroup(int hid, std::string& s); */ + + + bool RunQuery_Success(MySQL_Connection *, bool& prepared_stmt_with_no_params); + bool RunQuery_Failed(MySQL_Connection *, bool&, int&); + bool RunQuery_Continue(MySQL_Connection *, int); + bool ProcessingRequest_MatchEnvironment(MySQL_Connection *myconn); + void LogKillQueryTimeout(MySQL_Data_Stream *myds, char *filename, int line); }; /* diff --git a/include/MySQL_Variables.h b/include/MySQL_Variables.h index 063a7812d..32a281dce 100644 --- a/include/MySQL_Variables.h +++ b/include/MySQL_Variables.h @@ -8,21 +8,22 @@ #include #include -class Client_Session; +class MySQL_Session; extern const MARIADB_CHARSET_INFO * proxysql_find_charset_nr(unsigned int nr); extern MARIADB_CHARSET_INFO * proxysql_find_charset_name(const char *name); extern MARIADB_CHARSET_INFO * proxysql_find_charset_collate(const char *collatename); extern void print_backtrace(void); -typedef bool (*verify_var)(Client_Session* session, int idx, uint32_t client_hash, uint32_t server_hash); -typedef bool (*update_var)(Client_Session* session, int idx, int &_rc); +typedef bool (*verify_var)(MySQL_Session* session, int idx, uint32_t client_hash, uint32_t server_hash); +typedef bool (*update_var)(MySQL_Session* session, int idx, int &_rc); -bool validate_charset(Client_Session* session, int idx, int &_rc); -bool update_server_variable(Client_Session* session, int idx, int &_rc); -bool verify_server_variable(Client_Session* session, int idx, uint32_t client_hash, uint32_t server_hash); +bool validate_charset(MySQL_Session* session, int idx, int &_rc); +bool update_server_variable(MySQL_Session* session, int idx, int &_rc); +bool verify_server_variable(MySQL_Session* session, int idx, uint32_t client_hash, uint32_t server_hash); bool verify_set_names(Client_Session* session); -bool logbin_update_server_variable(Client_Session* session, int idx, int &_rc); +bool verify_set_names(MySQL_Session* session); +bool logbin_update_server_variable(MySQL_Session* session, int idx, int &_rc); class MySQL_Variables { static verify_var verifiers[SQL_NAME_LAST_HIGH_WM]; @@ -37,20 +38,22 @@ public: virtual ~MySQL_Variables(); - bool client_set_value(Client_Session* session, int idx, const std::string& value); - bool client_set_hash_and_value(Client_Session* session, int idx, const std::string& value, uint32_t hash); - const char* client_get_value(Client_Session* session, int idx) const; - uint32_t client_get_hash(Client_Session* session, int idx) const; + bool client_set_value(MySQL_Session* session, int idx, const std::string& value); + bool client_set_hash_and_value(MySQL_Session* session, int idx, const std::string& value, uint32_t hash); + const char* client_get_value(MySQL_Session* session, int idx) const; + uint32_t client_get_hash(MySQL_Session* session, int idx) const; - void server_set_value(Client_Session* session, int idx, const char* value); - void server_set_hash_and_value(Client_Session* session, int idx, const char* value, uint32_t hash); - const char* server_get_value(Client_Session* session, int idx) const; - inline uint32_t server_get_hash(Client_Session* session, int idx) const; + void server_set_value(MySQL_Session* session, int idx, const char* value); + void server_set_hash_and_value(MySQL_Session* session, int idx, const char* value, uint32_t hash); + const char* server_get_value(MySQL_Session* session, int idx) const; + inline uint32_t server_get_hash(MySQL_Session* session, int idx) const; bool verify_variable(Client_Session* session, int idx) const; + bool verify_variable(MySQL_Session* session, int idx) const; bool update_variable(Client_Session* session, session_status status, int &_rc); - bool parse_variable_boolean(Client_Session *sess, int idx, std::string &value1, bool* lock_hostgroup); - bool parse_variable_number(Client_Session *sess, int idx, std::string &value1, bool* lock_hostgroup); + bool update_variable(MySQL_Session* session, session_status status, int &_rc); + bool parse_variable_boolean(MySQL_Session *sess, int idx, std::string &value1, bool* lock_hostgroup); + bool parse_variable_number(MySQL_Session *sess, int idx, std::string &value1, bool* lock_hostgroup); }; #endif // #ifndef MYSQL_VARIABLES_H diff --git a/include/ProxySQL_Data_Stream.h b/include/ProxySQL_Data_Stream.h index 891b16cbc..f7a461c14 100644 --- a/include/ProxySQL_Data_Stream.h +++ b/include/ProxySQL_Data_Stream.h @@ -1,5 +1,5 @@ -#ifndef __CLASS_MYSQL_DATA_STREAM_H -#define __CLASS_MYSQL_DATA_STREAM_H +#ifndef __CLASS_PROXYSQL_DATA_STREAM_H +#define __CLASS_PROXYSQL_DATA_STREAM_H #include "proxysql.h" #include "cpp.h" @@ -9,6 +9,7 @@ #define QUEUE_T_DEFAULT_SIZE 32768 #define MY_SSL_BUFFER 8192 +/* // this class avoid copying data class MyDS_real_query { public: @@ -16,12 +17,6 @@ class MyDS_real_query { char *QueryPtr; // pointer to beginning of the query unsigned int QuerySize; // size of the query void init(PtrSize_t *_pkt) { -/* - assert(QueryPtr==NULL); - assert(QuerySize==0); - assert(pkt.ptr==NULL); - assert(pkt.size==0); -*/ pkt.ptr=_pkt->ptr; pkt.size=_pkt->size; QueryPtr=(char *)pkt.ptr+5; @@ -35,17 +30,20 @@ class MyDS_real_query { QueryPtr=NULL; } }; - +*/ enum sslstatus { SSLSTATUS_OK, SSLSTATUS_WANT_IO, SSLSTATUS_FAIL}; + class ProxySQL_Data_Stream { private: +/* int array2buffer(); int buffer2array(); void generate_compressed_packet(); enum sslstatus do_ssl_handshake(); void queue_encrypted_bytes(const char *buf, size_t len); +*/ public: queue_t queueIN; @@ -53,6 +51,7 @@ class ProxySQL_Data_Stream queue_t queueOUT; uint64_t pkts_sent; // counter of sent packets +/* struct { PtrSize_t pkt; unsigned int partial; @@ -64,6 +63,7 @@ class ProxySQL_Data_Stream MySQL_Protocol myprot; MyDS_real_query mysql_real_query; +*/ bytes_stats_t bytes_info; // bytes statistics PtrSize_t multi_pkt; @@ -87,9 +87,9 @@ class ProxySQL_Data_Stream ProxySQL_Poll *mypolls; //int listener; - MySQL_Connection *myconn; +// MySQL_Connection *myconn; Client_Session *sess; // pointer to the session using this data stream - MySQL_Backend *mybe; // if this is a connection to a mysql server, this points to a backend structure +// MySQL_Backend *mybe; // if this is a connection to a mysql server, this points to a backend structure char *x509_subject_alt_name; SSL *ssl; BIO *rbio_ssl; @@ -136,24 +136,24 @@ class ProxySQL_Data_Stream uint8_t pkt_sid; - bool com_field_list; - char *com_field_wild; +// bool com_field_list; +// char *com_field_wild; ProxySQL_Data_Stream(); ~ProxySQL_Data_Stream(); - int array2buffer_full(); - void init(); // initialize the data stream - void init(enum MySQL_DS_type, Client_Session *, int); // initialize with arguments +// int array2buffer_full(); +// void init(); // initialize the data stream +// void init(enum MySQL_DS_type, Client_Session *, int); // initialize with arguments void shut_soft(); void shut_hard(); - int read_from_net(); - int write_to_net(); - int write_to_net_poll(); +// int read_from_net(); +// int write_to_net(); +// int write_to_net_poll(); bool available_data_out(); void remove_pollout(); - void set_pollout(); - void mysql_free(); +// void set_pollout(); +// void mysql_free(); void set_net_failure(); void setDSS_STATE_QUERY_SENT_NET(); @@ -162,11 +162,12 @@ class ProxySQL_Data_Stream DSS=dss; } - int read_pkts(); - int write_pkts(); +// int read_pkts(); +// int write_pkts(); - void unplug_backend(); +// void unplug_backend(); +/* void check_data_flow(); int assign_fd_from_mysql_conn(); @@ -194,9 +195,10 @@ class ProxySQL_Data_Stream void destroy_MySQL_Connection_From_Pool(bool sq); void free_mysql_real_query(); +*/ void reinit_queues(); void destroy_queues(); bool data_in_rbio(); }; -#endif /* __CLASS_MYSQL_DATA_STREAM_H */ +#endif /* __CLASS_PROXYSQL_DATA_STREAM_H */ diff --git a/include/ProxyWorker_Thread.h b/include/ProxyWorker_Thread.h index 84866381c..abfd72524 100644 --- a/include/ProxyWorker_Thread.h +++ b/include/ProxyWorker_Thread.h @@ -164,7 +164,7 @@ class ProxyWorker_Thread void read_one_byte_from_pipe(unsigned int n); void tune_timeout_for_myds_needs_pause(ProxySQL_Data_Stream *myds); void tune_timeout_for_session_needs_pause(ProxySQL_Data_Stream *myds); - void configure_pollout(ProxySQL_Data_Stream *myds, unsigned int n); + void configure_pollout(MySQL_Data_Stream *myds, unsigned int n); protected: int nfds; @@ -216,7 +216,7 @@ class ProxyWorker_Thread pthread_mutex_t thread_mutex; ProxyWorker_Thread(); ~ProxyWorker_Thread(); - Client_Session * create_new_session_and_client_data_stream(int _fd); + MySQL_Session * create_new_session_and_client_mysql_data_stream(int _fd); bool init(); void run___get_multiple_idle_connections(int& num_idles); void run___cleanup_mirror_queue(); @@ -231,7 +231,7 @@ class ProxyWorker_Thread bool process_data_on_mysql_data_stream(ProxySQL_Data_Stream *myds, unsigned int n); void ProcessAllSessions_SortingSessions(); void ProcessAllSessions_CompletedMirrorSession(unsigned int& n, Client_Session *sess); - void ProcessAllSessions_MaintenanceLoop(Client_Session *sess, unsigned long long sess_time, unsigned int& total_active_transactions_); + void ProcessAllSessions_MaintenanceLoop(MySQL_Session *sess, unsigned long long sess_time, unsigned int& total_active_transactions_); void process_all_sessions(); void refresh_variables(); void register_session_connection_handler(Client_Session *_sess, bool _new=false); diff --git a/include/SQLite3_Server.h b/include/SQLite3_Server.h index cd0761474..1c26d5f33 100644 --- a/include/SQLite3_Server.h +++ b/include/SQLite3_Server.h @@ -62,7 +62,7 @@ class SQLite3_Server { unsigned int num_aurora_servers[3]; unsigned int max_num_aurora_servers; pthread_mutex_t aurora_mutex; - void populate_aws_aurora_table(Client_Session *sess); + void populate_aws_aurora_table(MySQL_Session *sess); void init_aurora_ifaces_string(std::string& s); #endif // TEST_AURORA #ifdef TEST_GALERA @@ -70,13 +70,13 @@ class SQLite3_Server { unsigned int num_galera_servers[3]; unsigned int max_num_galera_servers; pthread_mutex_t galera_mutex; - void populate_galera_table(Client_Session *sess); + void populate_galera_table(MySQL_Session *sess); void init_galera_ifaces_string(std::string& s); #endif // TEST_GALERA #ifdef TEST_GROUPREP unsigned int max_num_grouprep_servers; pthread_mutex_t grouprep_mutex; - void populate_grouprep_table(Client_Session *sess, int txs_behind = 0); + void populate_grouprep_table(MySQL_Session *sess, int txs_behind = 0); void init_grouprep_ifaces_string(std::string& s); group_rep_status grouprep_test_value(const std::string& srv_addr); #endif // TEST_GROUPREP diff --git a/include/cpp.h b/include/cpp.h index f00494c4b..8f2fbe8e9 100644 --- a/include/cpp.h +++ b/include/cpp.h @@ -18,7 +18,7 @@ #include "fileutils.hpp" #include "configfile.hpp" //#include "query_processor.h" -#include "proxysql_admin.h" +//#include "proxysql_admin.h" //#include "SQLite3_Server.h" #ifdef PROXYSQLCLICKHOUSE #include "ClickHouse_Server.h" diff --git a/include/mysql_backend.h b/include/mysql_backend.h index 29e845aeb..9a32226b5 100644 --- a/include/mysql_backend.h +++ b/include/mysql_backend.h @@ -7,12 +7,10 @@ class MySQL_Backend { public: - void * operator new(size_t); - void operator delete(void *); int hostgroup_id; char gtid_uuid[128]; uint64_t gtid_trxid; - ProxySQL_Data_Stream *server_myds; + MySQL_Data_Stream *server_myds; // mysql_cp_entry_t *server_mycpe; bytes_stats_t server_bytes_at_cmd; //MySQL_Hostgroup_Entry *mshge; diff --git a/include/mysql_connection.h b/include/mysql_connection.h index 1353fcb46..698c0982d 100644 --- a/include/mysql_connection.h +++ b/include/mysql_connection.h @@ -107,7 +107,7 @@ class MySQL_Connection { MySQL_ResultSet *MyRS_reuse; MySrvC *parent; MySQL_Connection_userinfo *userinfo; - ProxySQL_Data_Stream *myds; + MySQL_Data_Stream *myds; /** * @brief Keeps tracks of the 'server_status'. Do not confuse with the 'server_status' from the * 'MYSQL' connection itself. This flag keeps track of the configured server status from the diff --git a/include/proxysql_admin.h b/include/proxysql_admin.h index 05a43daec..2ff12716e 100644 --- a/include/proxysql_admin.h +++ b/include/proxysql_admin.h @@ -13,6 +13,7 @@ #include #include "ProxySQL_RESTAPI_Server.hpp" +//#include "MySQL_Session.h" typedef struct { uint32_t hash; uint32_t key; } t_symstruct; class ProxySQL_Config; @@ -116,6 +117,21 @@ struct admin_metrics_map_idx { // ProxySQL_Admin shared variables extern int admin__web_verbosity; +// class Admin_MySQL_Session is identical to MySQL_Session +// but has different session_type +// ... or no ?!?!!! +/* +class Admin_MySQL_Session: public MySQL_Session +{ + friend class Client_Session; + friend class MySQL_Session; + friend class Query_Info; + friend class MySQL_Protocol; + private: + public: +}; +*/ + class ProxySQL_Admin { private: volatile int main_shutdown; diff --git a/include/proxysql_structs.h b/include/proxysql_structs.h index 3a76aad57..b9f60f226 100644 --- a/include/proxysql_structs.h +++ b/include/proxysql_structs.h @@ -458,9 +458,11 @@ typedef struct __SQP_query_parser_t SQP_par_t; #ifndef PROXYSQL_CLASSES #define PROXYSQL_CLASSES class ProxySQL_Data_Stream; +class MySQL_Data_Stream; class MySQL_Connection_userinfo; class Client_Session; class MySQL_Session; +class ProxySQL_Admin; class MySQL_Backend; class MySQL_Monitor; class ProxyWorker_Thread; diff --git a/lib/ClickHouse_Server.cpp b/lib/ClickHouse_Server.cpp index d66db191b..26985baea 100644 --- a/lib/ClickHouse_Server.cpp +++ b/lib/ClickHouse_Server.cpp @@ -9,8 +9,12 @@ #include "MySQL_Logger.hpp" #include "ProxySQL_Data_Stream.h" +#include "MySQL_Data_Stream.h" +#include "MySQL_Session.h" #include "query_processor.h" +#include "proxysql_admin.h" + #include #include #include @@ -68,11 +72,11 @@ using namespace clickhouse; -__thread Client_Session * clickhouse_thread___mysql_sess; +__thread MySQL_Session * clickhouse_thread___mysql_sess; //static void ClickHouse_to_MySQL(SQLite3_result *result, char *error, int affected_rows, MySQL_Protocol *myprot) { inline void ClickHouse_to_MySQL(const Block& block) { - Client_Session *sess = clickhouse_thread___mysql_sess; + MySQL_Session *sess = clickhouse_thread___mysql_sess; MySQL_Protocol *myprot=NULL; myprot=&sess->client_myds->myprot; @@ -434,7 +438,8 @@ class sqlite3server_main_loop_listeners { static sqlite3server_main_loop_listeners S_amll; -void ClickHouse_Server_session_handler(Client_Session *sess, void *_pa, PtrSize_t *pkt) { +void ClickHouse_Server_session_handler(Client_Session *c_sess, void *_pa, PtrSize_t *pkt) { + MySQL_Session *sess = (MySQL_Session *)c_sess; char *error=NULL; int cols; int affected_rows; @@ -1077,7 +1082,7 @@ __run_query: if (clickhouse_sess->connected == true) { if (clickhouse_sess->schema_initialized == false) { if (sess && sess->client_myds) { - ProxySQL_Data_Stream *ds = sess->client_myds; + MySQL_Data_Stream *ds = sess->client_myds; if (ds->myconn && ds->myconn->userinfo && ds->myconn->userinfo->schemaname) { char *sn = ds->myconn->userinfo->schemaname; char *use_query = NULL; @@ -1215,8 +1220,8 @@ static void *child_mysql(void *arg) { ProxyWorker_Thread *mysql_thr=new ProxyWorker_Thread(); mysql_thr->curtime=monotonic_time(); - Client_Session *sess = NULL; - ProxySQL_Data_Stream *myds = NULL; + MySQL_Session *sess = NULL; + MySQL_Data_Stream *myds = NULL; ClickHouse_Session *sqlite_sess = new ClickHouse_Session(); sqlite_sess->init(); @@ -1224,7 +1229,7 @@ static void *child_mysql(void *arg) { GloQPro->init_thread(); mysql_thr->refresh_variables(); - sess=mysql_thr->create_new_session_and_client_data_stream(client); + sess=mysql_thr->create_new_session_and_client_mysql_data_stream(client); sess->thread=mysql_thr; sess->session_type = PROXYSQL_SESSION_CLICKHOUSE; sess->handler_function=ClickHouse_Server_session_handler; diff --git a/lib/Client_Session.cpp b/lib/Client_Session.cpp index 447d43266..4bd06dbf9 100644 --- a/lib/Client_Session.cpp +++ b/lib/Client_Session.cpp @@ -10,6 +10,7 @@ #include "set_parser.h" #include "ProxySQL_Data_Stream.h" +#include "MySQL_Data_Stream.h" #include "query_processor.h" #include "MySQL_PreparedStatement.h" #include "MySQL_Logger.hpp" @@ -26,30 +27,6 @@ #include "MySQL_Session.h" -#define SELECT_VERSION_COMMENT "select @@version_comment limit 1" -#define SELECT_VERSION_COMMENT_LEN 32 - -#define PROXYSQL_VERSION_COMMENT "\x01\x00\x00\x01\x01\x27\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x11\x40\x40\x76\x65\x72\x73\x69\x6f\x6e\x5f\x63\x6f\x6d\x6d\x65\x6e\x74\x00\x0c\x21\x00\x18\x00\x00\x00\xfd\x00\x00\x1f\x00\x00\x05\x00\x00\x03\xfe\x00\x00\x02\x00\x0b\x00\x00\x04\x0a(ProxySQL)\x05\x00\x00\x05\xfe\x00\x00\x02\x00" -#define PROXYSQL_VERSION_COMMENT_LEN 81 - -// PROXYSQL_VERSION_COMMENT_WITH_OK is sent instead of PROXYSQL_VERSION_COMMENT -// if Client supports CLIENT_DEPRECATE_EOF -#define PROXYSQL_VERSION_COMMENT_WITH_OK "\x01\x00\x00\x01\x01" \ -"\x27\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x11\x40\x40\x76\x65\x72\x73\x69\x6f\x6e\x5f\x63\x6f\x6d\x6d\x65\x6e\x74\x00\x0c\x21\x00\x18\x00\x00\x00\xfd\x00\x00\x1f\x00\x00" \ -"\x0b\x00\x00\x03\x0a(ProxySQL)" \ -"\x07\x00\x00\x04\xfe\x00\x00\x02\x00\x00\x00" -#define PROXYSQL_VERSION_COMMENT_WITH_OK_LEN 74 - -#define SELECT_CONNECTION_ID "SELECT CONNECTION_ID()" -#define SELECT_CONNECTION_ID_LEN 22 -#define SELECT_LAST_INSERT_ID "SELECT LAST_INSERT_ID()" -#define SELECT_LAST_INSERT_ID_LEN 23 -#define SELECT_LAST_INSERT_ID_LIMIT1 "SELECT LAST_INSERT_ID() LIMIT 1" -#define SELECT_LAST_INSERT_ID_LIMIT1_LEN 31 -#define SELECT_VARIABLE_IDENTITY "SELECT @@IDENTITY" -#define SELECT_VARIABLE_IDENTITY_LEN 17 -#define SELECT_VARIABLE_IDENTITY_LIMIT1 "SELECT @@IDENTITY LIMIT 1" -#define SELECT_VARIABLE_IDENTITY_LIMIT1_LEN 25 #define EXPMARIA @@ -71,48 +48,6 @@ static inline char is_normal_char(char c) { return 0; } -static const std::set mysql_variables_boolean = { - "aurora_read_replica_read_committed", - "foreign_key_checks", - "innodb_strict_mode", - "innodb_table_locks", - "sql_auto_is_null", - "sql_big_selects", - "sql_log_bin", - "sql_safe_updates", - "unique_checks", -}; - -static const std::set mysql_variables_numeric = { - "auto_increment_increment", - "auto_increment_offset", - "group_concat_max_len", - "innodb_lock_wait_timeout", - "join_buffer_size", - "lock_wait_timeout", - "long_query_time", - "max_execution_time", - "max_heap_table_size", - "max_join_size", - "max_sort_length", - "optimizer_prune_level", - "optimizer_search_depth", - "query_cache_type", - "sort_buffer_size", - "sql_select_limit", - "timestamp", - "tmp_table_size", - "wsrep_sync_wait" -}; -static const std::set mysql_variables_strings = { - "default_storage_engine", - "default_tmp_storage_engine", - "group_replication_consistency", - "lc_messages", - "lc_time_names", - "optimizer_switch", - "wsrep_osu_method", -}; extern MARIADB_CHARSET_INFO * proxysql_find_charset_name(const char * const name); extern MARIADB_CHARSET_INFO * proxysql_find_charset_collate_names(const char *csname, const char *collatename); @@ -522,7 +457,7 @@ Client_Session::Client_Session() { sending_set_autocommit=false; autocommit_on_hostgroup=-1; killed=false; - session_type=PROXYSQL_SESSION_MYSQL; + session_type=PROXYSQL_SESSION_MYSQL; // default //admin=false; connections_handler=false; max_connections_reached=false; @@ -534,7 +469,6 @@ Client_Session::Client_Session() { session_fast_forward=false; started_sending_data_to_client=false; handler_function=NULL; - client_myds=NULL; to_process=0; mybe=NULL; mirror=false; @@ -597,9 +531,11 @@ void Client_Session::reset() { default_hostgroup=-1; locked_on_hostgroup=-1; locked_on_hostgroup_and_all_variables_set=false; +/* if (session_type==PROXYSQL_SESSION_MYSQL) { ((MySQL_Session *)this)->mysql_session_reset(); } +*/ /* if (sess_STMTs_meta) { delete sess_STMTs_meta; @@ -631,11 +567,6 @@ void Client_Session::reset() { } } } - if (client_myds) { - if (client_myds->myconn) { - client_myds->myconn->reset(); - } - } } Client_Session::~Client_Session() { @@ -646,21 +577,6 @@ Client_Session::~Client_Session() { thread->status_variables.stvar[st_var_hostgroup_locked]--; } - if (client_myds) { - if (client_authenticated) { - switch (session_type) { -#ifdef PROXYSQLCLICKHOUSE - case PROXYSQL_SESSION_CLICKHOUSE: - GloClickHouseAuth->decrease_frontend_user_connections(client_myds->myconn->userinfo->username); - break; -#endif /* PROXYSQLCLICKHOUSE */ - default: - GloMyAuth->decrease_frontend_user_connections(client_myds->myconn->userinfo->username); - break; - } - } - delete client_myds; - } if (default_schema) { free(default_schema); } @@ -701,14 +617,15 @@ MySQL_Backend * Client_Session::find_mysql_backend(int hostgroup_id) { }; -MySQL_Backend * Client_Session::create_mysql_backend(int hostgroup_id, ProxySQL_Data_Stream *_myds) { +MySQL_Backend * Client_Session::create_mysql_backend(int hostgroup_id, ProxySQL_Data_Stream *pds) { MySQL_Backend *_mybe=new MySQL_Backend(); + MySQL_Data_Stream *_myds = (MySQL_Data_Stream *)pds; proxy_debug(PROXY_DEBUG_NET,4,"HID=%d, _myds=%p, _mybe=%p\n" , hostgroup_id, _myds, _mybe); _mybe->hostgroup_id=hostgroup_id; if (_myds) { _mybe->server_myds=_myds; } else { - _mybe->server_myds = new ProxySQL_Data_Stream(); + _mybe->server_myds = new MySQL_Data_Stream(); _mybe->server_myds->DSS=STATE_NOT_INITIALIZED; _mybe->server_myds->init(MYDS_BACKEND_NOT_CONNECTED, this, 0); } @@ -731,283 +648,6 @@ void Client_Session::reset_all_mysql_backends() { } }; -void Client_Session::writeout() { - int tps = 10; // throttling per second , by default every 100ms - int total_written = 0; - unsigned long long last_sent_=0; - bool disable_throttle = mysql_thread___throttle_max_bytes_per_second_to_client == 0; - int mwpl = mysql_thread___throttle_max_bytes_per_second_to_client; // max writes per call - mwpl = mwpl/tps; - if (session_type!=PROXYSQL_SESSION_MYSQL) { - disable_throttle = true; - } - if (client_myds) client_myds->array2buffer_full(); - if (mybe && mybe->server_myds && mybe->server_myds->myds_type==MYDS_BACKEND) { - if (session_type==PROXYSQL_SESSION_MYSQL) { - if (mybe->server_myds->net_failure==false) { - if (mybe->server_myds->poll_fds_idx>-1) { // NOTE: attempt to force writes - mybe->server_myds->array2buffer_full(); - } - } - } else { - mybe->server_myds->array2buffer_full(); - } - } - if (client_myds && thread->curtime >= client_myds->pause_until) { - if (mirror==false) { - bool runloop=false; - if (client_myds->mypolls) { - last_sent_ = client_myds->mypolls->last_sent[client_myds->poll_fds_idx]; - } - int retbytes=client_myds->write_to_net_poll(); - total_written+=retbytes; - if (retbytes==QUEUE_T_DEFAULT_SIZE) { // optimization to solve memory bloat - runloop=true; - } - while (runloop && (disable_throttle || total_written < mwpl)) { - runloop=false; // the default - client_myds->array2buffer_full(); - struct pollfd fds; - fds.fd=client_myds->fd; - fds.events=POLLOUT; - fds.revents=0; - int retpoll=poll(&fds, 1, 0); - if (retpoll>0) { - if (fds.revents==POLLOUT) { - retbytes=client_myds->write_to_net_poll(); - total_written+=retbytes; - if (retbytes==QUEUE_T_DEFAULT_SIZE) { // optimization to solve memory bloat - runloop=true; - } - } - } - } - } - } - - // flow control - if (!disable_throttle && total_written > 0) { - if (total_written > mwpl) { - unsigned long long add_ = 1000000/tps + 1000000/tps*((unsigned long long)total_written - (unsigned long long)mwpl)/mwpl; - pause_until = thread->curtime + add_; - client_myds->remove_pollout(); - client_myds->pause_until = thread->curtime + add_; - } else { - if (total_written >= QUEUE_T_DEFAULT_SIZE) { - unsigned long long time_diff = thread->curtime - last_sent_; - if (time_diff == 0) { // sending data really too fast! - unsigned long long add_ = 1000000/tps + 1000000/tps*((unsigned long long)total_written - (unsigned long long)mwpl)/mwpl; - pause_until = thread->curtime + add_; - client_myds->remove_pollout(); - client_myds->pause_until = thread->curtime + add_; - } else { - float current_Bps = (float)total_written*1000*1000/time_diff; - if (current_Bps > mysql_thread___throttle_max_bytes_per_second_to_client) { - unsigned long long add_ = 1000000/tps; - pause_until = thread->curtime + add_; - assert(pause_until > thread->curtime); - client_myds->remove_pollout(); - client_myds->pause_until = thread->curtime + add_; - } - } - } - } - } - - if (mybe) { - if (mybe->server_myds) mybe->server_myds->write_to_net_poll(); - } - proxy_debug(PROXY_DEBUG_NET,1,"Thread=%p, Session=%p -- Writeout Session %p\n" , this->thread, this, this); -} - -// FIXME: This function is currently disabled . See #469 -bool Client_Session::handler_CommitRollback(PtrSize_t *pkt) { - char c=((char *)pkt->ptr)[5]; - bool ret=false; - if (c=='c' || c=='C') { - if (strncasecmp((char *)"commit",(char *)pkt->ptr+5,6)==0) { - __sync_fetch_and_add(&MyHGM->status.commit_cnt, 1); - ret=true; - } - } else { - if (c=='r' || c=='R') { - if ( strncasecmp((char *)"rollback",(char *)pkt->ptr+5,8)==0 ) { - __sync_fetch_and_add(&MyHGM->status.rollback_cnt, 1); - ret=true; - } - } - } - - if (ret==false) { - return false; // quick exit - } - unsigned int nTrx=NumActiveTransactions(); - if (nTrx) { - // there is an active transaction, we must forward the request - return false; - } else { - // there is no active transaction, we will just reply OK - client_myds->DSS=STATE_QUERY_SENT_NET; - uint16_t setStatus = 0; - if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; - client_myds->myprot.generate_pkt_OK(true,NULL,NULL,1,0,0,setStatus,0,NULL); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; - if (mirror==false) { - RequestEnd_mysql(NULL); - } - l_free(pkt->size,pkt->ptr); - if (c=='c' || c=='C') { - __sync_fetch_and_add(&MyHGM->status.commit_cnt_filtered, 1); - } else { - __sync_fetch_and_add(&MyHGM->status.rollback_cnt_filtered, 1); - } - return true; - } - return false; -} - - -bool Client_Session::handler_SetAutocommit(PtrSize_t *pkt) { - autocommit_handled=false; - sending_set_autocommit=false; - size_t sal=strlen("set autocommit"); - char * _ptr = (char *)pkt->ptr; -#ifdef DEBUG - string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); - proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Parsing SET command = %s\n", nqn.c_str()); -#endif - if ( pkt->size >= 7+sal) { - if (strncasecmp((char *)"SET @@session.autocommit",(char *)pkt->ptr+5,strlen((char *)"SET @@session.autocommit"))==0) { - memmove(_ptr+9, _ptr+19, pkt->size - 19); - memset(_ptr+pkt->size-10,' ',10); - } - if (strncasecmp((char *)"set autocommit",(char *)pkt->ptr+5,sal)==0) { - void *p = NULL; - // make a copy - PtrSize_t _new_pkt; - _new_pkt.size = pkt->size; - _new_pkt.ptr = malloc(_new_pkt.size); - memcpy(_new_pkt.ptr, pkt->ptr, _new_pkt.size); - _ptr = (char *)_new_pkt.ptr; - for (int i=5+sal; i < (int)_new_pkt.size; i++) { - *((char *)_new_pkt.ptr+i) = tolower(*((char *)_new_pkt.ptr+i)); - } - p = memmem(_ptr+5+sal, pkt->size-5-sal, (void *)"false", 5); - if (p) { - memcpy(p,(void *)"0 ",5); - } - p = memmem(_ptr+5+sal, pkt->size-5-sal, (void *)"true", 4); - if (p) { - memcpy(p,(void *)"1 ",4); - } - p = memmem(_ptr+5+sal, pkt->size-5-sal, (void *)"off", 3); - if (p) { - memcpy(p,(void *)"0 ",3); - } - p = memmem(_ptr+5+sal, pkt->size-5-sal, (void *)"on", 2); - if (p) { - memcpy(p,(void *)"1 ",2); - } - unsigned int i; - bool eq=false; - int fd=-1; // first digit - for (i=5+sal;i<_new_pkt.size;i++) { - char c=((char *)_new_pkt.ptr)[i]; - if (c!='0' && c!='1' && c!=' ' && c!='=' && c!='/') { - free(_new_pkt.ptr); - return false; // found a not valid char - } - if (eq==false) { - if (c!=' ' && c!='=') { - free(_new_pkt.ptr); - return false; // found a not valid char - } - if (c=='=') eq=true; - } else { - if (c!='0' && c!='1' && c!=' ' && c!='/') { - free(_new_pkt.ptr); - return false; // found a not valid char - } - if (fd==-1) { - if (c=='0' || c=='1') { // found first digit - if (c=='0') - fd=0; - else - fd=1; - } - } else { - if (c=='0' || c=='1') { // found second digit - free(_new_pkt.ptr); - return false; - } else { - if (c=='/' || c==' ') { - break; - } - } - } - } - } - if (fd >= 0) { // we can set autocommit - autocommit_handled=true; -#ifdef DEBUG - proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Setting autocommit to = %d\n", fd); -#endif - __sync_fetch_and_add(&MyHGM->status.autocommit_cnt, 1); - // we immediately process the number of transactions - unsigned int nTrx=NumActiveTransactions(); - if (fd==1 && autocommit==true) { - // nothing to do, return OK - goto __ret_autocommit_OK; - } - if (fd==1 && autocommit==false) { - if (nTrx) { - // there is an active transaction, we need to forward it - // because this can potentially close the transaction - autocommit=true; - client_myds->myconn->set_autocommit(autocommit); - autocommit_on_hostgroup=FindOneActiveTransaction(); - free(_new_pkt.ptr); - sending_set_autocommit=true; - return false; - } else { - // as there is no active transaction, we do no need to forward it - // just change internal state - autocommit=true; - client_myds->myconn->set_autocommit(autocommit); - goto __ret_autocommit_OK; - } - } - - if (fd==0) { - autocommit=false; // we set it, no matter if already set or not - client_myds->myconn->set_autocommit(autocommit); - // it turned out I was wrong - // set autocommit=0 has no effect if there is an acrive transaction - // therefore, we never forward set autocommit = 0 - goto __ret_autocommit_OK; - } -__ret_autocommit_OK: - client_myds->DSS=STATE_QUERY_SENT_NET; - 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); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; - if (mirror==false) { - RequestEnd_mysql(NULL); - } - l_free(pkt->size,pkt->ptr); - __sync_fetch_and_add(&MyHGM->status.autocommit_cnt_filtered, 1); - free(_new_pkt.ptr); - return true; - } - free(_new_pkt.ptr); - } - } - return false; -} - void Client_Session::generate_proxysql_internal_session_json(json &j) { char buff[32]; sprintf(buff,"%p",this); @@ -1044,6 +684,10 @@ void Client_Session::generate_proxysql_internal_session_json(json &j) { j["default_schema"] = ( default_schema ? default_schema : "" ); j["user_attributes"] = ( user_attributes ? user_attributes : "" ); j["transaction_persistent"] = transaction_persistent; + MySQL_Data_Stream *client_myds = NULL; + if (session_type==PROXYSQL_SESSION_MYSQL) { + client_myds = ((MySQL_Session *)this)->client_myds; + } if (client_myds != NULL) { // only if client_myds is defined j["client"]["stream"]["pkts_recv"] = client_myds->pkts_recv; j["client"]["stream"]["pkts_sent"] = client_myds->pkts_sent; @@ -1102,8 +746,8 @@ void Client_Session::generate_proxysql_internal_session_json(json &j) { unsigned int i = _mybe->hostgroup_id; j["backends"][i]["hostgroup_id"] = i; j["backends"][i]["gtid"] = ( strlen(_mybe->gtid_uuid) ? _mybe->gtid_uuid : "" ); - if (_mybe->server_myds) { - ProxySQL_Data_Stream *_myds=_mybe->server_myds; + if (session_type==PROXYSQL_SESSION_MYSQL && _mybe->server_myds) { + MySQL_Data_Stream *_myds=_mybe->server_myds; sprintf(buff,"%p",_myds); j["backends"][i]["stream"]["address"] = buff; j["backends"][i]["stream"]["questions"] = _myds->statuses.questions; @@ -1189,6 +833,11 @@ void Client_Session::generate_proxysql_internal_session_json(json &j) { } void Client_Session::return_proxysql_internal(PtrSize_t *pkt) { + assert(session_type==PROXYSQL_SESSION_MYSQL); + MySQL_Data_Stream * client_myds = NULL; + if (session_type==PROXYSQL_SESSION_MYSQL) { + client_myds = ((MySQL_Session *)this)->client_myds; + } unsigned int l = 0; l = strlen((char *)"PROXYSQL INTERNAL SESSION"); if (pkt->size==(5+l) && strncasecmp((char *)"PROXYSQL INTERNAL SESSION", (char *)pkt->ptr+5, l)==0) { @@ -1207,489 +856,111 @@ void Client_Session::return_proxysql_internal(PtrSize_t *pkt) { return; } // default + assert(client_myds != NULL); client_myds->DSS=STATE_QUERY_SENT_NET; client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,1064,(char *)"42000",(char *)"Unknown PROXYSQL INTERNAL command",true); client_myds->DSS=STATE_SLEEP; status=WAITING_CLIENT_DATA; if (mirror==false) { - RequestEnd_mysql(NULL); + ((MySQL_Session *)this)->RequestEnd_mysql(NULL); } l_free(pkt->size,pkt->ptr); } -bool Client_Session::handler_special_queries(PtrSize_t *pkt) { - bool deprecate_eof_active = client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF; - - if (pkt->size>(5+18) && strncasecmp((char *)"PROXYSQL INTERNAL ",(char *)pkt->ptr+5,18)==0) { - return_proxysql_internal(pkt); - return true; - } - if (locked_on_hostgroup == -1) { - if (handler_SetAutocommit(pkt) == true) { - return true; - } - if (handler_CommitRollback(pkt) == true) { - return true; - } - } - //handle 2564 - if (pkt->size==SELECT_VERSION_COMMENT_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncmp((char *)SELECT_VERSION_COMMENT,(char *)pkt->ptr+5,pkt->size-5)==0) { - // FIXME: this doesn't return AUTOCOMMIT or IN_TRANS - PtrSize_t pkt_2; - if (deprecate_eof_active) { - pkt_2.size=PROXYSQL_VERSION_COMMENT_WITH_OK_LEN; - pkt_2.ptr=l_alloc(pkt_2.size); - memcpy(pkt_2.ptr,PROXYSQL_VERSION_COMMENT_WITH_OK,pkt_2.size); - } else { - pkt_2.size=PROXYSQL_VERSION_COMMENT_LEN; - pkt_2.ptr=l_alloc(pkt_2.size); - memcpy(pkt_2.ptr,PROXYSQL_VERSION_COMMENT,pkt_2.size); - } - status=WAITING_CLIENT_DATA; - client_myds->DSS=STATE_SLEEP; - client_myds->PSarrayOUT->add(pkt_2.ptr,pkt_2.size); - if (mirror==false) { - RequestEnd_mysql(NULL); - } - l_free(pkt->size,pkt->ptr); - return true; - } - if (pkt->size==strlen((char *)"select USER()")+5 && strncmp((char *)"select USER()",(char *)pkt->ptr+5,pkt->size-5)==0) { - // FIXME: this doesn't return AUTOCOMMIT or IN_TRANS - char *query1=(char *)"SELECT \"%s\" AS 'USER()'"; - char *query2=(char *)malloc(strlen(query1)+strlen(client_myds->myconn->userinfo->username)+10); - sprintf(query2,query1,client_myds->myconn->userinfo->username); - char *error; - int cols; - int affected_rows; - SQLite3_result *resultset; - GloAdmin->admindb->execute_statement(query2, &error , &cols , &affected_rows , &resultset); - SQLite3_to_MySQL(resultset, error, affected_rows, &client_myds->myprot, false, deprecate_eof_active); - delete resultset; - free(query2); - if (mirror==false) { - RequestEnd_mysql(NULL); - } - l_free(pkt->size,pkt->ptr); - return true; - } - if (locked_on_hostgroup >= 0 && (strncasecmp((char *)"SET ",(char *)pkt->ptr+5,4)==0)) { - // this is a circuit breaker, we will send everything to the backend - // - // also note that in the current implementation we stop tracking variables: - // this becomes a problem if mysql-set_query_lock_on_hostgroup is - // disabled while a session is already locked - return false; - } - if ((pkt->size < 60) && (pkt->size > 38) && (strncasecmp((char *)"SET SESSION character_set_server",(char *)pkt->ptr+5,32)==0) ) { // issue #601 - char *idx=NULL; - char *p=(char *)pkt->ptr+37; - idx=(char *)memchr(p,'=',pkt->size-37); - if (idx) { // we found = - PtrSize_t pkt_2; - pkt_2.size=5+strlen((char *)"SET NAMES ")+pkt->size-1-(idx-(char *)pkt->ptr); - pkt_2.ptr=l_alloc(pkt_2.size); - mysql_hdr Hdr; - memcpy(&Hdr,pkt->ptr,sizeof(mysql_hdr)); - Hdr.pkt_length=pkt_2.size-5; - memcpy((char *)pkt_2.ptr+4,(char *)pkt->ptr+4,1); - memcpy(pkt_2.ptr,&Hdr,sizeof(mysql_hdr)); - strcpy((char *)pkt_2.ptr+5,(char *)"SET NAMES "); - memcpy((char *)pkt_2.ptr+15,idx+1,pkt->size-1-(idx-(char *)pkt->ptr)); - l_free(pkt->size,pkt->ptr); - pkt->size=pkt_2.size; - pkt->ptr=pkt_2.ptr; - } - } - if ((pkt->size < 60) && (pkt->size > 39) && (strncasecmp((char *)"SET SESSION character_set_results",(char *)pkt->ptr+5,33)==0) ) { // like the above - char *idx=NULL; - char *p=(char *)pkt->ptr+38; - idx=(char *)memchr(p,'=',pkt->size-38); - if (idx) { // we found = - PtrSize_t pkt_2; - pkt_2.size=5+strlen((char *)"SET NAMES ")+pkt->size-1-(idx-(char *)pkt->ptr); - pkt_2.ptr=l_alloc(pkt_2.size); - mysql_hdr Hdr; - memcpy(&Hdr,pkt->ptr,sizeof(mysql_hdr)); - Hdr.pkt_length=pkt_2.size-5; - memcpy((char *)pkt_2.ptr+4,(char *)pkt->ptr+4,1); - memcpy(pkt_2.ptr,&Hdr,sizeof(mysql_hdr)); - strcpy((char *)pkt_2.ptr+5,(char *)"SET NAMES "); - memcpy((char *)pkt_2.ptr+15,idx+1,pkt->size-1-(idx-(char *)pkt->ptr)); - l_free(pkt->size,pkt->ptr); - pkt->size=pkt_2.size; - pkt->ptr=pkt_2.ptr; - } +/* +int Client_Session::handler_again___status_RESETTING_CONNECTION() { + assert(mybe->server_myds->myconn); + ProxySQL_Data_Stream *myds=mybe->server_myds; + MySQL_Connection *myconn=myds->myconn; + if (myds->mypolls==NULL) { + thread->mypolls.add(POLLIN|POLLOUT, myds->fd, myds, thread->curtime); } - if ( - (pkt->size < 100) && (pkt->size > 15) && (strncasecmp((char *)"SET NAMES ",(char *)pkt->ptr+5,10)==0) - && - (memchr((const void *)((char *)pkt->ptr+5),',',pkt->size-15)==NULL) // there is no comma - ) { - char *unstripped=strndup((char *)pkt->ptr+15,pkt->size-15); - char *csname=trim_spaces_and_quotes_in_place(unstripped); - //unsigned int charsetnr = 0; - const MARIADB_CHARSET_INFO * c; - char * collation_name_unstripped = NULL; - char * collation_name = NULL; - if (strcasestr(csname," COLLATE ")) { - collation_name_unstripped = strcasestr(csname," COLLATE ") + strlen(" COLLATE "); - collation_name = trim_spaces_and_quotes_in_place(collation_name_unstripped); - char *_s1=index(csname,' '); - char *_s2=index(csname,'\''); - char *_s3=index(csname,'"'); - char *_s = NULL; - if (_s1) { - _s = _s1; - } - if (_s2) { - if (_s) { - if (_s2 < _s) { - _s = _s2; - } - } else { - _s = _s2; - } - } - if (_s3) { - if (_s) { - if (_s3 < _s) { - _s = _s3; - } + myds->DSS=STATE_MARIADB_QUERY; + // we recreate local_stmts : see issue #752 + delete myconn->local_stmts; + myconn->local_stmts=new MySQL_STMTs_local_v14(false); // false by default, it is a backend + int rc=myconn->async_change_user(myds->revents); + if (rc==0) { + __sync_fetch_and_add(&MyHGM->status.backend_change_user, 1); + //myds->myconn->userinfo->set(client_myds->myconn->userinfo); + myds->myconn->reset(); + myds->DSS = STATE_MARIADB_GENERIC; + myconn->async_state_machine=ASYNC_IDLE; +// if (mysql_thread___multiplexing && (myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + myds->return_MySQL_Connection_To_Pool(); +// } else { +// myds->destroy_MySQL_Connection_From_Pool(true); +// } + delete mybe->server_myds; + mybe->server_myds=NULL; + set_status(session_status___NONE); + return -1; + } else { + if (rc==-1 || rc==-2) { + if (rc==-2) { + proxy_error("Change user timeout during COM_CHANGE_USER on %s , %d\n", myconn->parent->address, myconn->parent->port); + MyHGM->p_update_mysql_error_counter(p_mysql_error_type::mysql, myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, ER_PROXYSQL_CHANGE_USER_TIMEOUT); + } else { // rc==-1 + int myerr=mysql_errno(myconn->mysql); + MyHGM->p_update_mysql_error_counter( + p_mysql_error_type::mysql, + myconn->parent->myhgc->hid, + myconn->parent->address, + myconn->parent->port, + ( myerr ? myerr : ER_PROXYSQL_OFFLINE_SRV ) + ); + if (myerr != 0) { + proxy_error("Detected an error during COM_CHANGE_USER on (%d,%s,%d) , FD (Conn:%d , MyDS:%d) : %d, %s\n", myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myds->fd, myds->myconn->fd, myerr, mysql_error(myconn->mysql)); } else { - _s = _s3; + proxy_error( + "Detected an error during COM_CHANGE_USER on (%d,%s,%d) , FD (Conn:%d , MyDS:%d) : %d, %s\n", + myconn->parent->myhgc->hid, + myconn->parent->address, + myconn->parent->port, + myds->fd, + myds->myconn->fd, + ER_PROXYSQL_OFFLINE_SRV, + "Detected offline server prior to statement execution" + ); } } - if (_s) { - *_s = '\0'; + myds->destroy_MySQL_Connection_From_Pool(false); + myds->fd=0; + //delete mybe->server_myds; + //mybe->server_myds=NULL; + RequestEnd_mysql(myds); //fix bug #682 + return -1; + } else { + // rc==1 , nothing to do for now + if (myds->mypolls==NULL) { + thread->mypolls.add(POLLIN|POLLOUT, myds->fd, myds, thread->curtime); } + } + } + return 0; +} +*/ - _s1 = index(collation_name,' '); - _s2 = index(collation_name,'\''); - _s3 = index(collation_name,'"'); - _s = NULL; - if (_s1) { - _s = _s1; - } - if (_s2) { - if (_s) { - if (_s2 < _s) { - _s = _s2; - } - } else { - _s = _s2; - } +void Client_Session::handler_again___new_thread_to_kill_connection() { + MySQL_Data_Stream *myds=mybe->server_myds; + assert(session_type==PROXYSQL_SESSION_MYSQL); + if (myds->myconn && myds->myconn->mysql) { + if (myds->killed_at==0) { + myds->wait_until=0; + myds->killed_at=thread->curtime; + //fprintf(stderr,"Expired: %llu, %llu\n", mybe->server_myds->wait_until, thread->curtime); + MySQL_Data_Stream *client_myds = NULL; + if (session_type==PROXYSQL_SESSION_MYSQL) { + client_myds = ((MySQL_Session *)this)->client_myds; } - if (_s3) { - if (_s) { - if (_s3 < _s) { - _s = _s3; - } + MySQL_Connection_userinfo *ui=client_myds->myconn->userinfo; + char *auth_password=NULL; + if (ui->password) { + if (ui->password[0]=='*') { // we don't have the real password, let's pass sha1 + auth_password=ui->sha1_pass; } else { - _s = _s3; - } - } - if (_s) { - *_s = '\0'; - } - - c = proxysql_find_charset_collate_names(csname,collation_name); - } else { - c = proxysql_find_charset_name(csname); - } - free(unstripped); - if (c) { - client_myds->DSS=STATE_QUERY_SENT_NET; - client_myds->myconn->set_charset(c->nr, NAMES); - 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); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; - if (mirror==false) { - RequestEnd_mysql(NULL); - } - l_free(pkt->size,pkt->ptr); - __sync_fetch_and_add(&MyHGM->status.frontend_set_names, 1); - return true; - } - } - if ( (pkt->size == 18) && (strncasecmp((char *)"SHOW WARNINGS",(char *)pkt->ptr+5,13)==0) ) { - SQLite3_result * resultset=new SQLite3_result(3); - resultset->add_column_definition(SQLITE_TEXT,"Level"); - resultset->add_column_definition(SQLITE_TEXT,"Code"); - resultset->add_column_definition(SQLITE_TEXT,"Message"); - SQLite3_to_MySQL(resultset, NULL, 0, &client_myds->myprot, false, deprecate_eof_active); - delete resultset; - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; - if (mirror==false) { - RequestEnd_mysql(NULL); - } - l_free(pkt->size,pkt->ptr); - return true; - } - // 'LOAD DATA LOCAL INFILE' is unsupported. We report an specific error to inform clients about this fact. For more context see #833. - if ( (pkt->size >= 22 + 5) && (strncasecmp((char *)"LOAD DATA LOCAL INFILE",(char *)pkt->ptr+5, 22)==0) ) { - if (mysql_thread___enable_load_data_local_infile == false) { - client_myds->DSS=STATE_QUERY_SENT_NET; - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,1047,(char *)"HY000",(char *)"Unsupported 'LOAD DATA LOCAL INFILE' command",true); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; - if (mirror==false) { - RequestEnd_mysql(NULL); - } - l_free(pkt->size,pkt->ptr); - return true; - } else { - if (mysql_thread___verbose_query_error) { - proxy_warning( - "Command '%.*s' refers to file in ProxySQL instance, NOT on client side!\n", - pkt->size - sizeof(mysql_hdr) - 1, - static_cast(pkt->ptr) + 5 - ); - } else { - proxy_warning( - "Command 'LOAD DATA LOCAL INFILE' refers to file in ProxySQL instance, NOT on client side!\n" - ); - } - } - } - - return false; -} - -void Client_Session::handler_WCDSS_MYSQL_COM_QUERY___create_mirror_session() { - if (pktH->size < 15*1024*1024 && (qpo->mirror_hostgroup >= 0 || qpo->mirror_flagOUT >= 0)) { - // check if there are too many mirror sessions in queue - if (thread->mirror_queue_mysql_sessions->len >= (unsigned int)mysql_thread___mirror_max_queue_length) { - return; - } - // at this point, we will create the new session - // we will later decide if queue it or sent it immediately - -// int i=0; -// for (i=0;i<100;i++) { - Client_Session *newsess=NULL; - if (thread->mirror_queue_mysql_sessions_cache->len==0) { - newsess=new Client_Session(); - newsess->client_myds = new ProxySQL_Data_Stream(); - newsess->client_myds->DSS=STATE_SLEEP; - newsess->client_myds->sess=newsess; - newsess->client_myds->fd=0; - newsess->client_myds->myds_type=MYDS_FRONTEND; - newsess->client_myds->PSarrayOUT= new PtrSizeArray(); - newsess->thread_session_id=__sync_fetch_and_add(&glovars.thread_id,1); - if (newsess->thread_session_id==0) { - newsess->thread_session_id=__sync_fetch_and_add(&glovars.thread_id,1); - } - newsess->status=WAITING_CLIENT_DATA; - MySQL_Connection *myconn=new MySQL_Connection; - newsess->client_myds->attach_connection(myconn); - newsess->client_myds->myprot.init(&newsess->client_myds, newsess->client_myds->myconn->userinfo, newsess); - newsess->mirror=true; - newsess->client_myds->destroy_queues(); - } else { - newsess=(Client_Session *)thread->mirror_queue_mysql_sessions_cache->remove_index_fast(0); - } - newsess->client_myds->myconn->userinfo->set(client_myds->myconn->userinfo); - newsess->to_process=1; - newsess->default_hostgroup=default_hostgroup; - if (qpo->mirror_hostgroup>= 0) { - newsess->mirror_hostgroup=qpo->mirror_hostgroup; // in the new session we copy the mirror hostgroup - } else { - newsess->mirror_hostgroup=default_hostgroup; // copy the default - } - newsess->mirror_flagOUT=qpo->mirror_flagOUT; // in the new session we copy the mirror flagOUT - if (newsess->default_schema==NULL) { - newsess->default_schema=strdup(default_schema); - } else { - if (strcmp(newsess->default_schema,default_schema)) { - free(newsess->default_schema); - newsess->default_schema=strdup(default_schema); - } - } - newsess->mirrorPkt.size=pktH->size; - newsess->mirrorPkt.ptr=l_alloc(newsess->mirrorPkt.size); - memcpy(newsess->mirrorPkt.ptr,pktH->ptr,pktH->size); - - if (thread->mirror_queue_mysql_sessions->len==0) { - // there are no sessions in the queue, we try to execute immediately - // Only mysql_thread___mirror_max_concurrency mirror session can run in parallel - if (__sync_add_and_fetch(&GloPWTH->status_variables.mirror_sessions_current,1) > (unsigned int)mysql_thread___mirror_max_concurrency ) { - // if the limit is reached, we queue it instead - __sync_sub_and_fetch(&GloPWTH->status_variables.mirror_sessions_current,1); - thread->mirror_queue_mysql_sessions->add(newsess); - } else { - GloPWTH->status_variables.p_gauge_array[p_th_gauge::mirror_concurrency]->Increment(); - thread->register_session(newsess); - newsess->handler(); // execute immediately - //newsess->to_process=0; - if (newsess->status==WAITING_CLIENT_DATA) { // the mirror session has completed - thread->unregister_session(thread->mysql_sessions->len-1); - unsigned int l = (unsigned int)mysql_thread___mirror_max_concurrency; - if (thread->mirror_queue_mysql_sessions->len*0.3 > l) l=thread->mirror_queue_mysql_sessions->len*0.3; - if (thread->mirror_queue_mysql_sessions_cache->len <= l) { - bool to_cache=true; - if (newsess->mybe) { - if (newsess->mybe->server_myds) { - to_cache=false; - } - } - if (to_cache) { - __sync_sub_and_fetch(&GloPWTH->status_variables.mirror_sessions_current,1); - GloPWTH->status_variables.p_gauge_array[p_th_gauge::mirror_concurrency]->Decrement(); - thread->mirror_queue_mysql_sessions_cache->add(newsess); - } else { - delete newsess; - } - } else { - delete newsess; - } - } - } - } else { - thread->mirror_queue_mysql_sessions->add(newsess); - } - } -} - -int Client_Session::handler_again___status_PINGING_SERVER() { - assert(mybe->server_myds->myconn); - ProxySQL_Data_Stream *myds=mybe->server_myds; - MySQL_Connection *myconn=myds->myconn; - int rc=myconn->async_ping(myds->revents); - if (rc==0) { - myconn->async_state_machine=ASYNC_IDLE; - myconn->compute_unknown_transaction_status(); - //if (mysql_thread___multiplexing && (myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { - // due to issue #2096 we disable the global check on mysql_thread___multiplexing - if ((myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { - myds->return_MySQL_Connection_To_Pool(); - } else { - myds->destroy_MySQL_Connection_From_Pool(true); - } - delete mybe->server_myds; - mybe->server_myds=NULL; - set_status(session_status___NONE); - return -1; - } else { - if (rc==-1 || rc==-2) { - if (rc==-2) { - unsigned long long us = mysql_thread___ping_timeout_server*1000; - us += thread->curtime; - us -= myds->wait_until; - proxy_error("Ping timeout during ping on %s:%d after %lluus (timeout %dms)\n", myconn->parent->address, myconn->parent->port, us, mysql_thread___ping_timeout_server); - MyHGM->p_update_mysql_error_counter(p_mysql_error_type::proxysql, myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, ER_PROXYSQL_PING_TIMEOUT); - } else { // rc==-1 - int myerr=mysql_errno(myconn->mysql); - detected_broken_connection(__FILE__ , __LINE__ , __func__ , "during ping", myconn, myerr, mysql_error(myconn->mysql) , true); - MyHGM->p_update_mysql_error_counter(p_mysql_error_type::mysql, myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myerr); - } - myds->destroy_MySQL_Connection_From_Pool(false); - myds->fd=0; - delete mybe->server_myds; - mybe->server_myds=NULL; - return -1; - } else { - // rc==1 , nothing to do for now - if (myds->mypolls==NULL) { - thread->mypolls.add(POLLIN|POLLOUT, myds->fd, myds, thread->curtime); - } - } - } - return 0; -} - -int Client_Session::handler_again___status_RESETTING_CONNECTION() { - assert(mybe->server_myds->myconn); - ProxySQL_Data_Stream *myds=mybe->server_myds; - MySQL_Connection *myconn=myds->myconn; - if (myds->mypolls==NULL) { - thread->mypolls.add(POLLIN|POLLOUT, myds->fd, myds, thread->curtime); - } - myds->DSS=STATE_MARIADB_QUERY; - // we recreate local_stmts : see issue #752 - delete myconn->local_stmts; - myconn->local_stmts=new MySQL_STMTs_local_v14(false); // false by default, it is a backend - int rc=myconn->async_change_user(myds->revents); - if (rc==0) { - __sync_fetch_and_add(&MyHGM->status.backend_change_user, 1); - //myds->myconn->userinfo->set(client_myds->myconn->userinfo); - myds->myconn->reset(); - myds->DSS = STATE_MARIADB_GENERIC; - myconn->async_state_machine=ASYNC_IDLE; -// if (mysql_thread___multiplexing && (myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { - myds->return_MySQL_Connection_To_Pool(); -// } else { -// myds->destroy_MySQL_Connection_From_Pool(true); -// } - delete mybe->server_myds; - mybe->server_myds=NULL; - set_status(session_status___NONE); - return -1; - } else { - if (rc==-1 || rc==-2) { - if (rc==-2) { - proxy_error("Change user timeout during COM_CHANGE_USER on %s , %d\n", myconn->parent->address, myconn->parent->port); - MyHGM->p_update_mysql_error_counter(p_mysql_error_type::mysql, myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, ER_PROXYSQL_CHANGE_USER_TIMEOUT); - } else { // rc==-1 - int myerr=mysql_errno(myconn->mysql); - MyHGM->p_update_mysql_error_counter( - p_mysql_error_type::mysql, - myconn->parent->myhgc->hid, - myconn->parent->address, - myconn->parent->port, - ( myerr ? myerr : ER_PROXYSQL_OFFLINE_SRV ) - ); - if (myerr != 0) { - proxy_error("Detected an error during COM_CHANGE_USER on (%d,%s,%d) , FD (Conn:%d , MyDS:%d) : %d, %s\n", myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myds->fd, myds->myconn->fd, myerr, mysql_error(myconn->mysql)); - } else { - proxy_error( - "Detected an error during COM_CHANGE_USER on (%d,%s,%d) , FD (Conn:%d , MyDS:%d) : %d, %s\n", - myconn->parent->myhgc->hid, - myconn->parent->address, - myconn->parent->port, - myds->fd, - myds->myconn->fd, - ER_PROXYSQL_OFFLINE_SRV, - "Detected offline server prior to statement execution" - ); - } - } - myds->destroy_MySQL_Connection_From_Pool(false); - myds->fd=0; - //delete mybe->server_myds; - //mybe->server_myds=NULL; - RequestEnd_mysql(myds); //fix bug #682 - return -1; - } else { - // rc==1 , nothing to do for now - if (myds->mypolls==NULL) { - thread->mypolls.add(POLLIN|POLLOUT, myds->fd, myds, thread->curtime); - } - } - } - return 0; -} - - -void Client_Session::handler_again___new_thread_to_kill_connection() { - ProxySQL_Data_Stream *myds=mybe->server_myds; - if (myds->myconn && myds->myconn->mysql) { - if (myds->killed_at==0) { - myds->wait_until=0; - myds->killed_at=thread->curtime; - //fprintf(stderr,"Expired: %llu, %llu\n", mybe->server_myds->wait_until, thread->curtime); - MySQL_Connection_userinfo *ui=client_myds->myconn->userinfo; - char *auth_password=NULL; - if (ui->password) { - if (ui->password[0]=='*') { // we don't have the real password, let's pass sha1 - auth_password=ui->sha1_pass; - } else { - auth_password=ui->password; + auth_password=ui->password; } } KillArgs *ka = new KillArgs(ui->username, auth_password, myds->myconn->parent->address, myds->myconn->parent->port, myds->myconn->parent->myhgc->hid, myds->myconn->mysql->thread_id, KILL_QUERY, thread); @@ -1716,6 +987,7 @@ void Client_Session::handler_again___new_thread_to_kill_connection() { // true should jump to handler_again #define NEXT_IMMEDIATE_NEW(new_st) do { set_status(new_st); return true; } while (0) +/* bool Client_Session::handler_again___verify_backend_multi_statement() { if ((client_myds->myconn->options.client_flag & CLIENT_MULTI_STATEMENTS) != (mybe->server_myds->myconn->options.client_flag & CLIENT_MULTI_STATEMENTS)) { @@ -1744,7 +1016,8 @@ bool Client_Session::handler_again___verify_backend_multi_statement() { } return false; } - +*/ +/* bool Client_Session::handler_again___verify_init_connect() { if (mybe->server_myds->myconn->options.init_connect_sent==false) { // we needs to set it to true @@ -1773,7 +1046,8 @@ bool Client_Session::handler_again___verify_init_connect() { } return false; } - +*/ +/* bool Client_Session::handler_again___verify_backend_session_track_gtids() { bool ret = false; proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session %p , client: %s , backend: %s\n", this, client_myds->myconn->options.session_track_gtids, mybe->server_myds->myconn->options.session_track_gtids); @@ -1838,60 +1112,6 @@ bool Client_Session::handler_again___verify_backend_session_track_gtids() { return ret; } -bool Client_Session::handler_again___verify_ldap_user_variable() { - bool ret = false; - if (mybe->server_myds->myconn->options.ldap_user_variable_sent==false) { - ret = true; - } - if (mybe->server_myds->myconn->options.ldap_user_variable_value == NULL) { - ret = true; - } - if (ret==false) { - if (mybe->server_myds->myconn->options.ldap_user_variable_sent) { - if (client_myds && client_myds->myconn) { - if (client_myds->myconn->userinfo) { - if (client_myds->myconn->userinfo->fe_username) { - if (strcmp(mybe->server_myds->myconn->options.ldap_user_variable_value,client_myds->myconn->userinfo->fe_username)) { - ret = true; - free(mybe->server_myds->myconn->options.ldap_user_variable); - mybe->server_myds->myconn->options.ldap_user_variable = NULL; - free(mybe->server_myds->myconn->options.ldap_user_variable_value); - mybe->server_myds->myconn->options.ldap_user_variable_value = NULL; - mybe->server_myds->myconn->options.ldap_user_variable_sent = false; - } - } - } - } - } - } - if (ret) { - // we needs to set it to true - mybe->server_myds->myconn->options.ldap_user_variable_sent=true; - if (mysql_thread___ldap_user_variable) { - // we send ldap user variable query only if set - mybe->server_myds->myconn->options.ldap_user_variable=strdup(mysql_thread___ldap_user_variable); - switch(status) { // this switch can be replaced with a simple previous_status.push(status), but it is here for readibility - case PROCESSING_QUERY: - previous_status.push(PROCESSING_QUERY); - break; - case PROCESSING_STMT_PREPARE: - previous_status.push(PROCESSING_STMT_PREPARE); - break; - case PROCESSING_STMT_EXECUTE: - previous_status.push(PROCESSING_STMT_EXECUTE); - break; - default: - // LCOV_EXCL_START - assert(0); - break; - // LCOV_EXCL_STOP - } - NEXT_IMMEDIATE_NEW(SETTING_LDAP_USER_VARIABLE); - } - } - return false; -} - bool Client_Session::handler_again___verify_backend_autocommit() { if (sending_set_autocommit) { // if sending_set_autocommit==true, the next query proxysql is going @@ -1972,14 +1192,6 @@ bool Client_Session::handler_again___verify_backend_autocommit() { } } else { // mysql_thread___enforce_autocommit_on_reads == true // this code seems wrong. Removed -/* - if (mybe->server_myds->myconn->IsActiveTransaction() == false) { - if (status == PROCESSING_QUERY) { - previous_status.push(PROCESSING_QUERY); - NEXT_IMMEDIATE_NEW(CHANGING_AUTOCOMMIT); - } - } -*/ } } } @@ -2305,7 +1517,7 @@ bool Client_Session::handler_again___status_CHANGING_CHARSET(int *_rc) { ProxySQL_Data_Stream *myds=mybe->server_myds; MySQL_Connection *myconn=myds->myconn; - /* Validate that server can support client's charset */ + // Validate that server can support client's charset if (!validate_charset(this, SQL_CHARACTER_SET_CLIENT, *_rc)) { return false; } @@ -2507,25 +1719,6 @@ bool Client_Session::handler_again___status_SETTING_GENERIC_VARIABLE(int *_rc, c int myerr=mysql_errno(myconn->mysql); switch (myerr) { case 1231: -/* - too complicated code? - if (mysql_thread___multiplexing && (myconn->reusable==true) && myconn->IsActiveTransaction()==false && myconn->MultiplexDisabled()==false) { - myds->DSS=STATE_NOT_INITIALIZED; - if (mysql_thread___autocommit_false_not_reusable && myconn->IsAutoCommit()==false) { - if (mysql_thread___reset_connection_algorithm == 2) { - create_new_session_and_reset_mysql_connection(myds); - } else { - myds->destroy_MySQL_Connection_From_Pool(true); - } - } else { - myds->return_MySQL_Connection_To_Pool(); - } - } else { - myconn->async_state_machine=ASYNC_IDLE; - myds->DSS=STATE_MARIADB_GENERIC; - } - break; -*/ default: myds->destroy_MySQL_Connection_From_Pool(true); break; @@ -2674,7 +1867,7 @@ bool Client_Session::handler_again___status_CHANGING_SCHEMA(int *_rc) { } return false; } - +*/ bool Client_Session::handler_again___status_CONNECTING_SERVER(int *_rc) { //fprintf(stderr,"CONNECTING_SERVER\n"); @@ -2695,8 +1888,12 @@ bool Client_Session::handler_again___status_CONNECTING_SERVER(int *_rc) { if (thread) { thread->status_variables.stvar[st_var_max_connect_timeout_err]++; } - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,9001,(char *)"HY000",buf, true); - RequestEnd_mysql(mybe->server_myds); + assert(session_type==PROXYSQL_SESSION_MYSQL); + if (session_type==PROXYSQL_SESSION_MYSQL) { + MySQL_Data_Stream * client_myds = ((MySQL_Session *)this)->client_myds; + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,9001,(char *)"HY000",buf, true); + ((MySQL_Session *)this)->RequestEnd_mysql(mybe->server_myds); + } std::string errmsg; generate_status_one_hostgroup(current_hostgroup, errmsg); proxy_error("%s . HG status: %s\n", buf, errmsg.c_str()); @@ -2742,7 +1939,7 @@ bool Client_Session::handler_again___status_CONNECTING_SERVER(int *_rc) { *_rc=1; return false; } else { - ProxySQL_Data_Stream *myds=mybe->server_myds; + MySQL_Data_Stream *myds=mybe->server_myds; MySQL_Connection *myconn=myds->myconn; int rc; if (default_hostgroup<0) { @@ -2817,6 +2014,11 @@ bool Client_Session::handler_again___status_CONNECTING_SERVER(int *_rc) { NEXT_IMMEDIATE_NEW(CONNECTING_SERVER); } else { __exit_handler_again___status_CONNECTING_SERVER_with_err: + MySQL_Data_Stream * client_myds = NULL; + assert(session_type==PROXYSQL_SESSION_MYSQL); + if (session_type==PROXYSQL_SESSION_MYSQL) { + client_myds = ((MySQL_Session *)this)->client_myds; + } int myerr=mysql_errno(myconn->mysql); if (myerr) { char sqlstate[10]; @@ -2832,7 +2034,7 @@ __exit_handler_again___status_CONNECTING_SERVER_with_err: } if (session_fast_forward==false) { // see bug #979 - RequestEnd_mysql(myds); + ((MySQL_Session *)this)->RequestEnd_mysql(myds); } while (previous_status.size()) { st=previous_status.top(); @@ -2855,7 +2057,7 @@ __exit_handler_again___status_CONNECTING_SERVER_with_err: } bool Client_Session::handler_again___status_CHANGING_USER_SERVER(int *_rc) { assert(mybe->server_myds->myconn); - ProxySQL_Data_Stream *myds=mybe->server_myds; + MySQL_Data_Stream *myds=mybe->server_myds; MySQL_Connection *myconn=myds->myconn; myds->DSS=STATE_MARIADB_QUERY; enum session_status st=status; @@ -2872,10 +2074,15 @@ bool Client_Session::handler_again___status_CHANGING_USER_SERVER(int *_rc) { } int rc=myconn->async_change_user(myds->revents); if (rc==0) { - __sync_fetch_and_add(&MyHGM->status.backend_change_user, 1); - myds->myconn->userinfo->set(client_myds->myconn->userinfo); - myds->myconn->reset(); - myds->DSS = STATE_MARIADB_GENERIC; + assert(session_type==PROXYSQL_SESSION_MYSQL); + MySQL_Data_Stream *client_myds = NULL; + if (session_type==PROXYSQL_SESSION_MYSQL) { + client_myds = ((MySQL_Session *)this)->client_myds; + __sync_fetch_and_add(&MyHGM->status.backend_change_user, 1); + myds->myconn->userinfo->set(client_myds->myconn->userinfo); + myds->myconn->reset(); + myds->DSS = STATE_MARIADB_GENERIC; + } st = previous_status.top(); previous_status.pop(); NEXT_IMMEDIATE_NEW(st); @@ -2912,10 +2119,15 @@ bool Client_Session::handler_again___status_CHANGING_USER_SERVER(int *_rc) { previous_status.pop(); char sqlstate[10]; sprintf(sqlstate,"%s",mysql_sqlstate(myconn->mysql)); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,mysql_errno(myconn->mysql),sqlstate,mysql_error(myconn->mysql)); - myds->destroy_MySQL_Connection_From_Pool(true); - myds->fd=0; - RequestEnd_mysql(myds); //fix bug #682 + MySQL_Data_Stream *client_myds = NULL; + assert(session_type==PROXYSQL_SESSION_MYSQL); + if (session_type==PROXYSQL_SESSION_MYSQL) { + client_myds = ((MySQL_Session *)this)->client_myds; + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,mysql_errno(myconn->mysql),sqlstate,mysql_error(myconn->mysql)); + myds->destroy_MySQL_Connection_From_Pool(true); + myds->fd=0; + ((MySQL_Session *)this)->RequestEnd_mysql(myds); //fix bug #682 + } } } else { if (rc==-2) { @@ -2941,90 +2153,6 @@ bool Client_Session::handler_again___status_CHANGING_USER_SERVER(int *_rc) { return false; } -bool Client_Session::handler_again___status_CHANGING_AUTOCOMMIT(int *_rc) { - //fprintf(stderr,"CHANGING_AUTOCOMMIT\n"); - assert(mybe->server_myds->myconn); - ProxySQL_Data_Stream *myds=mybe->server_myds; - MySQL_Connection *myconn=myds->myconn; - myds->DSS=STATE_MARIADB_QUERY; - enum session_status st=status; - if (myds->mypolls==NULL) { - thread->mypolls.add(POLLIN|POLLOUT, mybe->server_myds->fd, mybe->server_myds, thread->curtime); - } - bool ac = autocommit; - if (autocommit == false) { // also IsAutoCommit==false - if (mysql_thread___enforce_autocommit_on_reads == false) { - if (mybe->server_myds->myconn->IsAutoCommit() == false) { - if (mybe->server_myds->myconn->IsActiveTransaction() == false) { - if (CurrentQuery.is_select_NOT_for_update()==true) { - // client wants autocommit=0 - // enforce_autocommit_on_reads=false - // there is no transaction - // this seems to be the first query, and a SELECT not FOR UPDATE - // we will switch back to autcommit=1 - ac = true; - } - } else { - st=previous_status.top(); - previous_status.pop(); - myds->DSS = STATE_MARIADB_GENERIC; - NEXT_IMMEDIATE_NEW(st); - } - } - } - } - int rc=myconn->async_set_autocommit(myds->revents, ac); - if (rc==0) { - st=previous_status.top(); - previous_status.pop(); - myds->DSS = STATE_MARIADB_GENERIC; - NEXT_IMMEDIATE_NEW(st); - } else { - if (rc==-1) { - // the command failed - int myerr=mysql_errno(myconn->mysql); - MyHGM->p_update_mysql_error_counter( - p_mysql_error_type::mysql, - myconn->parent->myhgc->hid, - myconn->parent->address, - myconn->parent->port, - ( myerr ? myerr : ER_PROXYSQL_OFFLINE_SRV ) - ); - if (myerr >= 2000 || myerr == 0) { - bool retry_conn=false; - // client error, serious - detected_broken_connection(__FILE__ , __LINE__ , __func__ , "during SET AUTOCOMMIT", myconn, myerr, mysql_error(myconn->mysql)); - if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { - retry_conn=true; - } - myds->destroy_MySQL_Connection_From_Pool(false); - myds->fd=0; - if (retry_conn) { - myds->DSS=STATE_NOT_INITIALIZED; - NEXT_IMMEDIATE_NEW(CONNECTING_SERVER); - } - *_rc=-1; - return false; - } else { - proxy_warning("Error during SET AUTOCOMMIT: %d, %s\n", myerr, mysql_error(myconn->mysql)); - // we won't go back to PROCESSING_QUERY - st=previous_status.top(); - previous_status.pop(); - char sqlstate[10]; - sprintf(sqlstate,"%s",mysql_sqlstate(myconn->mysql)); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,mysql_errno(myconn->mysql),sqlstate,mysql_error(myconn->mysql)); - myds->destroy_MySQL_Connection_From_Pool(true); - myds->fd=0; - RequestEnd_mysql(myds); - status=WAITING_CLIENT_DATA; - client_myds->DSS=STATE_SLEEP; - } - } else { - // rc==1 , nothing to do for now - } - } - return false; -} /* // this function was inline inside Client_Session::get_pkts_from_client @@ -3330,122 +2458,6 @@ void Client_Session::handler_WCDSS_MYSQL_COM_QUERY___not_mysql(PtrSize_t& pkt) { } -// this function was inline inside Client_Session::get_pkts_from_client -// where: -// status = WAITING_CLIENT_DATA -// client_myds->DSS = STATE_SLEEP -// enum_mysql_command = _MYSQL_COM_QUERY -// it searches for SQL injection -// it returns true if it detected an SQL injection -bool Client_Session::handler_WCDSS_MYSQL_COM_QUERY_detect_SQLi() { - if (client_myds->com_field_list == false) { - if (qpo->firewall_whitelist_mode != WUS_OFF) { - struct libinjection_sqli_state state; - int issqli; - const char * input = (char *)CurrentQuery.QueryPointer; - size_t slen = CurrentQuery.QueryLength; - libinjection_sqli_init(&state, input, slen, FLAG_SQL_MYSQL); - issqli = libinjection_is_sqli(&state); - if (issqli) { - bool allow_sqli = false; - allow_sqli = GloQPro->whitelisted_sqli_fingerprint(state.fingerprint); - if (allow_sqli) { - thread->status_variables.stvar[st_var_whitelisted_sqli_fingerprint]++; - } else { - thread->status_variables.stvar[st_var_automatic_detected_sqli]++; - char * username = client_myds->myconn->userinfo->username; - char * client_address = client_myds->addr.addr; - proxy_error("SQLinjection detected with fingerprint of '%s' from client %s@%s . Query listed below:\n", state.fingerprint, username, client_address); - fwrite(CurrentQuery.QueryPointer, CurrentQuery.QueryLength, 1, stderr); - fprintf(stderr,"\n"); - RequestEnd_mysql(NULL); - return true; - } - } - } - } - return false; -} - -// this function was inline inside Client_Session::get_pkts_from_client -// where: -// status = WAITING_CLIENT_DATA -// client_myds->DSS = STATE_SLEEP_MULTI_PACKET -// -// replacing the single goto with return true -bool Client_Session::handler_WCDSS_MULTI_PACKET(PtrSize_t& pkt) { - if (client_myds->multi_pkt.ptr==NULL) { - // not initialized yet - client_myds->multi_pkt.ptr=pkt.ptr; - client_myds->multi_pkt.size=pkt.size; - } else { - PtrSize_t tmp_pkt; - tmp_pkt.ptr=client_myds->multi_pkt.ptr; - tmp_pkt.size=client_myds->multi_pkt.size; - client_myds->multi_pkt.size = pkt.size + tmp_pkt.size-sizeof(mysql_hdr); - client_myds->multi_pkt.ptr = l_alloc(client_myds->multi_pkt.size); - memcpy(client_myds->multi_pkt.ptr, tmp_pkt.ptr, tmp_pkt.size); - memcpy((char *)client_myds->multi_pkt.ptr + tmp_pkt.size , (char *)pkt.ptr+sizeof(mysql_hdr) , pkt.size-sizeof(mysql_hdr)); // the header is not copied - l_free(tmp_pkt.size , tmp_pkt.ptr); - l_free(pkt.size , pkt.ptr); - } - if (pkt.size==(0xFFFFFF+sizeof(mysql_hdr))) { // there are more packets - //goto __get_pkts_from_client; - return true; - } else { - // no more packets, move everything back to pkt and proceed - pkt.ptr=client_myds->multi_pkt.ptr; - pkt.size=client_myds->multi_pkt.size; - client_myds->multi_pkt.size=0; - client_myds->multi_pkt.ptr=NULL; - client_myds->DSS=STATE_SLEEP; - } - return false; -} - - -// this function was inline inside Client_Session::get_pkts_from_client -// where: -// status = WAITING_CLIENT_DATA -// client_myds->DSS = STATE_SLEEP -// enum_mysql_command in a large list of possible values -// the most common values for enum_mysql_command are handled from the calling function -// here we only process the not so common ones -// we return false if the enum_mysql_command is not found -bool Client_Session::handler_WCDSS_MYSQL_COM__various(PtrSize_t* pkt, bool* wrong_pass) { - unsigned char c; - c=*((unsigned char *)pkt->ptr+sizeof(mysql_hdr)); - switch ((enum_mysql_command)c) { - case _MYSQL_COM_CHANGE_USER: - ((MySQL_Session *)this)->handler_WCDSS_MYSQL_COM_CHANGE_USER(pkt, wrong_pass); - break; - case _MYSQL_COM_PING: - ((MySQL_Session *)this)->handler_WCDSS_MYSQL_COM_PING(pkt); - break; - case _MYSQL_COM_SET_OPTION: - ((MySQL_Session *)this)->handler_WCDSS_MYSQL_COM_SET_OPTION(pkt); - break; - case _MYSQL_COM_STATISTICS: - handler_WCDSS_MYSQL_COM_STATISTICS(pkt); - break; - case _MYSQL_COM_INIT_DB: - handler_WCDSS_MYSQL_COM_INIT_DB(pkt); - break; - case _MYSQL_COM_FIELD_LIST: - ((MySQL_Session *)this)->handler_WCDSS_MYSQL_COM_FIELD_LIST(pkt); - break; - case _MYSQL_COM_PROCESS_KILL: - ((MySQL_Session *)this)->handler_WCDSS_MYSQL_COM_PROCESS_KILL(pkt); - break; - case _MYSQL_COM_RESET_CONNECTION: - ((MySQL_Session *)this)->handler_WCDSS_MYSQL_COM_RESET_CONNECTION(pkt); - break; - default: - return false; - break; - } - return true; -} // this function was inline inside Client_Session::get_pkts_from_client @@ -3455,6 +2467,11 @@ bool Client_Session::handler_WCDSS_MYSQL_COM__various(PtrSize_t* pkt, bool* wron // this is triggered when proxysql receives a packet when doesn't expect any // for example while it is supposed to be sending resultset to client void Client_Session::handler___status_NONE_or_default(PtrSize_t& pkt) { + if (session_type==PROXYSQL_SESSION_MYSQL) { + ((MySQL_Session *)this)->handler___status_NONE_or_default(pkt); + return; + } +/* char buf[INET6_ADDRSTRLEN]; switch (client_myds->client_addr->sa_family) { case AF_INET: { @@ -3489,40 +2506,17 @@ void Client_Session::handler___status_NONE_or_default(PtrSize_t& pkt) { thread->status_variables.stvar[st_var_unexpected_packet]++; } return; -} - -// this function was inline inside Client_Session::get_pkts_from_client -// where: -// status = WAITING_CLIENT_DATA -void Client_Session::handler___status_WAITING_CLIENT_DATA___default() { - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Statuses: WAITING_CLIENT_DATA - STATE_UNKNOWN\n"); - if (mirror==false) { - char buf[INET6_ADDRSTRLEN]; - switch (client_myds->client_addr->sa_family) { - case AF_INET: { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)client_myds->client_addr; - inet_ntop(client_myds->client_addr->sa_family, &ipv4->sin_addr, buf, INET_ADDRSTRLEN); - break; - } - case AF_INET6: { - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)client_myds->client_addr; - inet_ntop(client_myds->client_addr->sa_family, &ipv6->sin6_addr, buf, INET6_ADDRSTRLEN); - break; - } - default: - sprintf(buf, "localhost"); - break; - } - // PMC-10001: A unexpected packet has been received from client. This error has two potential causes: - // * Bug: ProxySQL state machine wasn't in the correct state when a legitimate client packet was received. - // * Client error: The client incorrectly sent a packet breaking MySQL protocol. - proxy_error2(10001, "Unexpected packet from client %s . Session_status: %d , client_status: %d Disconnecting it\n", buf, status, client_myds->status); - } +*/ } int Client_Session::get_pkts_from_client(bool& wrong_pass, PtrSize_t& pkt) { int handler_ret = 0; unsigned char c; + MySQL_Data_Stream *client_myds = NULL; + assert(session_type==PROXYSQL_SESSION_MYSQL || session_type==PROXYSQL_SESSION_ADMIN || session_type==PROXYSQL_SESSION_STATS || session_type==PROXYSQL_SESSION_SQLITE || session_type==PROXYSQL_SESSION_CLICKHOUSE); + if (session_type==PROXYSQL_SESSION_MYSQL || session_type==PROXYSQL_SESSION_ADMIN || session_type==PROXYSQL_SESSION_STATS || session_type==PROXYSQL_SESSION_SQLITE || session_type==PROXYSQL_SESSION_CLICKHOUSE) { + client_myds = ((MySQL_Session *)this)->client_myds; + } __get_pkts_from_client: @@ -3539,10 +2533,10 @@ __get_pkts_from_client: case CONNECTING_CLIENT: switch (client_myds->DSS) { case STATE_SERVER_HANDSHAKE: - handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(&pkt, &wrong_pass); + ((MySQL_Session *)this)->handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(&pkt, &wrong_pass); break; case STATE_SSL_INIT: - handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(&pkt, &wrong_pass); + ((MySQL_Session *)this)->handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(&pkt, &wrong_pass); break; default: proxy_error("Detected not valid state client state: %d\n", client_myds->DSS); @@ -3571,10 +2565,13 @@ __get_pkts_from_client: } switch (client_myds->DSS) { case STATE_SLEEP_MULTI_PACKET: - if (handler_WCDSS_MULTI_PACKET(pkt)) { - // if handler_WCDSS_MULTI_PACKET - // returns true it meansa we need to reiterate - goto __get_pkts_from_client; + assert(session_type==PROXYSQL_SESSION_MYSQL); + if (session_type==PROXYSQL_SESSION_MYSQL) { + if (((MySQL_Session *)this)->handler_WCDSS_MULTI_PACKET(pkt)) { + // if handler_WCDSS_MULTI_PACKET + // returns true it meansa we need to reiterate + goto __get_pkts_from_client; + } } // Note: the above function can change DSS to STATE_SLEEP // in that case we don't break from the witch but continue @@ -3640,7 +2637,7 @@ __get_pkts_from_client: // shortly after, the packets it used to contain the query will be deallocated CurrentQuery.begin((unsigned char *)pkt.ptr,pkt.size,true); } - rc_break=handler_special_queries(&pkt); + rc_break=((MySQL_Session *)this)->handler_special_queries(&pkt); if (rc_break==true) { if (mirror==false) { // track also special queries @@ -3686,7 +2683,7 @@ __get_pkts_from_client: } if (use_db_query) { - handler_WCDSS_MYSQL_COM_QUERY_USE_DB(&pkt); + ((MySQL_Session *)this)->handler_WCDSS_MYSQL_COM_QUERY_USE_DB(&pkt); if (mirror == false) { break; @@ -3707,12 +2704,14 @@ __get_pkts_from_client: (begint.tv_sec*1000000000+begint.tv_nsec); } assert(qpo); // GloQPro->process_mysql_query() should always return a qpo - rc_break=handler_WCDSS_MYSQL_COM_QUERY_qpo(&pkt, &lock_hostgroup); - if (mirror==false && rc_break==false) { - if (mysql_thread___automatic_detect_sqli) { - if (handler_WCDSS_MYSQL_COM_QUERY_detect_SQLi()) { - handler_ret = -1; - return handler_ret; + rc_break=((MySQL_Session *)this)->handler_WCDSS_MYSQL_COM_QUERY_qpo(&pkt, &lock_hostgroup); + if (session_type==PROXYSQL_SESSION_MYSQL) { + if (mirror==false && rc_break==false) { + if (mysql_thread___automatic_detect_sqli) { + if (((MySQL_Session *)this)->handler_WCDSS_MYSQL_COM_QUERY_detect_SQLi()) { + handler_ret = -1; + return handler_ret; + } } } } @@ -3725,7 +2724,7 @@ __get_pkts_from_client: } } if (mirror==false) { - handler_WCDSS_MYSQL_COM_QUERY___create_mirror_session(); + ((MySQL_Session *)this)->handler_WCDSS_MYSQL_COM_QUERY___create_mirror_session(); } if (autocommit_on_hostgroup>=0) { @@ -3759,7 +2758,7 @@ __get_pkts_from_client: sprintf(buf, err_msg, current_hostgroup, locked_on_hostgroup, nqn.c_str(), end); client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,9005,(char *)"HY000",buf, true); thread->status_variables.stvar[st_var_hostgroup_locked_queries]++; - RequestEnd_mysql(NULL); + ((MySQL_Session *)this)->RequestEnd_mysql(NULL); free(buf); l_free(pkt.size,pkt.ptr); break; @@ -3797,7 +2796,7 @@ __get_pkts_from_client: if (GloMyLdapAuth) { if (session_type==PROXYSQL_SESSION_MYSQL) { if (mysql_thread___add_ldap_user_comment && strlen(mysql_thread___add_ldap_user_comment)) { - add_ldap_comment_to_pkt(&pkt); + ((MySQL_Session *)this)->add_ldap_comment_to_pkt(&pkt); } } } @@ -3835,18 +2834,22 @@ __get_pkts_from_client: // The not common commands are handled by "default" , that // calls the following function // handler_WCDSS_MYSQL_COM__various - if (handler_WCDSS_MYSQL_COM__various(&pkt, &wrong_pass)==false) { - // If even this cannot find the command, we return an error to the client - proxy_error("RECEIVED AN UNKNOWN COMMAND: %d -- PLEASE REPORT A BUG\n", c); - l_free(pkt.size,pkt.ptr); - handler_ret = -1; // immediately drop the connection - return handler_ret; + if (session_type==PROXYSQL_SESSION_MYSQL || session_type==PROXYSQL_SESSION_ADMIN || session_type==PROXYSQL_SESSION_STATS || session_type==PROXYSQL_SESSION_SQLITE || session_type==PROXYSQL_SESSION_CLICKHOUSE) { + if (((MySQL_Session *)this)->handler_WCDSS_MYSQL_COM__various(&pkt, &wrong_pass)==false) { + // If even this cannot find the command, we return an error to the client + proxy_error("RECEIVED AN UNKNOWN COMMAND: %d -- PLEASE REPORT A BUG\n", c); + l_free(pkt.size,pkt.ptr); + handler_ret = -1; // immediately drop the connection + return handler_ret; + } } break; } break; default: - handler___status_WAITING_CLIENT_DATA___default(); + if (session_type==PROXYSQL_SESSION_MYSQL) { + ((MySQL_Session *)this)->handler___status_WAITING_CLIENT_DATA___default(); + } handler_ret = -1; return handler_ret; break; @@ -3887,7 +2890,9 @@ __get_pkts_from_client: // 0 : no action // -1 : the calling function will return // 1 : call to NEXT_IMMEDIATE -int Client_Session::handler_ProcessingQueryError_CheckBackendConnectionStatus(ProxySQL_Data_Stream *myds) { +int Client_Session::handler_ProcessingQueryError_CheckBackendConnectionStatus(ProxySQL_Data_Stream *pds) { + assert(pds->sess->session_type==PROXYSQL_SESSION_MYSQL); + MySQL_Data_Stream *myds = (MySQL_Data_Stream *)pds; MySQL_Connection *myconn = myds->myconn; // the query failed if ( @@ -4074,207 +3079,23 @@ void Client_Session::handler_rc0_PROCESSING_STMT_EXECUTE(ProxySQL_Data_Stream *m } */ -// this function used to be inline. -// now it returns: -// true: NEXT_IMMEDIATE(CONNECTING_SERVER) needs to be called -// false: continue -bool Client_Session::handler_minus1_ClientLibraryError(ProxySQL_Data_Stream *myds, int myerr, char **errmsg) { - MySQL_Connection *myconn = myds->myconn; - bool retry_conn=false; - // client error, serious - detected_broken_connection(__FILE__ , __LINE__ , __func__ , "running query", myconn, myerr, mysql_error(myconn->mysql) , true); - if (myds->query_retries_on_failure > 0) { - myds->query_retries_on_failure--; - if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { - if (myds->myconn->MyRS && myds->myconn->MyRS->transfer_started) { - // transfer to frontend has started, we cannot retry - } else { - if (myds->myconn->mysql->server_status & SERVER_MORE_RESULTS_EXIST) { - // transfer to frontend has started, because this is, at least, - // the second resultset coming from the server - // we cannot retry - proxy_warning("Disabling query retry because SERVER_MORE_RESULTS_EXIST is set\n"); - } else { - retry_conn=true; - proxy_warning("Retrying query.\n"); - } - } - } - } - myds->destroy_MySQL_Connection_From_Pool(false); - myds->fd=0; - if (retry_conn) { - myds->DSS=STATE_NOT_INITIALIZED; - //previous_status.push(PROCESSING_QUERY); - switch(status) { // this switch can be replaced with a simple previous_status.push(status), but it is here for readibility - case PROCESSING_QUERY: - previous_status.push(PROCESSING_QUERY); - break; - case PROCESSING_STMT_PREPARE: - previous_status.push(PROCESSING_STMT_PREPARE); - break; - case PROCESSING_STMT_EXECUTE: - previous_status.push(PROCESSING_STMT_EXECUTE); - break; - default: - // LCOV_EXCL_START - assert(0); - break; - // LCOV_EXCL_STOP - } - if (*errmsg) { - free(*errmsg); - *errmsg = NULL; - } - return true; - } - if (*errmsg) { - free(*errmsg); - *errmsg = NULL; - } - return false; -} - // this function was inline -void Client_Session::handler_minus1_LogErrorDuringQuery(MySQL_Connection *myconn, int myerr, char *errmsg) { - if (mysql_thread___verbose_query_error) { - proxy_warning("Error during query on (%d,%s,%d,%lu) , user \"%s@%s\" , schema \"%s\" , %d, %s . digest_text = \"%s\"\n", myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myconn->get_mysql_thread_id(), client_myds->myconn->userinfo->username, (client_myds->addr.addr ? client_myds->addr.addr : (char *)"unknown" ), client_myds->myconn->userinfo->schemaname, myerr, ( errmsg ? errmsg : mysql_error(myconn->mysql)), CurrentQuery.QueryParserArgs.digest_text ); - } else { - proxy_warning("Error during query on (%d,%s,%d,%lu): %d, %s\n", myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myconn->get_mysql_thread_id(), myerr, ( errmsg ? errmsg : mysql_error(myconn->mysql))); - } - MyHGM->add_mysql_errors(myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, client_myds->myconn->userinfo->username, (client_myds->addr.addr ? client_myds->addr.addr : (char *)"unknown" ), client_myds->myconn->userinfo->schemaname, myerr, (char *)( errmsg ? errmsg : mysql_error(myconn->mysql))); -} - - -// this function used to be inline. -// now it returns: -// true: -// if handler_ret == -1 : return -// if handler_ret == 0 : NEXT_IMMEDIATE(CONNECTING_SERVER) needs to be called -// false: continue -bool Client_Session::handler_minus1_HandleErrorCodes(ProxySQL_Data_Stream *myds, int myerr, char **errmsg, int& handler_ret) { - bool retry_conn=false; - MySQL_Connection * myconn = myds->myconn; - handler_ret = 0; // default - switch (myerr) { - case 1317: // Query execution was interrupted - if (killed==true) { // this session is being kiled - handler_ret = -1; - return true; - } - if (myds->killed_at) { - // we intentionally killed the query - break; - } - break; - case 1047: // WSREP has not yet prepared node for application use - case 1053: // Server shutdown in progress - myconn->parent->connect_error(myerr); - if (myds->query_retries_on_failure > 0) { - myds->query_retries_on_failure--; - if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { - retry_conn=true; - proxy_warning("Retrying query.\n"); - } - } - switch (myerr) { - case 1047: // WSREP has not yet prepared node for application use - case 1053: // Server shutdown in progress - myds->destroy_MySQL_Connection_From_Pool(false); - break; - default: - if (mysql_thread___reset_connection_algorithm == 2) { - create_new_session_and_reset_mysql_connection(myds); - } else { - myds->destroy_MySQL_Connection_From_Pool(true); - } - break; - } - myconn = myds->myconn; - myds->fd=0; - if (retry_conn) { - myds->DSS=STATE_NOT_INITIALIZED; - //previous_status.push(PROCESSING_QUERY); - switch(status) { // this switch can be replaced with a simple previous_status.push(status), but it is here for readibility - case PROCESSING_QUERY: - previous_status.push(PROCESSING_QUERY); - break; - case PROCESSING_STMT_PREPARE: - previous_status.push(PROCESSING_STMT_PREPARE); - break; - default: - // LCOV_EXCL_START - assert(0); - break; - // LCOV_EXCL_STOP - } - if (*errmsg) { - free(*errmsg); - *errmsg = NULL; - } - return true; // it will call NEXT_IMMEDIATE(CONNECTING_SERVER); - //NEXT_IMMEDIATE(CONNECTING_SERVER); - } - //handler_ret = -1; - //return handler_ret; - break; - case 1153: // ER_NET_PACKET_TOO_LARGE - proxy_warning("Error ER_NET_PACKET_TOO_LARGE during query on (%d,%s,%d,%lu): %d, %s\n", myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myconn->get_mysql_thread_id(), myerr, mysql_error(myconn->mysql)); - break; - default: - break; // continue normally - } - return false; -} - -// this function used to be inline. -void Client_Session::handler_minus1_GenerateErrorMessage(ProxySQL_Data_Stream *myds, MySQL_Connection *myconn, bool& wrong_pass) { +int Client_Session::RunQuery_mysql(ProxySQL_Data_Stream *pds, MySQL_Connection *myconn) { + PROXY_TRACE2(); + int rc = 0; + assert(session_type==PROXYSQL_SESSION_MYSQL); + MySQL_Data_Stream *myds = (MySQL_Data_Stream *)pds; switch (status) { case PROCESSING_QUERY: - if (myconn) { - MySQL_Result_to_MySQL_wire(myconn->mysql, myconn->MyRS, myds); - } else { - MySQL_Result_to_MySQL_wire(NULL, NULL, myds); - } + rc=myconn->async_query(myds->revents, myds->mysql_real_query.QueryPtr,myds->mysql_real_query.QuerySize); break; case PROCESSING_STMT_PREPARE: - { - char sqlstate[10]; - if (myconn && myconn->mysql) { - sprintf(sqlstate,"%s",mysql_sqlstate(myconn->mysql)); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,mysql_errno(myconn->mysql),sqlstate,(char *)mysql_stmt_error(myconn->query.stmt)); - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_CLOSE, this, NULL); - } else { - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1, 2013, (char *)"HY000" ,(char *)"Lost connection to MySQL server during query"); - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_CLOSE, this, NULL); - } - client_myds->pkt_sid++; - if (previous_status.size()) { - // an STMT_PREPARE failed - // we have a previous status, probably STMT_EXECUTE, - // but returning to that status is not safe after STMT_PREPARE failed - // for this reason we exit immediately - wrong_pass=true; - } - } + rc=myconn->async_query(myds->revents, (char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength,&CurrentQuery.mysql_stmt); break; case PROCESSING_STMT_EXECUTE: - { - char sqlstate[10]; - if (myconn && myconn->mysql) { - if (myconn->MyRS) { - PROXY_TRACE2(); - ((MySQL_Session *)(myds->sess))->handler_rc0_PROCESSING_STMT_EXECUTE(myds); - } else { - sprintf(sqlstate,"%s",mysql_sqlstate(myconn->mysql)); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,mysql_errno(myconn->mysql),sqlstate,(char *)mysql_stmt_error(myconn->query.stmt)); - } - } else { - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1, 2013, (char *)"HY000" ,(char *)"Lost connection to MySQL server during query"); - } - client_myds->pkt_sid++; - } + PROXY_TRACE2(); + rc=myconn->async_query(myds->revents, (char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength,&CurrentQuery.mysql_stmt, CurrentQuery.stmt_meta); break; default: // LCOV_EXCL_START @@ -4282,75 +3103,27 @@ void Client_Session::handler_minus1_GenerateErrorMessage(ProxySQL_Data_Stream *m break; // LCOV_EXCL_STOP } + return rc; } -// this function was inline -void Client_Session::handler_minus1_HandleBackendConnection(ProxySQL_Data_Stream *myds, MySQL_Connection *myconn) { - if (myds->myconn) { - myds->myconn->reduce_auto_increment_delay_token(); - if (mysql_thread___multiplexing && (myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { - 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_mysql_connection(myds); - } else { - myds->destroy_MySQL_Connection_From_Pool(true); - } - } else { - myds->return_MySQL_Connection_To_Pool(); - } - } else { - myconn->async_state_machine=ASYNC_IDLE; - myds->DSS=STATE_MARIADB_GENERIC; - } - } -} - -// this function was inline -int Client_Session::RunQuery_mysql(ProxySQL_Data_Stream *myds, MySQL_Connection *myconn) { - PROXY_TRACE2(); - int rc = 0; - switch (status) { - case PROCESSING_QUERY: - rc=myconn->async_query(myds->revents, myds->mysql_real_query.QueryPtr,myds->mysql_real_query.QuerySize); - break; - case PROCESSING_STMT_PREPARE: - rc=myconn->async_query(myds->revents, (char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength,&CurrentQuery.mysql_stmt); - break; - case PROCESSING_STMT_EXECUTE: - PROXY_TRACE2(); - rc=myconn->async_query(myds->revents, (char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength,&CurrentQuery.mysql_stmt, CurrentQuery.stmt_meta); +void Client_Session::writeout() { + switch (session_type) { + case PROXYSQL_SESSION_MYSQL: + case PROXYSQL_SESSION_ADMIN: + case PROXYSQL_SESSION_STATS: + case PROXYSQL_SESSION_SQLITE: + case PROXYSQL_SESSION_CLICKHOUSE: + ((MySQL_Session *)this)->writeout(); break; default: - // LCOV_EXCL_START - assert(0); break; - // LCOV_EXCL_STOP } - return rc; } // this function was inline void Client_Session::handler___status_WAITING_CLIENT_DATA() { - if (mybes) { - MySQL_Backend *_mybe; - unsigned int i; - for (i=0; i < mybes->len; i++) { - _mybe=(MySQL_Backend *)mybes->index(i); - if (_mybe->server_myds) { - ProxySQL_Data_Stream *_myds=_mybe->server_myds; - if (_myds->myconn) { - if (_myds->myconn->multiplex_delayed) { - if (_myds->wait_until <= thread->curtime) { - _myds->wait_until=0; - _myds->myconn->multiplex_delayed=false; - _myds->DSS=STATE_NOT_INITIALIZED; - _myds->return_MySQL_Connection_To_Pool(); - } - } - } - } - } + if (session_type==PROXYSQL_SESSION_MYSQL) { + ((MySQL_Session *)this)->handler___status_WAITING_CLIENT_DATA(); } } @@ -4383,6 +3156,9 @@ int Client_Session::handler() { } // FIXME: Sessions without frontend are an ugly hack if (session_fast_forward==false) { + MySQL_Data_Stream * client_myds = NULL; + assert(session_type==PROXYSQL_SESSION_MYSQL || session_type==PROXYSQL_SESSION_ADMIN || session_type==PROXYSQL_SESSION_STATS || session_type==PROXYSQL_SESSION_SQLITE || session_type==PROXYSQL_SESSION_CLICKHOUSE); + client_myds = ((MySQL_Session *)this)->client_myds; if (client_myds==NULL) { // if we are here, probably we are trying to ping backends proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Processing session %p without client_myds\n", this); @@ -4423,7 +3199,10 @@ handler_again: // register the mysql_data_stream thread->mypolls.add(POLLIN|POLLOUT, mybe->server_myds->fd, mybe->server_myds, thread->curtime); } - client_myds->PSarrayOUT->copy_add(mybe->server_myds->PSarrayIN, 0, mybe->server_myds->PSarrayIN->len); + if (session_type==PROXYSQL_SESSION_MYSQL) { + MySQL_Data_Stream *client_myds = ((MySQL_Session *)this)->client_myds; + client_myds->PSarrayOUT->copy_add(mybe->server_myds->PSarrayIN, 0, mybe->server_myds->PSarrayIN->len); + } while (mybe->server_myds->PSarrayIN->len) mybe->server_myds->PSarrayIN->remove_index(mybe->server_myds->PSarrayIN->len-1,NULL); break; case CONNECTING_CLIENT: @@ -4431,8 +3210,9 @@ handler_again: // FIXME: to implement break; case PINGING_SERVER: + assert(session_type==PROXYSQL_SESSION_MYSQL); { - int rc=handler_again___status_PINGING_SERVER(); + int rc = ((MySQL_Session *)this)->handler_again___status_PINGING_SERVER(); if (rc==-1) { // if the ping fails, we destroy the session handler_ret = -1; return handler_ret; @@ -4441,8 +3221,9 @@ handler_again: break; case RESETTING_CONNECTION: + assert(session_type==PROXYSQL_SESSION_MYSQL); { - int rc = handler_again___status_RESETTING_CONNECTION(); + int rc = ((MySQL_Session *)this)->handler_again___status_RESETTING_CONNECTION(); if (rc==-1) { // we always destroy the session handler_ret = -1; return handler_ret; @@ -4453,6 +3234,7 @@ handler_again: case PROCESSING_STMT_PREPARE: case PROCESSING_STMT_EXECUTE: case PROCESSING_QUERY: + assert(session_type==PROXYSQL_SESSION_MYSQL); //fprintf(stderr,"PROCESSING_QUERY\n"); if (pause_until > thread->curtime) { handler_ret = 0; @@ -4470,33 +3252,7 @@ handler_again: || (killed==true) // session was killed by admin ) { - // we only log in case on timing out here. Logging for 'killed' is done in the places that hold that contextual information. - if (mybe->server_myds->myconn && (mybe->server_myds->myconn->async_state_machine != ASYNC_IDLE) && mybe->server_myds->wait_until && (thread->curtime >= mybe->server_myds->wait_until)) { - std::string query {}; - - if (CurrentQuery.stmt_info == NULL) { // text protocol - query = std::string { mybe->server_myds->myconn->query.ptr, mybe->server_myds->myconn->query.length }; - } else { // prepared statement - query = std::string { CurrentQuery.stmt_info->query, CurrentQuery.stmt_info->query_length }; - } - - std::string client_addr { "" }; - int client_port = 0; - - if (client_myds) { - client_addr = client_myds->addr.addr ? client_myds->addr.addr : ""; - client_port = client_myds->addr.port; - } - - proxy_warning( - "Killing connection %s:%d because query '%s' from client '%s':%d timed out.\n", - mybe->server_myds->myconn->parent->address, - mybe->server_myds->myconn->parent->port, - query.c_str(), - client_addr.c_str(), - client_port - ); - } + ((MySQL_Session *)this)->LogKillQueryTimeout(mybe->server_myds, __FILE__, __LINE__); handler_again___new_thread_to_kill_connection(); } if (mybe->server_myds->DSS==STATE_NOT_INITIALIZED) { @@ -4519,7 +3275,7 @@ handler_again: } NEXT_IMMEDIATE(CONNECTING_SERVER); } else { - ProxySQL_Data_Stream *myds=mybe->server_myds; + MySQL_Data_Stream *myds=mybe->server_myds; MySQL_Connection *myconn=myds->myconn; mybe->server_myds->max_connect_time=0; // we insert it in mypolls only if not already there @@ -4527,85 +3283,17 @@ handler_again: thread->mypolls.add(POLLIN|POLLOUT, mybe->server_myds->fd, mybe->server_myds, thread->curtime); } if (default_hostgroup>=0) { - if (handler_again___verify_backend_user_schema()) { - goto handler_again; + if (session_type==PROXYSQL_SESSION_MYSQL) { + if (((MySQL_Session *)this)->handler_again___verify_backend_user_schema()) { + goto handler_again; + } } if (mirror==false) { // do not care about autocommit and charset if mirror proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session %p , default_HG=%d server_myds DSS=%d , locked_on_HG=%d\n", this, default_hostgroup, mybe->server_myds->DSS, locked_on_hostgroup); if (mybe->server_myds->DSS == STATE_READY || mybe->server_myds->DSS == STATE_MARIADB_GENERIC) { - if (handler_again___verify_init_connect()) { - goto handler_again; - } - if (use_ldap_auth) { - if (handler_again___verify_ldap_user_variable()) { - goto handler_again; - } - } - if (handler_again___verify_backend_autocommit()) { + if (((MySQL_Session *)this)->ProcessingRequest_MatchEnvironment(myconn)) { goto handler_again; } - if (locked_on_hostgroup == -1 || locked_on_hostgroup_and_all_variables_set == false ) { - - if (handler_again___verify_backend_multi_statement()) { - goto handler_again; - } - - if (handler_again___verify_backend_session_track_gtids()) { - goto handler_again; - } - - // Optimize network traffic when we can use 'SET NAMES' - if (verify_set_names(this)) { - goto handler_again; - } - - for (auto i = 0; i < SQL_NAME_LAST_LOW_WM; i++) { - auto client_hash = client_myds->myconn->var_hash[i]; -#ifdef DEBUG - if (GloVars.global.gdbg) { - switch (i) { - case SQL_CHARACTER_SET: - case SQL_SET_NAMES: - case SQL_CHARACTER_SET_RESULTS: - case SQL_CHARACTER_SET_CONNECTION: - case SQL_CHARACTER_SET_CLIENT: - case SQL_COLLATION_CONNECTION: - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 7, "Session %p , variable %s has value %s\n" , this, mysql_tracked_variables[i].set_variable_name , client_myds->myconn->variables[i].value); - default: - break; - } - } -#endif // DEBUG - if (client_hash) { - auto server_hash = myconn->var_hash[i]; - if (client_hash != server_hash) { - if(!myconn->var_absent[i] && mysql_variables.verify_variable(this, i)) { - goto handler_again; - } - } - } - } - MySQL_Connection *c_con = client_myds->myconn; - vector::const_iterator it_c = c_con->dynamic_variables_idx.begin(); // client connection iterator - for ( ; it_c != c_con->dynamic_variables_idx.end() ; it_c++) { - auto i = *it_c; - auto client_hash = c_con->var_hash[i]; - auto server_hash = myconn->var_hash[i]; - if (client_hash != server_hash) { - if( - !myconn->var_absent[i] - && - mysql_variables.verify_variable(this, i) - ) { - goto handler_again; - } - } - } - - if (locked_on_hostgroup != -1) { - locked_on_hostgroup_and_all_variables_set=true; - } - } } if (status==PROCESSING_STMT_EXECUTE) { CurrentQuery.mysql_stmt=myconn->local_stmts->find_backend_stmt_by_global_id(CurrentQuery.stmt_global_id); @@ -4650,140 +3338,22 @@ handler_again: } gtid_hid = -1; if (rc==0) { - - handler_rc0_Process_GTID(myconn); - - // if we are locked on hostgroup, the value of autocommit is copied from the backend connection - // see bug #3549 - if (locked_on_hostgroup >= 0) { - assert(myconn != NULL); - assert(myconn->mysql != NULL); - autocommit = myconn->mysql->server_status & SERVER_STATUS_AUTOCOMMIT; - } - - if (mirror == false) { - // Support for LAST_INSERT_ID() - if (myconn->mysql->insert_id) { - last_insert_id=myconn->mysql->insert_id; - } - if (myconn->mysql->affected_rows) { - if (myconn->mysql->affected_rows != ULLONG_MAX) { - last_HG_affected_rows = current_hostgroup; - if (mysql_thread___auto_increment_delay_multiplex && myconn->mysql->insert_id) { - myconn->auto_increment_delay_token = mysql_thread___auto_increment_delay_multiplex + 1; - __sync_fetch_and_add(&MyHGM->status.auto_increment_delay_multiplex, 1); - } - } - } - } - - switch (status) { - case PROCESSING_QUERY: - MySQL_Result_to_MySQL_wire(myconn->mysql, myconn->MyRS, myconn->myds); - break; - case PROCESSING_STMT_PREPARE: - { - enum session_status st; - if (((MySQL_Session *)this)->handler_rc0_PROCESSING_STMT_PREPARE(st, myds, prepared_stmt_with_no_params)) { - NEXT_IMMEDIATE(st); - } - } - break; - case PROCESSING_STMT_EXECUTE: - ((MySQL_Session *)this)->handler_rc0_PROCESSING_STMT_EXECUTE(myds); - break; - default: - // LCOV_EXCL_START - assert(0); - break; - // LCOV_EXCL_STOP - } - - if (mysql_thread___log_mysql_warnings_enabled) { - auto warn_no = mysql_warning_count(myconn->mysql); - if (warn_no > 0) { - RequestEnd_mysql(myds); - writeout(); - - myconn->async_state_machine=ASYNC_IDLE; - myds->DSS=STATE_MARIADB_GENERIC; - - NEXT_IMMEDIATE(SHOW_WARNINGS); - } + if (((MySQL_Session *)this)->RunQuery_Success(myconn, prepared_stmt_with_no_params)) { + goto handler_again; } - - RequestEnd_mysql(myds); - finishQuery(myds,myconn,prepared_stmt_with_no_params); } else { if (rc==-1) { // the query failed - int myerr=mysql_errno(myconn->mysql); - char *errmsg = NULL; - if (myerr == 0) { - if (CurrentQuery.mysql_stmt) { - myerr = mysql_stmt_errno(CurrentQuery.mysql_stmt); - errmsg = strdup(mysql_stmt_error(CurrentQuery.mysql_stmt)); - } - } - MyHGM->p_update_mysql_error_counter(p_mysql_error_type::mysql, myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myerr); - CurrentQuery.mysql_stmt=NULL; // immediately reset mysql_stmt - int rc1 = handler_ProcessingQueryError_CheckBackendConnectionStatus(myds); - if (rc1 == -1) { - handler_ret = -1; - return handler_ret; - } else { - if (rc1 == 1) - NEXT_IMMEDIATE(CONNECTING_SERVER); - } - if (myerr >= 2000 && myerr < 3000) { - if (handler_minus1_ClientLibraryError(myds, myerr, &errmsg)) { - NEXT_IMMEDIATE(CONNECTING_SERVER); - } else { - handler_ret = -1; - return handler_ret; - } + if (((MySQL_Session *)this)->RunQuery_Failed(myconn, wrong_pass, handler_ret)) { + goto handler_again; } else { - handler_minus1_LogErrorDuringQuery(myconn, myerr, errmsg); - if (handler_minus1_HandleErrorCodes(myds, myerr, &errmsg, handler_ret)) { - if (handler_ret == 0) - NEXT_IMMEDIATE(CONNECTING_SERVER); + if (handler_ret == -1) { return handler_ret; } - handler_minus1_GenerateErrorMessage(myds, myconn, wrong_pass); - RequestEnd_mysql(myds); - handler_minus1_HandleBackendConnection(myds, myconn); } } else { - switch (rc) { - // rc==1 , query is still running - // start sending to frontend if mysql_thread___threshold_resultset_size is reached - case 1: - if (myconn->MyRS && myconn->MyRS->result && myconn->MyRS->resultset_size > (unsigned int) mysql_thread___threshold_resultset_size) { - myconn->MyRS->get_resultset(client_myds->PSarrayOUT); - } - break; - // rc==2 : a multi-resultset (or multi statement) was detected, and the current statement is completed - case 2: - MySQL_Result_to_MySQL_wire(myconn->mysql, myconn->MyRS, myconn->myds); - if (myconn->MyRS) { // we also need to clear MyRS, so that the next staement will recreate it if needed - if (myconn->MyRS_reuse) { - delete myconn->MyRS_reuse; - } - //myconn->MyRS->reset_pid = false; - myconn->MyRS_reuse = myconn->MyRS; - myconn->MyRS=NULL; - } - NEXT_IMMEDIATE(PROCESSING_QUERY); - break; - // rc==3 , a multi statement query is still running - // start sending to frontend if mysql_thread___threshold_resultset_size is reached - case 3: - if (myconn->MyRS && myconn->MyRS->result && myconn->MyRS->resultset_size > (unsigned int) mysql_thread___threshold_resultset_size) { - myconn->MyRS->get_resultset(client_myds->PSarrayOUT); - } - break; - default: - break; + if (((MySQL_Session *)this)->RunQuery_Continue(myconn, rc)) { + goto handler_again; } } } @@ -4815,7 +3385,8 @@ handler_again: // to the connection pool when finished. Actual logging of received warnings is performed in // 'MySQL_Connection' while processing 'ASYNC_USE_RESULT_CONT'. { - ProxySQL_Data_Stream *myds=mybe->server_myds; + assert(session_type==PROXYSQL_SESSION_MYSQL); + MySQL_Data_Stream *myds=mybe->server_myds; MySQL_Connection *myconn=myds->myconn; // Setting POLLOUT is required just in case this state has been reached when 'RunQuery_mysql' from @@ -4841,7 +3412,7 @@ handler_again: ); } - RequestEnd_mysql(myds); + ((MySQL_Session *)this)->RequestEnd_mysql(myds); finishQuery(myds,myconn,prepared_stmt_with_no_params); handler_ret = 0; @@ -4866,9 +3437,14 @@ handler_again: default: { int rc = 0; - if (handler_again___multiple_statuses(&rc)) // a sort of catch all - goto handler_again; // we changed status - if (rc==-1) { // we have an error we can't handle + if (session_type==PROXYSQL_SESSION_MYSQL) { + if (((MySQL_Session *)this)->handler_again___multiple_statuses(&rc)) // a sort of catch all + goto handler_again; // we changed status + if (rc==-1) { // we have an error we can't handle + handler_ret = -1; + return handler_ret; + } + } else { handler_ret = -1; return handler_ret; } @@ -4883,1590 +3459,172 @@ __exit_DSS__STATE_NOT_INITIALIZED: if (mybe && mybe->server_myds) { if (mybe->server_myds->DSS > STATE_MARIADB_BEGIN && mybe->server_myds->DSS < STATE_MARIADB_END) { #ifdef DEBUG - ProxySQL_Data_Stream *myds=mybe->server_myds; - MySQL_Connection *myconn=mybe->server_myds->myconn; -#endif /* DEBUG */ - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Sess=%p, status=%d, server_myds->DSS==%d , revents==%d , async_state_machine=%d\n", this, status, mybe->server_myds->DSS, myds->revents, myconn->async_state_machine); - } - } - - writeout(); - - if (wrong_pass==true) { - client_myds->array2buffer_full(); - client_myds->write_to_net(); - handler_ret = -1; - return handler_ret; - } - handler_ret = 0; - return handler_ret; -} -// end ::handler() - - -bool Client_Session::handler_again___multiple_statuses(int *rc) { - bool ret = false; - switch(status) { - case CHANGING_USER_SERVER: - ret = handler_again___status_CHANGING_USER_SERVER(rc); - break; - case CHANGING_AUTOCOMMIT: - ret = handler_again___status_CHANGING_AUTOCOMMIT(rc); - break; - case CHANGING_SCHEMA: - ret = handler_again___status_CHANGING_SCHEMA(rc); - break; - case SETTING_LDAP_USER_VARIABLE: - ret = handler_again___status_SETTING_LDAP_USER_VARIABLE(rc); - break; - case SETTING_INIT_CONNECT: - ret = handler_again___status_SETTING_INIT_CONNECT(rc); - break; - case SETTING_MULTI_STMT: - ret = handler_again___status_SETTING_MULTI_STMT(rc); - break; - case SETTING_SESSION_TRACK_GTIDS: - ret = handler_again___status_SETTING_SESSION_TRACK_GTIDS(rc); - break; - case SETTING_SET_NAMES: - ret = handler_again___status_CHANGING_CHARSET(rc); - break; - default: - break; - } - return ret; -} - -void Client_Session::handler___status_CHANGING_USER_CLIENT___STATE_CLIENT_HANDSHAKE(PtrSize_t *pkt, bool *wrong_pass) { - // FIXME: no support for SSL yet - if ( - client_myds->myprot.process_pkt_auth_swich_response((unsigned char *)pkt->ptr,pkt->size)==true - ) { - l_free(pkt->size,pkt->ptr); - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p . Successful connection\n", this, client_myds); - client_myds->myprot.generate_pkt_OK(true,NULL,NULL,2,0,0,0,0,NULL); - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_CHANGE_USER_OK, this, NULL); - status=WAITING_CLIENT_DATA; - client_myds->DSS=STATE_SLEEP; - } else { - l_free(pkt->size,pkt->ptr); - *wrong_pass=true; - // FIXME: this should become close connection - client_myds->setDSS_STATE_QUERY_SENT_NET(); - char *client_addr=NULL; - if (client_myds->client_addr) { - char buf[512]; - switch (client_myds->client_addr->sa_family) { - case AF_INET: { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)client_myds->client_addr; - if (ipv4->sin_port) { - inet_ntop(client_myds->client_addr->sa_family, &ipv4->sin_addr, buf, INET_ADDRSTRLEN); - client_addr = strdup(buf); - } else { - client_addr = strdup((char *)"localhost"); - } - break; - } - case AF_INET6: { - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)client_myds->client_addr; - inet_ntop(client_myds->client_addr->sa_family, &ipv6->sin6_addr, buf, INET6_ADDRSTRLEN); - client_addr = strdup(buf); - break; - } - default: - client_addr = strdup((char *)"localhost"); - break; - } - } else { - client_addr = strdup((char *)""); - } - char *_s=(char *)malloc(strlen(client_myds->myconn->userinfo->username)+100+strlen(client_addr)); - sprintf(_s,"ProxySQL Error: Access denied for user '%s'@'%s' (using password: %s)", client_myds->myconn->userinfo->username, client_addr, (client_myds->myconn->userinfo->password ? "YES" : "NO")); - proxy_error("ProxySQL Error: Access denied for user '%s'@'%s' (using password: %s)", client_myds->myconn->userinfo->username, client_addr, (client_myds->myconn->userinfo->password ? "YES" : "NO")); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,2,1045,(char *)"28000", _s, true); -#ifdef DEBUG - if (client_myds->myconn->userinfo->password) { - char *tmp_pass=strdup(client_myds->myconn->userinfo->password); - int lpass = strlen(tmp_pass); - for (int i=2; imyconn->userinfo->username, client_addr, tmp_pass); - free(tmp_pass); - } else { - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p . Wrong credentials for frontend: %s:%s . No password. Disconnecting\n", this, client_myds, client_myds->myconn->userinfo->username, client_addr); - } -#endif //DEBUG - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_CHANGE_USER_ERR, this, NULL); - free(_s); - __sync_fetch_and_add(&MyHGM->status.access_denied_wrong_password, 1); - } -} - -void Client_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(PtrSize_t *pkt, bool *wrong_pass) { - bool is_encrypted = client_myds->encrypted; - bool handshake_response_return = client_myds->myprot.process_pkt_handshake_response((unsigned char *)pkt->ptr,pkt->size); - bool handshake_err = true; - - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p , handshake_response=%d , switching_auth_stage=%d , is_encrypted=%d , client_encrypted=%d\n", this, client_myds, handshake_response_return, client_myds->switching_auth_stage, is_encrypted, client_myds->encrypted); - if ( - (handshake_response_return == false) && (client_myds->switching_auth_stage == 1) - ) { - l_free(pkt->size,pkt->ptr); - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p . Returning\n", this, client_myds); - return; - } - - if ( - (is_encrypted == false) && // the connection was encrypted - (handshake_response_return == false) && // the authentication didn't complete - (client_myds->encrypted == true) // client is asking for encryption - ) { - // use SSL - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p . SSL_INIT\n", this, client_myds); - client_myds->DSS=STATE_SSL_INIT; - client_myds->rbio_ssl = BIO_new(BIO_s_mem()); - client_myds->wbio_ssl = BIO_new(BIO_s_mem()); - client_myds->ssl = GloVars.get_SSL_ctx(); - SSL_set_fd(client_myds->ssl, client_myds->fd); - SSL_set_accept_state(client_myds->ssl); - SSL_set_bio(client_myds->ssl, client_myds->rbio_ssl, client_myds->wbio_ssl); - l_free(pkt->size,pkt->ptr); - return; - } - - if ( - //(client_myds->myprot.process_pkt_handshake_response((unsigned char *)pkt->ptr,pkt->size)==true) - (handshake_response_return == true) - && - ( -#if defined(TEST_AURORA) || defined(TEST_GALERA) || defined(TEST_GROUPREP) - (default_hostgroup<0 && ( session_type == PROXYSQL_SESSION_ADMIN || session_type == PROXYSQL_SESSION_STATS || session_type == PROXYSQL_SESSION_SQLITE) ) -#else - (default_hostgroup<0 && ( session_type == PROXYSQL_SESSION_ADMIN || session_type == PROXYSQL_SESSION_STATS) ) -#endif // TEST_AURORA || TEST_GALERA || TEST_GROUPREP - || - (default_hostgroup == 0 && session_type == PROXYSQL_SESSION_CLICKHOUSE) - || - //(default_hostgroup>=0 && session_type == PROXYSQL_SESSION_MYSQL) - (default_hostgroup>=0 && ( session_type == PROXYSQL_SESSION_MYSQL || session_type == PROXYSQL_SESSION_SQLITE ) ) - || - ( - client_myds->encrypted==false - && - strncmp(client_myds->myconn->userinfo->username,mysql_thread___monitor_username,strlen(mysql_thread___monitor_username))==0 - ) - ) // Do not delete this line. See bug #492 - ) { - if (session_type == PROXYSQL_SESSION_ADMIN) { - if ( (default_hostgroup<0) || (strncmp(client_myds->myconn->userinfo->username,mysql_thread___monitor_username,strlen(mysql_thread___monitor_username))==0) ) { - if (default_hostgroup==STATS_HOSTGROUP) { - session_type = PROXYSQL_SESSION_STATS; - } - } - } - l_free(pkt->size,pkt->ptr); - //if (client_myds->encrypted==false) { - if (client_myds->myconn->userinfo->schemaname==NULL) { -#ifdef PROXYSQLCLICKHOUSE - if (session_type == PROXYSQL_SESSION_CLICKHOUSE) { - if (strlen(default_schema) == 0) { - free(default_schema); - default_schema = strdup((char *)"default"); - } - } -#endif /* PROXYSQLCLICKHOUSE */ - client_myds->myconn->userinfo->set_schemaname(default_schema,strlen(default_schema)); - } - int free_users=0; - int used_users=0; - if ( - ( max_connections_reached == false ) - && - ( session_type == PROXYSQL_SESSION_MYSQL || session_type == PROXYSQL_SESSION_CLICKHOUSE || session_type == PROXYSQL_SESSION_SQLITE) - ) { - //if (session_type == PROXYSQL_SESSION_MYSQL || session_type == PROXYSQL_SESSION_CLICKHOUSE) { - client_authenticated=true; - switch (session_type) { - case PROXYSQL_SESSION_SQLITE: -//#if defined(TEST_AURORA) || defined(TEST_GALERA) || defined(TEST_GROUPREP) - free_users=1; - break; -//#endif // TEST_AURORA || TEST_GALERA || TEST_GROUPREP - case PROXYSQL_SESSION_MYSQL: - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p , session_type=PROXYSQL_SESSION_MYSQL\n", this, client_myds); - if (use_ldap_auth == false) { - free_users = GloMyAuth->increase_frontend_user_connections(client_myds->myconn->userinfo->username, &used_users); - } else { - free_users = GloMyLdapAuth->increase_frontend_user_connections(client_myds->myconn->userinfo->fe_username, &used_users); - } - break; -#ifdef PROXYSQLCLICKHOUSE - case PROXYSQL_SESSION_CLICKHOUSE: - free_users=GloClickHouseAuth->increase_frontend_user_connections(client_myds->myconn->userinfo->username, &used_users); - break; -#endif /* PROXYSQLCLICKHOUSE */ - default: - // LCOV_EXCL_START - assert(0); - break; - // LCOV_EXCL_STOP - } - } else { - free_users=1; - } - if (max_connections_reached==true || free_users<=0) { - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p , max_connections_reached=%d , free_users=%d\n", this, client_myds, max_connections_reached, free_users); - client_authenticated=false; - *wrong_pass=true; - client_myds->setDSS_STATE_QUERY_SENT_NET(); - uint8_t _pid = 2; - if (client_myds->switching_auth_stage) _pid+=2; - if (max_connections_reached==true) { - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p , Too many connections\n", this, client_myds); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,_pid,1040,(char *)"08004", (char *)"Too many connections", true); - proxy_warning("mysql-max_connections reached. Returning 'Too many connections'\n"); - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_ERR, this, NULL, (char *)"mysql-max_connections reached"); - __sync_fetch_and_add(&MyHGM->status.access_denied_max_connections, 1); - } else { // see issue #794 - __sync_fetch_and_add(&MyHGM->status.access_denied_max_user_connections, 1); - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p . User '%s' has exceeded the 'max_user_connections' resource (current value: %d)\n", this, client_myds, client_myds->myconn->userinfo->username, used_users); - char *a=(char *)"User '%s' has exceeded the 'max_user_connections' resource (current value: %d)"; - char *b=(char *)malloc(strlen(a)+strlen(client_myds->myconn->userinfo->username)+16); - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_ERR, this, NULL, b); - sprintf(b,a,client_myds->myconn->userinfo->username,used_users); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,2,1226,(char *)"42000", b, true); - proxy_warning("User '%s' has exceeded the 'max_user_connections' resource (current value: %d)\n",client_myds->myconn->userinfo->username,used_users); - free(b); - } - __sync_add_and_fetch(&MyHGM->status.client_connections_aborted,1); - client_myds->DSS=STATE_SLEEP; - } else { - if ( - ( default_hostgroup==ADMIN_HOSTGROUP && strcmp(client_myds->myconn->userinfo->username,(char *)"admin")==0 ) - || - ( default_hostgroup==STATS_HOSTGROUP && strcmp(client_myds->myconn->userinfo->username,(char *)"stats")==0 ) - || - ( default_hostgroup < 0 && strcmp(client_myds->myconn->userinfo->username,(char *)"monitor")==0 ) - ) { - char *client_addr = NULL; - union { - struct sockaddr_in in; - struct sockaddr_in6 in6; - } custom_sockaddr; - struct sockaddr *addr=(struct sockaddr *)malloc(sizeof(custom_sockaddr)); - socklen_t addrlen=sizeof(custom_sockaddr); - memset(addr, 0, sizeof(custom_sockaddr)); - int rc = 0; - rc = getpeername(client_myds->fd, addr, &addrlen); - if (rc == 0) { - char buf[512]; - switch (addr->sa_family) { - case AF_INET: { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr; - inet_ntop(addr->sa_family, &ipv4->sin_addr, buf, INET_ADDRSTRLEN); - client_addr = strdup(buf); - break; - } - case AF_INET6: { - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr; - inet_ntop(addr->sa_family, &ipv6->sin6_addr, buf, INET6_ADDRSTRLEN); - client_addr = strdup(buf); - break; - } - default: - client_addr = strdup((char *)"localhost"); - break; - } - } else { - client_addr = strdup((char *)""); - } - uint8_t _pid = 2; - if (client_myds->switching_auth_stage) _pid+=2; - if (is_encrypted) _pid++; - if ( - (strcmp(client_addr,(char *)"127.0.0.1")==0) - || - (strcmp(client_addr,(char *)"localhost")==0) - || - (strcmp(client_addr,(char *)"::1")==0) - ) { - // we are good! - client_myds->myprot.generate_pkt_OK(true,NULL,NULL, _pid, 0,0,0,0,NULL); - handshake_err = false; - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_OK, this, NULL); - status=WAITING_CLIENT_DATA; - client_myds->DSS=STATE_CLIENT_AUTH_OK; - } else { - char *a=(char *)"User '%s' can only connect locally"; - char *b=(char *)malloc(strlen(a)+strlen(client_myds->myconn->userinfo->username)); - sprintf(b,a,client_myds->myconn->userinfo->username); - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_ERR, this, NULL, b); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL, _pid, 1040,(char *)"42000", b, true); - free(b); - } - free(addr); - free(client_addr); - } else { - uint8_t _pid = 2; - if (client_myds->switching_auth_stage) _pid+=2; - if (is_encrypted) _pid++; - // If this condition is met, it means that the - // 'STATE_SERVER_HANDSHAKE' being performed isn't from the start of a - // connection, but as a consequence of a 'COM_USER_CHANGE' which - // requires an 'Auth Switch'. Thus, we impose a 'pid' of '3' for the - // response 'OK' packet. See #3504 for more context. - if (change_user_auth_switch) { - _pid = 3; - change_user_auth_switch = 0; - } - if (use_ssl == true && is_encrypted == false) { - *wrong_pass=true; - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_ERR, this, NULL); - - char *_a=(char *)"ProxySQL Error: Access denied for user '%s' (using password: %s). SSL is required"; - char *_s=(char *)malloc(strlen(_a)+strlen(client_myds->myconn->userinfo->username)+32); - sprintf(_s, _a, client_myds->myconn->userinfo->username, (client_myds->myconn->userinfo->password ? "YES" : "NO")); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL, _pid, 1045,(char *)"28000", _s, true); - proxy_error("ProxySQL Error: Access denied for user '%s' (using password: %s). SSL is required\n", client_myds->myconn->userinfo->username, (client_myds->myconn->userinfo->password ? "YES" : "NO")); - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p . Access denied for user '%s' (using password: %s). SSL is required\n", this, client_myds, client_myds->myconn->userinfo->username, (client_myds->myconn->userinfo->password ? "YES" : "NO")); - __sync_add_and_fetch(&MyHGM->status.client_connections_aborted,1); - free(_s); - __sync_fetch_and_add(&MyHGM->status.access_denied_wrong_password, 1); - } else { - // we are good! - //client_myds->myprot.generate_pkt_OK(true,NULL,NULL, (is_encrypted ? 3 : 2), 0,0,0,0,NULL,false); - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p . STATE_CLIENT_AUTH_OK\n", this, client_myds); - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_OK, this, NULL); - client_myds->myprot.generate_pkt_OK(true,NULL,NULL, _pid, 0,0,0,0,NULL); - handshake_err = false; - status=WAITING_CLIENT_DATA; - client_myds->DSS=STATE_CLIENT_AUTH_OK; - } - } - } - } else { - l_free(pkt->size,pkt->ptr); - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p . Wrong credentials for frontend: disconnecting\n", this, client_myds); - *wrong_pass=true; - // FIXME: this should become close connection - client_myds->setDSS_STATE_QUERY_SENT_NET(); - char *client_addr=NULL; - if (client_myds->client_addr && client_myds->myconn->userinfo->username) { - char buf[512]; - switch (client_myds->client_addr->sa_family) { - case AF_INET: { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)client_myds->client_addr; - if (ipv4->sin_port) { - inet_ntop(client_myds->client_addr->sa_family, &ipv4->sin_addr, buf, INET_ADDRSTRLEN); - client_addr = strdup(buf); - } else { - client_addr = strdup((char *)"localhost"); - } - break; - } - case AF_INET6: { - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)client_myds->client_addr; - inet_ntop(client_myds->client_addr->sa_family, &ipv6->sin6_addr, buf, INET6_ADDRSTRLEN); - client_addr = strdup(buf); - break; - } - default: - client_addr = strdup((char *)"localhost"); - break; - } - } else { - client_addr = strdup((char *)""); - } - if (client_myds->myconn->userinfo->username) { - char *_s=(char *)malloc(strlen(client_myds->myconn->userinfo->username)+100+strlen(client_addr)); - uint8_t _pid = 2; - if (client_myds->switching_auth_stage) _pid+=2; - if (is_encrypted) _pid++; -#ifdef DEBUG - if (client_myds->myconn->userinfo->password) { - char *tmp_pass=strdup(client_myds->myconn->userinfo->password); - int lpass = strlen(tmp_pass); - for (int i=2; imyconn->userinfo->username, client_addr, tmp_pass); - free(tmp_pass); - } else { - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p . Error: Access denied for user '%s'@'%s' . No password. Disconnecting\n", this, client_myds, client_myds->myconn->userinfo->username, client_addr); - } -#endif // DEBUG - sprintf(_s,"ProxySQL Error: Access denied for user '%s'@'%s' (using password: %s)", client_myds->myconn->userinfo->username, client_addr, (client_myds->myconn->userinfo->password ? "YES" : "NO")); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL, _pid, 1045,(char *)"28000", _s, true); - proxy_error("ProxySQL Error: Access denied for user '%s'@'%s' (using password: %s)\n", client_myds->myconn->userinfo->username, client_addr, (client_myds->myconn->userinfo->password ? "YES" : "NO")); - free(_s); - __sync_fetch_and_add(&MyHGM->status.access_denied_wrong_password, 1); - } - if (client_addr) { - free(client_addr); - } - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_ERR, this, NULL); - __sync_add_and_fetch(&MyHGM->status.client_connections_aborted,1); - client_myds->DSS=STATE_SLEEP; - } - - if (mysql_thread___client_host_cache_size) { - GloPWTH->update_client_host_cache(client_myds->client_addr, handshake_err); - } -} - -/* -// Note: as commented in issue #546 and #547 , some clients ignore the status of CLIENT_MULTI_STATEMENTS -// therefore tracking it is not needed, unless in future this should become a security enhancement, -// returning errors to all clients trying to send multi-statements . -// see also #1140 -void Client_Session::handler_WCDSS_MYSQL_COM_SET_OPTION(PtrSize_t *pkt) { - gtid_hid=-1; - char v; - v=*((char *)pkt->ptr+3); - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_SET_OPTION packet , value %d\n", v); - client_myds->setDSS_STATE_QUERY_SENT_NET(); - unsigned int nTrx=NumActiveTransactions(); - uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); - if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; - - bool deprecate_eof_active = client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF; - if (deprecate_eof_active) - client_myds->myprot.generate_pkt_OK(true,NULL,NULL,1,0,0,setStatus,0,NULL,true); - else - client_myds->myprot.generate_pkt_EOF(true,NULL,NULL,1,0, setStatus ); - - if (v==1) { // disabled. MYSQL_OPTION_MULTI_STATEMENTS_OFF == 1 - client_myds->myconn->options.client_flag &= ~CLIENT_MULTI_STATEMENTS; - } else { // enabled, MYSQL_OPTION_MULTI_STATEMENTS_ON == 0 - client_myds->myconn->options.client_flag |= CLIENT_MULTI_STATEMENTS; - } - client_myds->DSS=STATE_SLEEP; - l_free(pkt->size,pkt->ptr); -} - -void Client_Session::handler_WCDSS_MYSQL_COM_PING(PtrSize_t *pkt) { - gtid_hid=-1; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_PING packet\n"); - l_free(pkt->size,pkt->ptr); - client_myds->setDSS_STATE_QUERY_SENT_NET(); - 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); - client_myds->DSS=STATE_SLEEP; -} - -void Client_Session::handler_WCDSS_MYSQL_COM_FIELD_LIST(PtrSize_t *pkt) { - if (session_type == PROXYSQL_SESSION_MYSQL) { - // FIXME: temporary - l_free(pkt->size,pkt->ptr); - client_myds->setDSS_STATE_QUERY_SENT_NET(); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,1045,(char *)"28000",(char *)"Command not supported", true); - client_myds->DSS=STATE_SLEEP; - } else { - l_free(pkt->size,pkt->ptr); - client_myds->setDSS_STATE_QUERY_SENT_NET(); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,1045,(char *)"28000",(char *)"Command not supported", true); - client_myds->DSS=STATE_SLEEP; - } -} - -void Client_Session::handler_WCDSS_MYSQL_COM_PROCESS_KILL(PtrSize_t *pkt) { - l_free(pkt->size,pkt->ptr); - client_myds->setDSS_STATE_QUERY_SENT_NET(); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,9003,(char *)"28000",(char *)"Command not supported"); - client_myds->DSS=STATE_SLEEP; -} -*/ - -void Client_Session::handler_WCDSS_MYSQL_COM_INIT_DB(PtrSize_t *pkt) { - gtid_hid=-1; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_INIT_DB packet\n"); - if (session_type == PROXYSQL_SESSION_MYSQL) { - __sync_fetch_and_add(&MyHGM->status.frontend_init_db, 1); - client_myds->myconn->userinfo->set_schemaname((char *)pkt->ptr+sizeof(mysql_hdr)+1,pkt->size-sizeof(mysql_hdr)-1); - l_free(pkt->size,pkt->ptr); - client_myds->setDSS_STATE_QUERY_SENT_NET(); - 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); - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_INITDB, this, NULL); - client_myds->DSS=STATE_SLEEP; - } else { - l_free(pkt->size,pkt->ptr); - client_myds->setDSS_STATE_QUERY_SENT_NET(); - 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); - client_myds->DSS=STATE_SLEEP; - } -} - -// this function was introduced due to isseu #718 -// some application (like the one written in Perl) do not use COM_INIT_DB , but COM_QUERY with USE dbname -void Client_Session::handler_WCDSS_MYSQL_COM_QUERY_USE_DB(PtrSize_t *pkt) { - gtid_hid=-1; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_QUERY with USE dbname\n"); - if (session_type == PROXYSQL_SESSION_MYSQL) { - __sync_fetch_and_add(&MyHGM->status.frontend_use_db, 1); - string nq=string((char *)pkt->ptr+sizeof(mysql_hdr)+1,pkt->size-sizeof(mysql_hdr)-1); - RE2::GlobalReplace(&nq,(char *)"(?U)/\\*.*\\*/",(char *)" "); - char *sn_tmp = (char *)nq.c_str(); - while (sn_tmp < ( nq.c_str() + nq.length() - 4 ) && *sn_tmp == ' ') - sn_tmp++; - //char *schemaname=strdup(nq.c_str()+4); - char *schemaname=strdup(sn_tmp+3); - char *schemanameptr=trim_spaces_and_quotes_in_place(schemaname); - // handle cases like "USE `schemaname` - if(schemanameptr[0]=='`' && schemanameptr[strlen(schemanameptr)-1]=='`') { - schemanameptr[strlen(schemanameptr)-1]='\0'; - schemanameptr++; - } - client_myds->myconn->userinfo->set_schemaname(schemanameptr,strlen(schemanameptr)); - free(schemaname); - if (mirror==false) { - RequestEnd_mysql(NULL); - } - l_free(pkt->size,pkt->ptr); - client_myds->setDSS_STATE_QUERY_SENT_NET(); - 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); - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_INITDB, this, NULL); - client_myds->DSS=STATE_SLEEP; - } else { - l_free(pkt->size,pkt->ptr); - client_myds->setDSS_STATE_QUERY_SENT_NET(); - 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); - client_myds->DSS=STATE_SLEEP; - } -} - - -// this function as inline in handler_WCDSS_MYSQL_COM_QUERY_qpo -void Client_Session::handler_WCD_SS_MCQ_qpo_QueryRewrite(PtrSize_t *pkt) { - // the query was rewritten - l_free(pkt->size,pkt->ptr); // free old pkt - // allocate new pkt - timespec begint; - if (thread->variables.stats_time_query_processor) { - clock_gettime(CLOCK_THREAD_CPUTIME_ID,&begint); - } - pkt->size=sizeof(mysql_hdr)+1+qpo->new_query->length(); - pkt->ptr=l_alloc(pkt->size); - mysql_hdr hdr; - hdr.pkt_id=0; - hdr.pkt_length=pkt->size-sizeof(mysql_hdr); - memcpy((unsigned char *)pkt->ptr, &hdr, sizeof(mysql_hdr)); // copy header - unsigned char *c=(unsigned char *)pkt->ptr+sizeof(mysql_hdr); - *c=(unsigned char)_MYSQL_COM_QUERY; // set command type - memcpy((unsigned char *)pkt->ptr+sizeof(mysql_hdr)+1,qpo->new_query->data(),qpo->new_query->length()); // copy query - CurrentQuery.query_parser_free(); - CurrentQuery.begin((unsigned char *)pkt->ptr,pkt->size,true); - delete qpo->new_query; - timespec endt; - if (thread->variables.stats_time_query_processor) { - clock_gettime(CLOCK_THREAD_CPUTIME_ID,&endt); - thread->status_variables.stvar[st_var_query_processor_time] = thread->status_variables.stvar[st_var_query_processor_time] + - (endt.tv_sec*1000000000+endt.tv_nsec) - - (begint.tv_sec*1000000000+begint.tv_nsec); - } -} - -// this function as inline in handler_WCDSS_MYSQL_COM_QUERY_qpo -void Client_Session::handler_WCD_SS_MCQ_qpo_OK_msg(PtrSize_t *pkt) { - gtid_hid = -1; - client_myds->DSS=STATE_QUERY_SENT_NET; - 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,client_myds->pkt_sid+1,0,0,setStatus,0,qpo->OK_msg); - RequestEnd_mysql(NULL); - l_free(pkt->size,pkt->ptr); -} - -// this function as inline in handler_WCDSS_MYSQL_COM_QUERY_qpo -void Client_Session::handler_WCD_SS_MCQ_qpo_error_msg(PtrSize_t *pkt) { - client_myds->DSS=STATE_QUERY_SENT_NET; - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,1148,(char *)"42000",qpo->error_msg); - RequestEnd_mysql(NULL); - l_free(pkt->size,pkt->ptr); -} - -// this function as inline in handler_WCDSS_MYSQL_COM_QUERY_qpo -void Client_Session::handler_WCD_SS_MCQ_qpo_LargePacket(PtrSize_t *pkt) { - // ER_NET_PACKET_TOO_LARGE - client_myds->DSS=STATE_QUERY_SENT_NET; - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,1153,(char *)"08S01",(char *)"Got a packet bigger than 'max_allowed_packet' bytes", true); - RequestEnd_mysql(NULL); - l_free(pkt->size,pkt->ptr); -} - -/* -// this function as inline in handler_WCDSS_MYSQL_COM_QUERY_qpo -// returned values: -// 0 : no action -// 1 : return false -// 2 : return true -int Client_Session::handler_WCD_SS_MCQ_qpo_Parse_SQL_LOG_BIN(PtrSize_t *pkt, bool *lock_hostgroup, unsigned int nTrx, string& nq) { - re2::RE2::Options *opt2=new re2::RE2::Options(RE2::Quiet); - opt2->set_case_sensitive(false); - char *pattern=(char *)"(?: *)SET *(?:|SESSION +|@@|@@session.)SQL_LOG_BIN *(?:|:)= *(\\d+) *(?:(|;|-- .*|#.*))$"; - re2::RE2 *re=new RE2(pattern, *opt2); - int i; - int rc=RE2::PartialMatch(nq, *re, &i); - delete re; - delete opt2; - if (rc && ( i==0 || i==1) ) { - //fprintf(stderr,"sql_log_bin=%d\n", i); - if (i == 1) { - if (!mysql_variables.client_set_value(this, SQL_SQL_LOG_BIN, "1")) - return 1; - } - else if (i == 0) { - if (!mysql_variables.client_set_value(this, SQL_SQL_LOG_BIN, "0")) - return 1; - } - -#ifdef DEBUG - proxy_info("Setting SQL_LOG_BIN to %d\n", i); -#endif -#ifdef DEBUG - { - string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); - proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Setting SQL_LOG_BIN to %d for query: %s\n", i, nqn.c_str()); - } -#endif - // we recompute command_type instead of taking it from the calling function - unsigned char command_type=*((unsigned char *)pkt->ptr+sizeof(mysql_hdr)); - if (command_type == _MYSQL_COM_QUERY) { - client_myds->DSS=STATE_QUERY_SENT_NET; - 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); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; - RequestEnd_mysql(NULL); - l_free(pkt->size,pkt->ptr); - return 2; - } - } else { - int kq = 0; - kq = strncmp((const char *)CurrentQuery.QueryPointer, (const char *)"SET @@SESSION.SQL_LOG_BIN = @MYSQLDUMP_TEMP_LOG_BIN;" , CurrentQuery.QueryLength); -#ifdef DEBUG - { - string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); - proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Setting SQL_LOG_BIN to %d for query: %s\n", i, nqn.c_str()); - } -#endif - if (kq == 0) { - client_myds->DSS=STATE_QUERY_SENT_NET; - 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); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; - RequestEnd_mysql(NULL); - l_free(pkt->size,pkt->ptr); - return 2; - } else { - string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); - proxy_error("Unable to parse query. If correct, report it as a bug: %s\n", nqn.c_str()); - unable_to_parse_set_statement(lock_hostgroup); - return 1; - } - } - return 0; -} -*/ - -bool Client_Session::handler_WCDSS_MYSQL_COM_QUERY_qpo(PtrSize_t *pkt, bool *lock_hostgroup, bool prepared) { -/* - lock_hostgroup: - If this variable is set to true, this session will get lock to a - specific hostgroup, and also have multiplexing disabled. - It means that parsing the query wasn't completely possible (mostly - a SET statement) and proxysql won't be able to set the same variable - in another connection. - This algorithm will be become obsolete once we implement session - tracking for MySQL 5.7+ -*/ - bool exit_after_SetParse = true; - unsigned char command_type=*((unsigned char *)pkt->ptr+sizeof(mysql_hdr)); - if (qpo->new_query) { - handler_WCD_SS_MCQ_qpo_QueryRewrite(pkt); - } - - if (pkt->size > (unsigned int) mysql_thread___max_allowed_packet) { - handler_WCD_SS_MCQ_qpo_LargePacket(pkt); - return true; - } - - if (qpo->OK_msg) { - handler_WCD_SS_MCQ_qpo_OK_msg(pkt); - return true; - } - - if (qpo->error_msg) { - handler_WCD_SS_MCQ_qpo_error_msg(pkt); - return true; - } - - if (prepared) { // for prepared statement we exit here - goto __exit_set_destination_hostgroup; - } - - // handle here #509, #815 and #816 - if (CurrentQuery.QueryParserArgs.digest_text) { - char *dig=CurrentQuery.QueryParserArgs.digest_text; - unsigned int nTrx=NumActiveTransactions(); - if ((locked_on_hostgroup == -1) && (strncasecmp(dig,(char *)"SET ",4)==0)) { - // this code is executed only if locked_on_hostgroup is not set yet - // if locked_on_hostgroup is set, we do not try to parse the SET statement -#ifdef DEBUG - { - string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); - proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Parsing SET command = %s\n", nqn.c_str()); - } -#endif - if (index(dig,';') && (index(dig,';') != dig + strlen(dig)-1)) { - string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); - proxy_warning( - "Unable to parse multi-statements command with SET statement from client" - " %s:%d: setting lock hostgroup. Command: %s\n", client_myds->addr.addr, - client_myds->addr.port, nqn.c_str() - ); - *lock_hostgroup = true; - return false; - } - int rc; - string nq=string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); - RE2::GlobalReplace(&nq,(char *)"^/\\*!\\d\\d\\d\\d\\d SET(.*)\\*/",(char *)"SET\\1"); - RE2::GlobalReplace(&nq,(char *)"(?U)/\\*.*\\*/",(char *)""); -/* - // we do not threat SET SQL_LOG_BIN as a special case - if (match_regexes && match_regexes[0]->match(dig)) { - int rc = handler_WCD_SS_MCQ_qpo_Parse_SQL_LOG_BIN(pkt, lock_hostgroup, nTrx, nq); - if (rc == 1) return false; - if (rc == 2) return true; - // if rc == 0 , continue as normal - } -*/ - if ( - ( - match_regexes && (match_regexes[1]->match(dig)) - ) - || - ( strncasecmp(dig,(char *)"SET NAMES", strlen((char *)"SET NAMES")) == 0) - || - ( strcasestr(dig,(char *)"autocommit")) - ) { - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Parsing SET command %s\n", nq.c_str()); - proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Parsing SET command = %s\n", nq.c_str()); - SetParser parser(nq); - std::map> set = parser.parse1(); - // Flag to be set if any variable within the 'SET' statement fails to be tracked, - // due to being unknown or because it's an user defined variable. - bool failed_to_parse_var = false; - for(auto it = std::begin(set); it != std::end(set); ++it) { - std::string var = it->first; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET variable %s\n", var.c_str()); - if (it->second.size() < 1 || it->second.size() > 2) { - // error not enough arguments - string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); - // PMC-10002: A query has failed to be parsed. This can be due a incorrect query or - // due to ProxySQL not being able to properly parse it. In case the query is correct a - // bug report should be filed including the offending query. - proxy_error2(10002, "Unable to parse query. If correct, report it as a bug: %s\n", nqn.c_str()); - proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Locking hostgroup for query %s\n", nqn.c_str()); - unable_to_parse_set_statement(lock_hostgroup); - return false; - } - auto values = std::begin(it->second); - if (var == "sql_mode") { - std::string value1 = *values; - if (strcasecmp(value1.c_str(),"NO_BACKSLASH_ESCAPE") != 0) { - // client is setting NO_BACKSLASH_ESCAPE in sql_mode - // Because we will reply with an OK packet without - // first setting sql_mode to the backend (this is - // by design) we need to set no_backslash_escapes - // in the client connection - if (client_myds && client_myds->myconn) { // some extra sanity check - client_myds->myconn->set_no_backslash_escapes(true); - } - } - if ( - ( strcasecmp(value1.c_str(),(char *)"CONCAT") == 0 ) - || - ( strcasecmp(value1.c_str(),(char *)"REPLACE") == 0 ) - || - ( strcasecmp(value1.c_str(),(char *)"IFNULL") == 0 ) - ) { - string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); - proxy_error2(10002, "Unable to parse query. If correct, report it as a bug: %s\n", nqn.c_str()); - proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Locking hostgroup for query %s\n", nqn.c_str()); - unable_to_parse_set_statement(lock_hostgroup); - return false; - } - std::size_t found_at = value1.find("@"); - if (found_at != std::string::npos) { - char *v1 = strdup(value1.c_str()); - char *v1t = v1; - proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Found @ in SQL_MODE . v1 = %s\n", v1); - char *v2 = NULL; - while (v1 && (v2 = strstr(v1,(const char *)"@"))) { - // we found a @ . Maybe we need to lock hostgroup - proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Found @ in SQL_MODE . v2 = %s\n", v2); - if (strncasecmp(v2,(const char *)"@@sql_mode",strlen((const char *)"@@sql_mode"))) { - unable_to_parse_set_statement(lock_hostgroup); - free(v1); - return false; - } else { - v2++; - } - if (strlen(v2) > 1) { - v1 = v2+1; - } - } - free(v1t); - } - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET SQL Mode value %s\n", value1.c_str()); - uint32_t sql_mode_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); - if (mysql_variables.client_get_hash(this, SQL_SQL_MODE) != sql_mode_int) { - if (!mysql_variables.client_set_value(this, SQL_SQL_MODE, value1.c_str())) { - return false; - } - proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection SQL Mode to %s\n", value1.c_str()); - } - } else if (mysql_variables_strings.find(var) != mysql_variables_strings.end()) { - std::string value1 = *values; - std::size_t found_at = value1.find("@"); - if (found_at != std::string::npos) { - unable_to_parse_set_statement(lock_hostgroup); - return false; - } - int idx = SQL_NAME_LAST_HIGH_WM; - for (int i = 0 ; i < SQL_NAME_LAST_HIGH_WM ; i++) { - if (mysql_tracked_variables[i].is_number == false && mysql_tracked_variables[i].is_bool == false) { - if (!strcasecmp(var.c_str(), mysql_tracked_variables[i].set_variable_name)) { - idx = mysql_tracked_variables[i].idx; - break; - } - } - } - if (idx != SQL_NAME_LAST_HIGH_WM) { - proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection %s to %s\n", var.c_str(), value1.c_str()); - uint32_t var_hash_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); - if (mysql_variables.client_get_hash(this, mysql_tracked_variables[idx].idx) != var_hash_int) { - if (!mysql_variables.client_set_value(this, mysql_tracked_variables[idx].idx, value1.c_str())) { - return false; - } - } - } - } else if (mysql_variables_boolean.find(var) != mysql_variables_boolean.end()) { - int idx = SQL_NAME_LAST_HIGH_WM; - for (int i = 0 ; i < SQL_NAME_LAST_HIGH_WM ; i++) { - if (mysql_tracked_variables[i].is_bool) { - if (!strcasecmp(var.c_str(), mysql_tracked_variables[i].set_variable_name)) { - idx = mysql_tracked_variables[i].idx; - break; - } - } - } - if (idx != SQL_NAME_LAST_HIGH_WM) { - if (mysql_variables.parse_variable_boolean(this,idx, *values, lock_hostgroup)==false) { - return false; - } - } - } else if (mysql_variables_numeric.find(var) != mysql_variables_numeric.end()) { - int idx = SQL_NAME_LAST_HIGH_WM; - for (int i = 0 ; i < SQL_NAME_LAST_HIGH_WM ; i++) { - if (mysql_tracked_variables[i].is_number) { - if (!strcasecmp(var.c_str(), mysql_tracked_variables[i].set_variable_name)) { - idx = mysql_tracked_variables[i].idx; - break; - } - } - } - if (idx != SQL_NAME_LAST_HIGH_WM) { - if (var == "query_cache_type") { - // note that query_cache_type variable can act both as boolean AND a number , but also accept "DEMAND" - // See https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_query_cache_type - std::string value1 = *values; - if (strcasecmp(value1.c_str(),"off")==0 || strcasecmp(value1.c_str(),"false")==0) { - value1 = "0"; - } else if (strcasecmp(value1.c_str(),"on")==0 || strcasecmp(value1.c_str(),"true")==0) { - value1 = "1"; - } else if (strcasecmp(value1.c_str(),"demand")==0 || strcasecmp(value1.c_str(),"true")==0) { - value1 = "2"; - } - if (mysql_variables.parse_variable_number(this,idx, value1, lock_hostgroup)==false) { - return false; - } - } else { - if (mysql_variables.parse_variable_number(this,idx, *values, lock_hostgroup)==false) { - return false; - } - } - } - } else if (var == "autocommit") { - std::string value1 = *values; - std::size_t found_at = value1.find("@"); - if (found_at != std::string::npos) { - unable_to_parse_set_statement(lock_hostgroup); - return false; - } - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET autocommit value %s\n", value1.c_str()); - int __tmp_autocommit = -1; - if ( - (strcasecmp(value1.c_str(),(char *)"0")==0) || - (strcasecmp(value1.c_str(),(char *)"false")==0) || - (strcasecmp(value1.c_str(),(char *)"off")==0) - ) { - __tmp_autocommit = 0; - } else { - if ( - (strcasecmp(value1.c_str(),(char *)"1")==0) || - (strcasecmp(value1.c_str(),(char *)"true")==0) || - (strcasecmp(value1.c_str(),(char *)"on")==0) - ) { - __tmp_autocommit = 1; - } - } - if (__tmp_autocommit >= 0 && autocommit_handled==false) { - int fd = __tmp_autocommit; - __sync_fetch_and_add(&MyHGM->status.autocommit_cnt, 1); - // we immediately process the number of transactions - unsigned int nTrx=NumActiveTransactions(); - if (fd==1 && autocommit==true) { - // nothing to do, return OK - } - if (fd==1 && autocommit==false) { - if (nTrx) { - // there is an active transaction, we need to forward it - // because this can potentially close the transaction - autocommit=true; - client_myds->myconn->set_autocommit(autocommit); - autocommit_on_hostgroup=FindOneActiveTransaction(); - exit_after_SetParse = false; - sending_set_autocommit=true; - } else { - // as there is no active transaction, we do no need to forward it - // just change internal state - autocommit=true; - client_myds->myconn->set_autocommit(autocommit); - } - } - - if (fd==0) { - autocommit=false; // we set it, no matter if already set or not - client_myds->myconn->set_autocommit(autocommit); - } - } else { - if (autocommit_handled==true) { - exit_after_SetParse = false; - } - } - } else if (var == "time_zone") { - std::string value1 = *values; - std::size_t found_at = value1.find("@"); - if (found_at != std::string::npos) { - unable_to_parse_set_statement(lock_hostgroup); - return false; - } - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET Time Zone value %s\n", value1.c_str()); - { - // reformat +1:23 to +01:23 - if (value1.length() == 5) { - if (value1[0]=='+' || value1[0]=='-') { - if (value1[2]==':') { - std::string s = std::string(value1,0,1); - s += "0"; - s += std::string(value1,1,4); - value1 = s; - } - } - } - } - uint32_t time_zone_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); - if (mysql_variables.client_get_hash(this, SQL_TIME_ZONE) != time_zone_int) { - if (!mysql_variables.client_set_value(this, SQL_TIME_ZONE, value1.c_str())) - return false; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection Time zone to %s\n", value1.c_str()); - } - } else if (var == "session_track_gtids") { - std::string value1 = *values; - if ((strcasecmp(value1.c_str(),"OWN_GTID")==0) || (strcasecmp(value1.c_str(),"OFF")==0) || (strcasecmp(value1.c_str(),"ALL_GTIDS")==0)) { - if (strcasecmp(value1.c_str(),"ALL_GTIDS")==0) { - // we convert session_track_gtids=ALL_GTIDS to session_track_gtids=OWN_GTID - std::string a = ""; - if (client_myds && client_myds->addr.addr) { - a = " . Client "; - a+= client_myds->addr.addr; - a+= ":" + std::to_string(client_myds->addr.port); - } - proxy_warning("SET session_track_gtids=ALL_GTIDS is not allowed. Switching to session_track_gtids=OWN_GTID%s\n", a.c_str()); - value1 = "OWN_GTID"; - } - proxy_debug(PROXY_DEBUG_MYSQL_COM, 7, "Processing SET session_track_gtids value %s\n", value1.c_str()); - uint32_t session_track_gtids_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); - if (client_myds->myconn->options.session_track_gtids_int != session_track_gtids_int) { - client_myds->myconn->options.session_track_gtids_int = session_track_gtids_int; - if (client_myds->myconn->options.session_track_gtids) { - free(client_myds->myconn->options.session_track_gtids); - } - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Changing connection session_track_gtids to %s\n", value1.c_str()); - client_myds->myconn->options.session_track_gtids=strdup(value1.c_str()); - } - } else { - unable_to_parse_set_statement(lock_hostgroup); - return false; - } - } else if ( (var == "character_set_results") || ( var == "collation_connection" ) || - (var == "character_set_connection") || (var == "character_set_client") || - (var == "character_set_database")) { - std::string value1 = *values; - int vl = strlen(value1.c_str()); - const char *v = value1.c_str(); - bool only_normal_chars = true; - for (int i=0; inr; - /* changing collation_connection the character_set_connection will be changed as well - * and vice versa - */ - if (var == "collation_connection") { - if (!mysql_variables.client_set_value(this, SQL_CHARACTER_SET_CONNECTION, ss.str().c_str())) - return false; - } - if (var == "character_set_connection") { - if (!mysql_variables.client_set_value(this, SQL_COLLATION_CONNECTION, ss.str().c_str())) - return false; - } - - /* this is explicit statement from client. we do not multiplex, therefor we must - * remember client's choice in the client's variable for future use in verifications, multiplexing etc. - */ - if (!mysql_variables.client_set_value(this, idx, ss.str().c_str())) - return false; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Changing connection %s to %s\n", var.c_str(), value1.c_str()); - } - } - } else { - unable_to_parse_set_statement(lock_hostgroup); - return false; - } - } else if (var == "names") { - std::string value1 = *values++; - std::size_t found_at = value1.find("@"); - if (found_at != std::string::npos) { - unable_to_parse_set_statement(lock_hostgroup); - return false; - } - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET NAMES %s\n", value1.c_str()); - const MARIADB_CHARSET_INFO * c; - std::string value2; - if (values != std::end(it->second)) { - value2 = *values; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET NAMES With COLLATE %s\n", value2.c_str()); - c = proxysql_find_charset_collate_names(value1.c_str(), value2.c_str()); - } else { - c = proxysql_find_charset_name(value1.c_str()); - } - if (!c) { - char *m = NULL; - char *errmsg = NULL; - if (value2.length()) { - m=(char *)"Unknown character set '%s' or collation '%s'"; - errmsg=(char *)malloc(value1.length() + value2.length() + strlen(m)); - sprintf(errmsg,m,value1.c_str(), value2.c_str()); - } else { - m=(char *)"Unknown character set: '%s'"; - errmsg=(char *)malloc(value1.length()+strlen(m)); - sprintf(errmsg,m,value1.c_str()); - } - client_myds->DSS=STATE_QUERY_SENT_NET; - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,1115,(char *)"42000",errmsg, true); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; - free(errmsg); - return true; - } else { - proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection charset to %d\n", c->nr); - client_myds->myconn->set_charset(c->nr, NAMES); - } - } else if (var == "tx_isolation") { - std::string value1 = *values; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET tx_isolation value %s\n", value1.c_str()); - auto pos = value1.find('-'); - if (pos != std::string::npos) - value1[pos] = ' '; - uint32_t isolation_level_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); - if (mysql_variables.client_get_hash(this, SQL_ISOLATION_LEVEL) != isolation_level_int) { - if (!mysql_variables.client_set_value(this, SQL_ISOLATION_LEVEL, value1.c_str())) - return false; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection TX ISOLATION to %s\n", value1.c_str()); - } - } else if (std::find(mysql_variables.ignore_vars.begin(), mysql_variables.ignore_vars.end(), var) != mysql_variables.ignore_vars.end()) { - // this is a variable we parse but ignore - // see MySQL_Variables::MySQL_Variables() for a list of ignored variables -#ifdef DEBUG - std::string value1 = *values; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET %s value %s\n", var.c_str(), value1.c_str()); -#endif // DEBUG - } else { - // At this point the variable is unknown to us, or it's a user variable - // prefixed by '@', in both cases, we should fail to parse. We don't - // fail inmediately so we can anyway keep track of the other variables - // supplied within the 'SET' statement being parsed. - failed_to_parse_var = true; - } - } - - if (failed_to_parse_var) { - unable_to_parse_set_statement(lock_hostgroup); - return false; - } -/* - if (exit_after_SetParse) { - goto __exit_set_destination_hostgroup; - } -*/ - // parseSetCommand wasn't able to parse anything... - if (set.size() == 0) { - // try case listed in #1373 - // 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 - // this is not a complete solution. A right solution involves true parsing - int query_no_space_length = nq.length(); - char *query_no_space=(char *)malloc(query_no_space_length+1); - memcpy(query_no_space,nq.c_str(),query_no_space_length); - query_no_space[query_no_space_length]='\0'; - query_no_space_length=remove_spaces(query_no_space); - - string nq1 = string(query_no_space); - free(query_no_space); - RE2::GlobalReplace(&nq1,(char *)"SESSION.",(char *)""); - RE2::GlobalReplace(&nq1,(char *)"SESSION ",(char *)""); - RE2::GlobalReplace(&nq1,(char *)"session.",(char *)""); - RE2::GlobalReplace(&nq1,(char *)"session ",(char *)""); - //fprintf(stderr,"%s\n",nq1.c_str()); - re2::RE2::Options *opt2=new re2::RE2::Options(RE2::Quiet); - opt2->set_case_sensitive(false); - char *pattern=(char *)"^SET @@SQL_MODE *(?:|:)= *(?:'||\")(.*)(?:'||\") *, *@@sql_auto_is_null *(?:|:)= *(?:(?:\\w|\\d)*) *, @@wait_timeout *(?:|:)= *(?:\\d*)$"; - re2::RE2 *re=new RE2(pattern, *opt2); - string s1; - rc=RE2::FullMatch(nq1, *re, &s1); - delete re; - delete opt2; - if (rc) { - uint32_t sql_mode_int=SpookyHash::Hash32(s1.c_str(),s1.length(),10); - if (mysql_variables.client_get_hash(this, SQL_SQL_MODE) != sql_mode_int) { - if (!mysql_variables.client_set_value(this, SQL_SQL_MODE, s1.c_str())) - return false; - std::size_t found_at = s1.find("@"); - if (found_at != std::string::npos) { - char *v1 = strdup(s1.c_str()); - char *v2 = NULL; - while (v1 && (v2 = strstr(v1,(const char *)"@"))) { - // we found a @ . Maybe we need to lock hostgroup - if (strncasecmp(v2,(const char *)"@@sql_mode",strlen((const char *)"@@sql_mode"))) { -#ifdef DEBUG - string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); - proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Locking hostgroup for query %s\n", nqn.c_str()); -#endif - *lock_hostgroup = true; - } - if (strlen(v2) > 1) { - v1 = v2+1; - } - } - free(v1); - if (*lock_hostgroup) { - unable_to_parse_set_statement(lock_hostgroup); - return false; - } - } - } - } else { - if (memchr((const char *)CurrentQuery.QueryPointer, '@', CurrentQuery.QueryLength)) { - unable_to_parse_set_statement(lock_hostgroup); - return false; - } - int kq = 0; - kq = strncmp((const char *)CurrentQuery.QueryPointer, (const char *)"/*!40101 SET SQL_MODE=@OLD_SQL_MODE */" , CurrentQuery.QueryLength); - if (kq != 0) { - kq = strncmp((const char *)CurrentQuery.QueryPointer, (const char *)"/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */" , CurrentQuery.QueryLength); - if (kq != 0) { - string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); - proxy_error2(10002, "Unable to parse query. If correct, report it as a bug: %s\n", nqn.c_str()); - return false; - } - } - } - } - - if (exit_after_SetParse) { - if (command_type == _MYSQL_COM_QUERY) { - client_myds->DSS=STATE_QUERY_SENT_NET; - 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); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; - RequestEnd_mysql(NULL); - l_free(pkt->size,pkt->ptr); - return true; - } - } - } else if (match_regexes && match_regexes[2]->match(dig)) { - SetParser parser(nq); - std::map> set = parser.parse2(); - for(auto it = std::begin(set); it != std::end(set); ++it) { - std::string var = it->first; - auto values = std::begin(it->second); - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET variable %s\n", var.c_str()); - if (var == "isolation level") { - std::string value1 = *values; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET SESSION TRANSACTION ISOLATION LEVEL value %s\n", value1.c_str()); - uint32_t isolation_level_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); - if (mysql_variables.client_get_hash(this, SQL_ISOLATION_LEVEL) != isolation_level_int) { - if (!mysql_variables.client_set_value(this, SQL_ISOLATION_LEVEL, value1.c_str())) - return false; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection TRANSACTION ISOLATION LEVEL to %s\n", value1.c_str()); - } - } else if (var == "read") { - std::string value1 = *values; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET SESSION TRANSACTION READ value %s\n", value1.c_str()); - uint32_t transaction_read_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); - if (mysql_variables.client_get_hash(this, SQL_TRANSACTION_READ) != transaction_read_int) { - if (!mysql_variables.client_set_value(this, SQL_TRANSACTION_READ, value1.c_str())) - return false; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection TRANSACTION READ to %s\n", value1.c_str()); - } - } else { - unable_to_parse_set_statement(lock_hostgroup); - return false; - } - } - if (exit_after_SetParse) { - if (command_type == _MYSQL_COM_QUERY) { - client_myds->DSS=STATE_QUERY_SENT_NET; - 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); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; - RequestEnd_mysql(NULL); - l_free(pkt->size,pkt->ptr); - return true; - } - } - } else if (match_regexes && match_regexes[3]->match(dig)) { - SetParser parser(nq); - std::string charset = parser.parse_character_set(); - const MARIADB_CHARSET_INFO * c; - if (!charset.empty()) { - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET CHARACTER SET %s\n", charset.c_str()); - c = proxysql_find_charset_name(charset.c_str()); - } else { - unable_to_parse_set_statement(lock_hostgroup); - return false; - } - if (!c) { - char *m = NULL; - char *errmsg = NULL; - m=(char *)"Unknown character set: '%s'"; - errmsg=(char *)malloc(charset.length()+strlen(m)); - sprintf(errmsg,m,charset.c_str()); - client_myds->DSS=STATE_QUERY_SENT_NET; - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,1115,(char *)"42000",errmsg, true); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; - free(errmsg); - return true; - } else { - proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection charset to %d\n", c->nr); - client_myds->myconn->set_charset(c->nr, CHARSET); - } - if (exit_after_SetParse) { - if (command_type == _MYSQL_COM_QUERY) { - client_myds->DSS=STATE_QUERY_SENT_NET; - 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); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; - RequestEnd_mysql(NULL); - l_free(pkt->size,pkt->ptr); - return true; - } - } - } else { - unable_to_parse_set_statement(lock_hostgroup); - return false; - } - } + ProxySQL_Data_Stream *myds=mybe->server_myds; + MySQL_Connection *myconn=mybe->server_myds->myconn; +#endif /* DEBUG */ + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Sess=%p, status=%d, server_myds->DSS==%d , revents==%d , async_state_machine=%d\n", this, status, mybe->server_myds->DSS, myds->revents, myconn->async_state_machine); + } } - if (mirror==true) { // for mirror session we exit here - current_hostgroup=qpo->destination_hostgroup; - return false; + writeout(); + + if (wrong_pass==true) { + MySQL_Data_Stream * client_myds = ((MySQL_Session *)this)->client_myds; + client_myds->array2buffer_full(); + client_myds->write_to_net(); + handler_ret = -1; + return handler_ret; } + handler_ret = 0; + return handler_ret; +} +// end ::handler() - // handle case #1797 - // handle case #2564 - if ((pkt->size==SELECT_CONNECTION_ID_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_CONNECTION_ID,(char *)pkt->ptr+5,pkt->size-5)==0)) { - char buf[32]; - char buf2[32]; - sprintf(buf,"%u",thread_session_id); - int l0=strlen("CONNECTION_ID()"); - memcpy(buf2,(char *)pkt->ptr+5+SELECT_CONNECTION_ID_LEN-l0,l0); - buf2[l0]=0; - unsigned int nTrx=NumActiveTransactions(); - uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); - if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; - ProxySQL_Data_Stream *myds=client_myds; - MySQL_Protocol *myprot=&client_myds->myprot; - myds->DSS=STATE_QUERY_SENT_DS; - int sid=1; - myprot->generate_pkt_column_count(true,NULL,NULL,sid,1); sid++; - myprot->generate_pkt_field(true,NULL,NULL,sid,(char *)"",(char *)"",(char *)"",buf2,(char *)"",63,31,MYSQL_TYPE_LONGLONG,161,0,false,0,NULL); sid++; - myds->DSS=STATE_COLUMN_DEFINITION; - bool deprecate_eof_active = myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF; - if (!deprecate_eof_active) { - myprot->generate_pkt_EOF(true,NULL,NULL,sid,0, setStatus); sid++; - } - char **p=(char **)malloc(sizeof(char*)*1); - unsigned long *l=(unsigned long *)malloc(sizeof(unsigned long *)*1); - l[0]=strlen(buf); - p[0]=buf; - myprot->generate_pkt_row(true,NULL,NULL,sid,1,l,p); sid++; - myds->DSS=STATE_ROW; +/* +// Note: as commented in issue #546 and #547 , some clients ignore the status of CLIENT_MULTI_STATEMENTS +// therefore tracking it is not needed, unless in future this should become a security enhancement, +// returning errors to all clients trying to send multi-statements . +// see also #1140 +void Client_Session::handler_WCDSS_MYSQL_COM_SET_OPTION(PtrSize_t *pkt) { + gtid_hid=-1; + char v; + v=*((char *)pkt->ptr+3); + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_SET_OPTION packet , value %d\n", v); + client_myds->setDSS_STATE_QUERY_SENT_NET(); + unsigned int nTrx=NumActiveTransactions(); + uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); + if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; - if (deprecate_eof_active) { - myprot->generate_pkt_OK(true,NULL,NULL,sid,0,0,setStatus,0,NULL,true); sid++; - } else { - myprot->generate_pkt_EOF(true,NULL,NULL,sid,0, setStatus); sid++; - } - myds->DSS=STATE_SLEEP; - RequestEnd_mysql(NULL); - l_free(pkt->size,pkt->ptr); - free(p); - free(l); - return true; + bool deprecate_eof_active = client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF; + if (deprecate_eof_active) + client_myds->myprot.generate_pkt_OK(true,NULL,NULL,1,0,0,setStatus,0,NULL,true); + else + client_myds->myprot.generate_pkt_EOF(true,NULL,NULL,1,0, setStatus ); + + if (v==1) { // disabled. MYSQL_OPTION_MULTI_STATEMENTS_OFF == 1 + client_myds->myconn->options.client_flag &= ~CLIENT_MULTI_STATEMENTS; + } else { // enabled, MYSQL_OPTION_MULTI_STATEMENTS_ON == 0 + client_myds->myconn->options.client_flag |= CLIENT_MULTI_STATEMENTS; } + client_myds->DSS=STATE_SLEEP; + l_free(pkt->size,pkt->ptr); +} - // handle case #1421 , about LAST_INSERT_ID - if (CurrentQuery.QueryParserArgs.digest_text) { - char *dig=CurrentQuery.QueryParserArgs.digest_text; - if (strcasestr(dig,"LAST_INSERT_ID") || strcasestr(dig,"@@IDENTITY")) { - // we need to try to execute it where the last write was successful - if (last_HG_affected_rows >= 0) { - MySQL_Backend * _mybe = NULL; - _mybe = find_mysql_backend(last_HG_affected_rows); - if (_mybe) { - if (_mybe->server_myds) { - if (_mybe->server_myds->myconn) { - if (_mybe->server_myds->myconn->mysql) { // we have an established connection - // this seems to be the right backend - qpo->destination_hostgroup = last_HG_affected_rows; - current_hostgroup = qpo->destination_hostgroup; - return false; // execute it on backend! - } - } - } - } - } - // if we reached here, we don't know the right backend - // we try to determine if it is a simple "SELECT LAST_INSERT_ID()" or "SELECT @@IDENTITY" and we return mysql->last_insert_id +void Client_Session::handler_WCDSS_MYSQL_COM_PING(PtrSize_t *pkt) { + gtid_hid=-1; + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_PING packet\n"); + l_free(pkt->size,pkt->ptr); + client_myds->setDSS_STATE_QUERY_SENT_NET(); + 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); + client_myds->DSS=STATE_SLEEP; +} - //handle 2564 - if ( - (pkt->size==SELECT_LAST_INSERT_ID_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_LAST_INSERT_ID,(char *)pkt->ptr+5,pkt->size-5)==0) - || - (pkt->size==SELECT_LAST_INSERT_ID_LIMIT1_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_LAST_INSERT_ID_LIMIT1,(char *)pkt->ptr+5,pkt->size-5)==0) - || - (pkt->size==SELECT_VARIABLE_IDENTITY_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_VARIABLE_IDENTITY,(char *)pkt->ptr+5,pkt->size-5)==0) - || - (pkt->size==SELECT_VARIABLE_IDENTITY_LIMIT1_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_VARIABLE_IDENTITY_LIMIT1,(char *)pkt->ptr+5,pkt->size-5)==0) - ) { - char buf[32]; - sprintf(buf,"%llu",last_insert_id); - char buf2[32]; - int l0=0; - if (strcasestr(dig,"LAST_INSERT_ID")){ - l0=strlen("LAST_INSERT_ID()"); - memcpy(buf2,(char *)pkt->ptr+5+SELECT_LAST_INSERT_ID_LEN-l0,l0); - }else if(strcasestr(dig,"@@IDENTITY")){ - l0=strlen("@@IDENTITY"); - memcpy(buf2,(char *)pkt->ptr+5+SELECT_VARIABLE_IDENTITY_LEN-l0,l0); - } - buf2[l0]=0; - unsigned int nTrx=NumActiveTransactions(); - uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); - if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; - ProxySQL_Data_Stream *myds=client_myds; - MySQL_Protocol *myprot=&client_myds->myprot; - myds->DSS=STATE_QUERY_SENT_DS; - int sid=1; - myprot->generate_pkt_column_count(true,NULL,NULL,sid,1); sid++; - myprot->generate_pkt_field(true,NULL,NULL,sid,(char *)"",(char *)"",(char *)"",buf2,(char *)"",63,31,MYSQL_TYPE_LONGLONG,161,0,false,0,NULL); sid++; - myds->DSS=STATE_COLUMN_DEFINITION; - - bool deprecate_eof_active = myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF; - if (!deprecate_eof_active) { - myprot->generate_pkt_EOF(true,NULL,NULL,sid,0, setStatus); sid++; - } - char **p=(char **)malloc(sizeof(char*)*1); - unsigned long *l=(unsigned long *)malloc(sizeof(unsigned long *)*1); - l[0]=strlen(buf); - p[0]=buf; - myprot->generate_pkt_row(true,NULL,NULL,sid,1,l,p); sid++; - myds->DSS=STATE_ROW; - if (deprecate_eof_active) { - myprot->generate_pkt_OK(true,NULL,NULL,sid,0,0,setStatus,0,NULL,true); sid++; - } else { - myprot->generate_pkt_EOF(true,NULL,NULL,sid,0, setStatus); sid++; - } - myds->DSS=STATE_SLEEP; - RequestEnd_mysql(NULL); - l_free(pkt->size,pkt->ptr); - free(p); - free(l); - return true; - } +void Client_Session::handler_WCDSS_MYSQL_COM_FIELD_LIST(PtrSize_t *pkt) { + if (session_type == PROXYSQL_SESSION_MYSQL) { + // FIXME: temporary + l_free(pkt->size,pkt->ptr); + client_myds->setDSS_STATE_QUERY_SENT_NET(); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,1045,(char *)"28000",(char *)"Command not supported", true); + client_myds->DSS=STATE_SLEEP; + } else { + l_free(pkt->size,pkt->ptr); + client_myds->setDSS_STATE_QUERY_SENT_NET(); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,1045,(char *)"28000",(char *)"Command not supported", true); + client_myds->DSS=STATE_SLEEP; + } +} + +void Client_Session::handler_WCDSS_MYSQL_COM_PROCESS_KILL(PtrSize_t *pkt) { + l_free(pkt->size,pkt->ptr); + client_myds->setDSS_STATE_QUERY_SENT_NET(); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,9003,(char *)"28000",(char *)"Command not supported"); + client_myds->DSS=STATE_SLEEP; +} +*/ - // if we reached here, we don't know the right backend and we cannot answer the query directly - // We continue the normal way - // as a precaution, we reset cache_ttl - qpo->cache_ttl = 0; +/* +// this function as inline in handler_WCDSS_MYSQL_COM_QUERY_qpo +// returned values: +// 0 : no action +// 1 : return false +// 2 : return true +int Client_Session::handler_WCD_SS_MCQ_qpo_Parse_SQL_LOG_BIN(PtrSize_t *pkt, bool *lock_hostgroup, unsigned int nTrx, string& nq) { + re2::RE2::Options *opt2=new re2::RE2::Options(RE2::Quiet); + opt2->set_case_sensitive(false); + char *pattern=(char *)"(?: *)SET *(?:|SESSION +|@@|@@session.)SQL_LOG_BIN *(?:|:)= *(\\d+) *(?:(|;|-- .*|#.*))$"; + re2::RE2 *re=new RE2(pattern, *opt2); + int i; + int rc=RE2::PartialMatch(nq, *re, &i); + delete re; + delete opt2; + if (rc && ( i==0 || i==1) ) { + //fprintf(stderr,"sql_log_bin=%d\n", i); + if (i == 1) { + if (!mysql_variables.client_set_value(this, SQL_SQL_LOG_BIN, "1")) + return 1; + } + else if (i == 0) { + if (!mysql_variables.client_set_value(this, SQL_SQL_LOG_BIN, "0")) + return 1; } - } - // handle command KILL #860 - if (prepared == false) { - if (handle_command_query_kill(pkt)) { - return true; +#ifdef DEBUG + proxy_info("Setting SQL_LOG_BIN to %d\n", i); +#endif +#ifdef DEBUG + { + string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); + proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Setting SQL_LOG_BIN to %d for query: %s\n", i, nqn.c_str()); } - } - if (qpo->cache_ttl>0) { - bool deprecate_eof_active = client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF; - uint32_t resbuf=0; - unsigned char *aa=GloQC->get( - client_myds->myconn->userinfo->hash, - (const unsigned char *)CurrentQuery.QueryPointer , - CurrentQuery.QueryLength , - &resbuf , - thread->curtime/1000 , - qpo->cache_ttl, - deprecate_eof_active - ); - if (aa) { - client_myds->buffer2resultset(aa,resbuf); - free(aa); - client_myds->PSarrayOUT->copy_add(client_myds->resultset,0,client_myds->resultset->len); - while (client_myds->resultset->len) client_myds->resultset->remove_index(client_myds->resultset->len-1,NULL); - if (transaction_persistent_hostgroup == -1) { - // not active, we can change it - current_hostgroup=-1; - } +#endif + // we recompute command_type instead of taking it from the calling function + unsigned char command_type=*((unsigned char *)pkt->ptr+sizeof(mysql_hdr)); + if (command_type == _MYSQL_COM_QUERY) { + client_myds->DSS=STATE_QUERY_SENT_NET; + 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); + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; RequestEnd_mysql(NULL); l_free(pkt->size,pkt->ptr); - return true; + return 2; } - } - -__exit_set_destination_hostgroup: - - if ( qpo->next_query_flagIN >= 0 ) { - next_query_flagIN=qpo->next_query_flagIN; - } - if ( qpo->destination_hostgroup >= 0 ) { - if (transaction_persistent_hostgroup == -1) { - current_hostgroup=qpo->destination_hostgroup; + } else { + int kq = 0; + kq = strncmp((const char *)CurrentQuery.QueryPointer, (const char *)"SET @@SESSION.SQL_LOG_BIN = @MYSQLDUMP_TEMP_LOG_BIN;" , CurrentQuery.QueryLength); +#ifdef DEBUG + { + string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); + proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Setting SQL_LOG_BIN to %d for query: %s\n", i, nqn.c_str()); } - } - - if (mysql_thread___set_query_lock_on_hostgroup == 1) { // algorithm introduced in 2.0.6 - if (locked_on_hostgroup >= 0) { - if (current_hostgroup != locked_on_hostgroup) { - client_myds->DSS=STATE_QUERY_SENT_NET; - char buf[140]; - sprintf(buf,"ProxySQL Error: connection is locked to hostgroup %d but trying to reach hostgroup %d", locked_on_hostgroup, current_hostgroup); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,9006,(char *)"Y0000",buf); - thread->status_variables.stvar[st_var_hostgroup_locked_queries]++; - RequestEnd_mysql(NULL); - l_free(pkt->size,pkt->ptr); - return true; - } +#endif + if (kq == 0) { + client_myds->DSS=STATE_QUERY_SENT_NET; + 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); + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; + RequestEnd_mysql(NULL); + l_free(pkt->size,pkt->ptr); + return 2; + } else { + string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); + proxy_error("Unable to parse query. If correct, report it as a bug: %s\n", nqn.c_str()); + unable_to_parse_set_statement(lock_hostgroup); + return 1; } } - return false; -} - -void Client_Session::handler_WCDSS_MYSQL_COM_STATISTICS(PtrSize_t *pkt) { - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_STATISTICS packet\n"); - l_free(pkt->size,pkt->ptr); - client_myds->setDSS_STATE_QUERY_SENT_NET(); - client_myds->myprot.generate_statistics_response(true,NULL,NULL); - client_myds->DSS=STATE_SLEEP; + return 0; } +*/ /* void Client_Session::handler_WCDSS_MYSQL_COM_CHANGE_USER(PtrSize_t *pkt, bool *wrong_pass) { @@ -6748,6 +3906,11 @@ void Client_Session::handler___client_DSS_QUERY_SENT___server_DSS_NOT_INITIALIZE // we didn't get a valid connection, we need to create one proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Sess=%p -- MySQL Connection has no FD\n", this); MySQL_Connection *myconn=mybe->server_myds->myconn; + MySQL_Data_Stream *client_myds = NULL; + if (this->session_type==PROXYSQL_SESSION_MYSQL) { + client_myds = ((MySQL_Session *)this)->client_myds; + } + assert(client_myds != NULL); myconn->userinfo->set(client_myds->myconn->userinfo); myconn->handler(0); @@ -6768,130 +3931,6 @@ void Client_Session::handler___client_DSS_QUERY_SENT___server_DSS_NOT_INITIALIZE } } -void Client_Session::MySQL_Stmt_Result_to_MySQL_wire(MYSQL_STMT *stmt, MySQL_Connection *myconn) { - MySQL_ResultSet *MyRS = NULL; - if (myconn) { - if (myconn->MyRS) { - MyRS = myconn->MyRS; - } - } -/* - MYSQL_RES *stmt_result=myconn->query.stmt_result; - if (stmt_result) { - MySQL_ResultSet *MyRS=new MySQL_ResultSet(); - MyRS->init(&client_myds->myprot, stmt_result, stmt->mysql, stmt); - MyRS->get_resultset(client_myds->PSarrayOUT); - CurrentQuery.rows_sent = MyRS->num_rows; - //removed bool resultset_completed=MyRS->get_resultset(client_myds->PSarrayOUT); - delete MyRS; -*/ - if (MyRS) { - assert(MyRS->result); - MyRS->init_with_stmt(myconn); - bool resultset_completed=MyRS->get_resultset(client_myds->PSarrayOUT); - CurrentQuery.rows_sent = MyRS->num_rows; - assert(resultset_completed); // the resultset should always be completed if MySQL_Result_to_MySQL_wire is called - } else { - MYSQL *mysql=stmt->mysql; - // no result set - int myerrno=mysql_stmt_errno(stmt); - if (myerrno==0) { - unsigned int num_rows = mysql_affected_rows(stmt->mysql); - unsigned int nTrx=NumActiveTransactions(); - uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); - if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; - if (mysql->server_status & SERVER_MORE_RESULTS_EXIST) - setStatus |= SERVER_MORE_RESULTS_EXIST; - setStatus |= ( mysql->server_status & ~SERVER_STATUS_AUTOCOMMIT ); // get flags from server_status but ignore autocommit - setStatus = setStatus & ~SERVER_STATUS_CURSOR_EXISTS; // Do not send cursor #1128 - client_myds->myprot.generate_pkt_OK(true,NULL,NULL,client_myds->pkt_sid+1,num_rows,mysql->insert_id, setStatus , mysql->warning_count,mysql->info); - client_myds->pkt_sid++; - } else { - // error - char sqlstate[10]; - sprintf(sqlstate,"%s",mysql_sqlstate(mysql)); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,mysql_errno(mysql),sqlstate,mysql_error(mysql)); - client_myds->pkt_sid++; - } - } -} - -void Client_Session::MySQL_Result_to_MySQL_wire(MYSQL *mysql, MySQL_ResultSet *MyRS, ProxySQL_Data_Stream *_myds) { - if (mysql == NULL) { - // error - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1, 2013, (char *)"HY000" ,(char *)"Lost connection to MySQL server during query"); - return; - } - if (MyRS) { - assert(MyRS->result); - bool transfer_started=MyRS->transfer_started; - bool resultset_completed=MyRS->get_resultset(client_myds->PSarrayOUT); - CurrentQuery.rows_sent = MyRS->num_rows; - bool com_field_list=client_myds->com_field_list; - assert(resultset_completed); // the resultset should always be completed if MySQL_Result_to_MySQL_wire is called - if (transfer_started==false) { // we have all the resultset when MySQL_Result_to_MySQL_wire was called - if (qpo && qpo->cache_ttl>0 && com_field_list==false) { // the resultset should be cached - if (mysql_errno(mysql)==0) { // no errors - if ( - (qpo->cache_empty_result==1) - || ( - (qpo->cache_empty_result == -1) - && - (thread->variables.query_cache_stores_empty_result || MyRS->num_rows) - ) - ) { - client_myds->resultset->copy_add(client_myds->PSarrayOUT,0,client_myds->PSarrayOUT->len); - client_myds->resultset_length=MyRS->resultset_size; - unsigned char *aa=client_myds->resultset2buffer(false); - while (client_myds->resultset->len) client_myds->resultset->remove_index(client_myds->resultset->len-1,NULL); - bool deprecate_eof_active = client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF; - GloQC->set( - client_myds->myconn->userinfo->hash , - (const unsigned char *)CurrentQuery.QueryPointer, - CurrentQuery.QueryLength, - aa , - client_myds->resultset_length , - thread->curtime/1000 , - thread->curtime/1000 , - thread->curtime/1000 + qpo->cache_ttl, - deprecate_eof_active - ); - l_free(client_myds->resultset_length,aa); - client_myds->resultset_length=0; - } - } - } - } - } else { // no result set - int myerrno=mysql_errno(mysql); - if (myerrno==0) { - unsigned int num_rows = mysql_affected_rows(mysql); - unsigned int nTrx=NumActiveTransactions(); - uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); - if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; - if (mysql->server_status & SERVER_MORE_RESULTS_EXIST) - setStatus |= SERVER_MORE_RESULTS_EXIST; - setStatus |= ( mysql->server_status & ~SERVER_STATUS_AUTOCOMMIT ); // get flags from server_status but ignore autocommit - setStatus = setStatus & ~SERVER_STATUS_CURSOR_EXISTS; // Do not send cursor #1128 - client_myds->myprot.generate_pkt_OK(true,NULL,NULL,client_myds->pkt_sid+1,num_rows,mysql->insert_id, setStatus, mysql->warning_count,mysql->info); - //client_myds->pkt_sid++; - } else { - // error - char sqlstate[10]; - sprintf(sqlstate,"%s",mysql_sqlstate(mysql)); - if (_myds && _myds->killed_at) { // see case #750 - if (_myds->kill_type == 0) { - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,1907,sqlstate,(char *)"Query execution was interrupted, query_timeout exceeded"); - } else { - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,1317,sqlstate,(char *)"Query execution was interrupted"); - } - } else { - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,mysql_errno(mysql),sqlstate,mysql_error(mysql)); - } - //client_myds->pkt_sid++; - } - } -} void Client_Session::SQLite3_to_MySQL(SQLite3_result *result, char *error, int affected_rows, MySQL_Protocol *myprot, bool in_transaction, bool deprecate_eof_active) { assert(myprot); @@ -7051,7 +4090,11 @@ int Client_Session::FindOneActiveTransaction() { unsigned long long Client_Session::IdleTime() { unsigned long long ret = 0; - if (client_myds==0) return 0; + MySQL_Data_Stream *client_myds = NULL; + if (this->session_type==PROXYSQL_SESSION_MYSQL) { + client_myds = ((MySQL_Session *)this)->client_myds; + } + if (client_myds==NULL) return 0; if (status!=WAITING_CLIENT_DATA && status!=CONNECTING_CLIENT) return 0; int idx=client_myds->poll_fds_idx; unsigned long long last_sent=thread->mypolls.last_sent[idx]; @@ -7084,46 +4127,6 @@ void Client_Session::LogQuery(ProxySQL_Data_Stream *myds) { } } } -// this should execute most of the commands executed when a request is finalized -// this should become the place to hook other functions -void Client_Session::RequestEnd_mysql(ProxySQL_Data_Stream *myds) { - // check if multiplexing needs to be disabled - char *qdt=CurrentQuery.get_digest_text(); - if (qdt && myds && myds->myconn) { - myds->myconn->ProcessQueryAndSetStatusFlags(qdt); - } - - switch (status) { - case PROCESSING_STMT_EXECUTE: - case PROCESSING_STMT_PREPARE: - // if a prepared statement is executed, LogQuery was already called - break; - default: - LogQuery(myds); - break; - } - - GloQPro->delete_QP_out(qpo); - // if there is an associated myds, clean its status - if (myds) { - // if there is a mysql connection, clean its status - if (myds->myconn) { - myds->myconn->async_free_result(); - myds->myconn->compute_unknown_transaction_status(); - } - myds->free_mysql_real_query(); - } - // reset status of the session - status=WAITING_CLIENT_DATA; - if (client_myds) { - // reset status of client data stream - client_myds->DSS=STATE_SLEEP; - // finalize the query - CurrentQuery.end(); - } - started_sending_data_to_client=false; -} - // this function tries to report all the memory statistics related to the sessions void Client_Session::Memory_Stats() { @@ -7133,9 +4136,15 @@ void Client_Session::Memory_Stats() { unsigned long long backend=0; unsigned long long frontend=0; unsigned long long internal=0; - internal+=sizeof(Client_Session); if (qpo) internal+=sizeof(Query_Processor_Output); + MySQL_Data_Stream *client_myds = NULL; + if (this->session_type==PROXYSQL_SESSION_MYSQL) { + client_myds = ((MySQL_Session *)this)->client_myds; + internal+=sizeof(MySQL_Session); + } else { + internal+=sizeof(Client_Session); + } if (client_myds) { internal+=sizeof(ProxySQL_Data_Stream); if (client_myds->queueIN.buffer) @@ -7157,6 +4166,7 @@ void Client_Session::Memory_Stats() { } } } + if (this->session_type==PROXYSQL_SESSION_MYSQL) { for (i=0; i < mybes->len; i++) { MySQL_Backend *_mybe=(MySQL_Backend *)mybes->index(i); internal+=sizeof(MySQL_Backend); @@ -7179,13 +4189,15 @@ void Client_Session::Memory_Stats() { } } } - } + } + } thread->status_variables.stvar[st_var_mysql_backend_buffers_bytes] += backend; thread->status_variables.stvar[st_var_mysql_frontend_buffers_bytes]+= frontend; thread->status_variables.stvar[st_var_mysql_session_internal_bytes] += internal; } +/* void Client_Session::create_new_session_and_reset_mysql_connection(ProxySQL_Data_Stream *_myds) { ProxySQL_Data_Stream *new_myds = NULL; MySQL_Connection * mc = _myds->myconn; @@ -7219,8 +4231,11 @@ void Client_Session::create_new_session_and_reset_mysql_connection(ProxySQL_Data delete new_sess; } } +*/ bool Client_Session::handle_command_query_kill(PtrSize_t *pkt) { + assert(this->session_type==PROXYSQL_SESSION_MYSQL); + MySQL_Data_Stream *client_myds = ((MySQL_Session *)this)->client_myds; unsigned char command_type=*((unsigned char *)pkt->ptr+sizeof(mysql_hdr)); if (CurrentQuery.QueryParserArgs.digest_text) { if (command_type == _MYSQL_COM_QUERY) { @@ -7262,7 +4277,7 @@ bool Client_Session::handle_command_query_kill(PtrSize_t *pkt) { client_myds->myprot.generate_pkt_OK(true,NULL,NULL,1,0,0,setStatus,0,NULL); client_myds->DSS=STATE_SLEEP; status=WAITING_CLIENT_DATA; - RequestEnd_mysql(NULL); + ((MySQL_Session *)this)->RequestEnd_mysql(NULL); l_free(pkt->size,pkt->ptr); return true; } @@ -7275,52 +4290,9 @@ bool Client_Session::handle_command_query_kill(PtrSize_t *pkt) { return false; } -void Client_Session::add_ldap_comment_to_pkt(PtrSize_t *_pkt) { - if (GloMyLdapAuth==NULL) - return; - if (use_ldap_auth == false) - return; - if (client_myds==NULL || client_myds->myconn==NULL || client_myds->myconn->userinfo==NULL) - return; - if (client_myds->myconn->userinfo->fe_username==NULL) - return; - char *fe=client_myds->myconn->userinfo->fe_username; - char *a = (char *)" /* %s=%s */"; - char *b = (char *)malloc(strlen(a)+strlen(fe)+strlen(mysql_thread___add_ldap_user_comment)); - sprintf(b,a,mysql_thread___add_ldap_user_comment,fe); - PtrSize_t _new_pkt; - _new_pkt.ptr = malloc(strlen(b) + _pkt->size); - memcpy(_new_pkt.ptr , _pkt->ptr, 5); - unsigned char *_c=(unsigned char *)_new_pkt.ptr; - _c+=5; - void *idx = memchr((char *)_pkt->ptr+5, ' ', _pkt->size-5); - if (idx) { - size_t first_word_len = (char *)idx - (char *)_pkt->ptr - 5; - if (((char *)_pkt->ptr+5)[0]=='/' && ((char *)_pkt->ptr+5)[1]=='*') { - b[1]=' '; - b[2]=' '; - b[strlen(b)-1] = ' '; - b[strlen(b)-2] = ' '; - } - memcpy(_c, (char *)_pkt->ptr+5, first_word_len); - _c+= first_word_len; - memcpy(_c,b,strlen(b)); - _c+= strlen(b); - memcpy(_c, (char *)idx, _pkt->size - 5 - first_word_len); - } else { - memcpy(_c, (char *)_pkt->ptr+5, _pkt->size-5); - _c+=_pkt->size-5; - memcpy(_c,b,strlen(b)); - } - l_free(_pkt->size,_pkt->ptr); - _pkt->size = _pkt->size + strlen(b); - _pkt->ptr = _new_pkt.ptr; - free(b); - CurrentQuery.QueryLength = _pkt->size - 5; - CurrentQuery.QueryPointer = (unsigned char *)_pkt->ptr + 5; -} - -void Client_Session::finishQuery(ProxySQL_Data_Stream *myds, MySQL_Connection *myconn, bool prepared_stmt_with_no_params) { +void Client_Session::finishQuery(ProxySQL_Data_Stream *pds, MySQL_Connection *myconn, bool prepared_stmt_with_no_params) { + assert(this->session_type==PROXYSQL_SESSION_MYSQL); + MySQL_Data_Stream * myds = (MySQL_Data_Stream *) pds; myds->myconn->reduce_auto_increment_delay_token(); if (locked_on_hostgroup >= 0) { if (qpo->multiplex == -1) { @@ -7344,7 +4316,7 @@ void Client_Session::finishQuery(ProxySQL_Data_Stream *myds, MySQL_Connection *m 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_mysql_connection(myds); + ((MySQL_Session *)this)->create_new_session_and_reset_mysql_connection(myds); } else { myds->destroy_MySQL_Connection_From_Pool(true); } @@ -7411,6 +4383,11 @@ bool Client_Session::known_query_for_locked_on_hostgroup(uint64_t digest) { void Client_Session::unable_to_parse_set_statement(bool *lock_hostgroup) { // we couldn't parse the query + MySQL_Data_Stream *client_myds = NULL; + if (this->session_type==PROXYSQL_SESSION_MYSQL) { + client_myds = ((MySQL_Session *)this)->client_myds; + } + assert(client_myds != NULL); string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Locking hostgroup for query %s\n", nqn.c_str()); if (qpo->multiplex == -1) { @@ -7447,7 +4424,7 @@ void Client_Session::unable_to_parse_set_statement(bool *lock_hostgroup) { bool Client_Session::has_any_backend() { for (unsigned int j=0;j < mybes->len;j++) { MySQL_Backend *tmp_mybe=(MySQL_Backend *)mybes->index(j); - ProxySQL_Data_Stream *__myds=tmp_mybe->server_myds; + MySQL_Data_Stream *__myds=tmp_mybe->server_myds; if (__myds->myconn) { return true; } @@ -7540,3 +4517,4 @@ void Client_Session::generate_status_one_hostgroup(int hid, std::string& s) { s = j_res.dump(); delete resultset; } + diff --git a/lib/Makefile b/lib/Makefile index 99b8344a9..07d33bd4d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -123,7 +123,7 @@ MYCXXFLAGS=-std=c++11 $(MYCFLAGS) $(PSQLCH) $(ENABLE_EPOLL) default: libproxysql.a .PHONY: default -_OBJ_CXX = ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo SpookyV2.oo MySQL_Authentication.oo gen_utils.oo sqlite3db.oo mysql_connection.oo MySQL_HostGroups_Manager.oo ProxySQL_Data_Stream.oo ProxyWorker_Thread.oo Client_Session.oo MySQL_Session.oo MySQL_Protocol.oo mysql_backend.oo Query_Processor.oo ProxySQL_Admin.oo ProxySQL_Config.oo ProxySQL_Restapi.oo MySQL_Monitor.oo MySQL_Logger.oo thread.oo MySQL_PreparedStatement.oo ProxySQL_Cluster.oo ClickHouse_Authentication.oo ClickHouse_Server.oo ProxySQL_Statistics.oo Chart_bundle_js.oo ProxySQL_HTTP_Server.oo ProxySQL_RESTAPI_Server.oo font-awesome.min.css.oo main-bundle.min.css.oo set_parser.oo MySQL_Variables.oo c_tokenizer.oo proxysql_utils.oo +_OBJ_CXX = ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo SpookyV2.oo MySQL_Authentication.oo gen_utils.oo sqlite3db.oo mysql_connection.oo MySQL_HostGroups_Manager.oo ProxySQL_Data_Stream.oo MySQL_Data_Stream.oo ProxyWorker_Thread.oo Client_Session.oo MySQL_Session.oo MySQL_Protocol.oo mysql_backend.oo Query_Processor.oo ProxySQL_Admin.oo ProxySQL_Config.oo ProxySQL_Restapi.oo MySQL_Monitor.oo MySQL_Logger.oo thread.oo MySQL_PreparedStatement.oo ProxySQL_Cluster.oo ClickHouse_Authentication.oo ClickHouse_Server.oo ProxySQL_Statistics.oo Chart_bundle_js.oo ProxySQL_HTTP_Server.oo ProxySQL_RESTAPI_Server.oo font-awesome.min.css.oo main-bundle.min.css.oo set_parser.oo MySQL_Variables.oo c_tokenizer.oo proxysql_utils.oo OBJ_CXX = $(patsubst %,$(ODIR)/%,$(_OBJ_CXX)) HEADERS = ../include/*.h ../include/*.hpp diff --git a/lib/MySQL_Data_Stream.cpp b/lib/MySQL_Data_Stream.cpp new file mode 100644 index 000000000..9d1a70ba9 --- /dev/null +++ b/lib/MySQL_Data_Stream.cpp @@ -0,0 +1,1453 @@ +#include "proxysql.h" +#include "cpp.h" +#include +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +#include "MySQL_PreparedStatement.h" +#include "ProxySQL_Data_Stream.h" +#include "MySQL_Data_Stream.h" +#include "MySQL_Session.h" + +#include + +/* + +in libssl 1.1.0 +struct bio_st { + const BIO_METHOD *method; + long (*callback) (struct bio_st *, int, const char *, int, long, long); + char *cb_arg; + int init; + int shutdown; + int flags; + int retry_reason; + int num; + void *ptr; + struct bio_st *next_bio; + struct bio_st *prev_bio; + int references; + uint64_t num_read; + uint64_t num_write; + CRYPTO_EX_DATA ex_data; + CRYPTO_RWLOCK *lock; +}; +*/ + +typedef int CRYPTO_REF_COUNT; + +/** + * @brief This is the 'bio_st' struct definition from libssl 3.0.0. NOTE: This is an internal struct from + * OpenSSL library, currently it's used for performing checks on the reads/writes performed on the BIO objects. + * It's extremely important to keep this struct up to date with each OpenSSL dependency update. + */ +struct bio_st { + OSSL_LIB_CTX *libctx; + const BIO_METHOD *method; + /* bio, mode, argp, argi, argl, ret */ +#ifndef OPENSSL_NO_DEPRECATED_3_0 + BIO_callback_fn callback; +#endif + BIO_callback_fn_ex callback_ex; + char *cb_arg; /* first argument for the callback */ + int init; + int shutdown; + int flags; /* extra storage */ + int retry_reason; + int num; + void *ptr; + struct bio_st *next_bio; /* used by filter BIOs */ + struct bio_st *prev_bio; /* used by filter BIOs */ + CRYPTO_REF_COUNT references; + uint64_t num_read; + uint64_t num_write; + CRYPTO_EX_DATA ex_data; + CRYPTO_RWLOCK *lock; +}; + + +#define RESULTSET_BUFLEN_DS_16K 16000 +#define RESULTSET_BUFLEN_DS_1M 1000*1024 + +extern ProxyWorker_Threads_Handler *GloPWTH; + +#ifdef DEBUG +static void __dump_pkt(const char *func, unsigned char *_ptr, unsigned int len) { + + if (GloVars.global.gdbg==0) return; + if (GloVars.global.gdbg_lvl[PROXY_DEBUG_PKT_ARRAY].verbosity < 8 ) return; + unsigned int i; + fprintf(stderr,"DUMP %d bytes FROM %s\n", len, func); + for(i = 0; i < len; i++) { + if(isprint(_ptr[i])) fprintf(stderr,"%c", _ptr[i]); else fprintf(stderr,"."); + if (i>0 && (i%16==15 || i==len-1)) { + unsigned int j; + if (i%16!=15) { + j=15-i%16; + while (j--) fprintf(stderr," "); + } + fprintf(stderr," --- "); + for (j=(i==len-1 ? ((int)(i/16))*16 : i-15 ) ; j<=i; j++) { + fprintf(stderr,"%02x ", _ptr[j]); + } + fprintf(stderr,"\n"); + } + } + fprintf(stderr,"\n\n"); + + +} +#endif + +#define queue_init(_q,_s) { \ + _q.size=_s; \ + _q.buffer=malloc(_q.size); \ + _q.head=0; \ + _q.tail=0; \ + _q.partial=0; \ + _q.pkt.ptr=NULL; \ + _q.pkt.size=0; \ +} + +#define queue_destroy(_q) { \ + if (_q.buffer) free(_q.buffer); \ + _q.buffer=NULL; \ + if (_q.pkt.ptr) { \ + l_free(_q.pkt.size,_q.pkt.ptr); \ + queueOUT.pkt.ptr=NULL; \ + } \ +} + +#define queue_zero(_q) { \ + memcpy(_q.buffer, (unsigned char *)_q.buffer + _q.tail, _q.head - _q.tail); \ + _q.head-=_q.tail; \ + _q.tail=0; \ +} + +#define queue_available(_q) (_q.size-_q.head) +#define queue_data(_q) (_q.head-_q.tail) + +#define queue_r(_q, _s) { \ + _q.tail+=_s; \ + if (_q.tail==_q.head) { \ + _q.head=0; \ + _q.tail=0; \ + } \ +} + +#define queue_w(_q,_s) (_q.head+=_s) + +#define queue_r_ptr(_q) ((unsigned char *)_q.buffer+_q.tail) +#define queue_w_ptr(_q) ((unsigned char *)_q.buffer+_q.head) + + + +//enum sslstatus { SSLSTATUS_OK, SSLSTATUS_WANT_IO, SSLSTATUS_FAIL}; + +static enum sslstatus get_sslstatus(SSL* ssl, int n) +{ + int err = SSL_get_error(ssl, n); + ERR_clear_error(); + switch (err) { + case SSL_ERROR_NONE: + return SSLSTATUS_OK; + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_READ: + return SSLSTATUS_WANT_IO; + case SSL_ERROR_ZERO_RETURN: + case SSL_ERROR_SYSCALL: + default: + return SSLSTATUS_FAIL; + } +} + + +void MySQL_Data_Stream::queue_encrypted_bytes(const char *buf, size_t len) { + ssl_write_buf = (char*)realloc(ssl_write_buf, ssl_write_len + len); + memcpy(ssl_write_buf + ssl_write_len, buf, len); + ssl_write_len += len; + //proxy_info("New ssl_write_len size: %u\n", ssl_write_len); +} + +enum sslstatus MySQL_Data_Stream::do_ssl_handshake() { + char buf[MY_SSL_BUFFER]; + enum sslstatus status; + int n = SSL_do_handshake(ssl); + if (n == 1) { + //proxy_info("SSL handshake completed\n"); + X509 *cert; + cert = SSL_get_peer_certificate(ssl); + if (cert) { + GENERAL_NAMES *alt_names = (stack_st_GENERAL_NAME *)X509_get_ext_d2i((X509*)cert, NID_subject_alt_name, 0, 0); + int alt_name_count = sk_GENERAL_NAME_num(alt_names); + + // Iterate all the SAN names, looking for SPIFFE identifier + for (int i = 0; i < alt_name_count; i++) { + GENERAL_NAME *san = sk_GENERAL_NAME_value(alt_names, i); + + // We only care about URI names + if (san->type == GEN_URI) { + if (san->d.uniformResourceIdentifier->data) { + const char* resource_data = + reinterpret_cast(san->d.uniformResourceIdentifier->data); + const char* spiffe_loc = strstr(resource_data, "spiffe"); + + // First name starting with 'spiffe' is considered the match. + if (spiffe_loc == resource_data) { + x509_subject_alt_name = strdup(resource_data); + } + } + } + } + + sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free); + X509_free(cert); + } else { + // we currently disable this annoying error + // in future we can configure this as per user level, specifying if the certificate is mandatory or not + // see issue #3424 + //proxy_error("X509 error: no required certificate sent by client\n"); + } + // In case the supplied certificate has a 'SAN'-'URI' identifier + // starting with 'spiffe', client certificate verification is performed. + if (x509_subject_alt_name != NULL) { + long rc = SSL_get_verify_result(ssl); + if (rc != X509_V_OK) { + proxy_error("Disconnecting %s:%d: X509 client SSL certificate verify error: (%d:%s)\n" , addr.addr, addr.port, rc, X509_verify_cert_error_string(rc)); + return SSLSTATUS_FAIL; + } + } + } + status = get_sslstatus(ssl, n); + //proxy_info("SSL status = %d\n", status); + /* Did SSL request to write bytes? */ + if (status == SSLSTATUS_WANT_IO) { + //proxy_info("SSL status is WANT_IO %d\n", status); + do { + n = BIO_read(wbio_ssl, buf, sizeof(buf)); + //proxy_info("BIO read = %d\n", n); + if (n > 0) { + //proxy_info("Queuing %d encrypted bytes\n", n); + queue_encrypted_bytes(buf, n); + } else if (!BIO_should_retry(wbio_ssl)) { + //proxy_info("BIO_should_retry failed\n"); + return SSLSTATUS_FAIL; + } + } while (n>0); + } + return status; +} + +// Constructor +MySQL_Data_Stream::MySQL_Data_Stream() { +/* + bytes_info.bytes_recv=0; + bytes_info.bytes_sent=0; + pkts_recv=0; + pkts_sent=0; + client_addr=NULL; + + addr.addr=NULL; + addr.port=0; + proxy_addr.addr=NULL; + proxy_addr.port=0; + + sess=NULL; +*/ + mysql_real_query.pkt.ptr=NULL; + mysql_real_query.pkt.size=0; + mysql_real_query.QueryPtr=NULL; + mysql_real_query.QuerySize=0; +/* + query_retries_on_failure=0; + connect_retries_on_failure=0; + max_connect_time=0; + wait_until=0; + pause_until=0; + kill_type=0; + connect_tries=0; + poll_fds_idx=-1; + resultset_length=0; + + revents = 0; + + PSarrayIN=NULL; + PSarrayOUT=NULL; + resultset=NULL; + queue_init(queueIN,QUEUE_T_DEFAULT_SIZE); + queue_init(queueOUT,QUEUE_T_DEFAULT_SIZE); +*/ + mybe=NULL; +/* + active=1; + mypolls=NULL; +*/ + myconn=NULL; // 20141011 +/* + DSS=STATE_NOT_CONNECTED; + encrypted=false; + switching_auth_stage = 0; + switching_auth_type = 0; + x509_subject_alt_name=NULL; + ssl=NULL; + rbio_ssl = NULL; + wbio_ssl = NULL; + ssl_write_len = 0; + ssl_write_buf = NULL; + net_failure=false; +*/ + CompPktIN.pkt.ptr=NULL; + CompPktIN.pkt.size=0; + CompPktIN.partial=0; + CompPktOUT.pkt.ptr=NULL; + CompPktOUT.pkt.size=0; + CompPktOUT.partial=0; +/* + multi_pkt.ptr=NULL; + multi_pkt.size=0; + + statuses.questions = 0; + statuses.myconnpoll_get = 0; + statuses.myconnpoll_put = 0; +*/ + com_field_wild=NULL; +} + +// Destructor +MySQL_Data_Stream::~MySQL_Data_Stream() { +/* + queue_destroy(queueIN); + queue_destroy(queueOUT); + if (client_addr) { + free(client_addr); + client_addr=NULL; + } + if (addr.addr) { + free(addr.addr); + addr.addr=NULL; + } + if (proxy_addr.addr) { + free(proxy_addr.addr); + proxy_addr.addr=NULL; + } +*/ + free_mysql_real_query(); + + if (com_field_wild) { + free(com_field_wild); + com_field_wild=NULL; + } +/* + proxy_debug(PROXY_DEBUG_NET,1, "Shutdown Data Stream. Session=%p, DataStream=%p\n" , sess, this); + PtrSize_t pkt; + if (PSarrayIN) { + while (PSarrayIN->len) { + PSarrayIN->remove_index_fast(0,&pkt); + l_free(pkt.size, pkt.ptr); + } + delete PSarrayIN; + } + if (PSarrayOUT) { + while (PSarrayOUT->len) { + PSarrayOUT->remove_index_fast(0,&pkt); + l_free(pkt.size, pkt.ptr); + } + delete PSarrayOUT; + } + if (resultset) { + while (resultset->len) { + resultset->remove_index_fast(0,&pkt); + l_free(pkt.size, pkt.ptr); + } + delete resultset; + } + if (mypolls) mypolls->remove_index_fast(poll_fds_idx); + +*/ + if (fd>0) { +// // Changing logic here. The socket should be closed only if it is not a backend + if (myds_type==MYDS_FRONTEND) { + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Sess:%p , MYDS:%p , MySQL_Connection %p %s: shutdown socket\n", sess, this, myconn, (myconn ? "not reusable" : "is empty")); + shut_hard(); + } + } + // Commenting the follow line of code and adding an assert. We should ensure that if a myconn exists it should be removed *before* + if (myds_type==MYDS_BACKEND || myds_type==MYDS_BACKEND_NOT_CONNECTED) { + assert(myconn==NULL); + } + if ( (myconn) && (myds_type==MYDS_FRONTEND) ) { delete myconn; myconn=NULL; } + if (encrypted) { + if (ssl) { + // NOTE: SSL standard requires a final 'close_notify' alert on socket + // shutdown. But for avoiding any kind of locking IO waiting for the + // other part, we perform a 'quiet' shutdown. For more context see + // MYSQL #29579. + SSL_set_quiet_shutdown(ssl, 1); + SSL_shutdown(ssl); + } + if (ssl) SSL_free(ssl); + } +/* + if (multi_pkt.ptr) { + l_free(multi_pkt.size,multi_pkt.ptr); + multi_pkt.ptr=NULL; + multi_pkt.size=0; + } +*/ + if (CompPktIN.pkt.ptr) { + l_free(CompPktIN.pkt.size,CompPktIN.pkt.ptr); + CompPktIN.pkt.ptr=NULL; + CompPktIN.pkt.size=0; + } + if (CompPktOUT.pkt.ptr) { + l_free(CompPktOUT.pkt.size,CompPktOUT.pkt.ptr); + CompPktOUT.pkt.ptr=NULL; + CompPktOUT.pkt.size=0; + } +/* + if (x509_subject_alt_name) { + free(x509_subject_alt_name); + x509_subject_alt_name=NULL; + } +*/ +} +// this function initializes a ProxySQL_Data_Stream +void MySQL_Data_Stream::init() { + if (myds_type!=MYDS_LISTENER) { + proxy_debug(PROXY_DEBUG_NET,1, "Init Data Stream. Session=%p, DataStream=%p -- type %d\n" , sess, this, myds_type); + if (PSarrayIN==NULL) PSarrayIN = new PtrSizeArray(); + if (PSarrayOUT==NULL) PSarrayOUT= new PtrSizeArray(); +// if (PSarrayOUTpending==NULL) PSarrayOUTpending= new PtrSizeArray(); + if (resultset==NULL) resultset = new PtrSizeArray(); + } + if (myds_type!=MYDS_FRONTEND) { + queue_destroy(queueIN); + queue_destroy(queueOUT); + } +} +/* +void ProxySQL_Data_Stream::reinit_queues() { + if (queueIN.buffer==NULL) + queue_init(queueIN,QUEUE_T_DEFAULT_SIZE); + if (queueOUT.buffer==NULL) + queue_init(queueOUT,QUEUE_T_DEFAULT_SIZE); +} +*/ +// this function initializes a ProxySQL_Data_Stream with arguments +void MySQL_Data_Stream::init(enum MySQL_DS_type _type, Client_Session *_sess, int _fd) { + myds_type=_type; + sess=_sess; + init(); + fd=_fd; + proxy_debug(PROXY_DEBUG_NET,1, "Initialized Data Stream. Session=%p, DataStream=%p, type=%d, fd=%d, myconn=%p\n" , sess, this, myds_type, fd, myconn); + //if (myconn==NULL) myconn = new MySQL_Connection(); + if (myconn) myconn->fd=fd; +} + +/* +// Soft shutdown of socket : it only deactivate the data stream +// TODO: should check the status of the data stream, and identify if it is safe to reconnect or if the session should be destroyed +void ProxySQL_Data_Stream::shut_soft() { + proxy_debug(PROXY_DEBUG_NET, 4, "Shutdown soft fd=%d. Session=%p, DataStream=%p\n", fd, sess, this); + active=0; + set_net_failure(); + //if (sess) sess->net_failure=1; +} + +// Hard shutdown of socket +void ProxySQL_Data_Stream::shut_hard() { + proxy_debug(PROXY_DEBUG_NET, 4, "Shutdown hard fd=%d. Session=%p, DataStream=%p\n", fd, sess, this); + set_net_failure(); + if (encrypted) { + // NOTE: SSL standard requires a final 'close_notify' alert on socket + // shutdown. But for avoiding any kind of locking IO waiting for the + // other part, we perform a 'quiet' shutdown. For more context see + // MYSQL #29579. + SSL_set_quiet_shutdown(ssl, 1); + SSL_shutdown(ssl); + } + if (fd >= 0) { + shutdown(fd, SHUT_RDWR); + close(fd); + fd = -1; + } +} +*/ + +void MySQL_Data_Stream::check_data_flow() { + if ( (PSarrayIN->len || queue_data(queueIN) ) && ( PSarrayOUT->len || queue_data(queueOUT) ) ){ + // there is data at both sides of the data stream: this is considered a fatal error + proxy_error("Session=%p, DataStream=%p -- Data at both ends of a MySQL data stream: IN <%d bytes %d packets> , OUT <%d bytes %d packets>\n", sess, this, PSarrayIN->len , queue_data(queueIN) , PSarrayOUT->len , queue_data(queueOUT)); + shut_soft(); + } + if ((myds_type==MYDS_BACKEND) && myconn && (myconn->fd==0) && (revents & POLLOUT)) { + int rc; + int error; + socklen_t len = sizeof(error); + rc=getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len); + assert(rc==0); + if (error==0) { + myconn->fd=fd; // connect succeeded + } else { + errno=error; + perror("check_data_flow"); + shut_soft(); + } + } +} + +int MySQL_Data_Stream::read_from_net() { + if (encrypted) { + //proxy_info("Entering\n"); + } + if ((revents & POLLIN)==0) return 0; + if (revents & POLLHUP) { + shut_soft(); + return -1; + } + + int r=0; + int s=queue_available(queueIN); + if (encrypted) { + // proxy_info("Queue available of %d bytes\n", s); + } + if (encrypted == false) { + if (pkts_recv) { + r = recv(fd, queue_w_ptr(queueIN), s, 0); + } else { + if (queueIN.partial == 0) { + // we are reading the very first packet + // to avoid issue with SSL, we will only read the header and eventually the first packet + r = recv(fd, queue_w_ptr(queueIN), 4, 0); + if (r == 4) { + // let's try to read a whole packet + mysql_hdr Hdr; + memcpy(&Hdr,queueIN.buffer,sizeof(mysql_hdr)); + r += recv(fd, queue_w_ptr(queueIN)+4, Hdr.pkt_length, 0); + } + } else { + r = recv(fd, queue_w_ptr(queueIN), s, 0); + } + } + } else { +/* + if (!SSL_is_init_finished(ssl)) { + int ret = SSL_do_handshake(ssl); + int ret2; + if (ret != 1) { + //ERR_print_errors_fp(stderr); + ret2 = SSL_get_error(ssl, ret); + fprintf(stderr,"%d\n",ret2); + } + return 0; + } else { + r = SSL_read (ssl, queue_w_ptr(queueIN), s); + } +*/ + PROXY_TRACE(); + if (s < MY_SSL_BUFFER) { + return 0; // no enough space for reads + } + char buf[MY_SSL_BUFFER]; + //ssize_t n = read(fd, buf, sizeof(buf)); + int n = recv(fd, buf, sizeof(buf), 0); + //proxy_info("SSL recv of %d bytes\n", n); + proxy_debug(PROXY_DEBUG_NET, 7, "Session=%p: recv() read %d bytes. num_write: %u , num_read: %u\n", sess, n, rbio_ssl->num_write , rbio_ssl->num_read); + if (n > 0 || rbio_ssl->num_write > rbio_ssl->num_read) { + //on_read_cb(buf, (size_t)n); + + char buf2[MY_SSL_BUFFER]; + int n2; + enum sslstatus status; + char *src = buf; + int len = n; + while (len > 0) { + n2 = BIO_write(rbio_ssl, src, len); + proxy_debug(PROXY_DEBUG_NET, 5, "Session=%p: write %d bytes into BIO %p, len=%d\n", sess, n2, rbio_ssl, len); + //proxy_info("BIO_write with len = %d and %d bytes\n", len , n2); + if (n2 <= 0) { + shut_soft(); + return -1; + } + src += n2; + len -= n2; + if (!SSL_is_init_finished(ssl)) { + //proxy_info("SSL_is_init_finished NOT completed\n"); + if (do_ssl_handshake() == SSLSTATUS_FAIL) { + //proxy_info("SSL_is_init_finished failed!!\n"); + shut_soft(); + return -1; + } + if (!SSL_is_init_finished(ssl)) { + //proxy_info("SSL_is_init_finished yet NOT completed\n"); + return 0; + } + } else { + //proxy_info("SSL_is_init_finished completed\n"); + } + } + n2 = SSL_read (ssl, queue_w_ptr(queueIN), s); + proxy_debug(PROXY_DEBUG_NET, 5, "Session=%p: read %d bytes from BIO %p into a buffer with %d bytes free\n", sess, n2, rbio_ssl, s); + r = n2; + //proxy_info("Read %d bytes from SSL\n", r); + if (n2 > 0) { + } +/* + do { + n2 = SSL_read(ssl, buf2, sizeof(buf2)); + if (n2 > 0) { + + } + } while (n > 0); +*/ + status = get_sslstatus(ssl, n2); + //proxy_info("SSL status = %d\n", status); + if (status == SSLSTATUS_WANT_IO) { + do { + n2 = BIO_read(wbio_ssl, buf2, sizeof(buf2)); + //proxy_info("BIO_read with %d bytes\n", n2); + if (n2 > 0) { + queue_encrypted_bytes(buf2, n2); + } else if (!BIO_should_retry(wbio_ssl)) { + shut_soft(); + return -1; + } + } while (n2>0); + } + if (status == SSLSTATUS_FAIL) { + shut_soft(); + return -1; + } + } else { + r = n; + //r += SSL_read (ssl, queue_w_ptr(queueIN), s); + //proxy_info("Read %d bytes from SSL\n", r); + } + } +//__exit_read_from_next: + proxy_debug(PROXY_DEBUG_NET, 5, "Session=%p: read %d bytes from fd %d into a buffer of %d bytes free\n", sess, r, fd, s); + //proxy_error("read %d bytes from fd %d into a buffer of %d bytes free\n", r, fd, s); + if (r < 1) { + if (encrypted==false) { + int myds_errno=errno; + if (r==0 || (r==-1 && myds_errno != EINTR && myds_errno != EAGAIN)) { + shut_soft(); + } + } else { + int ssl_ret=SSL_get_error(ssl, r); + if (ssl_ret!=SSL_ERROR_WANT_READ && ssl_ret!=SSL_ERROR_WANT_WRITE) shut_soft(); + if (r==0 && revents==1) { + // revents returns 1 , but recv() returns 0 , so there is no data. + // Therefore the socket is already closed + shut_soft(); + } + } + } else { + queue_w(queueIN,r); + bytes_info.bytes_recv+=r; + if (mypolls) mypolls->last_recv[poll_fds_idx]=sess->thread->curtime; + } + return r; +} + +int MySQL_Data_Stream::write_to_net() { + int bytes_io=0; + int s = queue_data(queueOUT); + int n; + if (encrypted) { + //proxy_info("Data in write buffer: %d bytes\n", s); + } + if (s==0) { + if (encrypted == false) { + return 0; + } + if (ssl_write_len == 0 && wbio_ssl->num_write == wbio_ssl->num_read) { + return 0; + } + } + //VALGRIND_DISABLE_ERROR_REPORTING; + // splitting the ternary operation in IF condition for better readability + if (encrypted) { + bytes_io = SSL_write (ssl, queue_r_ptr(queueOUT), s); + //proxy_info("Used SSL_write to write %d bytes\n", bytes_io); + if (ssl_write_len || wbio_ssl->num_write > wbio_ssl->num_read) { + //proxy_info("ssl_write_len = %d , num_write = %d , num_read = %d\n", ssl_write_len , wbio_ssl->num_write , wbio_ssl->num_read); + char buf[MY_SSL_BUFFER]; + do { + n = BIO_read(wbio_ssl, buf, sizeof(buf)); + //proxy_info("BIO read = %d\n", n); + if (n > 0) { + //proxy_info("Setting %d byte in queue encrypted\n", n); + queue_encrypted_bytes(buf, n); + } + else if (!BIO_should_retry(wbio_ssl)) { + //proxy_info("BIO_should_retry failed\n"); + shut_soft(); + return -1; + } + } while (n>0); + } + if (ssl_write_len) { + n = write(fd, ssl_write_buf, ssl_write_len); + //proxy_info("Calling write() on SSL: %d\n", n); + if (n>0) { + if ((size_t)n < ssl_write_len) { + memmove(ssl_write_buf, ssl_write_buf+n, ssl_write_len-n); + } + ssl_write_len -= n; + ssl_write_buf = (char*)realloc(ssl_write_buf, ssl_write_len); + //proxy_info("new ssl_write_len: %u\n", ssl_write_len); + //if (ssl_write_len) { + // return n; // stop here + //} else { + // rc = n; // and continue + //} + //bytes_io += n; + } else { + int myds_errno=errno; + if (n==0 || (n==-1 && myds_errno != EINTR && myds_errno != EAGAIN)) { + shut_soft(); + return 0; + } else { + return -1; + } + } + } + } else { +#ifdef __APPLE__ + bytes_io = send(fd, queue_r_ptr(queueOUT), s, 0); +#else + bytes_io = send(fd, queue_r_ptr(queueOUT), s, MSG_NOSIGNAL); +#endif + } + if (encrypted) { + //proxy_info("bytes_io: %d\n", bytes_io); + } + //VALGRIND_ENABLE_ERROR_REPORTING; + if (bytes_io < 0) { + if (encrypted==false) { + if ((poll_fds_idx < 0) || (mypolls->fds[poll_fds_idx].revents & POLLOUT)) { // in write_to_net_poll() we has remove this safety + // so we enforce it here + shut_soft(); + } + } else { + int ssl_ret=SSL_get_error(ssl, bytes_io); + if (ssl_ret!=SSL_ERROR_WANT_READ && ssl_ret!=SSL_ERROR_WANT_WRITE) shut_soft(); + } + } else { + queue_r(queueOUT, bytes_io); + if (mypolls) mypolls->last_sent[poll_fds_idx]=sess->thread->curtime; + bytes_info.bytes_sent+=bytes_io; + } + if (bytes_io > 0) { + if (myds_type == MYDS_FRONTEND) { + if (sess) { + if (sess->thread) { + sess->thread->status_variables.stvar[st_var_queries_frontends_bytes_sent] += bytes_io; + } + } + } + } + return bytes_io; +} +/* +bool ProxySQL_Data_Stream::available_data_out() { + int buflen=queue_data(queueOUT); + if (buflen || PSarrayOUT->len) { + return true; + } + return false; +} + +void ProxySQL_Data_Stream::remove_pollout() { + struct pollfd *_pollfd; + _pollfd=&mypolls->fds[poll_fds_idx]; + _pollfd->events = 0; +} +*/ +void MySQL_Data_Stream::set_pollout() { + struct pollfd *_pollfd; + _pollfd=&mypolls->fds[poll_fds_idx]; + if (DSS > STATE_MARIADB_BEGIN && DSS < STATE_MARIADB_END) { + _pollfd->events = myconn->wait_events; + } else { + _pollfd->events = POLLIN; + //if (PSarrayOUT->len || available_data_out() || queueOUT.partial || (encrypted && !SSL_is_init_finished(ssl))) { + if (PSarrayOUT->len || available_data_out() || queueOUT.partial) { + _pollfd->events |= POLLOUT; + } + if (encrypted) { + if (ssl_write_len || wbio_ssl->num_write > wbio_ssl->num_read) { + _pollfd->events |= POLLOUT; + } else { + if (!SSL_is_init_finished(ssl)) { + //proxy_info("SSL_is_init_finished NOT completed\n"); + if (do_ssl_handshake() == SSLSTATUS_FAIL) { + //proxy_info("SSL_is_init_finished failed!!\n"); + shut_soft(); + return; + } + if (!SSL_is_init_finished(ssl)) { + //proxy_info("SSL_is_init_finished yet NOT completed\n"); + return; + } + _pollfd->events |= POLLOUT; + } else { + //proxy_info("SSL_is_init_finished completed\n"); + } + } + } + } + proxy_debug(PROXY_DEBUG_NET,1,"Session=%p, DataStream=%p -- Setting poll events %d for FD %d , DSS=%d , myconn=%p\n", sess, this, _pollfd->events , fd, DSS, myconn); +} + +int MySQL_Data_Stream::write_to_net_poll() { + int rc=0; + if (active==0) return rc; +/* + if (encrypted && !SSL_is_init_finished(ssl)) { + int ret = SSL_do_handshake(ssl); + int ret2; + if (ret != 1) { + //ERR_print_errors_fp(stderr); + ret2 = SSL_get_error(ssl, ret); + fprintf(stderr,"%d\n",ret2); + } + return 0; + } +*/ + if (encrypted) { + if (!SSL_is_init_finished(ssl)) { + //proxy_info("SSL_is_init_finished completed: NO!\n"); + if (do_ssl_handshake() == SSLSTATUS_FAIL) { + //proxy_info("SSL_is_init_finished failed!!\n"); + shut_soft(); + return -1; + } + } else { + //proxy_info("SSL_is_init_finished completed: YES\n"); + } +/* + if (!SSL_is_init_finished(ssl)) { + proxy_info("SSL_is_init_finished completed: NO!\n"); + if (fd>0 && sess->session_type == PROXYSQL_SESSION_MYSQL) { + set_pollout(); + return 0; + } + } +*/ + //proxy_info("ssl_write_len: %u\n", ssl_write_len); + if (ssl_write_len) { + int n = write(fd, ssl_write_buf, ssl_write_len); + //proxy_info("Calling write() on SSL: %d\n", n); + if (n>0) { + if ((size_t)n < ssl_write_len) { + memmove(ssl_write_buf, ssl_write_buf+n, ssl_write_len-n); + } + ssl_write_len -= n; + ssl_write_buf = (char*)realloc(ssl_write_buf, ssl_write_len); + //proxy_info("new ssl_write_len: %u\n", ssl_write_len); + if (ssl_write_len) { + return n; // stop here + } else { + rc = n; // and continue + } + } else { + int myds_errno=errno; + if (n==0 || (n==-1 && myds_errno != EINTR && myds_errno != EAGAIN)) { + shut_soft(); + return 0; + } else { + return -1; + } + } + } + } + proxy_debug(PROXY_DEBUG_NET,1,"Session=%p, DataStream=%p --\n", sess, this); + bool call_write_to_net = false; + if (queue_data(queueOUT)) { + call_write_to_net = true; + } + if (call_write_to_net == false) { + if (encrypted) { + if (ssl_write_len || wbio_ssl->num_write > wbio_ssl->num_read) { + call_write_to_net = true; + } + } + } + if (call_write_to_net) { + if (sess->session_type == PROXYSQL_SESSION_MYSQL) { + if (poll_fds_idx>-1) { // NOTE: attempt to force writes + if (net_failure==false) + rc += write_to_net(); + } + } else { + rc += write_to_net(); + } + } + if (fd>0 && sess->session_type == PROXYSQL_SESSION_MYSQL) { + // PROXYSQL_SESSION_MYSQL is a requirement, because it uses threads pool + // the other session types do not + set_pollout(); + } + return rc; +} + +int MySQL_Data_Stream::read_pkts() { + int rc=0; + int r=0; + while((r=buffer2array())) rc+=r; + return rc; +} + +int MySQL_Data_Stream::buffer2array() { + int ret=0; + bool fast_mode=sess->session_fast_forward; + { + unsigned long s = queue_data(queueIN); + if (s==0) return ret; + if ((queueIN.pkt.size==0) && s= RESULTSET_BUFLEN_DS_16K) { + // legacy approach + queueIN.pkt.ptr=l_alloc(queueIN.pkt.size); + memcpy(queueIN.pkt.ptr, queue_r_ptr(queueIN) , queueIN.pkt.size); + queue_r(queueIN, queueIN.pkt.size); + PSarrayIN->add(queueIN.pkt.ptr,queueIN.pkt.size); + queueIN.pkt.ptr = NULL; + } else { + if (PSarrayIN->len == 0) { + // it is empty, create a new block + // we allocate RESULTSET_BUFLEN_DS_16K instead of queueIN.pkt.size + // the block may be used later + queueIN.pkt.ptr=l_alloc(RESULTSET_BUFLEN_DS_16K); + memcpy(queueIN.pkt.ptr, queue_r_ptr(queueIN) , queueIN.pkt.size); + queue_r(queueIN, queueIN.pkt.size); + PSarrayIN->add(queueIN.pkt.ptr,queueIN.pkt.size); + queueIN.pkt.ptr = NULL; + } else { + // get a pointer to the last entry in PSarrayIN + PtrSize_t *last_pkt = PSarrayIN->index(PSarrayIN->len - 1); + if ((last_pkt->size + queueIN.pkt.size) > RESULTSET_BUFLEN_DS_16K) { + // there is not enough space, create a new block + // we allocate RESULTSET_BUFLEN_DS_16K instead of queueIN.pkt.size + // the block may be used later + queueIN.pkt.ptr=l_alloc(RESULTSET_BUFLEN_DS_16K); + memcpy(queueIN.pkt.ptr, queue_r_ptr(queueIN) , queueIN.pkt.size); + queue_r(queueIN, queueIN.pkt.size); + PSarrayIN->add(queueIN.pkt.ptr,queueIN.pkt.size); + queueIN.pkt.ptr = NULL; + } else { + // we append the packet at the end of the previous packet + memcpy((char *)last_pkt->ptr+last_pkt->size, queue_r_ptr(queueIN) , queueIN.pkt.size); + last_pkt->size += queueIN.pkt.size; + queue_r(queueIN, queueIN.pkt.size); + + } + } + } + queueIN.pkt.size=0; + return ret; + } + + if (myconn->get_status(STATUS_MYSQL_CONNECTION_COMPRESSION)==true) { + if ((queueIN.pkt.size==0) && queue_data(queueIN)>=7) { + proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Session=%p . Reading the header of a new compressed packet\n", sess); + memcpy(&queueIN.hdr,queue_r_ptr(queueIN), sizeof(mysql_hdr)); + queue_r(queueIN,sizeof(mysql_hdr)); + pkt_sid=queueIN.hdr.pkt_id; + queueIN.pkt.size=queueIN.hdr.pkt_length+sizeof(mysql_hdr)+3; + queueIN.pkt.ptr=l_alloc(queueIN.pkt.size); + memcpy(queueIN.pkt.ptr, &queueIN.hdr, sizeof(mysql_hdr)); // immediately copy the header into the packet + memcpy((unsigned char *)queueIN.pkt.ptr+sizeof(mysql_hdr), queue_r_ptr(queueIN), 3); // copy 3 bytes, the length of the uncompressed payload + queue_r(queueIN,3); + queueIN.partial=7; + mysql_hdr *_hdr; + _hdr=(mysql_hdr *)queueIN.pkt.ptr; + myconn->compression_pkt_id=_hdr->pkt_id; + ret+=7; + } + } else { + + if ((queueIN.pkt.size==0) && queue_data(queueIN)>=sizeof(mysql_hdr)) { + proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Session=%p . Reading the header of a new packet\n", sess); + memcpy(&queueIN.hdr,queue_r_ptr(queueIN),sizeof(mysql_hdr)); + pkt_sid=queueIN.hdr.pkt_id; + queue_r(queueIN,sizeof(mysql_hdr)); + queueIN.pkt.size=queueIN.hdr.pkt_length+sizeof(mysql_hdr); + queueIN.pkt.ptr=l_alloc(queueIN.pkt.size); + memcpy(queueIN.pkt.ptr, &queueIN.hdr, sizeof(mysql_hdr)); // immediately copy the header into the packet + queueIN.partial=sizeof(mysql_hdr); + ret+=sizeof(mysql_hdr); + } + } + if ((queueIN.pkt.size>0) && queue_data(queueIN)) { + int b= ( queue_data(queueIN) > (queueIN.pkt.size - queueIN.partial) ? (queueIN.pkt.size - queueIN.partial) : queue_data(queueIN) ); + proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Session=%p . Copied %d bytes into packet\n", sess, b); + memcpy((unsigned char *)queueIN.pkt.ptr + queueIN.partial, queue_r_ptr(queueIN),b); + queue_r(queueIN,b); + queueIN.partial+=b; +// if (queueIN.partial == 80) { +// proxy_info("Breakpoint\n"); +// } + ret+=b; + } + if ((queueIN.pkt.size>0) && (queueIN.pkt.size==queueIN.partial) ) { + if (myconn->get_status(STATUS_MYSQL_CONNECTION_COMPRESSION)==true) { + Bytef *dest = NULL; + uLongf destLen; + proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Session=%p . Copied the whole compressed packet\n", sess); + unsigned int progress=0; + unsigned int datalength; + unsigned int payload_length=0; + unsigned char *u; + u=(unsigned char *)queueIN.pkt.ptr; + payload_length=*(u+6); + payload_length=payload_length*256+*(u+5); + payload_length=payload_length*256+*(u+4); + unsigned char *_ptr=(unsigned char *)queueIN.pkt.ptr+7; + + if (payload_length) { + // the payload is compressed + destLen=payload_length; + //dest=(Bytef *)l_alloc(destLen); + dest=(Bytef *)malloc(destLen); + int rc=uncompress(dest, &destLen, _ptr, queueIN.pkt.size-7); + if (rc!=Z_OK) { + // for some reason, uncompress failed + // accoding to debugging on #1410 , it seems some library may send uncompress data claiming it is compressed + // we try to assume it is not compressed, and we do some sanity check + memcpy(dest, _ptr, queueIN.pkt.size-7); + datalength=queueIN.pkt.size-7; + // some sanity check now + unsigned char _u; + bool sanity_check = false; + _u = *(u+9); + // 2nd and 3rd bytes are 0 + if (_u == 0) { + _u = *(u+8); + if (_u == 0) { + _u = *(u+7); + // 1st byte = size - 7 + unsigned int _size = _u ; + if (queueIN.pkt.size-7 == _size) { + sanity_check = true; + } + } + } + if (sanity_check == false) { + proxy_error("Unable to uncompress a compressed packet\n"); + shut_soft(); + return ret; + } + } + datalength=payload_length; + // change _ptr to the new buffer + _ptr=dest; + } else { + // the payload is not compressed + datalength=queueIN.pkt.size-7; + } + while (progress= progress + sizeof(mysql_hdr)); // FIXME: this is a too optimistic assumption + memcpy(&_a,_ptr+progress,sizeof(mysql_hdr)); + CompPktIN.pkt.size=_a.pkt_length+sizeof(mysql_hdr); + CompPktIN.pkt.ptr=(unsigned char *)l_alloc(CompPktIN.pkt.size); + if ((datalength-progress) >= CompPktIN.pkt.size) { + // we can copy the whole packet + memcpy(CompPktIN.pkt.ptr, _ptr+progress, CompPktIN.pkt.size); + CompPktIN.partial=0; // stays 0 + progress+=CompPktIN.pkt.size; + PSarrayIN->add(CompPktIN.pkt.ptr, CompPktIN.pkt.size); + CompPktIN.pkt.ptr=NULL; // sanity + } else { + // not enough data for the whole packet + memcpy(CompPktIN.pkt.ptr, _ptr+progress, (datalength-progress)); + CompPktIN.partial+=(datalength-progress); + progress=datalength; // we reached the end + } + } else { + if ((datalength-progress) >= (CompPktIN.pkt.size-CompPktIN.partial)) { + // we can copy till the end of the packet + memcpy((char *)CompPktIN.pkt.ptr + CompPktIN.partial , _ptr+progress, CompPktIN.pkt.size - CompPktIN.partial); + CompPktIN.partial=0; + progress+= CompPktIN.pkt.size - CompPktIN.partial; + PSarrayIN->add(CompPktIN.pkt.ptr, CompPktIN.pkt.size); + CompPktIN.pkt.ptr=NULL; // sanity + } else { + // not enough data for the whole packet + memcpy((char *)CompPktIN.pkt.ptr + CompPktIN.partial , _ptr+progress , (datalength-progress)); + CompPktIN.partial+=(datalength-progress); + progress=datalength; // we reached the end + } + } + } + if (payload_length) { + //l_free(destLen,dest); + free(dest); + } + l_free(queueIN.pkt.size,queueIN.pkt.ptr); + pkts_recv++; + queueIN.pkt.size=0; + queueIN.pkt.ptr=NULL; + } else { + PSarrayIN->add(queueIN.pkt.ptr,queueIN.pkt.size); + pkts_recv++; + queueIN.pkt.size=0; + queueIN.pkt.ptr=NULL; + } + } + return ret; +} + + +void MySQL_Data_Stream::generate_compressed_packet() { +#define MAX_COMPRESSED_PACKET_SIZE 10*1024*1024 + unsigned int total_size=0; + unsigned int i=0; + PtrSize_t *p=NULL; + while (ilen && total_sizeindex(i); + total_size+=p->size; + i++; + } + if (i>=2) { + // we successfully read at least 2 packets + if (total_size>MAX_COMPRESSED_PACKET_SIZE) { + // total_size is too big, we remove the last packet read + total_size-=p->size; + } + } + if (total_size <= MAX_COMPRESSED_PACKET_SIZE) { + // this worked in the past . it applies for small packets + uLong sourceLen=total_size; + Bytef *source=(Bytef *)l_alloc(total_size); + uLongf destLen=total_size*120/100+12; + Bytef *dest=(Bytef *)malloc(destLen); + i=0; + total_size=0; + while (total_sizeremove_index(0,&p2); + memcpy(source+total_size,p2.ptr,p2.size); + total_size+=p2.size; + l_free(p2.size,p2.ptr); + } + int rc=compress(dest, &destLen, source, sourceLen); + assert(rc==Z_OK); + l_free(total_size, source); + queueOUT.pkt.size=destLen+7; + queueOUT.pkt.ptr=l_alloc(queueOUT.pkt.size); + mysql_hdr hdr; + hdr.pkt_length=destLen; + hdr.pkt_id=++myconn->compression_pkt_id; + memcpy((unsigned char *)queueOUT.pkt.ptr,&hdr,sizeof(mysql_hdr)); + hdr.pkt_length=total_size; + memcpy((unsigned char *)queueOUT.pkt.ptr+4,&hdr,3); + memcpy((unsigned char *)queueOUT.pkt.ptr+7,dest,destLen); + free(dest); + } else { + // if we reach here, it means we have one single packet larger than MAX_COMPRESSED_PACKET_SIZE + PtrSize_t p2; + PSarrayOUT->remove_index(0,&p2); + + unsigned int len1=MAX_COMPRESSED_PACKET_SIZE/2; + unsigned int len2=p2.size-len1; + uLongf destLen1; + uLongf destLen2; + Bytef *dest1; + Bytef *dest2; + int rc; + + mysql_hdr hdr; + + destLen1=len1*120/100+12; + dest1=(Bytef *)malloc(destLen1+7); + destLen2=len2*120/100+12; + dest2=(Bytef *)malloc(destLen2+7); + rc=compress(dest1+7, &destLen1, (const unsigned char *)p2.ptr, len1); + assert(rc==Z_OK); + rc=compress(dest2+7, &destLen2, (const unsigned char *)p2.ptr+len1, len2); + assert(rc==Z_OK); + + hdr.pkt_length=destLen1; + hdr.pkt_id=++myconn->compression_pkt_id; + memcpy(dest1,&hdr,sizeof(mysql_hdr)); + hdr.pkt_length=len1; + memcpy((char *)dest1+sizeof(mysql_hdr),&hdr,3); + + hdr.pkt_length=destLen2; + hdr.pkt_id=++myconn->compression_pkt_id; + memcpy(dest2,&hdr,sizeof(mysql_hdr)); + hdr.pkt_length=len2; + memcpy((char *)dest2+sizeof(mysql_hdr),&hdr,3); + + queueOUT.pkt.size=destLen1+destLen2+7+7; + queueOUT.pkt.ptr=l_alloc(queueOUT.pkt.size); + memcpy((char *)queueOUT.pkt.ptr,dest1,destLen1+7); + memcpy((char *)queueOUT.pkt.ptr+destLen1+7,dest2,destLen2+7); + free(dest1); + free(dest2); + l_free(p2.size,p2.ptr); + } +} + + +int MySQL_Data_Stream::array2buffer() { + int ret=0; + unsigned int idx=0; + bool cont=true; + if (sess) { + if (sess->mirror==true) { // if this is a mirror session, just empty it + idx=PSarrayOUT->len; + goto __exit_array2buffer; + } + } + while (cont) { + //VALGRIND_DISABLE_ERROR_REPORTING; + if (queue_available(queueOUT)==0) { + goto __exit_array2buffer; + } + if (queueOUT.partial==0) { // read a new packet + if (PSarrayOUT->len-idx) { + proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Session=%p . DataStream: %p -- Removing a packet from array\n", sess, this); + if (queueOUT.pkt.ptr) { + l_free(queueOUT.pkt.size,queueOUT.pkt.ptr); + queueOUT.pkt.ptr=NULL; + } + //VALGRIND_ENABLE_ERROR_REPORTING; + if (myconn->get_status(STATUS_MYSQL_CONNECTION_COMPRESSION)==true) { + proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Session=%p . DataStream: %p -- Compression enabled\n", sess, this); + generate_compressed_packet(); // it is copied directly into queueOUT.pkt + } else { + //VALGRIND_DISABLE_ERROR_REPORTING; + memcpy(&queueOUT.pkt,PSarrayOUT->index(idx), sizeof(PtrSize_t)); + idx++; + //VALGRIND_ENABLE_ERROR_REPORTING; + // this is a special case, needed because compression is enabled *after* the first OK + if (DSS==STATE_CLIENT_AUTH_OK) { + DSS=STATE_SLEEP; + // enable compression + if (myconn->options.server_capabilities & CLIENT_COMPRESS) { + if (myconn->options.compression_min_length) { + myconn->set_status(true, STATUS_MYSQL_CONNECTION_COMPRESSION); + } + } else { + //explicitly disable compression + myconn->options.compression_min_length=0; + myconn->set_status(false, STATUS_MYSQL_CONNECTION_COMPRESSION); + } + } + } +#ifdef DEBUG + { __dump_pkt(__func__,(unsigned char *)queueOUT.pkt.ptr,queueOUT.pkt.size); } +#endif + } else { + cont=false; + continue; + } + } + int b= ( queue_available(queueOUT) > (queueOUT.pkt.size - queueOUT.partial) ? (queueOUT.pkt.size - queueOUT.partial) : queue_available(queueOUT) ); + //VALGRIND_DISABLE_ERROR_REPORTING; + memcpy(queue_w_ptr(queueOUT), (unsigned char *)queueOUT.pkt.ptr + queueOUT.partial, b); + //VALGRIND_ENABLE_ERROR_REPORTING; + queue_w(queueOUT,b); + proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Session=%p . DataStream: %p -- Copied %d bytes into send buffer\n", sess, this, b); + queueOUT.partial+=b; + ret=b; + if (queueOUT.partial==queueOUT.pkt.size) { + if (queueOUT.pkt.ptr) { + l_free(queueOUT.pkt.size,queueOUT.pkt.ptr); + queueOUT.pkt.ptr=NULL; + } + proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Session=%p . DataStream: %p -- Packet completely written into send buffer\n", sess, this); + queueOUT.partial=0; + pkts_sent+=1; + } + } +__exit_array2buffer: + if (idx) { + PSarrayOUT->remove_index_range(0,idx); + } + return ret; +} + +unsigned char * MySQL_Data_Stream::resultset2buffer(bool del) { + unsigned int i; + unsigned int l=0; + unsigned char *mybuff=(unsigned char *)l_alloc(resultset_length); + PtrSize_t *ps; + for (i=0;ilen;i++) { + ps=resultset->index(i); + memcpy(mybuff+l,ps->ptr,ps->size); + if (del) l_free(ps->size,ps->ptr); + l+=ps->size; + } + return mybuff; +}; + +void MySQL_Data_Stream::buffer2resultset(unsigned char *ptr, unsigned int size) { + unsigned char *__ptr=ptr; + mysql_hdr hdr; + unsigned int l; + void *buff = NULL; + unsigned int bl; + unsigned int bf; + while (__ptradd(buff,bl-bf); + buff=NULL; + } + } + if (buff == NULL) { + if (__ptr+RESULTSET_BUFLEN_DS_1M <= ptr+size) { + bl = RESULTSET_BUFLEN_DS_1M; + } else { + bl = RESULTSET_BUFLEN_DS_16K; + } + if (l > bl) { + bl = l; // make sure there is the space to copy a packet + } + buff = malloc(bl); + bf = bl; + } + memcpy((char *)buff + (bl-bf), __ptr, l); + bf -= l; + __ptr+=l; +/* + l=hdr.pkt_length+sizeof(mysql_hdr); + pkt=l_alloc(l); + memcpy(pkt,__ptr,l); + resultset->add(pkt,l); + __ptr+=l; +*/ + } + if (buff) { + // last buffer to add + resultset->add(buff,bl-bf); + } +}; +int MySQL_Data_Stream::array2buffer_full() { + int rc=0; + int r=0; + while((r=array2buffer())) rc+=r; + return rc; +} + +int MySQL_Data_Stream::assign_fd_from_mysql_conn() { + assert(myconn); + //proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Sess=%p, myds=%p, oldFD=%d, newFD=%d\n", this->sess, this, fd, myconn->myconn.net.fd); + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Sess=%p, myds=%p, oldFD=%d, newFD=%d\n", this->sess, this, fd, myconn->fd); + fd=myconn->fd; + return fd; +} + +void MySQL_Data_Stream::unplug_backend() { + DSS=STATE_NOT_INITIALIZED; + myconn=NULL; + myds_type=MYDS_BACKEND_NOT_CONNECTED; + mypolls->remove_index_fast(poll_fds_idx); + mypolls=NULL; + fd=0; +} + +/* +void ProxySQL_Data_Stream::set_net_failure() { + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Sess=%p, myds=%p , myds_type:%d\n", this->sess, this, myds_type); +#ifdef DEBUG + if (myds_type!=MYDS_FRONTEND) { + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Sess=%p, myds=%p , myds_type:%d not frontend\n", this->sess, this, myds_type); + } +#endif // DEBUG + net_failure=true; +} + +void ProxySQL_Data_Stream::setDSS_STATE_QUERY_SENT_NET() { + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Sess=%p, myds=%p\n", this->sess, this); + DSS=STATE_QUERY_SENT_NET; +} +*/ + +void MySQL_Data_Stream::return_MySQL_Connection_To_Pool() { + MySQL_Connection *mc=myconn; + mc->last_time_used=sess->thread->curtime; + // before detaching, check if last_HG_affected_rows matches . if yes, set it back to -1 + if (mybe) { + if (mybe->hostgroup_id == sess->last_HG_affected_rows) { + sess->last_HG_affected_rows = -1; + } + } + unsigned long long intv = mysql_thread___connection_max_age_ms; + intv *= 1000; + if ( + (( (intv) && (mc->last_time_used > mc->creation_time + intv) ) + || + ( mc->local_stmts->get_num_backend_stmts() > (unsigned int)GloPWTH->variables.max_stmts_per_connection )) + && + // NOTE: If the current session if in 'PINGING_SERVER' status, there is + // no need to reset the session. The destruction and creation of a new + // session in case this session has exceeded the time specified by + // 'connection_max_age_ms' will be deferred to the next time the session + // is used outside 'PINGING_SERVER' operation. For more context see #3502. + sess->status != PINGING_SERVER + ) { + if (mysql_thread___reset_connection_algorithm == 2) { + ((MySQL_Session *)sess)->create_new_session_and_reset_mysql_connection(this); + } else { + destroy_MySQL_Connection_From_Pool(true); + } + } else { + detach_connection(); + unplug_backend(); +#ifdef STRESSTEST_POOL + MyHGM->push_MyConn_to_pool(mc); // #644 +#else + sess->thread->push_MyConn_local(mc); +#endif + } +} + +void MySQL_Data_Stream::free_mysql_real_query() { + if (mysql_real_query.QueryPtr) { + mysql_real_query.end(); + } +} + +/* +void ProxySQL_Data_Stream::destroy_queues() { + queue_destroy(queueIN); + queue_destroy(queueOUT); +} +*/ + +void MySQL_Data_Stream::destroy_MySQL_Connection_From_Pool(bool sq) { + MySQL_Connection *mc=myconn; + mc->last_time_used=sess->thread->curtime; + detach_connection(); + unplug_backend(); + mc->send_quit=sq; + MyHGM->destroy_MyConn_from_pool(mc); +} + +/* +bool ProxySQL_Data_Stream::data_in_rbio() { + if (rbio_ssl->num_write > rbio_ssl->num_read) { + return true; + } + return false; +} +*/ diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index 4a53ffab0..ceed97803 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -18,6 +18,11 @@ #include "prometheus_helpers.h" #include "proxysql_utils.h" +#include "MySQL_Data_Stream.h" +#include "MySQL_Session.h" + +#include "proxysql_admin.h" + #define char_malloc (char *)malloc #define itostr(__s, __i) { __s=char_malloc(32); sprintf(__s, "%lld", __i); } @@ -3193,8 +3198,9 @@ MySQL_Connection * MySrvConnList::get_random_MyConn(Client_Session *sess, bool f } else { i=fastrand()%l; } - if (sess && sess->client_myds && sess->client_myds->myconn && sess->client_myds->myconn->userinfo) { - MySQL_Connection * client_conn = sess->client_myds->myconn; + // APPLY THIS LOGIC ONLY FOR MYSQL FRONTEND + if (sess && ((MySQL_Session *)sess)->client_myds && ((MySQL_Session *)sess)->client_myds->myconn && ((MySQL_Session *)sess)->client_myds->myconn->userinfo) { + MySQL_Connection * client_conn = ((MySQL_Session *)sess)->client_myds->myconn; get_random_MyConn_inner_search(i, l, conn_found_idx, connection_quality_level, number_of_matching_session_variables, client_conn); if (connection_quality_level !=3 ) { // we didn't find the perfect connection get_random_MyConn_inner_search(0, i, conn_found_idx, connection_quality_level, number_of_matching_session_variables, client_conn); diff --git a/lib/MySQL_Logger.cpp b/lib/MySQL_Logger.cpp index df978347b..5a0bf6502 100644 --- a/lib/MySQL_Logger.cpp +++ b/lib/MySQL_Logger.cpp @@ -7,6 +7,9 @@ #include "MySQL_PreparedStatement.h" #include "MySQL_Logger.hpp" +#include "MySQL_Session.h" +#include "MySQL_Data_Stream.h" + #include #include @@ -224,13 +227,16 @@ void MySQL_Event::write_auth(std::fstream *f, Client_Session *sess) { default: break; } - if (sess->client_myds) { - if (sess->client_myds->proxy_addr.addr) { - std::string s = sess->client_myds->proxy_addr.addr; - s += ":" + std::to_string(sess->client_myds->proxy_addr.port); - j["proxy_addr"] = s; + if (sess->session_type==PROXYSQL_SESSION_MYSQL || sess->session_type==PROXYSQL_SESSION_ADMIN || sess->session_type==PROXYSQL_SESSION_SQLITE) { + MySQL_Data_Stream *client_myds = ((MySQL_Session *)sess)->client_myds; + if (client_myds) { + if (client_myds->proxy_addr.addr) { + std::string s = client_myds->proxy_addr.addr; + s += ":" + std::to_string(client_myds->proxy_addr.port); + j["proxy_addr"] = s; + } + j["ssl"] = client_myds->encrypted; } - j["ssl"] = sess->client_myds->encrypted; } // for performance reason, we are moving the write lock // right before the write to disk @@ -623,10 +629,16 @@ void MySQL_Logger::audit_set_datadir(char *s) { flush_log(); }; -void MySQL_Logger::log_request(Client_Session *sess, ProxySQL_Data_Stream *myds) { +void MySQL_Logger::log_request(Client_Session *c_sess, ProxySQL_Data_Stream *pds) { if (events.enabled==false) return; if (events.logfile==NULL) return; + if (c_sess->session_type != PROXYSQL_SESSION_MYSQL) + return; + + MySQL_Session *sess = (MySQL_Session *)c_sess; + MySQL_Data_Stream *myds = (MySQL_Data_Stream *)pds; + MySQL_Connection_userinfo *ui=sess->client_myds->myconn->userinfo; uint64_t curtime_real=realtime_time(); @@ -741,11 +753,16 @@ void MySQL_Logger::log_request(Client_Session *sess, ProxySQL_Data_Stream *myds) } } -void MySQL_Logger::log_audit_entry(log_event_type _et, Client_Session *sess, ProxySQL_Data_Stream *myds, char *xi) { +void MySQL_Logger::log_audit_entry(log_event_type _et, Client_Session *c_sess, ProxySQL_Data_Stream *pds, char *xi) { if (audit.enabled==false) return; if (audit.logfile==NULL) return; - if (sess == NULL) return; + if (c_sess == NULL) return; + if (c_sess->session_type != PROXYSQL_SESSION_MYSQL) + return; + + MySQL_Session *sess = (MySQL_Session *)c_sess; + MySQL_Data_Stream *myds = (MySQL_Data_Stream *)pds; if (sess->client_myds == NULL) return; MySQL_Connection_userinfo *ui= NULL; diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 6ee6619b9..12e514411 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -6,6 +6,7 @@ #include "MySQL_PreparedStatement.h" #include "ProxySQL_Data_Stream.h" +#include "MySQL_Data_Stream.h" #include "MySQL_Authentication.hpp" #include "MySQL_LDAP_Authentication.hpp" #include "MySQL_Variables.h" @@ -297,7 +298,7 @@ MySQL_Prepared_Stmt_info::MySQL_Prepared_Stmt_info(unsigned char *pkt, unsigned -void MySQL_Protocol::init(ProxySQL_Data_Stream **__myds, MySQL_Connection_userinfo *__userinfo, Client_Session *__sess) { +void MySQL_Protocol::init(MySQL_Data_Stream **__myds, MySQL_Connection_userinfo *__userinfo, Client_Session *__sess) { myds=__myds; userinfo=__userinfo; sess=__sess; @@ -1464,7 +1465,8 @@ bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned in // Check and get 'Client Auth Plugin' if capability is supported char* client_auth_plugin = nullptr; if (pkt + len > pkt + cur) { - int capabilities = (*myds)->sess->client_myds->myconn->options.client_flag; + //int capabilities = (*myds)->sess->client_myds->myconn->options.client_flag; // was this a bug? + int capabilities = (*myds)->myconn->options.client_flag; if (capabilities & CLIENT_PLUGIN_AUTH) { client_auth_plugin = reinterpret_cast(pkt + cur); } @@ -1601,13 +1603,14 @@ bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned in if (default_transaction_isolation != j_user_attributes.end()) { std::string def_trx_isolation_val = j_user_attributes["default-transaction_isolation"].get(); - mysql_variables.client_set_value((*myds)->sess, SQL_ISOLATION_LEVEL, def_trx_isolation_val.c_str()); + mysql_variables.client_set_value((MySQL_Session *)((*myds)->sess), SQL_ISOLATION_LEVEL, def_trx_isolation_val.c_str()); } } } assert(sess); - assert(sess->client_myds); - MySQL_Connection *myconn=sess->client_myds->myconn; + assert(sess->session_type==PROXYSQL_SESSION_MYSQL); + assert(((MySQL_Session *)sess)->client_myds); + MySQL_Connection *myconn=((MySQL_Session *)sess)->client_myds->myconn; assert(myconn); myconn->set_charset(charset, CONNECT_START); @@ -1618,10 +1621,10 @@ bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned in /* We are processing handshake from client. Client sends us a character set it will use in communication. * we store this character set in the client's variables to use later in multiplexing with different backends */ - mysql_variables.client_set_value(sess, SQL_CHARACTER_SET_RESULTS, ss.str().c_str()); - mysql_variables.client_set_value(sess, SQL_CHARACTER_SET_CLIENT, ss.str().c_str()); - mysql_variables.client_set_value(sess, SQL_CHARACTER_SET_CONNECTION, ss.str().c_str()); - mysql_variables.client_set_value(sess, SQL_COLLATION_CONNECTION, ss.str().c_str()); + mysql_variables.client_set_value((MySQL_Session *)sess, SQL_CHARACTER_SET_RESULTS, ss.str().c_str()); + mysql_variables.client_set_value((MySQL_Session *)sess, SQL_CHARACTER_SET_CLIENT, ss.str().c_str()); + mysql_variables.client_set_value((MySQL_Session *)sess, SQL_CHARACTER_SET_CONNECTION, ss.str().c_str()); + mysql_variables.client_set_value((MySQL_Session *)sess, SQL_COLLATION_CONNECTION, ss.str().c_str()); } return ret; } @@ -2154,9 +2157,10 @@ __exit_do_auth: free(tmp_pass); } #endif - assert(sess); - assert(sess->client_myds); - myconn=sess->client_myds->myconn; + assert(sess != NULL); + assert(sess->session_type==PROXYSQL_SESSION_MYSQL || sess->session_type==PROXYSQL_SESSION_ADMIN || sess->session_type==PROXYSQL_SESSION_STATS || sess->session_type==PROXYSQL_SESSION_SQLITE || sess->session_type==PROXYSQL_SESSION_CLICKHOUSE); + assert(((MySQL_Session *)sess)->client_myds != NULL); + myconn=((MySQL_Session *)sess)->client_myds->myconn; assert(myconn); myconn->set_charset(charset, CONNECT_START); { @@ -2166,10 +2170,10 @@ __exit_do_auth: /* We are processing handshake from client. Client sends us a character set it will use in communication. * we store this character set in the client's variables to use later in multiplexing with different backends */ - mysql_variables.client_set_value(sess, SQL_CHARACTER_SET_RESULTS, ss.str().c_str()); - mysql_variables.client_set_value(sess, SQL_CHARACTER_SET_CLIENT, ss.str().c_str()); - mysql_variables.client_set_value(sess, SQL_CHARACTER_SET_CONNECTION, ss.str().c_str()); - mysql_variables.client_set_value(sess, SQL_COLLATION_CONNECTION, ss.str().c_str()); + mysql_variables.client_set_value((MySQL_Session *)sess, SQL_CHARACTER_SET_RESULTS, ss.str().c_str()); + mysql_variables.client_set_value((MySQL_Session *)sess, SQL_CHARACTER_SET_CLIENT, ss.str().c_str()); + mysql_variables.client_set_value((MySQL_Session *)sess, SQL_CHARACTER_SET_CONNECTION, ss.str().c_str()); + mysql_variables.client_set_value((MySQL_Session *)sess, SQL_COLLATION_CONNECTION, ss.str().c_str()); } // enable compression if (capabilities & CLIENT_COMPRESS) { @@ -2256,7 +2260,7 @@ bool MySQL_Protocol::verify_user_attributes(int calling_line, const char *callin auto default_transaction_isolation = j.find("default-transaction_isolation"); if (default_transaction_isolation != j.end()) { std::string default_transaction_isolation_value = j["default-transaction_isolation"].get(); - mysql_variables.client_set_value((*myds)->sess, SQL_ISOLATION_LEVEL, default_transaction_isolation_value.c_str()); + mysql_variables.client_set_value((MySQL_Session *)((*myds)->sess), SQL_ISOLATION_LEVEL, default_transaction_isolation_value.c_str()); } } } @@ -2654,7 +2658,7 @@ void MySQL_ResultSet::init(MySQL_Protocol *_myprot, MYSQL_RES *_res, MYSQL *_my, if (myprot==NULL) { return; // this is a mirror } - ProxySQL_Data_Stream * c_myds = *(myprot->myds); + MySQL_Data_Stream * c_myds = *(myprot->myds); if (c_myds->com_field_list==false) { myprot->generate_pkt_column_count(false,&pkt.ptr,&pkt.size,sid,num_fields,this); sid++; @@ -2727,7 +2731,7 @@ void MySQL_ResultSet::init_with_stmt(MySQL_Connection *myconn) { PROXY_TRACE2(); assert(stmt); MYSQL_STMT *_stmt = stmt; - ProxySQL_Data_Stream * c_myds = *(myprot->myds); + MySQL_Data_Stream * c_myds = *(myprot->myds); buffer_to_PSarrayOut(); unsigned long long total_size=0; MYSQL_ROWS *r=_stmt->result.data; @@ -2986,7 +2990,7 @@ void MySQL_ResultSet::add_eof() { resultset_completed=true; } -void MySQL_ResultSet::add_err(ProxySQL_Data_Stream *_myds) { +void MySQL_ResultSet::add_err(MySQL_Data_Stream *_myds) { PtrSize_t pkt; if (myprot) { MYSQL *_mysql=_myds->myconn->mysql; diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 6f4721018..5e5d564a1 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -1,16 +1,156 @@ #include "MySQL_Session.h" #include "ProxySQL_Data_Stream.h" +#include "MySQL_Data_Stream.h" #include "proxysql_utils.h" +#include "re2/re2.h" +#include "re2/regexp.h" +#include "SpookyV2.h" #include "query_processor.h" #include "MySQL_PreparedStatement.h" #include "MySQL_Authentication.hpp" #include "MySQL_LDAP_Authentication.hpp" +#include "MySQL_Logger.hpp" +#include "set_parser.h" + +#include "proxysql_admin.h" + +#include "libinjection.h" +#include "libinjection_sqli.h" + +#define SELECT_VERSION_COMMENT "select @@version_comment limit 1" +#define SELECT_VERSION_COMMENT_LEN 32 + +#define PROXYSQL_VERSION_COMMENT "\x01\x00\x00\x01\x01\x27\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x11\x40\x40\x76\x65\x72\x73\x69\x6f\x6e\x5f\x63\x6f\x6d\x6d\x65\x6e\x74\x00\x0c\x21\x00\x18\x00\x00\x00\xfd\x00\x00\x1f\x00\x00\x05\x00\x00\x03\xfe\x00\x00\x02\x00\x0b\x00\x00\x04\x0a(ProxySQL)\x05\x00\x00\x05\xfe\x00\x00\x02\x00" +#define PROXYSQL_VERSION_COMMENT_LEN 81 + +// PROXYSQL_VERSION_COMMENT_WITH_OK is sent instead of PROXYSQL_VERSION_COMMENT +// if Client supports CLIENT_DEPRECATE_EOF +#define PROXYSQL_VERSION_COMMENT_WITH_OK "\x01\x00\x00\x01\x01" \ +"\x27\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x11\x40\x40\x76\x65\x72\x73\x69\x6f\x6e\x5f\x63\x6f\x6d\x6d\x65\x6e\x74\x00\x0c\x21\x00\x18\x00\x00\x00\xfd\x00\x00\x1f\x00\x00" \ +"\x0b\x00\x00\x03\x0a(ProxySQL)" \ +"\x07\x00\x00\x04\xfe\x00\x00\x02\x00\x00\x00" +#define PROXYSQL_VERSION_COMMENT_WITH_OK_LEN 74 + +#define SELECT_CONNECTION_ID "SELECT CONNECTION_ID()" +#define SELECT_CONNECTION_ID_LEN 22 +#define SELECT_LAST_INSERT_ID "SELECT LAST_INSERT_ID()" +#define SELECT_LAST_INSERT_ID_LEN 23 +#define SELECT_LAST_INSERT_ID_LIMIT1 "SELECT LAST_INSERT_ID() LIMIT 1" +#define SELECT_LAST_INSERT_ID_LIMIT1_LEN 31 +#define SELECT_VARIABLE_IDENTITY "SELECT @@IDENTITY" +#define SELECT_VARIABLE_IDENTITY_LEN 17 +#define SELECT_VARIABLE_IDENTITY_LIMIT1 "SELECT @@IDENTITY LIMIT 1" +#define SELECT_VARIABLE_IDENTITY_LIMIT1_LEN 25 + +static const std::set mysql_variables_boolean = { + "aurora_read_replica_read_committed", + "foreign_key_checks", + "innodb_strict_mode", + "innodb_table_locks", + "sql_auto_is_null", + "sql_big_selects", + "sql_log_bin", + "sql_safe_updates", + "unique_checks", +}; + +static const std::set mysql_variables_numeric = { + "auto_increment_increment", + "auto_increment_offset", + "group_concat_max_len", + "innodb_lock_wait_timeout", + "join_buffer_size", + "lock_wait_timeout", + "long_query_time", + "max_execution_time", + "max_heap_table_size", + "max_join_size", + "max_sort_length", + "optimizer_prune_level", + "optimizer_search_depth", + "query_cache_type", + "sort_buffer_size", + "sql_select_limit", + "timestamp", + "tmp_table_size", + "wsrep_sync_wait" +}; +static const std::set mysql_variables_strings = { + "default_storage_engine", + "default_tmp_storage_engine", + "group_replication_consistency", + "lc_messages", + "lc_time_names", + "optimizer_switch", + "wsrep_osu_method", +}; + +static inline char is_digit(char c) { + if(c >= '0' && c <= '9') + return 1; + return 0; +} +static inline char is_normal_char(char c) { + if(c >= 'a' && c <= 'z') + return 1; + if(c >= 'A' && c <= 'Z') + return 1; + if(c >= '0' && c <= '9') + return 1; + if(c == '$' || c == '_') + return 1; + return 0; +} + +extern MARIADB_CHARSET_INFO * proxysql_find_charset_name(const char * const name); +extern MARIADB_CHARSET_INFO * proxysql_find_charset_collate_names(const char *csname, const char *collatename); +extern const MARIADB_CHARSET_INFO * proxysql_find_charset_nr(unsigned int nr); +extern MARIADB_CHARSET_INFO * proxysql_find_charset_collate(const char *collatename); extern MySQL_Authentication *GloMyAuth; extern MySQL_LDAP_Authentication *GloMyLdapAuth; +extern ProxySQL_Admin *GloAdmin; +extern MySQL_Logger *GloMyLogger; extern Query_Processor *GloQPro; extern MySQL_STMT_Manager_v14 *GloMyStmt; +extern Query_Cache *GloQC; +extern ProxySQL_Admin *GloAdmin; +extern ProxyWorker_Threads_Handler *GloPWTH; + +#ifdef PROXYSQLCLICKHOUSE +extern ClickHouse_Authentication *GloClickHouseAuth; +extern ClickHouse_Server *GloClickHouseServer; +#endif // PROXYSQLCLICKHOUSE + + +// NEXT_IMMEDIATE_NEW is a new macro to use *outside* handler(). +// handler() should check the return code of the function it calls, and if +// true should jump to handler_again +#define NEXT_IMMEDIATE_NEW(new_st) do { set_status(new_st); return true; } while (0) + +MySQL_Session::MySQL_Session() { + session_type=PROXYSQL_SESSION_MYSQL; // set type + client_myds = NULL; +} + +MySQL_Session::~MySQL_Session() { + if (client_myds) { + if (client_authenticated) { + switch (session_type) { +#ifdef PROXYSQLCLICKHOUSE + case PROXYSQL_SESSION_CLICKHOUSE: + GloClickHouseAuth->decrease_frontend_user_connections(client_myds->myconn->userinfo->username); + break; +#endif // PROXYSQLCLICKHOUSE + default: + GloMyAuth->decrease_frontend_user_connections(client_myds->myconn->userinfo->username); + break; + } + } + delete client_myds; + } +} void MySQL_Session::handler_WCDSS_MYSQL_COM_RESET_CONNECTION(PtrSize_t *pkt) { proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got MYSQL_COM_RESET_CONNECTION packet\n"); @@ -21,7 +161,7 @@ void MySQL_Session::handler_WCDSS_MYSQL_COM_RESET_CONNECTION(PtrSize_t *pkt) { bool transaction_persistent = this->transaction_persistent; // Re-initialize the session - reset(); + mysql_session_reset(); init(); // Recover the relevant session values @@ -127,7 +267,7 @@ void MySQL_Session::handler_WCDSS_MYSQL_COM_CHANGE_USER(PtrSize_t *pkt, bool *wr proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_CHANGE_USER packet\n"); //if (session_type == PROXYSQL_SESSION_MYSQL) { if (session_type == PROXYSQL_SESSION_MYSQL || session_type == PROXYSQL_SESSION_SQLITE) { - reset(); + mysql_session_reset(); init(); if (client_authenticated) { if (use_ldap_auth == false) { @@ -246,7 +386,7 @@ void MySQL_Session::handler_WCDSS_MYSQL_COM_STMT_SEND_LONG_DATA(PtrSize_t& pkt) l_free(pkt.size,pkt.ptr); } -// this function was inline inside Client_Session::get_pkts_from_client +// this function was inline inside MySQL_Session::get_pkts_from_client // where: // status = WAITING_CLIENT_DATA // client_myds->DSS = STATE_SLEEP @@ -364,7 +504,7 @@ void MySQL_Session::handler_WCDSS_MYSQL_COM_STMT_PREPARE(PtrSize_t& pkt) { } } -// this function was inline inside Client_Session::get_pkts_from_client +// this function was inline inside MySQL_Session::get_pkts_from_client // where: // status = WAITING_CLIENT_DATA // client_myds->DSS = STATE_SLEEP @@ -501,7 +641,8 @@ void MySQL_Session::handler_WCDSS_MYSQL_COM_STMT_EXECUTE(PtrSize_t& pkt) { // now it returns: // true: NEXT_IMMEDIATE(st) needs to be called // false: continue -bool MySQL_Session::handler_rc0_PROCESSING_STMT_PREPARE(enum session_status& st, ProxySQL_Data_Stream *myds, bool& prepared_stmt_with_no_params) { +bool MySQL_Session::handler_rc0_PROCESSING_STMT_PREPARE(enum session_status& st, ProxySQL_Data_Stream *pds, bool& prepared_stmt_with_no_params) { + MySQL_Data_Stream *myds = (MySQL_Data_Stream *)pds; thread->status_variables.stvar[st_var_backend_stmt_prepare]++; GloMyStmt->wrlock(); uint32_t client_stmtid; @@ -551,7 +692,8 @@ bool MySQL_Session::handler_rc0_PROCESSING_STMT_PREPARE(enum session_status& st, // this function used to be inline -void MySQL_Session::handler_rc0_PROCESSING_STMT_EXECUTE(ProxySQL_Data_Stream *myds) { +void MySQL_Session::handler_rc0_PROCESSING_STMT_EXECUTE(ProxySQL_Data_Stream *pds) { + MySQL_Data_Stream *myds = (MySQL_Data_Stream *)pds; thread->status_variables.stvar[st_var_backend_stmt_execute]++; PROXY_TRACE2(); if (CurrentQuery.mysql_stmt) { @@ -616,4 +758,4168 @@ void MySQL_Session::mysql_session_reset() { delete SLDH; SLDH=NULL; } + if (client_myds) { + if (client_myds->myconn) { + client_myds->myconn->reset(); + } + } + reset(); +} + +int MySQL_Session::handler_again___status_PINGING_SERVER() { + MySQL_Data_Stream *myds=mybe->server_myds; + assert(myds->myconn); + MySQL_Connection *myconn=myds->myconn; + int rc=myconn->async_ping(myds->revents); + if (rc==0) { + myconn->async_state_machine=ASYNC_IDLE; + myconn->compute_unknown_transaction_status(); + //if (mysql_thread___multiplexing && (myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + // due to issue #2096 we disable the global check on mysql_thread___multiplexing + if ((myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + myds->return_MySQL_Connection_To_Pool(); + } else { + myds->destroy_MySQL_Connection_From_Pool(true); + } + delete mybe->server_myds; + mybe->server_myds=NULL; + set_status(session_status___NONE); + return -1; + } else { + if (rc==-1 || rc==-2) { + if (rc==-2) { + unsigned long long us = mysql_thread___ping_timeout_server*1000; + us += thread->curtime; + us -= myds->wait_until; + proxy_error("Ping timeout during ping on %s:%d after %lluus (timeout %dms)\n", myconn->parent->address, myconn->parent->port, us, mysql_thread___ping_timeout_server); + MyHGM->p_update_mysql_error_counter(p_mysql_error_type::proxysql, myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, ER_PROXYSQL_PING_TIMEOUT); + } else { // rc==-1 + int myerr=mysql_errno(myconn->mysql); + detected_broken_connection(__FILE__ , __LINE__ , __func__ , "during ping", myconn, myerr, mysql_error(myconn->mysql) , true); + MyHGM->p_update_mysql_error_counter(p_mysql_error_type::mysql, myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myerr); + } + myds->destroy_MySQL_Connection_From_Pool(false); + myds->fd=0; + delete mybe->server_myds; + mybe->server_myds=NULL; + return -1; + } else { + // rc==1 , nothing to do for now + if (myds->mypolls==NULL) { + thread->mypolls.add(POLLIN|POLLOUT, myds->fd, myds, thread->curtime); + } + } + } + return 0; +} + +int MySQL_Session::handler_again___status_RESETTING_CONNECTION() { + MySQL_Data_Stream *myds=mybe->server_myds; + assert(myds->myconn); + MySQL_Connection *myconn=myds->myconn; + if (myds->mypolls==NULL) { + thread->mypolls.add(POLLIN|POLLOUT, myds->fd, myds, thread->curtime); + } + myds->DSS=STATE_MARIADB_QUERY; + // we recreate local_stmts : see issue #752 + delete myconn->local_stmts; + myconn->local_stmts=new MySQL_STMTs_local_v14(false); // false by default, it is a backend + int rc=myconn->async_change_user(myds->revents); + if (rc==0) { + __sync_fetch_and_add(&MyHGM->status.backend_change_user, 1); + //myds->myconn->userinfo->set(client_myds->myconn->userinfo); + myds->myconn->reset(); + myds->DSS = STATE_MARIADB_GENERIC; + myconn->async_state_machine=ASYNC_IDLE; +// if (mysql_thread___multiplexing && (myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + myds->return_MySQL_Connection_To_Pool(); +// } else { +// myds->destroy_MySQL_Connection_From_Pool(true); +// } + delete mybe->server_myds; + mybe->server_myds=NULL; + set_status(session_status___NONE); + return -1; + } else { + if (rc==-1 || rc==-2) { + if (rc==-2) { + proxy_error("Change user timeout during COM_CHANGE_USER on %s , %d\n", myconn->parent->address, myconn->parent->port); + MyHGM->p_update_mysql_error_counter(p_mysql_error_type::mysql, myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, ER_PROXYSQL_CHANGE_USER_TIMEOUT); + } else { // rc==-1 + int myerr=mysql_errno(myconn->mysql); + MyHGM->p_update_mysql_error_counter( + p_mysql_error_type::mysql, + myconn->parent->myhgc->hid, + myconn->parent->address, + myconn->parent->port, + ( myerr ? myerr : ER_PROXYSQL_OFFLINE_SRV ) + ); + if (myerr != 0) { + proxy_error("Detected an error during COM_CHANGE_USER on (%d,%s,%d) , FD (Conn:%d , MyDS:%d) : %d, %s\n", myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myds->fd, myds->myconn->fd, myerr, mysql_error(myconn->mysql)); + } else { + proxy_error( + "Detected an error during COM_CHANGE_USER on (%d,%s,%d) , FD (Conn:%d , MyDS:%d) : %d, %s\n", + myconn->parent->myhgc->hid, + myconn->parent->address, + myconn->parent->port, + myds->fd, + myds->myconn->fd, + ER_PROXYSQL_OFFLINE_SRV, + "Detected offline server prior to statement execution" + ); + } + } + myds->destroy_MySQL_Connection_From_Pool(false); + myds->fd=0; + //delete mybe->server_myds; + //mybe->server_myds=NULL; + RequestEnd_mysql(myds); //fix bug #682 + return -1; + } else { + // rc==1 , nothing to do for now + if (myds->mypolls==NULL) { + thread->mypolls.add(POLLIN|POLLOUT, myds->fd, myds, thread->curtime); + } + } + } + return 0; +} + +void MySQL_Session::create_new_session_and_reset_mysql_connection(MySQL_Data_Stream *_myds) { + MySQL_Data_Stream *new_myds = NULL; + MySQL_Connection * mc = _myds->myconn; + // we remove the connection from the original data stream + _myds->detach_connection(); + _myds->unplug_backend(); + + // we create a brand new session, a new data stream, and attach the connection to it + MySQL_Session * new_sess = new MySQL_Session(); + new_sess->mybe = new_sess->find_or_create_mysql_backend(mc->parent->myhgc->hid); + + new_myds = new_sess->mybe->server_myds; + new_myds->attach_connection(mc); + new_myds->assign_fd_from_mysql_conn(); + new_myds->myds_type = MYDS_BACKEND; + new_sess->to_process = 1; + new_myds->wait_until = thread->curtime + mysql_thread___connect_timeout_server*1000; // max_timeout + mc->last_time_used = thread->curtime; + new_myds->myprot.init(&new_myds, new_myds->myconn->userinfo, NULL); + new_sess->status = RESETTING_CONNECTION; + mc->async_state_machine = ASYNC_IDLE; // may not be true, but is used to correctly perform error handling + new_myds->DSS = STATE_MARIADB_QUERY; + thread->register_session_connection_handler(new_sess,true); + if (new_myds->mypolls==NULL) { + thread->mypolls.add(POLLIN|POLLOUT, new_myds->fd, new_myds, thread->curtime); + } + int rc = new_sess->handler(); + if (rc==-1) { + unsigned int sess_idx = thread->mysql_sessions->len-1; + thread->unregister_session(sess_idx); + delete new_sess; + } +} + +// this function used to be inline. +// now it returns: +// true: NEXT_IMMEDIATE(CONNECTING_SERVER) needs to be called +// false: continue +bool MySQL_Session::handler_minus1_ClientLibraryError(ProxySQL_Data_Stream *pds, int myerr, char **errmsg) { + MySQL_Data_Stream *myds = (MySQL_Data_Stream *)pds; + MySQL_Connection *myconn = myds->myconn; + bool retry_conn=false; + // client error, serious + detected_broken_connection(__FILE__ , __LINE__ , __func__ , "running query", myconn, myerr, mysql_error(myconn->mysql) , true); + if (myds->query_retries_on_failure > 0) { + myds->query_retries_on_failure--; + if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + if (myds->myconn->MyRS && myds->myconn->MyRS->transfer_started) { + // transfer to frontend has started, we cannot retry + } else { + if (myds->myconn->mysql->server_status & SERVER_MORE_RESULTS_EXIST) { + // transfer to frontend has started, because this is, at least, + // the second resultset coming from the server + // we cannot retry + proxy_warning("Disabling query retry because SERVER_MORE_RESULTS_EXIST is set\n"); + } else { + retry_conn=true; + proxy_warning("Retrying query.\n"); + } + } + } + } + myds->destroy_MySQL_Connection_From_Pool(false); + myds->fd=0; + if (retry_conn) { + myds->DSS=STATE_NOT_INITIALIZED; + //previous_status.push(PROCESSING_QUERY); + switch(status) { // this switch can be replaced with a simple previous_status.push(status), but it is here for readibility + case PROCESSING_QUERY: + previous_status.push(PROCESSING_QUERY); + break; + case PROCESSING_STMT_PREPARE: + previous_status.push(PROCESSING_STMT_PREPARE); + break; + case PROCESSING_STMT_EXECUTE: + previous_status.push(PROCESSING_STMT_EXECUTE); + break; + default: + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } + if (*errmsg) { + free(*errmsg); + *errmsg = NULL; + } + return true; + } + if (*errmsg) { + free(*errmsg); + *errmsg = NULL; + } + return false; +} + + +// this function was inline +void MySQL_Session::handler_minus1_LogErrorDuringQuery(MySQL_Connection *myconn, int myerr, char *errmsg) { + if (mysql_thread___verbose_query_error) { + proxy_warning("Error during query on (%d,%s,%d,%lu) , user \"%s@%s\" , schema \"%s\" , %d, %s . digest_text = \"%s\"\n", myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myconn->get_mysql_thread_id(), client_myds->myconn->userinfo->username, (client_myds->addr.addr ? client_myds->addr.addr : (char *)"unknown" ), client_myds->myconn->userinfo->schemaname, myerr, ( errmsg ? errmsg : mysql_error(myconn->mysql)), CurrentQuery.QueryParserArgs.digest_text ); + } else { + proxy_warning("Error during query on (%d,%s,%d,%lu): %d, %s\n", myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myconn->get_mysql_thread_id(), myerr, ( errmsg ? errmsg : mysql_error(myconn->mysql))); + } + MyHGM->add_mysql_errors(myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, client_myds->myconn->userinfo->username, (client_myds->addr.addr ? client_myds->addr.addr : (char *)"unknown" ), client_myds->myconn->userinfo->schemaname, myerr, (char *)( errmsg ? errmsg : mysql_error(myconn->mysql))); +} + + +// this function used to be inline. +// now it returns: +// true: +// if handler_ret == -1 : return +// if handler_ret == 0 : NEXT_IMMEDIATE(CONNECTING_SERVER) needs to be called +// false: continue +bool MySQL_Session::handler_minus1_HandleErrorCodes(ProxySQL_Data_Stream *pds, int myerr, char **errmsg, int& handler_ret) { + MySQL_Data_Stream *myds = (MySQL_Data_Stream *)pds; + bool retry_conn=false; + MySQL_Connection * myconn = myds->myconn; + handler_ret = 0; // default + switch (myerr) { + case 1317: // Query execution was interrupted + if (killed==true) { // this session is being kiled + handler_ret = -1; + return true; + } + if (myds->killed_at) { + // we intentionally killed the query + break; + } + break; + case 1047: // WSREP has not yet prepared node for application use + case 1053: // Server shutdown in progress + myconn->parent->connect_error(myerr); + if (myds->query_retries_on_failure > 0) { + myds->query_retries_on_failure--; + if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + retry_conn=true; + proxy_warning("Retrying query.\n"); + } + } + switch (myerr) { + case 1047: // WSREP has not yet prepared node for application use + case 1053: // Server shutdown in progress + myds->destroy_MySQL_Connection_From_Pool(false); + break; + default: + if (mysql_thread___reset_connection_algorithm == 2) { + create_new_session_and_reset_mysql_connection(myds); + } else { + myds->destroy_MySQL_Connection_From_Pool(true); + } + break; + } + myconn = myds->myconn; + myds->fd=0; + if (retry_conn) { + myds->DSS=STATE_NOT_INITIALIZED; + //previous_status.push(PROCESSING_QUERY); + switch(status) { // this switch can be replaced with a simple previous_status.push(status), but it is here for readibility + case PROCESSING_QUERY: + previous_status.push(PROCESSING_QUERY); + break; + case PROCESSING_STMT_PREPARE: + previous_status.push(PROCESSING_STMT_PREPARE); + break; + default: + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } + if (*errmsg) { + free(*errmsg); + *errmsg = NULL; + } + return true; // it will call NEXT_IMMEDIATE(CONNECTING_SERVER); + //NEXT_IMMEDIATE(CONNECTING_SERVER); + } + //handler_ret = -1; + //return handler_ret; + break; + case 1153: // ER_NET_PACKET_TOO_LARGE + proxy_warning("Error ER_NET_PACKET_TOO_LARGE during query on (%d,%s,%d,%lu): %d, %s\n", myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myconn->get_mysql_thread_id(), myerr, mysql_error(myconn->mysql)); + break; + default: + break; // continue normally + } + return false; +} + +// this function used to be inline. +void MySQL_Session::handler_minus1_GenerateErrorMessage(ProxySQL_Data_Stream *pds, MySQL_Connection *myconn, bool& wrong_pass) { + MySQL_Data_Stream *myds = (MySQL_Data_Stream *)pds; + switch (status) { + case PROCESSING_QUERY: + if (myconn) { + MySQL_Result_to_MySQL_wire(myconn->mysql, myconn->MyRS, myds); + } else { + MySQL_Result_to_MySQL_wire(NULL, NULL, myds); + } + break; + case PROCESSING_STMT_PREPARE: + { + char sqlstate[10]; + if (myconn && myconn->mysql) { + sprintf(sqlstate,"%s",mysql_sqlstate(myconn->mysql)); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,mysql_errno(myconn->mysql),sqlstate,(char *)mysql_stmt_error(myconn->query.stmt)); + GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_CLOSE, this, NULL); + } else { + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1, 2013, (char *)"HY000" ,(char *)"Lost connection to MySQL server during query"); + GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_CLOSE, this, NULL); + } + client_myds->pkt_sid++; + if (previous_status.size()) { + // an STMT_PREPARE failed + // we have a previous status, probably STMT_EXECUTE, + // but returning to that status is not safe after STMT_PREPARE failed + // for this reason we exit immediately + wrong_pass=true; + } + } + break; + case PROCESSING_STMT_EXECUTE: + { + char sqlstate[10]; + if (myconn && myconn->mysql) { + if (myconn->MyRS) { + PROXY_TRACE2(); + ((MySQL_Session *)(myds->sess))->handler_rc0_PROCESSING_STMT_EXECUTE(myds); + } else { + sprintf(sqlstate,"%s",mysql_sqlstate(myconn->mysql)); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,mysql_errno(myconn->mysql),sqlstate,(char *)mysql_stmt_error(myconn->query.stmt)); + } + } else { + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1, 2013, (char *)"HY000" ,(char *)"Lost connection to MySQL server during query"); + } + client_myds->pkt_sid++; + } + break; + default: + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } +} + +// this function was inline +void MySQL_Session::handler_minus1_HandleBackendConnection(ProxySQL_Data_Stream *pds, MySQL_Connection *myconn) { + MySQL_Data_Stream *myds = (MySQL_Data_Stream *)pds; + if (myds->myconn) { + myds->myconn->reduce_auto_increment_delay_token(); + if (mysql_thread___multiplexing && (myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + 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_mysql_connection(myds); + } else { + myds->destroy_MySQL_Connection_From_Pool(true); + } + } else { + myds->return_MySQL_Connection_To_Pool(); + } + } else { + myconn->async_state_machine=ASYNC_IDLE; + myds->DSS=STATE_MARIADB_GENERIC; + } + } +} + +void MySQL_Session::add_ldap_comment_to_pkt(PtrSize_t *_pkt) { + if (GloMyLdapAuth==NULL) + return; + if (use_ldap_auth == false) + return; + if (client_myds==NULL || client_myds->myconn==NULL || client_myds->myconn->userinfo==NULL) + return; + if (client_myds->myconn->userinfo->fe_username==NULL) + return; + char *fe=client_myds->myconn->userinfo->fe_username; + char *a = (char *)" /* %s=%s */"; + char *b = (char *)malloc(strlen(a)+strlen(fe)+strlen(mysql_thread___add_ldap_user_comment)); + sprintf(b,a,mysql_thread___add_ldap_user_comment,fe); + PtrSize_t _new_pkt; + _new_pkt.ptr = malloc(strlen(b) + _pkt->size); + memcpy(_new_pkt.ptr , _pkt->ptr, 5); + unsigned char *_c=(unsigned char *)_new_pkt.ptr; + _c+=5; + void *idx = memchr((char *)_pkt->ptr+5, ' ', _pkt->size-5); + if (idx) { + size_t first_word_len = (char *)idx - (char *)_pkt->ptr - 5; + if (((char *)_pkt->ptr+5)[0]=='/' && ((char *)_pkt->ptr+5)[1]=='*') { + b[1]=' '; + b[2]=' '; + b[strlen(b)-1] = ' '; + b[strlen(b)-2] = ' '; + } + memcpy(_c, (char *)_pkt->ptr+5, first_word_len); + _c+= first_word_len; + memcpy(_c,b,strlen(b)); + _c+= strlen(b); + memcpy(_c, (char *)idx, _pkt->size - 5 - first_word_len); + } else { + memcpy(_c, (char *)_pkt->ptr+5, _pkt->size-5); + _c+=_pkt->size-5; + memcpy(_c,b,strlen(b)); + } + l_free(_pkt->size,_pkt->ptr); + _pkt->size = _pkt->size + strlen(b); + _pkt->ptr = _new_pkt.ptr; + free(b); + CurrentQuery.QueryLength = _pkt->size - 5; + CurrentQuery.QueryPointer = (unsigned char *)_pkt->ptr + 5; +} + + +bool MySQL_Session::handler_again___verify_ldap_user_variable() { + bool ret = false; + if (mybe->server_myds->myconn->options.ldap_user_variable_sent==false) { + ret = true; + } + if (mybe->server_myds->myconn->options.ldap_user_variable_value == NULL) { + ret = true; + } + if (ret==false) { + if (mybe->server_myds->myconn->options.ldap_user_variable_sent) { + if (client_myds && client_myds->myconn) { + if (client_myds->myconn->userinfo) { + if (client_myds->myconn->userinfo->fe_username) { + if (strcmp(mybe->server_myds->myconn->options.ldap_user_variable_value,client_myds->myconn->userinfo->fe_username)) { + ret = true; + free(mybe->server_myds->myconn->options.ldap_user_variable); + mybe->server_myds->myconn->options.ldap_user_variable = NULL; + free(mybe->server_myds->myconn->options.ldap_user_variable_value); + mybe->server_myds->myconn->options.ldap_user_variable_value = NULL; + mybe->server_myds->myconn->options.ldap_user_variable_sent = false; + } + } + } + } + } + } + if (ret) { + // we needs to set it to true + mybe->server_myds->myconn->options.ldap_user_variable_sent=true; + if (mysql_thread___ldap_user_variable) { + // we send ldap user variable query only if set + mybe->server_myds->myconn->options.ldap_user_variable=strdup(mysql_thread___ldap_user_variable); + switch(status) { // this switch can be replaced with a simple previous_status.push(status), but it is here for readibility + case PROCESSING_QUERY: + previous_status.push(PROCESSING_QUERY); + break; + case PROCESSING_STMT_PREPARE: + previous_status.push(PROCESSING_STMT_PREPARE); + break; + case PROCESSING_STMT_EXECUTE: + previous_status.push(PROCESSING_STMT_EXECUTE); + break; + default: + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } + NEXT_IMMEDIATE_NEW(SETTING_LDAP_USER_VARIABLE); + } + } + return false; +} + +void MySQL_Session::MySQL_Result_to_MySQL_wire(MYSQL *mysql, MySQL_ResultSet *MyRS, ProxySQL_Data_Stream *_myds) { + if (mysql == NULL) { + // error + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1, 2013, (char *)"HY000" ,(char *)"Lost connection to MySQL server during query"); + return; + } + if (MyRS) { + assert(MyRS->result); + bool transfer_started=MyRS->transfer_started; + bool resultset_completed=MyRS->get_resultset(client_myds->PSarrayOUT); + CurrentQuery.rows_sent = MyRS->num_rows; + bool com_field_list=client_myds->com_field_list; + assert(resultset_completed); // the resultset should always be completed if MySQL_Result_to_MySQL_wire is called + if (transfer_started==false) { // we have all the resultset when MySQL_Result_to_MySQL_wire was called + if (qpo && qpo->cache_ttl>0 && com_field_list==false) { // the resultset should be cached + if (mysql_errno(mysql)==0) { // no errors + if ( + (qpo->cache_empty_result==1) + || ( + (qpo->cache_empty_result == -1) + && + (thread->variables.query_cache_stores_empty_result || MyRS->num_rows) + ) + ) { + client_myds->resultset->copy_add(client_myds->PSarrayOUT,0,client_myds->PSarrayOUT->len); + client_myds->resultset_length=MyRS->resultset_size; + unsigned char *aa=client_myds->resultset2buffer(false); + while (client_myds->resultset->len) client_myds->resultset->remove_index(client_myds->resultset->len-1,NULL); + bool deprecate_eof_active = client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF; + GloQC->set( + client_myds->myconn->userinfo->hash , + (const unsigned char *)CurrentQuery.QueryPointer, + CurrentQuery.QueryLength, + aa , + client_myds->resultset_length , + thread->curtime/1000 , + thread->curtime/1000 , + thread->curtime/1000 + qpo->cache_ttl, + deprecate_eof_active + ); + l_free(client_myds->resultset_length,aa); + client_myds->resultset_length=0; + } + } + } + } + } else { // no result set + int myerrno=mysql_errno(mysql); + if (myerrno==0) { + unsigned int num_rows = mysql_affected_rows(mysql); + unsigned int nTrx=NumActiveTransactions(); + uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); + if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; + if (mysql->server_status & SERVER_MORE_RESULTS_EXIST) + setStatus |= SERVER_MORE_RESULTS_EXIST; + setStatus |= ( mysql->server_status & ~SERVER_STATUS_AUTOCOMMIT ); // get flags from server_status but ignore autocommit + setStatus = setStatus & ~SERVER_STATUS_CURSOR_EXISTS; // Do not send cursor #1128 + client_myds->myprot.generate_pkt_OK(true,NULL,NULL,client_myds->pkt_sid+1,num_rows,mysql->insert_id, setStatus, mysql->warning_count,mysql->info); + //client_myds->pkt_sid++; + } else { + // error + char sqlstate[10]; + sprintf(sqlstate,"%s",mysql_sqlstate(mysql)); + if (_myds && _myds->killed_at) { // see case #750 + if (_myds->kill_type == 0) { + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,1907,sqlstate,(char *)"Query execution was interrupted, query_timeout exceeded"); + } else { + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,1317,sqlstate,(char *)"Query execution was interrupted"); + } + } else { + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,mysql_errno(mysql),sqlstate,mysql_error(mysql)); + } + //client_myds->pkt_sid++; + } + } +} + +void MySQL_Session::MySQL_Stmt_Result_to_MySQL_wire(MYSQL_STMT *stmt, MySQL_Connection *myconn) { + MySQL_ResultSet *MyRS = NULL; + if (myconn) { + if (myconn->MyRS) { + MyRS = myconn->MyRS; + } + } +/* + MYSQL_RES *stmt_result=myconn->query.stmt_result; + if (stmt_result) { + MySQL_ResultSet *MyRS=new MySQL_ResultSet(); + MyRS->init(&client_myds->myprot, stmt_result, stmt->mysql, stmt); + MyRS->get_resultset(client_myds->PSarrayOUT); + CurrentQuery.rows_sent = MyRS->num_rows; + //removed bool resultset_completed=MyRS->get_resultset(client_myds->PSarrayOUT); + delete MyRS; +*/ + if (MyRS) { + assert(MyRS->result); + MyRS->init_with_stmt(myconn); + bool resultset_completed=MyRS->get_resultset(client_myds->PSarrayOUT); + CurrentQuery.rows_sent = MyRS->num_rows; + assert(resultset_completed); // the resultset should always be completed if MySQL_Result_to_MySQL_wire is called + } else { + MYSQL *mysql=stmt->mysql; + // no result set + int myerrno=mysql_stmt_errno(stmt); + if (myerrno==0) { + unsigned int num_rows = mysql_affected_rows(stmt->mysql); + unsigned int nTrx=NumActiveTransactions(); + uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); + if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; + if (mysql->server_status & SERVER_MORE_RESULTS_EXIST) + setStatus |= SERVER_MORE_RESULTS_EXIST; + setStatus |= ( mysql->server_status & ~SERVER_STATUS_AUTOCOMMIT ); // get flags from server_status but ignore autocommit + setStatus = setStatus & ~SERVER_STATUS_CURSOR_EXISTS; // Do not send cursor #1128 + client_myds->myprot.generate_pkt_OK(true,NULL,NULL,client_myds->pkt_sid+1,num_rows,mysql->insert_id, setStatus , mysql->warning_count,mysql->info); + client_myds->pkt_sid++; + } else { + // error + char sqlstate[10]; + sprintf(sqlstate,"%s",mysql_sqlstate(mysql)); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,mysql_errno(mysql),sqlstate,mysql_error(mysql)); + client_myds->pkt_sid++; + } + } +} + +bool MySQL_Session::handler_WCDSS_MYSQL_COM_QUERY_qpo(PtrSize_t *pkt, bool *lock_hostgroup, bool prepared) { +/* + lock_hostgroup: + If this variable is set to true, this session will get lock to a + specific hostgroup, and also have multiplexing disabled. + It means that parsing the query wasn't completely possible (mostly + a SET statement) and proxysql won't be able to set the same variable + in another connection. + This algorithm will be become obsolete once we implement session + tracking for MySQL 5.7+ +*/ + bool exit_after_SetParse = true; + unsigned char command_type=*((unsigned char *)pkt->ptr+sizeof(mysql_hdr)); + MySQL_Data_Stream *client_myds = NULL; + if (this->session_type==PROXYSQL_SESSION_MYSQL) { + client_myds = ((MySQL_Session *)this)->client_myds; + } + assert(client_myds != NULL); + + if (qpo->new_query) { + handler_WCD_SS_MCQ_qpo_QueryRewrite(pkt); + } + + if (pkt->size > (unsigned int) mysql_thread___max_allowed_packet) { + handler_WCD_SS_MCQ_qpo_LargePacket(pkt); + return true; + } + + if (qpo->OK_msg) { + handler_WCD_SS_MCQ_qpo_OK_msg(pkt); + return true; + } + + if (qpo->error_msg) { + handler_WCD_SS_MCQ_qpo_error_msg(pkt); + return true; + } + + if (prepared) { // for prepared statement we exit here + goto __exit_set_destination_hostgroup; + } + + // handle here #509, #815 and #816 + if (CurrentQuery.QueryParserArgs.digest_text) { + char *dig=CurrentQuery.QueryParserArgs.digest_text; + unsigned int nTrx=NumActiveTransactions(); + if ((locked_on_hostgroup == -1) && (strncasecmp(dig,(char *)"SET ",4)==0)) { + // this code is executed only if locked_on_hostgroup is not set yet + // if locked_on_hostgroup is set, we do not try to parse the SET statement +#ifdef DEBUG + { + string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); + proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Parsing SET command = %s\n", nqn.c_str()); + } +#endif + if (index(dig,';') && (index(dig,';') != dig + strlen(dig)-1)) { + string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); + proxy_warning( + "Unable to parse multi-statements command with SET statement from client" + " %s:%d: setting lock hostgroup. Command: %s\n", client_myds->addr.addr, + client_myds->addr.port, nqn.c_str() + ); + *lock_hostgroup = true; + return false; + } + int rc; + string nq=string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); + RE2::GlobalReplace(&nq,(char *)"^/\\*!\\d\\d\\d\\d\\d SET(.*)\\*/",(char *)"SET\\1"); + RE2::GlobalReplace(&nq,(char *)"(?U)/\\*.*\\*/",(char *)""); +/* + // we do not threat SET SQL_LOG_BIN as a special case + if (match_regexes && match_regexes[0]->match(dig)) { + int rc = handler_WCD_SS_MCQ_qpo_Parse_SQL_LOG_BIN(pkt, lock_hostgroup, nTrx, nq); + if (rc == 1) return false; + if (rc == 2) return true; + // if rc == 0 , continue as normal + } +*/ + if ( + ( + match_regexes && (match_regexes[1]->match(dig)) + ) + || + ( strncasecmp(dig,(char *)"SET NAMES", strlen((char *)"SET NAMES")) == 0) + || + ( strcasestr(dig,(char *)"autocommit")) + ) { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Parsing SET command %s\n", nq.c_str()); + proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Parsing SET command = %s\n", nq.c_str()); + SetParser parser(nq); + std::map> set = parser.parse1(); + // Flag to be set if any variable within the 'SET' statement fails to be tracked, + // due to being unknown or because it's an user defined variable. + bool failed_to_parse_var = false; + for(auto it = std::begin(set); it != std::end(set); ++it) { + std::string var = it->first; + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET variable %s\n", var.c_str()); + if (it->second.size() < 1 || it->second.size() > 2) { + // error not enough arguments + string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); + // PMC-10002: A query has failed to be parsed. This can be due a incorrect query or + // due to ProxySQL not being able to properly parse it. In case the query is correct a + // bug report should be filed including the offending query. + proxy_error2(10002, "Unable to parse query. If correct, report it as a bug: %s\n", nqn.c_str()); + proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Locking hostgroup for query %s\n", nqn.c_str()); + unable_to_parse_set_statement(lock_hostgroup); + return false; + } + auto values = std::begin(it->second); + if (var == "sql_mode") { + std::string value1 = *values; + if (strcasecmp(value1.c_str(),"NO_BACKSLASH_ESCAPE") != 0) { + // client is setting NO_BACKSLASH_ESCAPE in sql_mode + // Because we will reply with an OK packet without + // first setting sql_mode to the backend (this is + // by design) we need to set no_backslash_escapes + // in the client connection + if (client_myds && client_myds->myconn) { // some extra sanity check + client_myds->myconn->set_no_backslash_escapes(true); + } + } + if ( + ( strcasecmp(value1.c_str(),(char *)"CONCAT") == 0 ) + || + ( strcasecmp(value1.c_str(),(char *)"REPLACE") == 0 ) + || + ( strcasecmp(value1.c_str(),(char *)"IFNULL") == 0 ) + ) { + string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); + proxy_error2(10002, "Unable to parse query. If correct, report it as a bug: %s\n", nqn.c_str()); + proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Locking hostgroup for query %s\n", nqn.c_str()); + unable_to_parse_set_statement(lock_hostgroup); + return false; + } + std::size_t found_at = value1.find("@"); + if (found_at != std::string::npos) { + char *v1 = strdup(value1.c_str()); + char *v1t = v1; + proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Found @ in SQL_MODE . v1 = %s\n", v1); + char *v2 = NULL; + while (v1 && (v2 = strstr(v1,(const char *)"@"))) { + // we found a @ . Maybe we need to lock hostgroup + proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Found @ in SQL_MODE . v2 = %s\n", v2); + if (strncasecmp(v2,(const char *)"@@sql_mode",strlen((const char *)"@@sql_mode"))) { + unable_to_parse_set_statement(lock_hostgroup); + free(v1); + return false; + } else { + v2++; + } + if (strlen(v2) > 1) { + v1 = v2+1; + } + } + free(v1t); + } + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET SQL Mode value %s\n", value1.c_str()); + uint32_t sql_mode_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); + if (mysql_variables.client_get_hash(this, SQL_SQL_MODE) != sql_mode_int) { + if (!mysql_variables.client_set_value(this, SQL_SQL_MODE, value1.c_str())) { + return false; + } + proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection SQL Mode to %s\n", value1.c_str()); + } + } else if (mysql_variables_strings.find(var) != mysql_variables_strings.end()) { + std::string value1 = *values; + std::size_t found_at = value1.find("@"); + if (found_at != std::string::npos) { + unable_to_parse_set_statement(lock_hostgroup); + return false; + } + int idx = SQL_NAME_LAST_HIGH_WM; + for (int i = 0 ; i < SQL_NAME_LAST_HIGH_WM ; i++) { + if (mysql_tracked_variables[i].is_number == false && mysql_tracked_variables[i].is_bool == false) { + if (!strcasecmp(var.c_str(), mysql_tracked_variables[i].set_variable_name)) { + idx = mysql_tracked_variables[i].idx; + break; + } + } + } + if (idx != SQL_NAME_LAST_HIGH_WM) { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection %s to %s\n", var.c_str(), value1.c_str()); + uint32_t var_hash_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); + if (mysql_variables.client_get_hash(this, mysql_tracked_variables[idx].idx) != var_hash_int) { + if (!mysql_variables.client_set_value(this, mysql_tracked_variables[idx].idx, value1.c_str())) { + return false; + } + } + } + } else if (mysql_variables_boolean.find(var) != mysql_variables_boolean.end()) { + int idx = SQL_NAME_LAST_HIGH_WM; + for (int i = 0 ; i < SQL_NAME_LAST_HIGH_WM ; i++) { + if (mysql_tracked_variables[i].is_bool) { + if (!strcasecmp(var.c_str(), mysql_tracked_variables[i].set_variable_name)) { + idx = mysql_tracked_variables[i].idx; + break; + } + } + } + if (idx != SQL_NAME_LAST_HIGH_WM) { + if (mysql_variables.parse_variable_boolean(this,idx, *values, lock_hostgroup)==false) { + return false; + } + } + } else if (mysql_variables_numeric.find(var) != mysql_variables_numeric.end()) { + int idx = SQL_NAME_LAST_HIGH_WM; + for (int i = 0 ; i < SQL_NAME_LAST_HIGH_WM ; i++) { + if (mysql_tracked_variables[i].is_number) { + if (!strcasecmp(var.c_str(), mysql_tracked_variables[i].set_variable_name)) { + idx = mysql_tracked_variables[i].idx; + break; + } + } + } + if (idx != SQL_NAME_LAST_HIGH_WM) { + if (var == "query_cache_type") { + // note that query_cache_type variable can act both as boolean AND a number , but also accept "DEMAND" + // See https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_query_cache_type + std::string value1 = *values; + if (strcasecmp(value1.c_str(),"off")==0 || strcasecmp(value1.c_str(),"false")==0) { + value1 = "0"; + } else if (strcasecmp(value1.c_str(),"on")==0 || strcasecmp(value1.c_str(),"true")==0) { + value1 = "1"; + } else if (strcasecmp(value1.c_str(),"demand")==0 || strcasecmp(value1.c_str(),"true")==0) { + value1 = "2"; + } + if (mysql_variables.parse_variable_number(this,idx, value1, lock_hostgroup)==false) { + return false; + } + } else { + if (mysql_variables.parse_variable_number(this,idx, *values, lock_hostgroup)==false) { + return false; + } + } + } + } else if (var == "autocommit") { + std::string value1 = *values; + std::size_t found_at = value1.find("@"); + if (found_at != std::string::npos) { + unable_to_parse_set_statement(lock_hostgroup); + return false; + } + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET autocommit value %s\n", value1.c_str()); + int __tmp_autocommit = -1; + if ( + (strcasecmp(value1.c_str(),(char *)"0")==0) || + (strcasecmp(value1.c_str(),(char *)"false")==0) || + (strcasecmp(value1.c_str(),(char *)"off")==0) + ) { + __tmp_autocommit = 0; + } else { + if ( + (strcasecmp(value1.c_str(),(char *)"1")==0) || + (strcasecmp(value1.c_str(),(char *)"true")==0) || + (strcasecmp(value1.c_str(),(char *)"on")==0) + ) { + __tmp_autocommit = 1; + } + } + if (__tmp_autocommit >= 0 && autocommit_handled==false) { + int fd = __tmp_autocommit; + __sync_fetch_and_add(&MyHGM->status.autocommit_cnt, 1); + // we immediately process the number of transactions + unsigned int nTrx=NumActiveTransactions(); + if (fd==1 && autocommit==true) { + // nothing to do, return OK + } + if (fd==1 && autocommit==false) { + if (nTrx) { + // there is an active transaction, we need to forward it + // because this can potentially close the transaction + autocommit=true; + client_myds->myconn->set_autocommit(autocommit); + autocommit_on_hostgroup=FindOneActiveTransaction(); + exit_after_SetParse = false; + sending_set_autocommit=true; + } else { + // as there is no active transaction, we do no need to forward it + // just change internal state + autocommit=true; + client_myds->myconn->set_autocommit(autocommit); + } + } + + if (fd==0) { + autocommit=false; // we set it, no matter if already set or not + client_myds->myconn->set_autocommit(autocommit); + } + } else { + if (autocommit_handled==true) { + exit_after_SetParse = false; + } + } + } else if (var == "time_zone") { + std::string value1 = *values; + std::size_t found_at = value1.find("@"); + if (found_at != std::string::npos) { + unable_to_parse_set_statement(lock_hostgroup); + return false; + } + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET Time Zone value %s\n", value1.c_str()); + { + // reformat +1:23 to +01:23 + if (value1.length() == 5) { + if (value1[0]=='+' || value1[0]=='-') { + if (value1[2]==':') { + std::string s = std::string(value1,0,1); + s += "0"; + s += std::string(value1,1,4); + value1 = s; + } + } + } + } + uint32_t time_zone_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); + if (mysql_variables.client_get_hash(this, SQL_TIME_ZONE) != time_zone_int) { + if (!mysql_variables.client_set_value(this, SQL_TIME_ZONE, value1.c_str())) + return false; + proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection Time zone to %s\n", value1.c_str()); + } + } else if (var == "session_track_gtids") { + std::string value1 = *values; + if ((strcasecmp(value1.c_str(),"OWN_GTID")==0) || (strcasecmp(value1.c_str(),"OFF")==0) || (strcasecmp(value1.c_str(),"ALL_GTIDS")==0)) { + if (strcasecmp(value1.c_str(),"ALL_GTIDS")==0) { + // we convert session_track_gtids=ALL_GTIDS to session_track_gtids=OWN_GTID + std::string a = ""; + if (client_myds && client_myds->addr.addr) { + a = " . Client "; + a+= client_myds->addr.addr; + a+= ":" + std::to_string(client_myds->addr.port); + } + proxy_warning("SET session_track_gtids=ALL_GTIDS is not allowed. Switching to session_track_gtids=OWN_GTID%s\n", a.c_str()); + value1 = "OWN_GTID"; + } + proxy_debug(PROXY_DEBUG_MYSQL_COM, 7, "Processing SET session_track_gtids value %s\n", value1.c_str()); + uint32_t session_track_gtids_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); + if (client_myds->myconn->options.session_track_gtids_int != session_track_gtids_int) { + client_myds->myconn->options.session_track_gtids_int = session_track_gtids_int; + if (client_myds->myconn->options.session_track_gtids) { + free(client_myds->myconn->options.session_track_gtids); + } + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Changing connection session_track_gtids to %s\n", value1.c_str()); + client_myds->myconn->options.session_track_gtids=strdup(value1.c_str()); + } + } else { + unable_to_parse_set_statement(lock_hostgroup); + return false; + } + } else if ( (var == "character_set_results") || ( var == "collation_connection" ) || + (var == "character_set_connection") || (var == "character_set_client") || + (var == "character_set_database")) { + std::string value1 = *values; + int vl = strlen(value1.c_str()); + const char *v = value1.c_str(); + bool only_normal_chars = true; + for (int i=0; inr; + /* changing collation_connection the character_set_connection will be changed as well + * and vice versa + */ + if (var == "collation_connection") { + if (!mysql_variables.client_set_value(this, SQL_CHARACTER_SET_CONNECTION, ss.str().c_str())) + return false; + } + if (var == "character_set_connection") { + if (!mysql_variables.client_set_value(this, SQL_COLLATION_CONNECTION, ss.str().c_str())) + return false; + } + + /* this is explicit statement from client. we do not multiplex, therefor we must + * remember client's choice in the client's variable for future use in verifications, multiplexing etc. + */ + if (!mysql_variables.client_set_value(this, idx, ss.str().c_str())) + return false; + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Changing connection %s to %s\n", var.c_str(), value1.c_str()); + } + } + } else { + unable_to_parse_set_statement(lock_hostgroup); + return false; + } + } else if (var == "names") { + std::string value1 = *values++; + std::size_t found_at = value1.find("@"); + if (found_at != std::string::npos) { + unable_to_parse_set_statement(lock_hostgroup); + return false; + } + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET NAMES %s\n", value1.c_str()); + const MARIADB_CHARSET_INFO * c; + std::string value2; + if (values != std::end(it->second)) { + value2 = *values; + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET NAMES With COLLATE %s\n", value2.c_str()); + c = proxysql_find_charset_collate_names(value1.c_str(), value2.c_str()); + } else { + c = proxysql_find_charset_name(value1.c_str()); + } + if (!c) { + char *m = NULL; + char *errmsg = NULL; + if (value2.length()) { + m=(char *)"Unknown character set '%s' or collation '%s'"; + errmsg=(char *)malloc(value1.length() + value2.length() + strlen(m)); + sprintf(errmsg,m,value1.c_str(), value2.c_str()); + } else { + m=(char *)"Unknown character set: '%s'"; + errmsg=(char *)malloc(value1.length()+strlen(m)); + sprintf(errmsg,m,value1.c_str()); + } + client_myds->DSS=STATE_QUERY_SENT_NET; + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,1115,(char *)"42000",errmsg, true); + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; + free(errmsg); + return true; + } else { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection charset to %d\n", c->nr); + client_myds->myconn->set_charset(c->nr, NAMES); + } + } else if (var == "tx_isolation") { + std::string value1 = *values; + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET tx_isolation value %s\n", value1.c_str()); + auto pos = value1.find('-'); + if (pos != std::string::npos) + value1[pos] = ' '; + uint32_t isolation_level_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); + if (mysql_variables.client_get_hash(this, SQL_ISOLATION_LEVEL) != isolation_level_int) { + if (!mysql_variables.client_set_value(this, SQL_ISOLATION_LEVEL, value1.c_str())) + return false; + proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection TX ISOLATION to %s\n", value1.c_str()); + } + } else if (std::find(mysql_variables.ignore_vars.begin(), mysql_variables.ignore_vars.end(), var) != mysql_variables.ignore_vars.end()) { + // this is a variable we parse but ignore + // see MySQL_Variables::MySQL_Variables() for a list of ignored variables +#ifdef DEBUG + std::string value1 = *values; + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET %s value %s\n", var.c_str(), value1.c_str()); +#endif // DEBUG + } else { + // At this point the variable is unknown to us, or it's a user variable + // prefixed by '@', in both cases, we should fail to parse. We don't + // fail inmediately so we can anyway keep track of the other variables + // supplied within the 'SET' statement being parsed. + failed_to_parse_var = true; + } + } + + if (failed_to_parse_var) { + unable_to_parse_set_statement(lock_hostgroup); + return false; + } +/* + if (exit_after_SetParse) { + goto __exit_set_destination_hostgroup; + } +*/ + // parseSetCommand wasn't able to parse anything... + if (set.size() == 0) { + // try case listed in #1373 + // 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 + // this is not a complete solution. A right solution involves true parsing + int query_no_space_length = nq.length(); + char *query_no_space=(char *)malloc(query_no_space_length+1); + memcpy(query_no_space,nq.c_str(),query_no_space_length); + query_no_space[query_no_space_length]='\0'; + query_no_space_length=remove_spaces(query_no_space); + + string nq1 = string(query_no_space); + free(query_no_space); + RE2::GlobalReplace(&nq1,(char *)"SESSION.",(char *)""); + RE2::GlobalReplace(&nq1,(char *)"SESSION ",(char *)""); + RE2::GlobalReplace(&nq1,(char *)"session.",(char *)""); + RE2::GlobalReplace(&nq1,(char *)"session ",(char *)""); + //fprintf(stderr,"%s\n",nq1.c_str()); + re2::RE2::Options *opt2=new re2::RE2::Options(RE2::Quiet); + opt2->set_case_sensitive(false); + char *pattern=(char *)"^SET @@SQL_MODE *(?:|:)= *(?:'||\")(.*)(?:'||\") *, *@@sql_auto_is_null *(?:|:)= *(?:(?:\\w|\\d)*) *, @@wait_timeout *(?:|:)= *(?:\\d*)$"; + re2::RE2 *re=new RE2(pattern, *opt2); + string s1; + rc=RE2::FullMatch(nq1, *re, &s1); + delete re; + delete opt2; + if (rc) { + uint32_t sql_mode_int=SpookyHash::Hash32(s1.c_str(),s1.length(),10); + if (mysql_variables.client_get_hash(this, SQL_SQL_MODE) != sql_mode_int) { + if (!mysql_variables.client_set_value(this, SQL_SQL_MODE, s1.c_str())) + return false; + std::size_t found_at = s1.find("@"); + if (found_at != std::string::npos) { + char *v1 = strdup(s1.c_str()); + char *v2 = NULL; + while (v1 && (v2 = strstr(v1,(const char *)"@"))) { + // we found a @ . Maybe we need to lock hostgroup + if (strncasecmp(v2,(const char *)"@@sql_mode",strlen((const char *)"@@sql_mode"))) { +#ifdef DEBUG + string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); + proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Locking hostgroup for query %s\n", nqn.c_str()); +#endif + *lock_hostgroup = true; + } + if (strlen(v2) > 1) { + v1 = v2+1; + } + } + free(v1); + if (*lock_hostgroup) { + unable_to_parse_set_statement(lock_hostgroup); + return false; + } + } + } + } else { + if (memchr((const char *)CurrentQuery.QueryPointer, '@', CurrentQuery.QueryLength)) { + unable_to_parse_set_statement(lock_hostgroup); + return false; + } + int kq = 0; + kq = strncmp((const char *)CurrentQuery.QueryPointer, (const char *)"/*!40101 SET SQL_MODE=@OLD_SQL_MODE */" , CurrentQuery.QueryLength); + if (kq != 0) { + kq = strncmp((const char *)CurrentQuery.QueryPointer, (const char *)"/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */" , CurrentQuery.QueryLength); + if (kq != 0) { + string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); + proxy_error2(10002, "Unable to parse query. If correct, report it as a bug: %s\n", nqn.c_str()); + return false; + } + } + } + } + + if (exit_after_SetParse) { + if (command_type == _MYSQL_COM_QUERY) { + client_myds->DSS=STATE_QUERY_SENT_NET; + 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); + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; + RequestEnd_mysql(NULL); + l_free(pkt->size,pkt->ptr); + return true; + } + } + } else if (match_regexes && match_regexes[2]->match(dig)) { + SetParser parser(nq); + std::map> set = parser.parse2(); + for(auto it = std::begin(set); it != std::end(set); ++it) { + std::string var = it->first; + auto values = std::begin(it->second); + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET variable %s\n", var.c_str()); + if (var == "isolation level") { + std::string value1 = *values; + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET SESSION TRANSACTION ISOLATION LEVEL value %s\n", value1.c_str()); + uint32_t isolation_level_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); + if (mysql_variables.client_get_hash(this, SQL_ISOLATION_LEVEL) != isolation_level_int) { + if (!mysql_variables.client_set_value(this, SQL_ISOLATION_LEVEL, value1.c_str())) + return false; + proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection TRANSACTION ISOLATION LEVEL to %s\n", value1.c_str()); + } + } else if (var == "read") { + std::string value1 = *values; + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET SESSION TRANSACTION READ value %s\n", value1.c_str()); + uint32_t transaction_read_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); + if (mysql_variables.client_get_hash(this, SQL_TRANSACTION_READ) != transaction_read_int) { + if (!mysql_variables.client_set_value(this, SQL_TRANSACTION_READ, value1.c_str())) + return false; + proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection TRANSACTION READ to %s\n", value1.c_str()); + } + } else { + unable_to_parse_set_statement(lock_hostgroup); + return false; + } + } + if (exit_after_SetParse) { + if (command_type == _MYSQL_COM_QUERY) { + client_myds->DSS=STATE_QUERY_SENT_NET; + 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); + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; + RequestEnd_mysql(NULL); + l_free(pkt->size,pkt->ptr); + return true; + } + } + } else if (match_regexes && match_regexes[3]->match(dig)) { + SetParser parser(nq); + std::string charset = parser.parse_character_set(); + const MARIADB_CHARSET_INFO * c; + if (!charset.empty()) { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET CHARACTER SET %s\n", charset.c_str()); + c = proxysql_find_charset_name(charset.c_str()); + } else { + unable_to_parse_set_statement(lock_hostgroup); + return false; + } + if (!c) { + char *m = NULL; + char *errmsg = NULL; + m=(char *)"Unknown character set: '%s'"; + errmsg=(char *)malloc(charset.length()+strlen(m)); + sprintf(errmsg,m,charset.c_str()); + client_myds->DSS=STATE_QUERY_SENT_NET; + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,1115,(char *)"42000",errmsg, true); + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; + free(errmsg); + return true; + } else { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection charset to %d\n", c->nr); + client_myds->myconn->set_charset(c->nr, CHARSET); + } + if (exit_after_SetParse) { + if (command_type == _MYSQL_COM_QUERY) { + client_myds->DSS=STATE_QUERY_SENT_NET; + 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); + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; + RequestEnd_mysql(NULL); + l_free(pkt->size,pkt->ptr); + return true; + } + } + } else { + unable_to_parse_set_statement(lock_hostgroup); + return false; + } + } + } + + if (mirror==true) { // for mirror session we exit here + current_hostgroup=qpo->destination_hostgroup; + return false; + } + + // handle case #1797 + // handle case #2564 + if ((pkt->size==SELECT_CONNECTION_ID_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_CONNECTION_ID,(char *)pkt->ptr+5,pkt->size-5)==0)) { + char buf[32]; + char buf2[32]; + sprintf(buf,"%u",thread_session_id); + int l0=strlen("CONNECTION_ID()"); + memcpy(buf2,(char *)pkt->ptr+5+SELECT_CONNECTION_ID_LEN-l0,l0); + buf2[l0]=0; + unsigned int nTrx=NumActiveTransactions(); + uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); + if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; + MySQL_Data_Stream *myds=client_myds; + MySQL_Protocol *myprot=&client_myds->myprot; + myds->DSS=STATE_QUERY_SENT_DS; + int sid=1; + myprot->generate_pkt_column_count(true,NULL,NULL,sid,1); sid++; + myprot->generate_pkt_field(true,NULL,NULL,sid,(char *)"",(char *)"",(char *)"",buf2,(char *)"",63,31,MYSQL_TYPE_LONGLONG,161,0,false,0,NULL); sid++; + myds->DSS=STATE_COLUMN_DEFINITION; + + bool deprecate_eof_active = myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF; + if (!deprecate_eof_active) { + myprot->generate_pkt_EOF(true,NULL,NULL,sid,0, setStatus); sid++; + } + + char **p=(char **)malloc(sizeof(char*)*1); + unsigned long *l=(unsigned long *)malloc(sizeof(unsigned long *)*1); + l[0]=strlen(buf); + p[0]=buf; + myprot->generate_pkt_row(true,NULL,NULL,sid,1,l,p); sid++; + myds->DSS=STATE_ROW; + + if (deprecate_eof_active) { + myprot->generate_pkt_OK(true,NULL,NULL,sid,0,0,setStatus,0,NULL,true); sid++; + } else { + myprot->generate_pkt_EOF(true,NULL,NULL,sid,0, setStatus); sid++; + } + myds->DSS=STATE_SLEEP; + RequestEnd_mysql(NULL); + l_free(pkt->size,pkt->ptr); + free(p); + free(l); + return true; + } + + // handle case #1421 , about LAST_INSERT_ID + if (CurrentQuery.QueryParserArgs.digest_text) { + char *dig=CurrentQuery.QueryParserArgs.digest_text; + if (strcasestr(dig,"LAST_INSERT_ID") || strcasestr(dig,"@@IDENTITY")) { + // we need to try to execute it where the last write was successful + if (last_HG_affected_rows >= 0) { + MySQL_Backend * _mybe = NULL; + _mybe = find_mysql_backend(last_HG_affected_rows); + if (_mybe) { + if (_mybe->server_myds) { + if (_mybe->server_myds->myconn) { + if (_mybe->server_myds->myconn->mysql) { // we have an established connection + // this seems to be the right backend + qpo->destination_hostgroup = last_HG_affected_rows; + current_hostgroup = qpo->destination_hostgroup; + return false; // execute it on backend! + } + } + } + } + } + // if we reached here, we don't know the right backend + // we try to determine if it is a simple "SELECT LAST_INSERT_ID()" or "SELECT @@IDENTITY" and we return mysql->last_insert_id + + //handle 2564 + if ( + (pkt->size==SELECT_LAST_INSERT_ID_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_LAST_INSERT_ID,(char *)pkt->ptr+5,pkt->size-5)==0) + || + (pkt->size==SELECT_LAST_INSERT_ID_LIMIT1_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_LAST_INSERT_ID_LIMIT1,(char *)pkt->ptr+5,pkt->size-5)==0) + || + (pkt->size==SELECT_VARIABLE_IDENTITY_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_VARIABLE_IDENTITY,(char *)pkt->ptr+5,pkt->size-5)==0) + || + (pkt->size==SELECT_VARIABLE_IDENTITY_LIMIT1_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_VARIABLE_IDENTITY_LIMIT1,(char *)pkt->ptr+5,pkt->size-5)==0) + ) { + char buf[32]; + sprintf(buf,"%llu",last_insert_id); + char buf2[32]; + int l0=0; + if (strcasestr(dig,"LAST_INSERT_ID")){ + l0=strlen("LAST_INSERT_ID()"); + memcpy(buf2,(char *)pkt->ptr+5+SELECT_LAST_INSERT_ID_LEN-l0,l0); + }else if(strcasestr(dig,"@@IDENTITY")){ + l0=strlen("@@IDENTITY"); + memcpy(buf2,(char *)pkt->ptr+5+SELECT_VARIABLE_IDENTITY_LEN-l0,l0); + } + buf2[l0]=0; + unsigned int nTrx=NumActiveTransactions(); + uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); + if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; + MySQL_Data_Stream *myds=client_myds; + MySQL_Protocol *myprot=&client_myds->myprot; + myds->DSS=STATE_QUERY_SENT_DS; + int sid=1; + myprot->generate_pkt_column_count(true,NULL,NULL,sid,1); sid++; + myprot->generate_pkt_field(true,NULL,NULL,sid,(char *)"",(char *)"",(char *)"",buf2,(char *)"",63,31,MYSQL_TYPE_LONGLONG,161,0,false,0,NULL); sid++; + myds->DSS=STATE_COLUMN_DEFINITION; + + bool deprecate_eof_active = myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF; + if (!deprecate_eof_active) { + myprot->generate_pkt_EOF(true,NULL,NULL,sid,0, setStatus); sid++; + } + char **p=(char **)malloc(sizeof(char*)*1); + unsigned long *l=(unsigned long *)malloc(sizeof(unsigned long *)*1); + l[0]=strlen(buf); + p[0]=buf; + myprot->generate_pkt_row(true,NULL,NULL,sid,1,l,p); sid++; + myds->DSS=STATE_ROW; + if (deprecate_eof_active) { + myprot->generate_pkt_OK(true,NULL,NULL,sid,0,0,setStatus,0,NULL,true); sid++; + } else { + myprot->generate_pkt_EOF(true,NULL,NULL,sid,0, setStatus); sid++; + } + myds->DSS=STATE_SLEEP; + RequestEnd_mysql(NULL); + l_free(pkt->size,pkt->ptr); + free(p); + free(l); + return true; + } + + // if we reached here, we don't know the right backend and we cannot answer the query directly + // We continue the normal way + + // as a precaution, we reset cache_ttl + qpo->cache_ttl = 0; + } + } + + // handle command KILL #860 + if (prepared == false) { + if (handle_command_query_kill(pkt)) { + return true; + } + } + if (qpo->cache_ttl>0) { + bool deprecate_eof_active = client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF; + uint32_t resbuf=0; + unsigned char *aa=GloQC->get( + client_myds->myconn->userinfo->hash, + (const unsigned char *)CurrentQuery.QueryPointer , + CurrentQuery.QueryLength , + &resbuf , + thread->curtime/1000 , + qpo->cache_ttl, + deprecate_eof_active + ); + if (aa) { + client_myds->buffer2resultset(aa,resbuf); + free(aa); + client_myds->PSarrayOUT->copy_add(client_myds->resultset,0,client_myds->resultset->len); + while (client_myds->resultset->len) client_myds->resultset->remove_index(client_myds->resultset->len-1,NULL); + if (transaction_persistent_hostgroup == -1) { + // not active, we can change it + current_hostgroup=-1; + } + RequestEnd_mysql(NULL); + l_free(pkt->size,pkt->ptr); + return true; + } + } + +__exit_set_destination_hostgroup: + + if ( qpo->next_query_flagIN >= 0 ) { + next_query_flagIN=qpo->next_query_flagIN; + } + if ( qpo->destination_hostgroup >= 0 ) { + if (transaction_persistent_hostgroup == -1) { + current_hostgroup=qpo->destination_hostgroup; + } + } + + if (mysql_thread___set_query_lock_on_hostgroup == 1) { // algorithm introduced in 2.0.6 + if (locked_on_hostgroup >= 0) { + if (current_hostgroup != locked_on_hostgroup) { + client_myds->DSS=STATE_QUERY_SENT_NET; + char buf[140]; + sprintf(buf,"ProxySQL Error: connection is locked to hostgroup %d but trying to reach hostgroup %d", locked_on_hostgroup, current_hostgroup); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,9006,(char *)"Y0000",buf); + thread->status_variables.stvar[st_var_hostgroup_locked_queries]++; + RequestEnd_mysql(NULL); + l_free(pkt->size,pkt->ptr); + return true; + } + } + } + return false; } + +void MySQL_Session::handler_WCDSS_MYSQL_COM_STATISTICS(PtrSize_t *pkt) { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_STATISTICS packet\n"); + l_free(pkt->size,pkt->ptr); + client_myds->setDSS_STATE_QUERY_SENT_NET(); + client_myds->myprot.generate_statistics_response(true,NULL,NULL); + client_myds->DSS=STATE_SLEEP; +} + +// this function as inline in handler_WCDSS_MYSQL_COM_QUERY_qpo +void MySQL_Session::handler_WCD_SS_MCQ_qpo_LargePacket(PtrSize_t *pkt) { + // ER_NET_PACKET_TOO_LARGE + client_myds->DSS=STATE_QUERY_SENT_NET; + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,1153,(char *)"08S01",(char *)"Got a packet bigger than 'max_allowed_packet' bytes", true); + RequestEnd_mysql(NULL); + l_free(pkt->size,pkt->ptr); +} + +// this should execute most of the commands executed when a request is finalized +// this should become the place to hook other functions +void MySQL_Session::RequestEnd_mysql(ProxySQL_Data_Stream *pds) { + MySQL_Data_Stream *myds = (MySQL_Data_Stream *)pds; + // check if multiplexing needs to be disabled + char *qdt=CurrentQuery.get_digest_text(); + if (qdt && myds && myds->myconn) { + myds->myconn->ProcessQueryAndSetStatusFlags(qdt); + } + + switch (status) { + case PROCESSING_STMT_EXECUTE: + case PROCESSING_STMT_PREPARE: + // if a prepared statement is executed, LogQuery was already called + break; + default: + LogQuery(myds); + break; + } + + GloQPro->delete_QP_out(qpo); + // if there is an associated myds, clean its status + if (myds) { + // if there is a mysql connection, clean its status + if (myds->myconn) { + myds->myconn->async_free_result(); + myds->myconn->compute_unknown_transaction_status(); + } + myds->free_mysql_real_query(); + } + // reset status of the session + status=WAITING_CLIENT_DATA; + if (client_myds) { + // reset status of client data stream + client_myds->DSS=STATE_SLEEP; + // finalize the query + CurrentQuery.end(); + } + started_sending_data_to_client=false; +} + +void MySQL_Session::writeout() { + int tps = 10; // throttling per second , by default every 100ms + int total_written = 0; + unsigned long long last_sent_=0; + bool disable_throttle = mysql_thread___throttle_max_bytes_per_second_to_client == 0; + int mwpl = mysql_thread___throttle_max_bytes_per_second_to_client; // max writes per call + mwpl = mwpl/tps; + if (session_type!=PROXYSQL_SESSION_MYSQL) { + disable_throttle = true; + } + if (client_myds) client_myds->array2buffer_full(); + if (mybe && mybe->server_myds && mybe->server_myds->myds_type==MYDS_BACKEND) { + if (session_type==PROXYSQL_SESSION_MYSQL) { + if (mybe->server_myds->net_failure==false) { + if (mybe->server_myds->poll_fds_idx>-1) { // NOTE: attempt to force writes + mybe->server_myds->array2buffer_full(); + } + } + } else { + mybe->server_myds->array2buffer_full(); + } + } + if (client_myds && thread->curtime >= client_myds->pause_until) { + if (mirror==false) { + bool runloop=false; + if (client_myds->mypolls) { + last_sent_ = client_myds->mypolls->last_sent[client_myds->poll_fds_idx]; + } + int retbytes=client_myds->write_to_net_poll(); + total_written+=retbytes; + if (retbytes==QUEUE_T_DEFAULT_SIZE) { // optimization to solve memory bloat + runloop=true; + } + while (runloop && (disable_throttle || total_written < mwpl)) { + runloop=false; // the default + client_myds->array2buffer_full(); + struct pollfd fds; + fds.fd=client_myds->fd; + fds.events=POLLOUT; + fds.revents=0; + int retpoll=poll(&fds, 1, 0); + if (retpoll>0) { + if (fds.revents==POLLOUT) { + retbytes=client_myds->write_to_net_poll(); + total_written+=retbytes; + if (retbytes==QUEUE_T_DEFAULT_SIZE) { // optimization to solve memory bloat + runloop=true; + } + } + } + } + } + } + + // flow control + if (!disable_throttle && total_written > 0) { + if (total_written > mwpl) { + unsigned long long add_ = 1000000/tps + 1000000/tps*((unsigned long long)total_written - (unsigned long long)mwpl)/mwpl; + pause_until = thread->curtime + add_; + client_myds->remove_pollout(); + client_myds->pause_until = thread->curtime + add_; + } else { + if (total_written >= QUEUE_T_DEFAULT_SIZE) { + unsigned long long time_diff = thread->curtime - last_sent_; + if (time_diff == 0) { // sending data really too fast! + unsigned long long add_ = 1000000/tps + 1000000/tps*((unsigned long long)total_written - (unsigned long long)mwpl)/mwpl; + pause_until = thread->curtime + add_; + client_myds->remove_pollout(); + client_myds->pause_until = thread->curtime + add_; + } else { + float current_Bps = (float)total_written*1000*1000/time_diff; + if (current_Bps > mysql_thread___throttle_max_bytes_per_second_to_client) { + unsigned long long add_ = 1000000/tps; + pause_until = thread->curtime + add_; + assert(pause_until > thread->curtime); + client_myds->remove_pollout(); + client_myds->pause_until = thread->curtime + add_; + } + } + } + } + } + + if (mybe) { + if (mybe->server_myds) mybe->server_myds->write_to_net_poll(); + } + proxy_debug(PROXY_DEBUG_NET,1,"Thread=%p, Session=%p -- Writeout Session %p\n" , this->thread, this, this); +} + +// FIXME: This function is currently disabled . See #469 +bool MySQL_Session::handler_CommitRollback(PtrSize_t *pkt) { + char c=((char *)pkt->ptr)[5]; + bool ret=false; + if (c=='c' || c=='C') { + if (strncasecmp((char *)"commit",(char *)pkt->ptr+5,6)==0) { + __sync_fetch_and_add(&MyHGM->status.commit_cnt, 1); + ret=true; + } + } else { + if (c=='r' || c=='R') { + if ( strncasecmp((char *)"rollback",(char *)pkt->ptr+5,8)==0 ) { + __sync_fetch_and_add(&MyHGM->status.rollback_cnt, 1); + ret=true; + } + } + } + + if (ret==false) { + return false; // quick exit + } + unsigned int nTrx=NumActiveTransactions(); + if (nTrx) { + // there is an active transaction, we must forward the request + return false; + } else { + // there is no active transaction, we will just reply OK + client_myds->DSS=STATE_QUERY_SENT_NET; + uint16_t setStatus = 0; + if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; + client_myds->myprot.generate_pkt_OK(true,NULL,NULL,1,0,0,setStatus,0,NULL); + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; + if (mirror==false) { + RequestEnd_mysql(NULL); + } + l_free(pkt->size,pkt->ptr); + if (c=='c' || c=='C') { + __sync_fetch_and_add(&MyHGM->status.commit_cnt_filtered, 1); + } else { + __sync_fetch_and_add(&MyHGM->status.rollback_cnt_filtered, 1); + } + return true; + } + return false; +} + + +bool MySQL_Session::handler_SetAutocommit(PtrSize_t *pkt) { + autocommit_handled=false; + sending_set_autocommit=false; + size_t sal=strlen("set autocommit"); + char * _ptr = (char *)pkt->ptr; +#ifdef DEBUG + string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); + proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Parsing SET command = %s\n", nqn.c_str()); +#endif + if ( pkt->size >= 7+sal) { + if (strncasecmp((char *)"SET @@session.autocommit",(char *)pkt->ptr+5,strlen((char *)"SET @@session.autocommit"))==0) { + memmove(_ptr+9, _ptr+19, pkt->size - 19); + memset(_ptr+pkt->size-10,' ',10); + } + if (strncasecmp((char *)"set autocommit",(char *)pkt->ptr+5,sal)==0) { + void *p = NULL; + // make a copy + PtrSize_t _new_pkt; + _new_pkt.size = pkt->size; + _new_pkt.ptr = malloc(_new_pkt.size); + memcpy(_new_pkt.ptr, pkt->ptr, _new_pkt.size); + _ptr = (char *)_new_pkt.ptr; + for (int i=5+sal; i < (int)_new_pkt.size; i++) { + *((char *)_new_pkt.ptr+i) = tolower(*((char *)_new_pkt.ptr+i)); + } + p = memmem(_ptr+5+sal, pkt->size-5-sal, (void *)"false", 5); + if (p) { + memcpy(p,(void *)"0 ",5); + } + p = memmem(_ptr+5+sal, pkt->size-5-sal, (void *)"true", 4); + if (p) { + memcpy(p,(void *)"1 ",4); + } + p = memmem(_ptr+5+sal, pkt->size-5-sal, (void *)"off", 3); + if (p) { + memcpy(p,(void *)"0 ",3); + } + p = memmem(_ptr+5+sal, pkt->size-5-sal, (void *)"on", 2); + if (p) { + memcpy(p,(void *)"1 ",2); + } + unsigned int i; + bool eq=false; + int fd=-1; // first digit + for (i=5+sal;i<_new_pkt.size;i++) { + char c=((char *)_new_pkt.ptr)[i]; + if (c!='0' && c!='1' && c!=' ' && c!='=' && c!='/') { + free(_new_pkt.ptr); + return false; // found a not valid char + } + if (eq==false) { + if (c!=' ' && c!='=') { + free(_new_pkt.ptr); + return false; // found a not valid char + } + if (c=='=') eq=true; + } else { + if (c!='0' && c!='1' && c!=' ' && c!='/') { + free(_new_pkt.ptr); + return false; // found a not valid char + } + if (fd==-1) { + if (c=='0' || c=='1') { // found first digit + if (c=='0') + fd=0; + else + fd=1; + } + } else { + if (c=='0' || c=='1') { // found second digit + free(_new_pkt.ptr); + return false; + } else { + if (c=='/' || c==' ') { + break; + } + } + } + } + } + if (fd >= 0) { // we can set autocommit + autocommit_handled=true; +#ifdef DEBUG + proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Setting autocommit to = %d\n", fd); +#endif + __sync_fetch_and_add(&MyHGM->status.autocommit_cnt, 1); + // we immediately process the number of transactions + unsigned int nTrx=NumActiveTransactions(); + if (fd==1 && autocommit==true) { + // nothing to do, return OK + goto __ret_autocommit_OK; + } + if (fd==1 && autocommit==false) { + if (nTrx) { + // there is an active transaction, we need to forward it + // because this can potentially close the transaction + autocommit=true; + client_myds->myconn->set_autocommit(autocommit); + autocommit_on_hostgroup=FindOneActiveTransaction(); + free(_new_pkt.ptr); + sending_set_autocommit=true; + return false; + } else { + // as there is no active transaction, we do no need to forward it + // just change internal state + autocommit=true; + client_myds->myconn->set_autocommit(autocommit); + goto __ret_autocommit_OK; + } + } + + if (fd==0) { + autocommit=false; // we set it, no matter if already set or not + client_myds->myconn->set_autocommit(autocommit); + // it turned out I was wrong + // set autocommit=0 has no effect if there is an acrive transaction + // therefore, we never forward set autocommit = 0 + goto __ret_autocommit_OK; + } +__ret_autocommit_OK: + client_myds->DSS=STATE_QUERY_SENT_NET; + 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); + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; + if (mirror==false) { + RequestEnd_mysql(NULL); + } + l_free(pkt->size,pkt->ptr); + __sync_fetch_and_add(&MyHGM->status.autocommit_cnt_filtered, 1); + free(_new_pkt.ptr); + return true; + } + free(_new_pkt.ptr); + } + } + return false; +} + +// this function was introduced due to isseu #718 +// some application (like the one written in Perl) do not use COM_INIT_DB , but COM_QUERY with USE dbname +void MySQL_Session::handler_WCDSS_MYSQL_COM_QUERY_USE_DB(PtrSize_t *pkt) { + gtid_hid=-1; + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_QUERY with USE dbname\n"); + if (session_type == PROXYSQL_SESSION_MYSQL) { + __sync_fetch_and_add(&MyHGM->status.frontend_use_db, 1); + string nq=string((char *)pkt->ptr+sizeof(mysql_hdr)+1,pkt->size-sizeof(mysql_hdr)-1); + RE2::GlobalReplace(&nq,(char *)"(?U)/\\*.*\\*/",(char *)" "); + char *sn_tmp = (char *)nq.c_str(); + while (sn_tmp < ( nq.c_str() + nq.length() - 4 ) && *sn_tmp == ' ') + sn_tmp++; + //char *schemaname=strdup(nq.c_str()+4); + char *schemaname=strdup(sn_tmp+3); + char *schemanameptr=trim_spaces_and_quotes_in_place(schemaname); + // handle cases like "USE `schemaname` + if(schemanameptr[0]=='`' && schemanameptr[strlen(schemanameptr)-1]=='`') { + schemanameptr[strlen(schemanameptr)-1]='\0'; + schemanameptr++; + } + client_myds->myconn->userinfo->set_schemaname(schemanameptr,strlen(schemanameptr)); + free(schemaname); + if (mirror==false) { + RequestEnd_mysql(NULL); + } + l_free(pkt->size,pkt->ptr); + client_myds->setDSS_STATE_QUERY_SENT_NET(); + 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); + GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_INITDB, this, NULL); + client_myds->DSS=STATE_SLEEP; + } else { + l_free(pkt->size,pkt->ptr); + client_myds->setDSS_STATE_QUERY_SENT_NET(); + 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); + client_myds->DSS=STATE_SLEEP; + } +} + + +// this function as inline in handler_WCDSS_MYSQL_COM_QUERY_qpo +void MySQL_Session::handler_WCD_SS_MCQ_qpo_QueryRewrite(PtrSize_t *pkt) { + // the query was rewritten + l_free(pkt->size,pkt->ptr); // free old pkt + // allocate new pkt + timespec begint; + if (thread->variables.stats_time_query_processor) { + clock_gettime(CLOCK_THREAD_CPUTIME_ID,&begint); + } + pkt->size=sizeof(mysql_hdr)+1+qpo->new_query->length(); + pkt->ptr=l_alloc(pkt->size); + mysql_hdr hdr; + hdr.pkt_id=0; + hdr.pkt_length=pkt->size-sizeof(mysql_hdr); + memcpy((unsigned char *)pkt->ptr, &hdr, sizeof(mysql_hdr)); // copy header + unsigned char *c=(unsigned char *)pkt->ptr+sizeof(mysql_hdr); + *c=(unsigned char)_MYSQL_COM_QUERY; // set command type + memcpy((unsigned char *)pkt->ptr+sizeof(mysql_hdr)+1,qpo->new_query->data(),qpo->new_query->length()); // copy query + CurrentQuery.query_parser_free(); + CurrentQuery.begin((unsigned char *)pkt->ptr,pkt->size,true); + delete qpo->new_query; + timespec endt; + if (thread->variables.stats_time_query_processor) { + clock_gettime(CLOCK_THREAD_CPUTIME_ID,&endt); + thread->status_variables.stvar[st_var_query_processor_time] = thread->status_variables.stvar[st_var_query_processor_time] + + (endt.tv_sec*1000000000+endt.tv_nsec) - + (begint.tv_sec*1000000000+begint.tv_nsec); + } +} + +// this function as inline in handler_WCDSS_MYSQL_COM_QUERY_qpo +void MySQL_Session::handler_WCD_SS_MCQ_qpo_OK_msg(PtrSize_t *pkt) { + gtid_hid = -1; + client_myds->DSS=STATE_QUERY_SENT_NET; + 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,client_myds->pkt_sid+1,0,0,setStatus,0,qpo->OK_msg); + RequestEnd_mysql(NULL); + l_free(pkt->size,pkt->ptr); +} + +// this function as inline in handler_WCDSS_MYSQL_COM_QUERY_qpo +void MySQL_Session::handler_WCD_SS_MCQ_qpo_error_msg(PtrSize_t *pkt) { + client_myds->DSS=STATE_QUERY_SENT_NET; + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,1148,(char *)"42000",qpo->error_msg); + RequestEnd_mysql(NULL); + l_free(pkt->size,pkt->ptr); +} + +void MySQL_Session::handler_WCDSS_MYSQL_COM_INIT_DB(PtrSize_t *pkt) { + gtid_hid=-1; + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_INIT_DB packet\n"); + if (session_type == PROXYSQL_SESSION_MYSQL) { + __sync_fetch_and_add(&MyHGM->status.frontend_init_db, 1); + client_myds->myconn->userinfo->set_schemaname((char *)pkt->ptr+sizeof(mysql_hdr)+1,pkt->size-sizeof(mysql_hdr)-1); + l_free(pkt->size,pkt->ptr); + client_myds->setDSS_STATE_QUERY_SENT_NET(); + 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); + GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_INITDB, this, NULL); + client_myds->DSS=STATE_SLEEP; + } else { + l_free(pkt->size,pkt->ptr); + client_myds->setDSS_STATE_QUERY_SENT_NET(); + 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); + client_myds->DSS=STATE_SLEEP; + } +} + +void MySQL_Session::handler___status_CHANGING_USER_CLIENT___STATE_CLIENT_HANDSHAKE(PtrSize_t *pkt, bool *wrong_pass) { + // FIXME: no support for SSL yet + if ( + client_myds->myprot.process_pkt_auth_swich_response((unsigned char *)pkt->ptr,pkt->size)==true + ) { + l_free(pkt->size,pkt->ptr); + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p . Successful connection\n", this, client_myds); + client_myds->myprot.generate_pkt_OK(true,NULL,NULL,2,0,0,0,0,NULL); + GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_CHANGE_USER_OK, this, NULL); + status=WAITING_CLIENT_DATA; + client_myds->DSS=STATE_SLEEP; + } else { + l_free(pkt->size,pkt->ptr); + *wrong_pass=true; + // FIXME: this should become close connection + client_myds->setDSS_STATE_QUERY_SENT_NET(); + char *client_addr=NULL; + if (client_myds->client_addr) { + char buf[512]; + switch (client_myds->client_addr->sa_family) { + case AF_INET: { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)client_myds->client_addr; + if (ipv4->sin_port) { + inet_ntop(client_myds->client_addr->sa_family, &ipv4->sin_addr, buf, INET_ADDRSTRLEN); + client_addr = strdup(buf); + } else { + client_addr = strdup((char *)"localhost"); + } + break; + } + case AF_INET6: { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)client_myds->client_addr; + inet_ntop(client_myds->client_addr->sa_family, &ipv6->sin6_addr, buf, INET6_ADDRSTRLEN); + client_addr = strdup(buf); + break; + } + default: + client_addr = strdup((char *)"localhost"); + break; + } + } else { + client_addr = strdup((char *)""); + } + char *_s=(char *)malloc(strlen(client_myds->myconn->userinfo->username)+100+strlen(client_addr)); + sprintf(_s,"ProxySQL Error: Access denied for user '%s'@'%s' (using password: %s)", client_myds->myconn->userinfo->username, client_addr, (client_myds->myconn->userinfo->password ? "YES" : "NO")); + proxy_error("ProxySQL Error: Access denied for user '%s'@'%s' (using password: %s)", client_myds->myconn->userinfo->username, client_addr, (client_myds->myconn->userinfo->password ? "YES" : "NO")); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,2,1045,(char *)"28000", _s, true); +#ifdef DEBUG + if (client_myds->myconn->userinfo->password) { + char *tmp_pass=strdup(client_myds->myconn->userinfo->password); + int lpass = strlen(tmp_pass); + for (int i=2; imyconn->userinfo->username, client_addr, tmp_pass); + free(tmp_pass); + } else { + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p . Wrong credentials for frontend: %s:%s . No password. Disconnecting\n", this, client_myds, client_myds->myconn->userinfo->username, client_addr); + } +#endif //DEBUG + GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_CHANGE_USER_ERR, this, NULL); + free(_s); + __sync_fetch_and_add(&MyHGM->status.access_denied_wrong_password, 1); + } +} + +void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(PtrSize_t *pkt, bool *wrong_pass) { + bool is_encrypted = client_myds->encrypted; + bool handshake_response_return = client_myds->myprot.process_pkt_handshake_response((unsigned char *)pkt->ptr,pkt->size); + bool handshake_err = true; + + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p , handshake_response=%d , switching_auth_stage=%d , is_encrypted=%d , client_encrypted=%d\n", this, client_myds, handshake_response_return, client_myds->switching_auth_stage, is_encrypted, client_myds->encrypted); + if ( + (handshake_response_return == false) && (client_myds->switching_auth_stage == 1) + ) { + l_free(pkt->size,pkt->ptr); + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p . Returning\n", this, client_myds); + return; + } + + if ( + (is_encrypted == false) && // the connection was encrypted + (handshake_response_return == false) && // the authentication didn't complete + (client_myds->encrypted == true) // client is asking for encryption + ) { + // use SSL + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p . SSL_INIT\n", this, client_myds); + client_myds->DSS=STATE_SSL_INIT; + client_myds->rbio_ssl = BIO_new(BIO_s_mem()); + client_myds->wbio_ssl = BIO_new(BIO_s_mem()); + client_myds->ssl = GloVars.get_SSL_ctx(); + SSL_set_fd(client_myds->ssl, client_myds->fd); + SSL_set_accept_state(client_myds->ssl); + SSL_set_bio(client_myds->ssl, client_myds->rbio_ssl, client_myds->wbio_ssl); + l_free(pkt->size,pkt->ptr); + return; + } + + if ( + //(client_myds->myprot.process_pkt_handshake_response((unsigned char *)pkt->ptr,pkt->size)==true) + (handshake_response_return == true) + && + ( +#if defined(TEST_AURORA) || defined(TEST_GALERA) || defined(TEST_GROUPREP) + (default_hostgroup<0 && ( session_type == PROXYSQL_SESSION_ADMIN || session_type == PROXYSQL_SESSION_STATS || session_type == PROXYSQL_SESSION_SQLITE) ) +#else + (default_hostgroup<0 && ( session_type == PROXYSQL_SESSION_ADMIN || session_type == PROXYSQL_SESSION_STATS) ) +#endif // TEST_AURORA || TEST_GALERA || TEST_GROUPREP + || + (default_hostgroup == 0 && session_type == PROXYSQL_SESSION_CLICKHOUSE) + || + //(default_hostgroup>=0 && session_type == PROXYSQL_SESSION_MYSQL) + (default_hostgroup>=0 && ( session_type == PROXYSQL_SESSION_MYSQL || session_type == PROXYSQL_SESSION_SQLITE ) ) + || + ( + client_myds->encrypted==false + && + strncmp(client_myds->myconn->userinfo->username,mysql_thread___monitor_username,strlen(mysql_thread___monitor_username))==0 + ) + ) // Do not delete this line. See bug #492 + ) { + if (session_type == PROXYSQL_SESSION_ADMIN) { + if ( (default_hostgroup<0) || (strncmp(client_myds->myconn->userinfo->username,mysql_thread___monitor_username,strlen(mysql_thread___monitor_username))==0) ) { + if (default_hostgroup==STATS_HOSTGROUP) { + session_type = PROXYSQL_SESSION_STATS; + } + } + } + l_free(pkt->size,pkt->ptr); + //if (client_myds->encrypted==false) { + if (client_myds->myconn->userinfo->schemaname==NULL) { +#ifdef PROXYSQLCLICKHOUSE + if (session_type == PROXYSQL_SESSION_CLICKHOUSE) { + if (strlen(default_schema) == 0) { + free(default_schema); + default_schema = strdup((char *)"default"); + } + } +#endif /* PROXYSQLCLICKHOUSE */ + client_myds->myconn->userinfo->set_schemaname(default_schema,strlen(default_schema)); + } + int free_users=0; + int used_users=0; + if ( + ( max_connections_reached == false ) + && + ( session_type == PROXYSQL_SESSION_MYSQL || session_type == PROXYSQL_SESSION_CLICKHOUSE || session_type == PROXYSQL_SESSION_SQLITE) + ) { + //if (session_type == PROXYSQL_SESSION_MYSQL || session_type == PROXYSQL_SESSION_CLICKHOUSE) { + client_authenticated=true; + switch (session_type) { + case PROXYSQL_SESSION_SQLITE: +//#if defined(TEST_AURORA) || defined(TEST_GALERA) || defined(TEST_GROUPREP) + free_users=1; + break; +//#endif // TEST_AURORA || TEST_GALERA || TEST_GROUPREP + case PROXYSQL_SESSION_MYSQL: + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p , session_type=PROXYSQL_SESSION_MYSQL\n", this, client_myds); + if (use_ldap_auth == false) { + free_users = GloMyAuth->increase_frontend_user_connections(client_myds->myconn->userinfo->username, &used_users); + } else { + free_users = GloMyLdapAuth->increase_frontend_user_connections(client_myds->myconn->userinfo->fe_username, &used_users); + } + break; +#ifdef PROXYSQLCLICKHOUSE + case PROXYSQL_SESSION_CLICKHOUSE: + free_users=GloClickHouseAuth->increase_frontend_user_connections(client_myds->myconn->userinfo->username, &used_users); + break; +#endif /* PROXYSQLCLICKHOUSE */ + default: + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } + } else { + free_users=1; + } + if (max_connections_reached==true || free_users<=0) { + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p , max_connections_reached=%d , free_users=%d\n", this, client_myds, max_connections_reached, free_users); + client_authenticated=false; + *wrong_pass=true; + client_myds->setDSS_STATE_QUERY_SENT_NET(); + uint8_t _pid = 2; + if (client_myds->switching_auth_stage) _pid+=2; + if (max_connections_reached==true) { + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p , Too many connections\n", this, client_myds); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,_pid,1040,(char *)"08004", (char *)"Too many connections", true); + proxy_warning("mysql-max_connections reached. Returning 'Too many connections'\n"); + GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_ERR, this, NULL, (char *)"mysql-max_connections reached"); + __sync_fetch_and_add(&MyHGM->status.access_denied_max_connections, 1); + } else { // see issue #794 + __sync_fetch_and_add(&MyHGM->status.access_denied_max_user_connections, 1); + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p . User '%s' has exceeded the 'max_user_connections' resource (current value: %d)\n", this, client_myds, client_myds->myconn->userinfo->username, used_users); + char *a=(char *)"User '%s' has exceeded the 'max_user_connections' resource (current value: %d)"; + char *b=(char *)malloc(strlen(a)+strlen(client_myds->myconn->userinfo->username)+16); + GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_ERR, this, NULL, b); + sprintf(b,a,client_myds->myconn->userinfo->username,used_users); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,2,1226,(char *)"42000", b, true); + proxy_warning("User '%s' has exceeded the 'max_user_connections' resource (current value: %d)\n",client_myds->myconn->userinfo->username,used_users); + free(b); + } + __sync_add_and_fetch(&MyHGM->status.client_connections_aborted,1); + client_myds->DSS=STATE_SLEEP; + } else { + if ( + ( default_hostgroup==ADMIN_HOSTGROUP && strcmp(client_myds->myconn->userinfo->username,(char *)"admin")==0 ) + || + ( default_hostgroup==STATS_HOSTGROUP && strcmp(client_myds->myconn->userinfo->username,(char *)"stats")==0 ) + || + ( default_hostgroup < 0 && strcmp(client_myds->myconn->userinfo->username,(char *)"monitor")==0 ) + ) { + char *client_addr = NULL; + union { + struct sockaddr_in in; + struct sockaddr_in6 in6; + } custom_sockaddr; + struct sockaddr *addr=(struct sockaddr *)malloc(sizeof(custom_sockaddr)); + socklen_t addrlen=sizeof(custom_sockaddr); + memset(addr, 0, sizeof(custom_sockaddr)); + int rc = 0; + rc = getpeername(client_myds->fd, addr, &addrlen); + if (rc == 0) { + char buf[512]; + switch (addr->sa_family) { + case AF_INET: { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr; + inet_ntop(addr->sa_family, &ipv4->sin_addr, buf, INET_ADDRSTRLEN); + client_addr = strdup(buf); + break; + } + case AF_INET6: { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr; + inet_ntop(addr->sa_family, &ipv6->sin6_addr, buf, INET6_ADDRSTRLEN); + client_addr = strdup(buf); + break; + } + default: + client_addr = strdup((char *)"localhost"); + break; + } + } else { + client_addr = strdup((char *)""); + } + uint8_t _pid = 2; + if (client_myds->switching_auth_stage) _pid+=2; + if (is_encrypted) _pid++; + if ( + (strcmp(client_addr,(char *)"127.0.0.1")==0) + || + (strcmp(client_addr,(char *)"localhost")==0) + || + (strcmp(client_addr,(char *)"::1")==0) + ) { + // we are good! + client_myds->myprot.generate_pkt_OK(true,NULL,NULL, _pid, 0,0,0,0,NULL); + handshake_err = false; + GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_OK, this, NULL); + status=WAITING_CLIENT_DATA; + client_myds->DSS=STATE_CLIENT_AUTH_OK; + } else { + char *a=(char *)"User '%s' can only connect locally"; + char *b=(char *)malloc(strlen(a)+strlen(client_myds->myconn->userinfo->username)); + sprintf(b,a,client_myds->myconn->userinfo->username); + GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_ERR, this, NULL, b); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL, _pid, 1040,(char *)"42000", b, true); + free(b); + } + free(addr); + free(client_addr); + } else { + uint8_t _pid = 2; + if (client_myds->switching_auth_stage) _pid+=2; + if (is_encrypted) _pid++; + // If this condition is met, it means that the + // 'STATE_SERVER_HANDSHAKE' being performed isn't from the start of a + // connection, but as a consequence of a 'COM_USER_CHANGE' which + // requires an 'Auth Switch'. Thus, we impose a 'pid' of '3' for the + // response 'OK' packet. See #3504 for more context. + if (change_user_auth_switch) { + _pid = 3; + change_user_auth_switch = 0; + } + if (use_ssl == true && is_encrypted == false) { + *wrong_pass=true; + GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_ERR, this, NULL); + + char *_a=(char *)"ProxySQL Error: Access denied for user '%s' (using password: %s). SSL is required"; + char *_s=(char *)malloc(strlen(_a)+strlen(client_myds->myconn->userinfo->username)+32); + sprintf(_s, _a, client_myds->myconn->userinfo->username, (client_myds->myconn->userinfo->password ? "YES" : "NO")); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL, _pid, 1045,(char *)"28000", _s, true); + proxy_error("ProxySQL Error: Access denied for user '%s' (using password: %s). SSL is required\n", client_myds->myconn->userinfo->username, (client_myds->myconn->userinfo->password ? "YES" : "NO")); + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p . Access denied for user '%s' (using password: %s). SSL is required\n", this, client_myds, client_myds->myconn->userinfo->username, (client_myds->myconn->userinfo->password ? "YES" : "NO")); + __sync_add_and_fetch(&MyHGM->status.client_connections_aborted,1); + free(_s); + __sync_fetch_and_add(&MyHGM->status.access_denied_wrong_password, 1); + } else { + // we are good! + //client_myds->myprot.generate_pkt_OK(true,NULL,NULL, (is_encrypted ? 3 : 2), 0,0,0,0,NULL,false); + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p . STATE_CLIENT_AUTH_OK\n", this, client_myds); + GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_OK, this, NULL); + client_myds->myprot.generate_pkt_OK(true,NULL,NULL, _pid, 0,0,0,0,NULL); + handshake_err = false; + status=WAITING_CLIENT_DATA; + client_myds->DSS=STATE_CLIENT_AUTH_OK; + } + } + } + } else { + l_free(pkt->size,pkt->ptr); + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p . Wrong credentials for frontend: disconnecting\n", this, client_myds); + *wrong_pass=true; + // FIXME: this should become close connection + client_myds->setDSS_STATE_QUERY_SENT_NET(); + char *client_addr=NULL; + if (client_myds->client_addr && client_myds->myconn->userinfo->username) { + char buf[512]; + switch (client_myds->client_addr->sa_family) { + case AF_INET: { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)client_myds->client_addr; + if (ipv4->sin_port) { + inet_ntop(client_myds->client_addr->sa_family, &ipv4->sin_addr, buf, INET_ADDRSTRLEN); + client_addr = strdup(buf); + } else { + client_addr = strdup((char *)"localhost"); + } + break; + } + case AF_INET6: { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)client_myds->client_addr; + inet_ntop(client_myds->client_addr->sa_family, &ipv6->sin6_addr, buf, INET6_ADDRSTRLEN); + client_addr = strdup(buf); + break; + } + default: + client_addr = strdup((char *)"localhost"); + break; + } + } else { + client_addr = strdup((char *)""); + } + if (client_myds->myconn->userinfo->username) { + char *_s=(char *)malloc(strlen(client_myds->myconn->userinfo->username)+100+strlen(client_addr)); + uint8_t _pid = 2; + if (client_myds->switching_auth_stage) _pid+=2; + if (is_encrypted) _pid++; +#ifdef DEBUG + if (client_myds->myconn->userinfo->password) { + char *tmp_pass=strdup(client_myds->myconn->userinfo->password); + int lpass = strlen(tmp_pass); + for (int i=2; imyconn->userinfo->username, client_addr, tmp_pass); + free(tmp_pass); + } else { + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p . Error: Access denied for user '%s'@'%s' . No password. Disconnecting\n", this, client_myds, client_myds->myconn->userinfo->username, client_addr); + } +#endif // DEBUG + sprintf(_s,"ProxySQL Error: Access denied for user '%s'@'%s' (using password: %s)", client_myds->myconn->userinfo->username, client_addr, (client_myds->myconn->userinfo->password ? "YES" : "NO")); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL, _pid, 1045,(char *)"28000", _s, true); + proxy_error("ProxySQL Error: Access denied for user '%s'@'%s' (using password: %s)\n", client_myds->myconn->userinfo->username, client_addr, (client_myds->myconn->userinfo->password ? "YES" : "NO")); + free(_s); + __sync_fetch_and_add(&MyHGM->status.access_denied_wrong_password, 1); + } + if (client_addr) { + free(client_addr); + } + GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_ERR, this, NULL); + __sync_add_and_fetch(&MyHGM->status.client_connections_aborted,1); + client_myds->DSS=STATE_SLEEP; + } + + if (mysql_thread___client_host_cache_size) { + GloPWTH->update_client_host_cache(client_myds->client_addr, handshake_err); + } +} + +bool MySQL_Session::RunQuery_Success(MySQL_Connection *myconn, bool& prepared_stmt_with_no_params) { + + handler_rc0_Process_GTID(myconn); + + MySQL_Data_Stream *myds = myconn->myds; + + // if we are locked on hostgroup, the value of autocommit is copied from the backend connection + // see bug #3549 + if (locked_on_hostgroup >= 0) { + assert(myconn != NULL); + assert(myconn->mysql != NULL); + autocommit = myconn->mysql->server_status & SERVER_STATUS_AUTOCOMMIT; + } + + if (mirror == false) { + // Support for LAST_INSERT_ID() + if (myconn->mysql->insert_id) { + last_insert_id=myconn->mysql->insert_id; + } + if (myconn->mysql->affected_rows) { + if (myconn->mysql->affected_rows != ULLONG_MAX) { + last_HG_affected_rows = current_hostgroup; + if (mysql_thread___auto_increment_delay_multiplex && myconn->mysql->insert_id) { + myconn->auto_increment_delay_token = mysql_thread___auto_increment_delay_multiplex + 1; + __sync_fetch_and_add(&MyHGM->status.auto_increment_delay_multiplex, 1); + } + } + } + } + + switch (status) { + case PROCESSING_QUERY: + MySQL_Result_to_MySQL_wire(myconn->mysql, myconn->MyRS, myconn->myds); + break; + case PROCESSING_STMT_PREPARE: + { + enum session_status st; + if (handler_rc0_PROCESSING_STMT_PREPARE(st, myds, prepared_stmt_with_no_params)) { + NEXT_IMMEDIATE_NEW(st); + } + } + break; + case PROCESSING_STMT_EXECUTE: + handler_rc0_PROCESSING_STMT_EXECUTE(myds); + break; + default: + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } + + if (mysql_thread___log_mysql_warnings_enabled) { + auto warn_no = mysql_warning_count(myconn->mysql); + if (warn_no > 0) { + RequestEnd_mysql(myds); + writeout(); + + myconn->async_state_machine=ASYNC_IDLE; + myds->DSS=STATE_MARIADB_GENERIC; + + NEXT_IMMEDIATE_NEW(SHOW_WARNINGS); + } + } + RequestEnd_mysql(myds); + finishQuery(myds,myconn,prepared_stmt_with_no_params); + return false; +} + + +bool MySQL_Session::RunQuery_Failed(MySQL_Connection *myconn, bool& wrong_pass, int& handler_ret) { + MySQL_Data_Stream *myds = myconn->myds; + int myerr=mysql_errno(myconn->mysql); + char *errmsg = NULL; + if (myerr == 0) { + if (CurrentQuery.mysql_stmt) { + myerr = mysql_stmt_errno(CurrentQuery.mysql_stmt); + errmsg = strdup(mysql_stmt_error(CurrentQuery.mysql_stmt)); + } + } + MyHGM->p_update_mysql_error_counter(p_mysql_error_type::mysql, myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, myerr); + CurrentQuery.mysql_stmt=NULL; // immediately reset mysql_stmt + int rc1 = handler_ProcessingQueryError_CheckBackendConnectionStatus(myds); + if (rc1 == -1) { + handler_ret = -1; + return false; + } else { + if (rc1 == 1) + NEXT_IMMEDIATE_NEW(CONNECTING_SERVER); + } + if (myerr >= 2000 && myerr < 3000) { + if (handler_minus1_ClientLibraryError(myds, myerr, &errmsg)) { + NEXT_IMMEDIATE_NEW(CONNECTING_SERVER); + } else { + handler_ret = -1; + return false; + } + } else { + handler_minus1_LogErrorDuringQuery(myconn, myerr, errmsg); + if (handler_minus1_HandleErrorCodes(myds, myerr, &errmsg, handler_ret)) { + if (handler_ret == 0) + NEXT_IMMEDIATE_NEW(CONNECTING_SERVER); + } + handler_minus1_GenerateErrorMessage(myds, myconn, wrong_pass); + RequestEnd_mysql(myds); + handler_minus1_HandleBackendConnection(myds, myconn); + } + return false; +} + +// this function was inline +void MySQL_Session::handler___status_WAITING_CLIENT_DATA() { + if (mybes) { + MySQL_Backend *_mybe; + unsigned int i; + for (i=0; i < mybes->len; i++) { + _mybe=(MySQL_Backend *)mybes->index(i); + if (_mybe->server_myds) { + MySQL_Data_Stream *_myds=_mybe->server_myds; + if (_myds->myconn) { + if (_myds->myconn->multiplex_delayed) { + if (_myds->wait_until <= thread->curtime) { + _myds->wait_until=0; + _myds->myconn->multiplex_delayed=false; + _myds->DSS=STATE_NOT_INITIALIZED; + _myds->return_MySQL_Connection_To_Pool(); + } + } + } + } + } + } +} + +bool MySQL_Session::ProcessingRequest_MatchEnvironment(MySQL_Connection *myconn) { + // if return true, the calling function will goto handler_again + if (handler_again___verify_init_connect()) { + return true; + } + if (use_ldap_auth) { + if (handler_again___verify_ldap_user_variable()) { + return true; + } + } + if (handler_again___verify_backend_autocommit()) { + return true; + } + if (locked_on_hostgroup == -1 || locked_on_hostgroup_and_all_variables_set == false ) { + + if (handler_again___verify_backend_multi_statement()) { + return true; + } + + if (handler_again___verify_backend_session_track_gtids()) { + return true; + } + + // Optimize network traffic when we can use 'SET NAMES' + if (verify_set_names(this)) { + return true; + } + + for (auto i = 0; i < SQL_NAME_LAST_LOW_WM; i++) { + auto client_hash = client_myds->myconn->var_hash[i]; +#ifdef DEBUG + if (GloVars.global.gdbg) { + switch (i) { + case SQL_CHARACTER_SET: + case SQL_SET_NAMES: + case SQL_CHARACTER_SET_RESULTS: + case SQL_CHARACTER_SET_CONNECTION: + case SQL_CHARACTER_SET_CLIENT: + case SQL_COLLATION_CONNECTION: + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 7, "Session %p , variable %s has value %s\n" , this, mysql_tracked_variables[i].set_variable_name , client_myds->myconn->variables[i].value); + default: + break; + } + } +#endif // DEBUG + if (client_hash) { + auto server_hash = myconn->var_hash[i]; + if (client_hash != server_hash) { + if(!myconn->var_absent[i] && mysql_variables.verify_variable(this, i)) { + return true; + } + } + } + } + MySQL_Connection *c_con = client_myds->myconn; + vector::const_iterator it_c = c_con->dynamic_variables_idx.begin(); // client connection iterator + for ( ; it_c != c_con->dynamic_variables_idx.end() ; it_c++) { + auto i = *it_c; + auto client_hash = c_con->var_hash[i]; + auto server_hash = myconn->var_hash[i]; + if (client_hash != server_hash) { + if( + !myconn->var_absent[i] + && + mysql_variables.verify_variable(this, i) + ) { + return true; + } + } + } + + if (locked_on_hostgroup != -1) { + locked_on_hostgroup_and_all_variables_set=true; + } + } + return false; +} + +bool MySQL_Session::RunQuery_Continue(MySQL_Connection *myconn, int rc) { + switch (rc) { + // rc==1 , query is still running + // start sending to frontend if mysql_thread___threshold_resultset_size is reached + case 1: + if (myconn->MyRS && myconn->MyRS->result && myconn->MyRS->resultset_size > (unsigned int) mysql_thread___threshold_resultset_size) { + myconn->MyRS->get_resultset(client_myds->PSarrayOUT); + } + break; + // rc==2 : a multi-resultset (or multi statement) was detected, and the current statement is completed + case 2: + MySQL_Result_to_MySQL_wire(myconn->mysql, myconn->MyRS, myconn->myds); + if (myconn->MyRS) { // we also need to clear MyRS, so that the next staement will recreate it if needed + if (myconn->MyRS_reuse) { + delete myconn->MyRS_reuse; + } + //myconn->MyRS->reset_pid = false; + myconn->MyRS_reuse = myconn->MyRS; + myconn->MyRS=NULL; + } + NEXT_IMMEDIATE_NEW(PROCESSING_QUERY); + break; + // rc==3 , a multi statement query is still running + // start sending to frontend if mysql_thread___threshold_resultset_size is reached + case 3: + if (myconn->MyRS && myconn->MyRS->result && myconn->MyRS->resultset_size > (unsigned int) mysql_thread___threshold_resultset_size) { + myconn->MyRS->get_resultset(client_myds->PSarrayOUT); + } + break; + default: + break; + } + return false; +} + +void MySQL_Session::LogKillQueryTimeout(MySQL_Data_Stream *myds, char *filename, int line) { + // we only log in case on timing out here. Logging for 'killed' is done in the places that hold that contextual information. + if (myds->myconn && (mybe->server_myds->myconn->async_state_machine != ASYNC_IDLE) && myds->wait_until && (thread->curtime >= myds->wait_until)) { + std::string query {}; + + if (CurrentQuery.stmt_info == NULL) { // text protocol + query = std::string { myds->myconn->query.ptr, myds->myconn->query.length }; + } else { // prepared statement + query = std::string { CurrentQuery.stmt_info->query, CurrentQuery.stmt_info->query_length }; + } + + std::string client_addr { "" }; + int client_port = 0; + + if (client_myds) { + client_addr = client_myds->addr.addr ? client_myds->addr.addr : ""; + client_port = client_myds->addr.port; + } + + proxy_warning( + " (%s:%d) Killing connection %s:%d because query '%s' from client '%s':%d timed out.\n", + filename, line, + myds->myconn->parent->address, + myds->myconn->parent->port, + query.c_str(), + client_addr.c_str(), + client_port + ); + } +} + +// this function was inline inside MySQL_Session::get_pkts_from_client +// where: +// status = WAITING_CLIENT_DATA +void MySQL_Session::handler___status_WAITING_CLIENT_DATA___default() { + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Statuses: WAITING_CLIENT_DATA - STATE_UNKNOWN\n"); + if (mirror==false) { + char buf[INET6_ADDRSTRLEN]; + switch (client_myds->client_addr->sa_family) { + case AF_INET: { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)client_myds->client_addr; + inet_ntop(client_myds->client_addr->sa_family, &ipv4->sin_addr, buf, INET_ADDRSTRLEN); + break; + } + case AF_INET6: { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)client_myds->client_addr; + inet_ntop(client_myds->client_addr->sa_family, &ipv6->sin6_addr, buf, INET6_ADDRSTRLEN); + break; + } + default: + sprintf(buf, "localhost"); + break; + } + // PMC-10001: A unexpected packet has been received from client. This error has two potential causes: + // * Bug: ProxySQL state machine wasn't in the correct state when a legitimate client packet was received. + // * Client error: The client incorrectly sent a packet breaking MySQL protocol. + proxy_error2(10001, "Unexpected packet from client %s . Session_status: %d , client_status: %d Disconnecting it\n", buf, status, client_myds->status); + } +} + +bool MySQL_Session::handler_again___verify_backend_multi_statement() { + if ((client_myds->myconn->options.client_flag & CLIENT_MULTI_STATEMENTS) != (mybe->server_myds->myconn->options.client_flag & CLIENT_MULTI_STATEMENTS)) { + + if (client_myds->myconn->options.client_flag & CLIENT_MULTI_STATEMENTS) + mybe->server_myds->myconn->options.client_flag |= CLIENT_MULTI_STATEMENTS; + else + mybe->server_myds->myconn->options.client_flag &= ~CLIENT_MULTI_STATEMENTS; + + switch(status) { // this switch can be replaced with a simple previous_status.push(status), but it is here for readibility + case PROCESSING_QUERY: + previous_status.push(PROCESSING_QUERY); + break; + case PROCESSING_STMT_PREPARE: + previous_status.push(PROCESSING_STMT_PREPARE); + break; + case PROCESSING_STMT_EXECUTE: + previous_status.push(PROCESSING_STMT_EXECUTE); + break; + default: + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } + NEXT_IMMEDIATE_NEW(SETTING_MULTI_STMT); + } + return false; +} + +bool MySQL_Session::handler_again___verify_init_connect() { + if (mybe->server_myds->myconn->options.init_connect_sent==false) { + // we needs to set it to true + mybe->server_myds->myconn->options.init_connect_sent=true; + if (mysql_thread___init_connect) { + // we send init connect queries only if set + mybe->server_myds->myconn->options.init_connect=strdup(mysql_thread___init_connect); + switch(status) { // this switch can be replaced with a simple previous_status.push(status), but it is here for readibility + case PROCESSING_QUERY: + previous_status.push(PROCESSING_QUERY); + break; + case PROCESSING_STMT_PREPARE: + previous_status.push(PROCESSING_STMT_PREPARE); + break; + case PROCESSING_STMT_EXECUTE: + previous_status.push(PROCESSING_STMT_EXECUTE); + break; + default: + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } + NEXT_IMMEDIATE_NEW(SETTING_INIT_CONNECT); + } + } + return false; +} + +bool MySQL_Session::handler_again___verify_backend_session_track_gtids() { + bool ret = false; + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session %p , client: %s , backend: %s\n", this, client_myds->myconn->options.session_track_gtids, mybe->server_myds->myconn->options.session_track_gtids); + // we first verify that the backend supports it + // if backend is old (or if it is not mysql) ignore this setting + if ((mybe->server_myds->myconn->mysql->server_capabilities & CLIENT_SESSION_TRACKING) == 0) { + // the backend doesn't support CLIENT_SESSION_TRACKING + return ret; // exit immediately + } + uint32_t b_int = mybe->server_myds->myconn->options.session_track_gtids_int; + uint32_t f_int = client_myds->myconn->options.session_track_gtids_int; + + // we need to precompute and hardcode the values for OFF and OWN_GTID + // for performance reason we hardcoded the values + // OFF = 114160514 + if ( + (b_int == 114160514) // OFF + || + (b_int == 0) // not configured yet + ) { + if (strcmp(mysql_thread___default_session_track_gtids, (char *)"OWN_GTID")==0) { + // backend connection doesn't have session_track_gtids enabled + ret = true; + } else { + if (f_int != 0 && f_int != 114160514) { + // client wants GTID + ret = true; + } + } + } + + if (ret) { + // we deprecated handler_again___verify_backend__generic_variable + // and moved the logic here + if (mybe->server_myds->myconn->options.session_track_gtids) { // reset current value + free(mybe->server_myds->myconn->options.session_track_gtids); + mybe->server_myds->myconn->options.session_track_gtids = NULL; + } + // because the only two possible values are OWN_GTID and OFF + // and because we don't mind receiving GTIDs , if we reach here + // it means we are setting it to OWN_GTID, either because the client + // wants it, or because it is the default + // therefore we hardcode "OWN_GTID" + mybe->server_myds->myconn->options.session_track_gtids = strdup((char *)"OWN_GTID"); + mybe->server_myds->myconn->options.session_track_gtids_int = + SpookyHash::Hash32((char *)"OWN_GTID", strlen((char *)"OWN_GTID"), 10); + // we now switch status to set session_track_gtids + switch(status) { + case PROCESSING_QUERY: + case PROCESSING_STMT_PREPARE: + case PROCESSING_STMT_EXECUTE: + previous_status.push(status); + break; + default: + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } + NEXT_IMMEDIATE_NEW(SETTING_SESSION_TRACK_GTIDS); + } + return ret; +} + +bool MySQL_Session::handler_again___verify_backend_autocommit() { + if (sending_set_autocommit) { + // if sending_set_autocommit==true, the next query proxysql is going + // to run defines autocommit, for example: + // * SET autocommit=1 , or + // * SET sql_mode='', autocommit=1 + // for this reason, matching autocommit beforehand is not required + // and we return + // + // Nonetheless, we need to set autocommit in backend's MySQL_Connection + MySQL_Connection *mc = mybe->server_myds->myconn; + mc->set_autocommit(autocommit); + mc->options.last_set_autocommit = ( mc->options.autocommit ? 1 : 0 ); + return false; + } + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session %p , client: %d , backend: %d\n", this, client_myds->myconn->options.autocommit, mybe->server_myds->myconn->options.autocommit); + if (autocommit != mybe->server_myds->myconn->IsAutoCommit()) { + // see case #485 + if (mysql_thread___enforce_autocommit_on_reads == false && autocommit == false) { + // enforce_autocommit_on_reads is disabled + // we need to check if it is a SELECT not FOR UPDATE + if (CurrentQuery.is_select_NOT_for_update()==false) { + //previous_status.push(PROCESSING_QUERY); + switch(status) { // this switch can be replaced with a simple previous_status.push(status), but it is here for readibility + case PROCESSING_QUERY: + previous_status.push(PROCESSING_QUERY); + break; + case PROCESSING_STMT_PREPARE: + previous_status.push(PROCESSING_STMT_PREPARE); + break; + case PROCESSING_STMT_EXECUTE: + previous_status.push(PROCESSING_STMT_EXECUTE); + break; + default: + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } + NEXT_IMMEDIATE_NEW(CHANGING_AUTOCOMMIT); + } + } else { + // in every other cases, enforce autocommit + //previous_status.push(PROCESSING_QUERY); + switch(status) { // this switch can be replaced with a simple previous_status.push(status), but it is here for readibility + case PROCESSING_QUERY: + previous_status.push(PROCESSING_QUERY); + break; + case PROCESSING_STMT_PREPARE: + previous_status.push(PROCESSING_STMT_PREPARE); + break; + case PROCESSING_STMT_EXECUTE: + previous_status.push(PROCESSING_STMT_EXECUTE); + break; + default: + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } + NEXT_IMMEDIATE_NEW(CHANGING_AUTOCOMMIT); + } + } else { + if (autocommit == false) { // also IsAutoCommit==false + if (mysql_thread___enforce_autocommit_on_reads == false) { + if (mybe->server_myds->myconn->IsActiveTransaction() == false) { + if (CurrentQuery.is_select_NOT_for_update()==true) { + // client wants autocommit=0 + // enforce_autocommit_on_reads=false + // there is no transaction + // this seems to be the first query, and a SELECT not FOR UPDATE + // we will switch back to autcommit=1 + if (status == PROCESSING_QUERY) { + previous_status.push(PROCESSING_QUERY); + NEXT_IMMEDIATE_NEW(CHANGING_AUTOCOMMIT); + } + } + } + } else { // mysql_thread___enforce_autocommit_on_reads == true + // this code seems wrong. Removed +/* + if (mybe->server_myds->myconn->IsActiveTransaction() == false) { + if (status == PROCESSING_QUERY) { + previous_status.push(PROCESSING_QUERY); + NEXT_IMMEDIATE_NEW(CHANGING_AUTOCOMMIT); + } + } +*/ + } + } + } + return false; +} + +bool MySQL_Session::handler_again___verify_backend_user_schema() { + MySQL_Data_Stream *myds=mybe->server_myds; + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session %p , client: %s , backend: %s\n", this, client_myds->myconn->userinfo->username, mybe->server_myds->myconn->userinfo->username); + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session %p , client: %s , backend: %s\n", this, client_myds->myconn->userinfo->schemaname, mybe->server_myds->myconn->userinfo->schemaname); + if (client_myds->myconn->userinfo->hash!=mybe->server_myds->myconn->userinfo->hash) { + if (strcmp(client_myds->myconn->userinfo->username,myds->myconn->userinfo->username)) { + //previous_status.push(PROCESSING_QUERY); + switch(status) { // this switch can be replaced with a simple previous_status.push(status), but it is here for readibility + case PROCESSING_QUERY: + previous_status.push(PROCESSING_QUERY); + break; + case PROCESSING_STMT_PREPARE: + previous_status.push(PROCESSING_STMT_PREPARE); + break; + case PROCESSING_STMT_EXECUTE: + previous_status.push(PROCESSING_STMT_EXECUTE); + break; + default: + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } + mybe->server_myds->wait_until = thread->curtime + mysql_thread___connect_timeout_server*1000; // max_timeout + NEXT_IMMEDIATE_NEW(CHANGING_USER_SERVER); + } + if (strcmp(client_myds->myconn->userinfo->schemaname,myds->myconn->userinfo->schemaname)) { + //previous_status.push(PROCESSING_QUERY); + switch(status) { // this switch can be replaced with a simple previous_status.push(status), but it is here for readibility + case PROCESSING_QUERY: + previous_status.push(PROCESSING_QUERY); + break; + case PROCESSING_STMT_PREPARE: + previous_status.push(PROCESSING_STMT_PREPARE); + break; + case PROCESSING_STMT_EXECUTE: + previous_status.push(PROCESSING_STMT_EXECUTE); + break; + default: + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } + NEXT_IMMEDIATE_NEW(CHANGING_SCHEMA); + } + } + // if we reach here, the username is the same + if (myds->myconn->requires_CHANGE_USER(client_myds->myconn)) { + // if we reach here, even if the username is the same, + // the backend connection has some session variable set + // that the client never asked for + // because we can't unset variables, we will reset the connection + switch(status) { + case PROCESSING_QUERY: + case PROCESSING_STMT_PREPARE: + case PROCESSING_STMT_EXECUTE: + previous_status.push(status); + break; + default: + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } + mybe->server_myds->wait_until = thread->curtime + mysql_thread___connect_timeout_server*1000; // max_timeout + NEXT_IMMEDIATE_NEW(CHANGING_USER_SERVER); + } + return false; +} + +bool MySQL_Session::handler_again___status_SETTING_INIT_CONNECT(int *_rc) { + bool ret=false; + MySQL_Data_Stream *myds=mybe->server_myds; + assert(myds->myconn); + MySQL_Connection *myconn=myds->myconn; + myds->DSS=STATE_MARIADB_QUERY; + enum session_status st=status; + if (myds->mypolls==NULL) { + thread->mypolls.add(POLLIN|POLLOUT, mybe->server_myds->fd, mybe->server_myds, thread->curtime); + } + int rc=myconn->async_send_simple_command(myds->revents,myconn->options.init_connect,strlen(myconn->options.init_connect)); + if (rc==0) { + myds->revents|=POLLOUT; // we also set again POLLOUT to send a query immediately! + //myds->free_mysql_real_query(); + myds->DSS = STATE_MARIADB_GENERIC; + st=previous_status.top(); + previous_status.pop(); + NEXT_IMMEDIATE_NEW(st); + } else { + if (rc==-1) { + // the command failed + int myerr=mysql_errno(myconn->mysql); + MyHGM->p_update_mysql_error_counter( + p_mysql_error_type::mysql, + myconn->parent->myhgc->hid, + myconn->parent->address, + myconn->parent->port, + ( myerr ? myerr : ER_PROXYSQL_OFFLINE_SRV ) + ); + if (myerr >= 2000 || myerr == 0) { + bool retry_conn=false; + // client error, serious + detected_broken_connection(__FILE__ , __LINE__ , __func__ , "while setting INIT CONNECT", myconn, myerr, mysql_error(myconn->mysql)); + //if ((myds->myconn->reusable==true) && ((myds->myprot.prot_status & SERVER_STATUS_IN_TRANS)==0)) { + if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + retry_conn=true; + } + myds->destroy_MySQL_Connection_From_Pool(false); + myds->fd=0; + if (retry_conn) { + myds->DSS=STATE_NOT_INITIALIZED; + //previous_status.push(PROCESSING_QUERY); + NEXT_IMMEDIATE_NEW(CONNECTING_SERVER); + } + *_rc=-1; // an error happened, we should destroy the Session + return ret; + } else { + proxy_warning("Error while setting INIT CONNECT on %s:%d hg %d : %d, %s\n", myconn->parent->address, myconn->parent->port, current_hostgroup, myerr, mysql_error(myconn->mysql)); + // we won't go back to PROCESSING_QUERY + st=previous_status.top(); + previous_status.pop(); + char sqlstate[10]; + sprintf(sqlstate,"%s",mysql_sqlstate(myconn->mysql)); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,mysql_errno(myconn->mysql),sqlstate,mysql_error(myconn->mysql)); + myds->destroy_MySQL_Connection_From_Pool(true); + myds->fd=0; + status=WAITING_CLIENT_DATA; + client_myds->DSS=STATE_SLEEP; + } + } else { + // rc==1 , nothing to do for now + } + } + return ret; +} + +bool MySQL_Session::handler_again___status_SETTING_LDAP_USER_VARIABLE(int *_rc) { + bool ret=false; + MySQL_Data_Stream *myds=mybe->server_myds; + assert(myds->myconn); + MySQL_Connection *myconn=myds->myconn; + myds->DSS=STATE_MARIADB_QUERY; + enum session_status st=status; + + if ( + (GloMyLdapAuth==NULL) || (use_ldap_auth==false) + || + (client_myds==NULL || client_myds->myconn==NULL || client_myds->myconn->userinfo==NULL) + ) { // nothing to do + myds->revents|=POLLOUT; // we also set again POLLOUT to send a query immediately! + //myds->free_mysql_real_query(); + myds->DSS = STATE_MARIADB_GENERIC; + st=previous_status.top(); + previous_status.pop(); + NEXT_IMMEDIATE_NEW(st); + } + + if (myds->mypolls==NULL) { + thread->mypolls.add(POLLIN|POLLOUT, mybe->server_myds->fd, mybe->server_myds, thread->curtime); + } + int rc; + if (myconn->async_state_machine == ASYNC_IDLE) { + char *fe=client_myds->myconn->userinfo->fe_username; + char *a = (char *)"SET @%s:='%s'"; + if (fe == NULL) { + fe = (char *)"unknown"; + } + if (myconn->options.ldap_user_variable_value) { + free(myconn->options.ldap_user_variable_value); + } + myconn->options.ldap_user_variable_value = strdup(fe); + char *buf = (char *)malloc(strlen(fe)+strlen(a)+strlen(myconn->options.ldap_user_variable)); + sprintf(buf,a,myconn->options.ldap_user_variable,fe); + rc = myconn->async_send_simple_command(myds->revents,buf,strlen(buf)); + free(buf); + } else { // if async_state_machine is not ASYNC_IDLE , arguments are ignored + rc = myconn->async_send_simple_command(myds->revents,(char *)"", 0); + } + if (rc==0) { + myds->revents|=POLLOUT; // we also set again POLLOUT to send a query immediately! + //myds->free_mysql_real_query(); + myds->DSS = STATE_MARIADB_GENERIC; + st=previous_status.top(); + previous_status.pop(); + NEXT_IMMEDIATE_NEW(st); + } else { + if (rc==-1) { + // the command failed + int myerr=mysql_errno(myconn->mysql); + MyHGM->p_update_mysql_error_counter( + p_mysql_error_type::mysql, + myconn->parent->myhgc->hid, + myconn->parent->address, + myconn->parent->port, + ( myerr ? myerr : ER_PROXYSQL_OFFLINE_SRV ) + ); + if (myerr >= 2000 || myerr == 0) { + bool retry_conn=false; + // client error, serious + detected_broken_connection(__FILE__ , __LINE__ , __func__ , "while setting LDAP USER VARIABLE", myconn, myerr, mysql_error(myconn->mysql)); + if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + retry_conn=true; + } + myds->destroy_MySQL_Connection_From_Pool(false); + myds->fd=0; + if (retry_conn) { + myds->DSS=STATE_NOT_INITIALIZED; + NEXT_IMMEDIATE_NEW(CONNECTING_SERVER); + } + *_rc=-1; // an error happened, we should destroy the Session + return ret; + } else { + proxy_warning("Error while setting LDAP USER VARIABLE: %s:%d hg %d : %d, %s\n", myconn->parent->address, myconn->parent->port, current_hostgroup, myerr, mysql_error(myconn->mysql)); + // we won't go back to PROCESSING_QUERY + st=previous_status.top(); + previous_status.pop(); + char sqlstate[10]; + sprintf(sqlstate,"%s",mysql_sqlstate(myconn->mysql)); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,mysql_errno(myconn->mysql),sqlstate,mysql_error(myconn->mysql)); + myds->destroy_MySQL_Connection_From_Pool(true); + myds->fd=0; + status=WAITING_CLIENT_DATA; + client_myds->DSS=STATE_SLEEP; + } + } else { + // rc==1 , nothing to do for now + } + } + return ret; +} + +bool MySQL_Session::handler_again___status_SETTING_SQL_LOG_BIN(int *_rc) { + bool ret=false; + MySQL_Data_Stream *myds=mybe->server_myds; + assert(myds->myconn); + MySQL_Connection *myconn=myds->myconn; + myds->DSS=STATE_MARIADB_QUERY; + enum session_status st=status; + if (myds->mypolls==NULL) { + thread->mypolls.add(POLLIN|POLLOUT, mybe->server_myds->fd, mybe->server_myds, thread->curtime); + } + char *query=NULL; + unsigned long query_length=0; + if (myconn->async_state_machine==ASYNC_IDLE) { + char *q=(char *)"SET SQL_LOG_BIN=%s"; + query=(char *)malloc(strlen(q)+8); + sprintf(query,q,mysql_variables.client_get_value(this, SQL_SQL_LOG_BIN)); + query_length=strlen(query); + } + int rc=myconn->async_send_simple_command(myds->revents,query,query_length); + if (query) { + free(query); + query=NULL; + } + if (rc==0) { + if (!strcmp("0", mysql_variables.client_get_value(this, SQL_SQL_LOG_BIN)) || !strcasecmp("OFF", mysql_variables.client_get_value(this, SQL_SQL_LOG_BIN))) { + // Pay attention here. STATUS_MYSQL_CONNECTION_SQL_LOG_BIN0 sets sql_log_bin to ZERO: + // - sql_log_bin=0 => true + // - sql_log_bin=1 => false + myconn->set_status(true, STATUS_MYSQL_CONNECTION_SQL_LOG_BIN0); + } else if (!strcmp("1", mysql_variables.client_get_value(this, SQL_SQL_LOG_BIN)) || !strcasecmp("ON", mysql_variables.client_get_value(this, SQL_SQL_LOG_BIN))) { + myconn->set_status(false, STATUS_MYSQL_CONNECTION_SQL_LOG_BIN0); + } + myds->revents|=POLLOUT; // we also set again POLLOUT to send a query immediately! + myds->DSS = STATE_MARIADB_GENERIC; + st=previous_status.top(); + previous_status.pop(); + NEXT_IMMEDIATE_NEW(st); + } else { + if (rc==-1) { + // the command failed + int myerr=mysql_errno(myconn->mysql); + MyHGM->p_update_mysql_error_counter( + p_mysql_error_type::mysql, + myconn->parent->myhgc->hid, + myconn->parent->address, + myconn->parent->port, + ( myerr ? myerr : ER_PROXYSQL_OFFLINE_SRV ) + ); + if (myerr >= 2000 || myerr == 0) { + bool retry_conn=false; + // client error, serious + detected_broken_connection(__FILE__ , __LINE__ , __func__ , "while setting SQL_LOG_BIN", myconn, myerr, mysql_error(myconn->mysql)); + if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + retry_conn=true; + } + myds->destroy_MySQL_Connection_From_Pool(false); + myds->fd=0; + if (retry_conn) { + myds->DSS=STATE_NOT_INITIALIZED; + NEXT_IMMEDIATE_NEW(CONNECTING_SERVER); + } + *_rc=-1; // an error happened, we should destroy the Session + return ret; + } else { + proxy_warning("Error while setting SQL_LOG_BIN: %s:%d hg %d : %d, %s\n", myconn->parent->address, myconn->parent->port, current_hostgroup, myerr, mysql_error(myconn->mysql)); + // we won't go back to PROCESSING_QUERY + st=previous_status.top(); + previous_status.pop(); + char sqlstate[10]; + sprintf(sqlstate,"%s",mysql_sqlstate(myconn->mysql)); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,mysql_errno(myconn->mysql),sqlstate,mysql_error(myconn->mysql)); + myds->destroy_MySQL_Connection_From_Pool(true); + myds->fd=0; + RequestEnd_mysql(myds); + } + } else { + // rc==1 , nothing to do for now + } + } + return ret; +} + +bool MySQL_Session::handler_again___status_CHANGING_CHARSET(int *_rc) { + MySQL_Data_Stream *myds=mybe->server_myds; + assert(myds->myconn); + MySQL_Connection *myconn=myds->myconn; + + /* Validate that server can support client's charset */ + if (!validate_charset(this, SQL_CHARACTER_SET_CLIENT, *_rc)) { + return false; + } + + myds->DSS=STATE_MARIADB_QUERY; + enum session_status st=status; + if (myds->mypolls==NULL) { + thread->mypolls.add(POLLIN|POLLOUT, mybe->server_myds->fd, mybe->server_myds, thread->curtime); + } + + mysql_variables.client_set_value(this, SQL_CHARACTER_SET, mysql_variables.client_get_value(this, SQL_CHARACTER_SET_CLIENT)); + int charset = atoi(mysql_variables.client_get_value(this, SQL_CHARACTER_SET_CLIENT)); + int rc=myconn->async_set_names(myds->revents, charset); + + if (rc==0) { + __sync_fetch_and_add(&MyHGM->status.backend_set_names, 1); + myds->DSS = STATE_MARIADB_GENERIC; + st=previous_status.top(); + previous_status.pop(); + NEXT_IMMEDIATE_NEW(st); + } else { + if (rc==-1) { + // the command failed + int myerr=mysql_errno(myconn->mysql); + MyHGM->p_update_mysql_error_counter( + p_mysql_error_type::mysql, + myconn->parent->myhgc->hid, + myconn->parent->address, + myconn->parent->port, + ( myerr ? myerr : ER_PROXYSQL_OFFLINE_SRV ) + ); + if (myerr >= 2000 || myerr == 0) { + if (myerr == 2019) { + proxy_error("Client trying to set a charset/collation (%u) not supported by backend (%s:%d). Changing it to %u\n", charset, myconn->parent->address, myconn->parent->port, mysql_tracked_variables[SQL_CHARACTER_SET].default_value); + } + bool retry_conn=false; + // client error, serious + detected_broken_connection(__FILE__ , __LINE__ , __func__ , "during SET NAMES", myconn, myerr, mysql_error(myconn->mysql)); + if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + retry_conn=true; + } + myds->destroy_MySQL_Connection_From_Pool(false); + myds->fd=0; + if (retry_conn) { + myds->DSS=STATE_NOT_INITIALIZED; + //previous_status.push(PROCESSING_QUERY); + NEXT_IMMEDIATE_NEW(CONNECTING_SERVER); + } + *_rc=-1; + return false; + } else { + proxy_warning("Error during SET NAMES: %d, %s\n", myerr, mysql_error(myconn->mysql)); + // we won't go back to PROCESSING_QUERY + st=previous_status.top(); + previous_status.pop(); + char sqlstate[10]; + sprintf(sqlstate,"%s",mysql_sqlstate(myconn->mysql)); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,mysql_errno(myconn->mysql),sqlstate,mysql_error(myconn->mysql)); + myds->destroy_MySQL_Connection_From_Pool(true); + myds->fd=0; + status=WAITING_CLIENT_DATA; + client_myds->DSS=STATE_SLEEP; + RequestEnd_mysql(myds); + } + } else { + // rc==1 , nothing to do for now + } + } + return false; +} + +bool MySQL_Session::handler_again___status_SETTING_GENERIC_VARIABLE(int *_rc, const char *var_name, const char *var_value, bool no_quote, bool set_transaction) { + bool ret = false; + MySQL_Data_Stream *myds=mybe->server_myds; + assert(myds->myconn); + MySQL_Connection *myconn=myds->myconn; + myds->DSS=STATE_MARIADB_QUERY; + enum session_status st=status; + if (myds->mypolls==NULL) { + thread->mypolls.add(POLLIN|POLLOUT, mybe->server_myds->fd, mybe->server_myds, thread->curtime); + } + char *query=NULL; + unsigned long query_length=0; + if (myconn->async_state_machine==ASYNC_IDLE) { + char *q = NULL; + if (set_transaction==false) { + if (no_quote) { + q=(char *)"SET %s=%s"; + } else { + q=(char *)"SET %s='%s'"; // default + if (var_value[0] && var_value[0]=='@') { + q=(char *)"SET %s=%s";} + if (strncasecmp(var_value,(char *)"CONCAT",6)==0) + q=(char *)"SET %s=%s"; + if (strncasecmp(var_value,(char *)"IFNULL",6)==0) + q=(char *)"SET %s=%s"; + if (strncasecmp(var_value,(char *)"REPLACE",7)==0) + q=(char *)"SET %s=%s"; + if (var_value[0] && var_value[0]=='(') { // the value is a subquery + q=(char *)"SET %s=%s"; + } + } + } else { + // NOTE: for now, only SET SESSION is supported + // the calling function is already passing "SESSION TRANSACTION" + q=(char *)"SET %s %s"; + } + query=(char *)malloc(strlen(q)+strlen(var_name)+strlen(var_value)); + if (strncasecmp("tx_isolation", var_name, 12) == 0) { + char *sv = mybe->server_myds->myconn->mysql->server_version; + if (strncmp(sv,(char *)"8",1)==0) { + sprintf(query,q,"transaction_isolation", var_value); + } + else { + sprintf(query,q,"tx_isolation", var_value); + } + } + else { + sprintf(query,q,var_name, var_value); + } + query_length=strlen(query); + } + int rc=myconn->async_send_simple_command(myds->revents,query,query_length); + if (query) { + free(query); + query=NULL; + } + if (rc==0) { + myds->revents|=POLLOUT; // we also set again POLLOUT to send a query immediately! + myds->DSS = STATE_MARIADB_GENERIC; + st=previous_status.top(); + previous_status.pop(); + NEXT_IMMEDIATE_NEW(st); + } else { + if (rc==-1) { + // the command failed + int myerr=mysql_errno(myconn->mysql); + MyHGM->p_update_mysql_error_counter( + p_mysql_error_type::mysql, + myconn->parent->myhgc->hid, + myconn->parent->address, + myconn->parent->port, + ( myerr ? myerr : ER_PROXYSQL_OFFLINE_SRV ) + ); + if (myerr >= 2000 || myerr == 0) { + bool retry_conn=false; + // client error, serious + std::string action = "while setting "; + action += var_name; + detected_broken_connection(__FILE__ , __LINE__ , __func__ , action.c_str(), myconn, myerr, mysql_error(myconn->mysql)); + //if ((myds->myconn->reusable==true) && ((myds->myprot.prot_status & SERVER_STATUS_IN_TRANS)==0)) { + if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + retry_conn=true; + } + myds->destroy_MySQL_Connection_From_Pool(false); + myds->fd=0; + if (retry_conn) { + myds->DSS=STATE_NOT_INITIALIZED; + NEXT_IMMEDIATE_NEW(CONNECTING_SERVER); + } + *_rc=-1; // an error happened, we should destroy the Session + return ret; + } else { + proxy_warning("Error while setting %s to \"%s\" on %s:%d hg %d : %d, %s\n", var_name, var_value, myconn->parent->address, myconn->parent->port, current_hostgroup, myerr, mysql_error(myconn->mysql)); + if ( + (myerr == 1064) // You have an error in your SQL syntax + || + (myerr == 1193) // variable is not found + || + (myerr == 1651) // Query cache is disabled + ) { + int idx = SQL_NAME_LAST_HIGH_WM; + for (int i=0; ivar_absent[idx] = true; + + myds->myconn->async_free_result(); + myconn->compute_unknown_transaction_status(); + + myds->revents|=POLLOUT; // we also set again POLLOUT to send a query immediately! + myds->DSS = STATE_MARIADB_GENERIC; + st=previous_status.top(); + previous_status.pop(); + NEXT_IMMEDIATE_NEW(st); + } + } + + // we won't go back to PROCESSING_QUERY + st=previous_status.top(); + previous_status.pop(); + char sqlstate[10]; + sprintf(sqlstate,"%s",mysql_sqlstate(myconn->mysql)); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,mysql_errno(myconn->mysql),sqlstate,mysql_error(myconn->mysql)); + int myerr=mysql_errno(myconn->mysql); + switch (myerr) { + case 1231: +/* + too complicated code? + if (mysql_thread___multiplexing && (myconn->reusable==true) && myconn->IsActiveTransaction()==false && myconn->MultiplexDisabled()==false) { + myds->DSS=STATE_NOT_INITIALIZED; + if (mysql_thread___autocommit_false_not_reusable && myconn->IsAutoCommit()==false) { + if (mysql_thread___reset_connection_algorithm == 2) { + create_new_session_and_reset_mysql_connection(myds); + } else { + myds->destroy_MySQL_Connection_From_Pool(true); + } + } else { + myds->return_MySQL_Connection_To_Pool(); + } + } else { + myconn->async_state_machine=ASYNC_IDLE; + myds->DSS=STATE_MARIADB_GENERIC; + } + break; +*/ + default: + myds->destroy_MySQL_Connection_From_Pool(true); + break; + } + myds->fd=0; + RequestEnd_mysql(myds); + ret=true; + } + } else { + // rc==1 , nothing to do for now + } + } + return ret; +} + +bool MySQL_Session::handler_again___status_SETTING_MULTI_STMT(int *_rc) { + MySQL_Data_Stream *myds=mybe->server_myds; + assert(myds->myconn); + MySQL_Connection *myconn=myds->myconn; + enum session_status st=status; + bool ret = false; + + if (myds->mypolls==NULL) { + thread->mypolls.add(POLLIN|POLLOUT, mybe->server_myds->fd, mybe->server_myds, thread->curtime); + } + int rc=myconn->async_set_option(myds->revents, myconn->options.client_flag & CLIENT_MULTI_STATEMENTS); + if (rc==0) { + myds->DSS = STATE_MARIADB_GENERIC; + st=previous_status.top(); + previous_status.pop(); + NEXT_IMMEDIATE_NEW(st); + } else { + if (rc==-1) { + // the command failed + int myerr=mysql_errno(myconn->mysql); + MyHGM->p_update_mysql_error_counter( + p_mysql_error_type::mysql, + myconn->parent->myhgc->hid, + myconn->parent->address, + myconn->parent->port, + ( myerr ? myerr : ER_PROXYSQL_OFFLINE_SRV ) + ); + if (myerr >= 2000 || myerr == 0) { + bool retry_conn=false; + // client error, serious + detected_broken_connection(__FILE__ , __LINE__ , __func__ , "while setting MYSQL_OPTION_MULTI_STATEMENTS", myconn, myerr, mysql_error(myconn->mysql)); + //if ((myds->myconn->reusable==true) && ((myds->myprot.prot_status & SERVER_STATUS_IN_TRANS)==0)) { + if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + retry_conn=true; + } + myds->destroy_MySQL_Connection_From_Pool(false); + myds->fd=0; + if (retry_conn) { + myds->DSS=STATE_NOT_INITIALIZED; + NEXT_IMMEDIATE_NEW(CONNECTING_SERVER); + } + *_rc=-1; // an error happened, we should destroy the Session + return ret; + } else { + proxy_warning("Error during MYSQL_OPTION_MULTI_STATEMENTS : %d, %s\n", myerr, mysql_error(myconn->mysql)); + // we won't go back to PROCESSING_QUERY + st=previous_status.top(); + previous_status.pop(); + char sqlstate[10]; + sprintf(sqlstate,"%s",mysql_sqlstate(myconn->mysql)); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,mysql_errno(myconn->mysql),sqlstate,mysql_error(myconn->mysql)); + myds->destroy_MySQL_Connection_From_Pool(true); + myds->fd=0; + RequestEnd_mysql(myds); + } + } else { + // rc==1 , nothing to do for now + } + } + return ret; +} + +bool MySQL_Session::handler_again___status_SETTING_SESSION_TRACK_GTIDS(int *_rc) { + bool ret=false; + assert(mybe->server_myds->myconn); + ret = handler_again___status_SETTING_GENERIC_VARIABLE(_rc, (char *)"SESSION_TRACK_GTIDS", mybe->server_myds->myconn->options.session_track_gtids, true); + return ret; +} + +bool MySQL_Session::handler_again___status_CHANGING_SCHEMA(int *_rc) { + bool ret=false; + //fprintf(stderr,"CHANGING_SCHEMA\n"); + assert(mybe->server_myds->myconn); + MySQL_Data_Stream *myds=mybe->server_myds; + MySQL_Connection *myconn=myds->myconn; + myds->DSS=STATE_MARIADB_QUERY; + enum session_status st=status; + if (myds->mypolls==NULL) { + thread->mypolls.add(POLLIN|POLLOUT, mybe->server_myds->fd, mybe->server_myds, thread->curtime); + } + int rc=myconn->async_select_db(myds->revents); + if (rc==0) { + __sync_fetch_and_add(&MyHGM->status.backend_init_db, 1); + myds->myconn->userinfo->set(client_myds->myconn->userinfo); + myds->DSS = STATE_MARIADB_GENERIC; + st=previous_status.top(); + previous_status.pop(); + NEXT_IMMEDIATE_NEW(st); + } else { + if (rc==-1) { + // the command failed + int myerr=mysql_errno(myconn->mysql); + MyHGM->p_update_mysql_error_counter( + p_mysql_error_type::mysql, + myconn->parent->myhgc->hid, + myconn->parent->address, + myconn->parent->port, + ( myerr ? myerr : ER_PROXYSQL_OFFLINE_SRV ) + ); + if (myerr >= 2000 || myerr == 0) { + bool retry_conn=false; + // client error, serious + detected_broken_connection(__FILE__ , __LINE__ , __func__ , "during INIT_DB", myconn, myerr, mysql_error(myconn->mysql)); + //if ((myds->myconn->reusable==true) && ((myds->myprot.prot_status & SERVER_STATUS_IN_TRANS)==0)) { + if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + retry_conn=true; + } + myds->destroy_MySQL_Connection_From_Pool(false); + myds->fd=0; + if (retry_conn) { + myds->DSS=STATE_NOT_INITIALIZED; + NEXT_IMMEDIATE_NEW(CONNECTING_SERVER); + } + *_rc=-1; // an error happened, we should destroy the Session + return ret; + } else { + proxy_warning("Error during INIT_DB: %d, %s\n", myerr, mysql_error(myconn->mysql)); + // we won't go back to PROCESSING_QUERY + st=previous_status.top(); + previous_status.pop(); + char sqlstate[10]; + sprintf(sqlstate,"%s",mysql_sqlstate(myconn->mysql)); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,mysql_errno(myconn->mysql),sqlstate,mysql_error(myconn->mysql)); + myds->destroy_MySQL_Connection_From_Pool(true); + myds->fd=0; + RequestEnd_mysql(myds); + } + } else { + // rc==1 , nothing to do for now + } + } + return false; +} + +bool MySQL_Session::handler_again___multiple_statuses(int *rc) { + bool ret = false; + switch(status) { + case CHANGING_USER_SERVER: + ret = handler_again___status_CHANGING_USER_SERVER(rc); + break; + case CHANGING_AUTOCOMMIT: + ret = handler_again___status_CHANGING_AUTOCOMMIT(rc); + break; + case CHANGING_SCHEMA: + ret = handler_again___status_CHANGING_SCHEMA(rc); + break; + case SETTING_LDAP_USER_VARIABLE: + ret = handler_again___status_SETTING_LDAP_USER_VARIABLE(rc); + break; + case SETTING_INIT_CONNECT: + ret = handler_again___status_SETTING_INIT_CONNECT(rc); + break; + case SETTING_MULTI_STMT: + ret = handler_again___status_SETTING_MULTI_STMT(rc); + break; + case SETTING_SESSION_TRACK_GTIDS: + ret = handler_again___status_SETTING_SESSION_TRACK_GTIDS(rc); + break; + case SETTING_SET_NAMES: + ret = handler_again___status_CHANGING_CHARSET(rc); + break; + default: + break; + } + return ret; +} + +// this function was inline inside MySQL_Session::get_pkts_from_client +// where: +// status = NONE or default +// +// this is triggered when proxysql receives a packet when doesn't expect any +// for example while it is supposed to be sending resultset to client +void MySQL_Session::handler___status_NONE_or_default(PtrSize_t& pkt) { + char buf[INET6_ADDRSTRLEN]; + switch (client_myds->client_addr->sa_family) { + case AF_INET: { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)client_myds->client_addr; + inet_ntop(client_myds->client_addr->sa_family, &ipv4->sin_addr, buf, INET_ADDRSTRLEN); + break; + } + case AF_INET6: { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)client_myds->client_addr; + inet_ntop(client_myds->client_addr->sa_family, &ipv6->sin6_addr, buf, INET6_ADDRSTRLEN); + break; + } + default: + sprintf(buf, "localhost"); + break; + } + if (pkt.size == 5) { + unsigned char c=*((unsigned char *)pkt.ptr+sizeof(mysql_hdr)); + if (c==_MYSQL_COM_QUIT) { + proxy_error("Unexpected COM_QUIT from client %s . Session_status: %d , client_status: %d Disconnecting it\n", buf, status, client_myds->status); + if (GloMyLogger) { GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_QUIT, this, NULL); } + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_QUIT packet\n"); + l_free(pkt.size,pkt.ptr); + if (thread) { + thread->status_variables.stvar[st_var_unexpected_com_quit]++; + } + return; + } + } + proxy_error2(10001, "Unexpected packet from client %s . Session_status: %d , client_status: %d Disconnecting it\n", buf, status, client_myds->status); + if (thread) { + thread->status_variables.stvar[st_var_unexpected_packet]++; + } + return; +} + +// this function was inline inside Client_Session::get_pkts_from_client +// where: +// status = WAITING_CLIENT_DATA +// client_myds->DSS = STATE_SLEEP +// enum_mysql_command in a large list of possible values +// the most common values for enum_mysql_command are handled from the calling function +// here we only process the not so common ones +// we return false if the enum_mysql_command is not found +bool MySQL_Session::handler_WCDSS_MYSQL_COM__various(PtrSize_t* pkt, bool* wrong_pass) { + unsigned char c; + c=*((unsigned char *)pkt->ptr+sizeof(mysql_hdr)); + switch ((enum_mysql_command)c) { + case _MYSQL_COM_CHANGE_USER: + handler_WCDSS_MYSQL_COM_CHANGE_USER(pkt, wrong_pass); + break; + case _MYSQL_COM_PING: + handler_WCDSS_MYSQL_COM_PING(pkt); + break; + case _MYSQL_COM_SET_OPTION: + handler_WCDSS_MYSQL_COM_SET_OPTION(pkt); + break; + case _MYSQL_COM_STATISTICS: + handler_WCDSS_MYSQL_COM_STATISTICS(pkt); + break; + case _MYSQL_COM_INIT_DB: + handler_WCDSS_MYSQL_COM_INIT_DB(pkt); + break; + case _MYSQL_COM_FIELD_LIST: + handler_WCDSS_MYSQL_COM_FIELD_LIST(pkt); + break; + case _MYSQL_COM_PROCESS_KILL: + handler_WCDSS_MYSQL_COM_PROCESS_KILL(pkt); + break; + case _MYSQL_COM_RESET_CONNECTION: + handler_WCDSS_MYSQL_COM_RESET_CONNECTION(pkt); + break; + default: + return false; + break; + } + return true; +} + + +// this function was inline inside Client_Session::get_pkts_from_client +// where: +// status = WAITING_CLIENT_DATA +// client_myds->DSS = STATE_SLEEP_MULTI_PACKET +// +// replacing the single goto with return true +bool MySQL_Session::handler_WCDSS_MULTI_PACKET(PtrSize_t& pkt) { + if (client_myds->multi_pkt.ptr==NULL) { + // not initialized yet + client_myds->multi_pkt.ptr=pkt.ptr; + client_myds->multi_pkt.size=pkt.size; + } else { + PtrSize_t tmp_pkt; + tmp_pkt.ptr=client_myds->multi_pkt.ptr; + tmp_pkt.size=client_myds->multi_pkt.size; + client_myds->multi_pkt.size = pkt.size + tmp_pkt.size-sizeof(mysql_hdr); + client_myds->multi_pkt.ptr = l_alloc(client_myds->multi_pkt.size); + memcpy(client_myds->multi_pkt.ptr, tmp_pkt.ptr, tmp_pkt.size); + memcpy((char *)client_myds->multi_pkt.ptr + tmp_pkt.size , (char *)pkt.ptr+sizeof(mysql_hdr) , pkt.size-sizeof(mysql_hdr)); // the header is not copied + l_free(tmp_pkt.size , tmp_pkt.ptr); + l_free(pkt.size , pkt.ptr); + } + if (pkt.size==(0xFFFFFF+sizeof(mysql_hdr))) { // there are more packets + //goto __get_pkts_from_client; + return true; + } else { + // no more packets, move everything back to pkt and proceed + pkt.ptr=client_myds->multi_pkt.ptr; + pkt.size=client_myds->multi_pkt.size; + client_myds->multi_pkt.size=0; + client_myds->multi_pkt.ptr=NULL; + client_myds->DSS=STATE_SLEEP; + } + return false; +} + +// this function was inline inside Client_Session::get_pkts_from_client +// where: +// status = WAITING_CLIENT_DATA +// client_myds->DSS = STATE_SLEEP +// enum_mysql_command = _MYSQL_COM_QUERY +// it searches for SQL injection +// it returns true if it detected an SQL injection +bool MySQL_Session::handler_WCDSS_MYSQL_COM_QUERY_detect_SQLi() { + if (client_myds->com_field_list == false) { + if (qpo->firewall_whitelist_mode != WUS_OFF) { + struct libinjection_sqli_state state; + int issqli; + const char * input = (char *)CurrentQuery.QueryPointer; + size_t slen = CurrentQuery.QueryLength; + libinjection_sqli_init(&state, input, slen, FLAG_SQL_MYSQL); + issqli = libinjection_is_sqli(&state); + if (issqli) { + bool allow_sqli = false; + allow_sqli = GloQPro->whitelisted_sqli_fingerprint(state.fingerprint); + if (allow_sqli) { + thread->status_variables.stvar[st_var_whitelisted_sqli_fingerprint]++; + } else { + thread->status_variables.stvar[st_var_automatic_detected_sqli]++; + char * username = client_myds->myconn->userinfo->username; + char * client_address = client_myds->addr.addr; + proxy_error("SQLinjection detected with fingerprint of '%s' from client %s@%s . Query listed below:\n", state.fingerprint, username, client_address); + fwrite(CurrentQuery.QueryPointer, CurrentQuery.QueryLength, 1, stderr); + fprintf(stderr,"\n"); + RequestEnd_mysql(NULL); + return true; + } + } + } + } + return false; +} + +bool MySQL_Session::handler_again___status_CHANGING_AUTOCOMMIT(int *_rc) { + //fprintf(stderr,"CHANGING_AUTOCOMMIT\n"); + assert(session_type==PROXYSQL_SESSION_MYSQL); + MySQL_Data_Stream *myds=mybe->server_myds; + MySQL_Connection *myconn=myds->myconn; + assert(myconn != NULL); + myds->DSS=STATE_MARIADB_QUERY; + enum session_status st=status; + if (myds->mypolls==NULL) { + thread->mypolls.add(POLLIN|POLLOUT, mybe->server_myds->fd, mybe->server_myds, thread->curtime); + } + bool ac = autocommit; + if (autocommit == false) { // also IsAutoCommit==false + if (mysql_thread___enforce_autocommit_on_reads == false) { + if (mybe->server_myds->myconn->IsAutoCommit() == false) { + if (mybe->server_myds->myconn->IsActiveTransaction() == false) { + if (CurrentQuery.is_select_NOT_for_update()==true) { + // client wants autocommit=0 + // enforce_autocommit_on_reads=false + // there is no transaction + // this seems to be the first query, and a SELECT not FOR UPDATE + // we will switch back to autcommit=1 + ac = true; + } + } else { + st=previous_status.top(); + previous_status.pop(); + myds->DSS = STATE_MARIADB_GENERIC; + NEXT_IMMEDIATE_NEW(st); + } + } + } + } + int rc=myconn->async_set_autocommit(myds->revents, ac); + if (rc==0) { + st=previous_status.top(); + previous_status.pop(); + myds->DSS = STATE_MARIADB_GENERIC; + NEXT_IMMEDIATE_NEW(st); + } else { + if (rc==-1) { + // the command failed + int myerr=mysql_errno(myconn->mysql); + MyHGM->p_update_mysql_error_counter( + p_mysql_error_type::mysql, + myconn->parent->myhgc->hid, + myconn->parent->address, + myconn->parent->port, + ( myerr ? myerr : ER_PROXYSQL_OFFLINE_SRV ) + ); + if (myerr >= 2000 || myerr == 0) { + bool retry_conn=false; + // client error, serious + detected_broken_connection(__FILE__ , __LINE__ , __func__ , "during SET AUTOCOMMIT", myconn, myerr, mysql_error(myconn->mysql)); + if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + retry_conn=true; + } + myds->destroy_MySQL_Connection_From_Pool(false); + myds->fd=0; + if (retry_conn) { + myds->DSS=STATE_NOT_INITIALIZED; + NEXT_IMMEDIATE_NEW(CONNECTING_SERVER); + } + *_rc=-1; + return false; + } else { + proxy_warning("Error during SET AUTOCOMMIT: %d, %s\n", myerr, mysql_error(myconn->mysql)); + // we won't go back to PROCESSING_QUERY + st=previous_status.top(); + previous_status.pop(); + char sqlstate[10]; + sprintf(sqlstate,"%s",mysql_sqlstate(myconn->mysql)); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,mysql_errno(myconn->mysql),sqlstate,mysql_error(myconn->mysql)); + myds->destroy_MySQL_Connection_From_Pool(true); + myds->fd=0; + RequestEnd_mysql(myds); + status=WAITING_CLIENT_DATA; + client_myds->DSS=STATE_SLEEP; + } + } else { + // rc==1 , nothing to do for now + } + } + return false; +} + +void MySQL_Session::handler_WCDSS_MYSQL_COM_QUERY___create_mirror_session() { + if (pktH->size < 15*1024*1024 && (qpo->mirror_hostgroup >= 0 || qpo->mirror_flagOUT >= 0)) { + // check if there are too many mirror sessions in queue + if (thread->mirror_queue_mysql_sessions->len >= (unsigned int)mysql_thread___mirror_max_queue_length) { + return; + } + // at this point, we will create the new session + // we will later decide if queue it or sent it immediately + +// int i=0; +// for (i=0;i<100;i++) { + MySQL_Session *newsess=NULL; + if (thread->mirror_queue_mysql_sessions_cache->len==0) { + newsess=new MySQL_Session(); + newsess->client_myds = new MySQL_Data_Stream(); + newsess->client_myds->DSS=STATE_SLEEP; + newsess->client_myds->sess=newsess; + newsess->client_myds->fd=0; + newsess->client_myds->myds_type=MYDS_FRONTEND; + newsess->client_myds->PSarrayOUT= new PtrSizeArray(); + newsess->thread_session_id=__sync_fetch_and_add(&glovars.thread_id,1); + if (newsess->thread_session_id==0) { + newsess->thread_session_id=__sync_fetch_and_add(&glovars.thread_id,1); + } + newsess->status=WAITING_CLIENT_DATA; + MySQL_Connection *myconn=new MySQL_Connection; + newsess->client_myds->attach_connection(myconn); + newsess->client_myds->myprot.init(&newsess->client_myds, newsess->client_myds->myconn->userinfo, newsess); + newsess->mirror=true; + newsess->client_myds->destroy_queues(); + } else { + newsess=(MySQL_Session *)thread->mirror_queue_mysql_sessions_cache->remove_index_fast(0); + } + newsess->client_myds->myconn->userinfo->set(client_myds->myconn->userinfo); + newsess->to_process=1; + newsess->default_hostgroup=default_hostgroup; + if (qpo->mirror_hostgroup>= 0) { + newsess->mirror_hostgroup=qpo->mirror_hostgroup; // in the new session we copy the mirror hostgroup + } else { + newsess->mirror_hostgroup=default_hostgroup; // copy the default + } + newsess->mirror_flagOUT=qpo->mirror_flagOUT; // in the new session we copy the mirror flagOUT + if (newsess->default_schema==NULL) { + newsess->default_schema=strdup(default_schema); + } else { + if (strcmp(newsess->default_schema,default_schema)) { + free(newsess->default_schema); + newsess->default_schema=strdup(default_schema); + } + } + newsess->mirrorPkt.size=pktH->size; + newsess->mirrorPkt.ptr=l_alloc(newsess->mirrorPkt.size); + memcpy(newsess->mirrorPkt.ptr,pktH->ptr,pktH->size); + + if (thread->mirror_queue_mysql_sessions->len==0) { + // there are no sessions in the queue, we try to execute immediately + // Only mysql_thread___mirror_max_concurrency mirror session can run in parallel + if (__sync_add_and_fetch(&GloPWTH->status_variables.mirror_sessions_current,1) > (unsigned int)mysql_thread___mirror_max_concurrency ) { + // if the limit is reached, we queue it instead + __sync_sub_and_fetch(&GloPWTH->status_variables.mirror_sessions_current,1); + thread->mirror_queue_mysql_sessions->add(newsess); + } else { + GloPWTH->status_variables.p_gauge_array[p_th_gauge::mirror_concurrency]->Increment(); + thread->register_session(newsess); + newsess->handler(); // execute immediately + //newsess->to_process=0; + if (newsess->status==WAITING_CLIENT_DATA) { // the mirror session has completed + thread->unregister_session(thread->mysql_sessions->len-1); + unsigned int l = (unsigned int)mysql_thread___mirror_max_concurrency; + if (thread->mirror_queue_mysql_sessions->len*0.3 > l) l=thread->mirror_queue_mysql_sessions->len*0.3; + if (thread->mirror_queue_mysql_sessions_cache->len <= l) { + bool to_cache=true; + if (newsess->mybe) { + if (newsess->mybe->server_myds) { + to_cache=false; + } + } + if (to_cache) { + __sync_sub_and_fetch(&GloPWTH->status_variables.mirror_sessions_current,1); + GloPWTH->status_variables.p_gauge_array[p_th_gauge::mirror_concurrency]->Decrement(); + thread->mirror_queue_mysql_sessions_cache->add(newsess); + } else { + delete newsess; + } + } else { + delete newsess; + } + } + } + } else { + thread->mirror_queue_mysql_sessions->add(newsess); + } + } +} + + +bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { + bool deprecate_eof_active = client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF; + + if (pkt->size>(5+18) && strncasecmp((char *)"PROXYSQL INTERNAL ",(char *)pkt->ptr+5,18)==0) { + return_proxysql_internal(pkt); + return true; + } + if (locked_on_hostgroup == -1) { + if (handler_SetAutocommit(pkt) == true) { + return true; + } + if (handler_CommitRollback(pkt) == true) { + return true; + } + } + + //handle 2564 + if (pkt->size==SELECT_VERSION_COMMENT_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncmp((char *)SELECT_VERSION_COMMENT,(char *)pkt->ptr+5,pkt->size-5)==0) { + // FIXME: this doesn't return AUTOCOMMIT or IN_TRANS + PtrSize_t pkt_2; + if (deprecate_eof_active) { + pkt_2.size=PROXYSQL_VERSION_COMMENT_WITH_OK_LEN; + pkt_2.ptr=l_alloc(pkt_2.size); + memcpy(pkt_2.ptr,PROXYSQL_VERSION_COMMENT_WITH_OK,pkt_2.size); + } else { + pkt_2.size=PROXYSQL_VERSION_COMMENT_LEN; + pkt_2.ptr=l_alloc(pkt_2.size); + memcpy(pkt_2.ptr,PROXYSQL_VERSION_COMMENT,pkt_2.size); + } + status=WAITING_CLIENT_DATA; + client_myds->DSS=STATE_SLEEP; + client_myds->PSarrayOUT->add(pkt_2.ptr,pkt_2.size); + if (mirror==false) { + RequestEnd_mysql(NULL); + } + l_free(pkt->size,pkt->ptr); + return true; + } + if (pkt->size==strlen((char *)"select USER()")+5 && strncmp((char *)"select USER()",(char *)pkt->ptr+5,pkt->size-5)==0) { + // FIXME: this doesn't return AUTOCOMMIT or IN_TRANS + char *query1=(char *)"SELECT \"%s\" AS 'USER()'"; + char *query2=(char *)malloc(strlen(query1)+strlen(client_myds->myconn->userinfo->username)+10); + sprintf(query2,query1,client_myds->myconn->userinfo->username); + char *error; + int cols; + int affected_rows; + SQLite3_result *resultset; + GloAdmin->admindb->execute_statement(query2, &error , &cols , &affected_rows , &resultset); + SQLite3_to_MySQL(resultset, error, affected_rows, &client_myds->myprot, false, deprecate_eof_active); + delete resultset; + free(query2); + if (mirror==false) { + RequestEnd_mysql(NULL); + } + l_free(pkt->size,pkt->ptr); + return true; + } + if (locked_on_hostgroup >= 0 && (strncasecmp((char *)"SET ",(char *)pkt->ptr+5,4)==0)) { + // this is a circuit breaker, we will send everything to the backend + // + // also note that in the current implementation we stop tracking variables: + // this becomes a problem if mysql-set_query_lock_on_hostgroup is + // disabled while a session is already locked + return false; + } + if ((pkt->size < 60) && (pkt->size > 38) && (strncasecmp((char *)"SET SESSION character_set_server",(char *)pkt->ptr+5,32)==0) ) { // issue #601 + char *idx=NULL; + char *p=(char *)pkt->ptr+37; + idx=(char *)memchr(p,'=',pkt->size-37); + if (idx) { // we found = + PtrSize_t pkt_2; + pkt_2.size=5+strlen((char *)"SET NAMES ")+pkt->size-1-(idx-(char *)pkt->ptr); + pkt_2.ptr=l_alloc(pkt_2.size); + mysql_hdr Hdr; + memcpy(&Hdr,pkt->ptr,sizeof(mysql_hdr)); + Hdr.pkt_length=pkt_2.size-5; + memcpy((char *)pkt_2.ptr+4,(char *)pkt->ptr+4,1); + memcpy(pkt_2.ptr,&Hdr,sizeof(mysql_hdr)); + strcpy((char *)pkt_2.ptr+5,(char *)"SET NAMES "); + memcpy((char *)pkt_2.ptr+15,idx+1,pkt->size-1-(idx-(char *)pkt->ptr)); + l_free(pkt->size,pkt->ptr); + pkt->size=pkt_2.size; + pkt->ptr=pkt_2.ptr; + } + } + if ((pkt->size < 60) && (pkt->size > 39) && (strncasecmp((char *)"SET SESSION character_set_results",(char *)pkt->ptr+5,33)==0) ) { // like the above + char *idx=NULL; + char *p=(char *)pkt->ptr+38; + idx=(char *)memchr(p,'=',pkt->size-38); + if (idx) { // we found = + PtrSize_t pkt_2; + pkt_2.size=5+strlen((char *)"SET NAMES ")+pkt->size-1-(idx-(char *)pkt->ptr); + pkt_2.ptr=l_alloc(pkt_2.size); + mysql_hdr Hdr; + memcpy(&Hdr,pkt->ptr,sizeof(mysql_hdr)); + Hdr.pkt_length=pkt_2.size-5; + memcpy((char *)pkt_2.ptr+4,(char *)pkt->ptr+4,1); + memcpy(pkt_2.ptr,&Hdr,sizeof(mysql_hdr)); + strcpy((char *)pkt_2.ptr+5,(char *)"SET NAMES "); + memcpy((char *)pkt_2.ptr+15,idx+1,pkt->size-1-(idx-(char *)pkt->ptr)); + l_free(pkt->size,pkt->ptr); + pkt->size=pkt_2.size; + pkt->ptr=pkt_2.ptr; + } + } + if ( + (pkt->size < 100) && (pkt->size > 15) && (strncasecmp((char *)"SET NAMES ",(char *)pkt->ptr+5,10)==0) + && + (memchr((const void *)((char *)pkt->ptr+5),',',pkt->size-15)==NULL) // there is no comma + ) { + char *unstripped=strndup((char *)pkt->ptr+15,pkt->size-15); + char *csname=trim_spaces_and_quotes_in_place(unstripped); + //unsigned int charsetnr = 0; + const MARIADB_CHARSET_INFO * c; + char * collation_name_unstripped = NULL; + char * collation_name = NULL; + if (strcasestr(csname," COLLATE ")) { + collation_name_unstripped = strcasestr(csname," COLLATE ") + strlen(" COLLATE "); + collation_name = trim_spaces_and_quotes_in_place(collation_name_unstripped); + char *_s1=index(csname,' '); + char *_s2=index(csname,'\''); + char *_s3=index(csname,'"'); + char *_s = NULL; + if (_s1) { + _s = _s1; + } + if (_s2) { + if (_s) { + if (_s2 < _s) { + _s = _s2; + } + } else { + _s = _s2; + } + } + if (_s3) { + if (_s) { + if (_s3 < _s) { + _s = _s3; + } + } else { + _s = _s3; + } + } + if (_s) { + *_s = '\0'; + } + + _s1 = index(collation_name,' '); + _s2 = index(collation_name,'\''); + _s3 = index(collation_name,'"'); + _s = NULL; + if (_s1) { + _s = _s1; + } + if (_s2) { + if (_s) { + if (_s2 < _s) { + _s = _s2; + } + } else { + _s = _s2; + } + } + if (_s3) { + if (_s) { + if (_s3 < _s) { + _s = _s3; + } + } else { + _s = _s3; + } + } + if (_s) { + *_s = '\0'; + } + + c = proxysql_find_charset_collate_names(csname,collation_name); + } else { + c = proxysql_find_charset_name(csname); + } + free(unstripped); + if (c) { + client_myds->DSS=STATE_QUERY_SENT_NET; + client_myds->myconn->set_charset(c->nr, NAMES); + 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); + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; + if (mirror==false) { + RequestEnd_mysql(NULL); + } + l_free(pkt->size,pkt->ptr); + __sync_fetch_and_add(&MyHGM->status.frontend_set_names, 1); + return true; + } + } + if ( (pkt->size == 18) && (strncasecmp((char *)"SHOW WARNINGS",(char *)pkt->ptr+5,13)==0) ) { + SQLite3_result * resultset=new SQLite3_result(3); + resultset->add_column_definition(SQLITE_TEXT,"Level"); + resultset->add_column_definition(SQLITE_TEXT,"Code"); + resultset->add_column_definition(SQLITE_TEXT,"Message"); + SQLite3_to_MySQL(resultset, NULL, 0, &client_myds->myprot, false, deprecate_eof_active); + delete resultset; + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; + if (mirror==false) { + RequestEnd_mysql(NULL); + } + l_free(pkt->size,pkt->ptr); + return true; + } + // 'LOAD DATA LOCAL INFILE' is unsupported. We report an specific error to inform clients about this fact. For more context see #833. + if ( (pkt->size >= 22 + 5) && (strncasecmp((char *)"LOAD DATA LOCAL INFILE",(char *)pkt->ptr+5, 22)==0) ) { + if (mysql_thread___enable_load_data_local_infile == false) { + client_myds->DSS=STATE_QUERY_SENT_NET; + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,1047,(char *)"HY000",(char *)"Unsupported 'LOAD DATA LOCAL INFILE' command",true); + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; + if (mirror==false) { + RequestEnd_mysql(NULL); + } + l_free(pkt->size,pkt->ptr); + return true; + } else { + if (mysql_thread___verbose_query_error) { + proxy_warning( + "Command '%.*s' refers to file in ProxySQL instance, NOT on client side!\n", + pkt->size - sizeof(mysql_hdr) - 1, + static_cast(pkt->ptr) + 5 + ); + } else { + proxy_warning( + "Command 'LOAD DATA LOCAL INFILE' refers to file in ProxySQL instance, NOT on client side!\n" + ); + } + } + } + + return false; +} + diff --git a/lib/MySQL_Variables.cpp b/lib/MySQL_Variables.cpp index ed19c327d..49790c8bb 100644 --- a/lib/MySQL_Variables.cpp +++ b/lib/MySQL_Variables.cpp @@ -1,13 +1,15 @@ #include "MySQL_Variables.h" #include "proxysql.h" -#include "Client_Session.h" +#include "MySQL_Session.h" #include "ProxySQL_Data_Stream.h" +#include "MySQL_Data_Stream.h" #include "SpookyV2.h" #include + static inline char is_digit(char c) { if(c >= '0' && c <= '9') return 1; @@ -76,7 +78,7 @@ MySQL_Variables::MySQL_Variables() { MySQL_Variables::~MySQL_Variables() {} -bool MySQL_Variables::client_set_hash_and_value(Client_Session* session, int idx, const std::string& value, uint32_t hash) { +bool MySQL_Variables::client_set_hash_and_value(MySQL_Session* session, int idx, const std::string& value, uint32_t hash) { if (!session || !session->client_myds || !session->client_myds->myconn) { proxy_warning("Session validation failed\n"); return false; @@ -91,7 +93,7 @@ bool MySQL_Variables::client_set_hash_and_value(Client_Session* session, int idx return true; } -void MySQL_Variables::server_set_hash_and_value(Client_Session* session, int idx, const char* value, uint32_t hash) { +void MySQL_Variables::server_set_hash_and_value(MySQL_Session* session, int idx, const char* value, uint32_t hash) { if (!session || !session->mybe || !session->mybe->server_myds || !session->mybe->server_myds->myconn || !value) { proxy_warning("Session validation failed\n"); return; @@ -137,7 +139,7 @@ void MySQL_Variables::server_set_hash_and_value(Client_Session* session, int idx * * @return 'true' in case of success, 'false' otherwise. */ -bool MySQL_Variables::client_set_value(Client_Session* session, int idx, const std::string& value) { +bool MySQL_Variables::client_set_value(MySQL_Session* session, int idx, const std::string& value) { if (!session || !session->client_myds || !session->client_myds->myconn) { proxy_warning("Session validation failed\n"); return false; @@ -189,21 +191,21 @@ bool MySQL_Variables::client_set_value(Client_Session* session, int idx, const s return true; } -const char* MySQL_Variables::client_get_value(Client_Session* session, int idx) const { +const char* MySQL_Variables::client_get_value(MySQL_Session* session, int idx) const { assert(session); assert(session->client_myds); assert(session->client_myds->myconn); return session->client_myds->myconn->variables[idx].value; } -uint32_t MySQL_Variables::client_get_hash(Client_Session* session, int idx) const { +uint32_t MySQL_Variables::client_get_hash(MySQL_Session* session, int idx) const { assert(session); assert(session->client_myds); assert(session->client_myds->myconn); return session->client_myds->myconn->var_hash[idx]; } -void MySQL_Variables::server_set_value(Client_Session* session, int idx, const char* value) { +void MySQL_Variables::server_set_value(MySQL_Session* session, int idx, const char* value) { assert(session); assert(session->mybe); assert(session->mybe->server_myds); @@ -219,7 +221,7 @@ void MySQL_Variables::server_set_value(Client_Session* session, int idx, const c session->mybe->server_myds->myconn->reorder_dynamic_variables_idx(); } -const char* MySQL_Variables::server_get_value(Client_Session* session, int idx) const { +const char* MySQL_Variables::server_get_value(MySQL_Session* session, int idx) const { assert(session); assert(session->mybe); assert(session->mybe->server_myds); @@ -227,7 +229,7 @@ const char* MySQL_Variables::server_get_value(Client_Session* session, int idx) return session->mybe->server_myds->myconn->variables[idx].value; } -uint32_t MySQL_Variables::server_get_hash(Client_Session* session, int idx) const { +uint32_t MySQL_Variables::server_get_hash(MySQL_Session* session, int idx) const { assert(session); assert(session->mybe); assert(session->mybe->server_myds); @@ -236,6 +238,11 @@ uint32_t MySQL_Variables::server_get_hash(Client_Session* session, int idx) cons } bool MySQL_Variables::update_variable(Client_Session* session, session_status status, int &_rc) { + assert(session->session_type==PROXYSQL_SESSION_MYSQL); + return update_variable((MySQL_Session *)session, status, _rc); +} + +bool MySQL_Variables::update_variable(MySQL_Session* session, session_status status, int &_rc) { int idx = SQL_NAME_LAST_HIGH_WM; if (session->status == SETTING_VARIABLE) { // if status is SETTING_VARIABLE , what variable needs to be changed is defined in changing_variable_idx @@ -253,6 +260,11 @@ bool MySQL_Variables::update_variable(Client_Session* session, session_status st } bool MySQL_Variables::verify_variable(Client_Session* session, int idx) const { + assert(session->session_type==PROXYSQL_SESSION_MYSQL); + return verify_variable((MySQL_Session *)session, idx); +} + +bool MySQL_Variables::verify_variable(MySQL_Session* session, int idx) const { auto ret = false; if (likely(verifiers[idx])) { auto client_hash = session->client_myds->myconn->var_hash[idx]; @@ -264,10 +276,10 @@ bool MySQL_Variables::verify_variable(Client_Session* session, int idx) const { return ret; } -bool validate_charset(Client_Session* session, int idx, int &_rc) { +bool validate_charset(MySQL_Session* session, int idx, int &_rc) { if (idx == SQL_CHARACTER_SET || idx == SQL_CHARACTER_SET_CLIENT || idx == SQL_CHARACTER_SET_RESULTS || idx == SQL_CHARACTER_SET_CONNECTION || idx == SQL_CHARACTER_SET_DATABASE || idx == SQL_COLLATION_CONNECTION) { - ProxySQL_Data_Stream *myds = session->mybe->server_myds; + MySQL_Data_Stream *myds = session->mybe->server_myds; MySQL_Connection *myconn = myds->myconn; char msg[128]; const MARIADB_CHARSET_INFO *ci = NULL; @@ -357,7 +369,7 @@ bool validate_charset(Client_Session* session, int idx, int &_rc) { return true; } -bool update_server_variable(Client_Session* session, int idx, int &_rc) { +bool update_server_variable(MySQL_Session* session, int idx, int &_rc) { bool no_quote = true; if (mysql_tracked_variables[idx].quote) no_quote = false; bool st = mysql_tracked_variables[idx].set_transaction; @@ -428,7 +440,12 @@ bool update_server_variable(Client_Session* session, int idx, int &_rc) { return ret; } -bool verify_set_names(Client_Session* session) { +bool verify_set_names(Client_Session * session) { + assert(session->session_type==PROXYSQL_SESSION_MYSQL); + return verify_set_names((MySQL_Session *)session); +} + +bool verify_set_names(MySQL_Session* session) { uint32_t client_charset_hash = mysql_variables.client_get_hash(session, SQL_CHARACTER_SET_CLIENT); if (client_charset_hash == 0) return false; @@ -482,7 +499,7 @@ bool verify_set_names(Client_Session* session) { return false; } -inline bool verify_server_variable(Client_Session* session, int idx, uint32_t client_hash, uint32_t server_hash) { +inline bool verify_server_variable(MySQL_Session* session, int idx, uint32_t client_hash, uint32_t server_hash) { if (client_hash && client_hash != server_hash) { // Edge case for set charset command, because we do not know database character set // for now we are setting connection and collation to empty @@ -518,12 +535,12 @@ inline bool verify_server_variable(Client_Session* session, int idx, uint32_t cl return false; } -bool logbin_update_server_variable(Client_Session* session, int idx, int &_rc) { +bool logbin_update_server_variable(MySQL_Session* session, int idx, int &_rc) { return session->handler_again___status_SETTING_SQL_LOG_BIN(&_rc); } -bool MySQL_Variables::parse_variable_boolean(Client_Session *sess, int idx, string& value1, bool * lock_hostgroup) { +bool MySQL_Variables::parse_variable_boolean(MySQL_Session *sess, int idx, string& value1, bool * lock_hostgroup) { proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET %s value %s\n", mysql_tracked_variables[idx].set_variable_name, value1.c_str()); int __tmp_value = -1; if ( @@ -564,7 +581,7 @@ bool MySQL_Variables::parse_variable_boolean(Client_Session *sess, int idx, stri -bool MySQL_Variables::parse_variable_number(Client_Session *sess, int idx, string& value1, bool * lock_hostgroup) { +bool MySQL_Variables::parse_variable_number(MySQL_Session *sess, int idx, string& value1, bool * lock_hostgroup) { int vl = strlen(value1.c_str()); const char *v = value1.c_str(); bool only_digit_chars = true; diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 9389261e0..62813b9dc 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -6,7 +6,6 @@ #include #include #include "MySQL_HostGroups_Manager.h" -#include "proxysql_admin.h" #include "re2/re2.h" #include "re2/regexp.h" #include "proxysql.h" @@ -15,8 +14,11 @@ #include "proxysql_utils.h" #include "prometheus_helpers.h" #include "cpp.h" +#include "MySQL_Session.h" +#include "proxysql_admin.h" #include "ProxySQL_Data_Stream.h" +#include "MySQL_Data_Stream.h" #include "query_processor.h" #include "ProxySQL_HTTP_Server.hpp" // HTTP server #include "MySQL_Authentication.hpp" @@ -1171,9 +1173,12 @@ int ProxySQL_Test___PurgeDigestTable(bool async_purge, bool parallel, char **msg int ProxySQL_Test___GenerateRandomQueryInDigestTable(int n) { //unsigned long long queries=n; //queries *= 1000; - Client_Session *sess = new Client_Session(); - - sess->client_myds = new ProxySQL_Data_Stream(); + MySQL_Session *sess = new MySQL_Session(); + // When the session is destroyed, client_connections is automatically decreased. + // Because this is not a real connection, we artificially increase + // client_connections + __sync_fetch_and_add(&MyHGM->status.client_connections,1); + sess->client_myds = new MySQL_Data_Stream(); sess->client_myds->fd=0; sess->client_myds->init(MYDS_FRONTEND, sess, sess->client_myds->fd); MySQL_Connection *myconn=new MySQL_Connection(); @@ -1393,7 +1398,7 @@ static admin_main_loop_listeners S_amll; -bool admin_handler_command_kill_connection(char *query_no_space, unsigned int query_no_space_length, Client_Session *sess, ProxySQL_Admin *pa) { +bool admin_handler_command_kill_connection(char *query_no_space, unsigned int query_no_space_length, MySQL_Session *sess, ProxySQL_Admin *pa) { uint32_t id=atoi(query_no_space+16); proxy_debug(PROXY_DEBUG_ADMIN, 4, "Trying to kill session %u\n", id); bool rc=GloPWTH->kill_session(id); @@ -1412,7 +1417,7 @@ bool admin_handler_command_kill_connection(char *query_no_space, unsigned int qu * returns false if the command is a valid one and is processed * return true if the command is not a valid one and needs to be executed by SQLite (that will return an error) */ -bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_space_length, Client_Session *sess, ProxySQL_Admin *pa) { +bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_space_length, MySQL_Session *sess, ProxySQL_Admin *pa) { if (!(strncasecmp("PROXYSQL CLUSTER_NODE_UUID ", query_no_space, strlen("PROXYSQL CLUSTER_NODE_UUID ")))) { int l = strlen("PROXYSQL CLUSTER_NODE_UUID "); if (sess->client_myds->addr.port == 0) { @@ -1750,7 +1755,7 @@ bool is_valid_global_variable(const char *var_name) { // multiple variables at once. // // It modifies the original query. -bool admin_handler_command_set(char *query_no_space, unsigned int query_no_space_length, Client_Session *sess, ProxySQL_Admin *pa, char **q, unsigned int *ql) { +bool admin_handler_command_set(char *query_no_space, unsigned int query_no_space_length, MySQL_Session *sess, ProxySQL_Admin *pa, char **q, unsigned int *ql) { if (!strstr(query_no_space,(char *)"password")) { // issue #599 proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received command %s\n", query_no_space); if (strncasecmp(query_no_space,(char *)"set autocommit",strlen((char *)"set autocommit"))) { @@ -1809,7 +1814,7 @@ bool admin_handler_command_set(char *query_no_space, unsigned int query_no_space /* Note: * This function can modify the original query */ -bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query_no_space_length, Client_Session *sess, ProxySQL_Admin *pa, char **q, unsigned int *ql) { +bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query_no_space_length, MySQL_Session *sess, ProxySQL_Admin *pa, char **q, unsigned int *ql) { proxy_debug(PROXY_DEBUG_ADMIN, 5, "Received command %s\n", query_no_space); #ifdef DEBUG @@ -3652,8 +3657,8 @@ std::string timediff_timezone_offset() { return time_zone_offset; } -void admin_session_handler(Client_Session *sess, void *_pa, PtrSize_t *pkt) { - +void admin_session_handler(Client_Session *c_sess, void *_pa, PtrSize_t *pkt) { + MySQL_Session *sess = (MySQL_Session *)c_sess; ProxySQL_Admin *pa=(ProxySQL_Admin *)_pa; bool needs_vacuum = false; char *error=NULL; @@ -5199,11 +5204,11 @@ void *child_mysql(void *arg) { mysql_thr->curtime=monotonic_time(); GloQPro->init_thread(); mysql_thr->refresh_variables(); - Client_Session *sess=mysql_thr->create_new_session_and_client_data_stream(client); + MySQL_Session *sess = mysql_thr->create_new_session_and_client_mysql_data_stream(client); sess->thread=mysql_thr; sess->session_type = PROXYSQL_SESSION_ADMIN; sess->handler_function=admin_session_handler; - ProxySQL_Data_Stream *myds=sess->client_myds; + MySQL_Data_Stream *myds=sess->client_myds; sess->start_time=mysql_thr->curtime; diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index 270edf1ee..8a2efe420 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -9,6 +9,8 @@ #include "ProxySQL_Cluster.hpp" #include "MySQL_LDAP_Authentication.hpp" +#include "proxysql_admin.h" + #ifdef DEBUG #define DEB "_DEBUG" #else diff --git a/lib/ProxySQL_Data_Stream.cpp b/lib/ProxySQL_Data_Stream.cpp index 8df2baecc..e9a755cd6 100644 --- a/lib/ProxySQL_Data_Stream.cpp +++ b/lib/ProxySQL_Data_Stream.cpp @@ -160,7 +160,7 @@ static enum sslstatus get_sslstatus(SSL* ssl, int n) } } - +/* void ProxySQL_Data_Stream::queue_encrypted_bytes(const char *buf, size_t len) { ssl_write_buf = (char*)realloc(ssl_write_buf, ssl_write_len + len); memcpy(ssl_write_buf + ssl_write_len, buf, len); @@ -219,7 +219,7 @@ enum sslstatus ProxySQL_Data_Stream::do_ssl_handshake() { } status = get_sslstatus(ssl, n); //proxy_info("SSL status = %d\n", status); - /* Did SSL request to write bytes? */ + // Did SSL request to write bytes? if (status == SSLSTATUS_WANT_IO) { //proxy_info("SSL status is WANT_IO %d\n", status); do { @@ -236,6 +236,7 @@ enum sslstatus ProxySQL_Data_Stream::do_ssl_handshake() { } return status; } +*/ // Constructor ProxySQL_Data_Stream::ProxySQL_Data_Stream() { @@ -251,11 +252,12 @@ ProxySQL_Data_Stream::ProxySQL_Data_Stream() { proxy_addr.port=0; sess=NULL; +/* mysql_real_query.pkt.ptr=NULL; mysql_real_query.pkt.size=0; mysql_real_query.QueryPtr=NULL; mysql_real_query.QuerySize=0; - +*/ query_retries_on_failure=0; connect_retries_on_failure=0; max_connect_time=0; @@ -273,10 +275,10 @@ ProxySQL_Data_Stream::ProxySQL_Data_Stream() { resultset=NULL; queue_init(queueIN,QUEUE_T_DEFAULT_SIZE); queue_init(queueOUT,QUEUE_T_DEFAULT_SIZE); - mybe=NULL; +// mybe=NULL; active=1; mypolls=NULL; - myconn=NULL; // 20141011 +// myconn=NULL; // 20141011 DSS=STATE_NOT_CONNECTED; encrypted=false; switching_auth_stage = 0; @@ -288,12 +290,14 @@ ProxySQL_Data_Stream::ProxySQL_Data_Stream() { ssl_write_len = 0; ssl_write_buf = NULL; net_failure=false; +/* CompPktIN.pkt.ptr=NULL; CompPktIN.pkt.size=0; CompPktIN.partial=0; CompPktOUT.pkt.ptr=NULL; CompPktOUT.pkt.size=0; CompPktOUT.partial=0; +*/ multi_pkt.ptr=NULL; multi_pkt.size=0; @@ -301,7 +305,7 @@ ProxySQL_Data_Stream::ProxySQL_Data_Stream() { statuses.myconnpoll_get = 0; statuses.myconnpoll_put = 0; - com_field_wild=NULL; +// com_field_wild=NULL; } // Destructor @@ -322,13 +326,14 @@ ProxySQL_Data_Stream::~ProxySQL_Data_Stream() { proxy_addr.addr=NULL; } +/* free_mysql_real_query(); if (com_field_wild) { free(com_field_wild); com_field_wild=NULL; } - +*/ proxy_debug(PROXY_DEBUG_NET,1, "Shutdown Data Stream. Session=%p, DataStream=%p\n" , sess, this); PtrSize_t pkt; if (PSarrayIN) { @@ -355,6 +360,7 @@ ProxySQL_Data_Stream::~ProxySQL_Data_Stream() { if (mypolls) mypolls->remove_index_fast(poll_fds_idx); +/* if (fd>0) { // // Changing logic here. The socket should be closed only if it is not a backend if (myds_type==MYDS_FRONTEND) { @@ -377,21 +383,14 @@ ProxySQL_Data_Stream::~ProxySQL_Data_Stream() { SSL_shutdown(ssl); } if (ssl) SSL_free(ssl); -/* - SSL_free() should also take care of these - if (rbio_ssl) { - BIO_free(rbio_ssl); - } - if (wbio_ssl) { - BIO_free(wbio_ssl); - } -*/ } +*/ if (multi_pkt.ptr) { l_free(multi_pkt.size,multi_pkt.ptr); multi_pkt.ptr=NULL; multi_pkt.size=0; } +/* if (CompPktIN.pkt.ptr) { l_free(CompPktIN.pkt.size,CompPktIN.pkt.ptr); CompPktIN.pkt.ptr=NULL; @@ -402,12 +401,14 @@ ProxySQL_Data_Stream::~ProxySQL_Data_Stream() { CompPktOUT.pkt.ptr=NULL; CompPktOUT.pkt.size=0; } +*/ if (x509_subject_alt_name) { free(x509_subject_alt_name); x509_subject_alt_name=NULL; } } +/* // this function initializes a ProxySQL_Data_Stream void ProxySQL_Data_Stream::init() { if (myds_type!=MYDS_LISTENER) { @@ -422,7 +423,7 @@ void ProxySQL_Data_Stream::init() { queue_destroy(queueOUT); } } - +*/ void ProxySQL_Data_Stream::reinit_queues() { if (queueIN.buffer==NULL) queue_init(queueIN,QUEUE_T_DEFAULT_SIZE); @@ -430,6 +431,7 @@ void ProxySQL_Data_Stream::reinit_queues() { queue_init(queueOUT,QUEUE_T_DEFAULT_SIZE); } +/* // this function initializes a ProxySQL_Data_Stream with arguments void ProxySQL_Data_Stream::init(enum MySQL_DS_type _type, Client_Session *_sess, int _fd) { myds_type=_type; @@ -440,6 +442,7 @@ void ProxySQL_Data_Stream::init(enum MySQL_DS_type _type, Client_Session *_sess, //if (myconn==NULL) myconn = new MySQL_Connection(); if (myconn) myconn->fd=fd; } +*/ // Soft shutdown of socket : it only deactivate the data stream // TODO: should check the status of the data stream, and identify if it is safe to reconnect or if the session should be destroyed @@ -469,6 +472,7 @@ void ProxySQL_Data_Stream::shut_hard() { } } +/* void ProxySQL_Data_Stream::check_data_flow() { if ( (PSarrayIN->len || queue_data(queueIN) ) && ( PSarrayOUT->len || queue_data(queueOUT) ) ){ // there is data at both sides of the data stream: this is considered a fatal error @@ -525,20 +529,6 @@ int ProxySQL_Data_Stream::read_from_net() { } } } else { -/* - if (!SSL_is_init_finished(ssl)) { - int ret = SSL_do_handshake(ssl); - int ret2; - if (ret != 1) { - //ERR_print_errors_fp(stderr); - ret2 = SSL_get_error(ssl, ret); - fprintf(stderr,"%d\n",ret2); - } - return 0; - } else { - r = SSL_read (ssl, queue_w_ptr(queueIN), s); - } -*/ PROXY_TRACE(); if (s < MY_SSL_BUFFER) { return 0; // no enough space for reads @@ -587,14 +577,6 @@ int ProxySQL_Data_Stream::read_from_net() { //proxy_info("Read %d bytes from SSL\n", r); if (n2 > 0) { } -/* - do { - n2 = SSL_read(ssl, buf2, sizeof(buf2)); - if (n2 > 0) { - - } - } while (n > 0); -*/ status = get_sslstatus(ssl, n2); //proxy_info("SSL status = %d\n", status); if (status == SSLSTATUS_WANT_IO) { @@ -745,7 +727,7 @@ int ProxySQL_Data_Stream::write_to_net() { } return bytes_io; } - +*/ bool ProxySQL_Data_Stream::available_data_out() { int buflen=queue_data(queueOUT); if (buflen || PSarrayOUT->len) { @@ -760,6 +742,7 @@ void ProxySQL_Data_Stream::remove_pollout() { _pollfd->events = 0; } +/* void ProxySQL_Data_Stream::set_pollout() { struct pollfd *_pollfd; _pollfd=&mypolls->fds[poll_fds_idx]; @@ -799,18 +782,6 @@ void ProxySQL_Data_Stream::set_pollout() { int ProxySQL_Data_Stream::write_to_net_poll() { int rc=0; if (active==0) return rc; -/* - if (encrypted && !SSL_is_init_finished(ssl)) { - int ret = SSL_do_handshake(ssl); - int ret2; - if (ret != 1) { - //ERR_print_errors_fp(stderr); - ret2 = SSL_get_error(ssl, ret); - fprintf(stderr,"%d\n",ret2); - } - return 0; - } -*/ if (encrypted) { if (!SSL_is_init_finished(ssl)) { //proxy_info("SSL_is_init_finished completed: NO!\n"); @@ -822,15 +793,6 @@ int ProxySQL_Data_Stream::write_to_net_poll() { } else { //proxy_info("SSL_is_init_finished completed: YES\n"); } -/* - if (!SSL_is_init_finished(ssl)) { - proxy_info("SSL_is_init_finished completed: NO!\n"); - if (fd>0 && sess->session_type == PROXYSQL_SESSION_MYSQL) { - set_pollout(); - return 0; - } - } -*/ //proxy_info("ssl_write_len: %u\n", ssl_write_len); if (ssl_write_len) { int n = write(fd, ssl_write_buf, ssl_write_len); @@ -887,7 +849,6 @@ int ProxySQL_Data_Stream::write_to_net_poll() { } return rc; } - int ProxySQL_Data_Stream::read_pkts() { int rc=0; int r=0; @@ -1195,8 +1156,8 @@ void ProxySQL_Data_Stream::generate_compressed_packet() { l_free(p2.size,p2.ptr); } } - - +*/ +/* int ProxySQL_Data_Stream::array2buffer() { int ret=0; unsigned int idx=0; @@ -1275,7 +1236,8 @@ __exit_array2buffer: } return ret; } - +*/ +/* unsigned char * ProxySQL_Data_Stream::resultset2buffer(bool del) { unsigned int i; unsigned int l=0; @@ -1322,27 +1284,23 @@ void ProxySQL_Data_Stream::buffer2resultset(unsigned char *ptr, unsigned int siz memcpy((char *)buff + (bl-bf), __ptr, l); bf -= l; __ptr+=l; -/* - l=hdr.pkt_length+sizeof(mysql_hdr); - pkt=l_alloc(l); - memcpy(pkt,__ptr,l); - resultset->add(pkt,l); - __ptr+=l; -*/ } if (buff) { // last buffer to add resultset->add(buff,bl-bf); } }; +*/ +/* int ProxySQL_Data_Stream::array2buffer_full() { int rc=0; int r=0; while((r=array2buffer())) rc+=r; return rc; } - +*/ +/* int ProxySQL_Data_Stream::assign_fd_from_mysql_conn() { assert(myconn); //proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Sess=%p, myds=%p, oldFD=%d, newFD=%d\n", this->sess, this, fd, myconn->myconn.net.fd); @@ -1359,6 +1317,7 @@ void ProxySQL_Data_Stream::unplug_backend() { mypolls=NULL; fd=0; } +*/ void ProxySQL_Data_Stream::set_net_failure() { proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Sess=%p, myds=%p , myds_type:%d\n", this->sess, this, myds_type); @@ -1375,6 +1334,7 @@ void ProxySQL_Data_Stream::setDSS_STATE_QUERY_SENT_NET() { DSS=STATE_QUERY_SENT_NET; } +/* void ProxySQL_Data_Stream::return_MySQL_Connection_To_Pool() { MySQL_Connection *mc=myconn; mc->last_time_used=sess->thread->curtime; @@ -1419,12 +1379,14 @@ void ProxySQL_Data_Stream::free_mysql_real_query() { mysql_real_query.end(); } } +*/ void ProxySQL_Data_Stream::destroy_queues() { queue_destroy(queueIN); queue_destroy(queueOUT); } +/* void ProxySQL_Data_Stream::destroy_MySQL_Connection_From_Pool(bool sq) { MySQL_Connection *mc=myconn; mc->last_time_used=sess->thread->curtime; @@ -1433,6 +1395,7 @@ void ProxySQL_Data_Stream::destroy_MySQL_Connection_From_Pool(bool sq) { mc->send_quit=sq; MyHGM->destroy_MyConn_from_pool(mc); } +*/ bool ProxySQL_Data_Stream::data_in_rbio() { if (rbio_ssl->num_write > rbio_ssl->num_read) { diff --git a/lib/ProxySQL_HTTP_Server.cpp b/lib/ProxySQL_HTTP_Server.cpp index 601aafca9..0731857bd 100644 --- a/lib/ProxySQL_HTTP_Server.cpp +++ b/lib/ProxySQL_HTTP_Server.cpp @@ -10,6 +10,8 @@ #include "SQLite3_Server.h" #include "MySQL_Authentication.hpp" +#include "proxysql_admin.h" + #include #include #include diff --git a/lib/ProxySQL_RESTAPI_Server.cpp b/lib/ProxySQL_RESTAPI_Server.cpp index 58fde5aeb..529adb2bf 100644 --- a/lib/ProxySQL_RESTAPI_Server.cpp +++ b/lib/ProxySQL_RESTAPI_Server.cpp @@ -8,6 +8,8 @@ #include "ProxySQL_RESTAPI_Server.hpp" #include "proxysql_utils.h" +#include "proxysql_admin.h" + using namespace httpserver; diff --git a/lib/ProxyWorker_Thread.cpp b/lib/ProxyWorker_Thread.cpp index 50ce363fe..d4b03adec 100644 --- a/lib/ProxyWorker_Thread.cpp +++ b/lib/ProxyWorker_Thread.cpp @@ -12,6 +12,8 @@ #include "re2/regexp.h" #include "ProxySQL_Data_Stream.h" +#include "MySQL_Data_Stream.h" +#include "MySQL_Session.h" #include "query_processor.h" #include "StatCounters.h" #include "MySQL_PreparedStatement.h" @@ -2801,11 +2803,11 @@ ProxyWorker_Thread::~ProxyWorker_Thread() { } -Client_Session * ProxyWorker_Thread::create_new_session_and_client_data_stream(int _fd) { +MySQL_Session * ProxyWorker_Thread::create_new_session_and_client_mysql_data_stream(int _fd) { int arg_on=1; - Client_Session *sess=new Client_Session; + MySQL_Session *sess=new MySQL_Session; register_session(sess); // register session - sess->client_myds = new ProxySQL_Data_Stream(); + sess->client_myds = new MySQL_Data_Stream(); sess->client_myds->fd=_fd; setsockopt(sess->client_myds->fd, IPPROTO_TCP, TCP_NODELAY, (char *) &arg_on, sizeof(arg_on)); @@ -2945,9 +2947,9 @@ void ProxyWorker_Thread::run___get_multiple_idle_connections(int& num_idles) { int i; num_idles=MyHGM->get_multiple_idle_connections(-1, curtime-mysql_thread___ping_interval_server_msec*1000, my_idle_conns, SESSIONS_FOR_CONNECTIONS_HANDLER); for (i=0; imybe=sess->find_or_create_mysql_backend(mc->parent->myhgc->hid); myds=sess->mybe->server_myds; @@ -3012,7 +3014,7 @@ void ProxyWorker_Thread::ProcessAllMyDS_BeforePoll() { } myds->revents=0; if (myds->myds_type!=MYDS_LISTENER) { - configure_pollout(myds, n); + configure_pollout((MySQL_Data_Stream *)myds, n); } } proxy_debug(PROXY_DEBUG_NET,1,"Poll for DataStream=%p will be called with FD=%d and events=%d\n", mypolls.myds[n], mypolls.fds[n].fd, mypolls.fds[n].events); @@ -3355,10 +3357,10 @@ void ProxyWorker_Thread::idle_thread_to_kill_idle_sessions() { } for (i=0;ilen; i++) { uint32_t sess_pos=mysess_idx; - Client_Session *mysess=(Client_Session *)mysql_sessions->index(sess_pos); + MySQL_Session *mysess=(MySQL_Session *)mysql_sessions->index(sess_pos); if (mysess->idle_since < min_idle || mysess->killed==true) { mysess->killed=true; - ProxySQL_Data_Stream *tmp_myds=mysess->client_myds; + MySQL_Data_Stream *tmp_myds=mysess->client_myds; int dsidx=tmp_myds->poll_fds_idx; //fprintf(stderr,"Removing session %p, DS %p idx %d\n",mysess,tmp_myds,dsidx); mypolls.remove_index_fast(dsidx); @@ -3368,7 +3370,7 @@ void ProxyWorker_Thread::idle_thread_to_kill_idle_sessions() { sessmap.erase(mysess->thread_session_id); if (mysql_sessions->len > 1) { // take the last element and adjust the map - Client_Session *mysess_last=(Client_Session *)mysql_sessions->index(mysql_sessions->len-1); + MySQL_Session *mysess_last=(MySQL_Session *)mysql_sessions->index(mysql_sessions->len-1); if (mysess->thread_session_id != mysess_last->thread_session_id) sessmap[mysess_last->thread_session_id]=sess_pos; } @@ -3385,8 +3387,8 @@ void ProxyWorker_Thread::idle_thread_prepares_session_to_send_to_worker_thread(i if (events[i].events) { uint32_t sess_thr_id=events[i].data.u32; uint32_t sess_pos=sessmap[sess_thr_id]; - Client_Session *mysess=(Client_Session *)mysql_sessions->index(sess_pos); - ProxySQL_Data_Stream *tmp_myds=mysess->client_myds; + MySQL_Session *mysess=(MySQL_Session *)mysql_sessions->index(sess_pos); + MySQL_Data_Stream *tmp_myds=mysess->client_myds; int dsidx=tmp_myds->poll_fds_idx; //fprintf(stderr,"Removing session %p, DS %p idx %d\n",mysess,tmp_myds,dsidx); mypolls.remove_index_fast(dsidx); @@ -3473,7 +3475,7 @@ void ProxyWorker_Thread::worker_thread_gets_sessions_from_idle_thread() { while (myexchange.resume_mysql_sessions->len) { Client_Session *mysess=(Client_Session *)myexchange.resume_mysql_sessions->remove_index_fast(0); register_session(mysess, false); - ProxySQL_Data_Stream *myds=mysess->client_myds; + ProxySQL_Data_Stream *myds=((MySQL_Session *)mysess)->client_myds; mypolls.add(POLLIN, myds->fd, myds, monotonic_time()); } } @@ -3528,10 +3530,11 @@ bool ProxyWorker_Thread::process_data_on_mysql_data_stream(ProxySQL_Data_Stream // // this can happen, for example, with a low wait_timeout and running transaction if (myds->sess->status==WAITING_CLIENT_DATA) { - if (myds->myconn->async_state_machine==ASYNC_IDLE) { - proxy_warning("Detected broken idle connection on %s:%d\n", myds->myconn->parent->address, myds->myconn->parent->port); - myds->destroy_MySQL_Connection_From_Pool(false); - myds->sess->set_unhealthy(); + MySQL_Data_Stream *_myds = (MySQL_Data_Stream *)myds; + if (_myds->myconn->async_state_machine==ASYNC_IDLE) { + proxy_warning("Detected broken idle connection on %s:%d\n", _myds->myconn->parent->address, _myds->myconn->parent->port); + _myds->destroy_MySQL_Connection_From_Pool(false); + _myds->sess->set_unhealthy(); return false; } } @@ -3543,14 +3546,15 @@ bool ProxyWorker_Thread::process_data_on_mysql_data_stream(ProxySQL_Data_Stream // only if we aren't using MariaDB Client Library int rb = 0; do { - rb = myds->read_from_net(); - if (rb > 0 && myds->myds_type == MYDS_FRONTEND) { + MySQL_Data_Stream *_myds = (MySQL_Data_Stream *)myds; + rb = _myds->read_from_net(); + if (rb > 0 && _myds->myds_type == MYDS_FRONTEND) { status_variables.stvar[st_var_queries_frontends_bytes_recv] += rb; } - myds->read_pkts(); + _myds->read_pkts(); - if (rb > 0 && myds->myds_type == MYDS_BACKEND) { - if (myds->sess->session_fast_forward) { + if (rb > 0 && _myds->myds_type == MYDS_BACKEND) { + if (_myds->sess->session_fast_forward) { struct pollfd _fds; nfds_t _nfds = 1; _fds.fd = mypolls.fds[n].fd; @@ -3559,7 +3563,7 @@ bool ProxyWorker_Thread::process_data_on_mysql_data_stream(ProxySQL_Data_Stream int _rc = poll(&_fds, _nfds, 0); if ((_rc > 0) && _fds.revents == POLLIN) { // there is more data - myds->revents = _fds.revents; + _myds->revents = _fds.revents; } else { rb = 0; // exit loop } @@ -3568,10 +3572,10 @@ bool ProxyWorker_Thread::process_data_on_mysql_data_stream(ProxySQL_Data_Stream } } else { bool set_rb_zero = true; - if (rb > 0 && myds->myds_type == MYDS_FRONTEND) { - if (myds->encrypted == true) { - if (SSL_is_init_finished(myds->ssl)) { - if (myds->data_in_rbio()) { + if (rb > 0 && _myds->myds_type == MYDS_FRONTEND) { + if (_myds->encrypted == true) { + if (SSL_is_init_finished(_myds->ssl)) { + if (_myds->data_in_rbio()) { set_rb_zero = false; } } @@ -3584,7 +3588,8 @@ bool ProxyWorker_Thread::process_data_on_mysql_data_stream(ProxySQL_Data_Stream } else { if (mypolls.fds[n].revents) { - myds->myconn->handler(mypolls.fds[n].revents); + MySQL_Data_Stream *_myds = (MySQL_Data_Stream *)myds; + _myds->myconn->handler(mypolls.fds[n].revents); } } if ( (mypolls.fds[n].events & POLLOUT) @@ -3593,20 +3598,21 @@ bool ProxyWorker_Thread::process_data_on_mysql_data_stream(ProxySQL_Data_Stream ) { myds->set_net_failure(); } - myds->check_data_flow(); + ((MySQL_Data_Stream *)myds)->check_data_flow(); } if (myds->active==0) { - if (myds->sess->client_myds==myds) { - proxy_debug(PROXY_DEBUG_NET,1, "Session=%p, DataStream=%p -- Deleting FD %d\n", myds->sess, myds, myds->fd); - myds->sess->set_unhealthy(); + MySQL_Session *mysess = (MySQL_Session *)(myds->sess); + if (mysess->client_myds==myds) { + proxy_debug(PROXY_DEBUG_NET,1, "Session=%p, DataStream=%p -- Deleting FD %d\n", mysess, myds, myds->fd); + mysess->set_unhealthy(); } else { // if this is a backend with fast_forward, set unhealthy // if this is a backend without fast_forward, do not set unhealthy: it will be handled by client library - if (myds->sess->session_fast_forward) { // if fast forward + if (mysess->session_fast_forward) { // if fast forward if (myds->myds_type==MYDS_BACKEND) { // and backend - myds->sess->set_unhealthy(); // set unhealthy + mysess->set_unhealthy(); // set unhealthy } } } @@ -3664,7 +3670,7 @@ void ProxyWorker_Thread::ProcessAllSessions_CompletedMirrorSession(unsigned int& // this function was inline in ProxyWorker_Thread::process_all_sessions() -void ProxyWorker_Thread::ProcessAllSessions_MaintenanceLoop(Client_Session *sess, unsigned long long sess_time, unsigned int& total_active_transactions_) { +void ProxyWorker_Thread::ProcessAllSessions_MaintenanceLoop(MySQL_Session *sess, unsigned long long sess_time, unsigned int& total_active_transactions_) { unsigned int numTrx=0; sess->active_transactions=sess->NumActiveTransactions(); { @@ -3759,7 +3765,11 @@ void ProxyWorker_Thread::process_all_sessions() { ProcessAllSessions_SortingSessions(); } for (n=0; nlen; n++) { - Client_Session *sess=(Client_Session *)mysql_sessions->index(n); + Client_Session *c_sess=(Client_Session *)mysql_sessions->index(n); + if (c_sess->session_type != PROXYSQL_SESSION_MYSQL) { + continue; + } + MySQL_Session *sess = (MySQL_Session *)c_sess; #ifdef DEBUG if(sess==sess_stopat) { sess_stopat=sess; @@ -3787,7 +3797,9 @@ void ProxyWorker_Thread::process_all_sessions() { if (idle_maintenance_thread==false) #endif // IDLE_THREADS { - ProcessAllSessions_MaintenanceLoop(sess, sess_time, total_active_transactions_); + if (sess->session_type==PROXYSQL_SESSION_MYSQL) { + ProcessAllSessions_MaintenanceLoop(sess, sess_time, total_active_transactions_); + } } #ifdef IDLE_THREADS else @@ -4172,7 +4184,7 @@ void ProxyWorker_Thread::listener_handle_new_connection(ProxySQL_Data_Stream *my // create a new client connection mypolls.fds[n].revents=0; - Client_Session *sess=create_new_session_and_client_data_stream(c); + MySQL_Session *sess=create_new_session_and_client_mysql_data_stream(c); __sync_add_and_fetch(&MyHGM->status.client_connections_created,1); if (__sync_add_and_fetch(&MyHGM->status.client_connections,1) > mysql_thread___max_connections) { sess->max_connections_reached=true; @@ -4621,7 +4633,11 @@ SQLite3_result * ProxyWorker_Threads_Handler::SQL3_Processlist() { pthread_mutex_lock(&thr->thread_mutex); unsigned int j; for (j=0; jmysql_sessions->len; j++) { - Client_Session *sess=(Client_Session *)thr->mysql_sessions->pdata[j]; + Client_Session *c_sess=(Client_Session *)thr->mysql_sessions->pdata[j]; + if (c_sess->session_type != PROXYSQL_SESSION_MYSQL) { + continue; + } + MySQL_Session *sess = (MySQL_Session *)c_sess; if (sess->client_myds) { char buf[1024]; char **pta=(char **)malloc(sizeof(char *)*colnum); @@ -5231,9 +5247,13 @@ void ProxyWorker_Thread::Get_Memory_Stats() { } -MySQL_Connection * ProxyWorker_Thread::get_MyConn_local(unsigned int _hid, Client_Session *sess, char *gtid_uuid, uint64_t gtid_trxid, int max_lag_ms) { +MySQL_Connection * ProxyWorker_Thread::get_MyConn_local(unsigned int _hid, Client_Session *_sess, char *gtid_uuid, uint64_t gtid_trxid, int max_lag_ms) { // some sanity check - if (sess == NULL) return NULL; + if (_sess == NULL) return NULL; + if (_sess->session_type != PROXYSQL_SESSION_MYSQL) { + return NULL; + } + MySQL_Session *sess = (MySQL_Session *)_sess; if (sess->client_myds == NULL) return NULL; if (sess->client_myds->myconn == NULL) return NULL; if (sess->client_myds->myconn->userinfo == NULL) return NULL; @@ -5360,7 +5380,11 @@ void ProxyWorker_Thread::Scan_Sessions_to_Kill_All() { void ProxyWorker_Thread::Scan_Sessions_to_Kill(PtrArray *mysess) { for (unsigned int n=0; nlen && ( kq.conn_ids.size() + kq.query_ids.size() ) ; n++) { - Client_Session *_sess=(Client_Session *)mysess->index(n); + Client_Session *c_sess=(Client_Session *)mysess->index(n); + if (c_sess->session_type != PROXYSQL_SESSION_MYSQL) { + continue; + } + MySQL_Session *_sess = (MySQL_Session *)c_sess; bool cont=true; for (std::vector::iterator it=kq.conn_ids.begin(); cont && it!=kq.conn_ids.end(); ++it) { thr_id_usr *t = *it; @@ -5405,9 +5429,13 @@ bool ProxyWorker_Thread::move_session_to_idle_mysql_sessions(ProxySQL_Data_Strea if (_tmp_idle < ( (curtime > (unsigned int)mysql_thread___session_idle_ms * 1000) ? (curtime - mysql_thread___session_idle_ms * 1000) : 0)) { // make sure data stream has no pending data out and session is not throttled (#1939) // because epoll thread does not handle data stream with data out - if (myds->sess->client_myds == myds && !myds->available_data_out() && myds->sess->pause_until <= curtime) { + if (myds->sess->session_type != PROXYSQL_SESSION_MYSQL) { + return false; + } + MySQL_Session *mysess = (MySQL_Session *)(myds->sess); + if (mysess->client_myds == myds && !myds->available_data_out() && mysess->pause_until <= curtime) { //unsigned int j; - bool has_backends = myds->sess->has_any_backend(); + bool has_backends = mysess->has_any_backend(); /* for (j=0;jsess->mybes->len;j++) { MySQL_Backend *tmp_mybe=(MySQL_Backend *)myds->sess->mybes->index(j); @@ -5418,14 +5446,14 @@ bool ProxyWorker_Thread::move_session_to_idle_mysql_sessions(ProxySQL_Data_Strea } */ if (has_backends==false) { - unsigned long long idle_since = curtime - myds->sess->IdleTime(); + unsigned long long idle_since = curtime - mysess->IdleTime(); mypolls.remove_index_fast(n); myds->mypolls=NULL; - unsigned int i = find_session_idx_in_mysql_sessions(myds->sess); - myds->sess->thread=NULL; + unsigned int i = find_session_idx_in_mysql_sessions(mysess); + mysess->thread=NULL; unregister_session(i); - myds->sess->idle_since = idle_since; - idle_mysql_sessions->add(myds->sess); + mysess->idle_since = idle_since; + idle_mysql_sessions->add(mysess); return true; } } @@ -5435,10 +5463,14 @@ bool ProxyWorker_Thread::move_session_to_idle_mysql_sessions(ProxySQL_Data_Strea #endif // IDLE_THREADS bool ProxyWorker_Thread::set_backend_to_be_skipped_if_frontend_is_slow(ProxySQL_Data_Stream *myds, unsigned int n) { - if (myds->sess && myds->sess->client_myds && myds->sess->mirror==false) { + if (myds->sess->session_type != PROXYSQL_SESSION_MYSQL) { + return false; + } + MySQL_Session *mysess = (MySQL_Session *)(myds->sess); + if (mysess && mysess->client_myds && mysess->mirror==false) { unsigned int buffered_data=0; - buffered_data = myds->sess->client_myds->PSarrayOUT->len * RESULTSET_BUFLEN; - buffered_data += myds->sess->client_myds->resultset->len * RESULTSET_BUFLEN; + buffered_data = mysess->client_myds->PSarrayOUT->len * RESULTSET_BUFLEN; + buffered_data += mysess->client_myds->resultset->len * RESULTSET_BUFLEN; // we pause receiving from backend at mysql_thread___threshold_resultset_size * 8 // but assuming that client isn't completely blocked, we will stop checking for data // only at mysql_thread___threshold_resultset_size * 4 @@ -5454,9 +5486,9 @@ bool ProxyWorker_Thread::set_backend_to_be_skipped_if_frontend_is_slow(ProxySQL_ void ProxyWorker_Thread::idle_thread_gets_sessions_from_worker_thread() { pthread_mutex_lock(&myexchange.mutex_idles); while (myexchange.idle_mysql_sessions->len) { - Client_Session *mysess=(Client_Session *)myexchange.idle_mysql_sessions->remove_index_fast(0); + MySQL_Session *mysess=(MySQL_Session *)myexchange.idle_mysql_sessions->remove_index_fast(0); register_session(mysess, false); - ProxySQL_Data_Stream *myds=mysess->client_myds; + MySQL_Data_Stream *myds=mysess->client_myds; mypolls.add(POLLIN, myds->fd, myds, monotonic_time()); // add in epoll() struct epoll_event event; @@ -5541,15 +5573,18 @@ void ProxyWorker_Thread::check_for_invalid_fd(unsigned int n) { // check if the FD is valid if (mypolls.fds[n].revents==POLLNVAL) { // debugging output before assert - ProxySQL_Data_Stream *_myds=mypolls.myds[n]; - if (_myds) { - if (_myds->myconn) { - proxy_error("revents==POLLNVAL for FD=%d, events=%d, MyDSFD=%d, MyConnFD=%d\n", mypolls.fds[n].fd, mypolls.fds[n].events, _myds->fd, _myds->myconn->fd); - assert(mypolls.fds[n].revents!=POLLNVAL); + ProxySQL_Data_Stream *pds=mypolls.myds[n]; + if (pds) { + if (pds->sess->session_type == PROXYSQL_SESSION_MYSQL) { + MySQL_Data_Stream * _myds = (MySQL_Data_Stream *)pds; + if (_myds->myconn) { + proxy_error("revents==POLLNVAL for FD=%d, events=%d, MyDSFD=%d, MyConnFD=%d\n", mypolls.fds[n].fd, mypolls.fds[n].events, _myds->fd, _myds->myconn->fd); + assert(mypolls.fds[n].revents!=POLLNVAL); + } } } // if we reached her, we didn't assert() yet - proxy_error("revents==POLLNVAL for FD=%d, events=%d, MyDSFD=%d\n", mypolls.fds[n].fd, mypolls.fds[n].events, _myds->fd); + proxy_error("revents==POLLNVAL for FD=%d, events=%d, MyDSFD=%d\n", mypolls.fds[n].fd, mypolls.fds[n].events, pds->fd); assert(mypolls.fds[n].revents!=POLLNVAL); } } @@ -5590,13 +5625,14 @@ void ProxyWorker_Thread::tune_timeout_for_session_needs_pause(ProxySQL_Data_Stre } } -void ProxyWorker_Thread::configure_pollout(ProxySQL_Data_Stream *myds, unsigned int n) { +void ProxyWorker_Thread::configure_pollout(MySQL_Data_Stream *myds, unsigned int n) { if (myds->myds_type==MYDS_FRONTEND && myds->DSS==STATE_SLEEP && myds->sess && myds->sess->status==WAITING_CLIENT_DATA) { myds->set_pollout(); } else { if (myds->DSS > STATE_MARIADB_BEGIN && myds->DSS < STATE_MARIADB_END) { mypolls.fds[n].events = POLLIN; - if (mypolls.myds[n]->myconn->async_exit_status & MYSQL_WAIT_WRITE) + assert(myds == mypolls.myds[n]); + if (myds->myconn->async_exit_status & MYSQL_WAIT_WRITE) mypolls.fds[n].events |= POLLOUT; } else { myds->set_pollout(); diff --git a/lib/Query_Cache.cpp b/lib/Query_Cache.cpp index 5076caeb2..9e7556484 100644 --- a/lib/Query_Cache.cpp +++ b/lib/Query_Cache.cpp @@ -6,6 +6,8 @@ #include "proxysql_atomic.h" #include "SpookyV2.h" #include "prometheus_helpers.h" +#include "ProxySQL_Data_Stream.h" +#include "MySQL_Data_Stream.h" #include "MySQL_Protocol.h" #define THR_UPDATE_CNT(__a, __b, __c, __d) \ diff --git a/lib/Query_Processor.cpp b/lib/Query_Processor.cpp index 27761c80d..d7cbafb54 100644 --- a/lib/Query_Processor.cpp +++ b/lib/Query_Processor.cpp @@ -8,6 +8,8 @@ #include "MySQL_PreparedStatement.h" #include "ProxySQL_Data_Stream.h" +#include "MySQL_Data_Stream.h" +#include "MySQL_Session.h" #include "query_processor.h" #include "SpookyV2.h" @@ -1432,6 +1434,10 @@ Query_Processor_Output * Query_Processor::process_mysql_query(Client_Session *se //} pthread_rwlock_unlock(&rwlock); } + MySQL_Data_Stream * client_myds = NULL; + if (sess->session_type==PROXYSQL_SESSION_MYSQL) { + client_myds = ((MySQL_Session *)sess)->client_myds; + } QP_rule_t *qr = NULL; re2_t *re2p; int flagIN=0; @@ -1462,13 +1468,13 @@ __internal_loop: continue; } if (qr->username && strlen(qr->username)) { - if (strcmp(qr->username,sess->client_myds->myconn->userinfo->username)!=0) { + if ( client_myds!= NULL && strcmp(qr->username,client_myds->myconn->userinfo->username)!=0) { proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "query rule %d has no matching username\n", qr->rule_id); continue; } } if (qr->schemaname && strlen(qr->schemaname)) { - if (strcmp(qr->schemaname,sess->client_myds->myconn->userinfo->schemaname)!=0) { + if ( client_myds!= NULL && strcmp(qr->schemaname,client_myds->myconn->userinfo->schemaname)!=0) { proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "query rule %d has no matching schemaname\n", qr->rule_id); continue; } @@ -1476,9 +1482,9 @@ __internal_loop: // match on client address if (qr->client_addr && strlen(qr->client_addr)) { - if (sess->client_myds->addr.addr) { + if (client_myds!= NULL && client_myds->addr.addr) { if (qr->client_addr_wildcard_position == -1) { // no wildcard , old algorithm - if (strcmp(qr->client_addr,sess->client_myds->addr.addr)!=0) { + if (strcmp(qr->client_addr,client_myds->addr.addr)!=0) { proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "query rule %d has no matching client_addr\n", qr->rule_id); continue; } @@ -1486,7 +1492,7 @@ __internal_loop: // catch all! // therefore we have a match } else { // client_addr_wildcard_position > 0 - if (strncmp(qr->client_addr,sess->client_myds->addr.addr,qr->client_addr_wildcard_position)!=0) { + if (strncmp(qr->client_addr,client_myds->addr.addr,qr->client_addr_wildcard_position)!=0) { proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "query rule %d has no matching client_addr\n", qr->rule_id); continue; } @@ -1495,9 +1501,9 @@ __internal_loop: } // match on proxy_addr - if (qr->proxy_addr && strlen(qr->proxy_addr)) { - if (sess->client_myds->proxy_addr.addr) { - if (strcmp(qr->proxy_addr,sess->client_myds->proxy_addr.addr)!=0) { + if (client_myds!= NULL && qr->proxy_addr && strlen(qr->proxy_addr)) { + if (client_myds->proxy_addr.addr) { + if (strcmp(qr->proxy_addr,client_myds->proxy_addr.addr)!=0) { proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "query rule %d has no matching proxy_addr\n", qr->rule_id); continue; } @@ -1505,8 +1511,8 @@ __internal_loop: } // match on proxy_port - if (qr->proxy_port>=0) { - if (qr->proxy_port!=sess->client_myds->proxy_addr.port) { + if (client_myds!= NULL && qr->proxy_port>=0) { + if (qr->proxy_port!=client_myds->proxy_addr.port) { proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "query rule %d has no matching proxy_port\n", qr->rule_id); continue; } @@ -1701,8 +1707,8 @@ __exit_process_mysql_query: if (_thr___rules_fast_routing___keys_values) { char keybuf[256]; char * keybuf_ptr = keybuf; - const char * u = sess->client_myds->myconn->userinfo->username; - const char * s = sess->client_myds->myconn->userinfo->schemaname; + const char * u = client_myds->myconn->userinfo->username; + const char * s = client_myds->myconn->userinfo->schemaname; size_t keylen = strlen(u)+strlen(rand_del)+strlen(s)+30; // 30 is a big number if (keylen > 250) { keybuf_ptr = (char *)malloc(keylen); @@ -1742,13 +1748,13 @@ __exit_process_mysql_query: char *username = NULL; char *client_address = NULL; bool check_run = true; - if (sess->client_myds) { + if (client_myds != NULL) { check_run = false; - if (sess->client_myds->myconn && sess->client_myds->myconn->userinfo && sess->client_myds->myconn->userinfo->username) { - if (sess->client_myds->addr.addr) { + if (client_myds->myconn && client_myds->myconn->userinfo && client_myds->myconn->userinfo->username) { + if (client_myds->addr.addr) { check_run = true; - username = sess->client_myds->myconn->userinfo->username; - client_address = sess->client_myds->addr.addr; + username = client_myds->myconn->userinfo->username; + client_address = client_myds->addr.addr; pthread_mutex_lock(&global_mysql_firewall_whitelist_mutex); // FIXME // for now this function search for either username@ip or username@'' @@ -1763,7 +1769,7 @@ __exit_process_mysql_query: ret->firewall_whitelist_mode = wus_status; if (wus_status == WUS_DETECTING || wus_status == WUS_PROTECTING) { bool allowed_query = false; - char * schemaname = sess->client_myds->myconn->userinfo->schemaname; + char * schemaname = client_myds->myconn->userinfo->schemaname; if (qp && qp->digest) { allowed_query = find_firewall_whitelist_rule(username, client_address, schemaname, flagIN, qp->digest); } @@ -1791,7 +1797,7 @@ __exit_process_mysql_query: if (ret->firewall_whitelist_mode == WUS_DETECTING) { action = (char *)"detected unknown"; } - proxy_warning("Firewall %s query with digest %s from user %s@%s\n", action, buf, username, sess->client_myds->addr.addr); + proxy_warning("Firewall %s query with digest %s from user %s@%s\n", action, buf, username, client_myds->addr.addr); } } } @@ -1926,10 +1932,14 @@ unsigned long long Query_Processor::query_parser_update_counters(Client_Session unsigned long long ret=_thr_commands_counters[c]->add_time(t); char *ca = (char *)""; + MySQL_Data_Stream * client_myds = NULL; + if (sess->session_type==PROXYSQL_SESSION_MYSQL) { + client_myds = ((MySQL_Session *)sess)->client_myds; + } if (mysql_thread___query_digests_track_hostname) { - if (sess->client_myds) { - if (sess->client_myds->addr.addr) { - ca = sess->client_myds->addr.addr; + if (client_myds) { + if (client_myds->addr.addr) { + ca = client_myds->addr.addr; } } } @@ -1940,10 +1950,10 @@ unsigned long long Query_Processor::query_parser_update_counters(Client_Session SpookyHash myhash; myhash.Init(19,3); assert(sess); - assert(sess->client_myds); - assert(sess->client_myds->myconn); - assert(sess->client_myds->myconn->userinfo); - MySQL_Connection_userinfo *ui=sess->client_myds->myconn->userinfo; + assert(client_myds); + assert(client_myds->myconn); + assert(client_myds->myconn->userinfo); + MySQL_Connection_userinfo *ui=client_myds->myconn->userinfo; assert(ui->username); assert(ui->schemaname); myhash.Update(ui->username,strlen(ui->username)); @@ -1959,10 +1969,10 @@ unsigned long long Query_Processor::query_parser_update_counters(Client_Session SpookyHash myhash; myhash.Init(19,3); assert(sess); - assert(sess->client_myds); - assert(sess->client_myds->myconn); - assert(sess->client_myds->myconn->userinfo); - MySQL_Connection_userinfo *ui=sess->client_myds->myconn->userinfo; + assert(client_myds); + assert(client_myds->myconn); + assert(client_myds->myconn->userinfo); + MySQL_Connection_userinfo *ui=client_myds->myconn->userinfo; assert(ui->username); assert(ui->schemaname); MySQL_STMT_Global_info *stmt_info=sess->CurrentQuery.stmt_info; @@ -1979,6 +1989,10 @@ unsigned long long Query_Processor::query_parser_update_counters(Client_Session } void Query_Processor::update_query_digest(SQP_par_t *qp, int hid, MySQL_Connection_userinfo *ui, unsigned long long t, unsigned long long n, MySQL_STMT_Global_info *_stmt_info, Client_Session *sess) { + MySQL_Data_Stream * client_myds = NULL; + if (sess->session_type==PROXYSQL_SESSION_MYSQL) { + client_myds = ((MySQL_Session *)sess)->client_myds; + } pthread_rwlock_wrlock(&digest_rwlock); QP_query_digest_stats *qds; @@ -2007,9 +2021,9 @@ void Query_Processor::update_query_digest(SQP_par_t *qp, int hid, MySQL_Connecti } char *ca = (char *)""; if (mysql_thread___query_digests_track_hostname) { - if (sess->client_myds) { - if (sess->client_myds->addr.addr) { - ca = sess->client_myds->addr.addr; + if (client_myds) { + if (client_myds->addr.addr) { + ca = client_myds->addr.addr; } } } diff --git a/lib/mysql_backend.cpp b/lib/mysql_backend.cpp index 8339100d6..c2ee07aad 100644 --- a/lib/mysql_backend.cpp +++ b/lib/mysql_backend.cpp @@ -1,14 +1,7 @@ #include "proxysql.h" #include "cpp.h" #include "ProxySQL_Data_Stream.h" - -void * MySQL_Backend::operator new(size_t size) { - return l_alloc(size); -} - -void MySQL_Backend::operator delete(void *ptr) { - l_free(sizeof(MySQL_Backend),ptr); -} +#include "MySQL_Data_Stream.h" MySQL_Backend::MySQL_Backend() { hostgroup_id=-1; diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index cffe4c822..721214732 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -7,8 +7,10 @@ #include "MySQL_PreparedStatement.h" #include "ProxySQL_Data_Stream.h" +#include "MySQL_Data_Stream.h" #include "query_processor.h" #include "MySQL_Variables.h" +#include "MySQL_Session.h" #include @@ -530,14 +532,14 @@ unsigned int MySQL_Connection::set_charset(unsigned int _c, enum charset_action // SQL_CHARACTER_SET should be set befor setting SQL_CHRACTER_ACTION std::stringstream ss; ss << _c; - mysql_variables.client_set_value(myds->sess, SQL_CHARACTER_SET, ss.str()); + mysql_variables.client_set_value((MySQL_Session *)(myds->sess), SQL_CHARACTER_SET, ss.str()); // When SQL_CHARACTER_ACTION is set character set variables are set according to // SQL_CHRACTER_SET value ss.str(std::string()); ss.clear(); ss << action; - mysql_variables.client_set_value(myds->sess, SQL_CHARACTER_ACTION, ss.str()); + mysql_variables.client_set_value((MySQL_Session *)(myds->sess), SQL_CHARACTER_ACTION, ss.str()); return _c; } @@ -718,7 +720,7 @@ void MySQL_Connection::connect_start() { mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&timeout); /* Take client character set and use it to connect to backend */ if (myds && myds->sess) { - csname = mysql_variables.client_get_value(myds->sess, SQL_CHARACTER_SET); + csname = mysql_variables.client_get_value((MySQL_Session *)(myds->sess), SQL_CHARACTER_SET); } const MARIADB_CHARSET_INFO * c = NULL; @@ -741,11 +743,11 @@ void MySQL_Connection::connect_start() { std::stringstream ss; ss << c->nr; - mysql_variables.server_set_value(myds->sess, SQL_CHARACTER_SET, ss.str().c_str()); - mysql_variables.server_set_value(myds->sess, SQL_CHARACTER_SET_RESULTS, ss.str().c_str()); - mysql_variables.server_set_value(myds->sess, SQL_CHARACTER_SET_CLIENT, ss.str().c_str()); - mysql_variables.server_set_value(myds->sess, SQL_CHARACTER_SET_CONNECTION, ss.str().c_str()); - mysql_variables.server_set_value(myds->sess, SQL_COLLATION_CONNECTION, ss.str().c_str()); + mysql_variables.server_set_value((MySQL_Session *)(myds->sess), SQL_CHARACTER_SET, ss.str().c_str()); + mysql_variables.server_set_value((MySQL_Session *)(myds->sess), SQL_CHARACTER_SET_RESULTS, ss.str().c_str()); + mysql_variables.server_set_value((MySQL_Session *)(myds->sess), SQL_CHARACTER_SET_CLIENT, ss.str().c_str()); + mysql_variables.server_set_value((MySQL_Session *)(myds->sess), SQL_CHARACTER_SET_CONNECTION, ss.str().c_str()); + mysql_variables.server_set_value((MySQL_Session *)(myds->sess), SQL_COLLATION_CONNECTION, ss.str().c_str()); } //mysql_options(mysql, MYSQL_SET_CHARSET_NAME, c->csname); mysql->charset = c; @@ -755,9 +757,10 @@ void MySQL_Connection::connect_start() { if (myds) { if (myds->sess) { - if (myds->sess->client_myds) { - if (myds->sess->client_myds->myconn) { - uint32_t orig_client_flags = myds->sess->client_myds->myconn->options.client_flag; + MySQL_Session * mysess = (MySQL_Session *)myds->sess; + if (mysess->client_myds) { + if (mysess->client_myds->myconn) { + uint32_t orig_client_flags = mysess->client_myds->myconn->options.client_flag; if (orig_client_flags & CLIENT_FOUND_ROWS) { client_flags |= CLIENT_FOUND_ROWS; } @@ -784,9 +787,10 @@ void MySQL_Connection::connect_start() { if (myds != NULL) { if (myds->sess != NULL) { - if (myds->sess->session_fast_forward == true) { // this is a fast_forward connection - assert(myds->sess->client_myds != NULL); - MySQL_Connection * c = myds->sess->client_myds->myconn; + MySQL_Session * mysess = (MySQL_Session *)myds->sess; + if (mysess->session_fast_forward == true) { // this is a fast_forward connection + assert(mysess->client_myds != NULL); + MySQL_Connection * c = mysess->client_myds->myconn; assert(c != NULL); mysql->options.client_flag &= ~(CLIENT_DEPRECATE_EOF); // we disable it by default // if both client_flag and server_capabilities (used for client) , set CLIENT_DEPRECATE_EOF @@ -836,11 +840,12 @@ void MySQL_Connection::change_user_start() { PROXY_TRACE(); //fprintf(stderr,"change_user_start FD %d\n", fd); MySQL_Connection_userinfo *_ui = NULL; - if (myds->sess->client_myds == NULL) { + MySQL_Session * mysess = (MySQL_Session *)myds->sess; + if (mysess->client_myds == NULL) { // if client_myds is not defined, we are using CHANGE_USER to reset the connection _ui = userinfo; } else { - _ui = myds->sess->client_myds->myconn->userinfo; + _ui = mysess->client_myds->myconn->userinfo; userinfo->set(_ui); // fix for bug #605 } char *auth_password=NULL; @@ -878,7 +883,8 @@ void MySQL_Connection::ping_cont(short event) { void MySQL_Connection::initdb_start() { PROXY_TRACE(); - MySQL_Connection_userinfo *client_ui=myds->sess->client_myds->myconn->userinfo; + MySQL_Session * mysess = (MySQL_Session *)myds->sess; + MySQL_Connection_userinfo *client_ui=mysess->client_myds->myconn->userinfo; async_exit_status = mysql_select_db_start(&interr,mysql,client_ui->schemaname); } @@ -912,14 +918,15 @@ void MySQL_Connection::set_autocommit_cont(short event) { void MySQL_Connection::set_names_start() { PROXY_TRACE(); - const MARIADB_CHARSET_INFO * c = proxysql_find_charset_nr(atoi(mysql_variables.client_get_value(myds->sess, SQL_CHARACTER_SET))); + MySQL_Session * mysess = (MySQL_Session *)myds->sess; + const MARIADB_CHARSET_INFO * c = proxysql_find_charset_nr(atoi(mysql_variables.client_get_value(mysess, SQL_CHARACTER_SET))); if (!c) { // LCOV_EXCL_START - proxy_error("Not existing charset number %u\n", atoi(mysql_variables.client_get_value(myds->sess, SQL_CHARACTER_SET))); + proxy_error("Not existing charset number %u\n", atoi(mysql_variables.client_get_value(mysess, SQL_CHARACTER_SET))); assert(0); // LCOV_EXCL_STOP } - async_exit_status = mysql_set_character_set_start(&interr,mysql, NULL, atoi(mysql_variables.client_get_value(myds->sess, SQL_CHARACTER_SET))); + async_exit_status = mysql_set_character_set_start(&interr,mysql, NULL, atoi(mysql_variables.client_get_value(mysess, SQL_CHARACTER_SET))); } void MySQL_Connection::set_names_cont(short event) { @@ -1276,13 +1283,14 @@ handler_again: NEXT_IMMEDIATE(ASYNC_STMT_EXECUTE_END); } else { if (myds->sess->mirror==false) { + MySQL_Session * mysess = (MySQL_Session *)myds->sess; if (MyRS_reuse == NULL) { MyRS = new MySQL_ResultSet(); - MyRS->init(&myds->sess->client_myds->myprot, query.stmt_result, mysql, query.stmt); + MyRS->init(&mysess->client_myds->myprot, query.stmt_result, mysql, query.stmt); } else { MyRS = MyRS_reuse; MyRS_reuse = NULL; - MyRS->init(&myds->sess->client_myds->myprot, query.stmt_result, mysql, query.stmt); + MyRS->init(&mysess->client_myds->myprot, query.stmt_result, mysql, query.stmt); } } else { /* @@ -1310,10 +1318,11 @@ handler_again: case ASYNC_STMT_EXECUTE_STORE_RESULT_CONT: PROXY_TRACE2(); { // this copied mostly from ASYNC_USE_RESULT_CONT - if (myds->sess && myds->sess->client_myds && myds->sess->mirror==false) { + MySQL_Session * mysess = (MySQL_Session *)myds->sess; + if (myds->sess && mysess->client_myds && mysess->mirror==false) { unsigned int buffered_data=0; - buffered_data = myds->sess->client_myds->PSarrayOUT->len * RESULTSET_BUFLEN; - buffered_data += myds->sess->client_myds->resultset->len * RESULTSET_BUFLEN; + buffered_data = mysess->client_myds->PSarrayOUT->len * RESULTSET_BUFLEN; + buffered_data += mysess->client_myds->resultset->len * RESULTSET_BUFLEN; if (buffered_data > (unsigned int)mysql_thread___threshold_resultset_size*8) { next_event(ASYNC_STMT_EXECUTE_STORE_RESULT_CONT); // we temporarily pause . See #1232 break; @@ -1454,14 +1463,15 @@ handler_again: if (mysql_result==NULL) { NEXT_IMMEDIATE(ASYNC_QUERY_END); } else { - if (myds->sess->mirror==false) { + MySQL_Session * mysess = (MySQL_Session *)myds->sess; + if (mysess->mirror==false) { if (MyRS_reuse == NULL) { MyRS = new MySQL_ResultSet(); - MyRS->init(&myds->sess->client_myds->myprot, mysql_result, mysql); + MyRS->init(&mysess->client_myds->myprot, mysql_result, mysql); } else { MyRS = MyRS_reuse; MyRS_reuse = NULL; - MyRS->init(&myds->sess->client_myds->myprot, mysql_result, mysql); + MyRS->init(&mysess->client_myds->myprot, mysql_result, mysql); } } else { if (MyRS_reuse == NULL) { @@ -1479,10 +1489,11 @@ handler_again: break; case ASYNC_USE_RESULT_CONT: { - if (myds->sess && myds->sess->client_myds && myds->sess->mirror==false) { + MySQL_Session * mysess = (MySQL_Session *)myds->sess; + if (mysess && mysess->client_myds && mysess->mirror==false) { unsigned int buffered_data=0; - buffered_data = myds->sess->client_myds->PSarrayOUT->len * RESULTSET_BUFLEN; - buffered_data += myds->sess->client_myds->resultset->len * RESULTSET_BUFLEN; + buffered_data = mysess->client_myds->PSarrayOUT->len * RESULTSET_BUFLEN; + buffered_data += mysess->client_myds->resultset->len * RESULTSET_BUFLEN; if (buffered_data > (unsigned int)mysql_thread___threshold_resultset_size*8) { next_event(ASYNC_USE_RESULT_CONT); // we temporarily pause . See #1232 break; @@ -2538,12 +2549,13 @@ void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { } if (mysql) { if (myds && myds->sess) { - if (myds->sess->client_myds && myds->sess->client_myds->myconn) { + MySQL_Session * mysess = (MySQL_Session *)myds->sess; + if (mysess->client_myds && mysess->client_myds->myconn) { // if SERVER_STATUS_NO_BACKSLASH_ESCAPES is changed it is likely // because of sql_mode was changed // we set the same on the client connection unsigned int ss = mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES; - myds->sess->client_myds->myconn->set_no_backslash_escapes(ss); + mysess->client_myds->myconn->set_no_backslash_escapes(ss); } } } diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index 583f5be60..b2eee3465 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -8,10 +8,14 @@ #include "MySQL_Logger.hpp" #include "ProxySQL_Data_Stream.h" +#include "MySQL_Data_Stream.h" +#include "MySQL_Session.h" #include "proxysql_utils.h" #include "query_processor.h" #include "SQLite3_Server.h" +#include "proxysql_admin.h" + #include #include #include @@ -309,8 +313,8 @@ bool match_monitor_query(const std::string& monitor_query, const std::string& qu } } -void SQLite3_Server_session_handler(Client_Session *sess, void *_pa, PtrSize_t *pkt) { - +void SQLite3_Server_session_handler(Client_Session *c_sess, void *_pa, PtrSize_t *pkt) { + MySQL_Session *sess = (MySQL_Session *)c_sess; char *error=NULL; int cols; int affected_rows; @@ -784,11 +788,11 @@ static void *child_mysql(void *arg) { GloQPro->init_thread(); mysql_thr->refresh_variables(); - Client_Session *sess=mysql_thr->create_new_session_and_client_data_stream(client); + MySQL_Session *sess=mysql_thr->create_new_session_and_client_mysql_data_stream(client); sess->thread=mysql_thr; sess->session_type = PROXYSQL_SESSION_SQLITE; sess->handler_function=SQLite3_Server_session_handler; - ProxySQL_Data_Stream *myds=sess->client_myds; + MySQL_Data_Stream *myds=sess->client_myds; fds[0].fd=client; fds[0].revents=0; @@ -1105,7 +1109,7 @@ SQLite3_Server::SQLite3_Server() { #ifdef TEST_GALERA -void SQLite3_Server::populate_galera_table(Client_Session *sess) { +void SQLite3_Server::populate_galera_table(MySQL_Session *sess) { // this function needs to be called with lock on mutex galera_mutex already acquired sessdb->execute("BEGIN TRANSACTION"); char *error=NULL; @@ -1158,7 +1162,7 @@ void SQLite3_Server::populate_galera_table(Client_Session *sess) { #endif // TEST_GALERA #ifdef TEST_AURORA -void SQLite3_Server::populate_aws_aurora_table(Client_Session *sess) { +void SQLite3_Server::populate_aws_aurora_table(MySQL_Session *sess) { // this function needs to be called with lock on mutex aurora_mutex already acquired sessdb->execute("DELETE FROM REPLICA_HOST_STATUS"); sqlite3_stmt *statement=NULL; @@ -1244,7 +1248,7 @@ void SQLite3_Server::populate_aws_aurora_table(Client_Session *sess) { * @param sess The current session performing a query. * @param txs_behind Unused parameter. */ -void SQLite3_Server::populate_grouprep_table(Client_Session *sess, int txs_behind) { +void SQLite3_Server::populate_grouprep_table(MySQL_Session *sess, int txs_behind) { GloAdmin->mysql_servers_wrlock(); // We are going to repopulate the map this->grouprep_map.clear(); @@ -1481,7 +1485,7 @@ bool SQLite3_Server::set_variable(char *name, char *value) { // this is the pub void SQLite3_Server::send_MySQL_OK(MySQL_Protocol *myprot, char *msg, int rows, uint16_t status) { assert(myprot); - ProxySQL_Data_Stream *myds=myprot->get_myds(); + MySQL_Data_Stream *myds=myprot->get_myds(); myds->DSS=STATE_QUERY_SENT_DS; myprot->generate_pkt_OK(true,NULL,NULL,1,rows,0,status,0,msg,false); myds->DSS=STATE_SLEEP; @@ -1489,7 +1493,7 @@ void SQLite3_Server::send_MySQL_OK(MySQL_Protocol *myprot, char *msg, int rows, void SQLite3_Server::send_MySQL_ERR(MySQL_Protocol *myprot, char *msg) { assert(myprot); - ProxySQL_Data_Stream *myds=myprot->get_myds(); + MySQL_Data_Stream *myds=myprot->get_myds(); myds->DSS=STATE_QUERY_SENT_DS; myprot->generate_pkt_ERR(true,NULL,NULL,1,1045,(char *)"28000",msg); myds->DSS=STATE_SLEEP; diff --git a/src/main.cpp b/src/main.cpp index 756d71f95..9b32b0f87 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,6 +22,8 @@ #include "proxysql_restapi.h" #include "Web_Interface.hpp" +#include "proxysql_admin.h" + #include #include #include diff --git a/test/tap/tests/test_admin_stats-t.cpp b/test/tap/tests/test_admin_stats-t.cpp index 5658cfad1..712876725 100644 --- a/test/tap/tests/test_admin_stats-t.cpp +++ b/test/tap/tests/test_admin_stats-t.cpp @@ -12,6 +12,15 @@ * @date 2021-10-28 */ +/* +NOTE: +Test 4 assumes that no new variable was introduced after +table history_mysql_status_variables was initialized. +If this is not true, manually run the following before running the test: + +DELETE FROM history_mysql_status_variables; +*/ + #include #include #include diff --git a/test/tap/tests/test_backend_conn_ping-t.cpp b/test/tap/tests/test_backend_conn_ping-t.cpp index f4d5f325d..a6052aee6 100644 --- a/test/tap/tests/test_backend_conn_ping-t.cpp +++ b/test/tap/tests/test_backend_conn_ping-t.cpp @@ -13,6 +13,12 @@ * any error reported by the client due to broken connections. */ +/* +NOTE: the parameters in this test are tuned in a way that if proxysql starts +with only 1 worker thread, it is unlikely to ping all connections on time. +See note on wait_timeout +*/ + #include #include #include @@ -33,6 +39,45 @@ using std::pair; using srv_cfg = vector>; +int wait_timeout = 10; + +// if only 1 worker thread is running, wait_timeout should be bigger +// 1 worker thread : wait_timeout = 45 +// 4 worker threads : wait_timeout = 10 +int compute_wait_timeout(MYSQL *my_conn) { + int res = EXIT_SUCCESS; + res = mysql_query(my_conn, "SELECT @@mysql-threads"); + if (res != EXIT_SUCCESS) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(my_conn)); + res = EXIT_FAILURE; + return res; + } + MYSQL_RES* my_res = mysql_store_result(my_conn); + if (my_res == nullptr) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(my_conn)); + res = EXIT_FAILURE; + return res; + } + + MYSQL_ROW row = mysql_fetch_row(my_res); + if (row == nullptr || row[0] == nullptr) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(my_conn)); + res = EXIT_FAILURE; + return res; + } else { + const char *val = row[0]; + diag("mysql-threads = %s", val); + if (strcmp(val,"1")==0) { + diag("Setting wait_timeout to 45 instead of 10"); + wait_timeout = 45; + } + } + mysql_free_result(my_res); + + return res; +} + + int change_mysql_cfg( const CommandLine& cl, const string& host, const string& port, const srv_cfg& new_srv_cfg, srv_cfg& out_old_srv_cfg ) { @@ -78,7 +123,9 @@ int change_mysql_cfg( mysql_free_result(my_res); - mysql_query(my_conn, string { "SET GLOBAL " + config_var.first + "=" + std::to_string(config_var.second) }.c_str()); + string query = string { "SET GLOBAL " + config_var.first + "=" + std::to_string(config_var.second) }; + diag("Setting on %s:%s : %s", host.c_str(), port.c_str(), query.c_str()); + mysql_query(my_conn, query.c_str()); if (res != EXIT_SUCCESS) { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(my_conn)); res = EXIT_FAILURE; @@ -337,6 +384,7 @@ int wait_target_backend_conns(MYSQL* admin, uint32_t tg_backend_conns, uint32_t break; } else { waited += 1; + diag("tg_backend_conns: %d, cur_conn_num: %ld , not matching after %lu checks", tg_backend_conns, cur_conn_num, waited); sleep(1); } } @@ -387,6 +435,10 @@ int main(int, char**) { return EXIT_FAILURE; } + if (compute_wait_timeout(proxy_admin) != EXIT_SUCCESS) { + return EXIT_FAILURE; + } + double intv = 5; double b = 128; double b_0 = 256; @@ -416,15 +468,22 @@ int main(int, char**) { MYSQL_QUERY(proxy_admin, "LOAD MYSQL SERVERS TO RUNTIME"); diag("Setting ProxySQL config..."); - // Set the backend connections ping frequency - MYSQL_QUERY(proxy_admin, string { "SET mysql-ping_interval_server_msec=" + std::to_string(freq) }.c_str()); - // Make sure no connection cleanup takes place - MYSQL_QUERY(proxy_admin, "SET mysql-free_connections_pct=100"); - // Don't retry on failure - MYSQL_QUERY(proxy_admin, "SET mysql-query_retries_on_failure=0"); - // Set a higher max_connection number for the servers - MYSQL_QUERY(proxy_admin, "LOAD MYSQL VARIABLES TO RUNTIME"); - + { + // Set the backend connections ping frequency + string query = string { "SET mysql-ping_interval_server_msec=" + std::to_string(freq) }; + diag("%s", query.c_str()); + MYSQL_QUERY(proxy_admin, query.c_str()); + // Make sure no connection cleanup takes place + query = "SET mysql-free_connections_pct=100"; + diag("%s", query.c_str()); + MYSQL_QUERY(proxy_admin, query.c_str()); + // Don't retry on failure + query = "SET mysql-query_retries_on_failure=0"; + diag("%s", query.c_str()); + MYSQL_QUERY(proxy_admin, query.c_str()); + // Set a higher max_connection number for the servers + MYSQL_QUERY(proxy_admin, "LOAD MYSQL VARIABLES TO RUNTIME"); + } // Configure MySQL infra servers with: 'wait_timeout' and 'max_connections' vector> servers_old_configs {}; @@ -440,7 +499,7 @@ int main(int, char**) { return EXIT_FAILURE; } - srv_cfg new_srv_cfg { { "wait_timeout", 10 }, { "max_connections", 2500 } }; + srv_cfg new_srv_cfg { { "wait_timeout", wait_timeout }, { "max_connections", 2500 } }; for (const mysql_res_row& srv_row : servers_rows) { srv_cfg old_srv_cfg {};