/* * bug-14013.c - mysql_stmt_store_result() results in error if stmt * has a cursor open. * * To trigger bug, see comments in main(). */ #include /* for strdup() */ #include #include #include #include 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); }