Merge pull request #4062 from sysown/v2.x-fast_forward_ssl2

Support for fast_forward and SSL #1459
pull/4076/head
René Cannaò 3 years ago committed by GitHub
commit a2be18881a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,4 @@
Please note that upgrading `mariadb-connector-c` can require some changes
in `include/MySQL_Data_Stream.h` where we define `P_MARIADB_TLS` as a copy
of `MARIADB_TLS` . If `MARIADB_TLS` is changed, `P_MARIADB_TLS` must be
updated too.

@ -6,6 +6,21 @@
#include "MySQL_Protocol.h"
#ifndef uchar
typedef unsigned char uchar;
#endif
#include "ma_pvio.h"
// here we define P_MARIADB_TLS as a copy of MARIADB_TLS
// copied from ma_tls.h
// note that ma_pvio.h defines it as void
typedef struct P_st_ma_pvio_tls {
void *data;
MARIADB_PVIO *pvio;
void *ssl;
} P_MARIADB_TLS;
#define QUEUE_T_DEFAULT_SIZE 32768
#define MY_SSL_BUFFER 8192
@ -196,6 +211,33 @@ class MySQL_Data_Stream
myconn=mc;
myconn->statuses.myconnpoll_get++;
mc->myds=this;
encrypted = false; // this is the default
// we handle encryption for backend
//
// we have a similar code in MySQL_Connection
// in case of ASYNC_CONNECT_SUCCESSFUL
if (sess != NULL && sess->session_fast_forward == true) {
// if frontend and backend connection use SSL we will set
// encrypted = true and we will start using the SSL structure
// directly from P_MARIADB_TLS structure.
//
// For futher details:
// - without ssl: we use the file descriptor from mysql connection
// - with ssl: we use the SSL structure from mysql connection
if (myconn->mysql && myconn->ret_mysql) {
if (myconn->mysql->options.use_ssl == 1) {
encrypted = true;
if (ssl == NULL) {
// check the definition of P_MARIADB_TLS
P_MARIADB_TLS * matls = (P_MARIADB_TLS *)myconn->mysql->net.pvio->ctls;
ssl = (SSL *)matls->ssl;
rbio_ssl = BIO_new(BIO_s_mem());
wbio_ssl = BIO_new(BIO_s_mem());
SSL_set_bio(ssl, rbio_ssl, wbio_ssl);
}
}
}
}
}
// safe way to detach a MySQL Connection
@ -205,6 +247,15 @@ class MySQL_Data_Stream
statuses.myconnpoll_put++;
myconn->myds=NULL;
myconn=NULL;
if (encrypted == true) {
if (sess != NULL && sess->session_fast_forward == true) {
// it seems we are a connection with SSL on a fast_forward session.
// See attach_connection() for more details .
// We now disable SSL metadata from the Data Stream
encrypted = false;
ssl = NULL;
}
}
}
void return_MySQL_Connection_To_Pool();

@ -567,6 +567,8 @@ void ClickHouse_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t
query_no_space[query_no_space_length]=0;
}
proxy_debug(PROXY_DEBUG_SQLITE, 4, "Received query on Session %p , thread_session_id %u : %s\n", sess, sess->thread_session_id, query_no_space);
if (sess->session_type == PROXYSQL_SESSION_CLICKHOUSE) {
if (!strncasecmp("SET ", query_no_space, 4)) {
@ -1365,9 +1367,24 @@ static void *child_mysql(void *arg) {
}
}
myds->revents=fds[0].revents;
myds->read_from_net();
int rb = 0;
rb - myds->read_from_net();
if (myds->net_failure) goto __exit_child_mysql;
myds->read_pkts();
if (myds->encrypted == true) {
// PMC-10004
// we probably should use SSL_pending() and/or SSL_has_pending() to determine
// if there is more data to be read, but it doesn't seem to be working.
// Therefore we hardcored the values 4096 (4K) as a special case and
// we try to call read_from_net() again.
// Previously we hardcoded 16KB but it seems that it can return in smaller
// chunks of 4KB.
while (rb > 0 && rb%4096 == 0) {
rb = myds->read_from_net();
if (myds->net_failure) goto __exit_child_mysql;
myds->read_pkts();
}
}
sess->to_process=1;
int rc=sess->handler();
if (rc==-1) goto __exit_child_mysql;

@ -1947,7 +1947,10 @@ __do_auth:
#endif
(*myds)->sess->schema_locked=schema_locked;
(*myds)->sess->transaction_persistent=transaction_persistent;
(*myds)->sess->session_fast_forward=fast_forward;
(*myds)->sess->session_fast_forward=false; // default
if ((*myds)->sess->session_type == PROXYSQL_SESSION_MYSQL) {
(*myds)->sess->session_fast_forward=fast_forward;
}
(*myds)->sess->user_max_connections=max_connections;
}
if (password == NULL) {

@ -21,6 +21,8 @@
#include "MySQL_PreparedStatement.h"
#include "MySQL_Logger.hpp"
#include <fcntl.h>
using std::vector;
using std::function;
@ -2834,6 +2836,20 @@ MySQL_Session * MySQL_Thread::create_new_session_and_client_data_stream(int _fd)
register_session(sess); // register session
sess->client_myds = new MySQL_Data_Stream();
sess->client_myds->fd=_fd;
// set not blocking for client connections too!
{
// PMC-10004
// While implementing SSL and fast_forward it was noticed that all frontend connections
// are in blocking, although this was never a problem because we call poll() before reading.
// Although it became a problem with fast_forward, SSL and large packets because SSL handled
// data in chunks of 16KB and there may be data inside SSL even when there is no data
// received from the network.
// The only modules that seems to be affected by this issue are Admin, SQLite3 Server
// and Clickhouse Server
int nb = fcntl(_fd, F_SETFL, fcntl(_fd, F_GETFL, 0) | O_NONBLOCK);
assert (nb != -1);
}
setsockopt(sess->client_myds->fd, IPPROTO_TCP, TCP_NODELAY, (char *) &arg_on, sizeof(arg_on));
if (mysql_thread___use_tcp_keepalive) {
@ -3578,17 +3594,40 @@ bool MySQL_Thread::process_data_on_data_stream(MySQL_Data_Stream *myds, unsigned
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;
_fds.events = POLLIN;
_fds.revents = 0;
int _rc = poll(&_fds, _nfds, 0);
if ((_rc > 0) && _fds.revents == POLLIN) {
// there is more data
myds->revents = _fds.revents;
} else {
if (myds->encrypted == true) { // we are in fast_forward mode and encrypted == true
// PMC-10004
// we probably should use SSL_pending() and/or SSL_has_pending() to determine
// if there is more data to be read, but it doesn't seem to be working.
// Therefore we hardcored the value 16384 (16KB) as a special case and
// we try to call read_from_net() again
/*
int sslp = SSL_pending(myds->ssl);
int sslhp = SSL_has_pending(myds->ssl);
proxy_debug(PROXY_DEBUG_NET, 5, "Session=%p: in fast_forward mode and SSL read %d bytes , SSL_pending: %d bytes , SSL_has_pending: %d\n", myds->sess, rb, sslp, sslhp);
*/
proxy_debug(PROXY_DEBUG_NET, 5, "Session=%p, DataStream=%p , thread_session_id=%u -- in fast_forward mode and SSL read %d bytes\n", myds->sess, myds, myds->sess->thread_session_id, rb);
while (rb == 16384) {
rb = myds->read_from_net();
if (rb > 0 && myds->myds_type == MYDS_FRONTEND) {
status_variables.stvar[st_var_queries_frontends_bytes_recv] += rb;
}
proxy_debug(PROXY_DEBUG_NET, 5, "Session=%p, DataStream=%p -- in fast_forward mode and SSL read %d bytes\n", myds->sess, myds, rb);
myds->read_pkts();
}
rb = 0; // exit loop
} else { // we are in fast_forward mode and encrypted == false
struct pollfd _fds;
nfds_t _nfds = 1;
_fds.fd = mypolls.fds[n].fd;
_fds.events = POLLIN;
_fds.revents = 0;
int _rc = poll(&_fds, _nfds, 0);
if ((_rc > 0) && _fds.revents == POLLIN) {
// there is more data
myds->revents = _fds.revents;
} else {
rb = 0; // exit loop
}
}
} else {
rb = 0; // exit loop

@ -5458,9 +5458,24 @@ void *child_mysql(void *arg) {
}
mysql_thr->curtime = monotonic_time();
myds->revents=fds[0].revents;
myds->read_from_net();
int rb = 0;
rb = myds->read_from_net();
if (myds->net_failure) goto __exit_child_mysql;
myds->read_pkts();
if (myds->encrypted == true) {
// PMC-10004
// we probably should use SSL_pending() and/or SSL_has_pending() to determine
// if there is more data to be read, but it doesn't seem to be working.
// Therefore we hardcored the values 4096 (4K) as a special case and
// we try to call read_from_net() again.
// Previously we hardcoded 16KB but it seems that it can return in smaller
// chunks of 4KB.
while (rb > 0 && rb%4096 == 0) {
rb = myds->read_from_net();
if (myds->net_failure) goto __exit_child_mysql;
myds->read_pkts();
}
}
sess->to_process=1;
int rc=sess->handler();
if (rc==-1) goto __exit_child_mysql;

@ -22,6 +22,7 @@ void ma_free_root(MA_MEM_ROOT *root, myf MyFLAGS);
void *ma_alloc_root(MA_MEM_ROOT *mem_root, size_t Size);
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
void * ma_alloc_root(MA_MEM_ROOT *mem_root, size_t Size)
{
size_t get_size;
@ -1113,6 +1114,33 @@ handler_again:
}
break;
case ASYNC_CONNECT_SUCCESSFUL:
if (mysql && ret_mysql) {
// we handle encryption for backend
//
// we have a similar code in MySQL_Data_Stream::attach_connection()
// see there for further details
if (mysql->options.use_ssl == 1)
if (myds)
if (myds->sess != NULL)
if (myds->sess->session_fast_forward == true) {
assert(myds->ssl==NULL);
if (myds->ssl == NULL) {
// check the definition of P_MARIADB_TLS
P_MARIADB_TLS * matls = (P_MARIADB_TLS *)mysql->net.pvio->ctls;
if (matls != NULL) {
myds->encrypted = true;
myds->ssl = (SSL *)matls->ssl;
myds->rbio_ssl = BIO_new(BIO_s_mem());
myds->wbio_ssl = BIO_new(BIO_s_mem());
SSL_set_bio(myds->ssl, myds->rbio_ssl, myds->wbio_ssl);
} else {
// if mysql->options.use_ssl == 1 but matls == NULL
// it means that ProxySQL tried to use SSL to connect to the backend
// but the backend didn't support SSL
}
}
}
}
__sync_fetch_and_add(&MyHGM->status.server_connections_connected,1);
__sync_fetch_and_add(&parent->connect_OK,1);
options.client_flag = mysql->client_flag;

@ -118,7 +118,9 @@ static void __dump_pkt(const char *func, unsigned char *_ptr, unsigned int len)
}
#define queue_zero(_q) { \
memcpy(_q.buffer, (unsigned char *)_q.buffer + _q.tail, _q.head - _q.tail); \
if (_q.tail != 0) { \
memcpy(_q.buffer, (unsigned char *)_q.buffer + _q.tail, _q.head - _q.tail); \
} \
_q.head-=_q.tail; \
_q.tail=0; \
}
@ -385,15 +387,6 @@ MySQL_Data_Stream::~MySQL_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);
@ -532,7 +525,7 @@ int MySQL_Data_Stream::read_from_net() {
r = recv(fd, queue_w_ptr(queueIN), s, 0);
}
}
} else {
} else { // encrypted == true
/*
if (!SSL_is_init_finished(ssl)) {
int ret = SSL_do_handshake(ssl);
@ -638,11 +631,21 @@ int MySQL_Data_Stream::read_from_net() {
}
} 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();
proxy_debug(PROXY_DEBUG_NET, 5, "Session=%p, Datastream=%p -- session_id: %u , SSL_get_error(): %d , errno: %d\n", sess, this, sess->thread_session_id, ssl_ret, errno);
if (ssl_ret == SSL_ERROR_SYSCALL && (errno == EINTR || errno == EAGAIN)) {
// the read was interrupted, do nothing
proxy_debug(PROXY_DEBUG_NET, 5, "Session=%p, Datastream=%p -- SSL_get_error() is SSL_ERROR_SYSCALL, errno: %d\n", sess, this, errno);
} else {
if (r==0) { // we couldn't read any data
if (revents==1) {
// revents returns 1 , but recv() returns 0 , so there is no data.
// Therefore the socket is already closed
proxy_debug(PROXY_DEBUG_NET, 5, "Session=%p, Datastream=%p -- shutdown soft\n", sess, this);
shut_soft();
}
}
if (ssl_ret!=SSL_ERROR_WANT_READ && ssl_ret!=SSL_ERROR_WANT_WRITE) shut_soft();
// it seems we end in shut_soft() anyway
}
}
} else {
@ -673,11 +676,13 @@ int MySQL_Data_Stream::write_to_net() {
if (encrypted) {
bytes_io = SSL_write (ssl, queue_r_ptr(queueOUT), s);
//proxy_info("Used SSL_write to write %d bytes\n", bytes_io);
proxy_debug(PROXY_DEBUG_NET, 7, "Session=%p, Datastream=%p: SSL_write() wrote %d bytes . queueOUT before: %u\n", sess, this, bytes_io, queue_data(queueOUT));
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_debug(PROXY_DEBUG_NET, 7, "Session=%p, Datastream=%p: BIO_read() read %d bytes\n", sess, this, n);
//proxy_info("BIO read = %d\n", n);
if (n > 0) {
//proxy_info("Setting %d byte in queue encrypted\n", n);
@ -690,8 +695,10 @@ int MySQL_Data_Stream::write_to_net() {
}
} while (n>0);
}
proxy_debug(PROXY_DEBUG_NET, 7, "Session=%p, Datastream=%p: current ssl_write_len is %lu bytes\n", sess, this, ssl_write_len);
if (ssl_write_len) {
n = write(fd, ssl_write_buf, ssl_write_len);
proxy_debug(PROXY_DEBUG_NET, 7, "Session=%p, Datastream=%p: write() wrote %d bytes in FD %d\n", sess, this, n, fd);
//proxy_info("Calling write() on SSL: %d\n", n);
if (n>0) {
if ((size_t)n < ssl_write_len) {
@ -722,6 +729,7 @@ int MySQL_Data_Stream::write_to_net() {
#else
bytes_io = send(fd, queue_r_ptr(queueOUT), s, MSG_NOSIGNAL);
#endif
proxy_debug(PROXY_DEBUG_NET, 7, "Session=%p, Datastream=%p: send() wrote %d bytes in FD %d\n", sess, this, bytes_io, fd);
}
if (encrypted) {
//proxy_info("bytes_io: %d\n", bytes_io);

@ -372,6 +372,7 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p
query_no_space[query_no_space_length]=0;
}
proxy_debug(PROXY_DEBUG_SQLITE, 4, "Received query on Session %p , thread_session_id %u : %s\n", sess, sess->thread_session_id, query_no_space);
{
SQLite3_Session *sqlite_sess = (SQLite3_Session *)sess->thread->gen_args;
@ -935,11 +936,25 @@ static void *child_mysql(void *arg) {
// FIXME: CI test test_sqlite3_server-t or test_sqlite3_server_and_fast_routing-t
// seems to result in fds->fd = -1
// it needs investigation
myds->read_from_net();
int rb = 0;
rb = myds->read_from_net();
if (myds->net_failure) goto __exit_child_mysql;
myds->read_pkts();
if (myds->encrypted == true) {
// PMC-10004
// we probably should use SSL_pending() and/or SSL_has_pending() to determine
// if there is more data to be read, but it doesn't seem to be working.
// Therefore we hardcored the values 4096 (4K) as a special case and
// we try to call read_from_net() again.
// Previously we hardcoded 16KB but it seems that it can return in smaller
// chunks of 4KB.
while (rb > 0 && rb%4096 == 0) {
rb = myds->read_from_net();
if (myds->net_failure) goto __exit_child_mysql;
myds->read_pkts();
}
}
sess->to_process=1;
// Get and set the client address before the sesion is processed.
union {
struct sockaddr_in in;

@ -0,0 +1,267 @@
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <vector>
#include <string>
#include <sstream>
#include <mysql.h>
#include "tap.h"
#include "command_line.h"
#include "utils.h"
/*
Several bugs were identified and fixed while developing this test:
- several memory corruption in SSL and fast_forward
- SQLite3 Server stops working if the user is a fast_forward user
- potential stalls with large resultset
- connections being closed because of interrupted syscall in SSL (SSL_ERROR_SYSCALL)
*/
char * username = (char *)"user1459";
char * password = (char *)"pass1459";
std::vector<std::string> queries_set1 = {
"SET mysql-have_ssl='false'",
"LOAD MYSQL VARIABLES TO RUNTIME",
"DELETE FROM mysql_servers WHERE hostgroup_id = 1459",
"INSERT INTO mysql_servers (hostgroup_id, hostname, port, use_ssl) VALUES (1459, '127.0.0.1', 6030, 0)",
"LOAD MYSQL SERVERS TO RUNTIME",
"DELETE FROM mysql_users WHERE username = 'user1459'",
"INSERT INTO mysql_users (username,password,fast_forward,default_hostgroup) VALUES ('" + std::string(username) + "','" + std::string(password) + "',1,1459)",
"LOAD MYSQL USERS TO RUNTIME",
};
std::vector<std::string> queries_set2 = {
"SET mysql-have_ssl='true'",
"LOAD MYSQL VARIABLES TO RUNTIME",
};
std::vector<std::string> queries_set3 = {
"SET mysql-have_ssl='false'",
"LOAD MYSQL VARIABLES TO RUNTIME",
"UPDATE mysql_servers SET use_ssl=1 WHERE hostgroup_id = 1459",
"LOAD MYSQL SERVERS TO RUNTIME",
};
std::vector<std::string> queries_set4 = {
"SET mysql-have_ssl='true'",
"LOAD MYSQL VARIABLES TO RUNTIME",
"UPDATE mysql_servers SET use_ssl=1 WHERE hostgroup_id = 1459",
"LOAD MYSQL SERVERS TO RUNTIME",
};
std::vector<std::string> queries_SQL1 = {
"DROP TABLE IF EXISTS tbl1459",
"CREATE TABLE tbl1459 (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , i1 INTEGER , i2 INTEGER)",
"INSERT INTO tbl1459 VALUES (NULL, 1, 2)",
};
std::vector<std::string> queries_SQL2 = {
"INSERT INTO tbl1459 SELECT NULL , i1 + id, i2 + id FROM tbl1459",
};
std::vector<unsigned long long int> queries_limits = {
1, 10, 20, 27, 103, 169, 320, 450, 512, 619, 915, 1022,
1033, 1145, 1516, 1920, 2034, 5014, 9932, 10111,
11033, 11145, 12516, 11920, 12034, 15014, 19932,
21033, 21145, 22516, 21920, 22034, 25014, 29932
};
int run_queries_sets(std::vector<std::string>& queries, MYSQL *my, const std::string& message_prefix) {
for (std::vector<std::string>::iterator it = queries.begin(); it != queries.end(); it++) {
std::string q = *it;
diag("%s: %s", message_prefix.c_str(), q.c_str());
MYSQL_QUERY(my, q.c_str());
}
return 0;
}
#define ITER1 10
// ITER2 is big because we iterate many times in case SSL_ERROR_SYSCALL (interrupted syscall, that is normally a rare event) is triggered
#define ITER2 20
int main(int argc, char** argv) {
CommandLine cl;
if(cl.getEnv())
return exit_status();
unsigned int p = 0;
p += 5*ITER1;
p += (5-3)*ITER2*queries_limits.size(); // only on encrypted backend connections
plan(p);
diag("Testing SSL and fast_forward");
MYSQL* mysqladmin = mysql_init(NULL);
if (!mysqladmin)
return exit_status();
if (!mysql_real_connect(mysqladmin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) {
fprintf(stderr, "File %s, line %d, Error: %s\n",
__FILE__, __LINE__, mysql_error(mysqladmin));
return exit_status();
}
MYSQL * mysqls[5];
for (int i = 0 ; i<5 ; i++) {
mysqls[i] = NULL;
}
// We will loop ITER1 times.
// On each iteration we create 5 connections with different configuration and run a simple SELECT 1
for (int it = 0 ; it<ITER1 ; it++) {
diag("We will reconfigure ProxySQL to use SQLite3 Server on hostgroup 1459, IP 127.0.0.1 and port 6030");
if (run_queries_sets(queries_set1, mysqladmin, "Running on Admin"))
return exit_status();
diag("We now create a connection not using SSL for either client or backend");
mysqls[0] = mysql_init(NULL);
if (!mysqls[0])
return exit_status();
if (!mysql_real_connect(mysqls[0], cl.host, username, password, NULL, cl.port, NULL, 0)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysqls[0]));
return exit_status();
}
MYSQL_QUERY(mysqls[0], "select 1");
MYSQL_RES* result = mysql_store_result(mysqls[0]);
ok(mysql_num_rows(result) == 1, "Line %d: Select statement should be executed on connection 1" , __LINE__);
mysql_free_result(result);
diag("We now create a connection using SSL for client connection only and no SSL for backend");
if (run_queries_sets(queries_set2, mysqladmin, "Running on Admin"))
return exit_status();
mysqls[1] = mysql_init(NULL);
if (!mysqls[1])
return exit_status();
mysql_ssl_set(mysqls[1], NULL, NULL, NULL, NULL, NULL);
if (!mysql_real_connect(mysqls[1], cl.host, username, password, NULL, cl.port, NULL, CLIENT_SSL)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysqls[1]));
return exit_status();
}
MYSQL_QUERY(mysqls[1], "select 1");
result = mysql_store_result(mysqls[1]);
ok(mysql_num_rows(result) == 1, "Line %d: Select statement should be executed on connection 2" , __LINE__);
mysql_free_result(result);
diag("We now create a connection trying to use SSL for backend connection (but SSL is disabled globally) and not SSL for frontend");
if (run_queries_sets(queries_set3, mysqladmin, "Running on Admin"))
return exit_status();
mysqls[2] = mysql_init(NULL);
if (!mysqls[2])
return exit_status();
if (!mysql_real_connect(mysqls[2], cl.host, username, password, NULL, cl.port, NULL, 0)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysqls[2]));
return exit_status();
}
MYSQL_QUERY(mysqls[2], "select 1");
result = mysql_store_result(mysqls[2]);
ok(mysql_num_rows(result) == 1, "Line %d: Select statement should be executed on connection 3" , __LINE__);
mysql_free_result(result);
diag("We now create a connection trying to use SSL for backend connection and not SSL for frontend");
if (run_queries_sets(queries_set4, mysqladmin, "Running on Admin"))
return exit_status();
mysqls[3] = mysql_init(NULL);
if (!mysqls[3])
return exit_status();
if (!mysql_real_connect(mysqls[3], cl.host, username, password, NULL, cl.port, NULL, 0)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysqls[3]));
return exit_status();
}
MYSQL_QUERY(mysqls[3], "select 1");
result = mysql_store_result(mysqls[3]);
ok(mysql_num_rows(result) == 1, "Line %d: Select statement should be executed on connection 4" , __LINE__);
mysql_free_result(result);
diag("We now create a connection using SSL for both client or backend");
if (run_queries_sets(queries_set4, mysqladmin, "Running on Admin")) // note: we use queries_set4 again
return exit_status();
mysqls[4] = mysql_init(NULL);
if (!mysqls[4])
return exit_status();
mysql_ssl_set(mysqls[4], NULL, NULL, NULL, NULL, NULL);
if (!mysql_real_connect(mysqls[4], cl.host, username, password, NULL, cl.port, NULL, CLIENT_SSL)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysqls[4]));
return exit_status();
}
MYSQL_QUERY(mysqls[4], "select 1");
result = mysql_store_result(mysqls[4]);
ok(mysql_num_rows(result) == 1, "Line %d: Select statement should be executed on connection 5" , __LINE__);
mysql_free_result(result);
if (it != ITER1 - 1) {
for (int i = 0 ; i<5 ; i++) {
mysql_close(mysqls[i]);
}
}
}
// We now populate a table named tbl1459
if (run_queries_sets(queries_SQL1, mysqls[0], "Running on SQLite3"))
return exit_status();
for (int i = 0 ; i<15 ; i++) {
if (run_queries_sets(queries_SQL2, mysqls[i%5], "Running on SQLite3"))
return exit_status();
}
for (int it = 0 ; it<ITER2 ; it++) {
// we now run each SELECT FROM tbl1459 on each connections
// we intentionally have the connections in the inner loop so to use all the connection through the test
for (auto it = queries_limits.begin(); it != queries_limits.end(); it++) {
unsigned long long int l = *it;
std::string q = "SELECT * FROM tbl1459 LIMIT " + std::to_string(l);
for (int i=3; i<5; i++) { // only on encrypted backend connections
MYSQL_QUERY(mysqls[i], q.c_str());
MYSQL_RES* result = mysql_store_result(mysqls[i]);
unsigned long long int rr = mysql_num_rows(result);
ok(rr == l, "Line: %d: Select statement on connection %d (%lu): expects %llu rows, returned %llu", __LINE__, i, mysqls[i]->thread_id, l, rr);
mysql_free_result(result);
}
}
}
for (int i=0; i<5; i++) {
diag("Connection %d has thread_id: %lu", i, mysqls[i]->thread_id);
}
mysql_close(mysqladmin);
return exit_status();
}

@ -0,0 +1,255 @@
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <vector>
#include <string>
#include <sstream>
#include <mysql.h>
#include "tap.h"
#include "command_line.h"
#include "utils.h"
/*
this test uses a lot of code from test_ssl_fast_forward-t.cpp
*/
char * username = (char *)"user1459";
char * password = (char *)"pass1459";
std::vector<std::string> queries_set1 = {
"SET mysql-have_ssl='false'",
"LOAD MYSQL VARIABLES TO RUNTIME",
"DELETE FROM mysql_servers WHERE hostgroup_id = 1459",
"INSERT INTO mysql_servers (hostgroup_id, hostname, port, use_ssl) VALUES (1459, '127.0.0.1', 6030, 0)",
"LOAD MYSQL SERVERS TO RUNTIME",
"DELETE FROM mysql_users WHERE username = 'user1459'",
"INSERT INTO mysql_users (username,password,fast_forward,default_hostgroup) VALUES ('" + std::string(username) + "','" + std::string(password) + "',1,1459)",
"LOAD MYSQL USERS TO RUNTIME",
};
std::vector<std::string> queries_set2 = {
"SET mysql-have_ssl='true'",
"LOAD MYSQL VARIABLES TO RUNTIME",
};
std::vector<std::string> queries_set3 = {
"SET mysql-have_ssl='false'",
"LOAD MYSQL VARIABLES TO RUNTIME",
"UPDATE mysql_servers SET use_ssl=1 WHERE hostgroup_id = 1459",
"LOAD MYSQL SERVERS TO RUNTIME",
};
std::vector<std::string> queries_set4 = {
"SET mysql-have_ssl='true'",
"LOAD MYSQL VARIABLES TO RUNTIME",
"UPDATE mysql_servers SET use_ssl=1 WHERE hostgroup_id = 1459",
"LOAD MYSQL SERVERS TO RUNTIME",
};
std::vector<std::string> queries_SQL3 = {
"DROP TABLE IF EXISTS tbl1459v",
"CREATE TABLE tbl1459v (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , t1 VARCHAR)",
};
int run_queries_sets(std::vector<std::string>& queries, MYSQL *my, const std::string& message_prefix) {
for (std::vector<std::string>::iterator it = queries.begin(); it != queries.end(); it++) {
std::string q = *it;
diag("%s: %s", message_prefix.c_str(), q.c_str());
MYSQL_QUERY(my, q.c_str());
}
return 0;
}
#define ITER1 1
#define LL 16000 // lower limit
#define UL 96000 // upper limit
int main(int argc, char** argv) {
CommandLine cl;
if(cl.getEnv())
return exit_status();
unsigned int p = 0;
p += 5*ITER1;
//p += (5-3)*ITER2*queries_limits.size(); // only on encrypted backend connections
p += ((UL-LL)/1000*2);
plan(p);
diag("Testing SSL and fast_forward");
MYSQL* mysqladmin = mysql_init(NULL);
if (!mysqladmin)
return exit_status();
if (!mysql_real_connect(mysqladmin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) {
fprintf(stderr, "File %s, line %d, Error: %s\n",
__FILE__, __LINE__, mysql_error(mysqladmin));
return exit_status();
}
MYSQL * mysqls[5];
for (int i = 0 ; i<5 ; i++) {
mysqls[i] = NULL;
}
// We will loop ITER1 times.
// On each iteration we create 5 connections with different configuration and run a simple SELECT 1
for (int it = 0 ; it<ITER1 ; it++) {
diag("We will reconfigure ProxySQL to use SQLite3 Server on hostgroup 1459, IP 127.0.0.1 and port 6030");
if (run_queries_sets(queries_set1, mysqladmin, "Running on Admin"))
return exit_status();
diag("We now create a connection not using SSL for either client or backend");
mysqls[0] = mysql_init(NULL);
if (!mysqls[0])
return exit_status();
if (!mysql_real_connect(mysqls[0], cl.host, username, password, NULL, cl.port, NULL, 0)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysqls[0]));
return exit_status();
}
MYSQL_QUERY(mysqls[0], "select 1");
MYSQL_RES* result = mysql_store_result(mysqls[0]);
ok(mysql_num_rows(result) == 1, "Line %d: Select statement should be executed on connection 1" , __LINE__);
mysql_free_result(result);
diag("We now create a connection using SSL for client connection only and no SSL for backend");
if (run_queries_sets(queries_set2, mysqladmin, "Running on Admin"))
return exit_status();
mysqls[1] = mysql_init(NULL);
if (!mysqls[1])
return exit_status();
mysql_ssl_set(mysqls[1], NULL, NULL, NULL, NULL, NULL);
if (!mysql_real_connect(mysqls[1], cl.host, username, password, NULL, cl.port, NULL, CLIENT_SSL)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysqls[1]));
return exit_status();
}
MYSQL_QUERY(mysqls[1], "select 1");
result = mysql_store_result(mysqls[1]);
ok(mysql_num_rows(result) == 1, "Line %d: Select statement should be executed on connection 2" , __LINE__);
mysql_free_result(result);
diag("We now create a connection trying to use SSL for backend connection (but SSL is disabled globally) and not SSL for frontend");
if (run_queries_sets(queries_set3, mysqladmin, "Running on Admin"))
return exit_status();
mysqls[2] = mysql_init(NULL);
if (!mysqls[2])
return exit_status();
if (!mysql_real_connect(mysqls[2], cl.host, username, password, NULL, cl.port, NULL, 0)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysqls[2]));
return exit_status();
}
MYSQL_QUERY(mysqls[2], "select 1");
result = mysql_store_result(mysqls[2]);
ok(mysql_num_rows(result) == 1, "Line %d: Select statement should be executed on connection 3" , __LINE__);
mysql_free_result(result);
diag("We now create a connection trying to use SSL for backend connection and not SSL for frontend");
if (run_queries_sets(queries_set4, mysqladmin, "Running on Admin"))
return exit_status();
mysqls[3] = mysql_init(NULL);
if (!mysqls[3])
return exit_status();
if (!mysql_real_connect(mysqls[3], cl.host, username, password, NULL, cl.port, NULL, 0)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysqls[3]));
return exit_status();
}
MYSQL_QUERY(mysqls[3], "select 1");
result = mysql_store_result(mysqls[3]);
ok(mysql_num_rows(result) == 1, "Line %d: Select statement should be executed on connection 4" , __LINE__);
mysql_free_result(result);
diag("We now create a connection using SSL for both client or backend");
if (run_queries_sets(queries_set4, mysqladmin, "Running on Admin")) // note: we use queries_set4 again
return exit_status();
mysqls[4] = mysql_init(NULL);
if (!mysqls[4])
return exit_status();
mysql_ssl_set(mysqls[4], NULL, NULL, NULL, NULL, NULL);
if (!mysql_real_connect(mysqls[4], cl.host, username, password, NULL, cl.port, NULL, CLIENT_SSL)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysqls[4]));
return exit_status();
}
MYSQL_QUERY(mysqls[4], "select 1");
result = mysql_store_result(mysqls[4]);
ok(mysql_num_rows(result) == 1, "Line %d: Select statement should be executed on connection 5" , __LINE__);
mysql_free_result(result);
if (it != ITER1 - 1) {
for (int i = 0 ; i<5 ; i++) {
mysql_close(mysqls[i]);
}
}
}
for (int i=0; i<5; i++) {
diag("Connection %d has thread_id: %lu", i, mysqls[i]->thread_id);
}
// We now sends long INSERTs. This code is similar of test_ssl_large_query-t.cpp
// We now populate a table named tbl1459v
if (run_queries_sets(queries_SQL3, mysqls[0], "Running on SQLite3"))
return exit_status();
std::string s0 = "0";
//for (int i=16001; i<=48000; i++) {
for (int i=LL; i<UL; i+=10) {
std::string s1= "";
for (int j=0; j<i; j++) {
s1 += s0;
}
s1 += "')";
// we intentionally have the connections in the inner loop so to use all the connection through the test
// we only loop on the backend connections with SSL
for (int c=3; c<5; c++) {
unsigned int id = c*UL+i;
std::string s = "INSERT INTO tbl1459v VALUES (" + std::to_string(id) + ",'";
s += s1;
std::string del = "DELETE FROM tbl1459v WHERE id = " + std::to_string(id);
MYSQL_QUERY(mysqls[c], del.c_str());
MYSQL_QUERY(mysqls[c], s.c_str());
if (i%1000 == 0) {
ok(i, "Executed INSERT with id=%d (%lu bytes) on connection %d", id, s.length(), c); // this can be a simple diag, but we use ok() to track progress
}
}
}
mysql_close(mysqladmin);
return exit_status();
}

@ -0,0 +1,142 @@
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <vector>
#include <string>
#include <sstream>
#include <mutex>
#include <mysql.h>
#include "tap.h"
#include "command_line.h"
#include "utils.h"
#include <assert.h>
char * username = (char *)"user1459";
char * password = (char *)"pass1459";
const std::string lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
std::vector<std::string> queries_set1 = {
"SET mysql-have_ssl='true'",
"LOAD MYSQL VARIABLES TO RUNTIME",
"DELETE FROM mysql_servers WHERE hostgroup_id = 1459",
"INSERT INTO mysql_servers (hostgroup_id, hostname, port, use_ssl) VALUES (1459, '127.0.0.1', 6030, 1)",
"LOAD MYSQL SERVERS TO RUNTIME",
"DELETE FROM mysql_users WHERE username = 'user1459'",
"INSERT INTO mysql_users (username,password,fast_forward,default_hostgroup) VALUES ('" + std::string(username) + "','" + std::string(password) + "',1,1459)",
"LOAD MYSQL USERS TO RUNTIME",
};
int run_queries_sets(std::vector<std::string>& queries, MYSQL *my, const std::string& message_prefix) {
for (std::vector<std::string>::iterator it = queries.begin(); it != queries.end(); it++) {
std::string q = *it;
diag("%s: %s", message_prefix.c_str(), q.c_str());
MYSQL_QUERY(my, q.c_str());
}
return 0;
}
#define ITER 400
#define NTHR 4
#define CPTH 100
CommandLine cl;
std::mutex mtx_;
int my_conn_thread_in(void *arg) {
MYSQL * mysqls[CPTH];
for (int i = 0 ; i<CPTH ; i++) {
mysqls[i] = mysql_init(NULL);
if (i%2==0) {
mysql_ssl_set(mysqls[i], NULL, NULL, NULL, NULL, NULL);
}
if (!mysql_real_connect(mysqls[i], cl.host, username, password, NULL, cl.port, NULL, 0)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysqls[i]));
return exit_status();
}
if (i%2==0) {
const char * c = mysql_get_ssl_cipher(mysqls[i]);
std::lock_guard<std::mutex> lock(mtx_);
ok(c != NULL , "Cipher in use: %s", c == NULL ? "NULL" : c);
}
}
for (int i=1; i<=ITER; i++) {
std::string s = "SELECT ''";
for (int j=0; j<i; j++) {
s+= "||'" + lorem + "'";
}
for (int j=0; j<4; j++) { // we run the same query on 4 different connections
MYSQL_QUERY(mysqls[(i+j)%CPTH], s.c_str());
MYSQL_RES* result = mysql_store_result(mysqls[(i+j)%CPTH]);
MYSQL_ROW row = mysql_fetch_row(result);
long int rl = strlen(row[0]);
mysql_free_result(result);
if (j==0) {
std::lock_guard<std::mutex> lock(mtx_);
ok(s.length() == rl + strlen((const char *)"SELECT ''") + i*4 , "Line %d : on connection %ld , executed SELECT %ld bytes long. Length returned: %ld", __LINE__ , mysqls[(i+j)%CPTH]->thread_id, s.length(), rl);
}
}
}
return 0;
}
void * my_conn_thread(void *arg) {
diag("Starting thread...");
int rc = my_conn_thread_in(NULL);
diag("... thread ended!");
return NULL;
}
int main(int argc, char** argv) {
if(cl.getEnv())
return exit_status();
plan(NTHR*(ITER+CPTH/2));
diag("Testing SSL and fast_forward");
MYSQL* mysqladmin = mysql_init(NULL);
if (!mysqladmin)
return exit_status();
if (!mysql_real_connect(mysqladmin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) {
fprintf(stderr, "File %s, line %d, Error: %s\n",
__FILE__, __LINE__, mysql_error(mysqladmin));
return exit_status();
}
// We will loop ITER1 times.
// On each iteration we create 5 connections with different configuration and run a simple SELECT 1
//for (int it = 0 ; it<ITER1 ; it++) {
diag("We will reconfigure ProxySQL to use SQLite3 Server on hostgroup 1459, IP 127.0.0.1 and port 6030");
if (run_queries_sets(queries_set1, mysqladmin, "Running on Admin"))
return exit_status();
pthread_t *thi=(pthread_t *)malloc(sizeof(pthread_t)*NTHR);
if (thi==NULL)
return exit_status();
for (unsigned int i=0; i<NTHR; i++) {
if ( pthread_create(&thi[i], NULL, my_conn_thread , NULL) != 0 )
perror("Thread creation");
}
for (unsigned int i=0; i<NTHR; i++) {
pthread_join(thi[i], NULL);
}
mysql_close(mysqladmin);
return exit_status();
}

@ -0,0 +1,106 @@
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <vector>
#include <string>
#include <sstream>
#include <mysql.h>
#include "tap.h"
#include "command_line.h"
#include "utils.h"
char * username = (char *)"user1459";
char * password = (char *)"pass1459";
std::vector<std::string> queries_set1 = {
"SET mysql-have_ssl='true'",
"LOAD MYSQL VARIABLES TO RUNTIME",
"DELETE FROM mysql_servers WHERE hostgroup_id = 1459",
"INSERT INTO mysql_servers (hostgroup_id, hostname, port, use_ssl) VALUES (1459, '127.0.0.1', 6030, 0)",
"LOAD MYSQL SERVERS TO RUNTIME",
"DELETE FROM mysql_users WHERE username = 'user1459'",
"INSERT INTO mysql_users (username,password,default_hostgroup) VALUES ('" + std::string(username) + "','" + std::string(password) + "',1459)",
"LOAD MYSQL USERS TO RUNTIME",
};
std::vector<std::string> queries_SQL1 = {
"DROP TABLE IF EXISTS tbl1459",
"CREATE TABLE tbl1459 (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , t1 VARCHAR)",
};
int run_queries_sets(std::vector<std::string>& queries, MYSQL *my, const std::string& message_prefix) {
for (std::vector<std::string>::iterator it = queries.begin(); it != queries.end(); it++) {
std::string q = *it;
diag("%s: %s", message_prefix.c_str(), q.c_str());
MYSQL_QUERY(my, q.c_str());
}
return 0;
}
int main(int argc, char** argv) {
CommandLine cl;
if(cl.getEnv())
return exit_status();
plan(33);
diag("Testing SSL and fast_forward");
MYSQL* mysqladmin = mysql_init(NULL);
if (!mysqladmin)
return exit_status();
if (!mysql_real_connect(mysqladmin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) {
fprintf(stderr, "File %s, line %d, Error: %s\n",
__FILE__, __LINE__, mysql_error(mysqladmin));
return exit_status();
}
MYSQL * mysql = NULL;
diag("We will reconfigure ProxySQL to use SQLite3 Server on hostgroup 1459, IP 127.0.0.1 and port 6030");
if (run_queries_sets(queries_set1, mysqladmin, "Running on Admin"))
return exit_status();
mysql = mysql_init(NULL);
if (!mysql)
return exit_status();
mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL);
if (!mysql_real_connect(mysql, cl.host, username, password, NULL, cl.port, NULL, CLIENT_SSL)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysql));
return exit_status();
}
const char * c = mysql_get_ssl_cipher(mysql);
ok(c != NULL , "Cipher in use: %s", c == NULL ? "NULL" : c);
// We now create a table named tbl1459
if (run_queries_sets(queries_SQL1, mysql, "Running on SQLite3"))
return exit_status();
std::string s0 = "0";
for (int i=16001; i<=48000; i++) {
std::string s = "INSERT INTO tbl1459 VALUES (" + std::to_string(i) + ",'";
for (int j=0; j<i; j++) {
s += s0;
}
s += "')";
MYSQL_QUERY(mysql, s.c_str());
if (i%1000 == 0) {
ok(i, "Executed INSERT with id=%d", i); // this can be a simple diag, but we use ok() to track progress
}
}
mysql_close(mysql);
mysql_close(mysqladmin);
return exit_status();
}

@ -0,0 +1,128 @@
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <vector>
#include <string>
#include <sstream>
#include <mysql.h>
#include "tap.h"
#include "command_line.h"
#include "utils.h"
char * username = (char *)"user1459";
char * password = (char *)"pass1459";
std::vector<std::string> queries_set1 = {
"SET mysql-have_ssl='true'",
"LOAD MYSQL VARIABLES TO RUNTIME",
"DELETE FROM mysql_users WHERE username = 'user1459'",
"INSERT INTO mysql_users (username,password,default_hostgroup) VALUES ('" + std::string(username) + "','" + std::string(password) + "',1459)",
"LOAD MYSQL USERS TO RUNTIME",
};
std::vector<std::string> queries_SQL1 = {
"DROP TABLE IF EXISTS tbl1459",
"CREATE TABLE tbl1459 (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , t1 VARCHAR)",
};
int run_queries_sets(std::vector<std::string>& queries, MYSQL *my, const std::string& message_prefix) {
for (std::vector<std::string>::iterator it = queries.begin(); it != queries.end(); it++) {
std::string q = *it;
diag("%s: %s", message_prefix.c_str(), q.c_str());
MYSQL_QUERY(my, q.c_str());
}
return 0;
}
#define ITER 400
const std::string lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
int main(int argc, char** argv) {
CommandLine cl;
if(cl.getEnv())
return exit_status();
plan(2+2*ITER);
diag("Testing SSL and fast_forward");
MYSQL* mysqladmin = mysql_init(NULL);
if (!mysqladmin)
return exit_status();
if (!mysql_real_connect(mysqladmin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) {
fprintf(stderr, "File %s, line %d, Error: %s\n",
__FILE__, __LINE__, mysql_error(mysqladmin));
return exit_status();
}
if (run_queries_sets(queries_set1, mysqladmin, "Running on Admin"))
return exit_status();
MYSQL * mysqladmin2 = mysql_init(NULL);
if (!mysqladmin2)
return exit_status();
mysql_ssl_set(mysqladmin2, NULL, NULL, NULL, NULL, NULL);
if (!mysql_real_connect(mysqladmin2, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) {
fprintf(stderr, "File %s, line %d, Error: %s\n",
__FILE__, __LINE__, mysql_error(mysqladmin2));
return exit_status();
}
{
const char * c = mysql_get_ssl_cipher(mysqladmin2);
ok(c != NULL , "Cipher in use: %s", c == NULL ? "NULL" : c);
}
MYSQL * mysqllite3 = mysql_init(NULL);
if (!mysqllite3)
return exit_status();
mysql_ssl_set(mysqllite3, NULL, NULL, NULL, NULL, NULL);
if (!mysql_real_connect(mysqllite3, cl.host, username, password, NULL, 6030, NULL, 0)) {
fprintf(stderr, "File %s, line %d, Error: %s\n",
__FILE__, __LINE__, mysql_error(mysqllite3));
return exit_status();
}
{
const char * c = mysql_get_ssl_cipher(mysqllite3);
ok(c != NULL , "Cipher in use: %s", c == NULL ? "NULL" : c);
}
for (int i=1; i<=ITER; i++) {
std::string s = "SELECT ''";
for (int j=0; j<i; j++) {
s+= "||'" + lorem + "'";
}
MYSQL_QUERY(mysqladmin2, s.c_str());
MYSQL_RES* result = mysql_store_result(mysqladmin2);
MYSQL_ROW row = mysql_fetch_row(result);
long int rl = strlen(row[0]);
mysql_free_result(result);
ok(s.length() == rl + strlen((const char *)"SELECT ''") + i*4 , "Line %d , Admin: Executed SELECT %ld bytes long. Length returned: %ld", __LINE__ , s.length(), rl);
MYSQL_QUERY(mysqllite3, s.c_str());
result = mysql_store_result(mysqllite3);
row = mysql_fetch_row(result);
rl = strlen(row[0]);
mysql_free_result(result);
ok(s.length() == rl + strlen((const char *)"SELECT ''") + i*4 , "Line %d , SQLite3: Executed SELECT %ld bytes long. Length returned: %ld", __LINE__ , s.length(), rl);
}
mysql_close(mysqladmin);
return exit_status();
}
Loading…
Cancel
Save