/*
 * bug-14013.c - mysql_stmt_store_result() results in error if stmt
 * has a cursor open.
 *
 * To trigger bug, see comments in main().
 */

#include <string.h>		/* for strdup() */
#include <my_global.h>
#include <my_sys.h>
#include <mysql.h>
#include <my_getopt.h>

static char *opt_host_name = NULL;		/* server host (default=localhost) */
static char *opt_user_name = NULL;		/* username (default=login name) */
static char *opt_password = NULL;		/* password (default=none) */
static unsigned int opt_port_num = 0;	/* port number (use built-in value) */
static char *opt_socket_name = NULL;	/* socket name (use built-in value) */
static char *opt_db_name = NULL;		/* database name (default=none) */
static unsigned int opt_flags = 0;		/* connection flags (none) */

static int ask_password = 0;			/* whether to solicit password */

static MYSQL *conn;						/* pointer to connection handler */

static const char *client_groups[] = { "client", NULL };

static struct my_option my_opts[] =		/* option information structures */
{
	{"help", '?', "Display this help and exit",
	NULL, NULL, NULL,
	GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
	{"host", 'h', "Host to connect to",
	(gptr *) &opt_host_name, NULL, NULL,
	GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
	{"password", 'p', "Password",
	(gptr *) &opt_password, NULL, NULL,
	GET_STR_ALLOC, OPT_ARG, 0, 0, 0, 0, 0, 0},
	{"port", 'P', "Port number",
	(gptr *) &opt_port_num, NULL, NULL,
	GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
	{"socket", 'S', "Socket path",
	(gptr *) &opt_socket_name, NULL, NULL,
	GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
	{"user", 'u', "User name",
	(gptr *) &opt_user_name, NULL, NULL,
	GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
	{ NULL, 0, NULL, NULL, NULL, NULL, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }
};

static void
print_error (MYSQL *conn, char *message)
{
	fprintf (stderr, "%s\n", message);
	if (conn != NULL)
	{
		fprintf (stderr, "Error %u (%s): %s\n",
			mysql_errno (conn), mysql_sqlstate(conn), mysql_error (conn));
	}
}

static void
print_stmt_error (MYSQL_STMT *stmt, char *message)
{
	fprintf (stderr, "%s\n", message);
	if (stmt != NULL)
	{
		fprintf (stderr, "Error %u (%s): %s\n",
				mysql_stmt_errno (stmt),
				mysql_stmt_sqlstate(stmt),
				mysql_stmt_error (stmt));
	}
}

static my_bool
get_one_option (int optid, const struct my_option *opt, char *argument)
{
	switch (optid)
	{
	case '?':
		my_print_help (my_opts);	/* print help message */
		exit (0);
	case 'p':						/* password */
		if (!argument)				/* no value given; solicit it later */
			ask_password = 1;
		else						/* copy password, wipe out original */
		{
			opt_password = strdup (argument);
			if (opt_password == NULL)
			{
				print_error (NULL, "could not allocate password buffer");
				exit (1);
			}
			while (*argument)
				*argument++ = 'x';
			ask_password = 0;
		}
		break;
	}
	return (0);
}


int
main (int argc, char *argv[])
{
int opt_err;
MYSQL_STMT	*stmt;
char		*use_stmt = "USE test";
char		*drop_stmt = "DROP TABLE IF EXISTS t";
char		*create_stmt = "CREATE TABLE t (i INT)";
char		*insert_stmt = "INSERT INTO t VALUES(1),(2)";
char		*stmt_str = "SELECT i FROM t";
MYSQL_BIND	param[1];
int			my_int;
my_bool		is_null[1];

int rc;
unsigned long type;
unsigned long prefetch_rows = 5;

/*
 * BUG:
 * If both of these variables are 1, an error will occur for
 * mysql_stmt_store_result().  Any other combination of 0/1 works.
 */

int use_cursor = 1;
int store_result = 1;

	MY_INIT (argv[0]);
	load_defaults ("my", client_groups, &argc, &argv);

	if ((opt_err = handle_options (&argc, &argv, my_opts, get_one_option)))
		exit (opt_err);

	/* solicit password if necessary */
	if (ask_password)
		opt_password = get_tty_password (NULL);

	/* get database name if present on command line */
	if (argc > 0)
	{
		opt_db_name = argv[0];
		--argc; ++argv;
	}

	/* initialize connection handler */
	conn = mysql_init (NULL);
	if (conn == NULL)
	{
		print_error (NULL, "mysql_init() failed (probably out of memory)");
		exit (1);
	}

	/* connect to server */
	if (mysql_real_connect (conn, opt_host_name, opt_user_name, opt_password,
			opt_db_name, opt_port_num, opt_socket_name, opt_flags) == NULL)
	{
		print_error (conn, "mysql_real_connect() failed");
		mysql_close (conn);
		exit (1);
	}

	/* select database and create test table */

	if (mysql_query (conn, use_stmt) != 0
		|| mysql_query (conn, drop_stmt) != 0
		|| mysql_query (conn, create_stmt) != 0
		|| mysql_query (conn, insert_stmt) != 0)
	{
		print_error (conn, "Could not set up test table");
		exit (1);
	}

	stmt = mysql_stmt_init (conn);	/* allocate statement handler */
	if (stmt == NULL)
	{
		print_error (conn, "Could not initialize statement handler");
		exit (1);
	}

if (use_cursor)
{
	stmt = mysql_stmt_init(conn);
	type = (unsigned long) CURSOR_TYPE_READ_ONLY;
	rc = mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type);
	if (rc)
	{
		print_stmt_error (stmt, "Could not open cursor");
		exit (1);
	}
	rc = mysql_stmt_attr_set(stmt, STMT_ATTR_PREFETCH_ROWS,
						  (void*) &prefetch_rows);
	if (rc)
	{
		print_stmt_error (stmt, "Could not set prefetch size");
		exit (1);
	}
}

	if (mysql_stmt_prepare (stmt, stmt_str, strlen (stmt_str)) != 0)
	{
		print_stmt_error (stmt, "Could not prepare SELECT statement");
		exit (1);
	}

	if (mysql_stmt_field_count (stmt) != 1)
	{
		print_stmt_error (stmt, "Unexpected column count from SELECT");
		exit (1);
	}

	/*
	 * initialize the result column structures
	 */

	memset ((void *) param, 0, sizeof (param));	/* zero the structures */

	/* set up INT parameter */

	param[0].buffer_type = MYSQL_TYPE_LONG;
	param[0].buffer = (void *) &my_int;
	param[0].is_unsigned = 0;
	param[0].is_null = &is_null[0];
	/* buffer_length, length need not be set */

	if (mysql_stmt_bind_result (stmt, param) != 0)
	{
		print_stmt_error (stmt, "Could not bind parameters for SELECT");
		exit (1);
	}

	if (mysql_stmt_execute (stmt) != 0)
	{
		print_stmt_error (stmt, "Could not execute SELECT");
		exit (1);
	}

	/*
	 * fetch result set into client memory; this is optional, but it
	 * allows mysql_stmt_num_rows() to be called to determine the
	 * number of rows in the result set.
	 */

if (store_result)
{
	if (mysql_stmt_store_result (stmt) != 0)
	{
		print_stmt_error (stmt, "Could not buffer result set");
		exit (1);
	}
	else
	{
		/* mysql_stmt_store_result() makes row count available */
		printf ("Number of rows retrieved: %lu\n",
						(unsigned long) mysql_stmt_num_rows (stmt));
	}
}

	while (mysql_stmt_fetch (stmt) == 0)	/* fetch each row */
	{
		/* display row values */
		printf ("%d\n", my_int);
	}

	mysql_stmt_free_result (stmt);			/* deallocate result set */


	mysql_stmt_close (stmt);		/* deallocate statement handler */

	/* disconnect from server */
	mysql_close (conn);
	exit (0);
}
