/*
 * spbug.c - demonstrate bug (in parsing?) such that a stored routine
 * that does *not* use a compound statement results in a syntax error
 * under multi-statement execution.
 */

#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 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);
}

void
print_dashes (MYSQL_RES *res_set)
{
MYSQL_FIELD		*field;
unsigned int	i, j;

	mysql_field_seek (res_set, 0);
	fputc ('+', stdout);
	for (i = 0; i < mysql_num_fields (res_set); i++)
	{
		field = mysql_fetch_field (res_set);
		for (j = 0; j < field->max_length + 2; j++)
			fputc ('-', stdout);
		fputc ('+', stdout);
	}
	fputc ('\n', stdout);
}

void
process_multi_statement (MYSQL *conn, char *stmt_str)
{
MYSQL_RES	*res_set;
int			status;
int			keep_going = 1;

	if (mysql_query (conn, stmt_str) != 0)	/* the statement(s) failed */
	{
		print_error (conn, "Could not execute statement(s)");
		return;
	}

	/* the statement(s) succeeded; enter result-retrieval loop */
	do {
		/* determine whether current statement returned data */
		res_set = mysql_store_result (conn);
		if (res_set)			/* a result set was returned */
		{
			/* print number of rows, and discard data */
			printf ("%lu rows returned\n",
				(unsigned long) mysql_num_rows (res_set));
			mysql_free_result (res_set);
		}
		else					/* no result set was returned */
		{
			/*
			 * does the lack of a result set mean that the statement didn't
			 * return one, or that it should have but an error occurred?
			 */
			if (mysql_field_count (conn) == 0)
			{
				/*
				 * statement generated no result set (it was not a SELECT,
				 * SHOW, DESCRIBE, etc.); just report rows-affected value.
				 */
				printf ("%lu rows affected\n",
							(unsigned long) mysql_affected_rows (conn));
			}
			else	/* an error occurred */
			{
				print_error (conn, "Could not retrieve result set");
				keep_going = 0;
			}
		}
		/* determine whether more results exist */
		/* 0 = yes, -1 = no, >0 = error */
		status = mysql_next_result (conn);
		if (status != 0)		/* no more results, or an error occurred */
		{
			keep_going = 0;
			if (status > 0)		/* error */
				print_error (conn, "Could not execute statement");
		}
	} while (keep_going);
}

int
main (int argc, char *argv[])
{
int opt_err;

	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 */
	opt_flags |= CLIENT_MULTI_STATEMENTS;
	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);
	}

	char *str;
	/* this succeeds */
	str = "DROP PROCEDURE IF EXISTS p; CREATE PROCEDURE p() BEGIN SELECT 1; END; CALL p()";
	process_multi_statement (conn, str);

	/* this fails */
	str = "DROP PROCEDURE IF EXISTS p; CREATE PROCEDURE p() SELECT 1; CALL p()";
	process_multi_statement (conn, str);

	/* just to prove it's not an order effect, this succeeds */
	str = "DROP PROCEDURE IF EXISTS p; CREATE PROCEDURE p() BEGIN SELECT 1; END; CALL p()";
	process_multi_statement (conn, str);

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