#include <iostream>
#include <pthread.h>
#include "mysql.h"

using namespace std;

//MySQL server details
//Modify to match your server details
#define HOST "monyog.cgrenhjbkg50.us-east-1.rds.amazonaws.com"
#define PORT 3306
#define USERNAME "test_ssl"
#define PASSWORD "test_ssl"

//Number of threads to be created
#define THREADS_TO_CREATE 3


static void *Threads(void * arg);

int main() {
    pthread_t array_of_threads[THREADS_TO_CREATE];
    int thread_index_array[THREADS_TO_CREATE];
    int thread_index;


    /*
     * mysql_library_init() - initializes the MySQL library before any other MySQL function is called.
     * 
     * Refer: http://dev.mysql.com/doc/refman/5.0/en/mysql-library-init.html
     * 
     * Returns:
     * 0 (Zero) if successful. Nonzero if an error occurred.
     */
    if (mysql_library_init(0, NULL, NULL)) {
        cout << "mysql_library_init() is un-successful\n";
        cout << "Exiting program\n";
        return -1;
    } else {
        cout << "mysql_library_init() is successful\n";
    }

    /*
     * Spawn threads to connect and execute the query on MySQL Server.
     */
    for (thread_index = 0; thread_index < THREADS_TO_CREATE; thread_index++) {
        thread_index_array[thread_index] = thread_index;
        pthread_create(&array_of_threads[thread_index], NULL, Threads, &thread_index_array[thread_index]);
    }

    /*
     * Joining the completed threads.
     */
    for (thread_index = 0; thread_index < THREADS_TO_CREATE; thread_index++) {
        pthread_join(array_of_threads[thread_index], NULL);
    }

    /*
     * mysql_library_end() - finalizes the MySQL library. This is called when done using the library.
     * 
     * Refer: http://dev.mysql.com/doc/refman/5.0/en/mysql-library-end.html
     * 
     * Returns: none
     */
    mysql_library_end();

    return 0;
}

void *Threads(void * arg) {

     int *a =(int *) arg;    
     
     /*
     * Calling implicit call to mysql_thread_init(). 
     * 
     * Refer: http://dev.mysql.com/doc/refman/5.0/en/mysql-thread-init.html
     * 
     */
    mysql_thread_init();

    int connect_timeout(30);

    MYSQL * mysql;
    MYSQL_RES * res;

    /*
     * Initializing a MySQL object suitable for mysql_real_connect()
     * if the object initialized is NULL then we are returning from the function. 
     * 
     * Refer: http://dev.mysql.com/doc/refman/5.0/en/my-init.html
     * 
     * Returns: none
     */
    mysql = mysql_init(NULL);
    if (mysql == NULL) {
        cout << "Failed to initialize the MySQL object\n";
        mysql_thread_end();
        return NULL;
    }


    /* Setting ssl option
    *
    *  Refer: https://dev.mysql.com/doc/refman/5.7/en/mysql-ssl-set.html
    *  Returns:0
    *  
    */
    mysql_ssl_set(mysql, NULL, NULL, "cacert.pem", NULL, NULL);
	
    /*
     * Setting up MySQL client connect timeout to 30 seconds.
     * MYSQL_OPT_CONNECT_TIMEOUT (argument type: unsigned int *)
     * Connect timeout is in seconds.
     * 
     * Refer: http://dev.mysql.com/doc/refman/5.0/en/mysql-options.html
     * 
     * Returns:
     * 0 (Zero) for success. Nonzero if unknown option specified.
     */
    if (mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (const char *) &connect_timeout)) {
        cout << "Unknown option set\n";
    }
     
    /*
     * attempting to establish connection to a MySQLServer.
     * 
     * Refer: http://dev.mysql.com/doc/refman/5.0/en/mysql-real-connect.html
     * 
     * Returns:
     * A MYSQL* connection handle if the connection was successful, 
     * NULL if the connection was unsuccessful.
     */
    if (mysql_real_connect(mysql, HOST, USERNAME, PASSWORD, NULL, PORT, NULL, 0) == NULL) {
        cout << "Connection "<<*a<< " - Error mysql_real_connect: " << mysql_error(mysql) << "\n";
        if (mysql) {
            mysql_close(mysql);
        }

        /*
         * Freeing memory allocated by mysql_thread_init().
         */
        mysql_thread_end();
        return NULL;
    }
    cout <<"Connection "<< *a<<" - connected successfully\n";

        /*
         * Executing SQL statement.
         * 
         * Refer: http://dev.mysql.com/doc/refman/5.0/en/mysql-real-query.html
         * 
         * Returns: 
         * 0 (Zero) if the statement was successful. Nonzero if an error occurred.
         */
        if (mysql_query(mysql, "SELECT SLEEP(1)")) {
            cout << "Error mysql_query: " << mysql_error(mysql) << "\n";
            if (mysql) {
                mysql_close(mysql);
            }

            /*
             * Freeing memory allocated by mysql_thread_init().
             */
            mysql_thread_end();
            return NULL;
        }
	cout<<"Connection "<<*a << " - Executed Successfully\n";

        /*
         * Storing the result to MYSQL_RES, to reuse the connection for querying again
         * and then freeing the result set. 
         * 
         * Refer: http://dev.mysql.com/doc/refman/5.0/en/mysql-store-result.html
         * Refer: http://dev.mysql.com/doc/refman/5.0/en/mysql-free-result.html
         */
        res = mysql_store_result(mysql);
        mysql_free_result(res);

    /*
     * Closing a previously opened connection.
     * 
     * Refer: http://dev.mysql.com/doc/refman/5.0/en/mysql-close.html
     */
    mysql_close(mysql);

    /*
     * Freeing memory allocated by mysql_thread_init().
     * 
     * Refer: http://dev.mysql.com/doc/refman/5.0/en/mysql-thread-end.html
     */
    mysql_thread_end();

    return NULL;
}
