#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <mysql.h>

#define STMT_QUERY "select * from test.t1"
#define STRING_SIZE 50


MYSQL *test_connect(char *host, unsigned int port, char *user, char *passwd)
{
    MYSQL *mysql = mysql_init(NULL);
    unsigned int ssl_mode = SSL_MODE_DISABLED;
    mysql_options(mysql, MYSQL_OPT_SSL_MODE, &ssl_mode);

    if (NULL == mysql_real_connect(mysql, host, user, passwd, NULL, port, NULL, 0)) {
        fprintf(stderr, "Connect to [%s:%u] with [%s:%s] failed(%u:%s)\n",
                host, port, user, passwd, mysql_errno(mysql), mysql_error(mysql));
        return NULL;
    }

    fprintf(stdout, "Connect to [%s:%u] with [%s:%s]\n", host, port, user, passwd);

    return mysql;
}

void test_init(MYSQL *mysql)
{
    mysql_query(mysql, "drop table if exists test.t1");
    mysql_query(mysql, "create table test.t1(a int primary key, b char(10))");
    mysql_query(mysql, "insert into test.t1 values(1,'111a')");   
    mysql_query(mysql, "insert into test.t1 values(2,'222b')");   
    mysql_query(mysql, "insert into test.t1 values(3,'333c')");   
    mysql_query(mysql, "insert into test.t1 values(4,'444d')");   
}

MYSQL_STMT *test_stmt_init(MYSQL *mysql)
{
    MYSQL_STMT *stmt = mysql_stmt_init(mysql);
    if (!stmt)
        fprintf(stderr, "mysql_stmt_init() failed(%u:%s)\n", mysql_errno(mysql), mysql_error(mysql));
    return stmt;
}

int test_stmt_prepare(MYSQL_STMT *stmt)
{
    if (mysql_stmt_prepare(stmt, STMT_QUERY, strlen(STMT_QUERY))) {
        fprintf(stderr, "mysql_stmt_prepare() failed(%u:%s)\n", mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
        return -1;
    }
    return 0; 
}

int test_stmt_open_cursor(MYSQL_STMT *stmt)
{
    unsigned long type = (unsigned long) CURSOR_TYPE_READ_ONLY;
    if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void *) &type)) {
        fprintf(stderr, "mysql_stmt_attr_set(CURSOR_TYPE_READ_ONLY) failed(%u:%s)\n", mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
        return -1;
    }

    unsigned long prefetch_rows = 2;
    if (mysql_stmt_attr_set(stmt, STMT_ATTR_PREFETCH_ROWS, (void *) &prefetch_rows)) {
        fprintf(stderr, "mysql_stmt_attr_set(STMT_ATTR_PREFETCH_ROWS) failed(%u:%s)\n", mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
        return -1;
    }
    return 0;
}

int test_stmt_close_cursor(MYSQL_STMT *stmt)
{
    unsigned long type = (unsigned long) CURSOR_TYPE_NO_CURSOR;
    if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void *) &type)) {
        fprintf(stderr, "mysql_stmt_attr_set(CURSOR_TYPE_NO_CURSOR) failed(%u:%s)\n", mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
        return -1;
    }
    return 0;
}

int main(int argc, char* argv[])
{
    char *host   = argv[1];
    char *port   = argv[2];
    char *user   = argv[3];
    char *passwd = argv[4];

    MYSQL *mysql = NULL;
    MYSQL_STMT *stmt = NULL;

    mysql = test_connect(host, atoi(port), user, passwd);
    if (!mysql)
        goto exit;
   
    test_init(mysql);

    stmt = test_stmt_init(mysql);
    if (!stmt)
        goto exit;

    if (test_stmt_prepare(stmt))
        goto exit; 

    MYSQL_BIND bind[2];
    unsigned long length[2];
    bool is_null[2];
    bool error[2];
    int row_count = 0;
    int int_data;
    char str_data[STRING_SIZE];

    /* INTEGER COLUMN */
    bind[0].buffer_type= MYSQL_TYPE_LONG;
    bind[0].buffer= (char *)&int_data;
    bind[0].is_null= &is_null[0];
    bind[0].length= &length[0];
    bind[0].error= &error[0];

    /* STRING COLUMN */
    bind[1].buffer_type= MYSQL_TYPE_STRING;
    bind[1].buffer= (char *)str_data;
    bind[1].buffer_length= STRING_SIZE;
    bind[1].is_null= &is_null[1];
    bind[1].length= &length[1];
    bind[1].error= &error[1];

    /* Bind the result buffers */
    if (mysql_stmt_bind_result(stmt, bind)) {
        fprintf(stderr, "mysql_stmt_bind_result() failed(%u:%s)\n", mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
        goto exit;
    }

    if (test_stmt_open_cursor(stmt))
        goto exit;

    if (mysql_stmt_execute(stmt)) {
        fprintf(stderr, "mysql_stmt_execute() with cursor failed(%u:%s)\n", mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
        goto exit;
    }
    
    fprintf(stdout, "cursor fetch:\n");
    while (!mysql_stmt_fetch(stmt)) {
        row_count++;
        fprintf(stdout, "  Row %d\n", row_count);

        /* column 1 */
        fprintf(stdout, "    column1 (integer)  :  %d\n", int_data);

        /* column 2 */
        fprintf(stdout, "    column2 (string)   :  %s\n", str_data);
        fprintf(stdout, "\n");

        if (row_count >= 2)
            break;
    }
    
    if (mysql_stmt_reset(stmt)) {
        fprintf(stderr, "mysql_stmt_reset() failed(%u:%s)\n", mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
        goto exit;
    }

    if (test_stmt_close_cursor(stmt))
        goto exit;
    
    if (mysql_stmt_execute(stmt)) {
        fprintf(stderr, "mysql_stmt_execute() without cursor failed(%u:%s)\n", mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
        goto exit;
    }
    
    if (mysql_stmt_store_result(stmt)) {
        fprintf(stderr, "mysql_stmt_store_result() failed(%u:%s)\n", mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
        goto exit;
    }
    
    fprintf(stdout, "fetch:\n");
    row_count = 0;
    while (!mysql_stmt_fetch(stmt)) {
        row_count++;
        fprintf(stdout, "  row %d\n", row_count);

        /* column 1 */
        fprintf(stdout, "   column1 (integer)  :  %d\n", int_data);

        /* column 2 */
        fprintf(stdout, "   column2 (string)   :  %s\n", str_data);
        fprintf(stdout, "\n");
    }

  exit:
    if (stmt)
        mysql_stmt_close(stmt);
    if (mysql)
        mysql_close(mysql);
}
